import of upstream 2.4.34.4 from kernel.org
[linux-2.4.git] / arch / arm / kernel / sys_arm.c
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/errno.h>
16 #include <linux/sched.h>
17 #include <linux/slab.h>
18 #include <linux/mm.h>
19 #include <linux/sem.h>
20 #include <linux/msg.h>
21 #include <linux/shm.h>
22 #include <linux/stat.h>
23 #include <linux/mman.h>
24 #include <linux/fs.h>
25 #include <linux/file.h>
26 #include <linux/utsname.h>
27
28 #include <asm/uaccess.h>
29 #include <asm/ipc.h>
30
31 extern 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 /*
36  * sys_pipe() is the normal C calling standard for creating
37  * a pipe. It's not the way unix traditionally does this, though.
38  */
39 asmlinkage int sys_pipe(unsigned long * fildes)
40 {
41         int fd[2];
42         int error;
43
44         error = do_pipe(fd);
45         if (!error) {
46                 if (copy_to_user(fildes, fd, 2*sizeof(int)))
47                         error = -EFAULT;
48         }
49         return error;
50 }
51
52 /* common code for old and new mmaps */
53 inline long do_mmap2(
54         unsigned long addr, unsigned long len,
55         unsigned long prot, unsigned long flags,
56         unsigned long fd, unsigned long pgoff)
57 {
58         int error = -EINVAL;
59         struct file * file = NULL;
60
61         flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
62
63         /*
64          * If we are doing a fixed mapping, and address < PAGE_SIZE,
65          * then deny it.
66          */
67         if (flags & MAP_FIXED && addr < PAGE_SIZE && vectors_base() == 0)
68                 goto out;
69
70         error = -EBADF;
71         if (!(flags & MAP_ANONYMOUS)) {
72                 file = fget(fd);
73                 if (!file)
74                         goto out;
75         }
76
77         down_write(&current->mm->mmap_sem);
78         error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
79         up_write(&current->mm->mmap_sem);
80
81         if (file)
82                 fput(file);
83 out:
84         return error;
85 }
86
87 struct mmap_arg_struct {
88         unsigned long addr;
89         unsigned long len;
90         unsigned long prot;
91         unsigned long flags;
92         unsigned long fd;
93         unsigned long offset;
94 };
95
96 asmlinkage int old_mmap(struct mmap_arg_struct *arg)
97 {
98         int error = -EFAULT;
99         struct mmap_arg_struct a;
100
101         if (copy_from_user(&a, arg, sizeof(a)))
102                 goto out;;
103
104         error = -EINVAL;
105         if (a.offset & ~PAGE_MASK)
106                 goto out;
107
108         error = do_mmap2(a.addr, a.len, a.prot, a.flags, a.fd, a.offset >> PAGE_SHIFT);
109 out:
110         return error;
111 }
112
113 asmlinkage unsigned long
114 sys_arm_mremap(unsigned long addr, unsigned long old_len,
115                unsigned long new_len, unsigned long flags,
116                unsigned long new_addr)
117 {
118         unsigned long ret = -EINVAL;
119
120         /*
121          * If we are doing a fixed mapping, and address < PAGE_SIZE,
122          * then deny it.
123          */
124         if (flags & MREMAP_FIXED && new_addr < PAGE_SIZE &&
125             vectors_base() == 0)
126                 goto out;
127
128         down_write(&current->mm->mmap_sem);
129         ret = do_mremap(addr, old_len, new_len, flags, new_addr);
130         up_write(&current->mm->mmap_sem);
131
132 out:
133         return ret;
134 }
135
136 /*
137  * Perform the select(nd, in, out, ex, tv) and mmap() system
138  * calls.
139  */
140 extern asmlinkage int sys_select(int, fd_set *, fd_set *, fd_set *, struct timeval *);
141
142 struct sel_arg_struct {
143         unsigned long n;
144         fd_set *inp, *outp, *exp;
145         struct timeval *tvp;
146 };
147
148 asmlinkage int old_select(struct sel_arg_struct *arg)
149 {
150         struct sel_arg_struct a;
151
152         if (copy_from_user(&a, arg, sizeof(a)))
153                 return -EFAULT;
154         /* sys_select() does the appropriate kernel locking */
155         return sys_select(a.n, a.inp, a.outp, a.exp, a.tvp);
156 }
157
158 /*
159  * sys_ipc() is the de-multiplexer for the SysV IPC calls..
160  *
161  * This is really horribly ugly.
162  */
163 asmlinkage int sys_ipc (uint call, int first, int second, int third, void *ptr, long fifth)
164 {
165         int version, ret;
166
167         version = call >> 16; /* hack for backward compatibility */
168         call &= 0xffff;
169
170         switch (call) {
171         case SEMOP:
172                 return sys_semop (first, (struct sembuf *)ptr, second);
173         case SEMGET:
174                 return sys_semget (first, second, third);
175         case SEMCTL: {
176                 union semun fourth;
177                 if (!ptr)
178                         return -EINVAL;
179                 if (get_user(fourth.__pad, (void **) ptr))
180                         return -EFAULT;
181                 return sys_semctl (first, second, third, fourth);
182         }
183
184         case MSGSND:
185                 return sys_msgsnd (first, (struct msgbuf *) ptr, 
186                                    second, third);
187         case MSGRCV:
188                 switch (version) {
189                 case 0: {
190                         struct ipc_kludge tmp;
191                         if (!ptr)
192                                 return -EINVAL;
193                         if (copy_from_user(&tmp,(struct ipc_kludge *) ptr,
194                                            sizeof (tmp)))
195                                 return -EFAULT;
196                         return sys_msgrcv (first, tmp.msgp, second,
197                                            tmp.msgtyp, third);
198                 }
199                 default:
200                         return sys_msgrcv (first,
201                                            (struct msgbuf *) ptr,
202                                            second, fifth, third);
203                 }
204         case MSGGET:
205                 return sys_msgget ((key_t) first, second);
206         case MSGCTL:
207                 return sys_msgctl (first, second, (struct msqid_ds *) ptr);
208
209         case SHMAT:
210                 switch (version) {
211                 default: {
212                         ulong raddr;
213                         ret = sys_shmat (first, (char *) ptr, second, &raddr);
214                         if (ret)
215                                 return ret;
216                         return put_user (raddr, (ulong *) third);
217                 }
218                 case 1: /* iBCS2 emulator entry point */
219                         if (!segment_eq(get_fs(), get_ds()))
220                                 return -EINVAL;
221                         return sys_shmat (first, (char *) ptr,
222                                           second, (ulong *) third);
223                 }
224         case SHMDT: 
225                 return sys_shmdt ((char *)ptr);
226         case SHMGET:
227                 return sys_shmget (first, second, third);
228         case SHMCTL:
229                 return sys_shmctl (first, second,
230                                    (struct shmid_ds *) ptr);
231         default:
232                 return -EINVAL;
233         }
234 }
235
236 /* Fork a new task - this creates a new program thread.
237  * This is called indirectly via a small wrapper
238  */
239 asmlinkage int sys_fork(struct pt_regs *regs)
240 {
241         return do_fork(SIGCHLD, regs->ARM_sp, regs, 0);
242 }
243
244 /* Clone a task - this clones the calling program thread.
245  * This is called indirectly via a small wrapper
246  */
247 asmlinkage int sys_clone(unsigned long clone_flags, unsigned long newsp, struct pt_regs *regs)
248 {
249         if (!newsp)
250                 newsp = regs->ARM_sp;
251         return do_fork(clone_flags, newsp, regs, 0);
252 }
253
254 asmlinkage int sys_vfork(struct pt_regs *regs)
255 {
256         return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs->ARM_sp, regs, 0);
257 }
258
259 /* sys_execve() executes a new program.
260  * This is called indirectly via a small wrapper
261  */
262 asmlinkage int sys_execve(char *filenamei, char **argv, char **envp, struct pt_regs *regs)
263 {
264         int error;
265         char * filename;
266
267         filename = getname(filenamei);
268         error = PTR_ERR(filename);
269         if (IS_ERR(filename))
270                 goto out;
271         error = do_execve(filename, argv, envp, regs);
272         putname(filename);
273 out:
274         return error;
275 }
276
277 asmlinkage int sys_pause(void)
278 {
279         current->state = TASK_INTERRUPTIBLE;
280         schedule();
281         return -ERESTARTNOHAND;
282 }