
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/smp.h>
#include <linux/smp_lock.h>
#include <linux/sem.h>
#include <linux/msg.h>
#include <linux/shm.h>
#include <linux/stat.h>
#include <linux/mman.h>
#include <linux/sys.h>
#include <linux/unistd.h>
#include <asm/ipc.h>
#include <asm/uaccess.h>
#include <asm/ppcops.h>

#ifdef CONFIG_XMON
static inline int sc_enter(int sc) {
	extern int _sc_enter(int, int);
	return _sc_enter(current->pid, sc);
}

static inline void sc_leave(int pos, int rv) {
	extern void _sc_leave(int, int);
	_sc_leave(pos, rv);
}

#else
#define sc_enter(x,y)
#define sc_leave(x,y)
#endif

asmlinkage void syscall(struct pt_regs *regs) {
	extern void syscall_trace(void);
	extern void *sys_call_table[];
	long long (*sc)(unsigned long a1, unsigned long a2, unsigned long a3,
	          unsigned long a4, unsigned long a5, unsigned long a6,
	          struct pt_regs *regs);
	int theCall = regs->gpr[0];
	int rtval;
	long long tval;
	int flags;

#ifdef CONFIG_XMON
	int sc_pos;
        sc_pos = sc_enter(theCall);
#endif
	save_flags(flags);
	sti();

	regs->orig_gpr3 = regs->gpr[3];
	regs->ccr &= ~0x10000000;		/* assume no errors ... */
	if (theCall == 0x7777) {
		/*
		 * Restartable system call nonsense.
		 */
		extern int sys_sigreturn(struct pt_regs *);
		rtval = sys_sigreturn(regs);
		if (rtval >= 0) {
			restore_flags(flags);
#ifdef CONFIG_XMON
                        sc_leave(sc_pos, regs->gpr[3]);
#endif
			return;
		}

	restart_check:
		regs->result = rtval;
		if (rtval < 0 && -rtval < _LAST_ERRNO) {
			regs->gpr[3] = (unsigned long) -rtval;
			regs->ccr |= 0x10000000;	/* mark for errno set */
			if (-rtval == ERESTARTNOHAND)
				/* no handler for EINTR restart */
				regs->gpr[3] = EINTR;
		}
		else {
			regs->gpr[3] = (unsigned long) rtval;
                        regs->gpr[4] = (unsigned long) (tval & 0xffffffff);
		}
		if (current->flags & PF_TRACESYS)
			syscall_trace();
		restore_flags(flags);
#ifdef CONFIG_XMON
		sc_leave(sc_pos, regs->gpr[3]);
#endif
		return;
	}
	if (current->flags & PF_TRACESYS)
		syscall_trace();
	if (theCall < 0 || theCall > NR_syscalls) {
		/*
		 * Invalid call.
		 */
		regs->gpr[3] = ENOSYS;
		regs->ccr |= 0x10000000;	/* mark for errno set */
		if (current->flags & PF_TRACESYS)
			syscall_trace();
		restore_flags(flags);
#ifdef CONFIG_XMON
		sc_leave(sc_pos, regs->gpr[3]);
#endif
		return;
	}
	sc = (long long (*)(unsigned long, unsigned long, unsigned long,
	              unsigned long, unsigned long, unsigned long,
	              struct pt_regs *)) sys_call_table[theCall];
	if (!sc) {
	    /*
	     * No such call.
	     */
	     regs->gpr[3] = ENOSYS;
	     regs->ccr |= 0x10000000;	/* mark for errno set */
	     if (current->flags & PF_TRACESYS)
		     syscall_trace();
	     restore_flags(flags);
#ifdef CONFIG_XMON
		sc_leave(sc_pos, regs->gpr[3]);
#endif
	     return;
	}
	tval = (*sc)(regs->gpr[3], regs->gpr[4], regs->gpr[5],
		      regs->gpr[6], regs->gpr[7], regs->gpr[8],
		      regs);
	rtval = (int)(tval >> 32);
	goto restart_check;
}
