patch-2.1.44 linux/arch/mips/kernel/syscall.c
Next file: linux/arch/mips/kernel/syscalls.h
Previous file: linux/arch/mips/kernel/signal.c
Back to the patch index
Back to the overall index
- Lines: 456
- Date:
Thu Jun 26 12:33:37 1997
- Orig file:
v2.1.43/linux/arch/mips/kernel/syscall.c
- Orig date:
Sun Jan 26 02:07:05 1997
diff -u --recursive --new-file v2.1.43/linux/arch/mips/kernel/syscall.c linux/arch/mips/kernel/syscall.c
@@ -5,8 +5,16 @@
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
- * Copyright (C) 1995 by Ralf Baechle
+ * Copyright (C) 1995, 1996 by Ralf Baechle
+ *
+ * TODO: Implement the compatibility syscalls.
+ * Don't waste that much memory for empty entries in the syscall
+ * table.
*/
+#undef CONF_PRINT_SYSCALLS
+#undef CONF_DEBUG_IRIX
+
+#include <linux/config.h>
#include <linux/linkage.h>
#include <linux/mm.h>
#include <linux/smp.h>
@@ -14,73 +22,79 @@
#include <linux/mman.h>
#include <linux/sched.h>
#include <linux/unistd.h>
+#include <asm/branch.h>
#include <asm/ptrace.h>
-#include <asm/segment.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[];
asmlinkage int sys_pipe(struct pt_regs *regs)
{
int fd[2];
- int error;
+ int error, res;
lock_kernel();
error = do_pipe(fd);
- if (error)
+ if (error) {
+ res = error;
goto out;
- regs->reg2 = fd[0];
- regs->reg3 = fd[1];
+ }
+ regs->regs[3] = fd[1];
+ res = fd[0];
out:
unlock_kernel();
- return error;
+ return res;
}
asmlinkage unsigned long sys_mmap(unsigned long addr, size_t len, int prot,
int flags, int fd, off_t offset)
{
struct file * file = NULL;
- int ret = -EBADF;
+ unsigned long res;
lock_kernel();
- if (flags & MAP_RENAME) {
+ if (!(flags & MAP_ANONYMOUS)) {
if (fd >= NR_OPEN || !(file = current->files->fd[fd]))
- goto out;
+ return -EBADF;
}
flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
- ret = do_mmap(file, addr, len, prot, flags, offset);
-out:
+ res = do_mmap(file, addr, len, prot, flags, offset);
+
unlock_kernel();
- return ret;
+ return res;
}
asmlinkage int sys_idle(void)
{
- int ret = -EPERM;
+ int ret = -EPERM;
lock_kernel();
if (current->pid != 0)
goto out;
-
/* endless idle loop with no priority at all */
+ current->priority = -100;
current->counter = -100;
for (;;) {
/*
- * R4[26]00 have wait, R4[04]00 don't.
+ * R4[236]00 have wait, R4[04]00 don't.
+ * FIXME: We should save power by reducing the clock where
+ * possible. Should help alot for battery powered
+ * R4200/4300i systems.
*/
if (wait_available && !need_resched)
__asm__(".set\tmips3\n\t"
"wait\n\t"
".set\tmips0\n\t");
+ run_task_queue(&tq_scheduler);
schedule();
}
- ret = 0;
out:
unlock_kernel();
return ret;
@@ -88,28 +102,28 @@
asmlinkage int sys_fork(struct pt_regs *regs)
{
- int ret;
+ int res;
lock_kernel();
- ret = do_fork(SIGCHLD, regs->reg29, regs);
+ res = do_fork(SIGCHLD, regs->regs[29], regs);
unlock_kernel();
- return ret;
+ return res;
}
asmlinkage int sys_clone(struct pt_regs *regs)
{
unsigned long clone_flags;
unsigned long newsp;
- int ret;
+ int res;
lock_kernel();
- clone_flags = regs->reg4;
- newsp = regs->reg5;
+ clone_flags = regs->regs[4];
+ newsp = regs->regs[5];
if (!newsp)
- newsp = regs->reg29;
- ret = do_fork(clone_flags, newsp, regs);
+ newsp = regs->regs[29];
+ res = do_fork(clone_flags, newsp, regs);
unlock_kernel();
- return ret;
+ return res;
}
/*
@@ -117,72 +131,136 @@
*/
asmlinkage int sys_execve(struct pt_regs *regs)
{
- int error;
+ int res;
char * filename;
lock_kernel();
- error = getname((char *) regs->reg4, &filename);
- if (error)
+ res = getname((char *) (long)regs->regs[4], &filename);
+ if (res)
goto out;
- error = do_execve(filename, (char **) regs->reg5,
- (char **) regs->reg6, regs);
+ res = do_execve(filename, (char **) (long)regs->regs[5],
+ (char **) (long)regs->regs[6], regs);
putname(filename);
+
out:
unlock_kernel();
- return error;
+ return res;
}
/*
* Do the indirect syscall syscall.
+ * Don't care about kernel locking; the actual syscall will do it.
*/
-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);
}
+/*
+ * If we ever come here the user sp is bad. Zap the process right away.
+ * Due to the bad stack signaling wouldn't work.
+ * XXX kernel locking???
+ */
+asmlinkage void bad_stack(void)
+{
+ do_exit(SIGSEGV);
+}
+
+#ifdef CONF_PRINT_SYSCALLS
+#define SYS(fun, narg) #fun,
+static char *sfnames[] = {
+#include "syscalls.h"
+};
+#endif
+
+#if defined(CONFIG_BINFMT_IRIX) && defined(CONF_DEBUG_IRIX)
+#define SYS(fun, narg) #fun,
+static char *irix_sys_names[] = {
+#include "irix5sys.h"
+};
+#endif
+
+/*
+ * This isn't entirely correct with respect to kernel locking ...
+ */
void do_sys(struct pt_regs *regs)
{
unsigned long syscallnr, usp;
syscall_t syscall;
int errno, narg;
- /*
- * Compute the return address;
- */
- if (regs->cp0_cause & CAUSEF_BD)
- {
+ /* Skip syscall instruction */
+ if (delay_slot(regs)) {
/*
- * This syscall is in a branch delay slot. Since we don't do
- * branch delay slot handling we would get a process trying
- * to do syscalls ever and ever again. So better zap it.
+ * By convention "li v0,<syscallno>" is always preceeding
+ * the syscall instruction. So if we're in a delay slot
+ * userland is screwed up.
*/
- printk("%s: syscall in branch delay slot.\n", current->comm);
- current->sig->action[SIGILL-1].sa_handler = NULL;
- current->blocked &= ~(1<<(SIGILL-1));
- send_sig(SIGILL, current, 1);
+ force_sig(SIGILL, current);
return;
}
regs->cp0_epc += 4;
- syscallnr = regs->reg2;
+ syscallnr = regs->regs[2];
if (syscallnr > (__NR_Linux + __NR_Linux_syscalls))
goto illegal_syscall;
@@ -191,37 +269,64 @@
goto illegal_syscall;
narg = sys_narg_table[syscallnr];
- if (narg > 4)
- {
+#ifdef CONF_PRINT_SYSCALLS
+ 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
+ if (narg > 4) {
/*
* Verify that we can safely get the additional parameters
* from the user stack. Of course I could read the params
* from unaligned addresses ... Consider this a programming
* course caliber .45.
*/
- usp = regs->reg29;
- if (usp & 3)
- {
+ usp = regs->regs[29];
+ if (usp & 3) {
printk("unaligned usp\n");
- send_sig(SIGSEGV, current, 1);
- regs->reg2 = EFAULT;
- regs->reg7 = 1;
+ 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))) {
+ regs->regs[2] = EFAULT;
+ regs->regs[7] = 1;
return;
}
- errno = verify_area(VERIFY_READ, (void *) (usp + 16),
- (narg - 4) * sizeof(unsigned long));
- if (errno < 0)
- goto bad_syscall;
}
if ((current->flags & PF_TRACESYS) == 0)
{
errno = do_syscalls(regs, syscall, narg);
- if (errno < 0 || current->errno)
+ if ((errno < 0 && errno > (-ENOIOCTLCMD - 1)) || current->errno) {
goto bad_syscall;
-
- regs->reg2 = errno;
- regs->reg7 = 0;
+ }
+ regs->regs[2] = errno;
+ regs->regs[7] = 0;
}
else
{
@@ -230,25 +335,49 @@
errno = do_syscalls(regs, syscall, narg);
if (errno < 0 || current->errno)
{
- regs->reg2 = -errno;
- regs->reg7 = 1;
+ regs->regs[2] = -errno;
+ regs->regs[7] = 1;
}
else
{
- regs->reg2 = errno;
- regs->reg7 = 0;
+ regs->regs[2] = errno;
+ regs->regs[7] = 0;
}
syscall_trace();
}
+#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;
bad_syscall:
- regs->reg2 = -errno;
- regs->reg7 = 1;
+ regs->regs[0] = regs->regs[2] = -errno;
+ regs->regs[7] = 1;
+#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->reg2 = ENOSYS;
- regs->reg7 = 1;
+
+ regs->regs[2] = ENOSYS;
+ regs->regs[7] = 1;
+#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;
}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov