11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * linux/arch/arm/kernel/sys_arm.c 31da177e4SLinus Torvalds * 41da177e4SLinus Torvalds * Copyright (C) People who wrote linux/arch/i386/kernel/sys_i386.c 51da177e4SLinus Torvalds * Copyright (C) 1995, 1996 Russell King. 61da177e4SLinus Torvalds * 71da177e4SLinus Torvalds * This program is free software; you can redistribute it and/or modify 81da177e4SLinus Torvalds * it under the terms of the GNU General Public License version 2 as 91da177e4SLinus Torvalds * published by the Free Software Foundation. 101da177e4SLinus Torvalds * 111da177e4SLinus Torvalds * This file contains various random system calls that 121da177e4SLinus Torvalds * have a non-standard calling sequence on the Linux/arm 131da177e4SLinus Torvalds * platform. 141da177e4SLinus Torvalds */ 151da177e4SLinus Torvalds #include <linux/module.h> 161da177e4SLinus Torvalds #include <linux/errno.h> 171da177e4SLinus Torvalds #include <linux/sched.h> 181da177e4SLinus Torvalds #include <linux/slab.h> 191da177e4SLinus Torvalds #include <linux/mm.h> 201da177e4SLinus Torvalds #include <linux/sem.h> 211da177e4SLinus Torvalds #include <linux/msg.h> 221da177e4SLinus Torvalds #include <linux/shm.h> 231da177e4SLinus Torvalds #include <linux/stat.h> 241da177e4SLinus Torvalds #include <linux/syscalls.h> 251da177e4SLinus Torvalds #include <linux/mman.h> 261da177e4SLinus Torvalds #include <linux/fs.h> 271da177e4SLinus Torvalds #include <linux/file.h> 281da177e4SLinus Torvalds #include <linux/utsname.h> 29*cba4fbbfSAdrian Bunk #include <linux/ipc.h> 301da177e4SLinus Torvalds 311da177e4SLinus Torvalds #include <asm/uaccess.h> 321da177e4SLinus Torvalds 331da177e4SLinus Torvalds extern unsigned long do_mremap(unsigned long addr, unsigned long old_len, 341da177e4SLinus Torvalds unsigned long new_len, unsigned long flags, 351da177e4SLinus Torvalds unsigned long new_addr); 361da177e4SLinus Torvalds 371da177e4SLinus Torvalds /* 381da177e4SLinus Torvalds * sys_pipe() is the normal C calling standard for creating 391da177e4SLinus Torvalds * a pipe. It's not the way unix traditionally does this, though. 401da177e4SLinus Torvalds */ 411da177e4SLinus Torvalds asmlinkage int sys_pipe(unsigned long __user *fildes) 421da177e4SLinus Torvalds { 431da177e4SLinus Torvalds int fd[2]; 441da177e4SLinus Torvalds int error; 451da177e4SLinus Torvalds 461da177e4SLinus Torvalds error = do_pipe(fd); 471da177e4SLinus Torvalds if (!error) { 481da177e4SLinus Torvalds if (copy_to_user(fildes, fd, 2*sizeof(int))) 491da177e4SLinus Torvalds error = -EFAULT; 501da177e4SLinus Torvalds } 511da177e4SLinus Torvalds return error; 521da177e4SLinus Torvalds } 531da177e4SLinus Torvalds 541da177e4SLinus Torvalds /* common code for old and new mmaps */ 551da177e4SLinus Torvalds inline long do_mmap2( 561da177e4SLinus Torvalds unsigned long addr, unsigned long len, 571da177e4SLinus Torvalds unsigned long prot, unsigned long flags, 581da177e4SLinus Torvalds unsigned long fd, unsigned long pgoff) 591da177e4SLinus Torvalds { 601da177e4SLinus Torvalds int error = -EINVAL; 611da177e4SLinus Torvalds struct file * file = NULL; 621da177e4SLinus Torvalds 631da177e4SLinus Torvalds flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); 641da177e4SLinus Torvalds 656119be0bSHugh Dickins if (flags & MAP_FIXED && addr < FIRST_USER_ADDRESS) 661da177e4SLinus Torvalds goto out; 671da177e4SLinus Torvalds 681da177e4SLinus Torvalds error = -EBADF; 691da177e4SLinus Torvalds if (!(flags & MAP_ANONYMOUS)) { 701da177e4SLinus Torvalds file = fget(fd); 711da177e4SLinus Torvalds if (!file) 721da177e4SLinus Torvalds goto out; 731da177e4SLinus Torvalds } 741da177e4SLinus Torvalds 751da177e4SLinus Torvalds down_write(¤t->mm->mmap_sem); 761da177e4SLinus Torvalds error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff); 771da177e4SLinus Torvalds up_write(¤t->mm->mmap_sem); 781da177e4SLinus Torvalds 791da177e4SLinus Torvalds if (file) 801da177e4SLinus Torvalds fput(file); 811da177e4SLinus Torvalds out: 821da177e4SLinus Torvalds return error; 831da177e4SLinus Torvalds } 841da177e4SLinus Torvalds 851da177e4SLinus Torvalds struct mmap_arg_struct { 861da177e4SLinus Torvalds unsigned long addr; 871da177e4SLinus Torvalds unsigned long len; 881da177e4SLinus Torvalds unsigned long prot; 891da177e4SLinus Torvalds unsigned long flags; 901da177e4SLinus Torvalds unsigned long fd; 911da177e4SLinus Torvalds unsigned long offset; 921da177e4SLinus Torvalds }; 931da177e4SLinus Torvalds 941da177e4SLinus Torvalds asmlinkage int old_mmap(struct mmap_arg_struct __user *arg) 951da177e4SLinus Torvalds { 961da177e4SLinus Torvalds int error = -EFAULT; 971da177e4SLinus Torvalds struct mmap_arg_struct a; 981da177e4SLinus Torvalds 991da177e4SLinus Torvalds if (copy_from_user(&a, arg, sizeof(a))) 1001da177e4SLinus Torvalds goto out; 1011da177e4SLinus Torvalds 1021da177e4SLinus Torvalds error = -EINVAL; 1031da177e4SLinus Torvalds if (a.offset & ~PAGE_MASK) 1041da177e4SLinus Torvalds goto out; 1051da177e4SLinus Torvalds 1061da177e4SLinus Torvalds error = do_mmap2(a.addr, a.len, a.prot, a.flags, a.fd, a.offset >> PAGE_SHIFT); 1071da177e4SLinus Torvalds out: 1081da177e4SLinus Torvalds return error; 1091da177e4SLinus Torvalds } 1101da177e4SLinus Torvalds 1111da177e4SLinus Torvalds asmlinkage unsigned long 1121da177e4SLinus Torvalds sys_arm_mremap(unsigned long addr, unsigned long old_len, 1131da177e4SLinus Torvalds unsigned long new_len, unsigned long flags, 1141da177e4SLinus Torvalds unsigned long new_addr) 1151da177e4SLinus Torvalds { 1161da177e4SLinus Torvalds unsigned long ret = -EINVAL; 1171da177e4SLinus Torvalds 1186119be0bSHugh Dickins if (flags & MREMAP_FIXED && new_addr < FIRST_USER_ADDRESS) 1191da177e4SLinus Torvalds goto out; 1201da177e4SLinus Torvalds 1211da177e4SLinus Torvalds down_write(¤t->mm->mmap_sem); 1221da177e4SLinus Torvalds ret = do_mremap(addr, old_len, new_len, flags, new_addr); 1231da177e4SLinus Torvalds up_write(¤t->mm->mmap_sem); 1241da177e4SLinus Torvalds 1251da177e4SLinus Torvalds out: 1261da177e4SLinus Torvalds return ret; 1271da177e4SLinus Torvalds } 1281da177e4SLinus Torvalds 1291da177e4SLinus Torvalds /* 1301da177e4SLinus Torvalds * Perform the select(nd, in, out, ex, tv) and mmap() system 1311da177e4SLinus Torvalds * calls. 1321da177e4SLinus Torvalds */ 1331da177e4SLinus Torvalds 1341da177e4SLinus Torvalds struct sel_arg_struct { 1351da177e4SLinus Torvalds unsigned long n; 1361da177e4SLinus Torvalds fd_set __user *inp, *outp, *exp; 1371da177e4SLinus Torvalds struct timeval __user *tvp; 1381da177e4SLinus Torvalds }; 1391da177e4SLinus Torvalds 1401da177e4SLinus Torvalds asmlinkage int old_select(struct sel_arg_struct __user *arg) 1411da177e4SLinus Torvalds { 1421da177e4SLinus Torvalds struct sel_arg_struct a; 1431da177e4SLinus Torvalds 1441da177e4SLinus Torvalds if (copy_from_user(&a, arg, sizeof(a))) 1451da177e4SLinus Torvalds return -EFAULT; 1461da177e4SLinus Torvalds /* sys_select() does the appropriate kernel locking */ 1471da177e4SLinus Torvalds return sys_select(a.n, a.inp, a.outp, a.exp, a.tvp); 1481da177e4SLinus Torvalds } 1491da177e4SLinus Torvalds 150dd35afc2SNicolas Pitre #if !defined(CONFIG_AEABI) || defined(CONFIG_OABI_COMPAT) 1511da177e4SLinus Torvalds /* 1521da177e4SLinus Torvalds * sys_ipc() is the de-multiplexer for the SysV IPC calls.. 1531da177e4SLinus Torvalds * 1541da177e4SLinus Torvalds * This is really horribly ugly. 1551da177e4SLinus Torvalds */ 1561da177e4SLinus Torvalds asmlinkage int sys_ipc(uint call, int first, int second, int third, 1571da177e4SLinus Torvalds void __user *ptr, long fifth) 1581da177e4SLinus Torvalds { 1591da177e4SLinus Torvalds int version, ret; 1601da177e4SLinus Torvalds 1611da177e4SLinus Torvalds version = call >> 16; /* hack for backward compatibility */ 1621da177e4SLinus Torvalds call &= 0xffff; 1631da177e4SLinus Torvalds 1641da177e4SLinus Torvalds switch (call) { 1651da177e4SLinus Torvalds case SEMOP: 1661da177e4SLinus Torvalds return sys_semtimedop (first, (struct sembuf __user *)ptr, second, NULL); 1671da177e4SLinus Torvalds case SEMTIMEDOP: 1681da177e4SLinus Torvalds return sys_semtimedop(first, (struct sembuf __user *)ptr, second, 1691da177e4SLinus Torvalds (const struct timespec __user *)fifth); 1701da177e4SLinus Torvalds 1711da177e4SLinus Torvalds case SEMGET: 1721da177e4SLinus Torvalds return sys_semget (first, second, third); 1731da177e4SLinus Torvalds case SEMCTL: { 1741da177e4SLinus Torvalds union semun fourth; 1751da177e4SLinus Torvalds if (!ptr) 1761da177e4SLinus Torvalds return -EINVAL; 1771da177e4SLinus Torvalds if (get_user(fourth.__pad, (void __user * __user *) ptr)) 1781da177e4SLinus Torvalds return -EFAULT; 1791da177e4SLinus Torvalds return sys_semctl (first, second, third, fourth); 1801da177e4SLinus Torvalds } 1811da177e4SLinus Torvalds 1821da177e4SLinus Torvalds case MSGSND: 1831da177e4SLinus Torvalds return sys_msgsnd(first, (struct msgbuf __user *) ptr, 1841da177e4SLinus Torvalds second, third); 1851da177e4SLinus Torvalds case MSGRCV: 1861da177e4SLinus Torvalds switch (version) { 1871da177e4SLinus Torvalds case 0: { 1881da177e4SLinus Torvalds struct ipc_kludge tmp; 1891da177e4SLinus Torvalds if (!ptr) 1901da177e4SLinus Torvalds return -EINVAL; 1911da177e4SLinus Torvalds if (copy_from_user(&tmp,(struct ipc_kludge __user *)ptr, 1921da177e4SLinus Torvalds sizeof (tmp))) 1931da177e4SLinus Torvalds return -EFAULT; 1941da177e4SLinus Torvalds return sys_msgrcv (first, tmp.msgp, second, 1951da177e4SLinus Torvalds tmp.msgtyp, third); 1961da177e4SLinus Torvalds } 1971da177e4SLinus Torvalds default: 1981da177e4SLinus Torvalds return sys_msgrcv (first, 1991da177e4SLinus Torvalds (struct msgbuf __user *) ptr, 2001da177e4SLinus Torvalds second, fifth, third); 2011da177e4SLinus Torvalds } 2021da177e4SLinus Torvalds case MSGGET: 2031da177e4SLinus Torvalds return sys_msgget ((key_t) first, second); 2041da177e4SLinus Torvalds case MSGCTL: 2051da177e4SLinus Torvalds return sys_msgctl(first, second, (struct msqid_ds __user *)ptr); 2061da177e4SLinus Torvalds 2071da177e4SLinus Torvalds case SHMAT: 2081da177e4SLinus Torvalds switch (version) { 2091da177e4SLinus Torvalds default: { 2101da177e4SLinus Torvalds ulong raddr; 2111da177e4SLinus Torvalds ret = do_shmat(first, (char __user *)ptr, second, &raddr); 2121da177e4SLinus Torvalds if (ret) 2131da177e4SLinus Torvalds return ret; 2141da177e4SLinus Torvalds return put_user(raddr, (ulong __user *)third); 2151da177e4SLinus Torvalds } 2161da177e4SLinus Torvalds case 1: /* Of course, we don't support iBCS2! */ 2171da177e4SLinus Torvalds return -EINVAL; 2181da177e4SLinus Torvalds } 2191da177e4SLinus Torvalds case SHMDT: 2201da177e4SLinus Torvalds return sys_shmdt ((char __user *)ptr); 2211da177e4SLinus Torvalds case SHMGET: 2221da177e4SLinus Torvalds return sys_shmget (first, second, third); 2231da177e4SLinus Torvalds case SHMCTL: 2241da177e4SLinus Torvalds return sys_shmctl (first, second, 2251da177e4SLinus Torvalds (struct shmid_ds __user *) ptr); 2261da177e4SLinus Torvalds default: 2271da177e4SLinus Torvalds return -ENOSYS; 2281da177e4SLinus Torvalds } 2291da177e4SLinus Torvalds } 230dd35afc2SNicolas Pitre #endif 2311da177e4SLinus Torvalds 2321da177e4SLinus Torvalds /* Fork a new task - this creates a new program thread. 2331da177e4SLinus Torvalds * This is called indirectly via a small wrapper 2341da177e4SLinus Torvalds */ 2351da177e4SLinus Torvalds asmlinkage int sys_fork(struct pt_regs *regs) 2361da177e4SLinus Torvalds { 237f24284adSHyok S. Choi #ifdef CONFIG_MMU 2381da177e4SLinus Torvalds return do_fork(SIGCHLD, regs->ARM_sp, regs, 0, NULL, NULL); 239f24284adSHyok S. Choi #else 240f24284adSHyok S. Choi /* can not support in nommu mode */ 241f24284adSHyok S. Choi return(-EINVAL); 242f24284adSHyok S. Choi #endif 2431da177e4SLinus Torvalds } 2441da177e4SLinus Torvalds 2451da177e4SLinus Torvalds /* Clone a task - this clones the calling program thread. 2461da177e4SLinus Torvalds * This is called indirectly via a small wrapper 2471da177e4SLinus Torvalds */ 2481da177e4SLinus Torvalds asmlinkage int sys_clone(unsigned long clone_flags, unsigned long newsp, 2491da177e4SLinus Torvalds int __user *parent_tidptr, int tls_val, 2501da177e4SLinus Torvalds int __user *child_tidptr, struct pt_regs *regs) 2511da177e4SLinus Torvalds { 2521da177e4SLinus Torvalds if (!newsp) 2531da177e4SLinus Torvalds newsp = regs->ARM_sp; 2541da177e4SLinus Torvalds 2551da177e4SLinus Torvalds return do_fork(clone_flags, newsp, regs, 0, parent_tidptr, child_tidptr); 2561da177e4SLinus Torvalds } 2571da177e4SLinus Torvalds 2581da177e4SLinus Torvalds asmlinkage int sys_vfork(struct pt_regs *regs) 2591da177e4SLinus Torvalds { 2601da177e4SLinus Torvalds return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs->ARM_sp, regs, 0, NULL, NULL); 2611da177e4SLinus Torvalds } 2621da177e4SLinus Torvalds 2631da177e4SLinus Torvalds /* sys_execve() executes a new program. 2641da177e4SLinus Torvalds * This is called indirectly via a small wrapper 2651da177e4SLinus Torvalds */ 2661da177e4SLinus Torvalds asmlinkage int sys_execve(char __user *filenamei, char __user * __user *argv, 2671da177e4SLinus Torvalds char __user * __user *envp, struct pt_regs *regs) 2681da177e4SLinus Torvalds { 2691da177e4SLinus Torvalds int error; 2701da177e4SLinus Torvalds char * filename; 2711da177e4SLinus Torvalds 2721da177e4SLinus Torvalds filename = getname(filenamei); 2731da177e4SLinus Torvalds error = PTR_ERR(filename); 2741da177e4SLinus Torvalds if (IS_ERR(filename)) 2751da177e4SLinus Torvalds goto out; 2761da177e4SLinus Torvalds error = do_execve(filename, argv, envp, regs); 2771da177e4SLinus Torvalds putname(filename); 2781da177e4SLinus Torvalds out: 2791da177e4SLinus Torvalds return error; 2801da177e4SLinus Torvalds } 2811da177e4SLinus Torvalds 2823db03b4aSArnd Bergmann int kernel_execve(const char *filename, char *const argv[], char *const envp[]) 2831da177e4SLinus Torvalds { 2841da177e4SLinus Torvalds struct pt_regs regs; 2851da177e4SLinus Torvalds int ret; 2861da177e4SLinus Torvalds 2871da177e4SLinus Torvalds memset(®s, 0, sizeof(struct pt_regs)); 2881da177e4SLinus Torvalds ret = do_execve((char *)filename, (char __user * __user *)argv, 2891da177e4SLinus Torvalds (char __user * __user *)envp, ®s); 2901da177e4SLinus Torvalds if (ret < 0) 2911da177e4SLinus Torvalds goto out; 2921da177e4SLinus Torvalds 2931da177e4SLinus Torvalds /* 2941da177e4SLinus Torvalds * Save argc to the register structure for userspace. 2951da177e4SLinus Torvalds */ 2961da177e4SLinus Torvalds regs.ARM_r0 = ret; 2971da177e4SLinus Torvalds 2981da177e4SLinus Torvalds /* 2991da177e4SLinus Torvalds * We were successful. We won't be returning to our caller, but 3001da177e4SLinus Torvalds * instead to user space by manipulating the kernel stack. 3011da177e4SLinus Torvalds */ 3021da177e4SLinus Torvalds asm( "add r0, %0, %1\n\t" 3031da177e4SLinus Torvalds "mov r1, %2\n\t" 3041da177e4SLinus Torvalds "mov r2, %3\n\t" 3051da177e4SLinus Torvalds "bl memmove\n\t" /* copy regs to top of stack */ 3061da177e4SLinus Torvalds "mov r8, #0\n\t" /* not a syscall */ 3071da177e4SLinus Torvalds "mov r9, %0\n\t" /* thread structure */ 3081da177e4SLinus Torvalds "mov sp, r0\n\t" /* reposition stack pointer */ 3091da177e4SLinus Torvalds "b ret_to_user" 3101da177e4SLinus Torvalds : 3111da177e4SLinus Torvalds : "r" (current_thread_info()), 3124f7a1812SRussell King "Ir" (THREAD_START_SP - sizeof(regs)), 3131da177e4SLinus Torvalds "r" (®s), 3141da177e4SLinus Torvalds "Ir" (sizeof(regs)) 315c2f48086SNicolas Pitre : "r0", "r1", "r2", "r3", "ip", "lr", "memory"); 3161da177e4SLinus Torvalds 3171da177e4SLinus Torvalds out: 3181da177e4SLinus Torvalds return ret; 3191da177e4SLinus Torvalds } 3203db03b4aSArnd Bergmann EXPORT_SYMBOL(kernel_execve); 32168d9102fSNicolas Pitre 32268d9102fSNicolas Pitre /* 3236cbdc8c5SSimon Arlott * Since loff_t is a 64 bit type we avoid a lot of ABI hassle 32468d9102fSNicolas Pitre * with a different argument ordering. 32568d9102fSNicolas Pitre */ 32668d9102fSNicolas Pitre asmlinkage long sys_arm_fadvise64_64(int fd, int advice, 32768d9102fSNicolas Pitre loff_t offset, loff_t len) 32868d9102fSNicolas Pitre { 32968d9102fSNicolas Pitre return sys_fadvise64_64(fd, offset, len, advice); 33068d9102fSNicolas Pitre } 331