13e27bdb96e20ee7877a8c545bc627dc3bea6aea
[linux-2.6.git] / arch / s390 / kernel / sys_s390.c
1 /*
2  *  arch/s390/kernel/sys_s390.c
3  *
4  *  S390 version
5  *    Copyright (C) 1999,2000 IBM Deutschland Entwicklung GmbH, IBM Corporation
6  *    Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com),
7  *               Thomas Spatzier (tspat@de.ibm.com)
8  *
9  *  Derived from "arch/i386/kernel/sys_i386.c"
10  *
11  *  This file contains various random system calls that
12  *  have a non-standard calling sequence on the Linux/s390
13  *  platform.
14  */
15
16 #include <linux/errno.h>
17 #include <linux/sched.h>
18 #include <linux/mm.h>
19 #include <linux/smp.h>
20 #include <linux/sem.h>
21 #include <linux/msg.h>
22 #include <linux/shm.h>
23 #include <linux/stat.h>
24 #include <linux/syscalls.h>
25 #include <linux/mman.h>
26 #include <linux/file.h>
27 #include <linux/utsname.h>
28 #include <linux/personality.h>
29 #include <linux/unistd.h>
30
31 #include <asm/uaccess.h>
32 #include <asm/ipc.h>
33
34 /*
35  * sys_pipe() is the normal C calling standard for creating
36  * a pipe. It's not the way Unix traditionally does this, though.
37  */
38 asmlinkage long sys_pipe(unsigned long __user *fildes)
39 {
40         int fd[2];
41         int error;
42
43         error = do_pipe(fd);
44         if (!error) {
45                 if (copy_to_user(fildes, fd, 2*sizeof(int)))
46                         error = -EFAULT;
47         }
48         return error;
49 }
50
51 /* common code for old and new mmaps */
52 static inline long do_mmap2(
53         unsigned long addr, unsigned long len,
54         unsigned long prot, unsigned long flags,
55         unsigned long fd, unsigned long pgoff)
56 {
57         long error = -EBADF;
58         struct file * file = NULL;
59
60         flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
61         if (!(flags & MAP_ANONYMOUS)) {
62                 file = fget(fd);
63                 if (!file)
64                         goto out;
65         }
66
67         down_write(&current->mm->mmap_sem);
68         error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
69         up_write(&current->mm->mmap_sem);
70
71         if (file)
72                 fput(file);
73 out:
74         return error;
75 }
76
77 /*
78  * Perform the select(nd, in, out, ex, tv) and mmap() system
79  * calls. Linux for S/390 isn't able to handle more than 5
80  * system call parameters, so these system calls used a memory
81  * block for parameter passing..
82  */
83
84 struct mmap_arg_struct {
85         unsigned long addr;
86         unsigned long len;
87         unsigned long prot;
88         unsigned long flags;
89         unsigned long fd;
90         unsigned long offset;
91 };
92
93 asmlinkage long sys_mmap2(struct mmap_arg_struct __user  *arg)
94 {
95         struct mmap_arg_struct a;
96         int error = -EFAULT;
97
98         if (copy_from_user(&a, arg, sizeof(a)))
99                 goto out;
100         error = do_mmap2(a.addr, a.len, a.prot, a.flags, a.fd, a.offset);
101 out:
102         return error;
103 }
104
105 asmlinkage long old_mmap(struct mmap_arg_struct __user *arg)
106 {
107         struct mmap_arg_struct a;
108         long error = -EFAULT;
109
110         if (copy_from_user(&a, arg, sizeof(a)))
111                 goto out;
112
113         error = -EINVAL;
114         if (a.offset & ~PAGE_MASK)
115                 goto out;
116
117         error = do_mmap2(a.addr, a.len, a.prot, a.flags, a.fd, a.offset >> PAGE_SHIFT);
118 out:
119         return error;
120 }
121
122 #ifndef CONFIG_64BIT
123 struct sel_arg_struct {
124         unsigned long n;
125         fd_set __user *inp, *outp, *exp;
126         struct timeval __user *tvp;
127 };
128
129 asmlinkage long old_select(struct sel_arg_struct __user *arg)
130 {
131         struct sel_arg_struct a;
132
133         if (copy_from_user(&a, arg, sizeof(a)))
134                 return -EFAULT;
135         /* sys_select() does the appropriate kernel locking */
136         return sys_select(a.n, a.inp, a.outp, a.exp, a.tvp);
137
138 }
139 #endif /* CONFIG_64BIT */
140
141 /*
142  * sys_ipc() is the de-multiplexer for the SysV IPC calls..
143  *
144  * This is really horribly ugly.
145  */
146 asmlinkage long sys_ipc(uint call, int first, unsigned long second,
147                                   unsigned long third, void __user *ptr)
148 {
149         struct ipc_kludge tmp;
150         int ret;
151
152         switch (call) {
153         case SEMOP:
154                 return sys_semtimedop(first, (struct sembuf __user *)ptr,
155                                        (unsigned)second, NULL);
156         case SEMTIMEDOP:
157                 return sys_semtimedop(first, (struct sembuf __user *)ptr,
158                                        (unsigned)second,
159                                        (const struct timespec __user *) third);
160         case SEMGET:
161                 return sys_semget(first, (int)second, third);
162         case SEMCTL: {
163                 union semun fourth;
164                 if (!ptr)
165                         return -EINVAL;
166                 if (get_user(fourth.__pad, (void __user * __user *) ptr))
167                         return -EFAULT;
168                 return sys_semctl(first, (int)second, third, fourth);
169         }
170         case MSGSND:
171                 return sys_msgsnd (first, (struct msgbuf __user *) ptr,
172                                    (size_t)second, third);
173                 break;
174         case MSGRCV:
175                 if (!ptr)
176                         return -EINVAL;
177                 if (copy_from_user (&tmp, (struct ipc_kludge __user *) ptr,
178                                     sizeof (struct ipc_kludge)))
179                         return -EFAULT;
180                 return sys_msgrcv (first, tmp.msgp,
181                                    (size_t)second, tmp.msgtyp, third);
182         case MSGGET:
183                 return sys_msgget((key_t)first, (int)second);
184         case MSGCTL:
185                 return sys_msgctl(first, (int)second,
186                                    (struct msqid_ds __user *)ptr);
187
188         case SHMAT: {
189                 ulong raddr;
190                 ret = do_shmat(first, (char __user *)ptr,
191                                 (int)second, &raddr);
192                 if (ret)
193                         return ret;
194                 return put_user (raddr, (ulong __user *) third);
195                 break;
196         }
197         case SHMDT:
198                 return sys_shmdt ((char __user *)ptr);
199         case SHMGET:
200                 return sys_shmget(first, (size_t)second, third);
201         case SHMCTL:
202                 return sys_shmctl(first, (int)second,
203                                    (struct shmid_ds __user *) ptr);
204         default:
205                 return -ENOSYS;
206
207         }
208
209         return -EINVAL;
210 }
211
212 #ifdef CONFIG_64BIT
213 asmlinkage long s390x_newuname(struct new_utsname __user *name)
214 {
215         int ret = sys_newuname(name);
216
217         if (current->personality == PER_LINUX32 && !ret) {
218                 ret = copy_to_user(name->machine, "s390\0\0\0\0", 8);
219                 if (ret) ret = -EFAULT;
220         }
221         return ret;
222 }
223
224 asmlinkage long s390x_personality(unsigned long personality)
225 {
226         int ret;
227
228         if (current->personality == PER_LINUX32 && personality == PER_LINUX)
229                 personality = PER_LINUX32;
230         ret = sys_personality(personality);
231         if (ret == PER_LINUX32)
232                 ret = PER_LINUX;
233
234         return ret;
235 }
236 #endif /* CONFIG_64BIT */
237
238 /*
239  * Wrapper function for sys_fadvise64/fadvise64_64
240  */
241 #ifndef CONFIG_64BIT
242
243 asmlinkage long
244 s390_fadvise64(int fd, u32 offset_high, u32 offset_low, size_t len, int advice)
245 {
246         return sys_fadvise64(fd, (u64) offset_high << 32 | offset_low,
247                         len, advice);
248 }
249
250 #endif
251
252 struct fadvise64_64_args {
253         int fd;
254         long long offset;
255         long long len;
256         int advice;
257 };
258
259 asmlinkage long
260 s390_fadvise64_64(struct fadvise64_64_args __user *args)
261 {
262         struct fadvise64_64_args a;
263
264         if ( copy_from_user(&a, args, sizeof(a)) )
265                 return -EFAULT;
266         return sys_fadvise64_64(a.fd, a.offset, a.len, a.advice);
267 }
268
269 #ifndef CONFIG_64BIT
270 /*
271  * This is a wrapper to call sys_fallocate(). For 31 bit s390 the last
272  * 64 bit argument "len" is split into the upper and lower 32 bits. The
273  * system call wrapper in the user space loads the value to %r6/%r7.
274  * The code in entry.S keeps the values in %r2 - %r6 where they are and
275  * stores %r7 to 96(%r15). But the standard C linkage requires that
276  * the whole 64 bit value for len is stored on the stack and doesn't
277  * use %r6 at all. So s390_fallocate has to convert the arguments from
278  *   %r2: fd, %r3: mode, %r4/%r5: offset, %r6/96(%r15)-99(%r15): len
279  * to
280  *   %r2: fd, %r3: mode, %r4/%r5: offset, 96(%r15)-103(%r15): len
281  */
282 asmlinkage long s390_fallocate(int fd, int mode, loff_t offset,
283                                u32 len_high, u32 len_low)
284 {
285         return sys_fallocate(fd, mode, offset, ((u64)len_high << 32) | len_low);
286 }
287 #endif