summaryrefslogtreecommitdiffstats
path: root/arch/mips/kernel/syscall.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/mips/kernel/syscall.c')
-rw-r--r--arch/mips/kernel/syscall.c213
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;
}