diff options
Diffstat (limited to 'arch/mips/kernel/syscall.c')
-rw-r--r-- | arch/mips/kernel/syscall.c | 213 |
1 files changed, 139 insertions, 74 deletions
diff --git a/arch/mips/kernel/syscall.c b/arch/mips/kernel/syscall.c index 144a3c905..3b595e05d 100644 --- a/arch/mips/kernel/syscall.c +++ b/arch/mips/kernel/syscall.c @@ -12,7 +12,9 @@ * table. */ #undef CONF_PRINT_SYSCALLS +#undef CONF_DEBUG_IRIX +#include <linux/config.h> #include <linux/linkage.h> #include <linux/mm.h> #include <linux/mman.h> @@ -20,25 +22,16 @@ #include <linux/unistd.h> #include <asm/branch.h> #include <asm/ptrace.h> -#include <asm/uaccess.h> #include <asm/signal.h> +#include <asm/uaccess.h> extern asmlinkage void syscall_trace(void); typedef asmlinkage int (*syscall_t)(void *a0,...); -extern asmlinkage int do_syscalls(struct pt_regs *regs, syscall_t fun, - int narg); +extern asmlinkage int (*do_syscalls)(struct pt_regs *regs, syscall_t fun, + int narg); extern syscall_t sys_call_table[]; extern unsigned char sys_narg_table[]; -/* - * The pipe syscall has a unusual calling convention. We return the two - * filedescriptors in the result registers v0/v1. The syscall wrapper - * from libc places these results in the array to which the argument of - * pipe points to. This is like other MIPS operating systems and unlike - * Linux/i386 where the kernel itself places the results in the file - * descriptor array itself. This calling convention also has the advantage - * of lower overhead because we don't need to call verify_area. - */ asmlinkage int sys_pipe(struct pt_regs *regs) { int fd[2]; @@ -74,9 +67,10 @@ asmlinkage int sys_idle(void) current->counter = -100; for (;;) { /* - * Not all MIPS R-series CPUs have the wait instruction. + * R4[236]00 have wait, R4[04]00 don't. * FIXME: We should save power by reducing the clock where - * possible. + * possible. Should help alot for battery powered + * R4200/4300i systems. */ if (wait_available && !need_resched) __asm__(".set\tmips3\n\t" @@ -86,33 +80,6 @@ asmlinkage int sys_idle(void) } } -#if 0 -/* - * RISC/os compatible SysV flavoured fork(2) syscall. - * - * This call has a different calling sequence: - * child return value: pid of parent, secondary result = 1. - * parent return value: pid of child, secondary result value = 0. - * error: errno, secondary result = 0. - */ -asmlinkage int sys_sysv_fork(struct pt_regs *regs) -{ - int pid; - - pid = do_fork(SIGCHLD, regs->regs[29], regs); - if (pid == 0) { /* child */ - regs->regs[3] = 1; - return current->p_pptr->pid; - } /* parent or error */ - - regs->regs[3] = 0; - return pid; -} -#endif - -/* - * Normal Linux fork(2) syscall - */ asmlinkage int sys_fork(struct pt_regs *regs) { return do_fork(SIGCHLD, regs->regs[29], regs); @@ -144,33 +111,73 @@ asmlinkage int sys_execve(struct pt_regs *regs) error = do_execve(filename, (char **) (long)regs->regs[5], (char **) (long)regs->regs[6], regs); putname(filename); - return error; } /* * Do the indirect syscall syscall. */ -asmlinkage int sys_syscall(unsigned long a0, unsigned long a1, unsigned long a2, - unsigned long a3, unsigned long a4, unsigned long a5, - unsigned long a6) +asmlinkage int sys_syscall(struct pt_regs *regs) { syscall_t syscall; + unsigned long syscallnr = regs->regs[4]; + unsigned long a0, a1, a2, a3, a4, a5, a6; + int nargs, errno; - if (a0 > __NR_Linux + __NR_Linux_syscalls) + if (syscallnr > __NR_Linux + __NR_Linux_syscalls) return -ENOSYS; - syscall = sys_call_table[a0]; + syscall = sys_call_table[syscallnr]; + nargs = sys_narg_table[syscallnr]; /* * Prevent stack overflow by recursive * syscall(__NR_syscall, __NR_syscall,...); */ - if (syscall == (syscall_t) sys_syscall) + if (syscall == (syscall_t) sys_syscall) { return -EINVAL; + } - if (syscall == NULL) + if (syscall == NULL) { return -ENOSYS; + } + if(nargs > 3) { + unsigned long usp = regs->regs[29]; + unsigned long *sp = (unsigned long *) usp; + if(usp & 3) { + printk("unaligned usp -EFAULT\n"); + force_sig(SIGSEGV, current); + return -EFAULT; + } + errno = verify_area(VERIFY_READ, (void *) (usp + 16), + (nargs - 3) * sizeof(unsigned long)); + if(errno) { + return -EFAULT; + } + switch(nargs) { + case 7: + a3 = sp[4]; a4 = sp[5]; a5 = sp[6]; a6 = sp[7]; + break; + case 6: + a3 = sp[4]; a4 = sp[5]; a5 = sp[6]; a6 = 0; + break; + case 5: + a3 = sp[4]; a4 = sp[5]; a5 = a6 = 0; + break; + case 4: + a3 = sp[4]; a4 = a5 = a6 = 0; + break; + + default: + a3 = a4 = a5 = a6 = 0; + break; + } + } else { + a3 = a4 = a5 = a6 = 0; + } + a0 = regs->regs[5]; a1 = regs->regs[6]; a2 = regs->regs[7]; + if(nargs == 0) + a0 = (unsigned long) regs; return syscall((void *)a0, a1, a2, a3, a4, a5, a6); } @@ -190,13 +197,20 @@ static char *sfnames[] = { }; #endif -asmlinkage void do_sys(struct pt_regs *regs) +#if defined(CONFIG_BINFMT_IRIX) && defined(CONF_DEBUG_IRIX) +#define SYS(fun, narg) #fun, +static char *irix_sys_names[] = { +#include "irix5sys.h" +}; +#endif + +void do_sys(struct pt_regs *regs) { unsigned long syscallnr, usp; syscall_t syscall; int errno, narg; - /* Skip syscall instruction */ + /* Skip syscall instruction */ if (delay_slot(regs)) { /* * By convention "li v0,<syscallno>" is always preceeding @@ -211,70 +225,121 @@ asmlinkage void do_sys(struct pt_regs *regs) syscallnr = regs->regs[2]; if (syscallnr > (__NR_Linux + __NR_Linux_syscalls)) goto illegal_syscall; + syscall = sys_call_table[syscallnr]; + if (syscall == NULL) + goto illegal_syscall; + narg = sys_narg_table[syscallnr]; #ifdef CONF_PRINT_SYSCALLS - printk("do_sys(): %s()", sfnames[syscallnr - __NR_Linux]); + if(syscallnr >= 4000) + printk("do_sys(%s:%d): %s(%08lx,%08lx,%08lx,%08lx)<pc=%08lx>", + current->comm, current->pid, sfnames[syscallnr - __NR_Linux], + regs->regs[4], regs->regs[5], regs->regs[6], regs->regs[7], + regs->cp0_epc); +#endif +#if defined(CONFIG_BINFMT_IRIX) && defined(CONF_DEBUG_IRIX) + if(syscallnr < 2000 && syscallnr >= 1000) { + printk("irix_sys(%s:%d): %s(", current->comm, + current->pid, irix_sys_names[syscallnr - 1000]); + if((narg < 4) && (narg != 0)) { + int i = 0; + + while(i < (narg - 1)) { + printk("%08lx, ", regs->regs[i + 4]); + i++; + } + printk("%08lx) ", regs->regs[i + 4]); + } else if(narg == 0) { + printk("%08lx, %08lx, %08lx, %08lx) ", + regs->regs[4], regs->regs[5], regs->regs[6], + regs->regs[7]); + } else + printk("narg=%d) ", narg); + } #endif - narg = sys_narg_table[syscallnr]; if (narg > 4) { /* * Verify that we can safely get the additional parameters - * from the user stack. + * from the user stack. Of course I could read the params + * from unaligned addresses ... Consider this a programming + * course caliber .45. */ usp = regs->regs[29]; if (usp & 3) { printk("unaligned usp\n"); - do_exit(SIGBUS); + force_sig(SIGSEGV, current); + regs->regs[2] = EFAULT; + regs->regs[7] = 1; return; } - if (!access_ok(VERIFY_READ, (void *) (usp + 16), - (narg - 4) * sizeof(unsigned long))) { - errno = -EFAULT; - goto syscall_error; + (narg - 4) * sizeof(unsigned long))) { + regs->regs[2] = EFAULT; + regs->regs[7] = 1; + return; } } - if ((current->flags & PF_TRACESYS) == 0) { + if ((current->flags & PF_TRACESYS) == 0) + { errno = do_syscalls(regs, syscall, narg); - if (errno < 0) - goto syscall_error; - + if ((errno < 0 && errno > (-ENOIOCTLCMD - 1)) || current->errno) { + goto bad_syscall; + } regs->regs[2] = errno; regs->regs[7] = 0; - } else { + } + else + { syscall_trace(); errno = do_syscalls(regs, syscall, narg); - if (errno < 0) { + if (errno < 0 || current->errno) + { regs->regs[2] = -errno; regs->regs[7] = 1; - } else { + } + else + { regs->regs[2] = errno; regs->regs[7] = 0; } syscall_trace(); } -#ifdef CONF_PRINT_SYSCALLS +#if defined(CONF_PRINT_SYSCALLS) || \ + (defined(CONFIG_BINFMT_IRIX) && defined(CONF_DEBUG_IRIX)) +#if 0 printk(" returning: normal\n"); +#else + if(syscallnr >= 4000 && syscallnr < 5000) + printk(" returning: %08lx\n", (unsigned long) errno); +#endif #endif return; -syscall_error: - regs->regs[2] = -errno; +bad_syscall: + regs->regs[0] = regs->regs[2] = -errno; regs->regs[7] = 1; -#ifdef CONF_PRINT_SYSCALLS - printk(" returning: syscall_error, errno=%d\n", -errno); +#if defined(CONF_PRINT_SYSCALLS) || \ + (defined(CONFIG_BINFMT_IRIX) && defined(CONF_DEBUG_IRIX)) +#if 0 + printk(" returning: bad_syscall\n"); +#else + if(syscallnr >= 4000 && syscallnr < 5000) + printk(" returning error: %d\n", errno); +#endif #endif return; - illegal_syscall: + regs->regs[2] = ENOSYS; regs->regs[7] = 1; -#ifdef CONF_PRINT_SYSCALLS - printk(" returning: illegal_syscall\n"); +#if defined(CONF_PRINT_SYSCALLS) || \ + (defined(CONFIG_BINFMT_IRIX) && defined(CONF_DEBUG_IRIX)) + if(syscallnr >= 1000 && syscallnr < 2000) + printk(" returning: illegal_syscall\n"); #endif return; } |