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 */
sys_pipe(unsigned long * fildes)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 */
do_mmap2(unsigned long addr,unsigned long len,unsigned long prot,unsigned long flags,unsigned long fd,unsigned long pgoff)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(¤t->mm->mmap_sem);
78 error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
79 up_write(¤t->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
old_mmap(struct mmap_arg_struct * arg)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
sys_arm_mremap(unsigned long addr,unsigned long old_len,unsigned long new_len,unsigned long flags,unsigned long new_addr)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(¤t->mm->mmap_sem);
129 ret = do_mremap(addr, old_len, new_len, flags, new_addr);
130 up_write(¤t->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
old_select(struct sel_arg_struct * arg)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 */
sys_ipc(uint call,int first,int second,int third,void * ptr,long fifth)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 */
sys_fork(struct pt_regs * regs)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 */
sys_clone(unsigned long clone_flags,unsigned long newsp,struct pt_regs * regs)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
sys_vfork(struct pt_regs * regs)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 */
sys_execve(char * filenamei,char ** argv,char ** envp,struct pt_regs * regs)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
sys_pause(void)277 asmlinkage int sys_pause(void)
278 {
279 current->state = TASK_INTERRUPTIBLE;
280 schedule();
281 return -ERESTARTNOHAND;
282 }
283