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