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