/* $Id: syscall.c,v 1.2 2005/06/16 13:46:21 cgoh Exp $
 *
 * syscall.c: system call implementations
 *
 * This file is subject to the terms and conditions of the GNU General Public
 * License.  See the file "COPYING" in the main directory of this archive
 * for more details.
 *
 * Copyright (C) Equator Technologies
 */
#include <linux/config.h>
#include <linux/linkage.h>
#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/smp_lock.h>
#include <linux/file.h>

#include <asm/debug.h>
#include <asm/mman.h>
#include <asm/uaccess.h>

#include "regspass.h"



asmlinkage int sys_clone( int flags, int usp, void *padd2, void *padd3, void *padd4, void *padd5, void *padd6, ctxSyscallContext_t syscall_context )
{
    struct taskContextAndDelta  regs;

    regs.sp                      = syscall_context->sp;
    regs.excSP                   = syscall_context->excSP;
    regs.context.header.u.s.psw  = syscall_context->header.u.s.psw;

    if (SAVE_CONTEXT(&regs.context) == 0) {
       /* parent */
        regs.context.header.u.s.psw= syscall_context->header.u.s.psw;
	    return do_fork(flags, usp, (void*)&regs);
    } else {
       /* child */
        syscall_context= SHIFT_POINTER(syscall_context, regs.delta);
        syscall_context->sp   = regs.sp;
        syscall_context->excSP= regs.excSP;
        TERMINATE_SWITCH();
        return 0;
    }          
}


asmlinkage int sys_fork(void *padd0, void *padd1, void *padd2, void *padd3, void *padd4, void *padd5, void *padd6, ctxSyscallContext_t syscall_context)
{
    struct taskContextAndDelta  regs;

    regs.sp                      = syscall_context->sp;
    regs.excSP                   = syscall_context->excSP;
    regs.context.header.u.s.psw  = syscall_context->header.u.s.psw;

    if (SAVE_CONTEXT(&regs.context) == 0) {	
       /* parent */
        regs.context.header.u.s.psw= syscall_context->header.u.s.psw;
	    return do_fork(SIGCHLD, syscall_context->sp, (void*)&regs);
    } else {
       /* child */
        syscall_context= SHIFT_POINTER(syscall_context, regs.delta);
        syscall_context->sp   = regs.sp;
        syscall_context->excSP= regs.excSP;
        TERMINATE_SWITCH();
        return 0;
    }          
}




asmlinkage int sys_execve(const char *filename, char *const argv [], char *const envp[])
{
	int                   error;
    struct thread_struct  regs;

    lock_kernel();

    filename = getname(filename);
	error    = PTR_ERR(filename);

	if (!IS_ERR(filename)) {
	    error = do_execve(filename, argv, envp, &regs);
	    putname(filename);
    }

    unlock_kernel();

    if (error) {
	    return error;
    } else {
        current->tss.start_fun= regs.start_fun;
        current->tss.args     = regs.args;
        current->tss.atomic_start = 0;
        current->tss.atomic_end = 0;

        INITIATE_SWITCH();
        RESTORE_CONTEXT( &regs.regs );
    }
}



asmlinkage int sys_pipe(int filedes[2])
{
    int fd[2];
    int error;

    lock_kernel();
    error = do_pipe(fd);
    unlock_kernel();
    if (!error) {
        if (copy_to_user(filedes, fd, 2*sizeof(int)))
            error = -EFAULT;
    }
    return error;
}



asmlinkage int sys_syscall(int function_code, void *a0, void *a1, void *a2, void *a3, void *a4, void *a5, ctxSyscallContext_t syscall_context)
{
    if (function_code == __NR_syscall) {
   /*
	* Prevent trouble by recursive
	* syscall(__NR_syscall, __NR_syscall,...);
	*/
        return -EINVAL;
    } else {
	    return syscall_handler(a0,a1,a2,a3,a4,a5,0,function_code,syscall_context);
    }
}



asmlinkage int sys_mmap(void *addr, size_t len, int prot , int flags, int fd, off_t offset)
{
	struct file * file = NULL;
	unsigned long error = -EFAULT;

	lock_kernel();
	if (!(flags & MAP_ANONYMOUS)) {
		error = -EBADF;
		file = fget(fd);
		if (!file)
			goto out;
	}
    flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
    error = do_mmap(file, addr, len, prot, flags, offset);
    if (file)
            fput(file);
out:
	unlock_kernel();
	return error;
}



asmlinkage int sys_uname(struct utsname *name)
{
	return -ENOSYS;
}

asmlinkage int sys_olduname()
{
	return -ENOSYS;
}

asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int turn_on)
{
	return -ENOSYS;
}

asmlinkage int sys_iopl(int level)
{
	return -ENOSYS;
}

asmlinkage int sys_idle(void)
{
	return -ENOSYS;
}

asmlinkage int sys_vm86(void *v86)
{
	return -ENOSYS;
}

/* ETI special syscalls */
#define IN_CODE_RANGE(mm, x)	((mm)->start_code <= (x) && (x) < (mm)->end_code)

asmlinkage int sys_eti_register(unsigned atomic_start, unsigned atomic_end) {

    struct task_struct* tsk = current;
	struct mm_struct*   mm  = tsk->mm;

	if ( IN_CODE_RANGE(mm, atomic_start) && IN_CODE_RANGE(mm, atomic_end) ) {
	    tsk->tss.atomic_start = atomic_start;
	    tsk->tss.atomic_end   = atomic_end;

	    return 0;
	}
	else {
	    return -EINVAL;
	}
}

/* test and set */
asmlinkage int sys_eti_tas(int *spinlock_u)
{
	long int ret;
	int spinlock;

	if (get_user(spinlock, spinlock_u))
		return -EFAULT;

	if ( (ret = spinlock) == 0 ) {
		spinlock = 1;
	}

	if (ret == 0)
		put_user(spinlock, spinlock_u);

	return ret;
}

/* compare and swap */
asmlinkage int sys_eti_cas(long int *p_u, long int oldval, long int newval)
{
	int res = 0;
	long int p;

	/* perform: p = *p_u */
	if (get_user(p, p_u))
		return -EFAULT;

	if (p == oldval) {
		p = newval;
		res = 1;

		/* perform: *p_u = p */
		put_user(p, p_u);
	}
	
	return res;
}


/*
 * Set the signal handlers return address
 */ 
asmlinkage int sys_eti_signal_return( unsigned long return_address )
{
    struct task_struct* tsk = current;

	if ( IN_CODE_RANGE(tsk->mm, return_address) ) {
	    tsk->tss.signal_return= return_address;

	    return 0;
	} else {
	    return -EINVAL;
	}
}
