linux/arch/arm/kernel/sys_arm.c
<<
>>
Prefs
   1/*
   2 *  linux/arch/arm/kernel/sys_arm.c
   3 *
   4 *  Copyright (C) People who wrote linux/arch/i386/kernel/sys_i386.c
   5 *  Copyright (C) 1995, 1996 Russell King.
   6 *
   7 * This program is free software; you can redistribute it and/or modify
   8 * it under the terms of the GNU General Public License version 2 as
   9 * published by the Free Software Foundation.
  10 *
  11 *  This file contains various random system calls that
  12 *  have a non-standard calling sequence on the Linux/arm
  13 *  platform.
  14 */
  15#include <linux/module.h>
  16#include <linux/errno.h>
  17#include <linux/sched.h>
  18#include <linux/slab.h>
  19#include <linux/mm.h>
  20#include <linux/sem.h>
  21#include <linux/msg.h>
  22#include <linux/shm.h>
  23#include <linux/stat.h>
  24#include <linux/syscalls.h>
  25#include <linux/mman.h>
  26#include <linux/fs.h>
  27#include <linux/file.h>
  28#include <linux/ipc.h>
  29#include <linux/uaccess.h>
  30
  31extern unsigned long do_mremap(unsigned long addr, unsigned long old_len,
  32                               unsigned long new_len, unsigned long flags,
  33                               unsigned long new_addr);
  34
  35/* common code for old and new mmaps */
  36inline long do_mmap2(
  37        unsigned long addr, unsigned long len,
  38        unsigned long prot, unsigned long flags,
  39        unsigned long fd, unsigned long pgoff)
  40{
  41        int error = -EINVAL;
  42        struct file * file = NULL;
  43
  44        flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
  45
  46        if (flags & MAP_FIXED && addr < FIRST_USER_ADDRESS)
  47                goto out;
  48
  49        error = -EBADF;
  50        if (!(flags & MAP_ANONYMOUS)) {
  51                file = fget(fd);
  52                if (!file)
  53                        goto out;
  54        }
  55
  56        down_write(&current->mm->mmap_sem);
  57        error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
  58        up_write(&current->mm->mmap_sem);
  59
  60        if (file)
  61                fput(file);
  62out:
  63        return error;
  64}
  65
  66struct mmap_arg_struct {
  67        unsigned long addr;
  68        unsigned long len;
  69        unsigned long prot;
  70        unsigned long flags;
  71        unsigned long fd;
  72        unsigned long offset;
  73};
  74
  75asmlinkage int old_mmap(struct mmap_arg_struct __user *arg)
  76{
  77        int error = -EFAULT;
  78        struct mmap_arg_struct a;
  79
  80        if (copy_from_user(&a, arg, sizeof(a)))
  81                goto out;
  82
  83        error = -EINVAL;
  84        if (a.offset & ~PAGE_MASK)
  85                goto out;
  86
  87        error = do_mmap2(a.addr, a.len, a.prot, a.flags, a.fd, a.offset >> PAGE_SHIFT);
  88out:
  89        return error;
  90}
  91
  92asmlinkage unsigned long
  93sys_arm_mremap(unsigned long addr, unsigned long old_len,
  94               unsigned long new_len, unsigned long flags,
  95               unsigned long new_addr)
  96{
  97        unsigned long ret = -EINVAL;
  98
  99        if (flags & MREMAP_FIXED && new_addr < FIRST_USER_ADDRESS)
 100                goto out;
 101
 102        down_write(&current->mm->mmap_sem);
 103        ret = do_mremap(addr, old_len, new_len, flags, new_addr);
 104        up_write(&current->mm->mmap_sem);
 105
 106out:
 107        return ret;
 108}
 109
 110/*
 111 * Perform the select(nd, in, out, ex, tv) and mmap() system
 112 * calls.
 113 */
 114
 115struct sel_arg_struct {
 116        unsigned long n;
 117        fd_set __user *inp, *outp, *exp;
 118        struct timeval __user *tvp;
 119};
 120
 121asmlinkage int old_select(struct sel_arg_struct __user *arg)
 122{
 123        struct sel_arg_struct a;
 124
 125        if (copy_from_user(&a, arg, sizeof(a)))
 126                return -EFAULT;
 127        /* sys_select() does the appropriate kernel locking */
 128        return sys_select(a.n, a.inp, a.outp, a.exp, a.tvp);
 129}
 130
 131#if !defined(CONFIG_AEABI) || defined(CONFIG_OABI_COMPAT)
 132/*
 133 * sys_ipc() is the de-multiplexer for the SysV IPC calls..
 134 *
 135 * This is really horribly ugly.
 136 */
 137asmlinkage int sys_ipc(uint call, int first, int second, int third,
 138                       void __user *ptr, long fifth)
 139{
 140        int version, ret;
 141
 142        version = call >> 16; /* hack for backward compatibility */
 143        call &= 0xffff;
 144
 145        switch (call) {
 146        case SEMOP:
 147                return sys_semtimedop (first, (struct sembuf __user *)ptr, second, NULL);
 148        case SEMTIMEDOP:
 149                return sys_semtimedop(first, (struct sembuf __user *)ptr, second,
 150                                        (const struct timespec __user *)fifth);
 151
 152        case SEMGET:
 153                return sys_semget (first, second, third);
 154        case SEMCTL: {
 155                union semun fourth;
 156                if (!ptr)
 157                        return -EINVAL;
 158                if (get_user(fourth.__pad, (void __user * __user *) ptr))
 159                        return -EFAULT;
 160                return sys_semctl (first, second, third, fourth);
 161        }
 162
 163        case MSGSND:
 164                return sys_msgsnd(first, (struct msgbuf __user *) ptr, 
 165                                  second, third);
 166        case MSGRCV:
 167                switch (version) {
 168                case 0: {
 169                        struct ipc_kludge tmp;
 170                        if (!ptr)
 171                                return -EINVAL;
 172                        if (copy_from_user(&tmp,(struct ipc_kludge __user *)ptr,
 173                                           sizeof (tmp)))
 174                                return -EFAULT;
 175                        return sys_msgrcv (first, tmp.msgp, second,
 176                                           tmp.msgtyp, third);
 177                }
 178                default:
 179                        return sys_msgrcv (first,
 180                                           (struct msgbuf __user *) ptr,
 181                                           second, fifth, third);
 182                }
 183        case MSGGET:
 184                return sys_msgget ((key_t) first, second);
 185        case MSGCTL:
 186                return sys_msgctl(first, second, (struct msqid_ds __user *)ptr);
 187
 188        case SHMAT:
 189                switch (version) {
 190                default: {
 191                        ulong raddr;
 192                        ret = do_shmat(first, (char __user *)ptr, second, &raddr);
 193                        if (ret)
 194                                return ret;
 195                        return put_user(raddr, (ulong __user *)third);
 196                }
 197                case 1: /* Of course, we don't support iBCS2! */
 198                        return -EINVAL;
 199                }
 200        case SHMDT: 
 201                return sys_shmdt ((char __user *)ptr);
 202        case SHMGET:
 203                return sys_shmget (first, second, third);
 204        case SHMCTL:
 205                return sys_shmctl (first, second,
 206                                   (struct shmid_ds __user *) ptr);
 207        default:
 208                return -ENOSYS;
 209        }
 210}
 211#endif
 212
 213/* Fork a new task - this creates a new program thread.
 214 * This is called indirectly via a small wrapper
 215 */
 216asmlinkage int sys_fork(struct pt_regs *regs)
 217{
 218#ifdef CONFIG_MMU
 219        return do_fork(SIGCHLD, regs->ARM_sp, regs, 0, NULL, NULL);
 220#else
 221        /* can not support in nommu mode */
 222        return(-EINVAL);
 223#endif
 224}
 225
 226/* Clone a task - this clones the calling program thread.
 227 * This is called indirectly via a small wrapper
 228 */
 229asmlinkage int sys_clone(unsigned long clone_flags, unsigned long newsp,
 230                         int __user *parent_tidptr, int tls_val,
 231                         int __user *child_tidptr, struct pt_regs *regs)
 232{
 233        if (!newsp)
 234                newsp = regs->ARM_sp;
 235
 236        return do_fork(clone_flags, newsp, regs, 0, parent_tidptr, child_tidptr);
 237}
 238
 239asmlinkage int sys_vfork(struct pt_regs *regs)
 240{
 241        return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs->ARM_sp, regs, 0, NULL, NULL);
 242}
 243
 244/* sys_execve() executes a new program.
 245 * This is called indirectly via a small wrapper
 246 */
 247asmlinkage int sys_execve(char __user *filenamei, char __user * __user *argv,
 248                          char __user * __user *envp, struct pt_regs *regs)
 249{
 250        int error;
 251        char * filename;
 252
 253        filename = getname(filenamei);
 254        error = PTR_ERR(filename);
 255        if (IS_ERR(filename))
 256                goto out;
 257        error = do_execve(filename, argv, envp, regs);
 258        putname(filename);
 259out:
 260        return error;
 261}
 262
 263int kernel_execve(const char *filename, char *const argv[], char *const envp[])
 264{
 265        struct pt_regs regs;
 266        int ret;
 267
 268        memset(&regs, 0, sizeof(struct pt_regs));
 269        ret = do_execve((char *)filename, (char __user * __user *)argv,
 270                        (char __user * __user *)envp, &regs);
 271        if (ret < 0)
 272                goto out;
 273
 274        /*
 275         * Save argc to the register structure for userspace.
 276         */
 277        regs.ARM_r0 = ret;
 278
 279        /*
 280         * We were successful.  We won't be returning to our caller, but
 281         * instead to user space by manipulating the kernel stack.
 282         */
 283        asm(    "add    r0, %0, %1\n\t"
 284                "mov    r1, %2\n\t"
 285                "mov    r2, %3\n\t"
 286                "bl     memmove\n\t"    /* copy regs to top of stack */
 287                "mov    r8, #0\n\t"     /* not a syscall */
 288                "mov    r9, %0\n\t"     /* thread structure */
 289                "mov    sp, r0\n\t"     /* reposition stack pointer */
 290                "b      ret_to_user"
 291                :
 292                : "r" (current_thread_info()),
 293                  "Ir" (THREAD_START_SP - sizeof(regs)),
 294                  "r" (&regs),
 295                  "Ir" (sizeof(regs))
 296                : "r0", "r1", "r2", "r3", "ip", "lr", "memory");
 297
 298 out:
 299        return ret;
 300}
 301EXPORT_SYMBOL(kernel_execve);
 302
 303/*
 304 * Since loff_t is a 64 bit type we avoid a lot of ABI hassle
 305 * with a different argument ordering.
 306 */
 307asmlinkage long sys_arm_fadvise64_64(int fd, int advice,
 308                                     loff_t offset, loff_t len)
 309{
 310        return sys_fadvise64_64(fd, offset, len, advice);
 311}
 312