flag parameters: pipe
[safe/jmp/linux-2.6] / arch / mips / kernel / syscall.c
index ae2a131..343015a 100644 (file)
@@ -8,11 +8,12 @@
  * Copyright (C) 2001 MIPS Technologies, Inc.
  */
 #include <linux/a.out.h>
+#include <linux/capability.h>
 #include <linux/errno.h>
 #include <linux/linkage.h>
 #include <linux/mm.h>
+#include <linux/fs.h>
 #include <linux/smp.h>
-#include <linux/smp_lock.h>
 #include <linux/mman.h>
 #include <linux/ptrace.h>
 #include <linux/sched.h>
 #include <linux/msg.h>
 #include <linux/shm.h>
 #include <linux/compiler.h>
+#include <linux/module.h>
+#include <linux/ipc.h>
 
 #include <asm/branch.h>
 #include <asm/cachectl.h>
 #include <asm/cacheflush.h>
-#include <asm/ipc.h>
-#include <asm/offset.h>
+#include <asm/asm-offsets.h>
 #include <asm/signal.h>
 #include <asm/sim.h>
 #include <asm/shmparam.h>
 #include <asm/sysmips.h>
 #include <asm/uaccess.h>
 
-asmlinkage int sys_pipe(nabi_no_regargs volatile struct pt_regs regs)
+/*
+ * For historic reasons the pipe(2) syscall on MIPS has an unusual calling
+ * convention.  It returns results in registers $v0 / $v1 which means there
+ * is no need for it to do verify the validity of a userspace pointer
+ * argument.  Historically that used to be expensive in Linux.  These days
+ * the performance advantage is negligible.
+ */
+asmlinkage int sysm_pipe(nabi_no_regargs volatile struct pt_regs regs)
 {
        int fd[2];
        int error, res;
 
-       error = do_pipe(fd);
+       error = do_pipe_flags(fd, 0);
        if (error) {
                res = error;
                goto out;
@@ -56,6 +65,8 @@ out:
 
 unsigned long shm_align_mask = PAGE_SIZE - 1;  /* Sane caches */
 
+EXPORT_SYMBOL(shm_align_mask);
+
 #define COLOUR_ALIGN(addr,pgoff)                               \
        ((((addr) + shm_align_mask) & ~shm_align_mask) +        \
         (((pgoff) << PAGE_SHIFT) & shm_align_mask))
@@ -69,7 +80,14 @@ unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr,
 
        task_size = STACK_TOP;
 
+       if (len > task_size)
+               return -ENOMEM;
+
        if (flags & MAP_FIXED) {
+               /* Even MAP_FIXED mappings must reside within task_size.  */
+               if (task_size - len < addr)
+                       return -EINVAL;
+
                /*
                 * We do not accept a shared mapping if it would violate
                 * cache aliasing constraints.
@@ -79,8 +97,6 @@ unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr,
                return addr;
        }
 
-       if (len > task_size)
-               return -ENOMEM;
        do_color_align = 0;
        if (filp || (flags & MAP_SHARED))
                do_color_align = 1;
@@ -157,30 +173,47 @@ asmlinkage unsigned long
 sys_mmap2(unsigned long addr, unsigned long len, unsigned long prot,
           unsigned long flags, unsigned long fd, unsigned long pgoff)
 {
-       return do_mmap2(addr, len, prot, flags, fd, pgoff);
+       if (pgoff & (~PAGE_MASK >> 12))
+               return -EINVAL;
+
+       return do_mmap2(addr, len, prot, flags, fd, pgoff >> (PAGE_SHIFT-12));
 }
 
 save_static_function(sys_fork);
-__attribute_used__ noinline static int
+static int __used noinline
 _sys_fork(nabi_no_regargs struct pt_regs regs)
 {
        return do_fork(SIGCHLD, regs.regs[29], &regs, 0, NULL, NULL);
 }
 
 save_static_function(sys_clone);
-__attribute_used__ noinline static int
+static int __used noinline
 _sys_clone(nabi_no_regargs struct pt_regs regs)
 {
        unsigned long clone_flags;
        unsigned long newsp;
-       int *parent_tidptr, *child_tidptr;
+       int __user *parent_tidptr, *child_tidptr;
 
        clone_flags = regs.regs[4];
        newsp = regs.regs[5];
        if (!newsp)
                newsp = regs.regs[29];
-       parent_tidptr = (int *) regs.regs[6];
-       child_tidptr = (int *) regs.regs[7];
+       parent_tidptr = (int __user *) regs.regs[6];
+#ifdef CONFIG_32BIT
+       /* We need to fetch the fifth argument off the stack.  */
+       child_tidptr = NULL;
+       if (clone_flags & (CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID)) {
+               int __user *__user *usp = (int __user *__user *) regs.regs[29];
+               if (regs.regs[2] == __NR_syscall) {
+                       if (get_user (child_tidptr, &usp[5]))
+                               return -EFAULT;
+               }
+               else if (get_user (child_tidptr, &usp[4]))
+                       return -EFAULT;
+       }
+#else
+       child_tidptr = (int __user *) regs.regs[8];
+#endif
        return do_fork(clone_flags, newsp, &regs, 0,
                       parent_tidptr, child_tidptr);
 }
@@ -193,12 +226,12 @@ asmlinkage int sys_execve(nabi_no_regargs struct pt_regs regs)
        int error;
        char * filename;
 
-       filename = getname((char *) (long)regs.regs[4]);
+       filename = getname((char __user *) (long)regs.regs[4]);
        error = PTR_ERR(filename);
        if (IS_ERR(filename))
                goto out;
-       error = do_execve(filename, (char **) (long)regs.regs[5],
-                         (char **) (long)regs.regs[6], &regs);
+       error = do_execve(filename, (char __user *__user *) (long)regs.regs[5],
+                         (char __user *__user *) (long)regs.regs[6], &regs);
        putname(filename);
 
 out:
@@ -208,9 +241,9 @@ out:
 /*
  * Compacrapability ...
  */
-asmlinkage int sys_uname(struct old_utsname * name)
+asmlinkage int sys_uname(struct old_utsname __user * name)
 {
-       if (name && !copy_to_user(name, &system_utsname, sizeof (*name)))
+       if (name && !copy_to_user(name, utsname(), sizeof (*name)))
                return 0;
        return -EFAULT;
 }
@@ -218,72 +251,71 @@ asmlinkage int sys_uname(struct old_utsname * name)
 /*
  * Compacrapability ...
  */
-asmlinkage int sys_olduname(struct oldold_utsname * name)
+asmlinkage int sys_olduname(struct oldold_utsname __user * name)
 {
        int error;
 
        if (!name)
                return -EFAULT;
-       if (!access_ok(VERIFY_WRITE,name,sizeof(struct oldold_utsname)))
+       if (!access_ok(VERIFY_WRITE, name, sizeof(struct oldold_utsname)))
                return -EFAULT;
 
-       error = __copy_to_user(&name->sysname,&system_utsname.sysname,__OLD_UTS_LEN);
-       error -= __put_user(0,name->sysname+__OLD_UTS_LEN);
-       error -= __copy_to_user(&name->nodename,&system_utsname.nodename,__OLD_UTS_LEN);
-       error -= __put_user(0,name->nodename+__OLD_UTS_LEN);
-       error -= __copy_to_user(&name->release,&system_utsname.release,__OLD_UTS_LEN);
-       error -= __put_user(0,name->release+__OLD_UTS_LEN);
-       error -= __copy_to_user(&name->version,&system_utsname.version,__OLD_UTS_LEN);
-       error -= __put_user(0,name->version+__OLD_UTS_LEN);
-       error -= __copy_to_user(&name->machine,&system_utsname.machine,__OLD_UTS_LEN);
-       error = __put_user(0,name->machine+__OLD_UTS_LEN);
+       error = __copy_to_user(&name->sysname, &utsname()->sysname,
+                              __OLD_UTS_LEN);
+       error -= __put_user(0, name->sysname + __OLD_UTS_LEN);
+       error -= __copy_to_user(&name->nodename, &utsname()->nodename,
+                               __OLD_UTS_LEN);
+       error -= __put_user(0, name->nodename + __OLD_UTS_LEN);
+       error -= __copy_to_user(&name->release, &utsname()->release,
+                               __OLD_UTS_LEN);
+       error -= __put_user(0, name->release + __OLD_UTS_LEN);
+       error -= __copy_to_user(&name->version, &utsname()->version,
+                               __OLD_UTS_LEN);
+       error -= __put_user(0, name->version + __OLD_UTS_LEN);
+       error -= __copy_to_user(&name->machine, &utsname()->machine,
+                               __OLD_UTS_LEN);
+       error = __put_user(0, name->machine + __OLD_UTS_LEN);
        error = error ? -EFAULT : 0;
 
        return error;
 }
 
-asmlinkage int _sys_sysmips(int cmd, long arg1, int arg2, int arg3)
+asmlinkage int sys_set_thread_area(unsigned long addr)
 {
-       int     tmp, len;
-       char    *name;
-
-       switch(cmd) {
-       case SETNAME: {
-               char nodename[__NEW_UTS_LEN + 1];
+       struct thread_info *ti = task_thread_info(current);
 
-               if (!capable(CAP_SYS_ADMIN))
-                       return -EPERM;
+       ti->tp_value = addr;
+       if (cpu_has_userlocal)
+               write_c0_userlocal(addr);
 
-               name = (char *) arg1;
-
-               len = strncpy_from_user(nodename, name, __NEW_UTS_LEN);
-               if (len < 0)
-                       return -EFAULT;
-
-               down_write(&uts_sem);
-               strncpy(system_utsname.nodename, nodename, len);
-               nodename[__NEW_UTS_LEN] = '\0';
-               strlcpy(system_utsname.nodename, nodename,
-                       sizeof(system_utsname.nodename));
-               up_write(&uts_sem);
-               return 0;
-       }
+       return 0;
+}
 
+asmlinkage int _sys_sysmips(int cmd, long arg1, int arg2, int arg3)
+{
+       switch (cmd) {
        case MIPS_ATOMIC_SET:
                printk(KERN_CRIT "How did I get here?\n");
                return -EINVAL;
 
        case MIPS_FIXADE:
-               tmp = current->thread.mflags & ~3;
-               current->thread.mflags = tmp | (arg1 & 3);
+               if (arg1 & ~3)
+                       return -EINVAL;
+
+               if (arg1 & 1)
+                       set_thread_flag(TIF_FIXADE);
+               else
+                       clear_thread_flag(TIF_FIXADE);
+               if (arg1 & 2)
+                       set_thread_flag(TIF_LOGADE);
+               else
+                       clear_thread_flag(TIF_FIXADE);
+
                return 0;
 
        case FLUSH_CACHE:
                __flush_cache_all();
                return 0;
-
-       case MIPS_RDNVRAM:
-               return -EIO;
        }
 
        return -EINVAL;
@@ -294,8 +326,8 @@ asmlinkage int _sys_sysmips(int cmd, long arg1, int arg2, int arg3)
  *
  * This is really horribly ugly.
  */
-asmlinkage int sys_ipc (uint call, int first, int second,
-                       unsigned long third, void *ptr, long fifth)
+asmlinkage int sys_ipc(unsigned int call, int first, int second,
+                      unsigned long third, void __user *ptr, long fifth)
 {
        int version, ret;
 
@@ -304,25 +336,26 @@ asmlinkage int sys_ipc (uint call, int first, int second,
 
        switch (call) {
        case SEMOP:
-               return sys_semtimedop (first, (struct sembuf *)ptr, second,
-                                      NULL);
+               return sys_semtimedop(first, (struct sembuf __user *)ptr,
+                                     second, NULL);
        case SEMTIMEDOP:
-               return sys_semtimedop (first, (struct sembuf *)ptr, second,
-                                      (const struct timespec __user *)fifth);
+               return sys_semtimedop(first, (struct sembuf __user *)ptr,
+                                     second,
+                                     (const struct timespec __user *)fifth);
        case SEMGET:
-               return sys_semget (first, second, third);
+               return sys_semget(first, second, third);
        case SEMCTL: {
                union semun fourth;
                if (!ptr)
                        return -EINVAL;
-               if (get_user(fourth.__pad, (void **) ptr))
+               if (get_user(fourth.__pad, (void __user *__user *) ptr))
                        return -EFAULT;
-               return sys_semctl (first, second, third, fourth);
+               return sys_semctl(first, second, third, fourth);
        }
 
        case MSGSND:
-               return sys_msgsnd (first, (struct msgbuf *) ptr,
-                                  second, third);
+               return sys_msgsnd(first, (struct msgbuf __user *) ptr,
+                                 second, third);
        case MSGRCV:
                switch (version) {
                case 0: {
@@ -331,43 +364,46 @@ asmlinkage int sys_ipc (uint call, int first, int second,
                                return -EINVAL;
 
                        if (copy_from_user(&tmp,
-                                          (struct ipc_kludge *) ptr,
-                                          sizeof (tmp)))
+                                          (struct ipc_kludge __user *) ptr,
+                                          sizeof(tmp)))
                                return -EFAULT;
-                       return sys_msgrcv (first, tmp.msgp, second,
-                                          tmp.msgtyp, third);
+                       return sys_msgrcv(first, tmp.msgp, second,
+                                         tmp.msgtyp, third);
                }
                default:
-                       return sys_msgrcv (first,
-                                          (struct msgbuf *) ptr,
-                                          second, fifth, third);
+                       return sys_msgrcv(first,
+                                         (struct msgbuf __user *) ptr,
+                                         second, fifth, third);
                }
        case MSGGET:
-               return sys_msgget ((key_t) first, second);
+               return sys_msgget((key_t) first, second);
        case MSGCTL:
-               return sys_msgctl (first, second, (struct msqid_ds *) ptr);
+               return sys_msgctl(first, second,
+                                 (struct msqid_ds __user *) ptr);
 
        case SHMAT:
                switch (version) {
                default: {
-                       ulong raddr;
-                       ret = do_shmat (first, (char *) ptr, second, &raddr);
+                       unsigned long raddr;
+                       ret = do_shmat(first, (char __user *) ptr, second,
+                                      &raddr);
                        if (ret)
                                return ret;
-                       return put_user (raddr, (ulong *) third);
+                       return put_user(raddr, (unsigned long __user *) third);
                }
                case 1: /* iBCS2 emulator entry point */
                        if (!segment_eq(get_fs(), get_ds()))
                                return -EINVAL;
-                       return do_shmat (first, (char *) ptr, second, (ulong *) third);
+                       return do_shmat(first, (char __user *) ptr, second,
+                                       (unsigned long *) third);
                }
        case SHMDT:
-               return sys_shmdt ((char *)ptr);
+               return sys_shmdt((char __user *)ptr);
        case SHMGET:
-               return sys_shmget (first, second, third);
+               return sys_shmget(first, second, third);
        case SHMCTL:
-               return sys_shmctl (first, second,
-                                  (struct shmid_ds *) ptr);
+               return sys_shmctl(first, second,
+                                 (struct shmid_ds __user *) ptr);
        default:
                return -ENOSYS;
        }
@@ -389,3 +425,32 @@ asmlinkage void bad_stack(void)
 {
        do_exit(SIGSEGV);
 }
+
+/*
+ * Do a system call from kernel instead of calling sys_execve so we
+ * end up with proper pt_regs.
+ */
+int kernel_execve(const char *filename, char *const argv[], char *const envp[])
+{
+       register unsigned long __a0 asm("$4") = (unsigned long) filename;
+       register unsigned long __a1 asm("$5") = (unsigned long) argv;
+       register unsigned long __a2 asm("$6") = (unsigned long) envp;
+       register unsigned long __a3 asm("$7");
+       unsigned long __v0;
+
+       __asm__ volatile ("                                     \n"
+       "       .set    noreorder                               \n"
+       "       li      $2, %5          # __NR_execve           \n"
+       "       syscall                                         \n"
+       "       move    %0, $2                                  \n"
+       "       .set    reorder                                 \n"
+       : "=&r" (__v0), "=r" (__a3)
+       : "r" (__a0), "r" (__a1), "r" (__a2), "i" (__NR_execve)
+       : "$2", "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", "$24",
+         "memory");
+
+       if (__a3 == 0)
+               return __v0;
+
+       return -__v0;
+}