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

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