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(&current->mm->mmap_sem);
81	error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
82	up_write(&current->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(&current->mm->mmap_sem);
131	ret = do_mremap(addr, old_len, new_len, flags, new_addr);
132	up_write(&current->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(&regs, 0, sizeof(struct pt_regs));
287        ret = do_execve((char *)filename, (char __user * __user *)argv,                         (char __user * __user *)envp, &regs);
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" (&regs),
312                  "Ir" (sizeof(regs))
313                : "r0", "r1", "r2", "r3", "ip", "memory");
314
315 out:
316        return ret;
317}
318
319EXPORT_SYMBOL(kernel_execve);
320