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> 29cba4fbbfSAdrian Bunk #include <linux/ipc.h> 30*33fa9b13SRussell King #include <linux/uaccess.h> 311da177e4SLinus Torvalds 321da177e4SLinus Torvalds extern unsigned long do_mremap(unsigned long addr, unsigned long old_len, 331da177e4SLinus Torvalds unsigned long new_len, unsigned long flags, 341da177e4SLinus Torvalds unsigned long new_addr); 351da177e4SLinus Torvalds 361da177e4SLinus Torvalds /* common code for old and new mmaps */ 371da177e4SLinus Torvalds inline long do_mmap2( 381da177e4SLinus Torvalds unsigned long addr, unsigned long len, 391da177e4SLinus Torvalds unsigned long prot, unsigned long flags, 401da177e4SLinus Torvalds unsigned long fd, unsigned long pgoff) 411da177e4SLinus Torvalds { 421da177e4SLinus Torvalds int error = -EINVAL; 431da177e4SLinus Torvalds struct file * file = NULL; 441da177e4SLinus Torvalds 451da177e4SLinus Torvalds flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); 461da177e4SLinus Torvalds 476119be0bSHugh Dickins if (flags & MAP_FIXED && addr < FIRST_USER_ADDRESS) 481da177e4SLinus Torvalds goto out; 491da177e4SLinus Torvalds 501da177e4SLinus Torvalds error = -EBADF; 511da177e4SLinus Torvalds if (!(flags & MAP_ANONYMOUS)) { 521da177e4SLinus Torvalds file = fget(fd); 531da177e4SLinus Torvalds if (!file) 541da177e4SLinus Torvalds goto out; 551da177e4SLinus Torvalds } 561da177e4SLinus Torvalds 571da177e4SLinus Torvalds down_write(¤t->mm->mmap_sem); 581da177e4SLinus Torvalds error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff); 591da177e4SLinus Torvalds up_write(¤t->mm->mmap_sem); 601da177e4SLinus Torvalds 611da177e4SLinus Torvalds if (file) 621da177e4SLinus Torvalds fput(file); 631da177e4SLinus Torvalds out: 641da177e4SLinus Torvalds return error; 651da177e4SLinus Torvalds } 661da177e4SLinus Torvalds 671da177e4SLinus Torvalds struct mmap_arg_struct { 681da177e4SLinus Torvalds unsigned long addr; 691da177e4SLinus Torvalds unsigned long len; 701da177e4SLinus Torvalds unsigned long prot; 711da177e4SLinus Torvalds unsigned long flags; 721da177e4SLinus Torvalds unsigned long fd; 731da177e4SLinus Torvalds unsigned long offset; 741da177e4SLinus Torvalds }; 751da177e4SLinus Torvalds 761da177e4SLinus Torvalds asmlinkage int old_mmap(struct mmap_arg_struct __user *arg) 771da177e4SLinus Torvalds { 781da177e4SLinus Torvalds int error = -EFAULT; 791da177e4SLinus Torvalds struct mmap_arg_struct a; 801da177e4SLinus Torvalds 811da177e4SLinus Torvalds if (copy_from_user(&a, arg, sizeof(a))) 821da177e4SLinus Torvalds goto out; 831da177e4SLinus Torvalds 841da177e4SLinus Torvalds error = -EINVAL; 851da177e4SLinus Torvalds if (a.offset & ~PAGE_MASK) 861da177e4SLinus Torvalds goto out; 871da177e4SLinus Torvalds 881da177e4SLinus Torvalds error = do_mmap2(a.addr, a.len, a.prot, a.flags, a.fd, a.offset >> PAGE_SHIFT); 891da177e4SLinus Torvalds out: 901da177e4SLinus Torvalds return error; 911da177e4SLinus Torvalds } 921da177e4SLinus Torvalds 931da177e4SLinus Torvalds asmlinkage unsigned long 941da177e4SLinus Torvalds sys_arm_mremap(unsigned long addr, unsigned long old_len, 951da177e4SLinus Torvalds unsigned long new_len, unsigned long flags, 961da177e4SLinus Torvalds unsigned long new_addr) 971da177e4SLinus Torvalds { 981da177e4SLinus Torvalds unsigned long ret = -EINVAL; 991da177e4SLinus Torvalds 1006119be0bSHugh Dickins if (flags & MREMAP_FIXED && new_addr < FIRST_USER_ADDRESS) 1011da177e4SLinus Torvalds goto out; 1021da177e4SLinus Torvalds 1031da177e4SLinus Torvalds down_write(¤t->mm->mmap_sem); 1041da177e4SLinus Torvalds ret = do_mremap(addr, old_len, new_len, flags, new_addr); 1051da177e4SLinus Torvalds up_write(¤t->mm->mmap_sem); 1061da177e4SLinus Torvalds 1071da177e4SLinus Torvalds out: 1081da177e4SLinus Torvalds return ret; 1091da177e4SLinus Torvalds } 1101da177e4SLinus Torvalds 1111da177e4SLinus Torvalds /* 1121da177e4SLinus Torvalds * Perform the select(nd, in, out, ex, tv) and mmap() system 1131da177e4SLinus Torvalds * calls. 1141da177e4SLinus Torvalds */ 1151da177e4SLinus Torvalds 1161da177e4SLinus Torvalds struct sel_arg_struct { 1171da177e4SLinus Torvalds unsigned long n; 1181da177e4SLinus Torvalds fd_set __user *inp, *outp, *exp; 1191da177e4SLinus Torvalds struct timeval __user *tvp; 1201da177e4SLinus Torvalds }; 1211da177e4SLinus Torvalds 1221da177e4SLinus Torvalds asmlinkage int old_select(struct sel_arg_struct __user *arg) 1231da177e4SLinus Torvalds { 1241da177e4SLinus Torvalds struct sel_arg_struct a; 1251da177e4SLinus Torvalds 1261da177e4SLinus Torvalds if (copy_from_user(&a, arg, sizeof(a))) 1271da177e4SLinus Torvalds return -EFAULT; 1281da177e4SLinus Torvalds /* sys_select() does the appropriate kernel locking */ 1291da177e4SLinus Torvalds return sys_select(a.n, a.inp, a.outp, a.exp, a.tvp); 1301da177e4SLinus Torvalds } 1311da177e4SLinus Torvalds 132dd35afc2SNicolas Pitre #if !defined(CONFIG_AEABI) || defined(CONFIG_OABI_COMPAT) 1331da177e4SLinus Torvalds /* 1341da177e4SLinus Torvalds * sys_ipc() is the de-multiplexer for the SysV IPC calls.. 1351da177e4SLinus Torvalds * 1361da177e4SLinus Torvalds * This is really horribly ugly. 1371da177e4SLinus Torvalds */ 1381da177e4SLinus Torvalds asmlinkage int sys_ipc(uint call, int first, int second, int third, 1391da177e4SLinus Torvalds void __user *ptr, long fifth) 1401da177e4SLinus Torvalds { 1411da177e4SLinus Torvalds int version, ret; 1421da177e4SLinus Torvalds 1431da177e4SLinus Torvalds version = call >> 16; /* hack for backward compatibility */ 1441da177e4SLinus Torvalds call &= 0xffff; 1451da177e4SLinus Torvalds 1461da177e4SLinus Torvalds switch (call) { 1471da177e4SLinus Torvalds case SEMOP: 1481da177e4SLinus Torvalds return sys_semtimedop (first, (struct sembuf __user *)ptr, second, NULL); 1491da177e4SLinus Torvalds case SEMTIMEDOP: 1501da177e4SLinus Torvalds return sys_semtimedop(first, (struct sembuf __user *)ptr, second, 1511da177e4SLinus Torvalds (const struct timespec __user *)fifth); 1521da177e4SLinus Torvalds 1531da177e4SLinus Torvalds case SEMGET: 1541da177e4SLinus Torvalds return sys_semget (first, second, third); 1551da177e4SLinus Torvalds case SEMCTL: { 1561da177e4SLinus Torvalds union semun fourth; 1571da177e4SLinus Torvalds if (!ptr) 1581da177e4SLinus Torvalds return -EINVAL; 1591da177e4SLinus Torvalds if (get_user(fourth.__pad, (void __user * __user *) ptr)) 1601da177e4SLinus Torvalds return -EFAULT; 1611da177e4SLinus Torvalds return sys_semctl (first, second, third, fourth); 1621da177e4SLinus Torvalds } 1631da177e4SLinus Torvalds 1641da177e4SLinus Torvalds case MSGSND: 1651da177e4SLinus Torvalds return sys_msgsnd(first, (struct msgbuf __user *) ptr, 1661da177e4SLinus Torvalds second, third); 1671da177e4SLinus Torvalds case MSGRCV: 1681da177e4SLinus Torvalds switch (version) { 1691da177e4SLinus Torvalds case 0: { 1701da177e4SLinus Torvalds struct ipc_kludge tmp; 1711da177e4SLinus Torvalds if (!ptr) 1721da177e4SLinus Torvalds return -EINVAL; 1731da177e4SLinus Torvalds if (copy_from_user(&tmp,(struct ipc_kludge __user *)ptr, 1741da177e4SLinus Torvalds sizeof (tmp))) 1751da177e4SLinus Torvalds return -EFAULT; 1761da177e4SLinus Torvalds return sys_msgrcv (first, tmp.msgp, second, 1771da177e4SLinus Torvalds tmp.msgtyp, third); 1781da177e4SLinus Torvalds } 1791da177e4SLinus Torvalds default: 1801da177e4SLinus Torvalds return sys_msgrcv (first, 1811da177e4SLinus Torvalds (struct msgbuf __user *) ptr, 1821da177e4SLinus Torvalds second, fifth, third); 1831da177e4SLinus Torvalds } 1841da177e4SLinus Torvalds case MSGGET: 1851da177e4SLinus Torvalds return sys_msgget ((key_t) first, second); 1861da177e4SLinus Torvalds case MSGCTL: 1871da177e4SLinus Torvalds return sys_msgctl(first, second, (struct msqid_ds __user *)ptr); 1881da177e4SLinus Torvalds 1891da177e4SLinus Torvalds case SHMAT: 1901da177e4SLinus Torvalds switch (version) { 1911da177e4SLinus Torvalds default: { 1921da177e4SLinus Torvalds ulong raddr; 1931da177e4SLinus Torvalds ret = do_shmat(first, (char __user *)ptr, second, &raddr); 1941da177e4SLinus Torvalds if (ret) 1951da177e4SLinus Torvalds return ret; 1961da177e4SLinus Torvalds return put_user(raddr, (ulong __user *)third); 1971da177e4SLinus Torvalds } 1981da177e4SLinus Torvalds case 1: /* Of course, we don't support iBCS2! */ 1991da177e4SLinus Torvalds return -EINVAL; 2001da177e4SLinus Torvalds } 2011da177e4SLinus Torvalds case SHMDT: 2021da177e4SLinus Torvalds return sys_shmdt ((char __user *)ptr); 2031da177e4SLinus Torvalds case SHMGET: 2041da177e4SLinus Torvalds return sys_shmget (first, second, third); 2051da177e4SLinus Torvalds case SHMCTL: 2061da177e4SLinus Torvalds return sys_shmctl (first, second, 2071da177e4SLinus Torvalds (struct shmid_ds __user *) ptr); 2081da177e4SLinus Torvalds default: 2091da177e4SLinus Torvalds return -ENOSYS; 2101da177e4SLinus Torvalds } 2111da177e4SLinus Torvalds } 212dd35afc2SNicolas Pitre #endif 2131da177e4SLinus Torvalds 2141da177e4SLinus Torvalds /* Fork a new task - this creates a new program thread. 2151da177e4SLinus Torvalds * This is called indirectly via a small wrapper 2161da177e4SLinus Torvalds */ 2171da177e4SLinus Torvalds asmlinkage int sys_fork(struct pt_regs *regs) 2181da177e4SLinus Torvalds { 219f24284adSHyok S. Choi #ifdef CONFIG_MMU 2201da177e4SLinus Torvalds return do_fork(SIGCHLD, regs->ARM_sp, regs, 0, NULL, NULL); 221f24284adSHyok S. Choi #else 222f24284adSHyok S. Choi /* can not support in nommu mode */ 223f24284adSHyok S. Choi return(-EINVAL); 224f24284adSHyok S. Choi #endif 2251da177e4SLinus Torvalds } 2261da177e4SLinus Torvalds 2271da177e4SLinus Torvalds /* Clone a task - this clones the calling program thread. 2281da177e4SLinus Torvalds * This is called indirectly via a small wrapper 2291da177e4SLinus Torvalds */ 2301da177e4SLinus Torvalds asmlinkage int sys_clone(unsigned long clone_flags, unsigned long newsp, 2311da177e4SLinus Torvalds int __user *parent_tidptr, int tls_val, 2321da177e4SLinus Torvalds int __user *child_tidptr, struct pt_regs *regs) 2331da177e4SLinus Torvalds { 2341da177e4SLinus Torvalds if (!newsp) 2351da177e4SLinus Torvalds newsp = regs->ARM_sp; 2361da177e4SLinus Torvalds 2371da177e4SLinus Torvalds return do_fork(clone_flags, newsp, regs, 0, parent_tidptr, child_tidptr); 2381da177e4SLinus Torvalds } 2391da177e4SLinus Torvalds 2401da177e4SLinus Torvalds asmlinkage int sys_vfork(struct pt_regs *regs) 2411da177e4SLinus Torvalds { 2421da177e4SLinus Torvalds return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs->ARM_sp, regs, 0, NULL, NULL); 2431da177e4SLinus Torvalds } 2441da177e4SLinus Torvalds 2451da177e4SLinus Torvalds /* sys_execve() executes a new program. 2461da177e4SLinus Torvalds * This is called indirectly via a small wrapper 2471da177e4SLinus Torvalds */ 2481da177e4SLinus Torvalds asmlinkage int sys_execve(char __user *filenamei, char __user * __user *argv, 2491da177e4SLinus Torvalds char __user * __user *envp, struct pt_regs *regs) 2501da177e4SLinus Torvalds { 2511da177e4SLinus Torvalds int error; 2521da177e4SLinus Torvalds char * filename; 2531da177e4SLinus Torvalds 2541da177e4SLinus Torvalds filename = getname(filenamei); 2551da177e4SLinus Torvalds error = PTR_ERR(filename); 2561da177e4SLinus Torvalds if (IS_ERR(filename)) 2571da177e4SLinus Torvalds goto out; 2581da177e4SLinus Torvalds error = do_execve(filename, argv, envp, regs); 2591da177e4SLinus Torvalds putname(filename); 2601da177e4SLinus Torvalds out: 2611da177e4SLinus Torvalds return error; 2621da177e4SLinus Torvalds } 2631da177e4SLinus Torvalds 2643db03b4aSArnd Bergmann int kernel_execve(const char *filename, char *const argv[], char *const envp[]) 2651da177e4SLinus Torvalds { 2661da177e4SLinus Torvalds struct pt_regs regs; 2671da177e4SLinus Torvalds int ret; 2681da177e4SLinus Torvalds 2691da177e4SLinus Torvalds memset(®s, 0, sizeof(struct pt_regs)); 2701da177e4SLinus Torvalds ret = do_execve((char *)filename, (char __user * __user *)argv, 2711da177e4SLinus Torvalds (char __user * __user *)envp, ®s); 2721da177e4SLinus Torvalds if (ret < 0) 2731da177e4SLinus Torvalds goto out; 2741da177e4SLinus Torvalds 2751da177e4SLinus Torvalds /* 2761da177e4SLinus Torvalds * Save argc to the register structure for userspace. 2771da177e4SLinus Torvalds */ 2781da177e4SLinus Torvalds regs.ARM_r0 = ret; 2791da177e4SLinus Torvalds 2801da177e4SLinus Torvalds /* 2811da177e4SLinus Torvalds * We were successful. We won't be returning to our caller, but 2821da177e4SLinus Torvalds * instead to user space by manipulating the kernel stack. 2831da177e4SLinus Torvalds */ 2841da177e4SLinus Torvalds asm( "add r0, %0, %1\n\t" 2851da177e4SLinus Torvalds "mov r1, %2\n\t" 2861da177e4SLinus Torvalds "mov r2, %3\n\t" 2871da177e4SLinus Torvalds "bl memmove\n\t" /* copy regs to top of stack */ 2881da177e4SLinus Torvalds "mov r8, #0\n\t" /* not a syscall */ 2891da177e4SLinus Torvalds "mov r9, %0\n\t" /* thread structure */ 2901da177e4SLinus Torvalds "mov sp, r0\n\t" /* reposition stack pointer */ 2911da177e4SLinus Torvalds "b ret_to_user" 2921da177e4SLinus Torvalds : 2931da177e4SLinus Torvalds : "r" (current_thread_info()), 2944f7a1812SRussell King "Ir" (THREAD_START_SP - sizeof(regs)), 2951da177e4SLinus Torvalds "r" (®s), 2961da177e4SLinus Torvalds "Ir" (sizeof(regs)) 297c2f48086SNicolas Pitre : "r0", "r1", "r2", "r3", "ip", "lr", "memory"); 2981da177e4SLinus Torvalds 2991da177e4SLinus Torvalds out: 3001da177e4SLinus Torvalds return ret; 3011da177e4SLinus Torvalds } 3023db03b4aSArnd Bergmann EXPORT_SYMBOL(kernel_execve); 30368d9102fSNicolas Pitre 30468d9102fSNicolas Pitre /* 3056cbdc8c5SSimon Arlott * Since loff_t is a 64 bit type we avoid a lot of ABI hassle 30668d9102fSNicolas Pitre * with a different argument ordering. 30768d9102fSNicolas Pitre */ 30868d9102fSNicolas Pitre asmlinkage long sys_arm_fadvise64_64(int fd, int advice, 30968d9102fSNicolas Pitre loff_t offset, loff_t len) 31068d9102fSNicolas Pitre { 31168d9102fSNicolas Pitre return sys_fadvise64_64(fd, offset, len, advice); 31268d9102fSNicolas Pitre } 313