Merge branch 'work'
[linux-2.6] / arch / arm / kernel / sys_oabi-compat.c
1 /*
2  *  arch/arm/kernel/sys_oabi-compat.c
3  *
4  *  Compatibility wrappers for syscalls that are used from
5  *  old ABI user space binaries with an EABI kernel.
6  *
7  *  Author:     Nicolas Pitre
8  *  Created:    Oct 7, 2005
9  *  Copyright:  MontaVista Software, Inc.
10  *
11  *  This program is free software; you can redistribute it and/or modify
12  *  it under the terms of the GNU General Public License version 2 as
13  *  published by the Free Software Foundation.
14  */
15
16 /*
17  * The legacy ABI and the new ARM EABI have different rules making some
18  * syscalls incompatible especially with structure arguments.
19  * Most notably, Eabi says 64-bit members should be 64-bit aligned instead of
20  * simply word aligned.  EABI also pads structures to the size of the largest
21  * member it contains instead of the invariant 32-bit.
22  *
23  * The following syscalls are affected:
24  *
25  * sys_stat64:
26  * sys_lstat64:
27  * sys_fstat64:
28  *
29  *   struct stat64 has different sizes and some members are shifted
30  *   Compatibility wrappers are needed for them and provided below.
31  *
32  * sys_fcntl64:
33  *
34  *   struct flock64 has different sizes and some members are shifted
35  *   A compatibility wrapper is needed and provided below.
36  *
37  * sys_statfs64:
38  * sys_fstatfs64:
39  *
40  *   struct statfs64 has extra padding with EABI growing its size from
41  *   84 to 88.  This struct is now __attribute__((packed,aligned(4)))
42  *   with a small assembly wrapper to force the sz argument to 84 if it is 88
43  *   to avoid copying the extra padding over user space unexpecting it.
44  *
45  * sys_newuname:
46  *
47  *   struct new_utsname has no padding with EABI.  No problem there.
48  *
49  * sys_epoll_ctl:
50  * sys_epoll_wait:
51  *
52  *   struct epoll_event has its second member shifted also affecting the
53  *   structure size. Compatibility wrappers are needed and provided below.
54  *
55  * sys_ipc:
56  * sys_semop:
57  * sys_semtimedop:
58  *
59  *   struct sembuf loses its padding with EABI.  Since arrays of them are
60  *   used they have to be copyed to remove the padding. Compatibility wrappers
61  *   provided below.
62  */
63
64 #include <linux/syscalls.h>
65 #include <linux/errno.h>
66 #include <linux/fs.h>
67 #include <linux/fcntl.h>
68 #include <linux/eventpoll.h>
69 #include <linux/sem.h>
70 #include <asm/ipc.h>
71 #include <asm/uaccess.h>
72
73 struct oldabi_stat64 {
74         unsigned long long st_dev;
75         unsigned int    __pad1;
76         unsigned long   __st_ino;
77         unsigned int    st_mode;
78         unsigned int    st_nlink;
79
80         unsigned long   st_uid;
81         unsigned long   st_gid;
82
83         unsigned long long st_rdev;
84         unsigned int    __pad2;
85
86         long long       st_size;
87         unsigned long   st_blksize;
88         unsigned long long st_blocks;
89
90         unsigned long   st_atime;
91         unsigned long   st_atime_nsec;
92
93         unsigned long   st_mtime;
94         unsigned long   st_mtime_nsec;
95
96         unsigned long   st_ctime;
97         unsigned long   st_ctime_nsec;
98
99         unsigned long long st_ino;
100 } __attribute__ ((packed,aligned(4)));
101
102 static long cp_oldabi_stat64(struct kstat *stat,
103                              struct oldabi_stat64 __user *statbuf)
104 {
105         struct oldabi_stat64 tmp;
106
107         tmp.st_dev = huge_encode_dev(stat->dev);
108         tmp.__pad1 = 0;
109         tmp.__st_ino = stat->ino;
110         tmp.st_mode = stat->mode;
111         tmp.st_nlink = stat->nlink;
112         tmp.st_uid = stat->uid;
113         tmp.st_gid = stat->gid;
114         tmp.st_rdev = huge_encode_dev(stat->rdev);
115         tmp.st_size = stat->size;
116         tmp.st_blocks = stat->blocks;
117         tmp.__pad2 = 0;
118         tmp.st_blksize = stat->blksize;
119         tmp.st_atime = stat->atime.tv_sec;
120         tmp.st_atime_nsec = stat->atime.tv_nsec;
121         tmp.st_mtime = stat->mtime.tv_sec;
122         tmp.st_mtime_nsec = stat->mtime.tv_nsec;
123         tmp.st_ctime = stat->ctime.tv_sec;
124         tmp.st_ctime_nsec = stat->ctime.tv_nsec;
125         tmp.st_ino = stat->ino;
126         return copy_to_user(statbuf,&tmp,sizeof(tmp)) ? -EFAULT : 0;
127 }
128
129 asmlinkage long sys_oabi_stat64(char __user * filename,
130                                 struct oldabi_stat64 __user * statbuf)
131 {
132         struct kstat stat;
133         int error = vfs_stat(filename, &stat);
134         if (!error)
135                 error = cp_oldabi_stat64(&stat, statbuf);
136         return error;
137 }
138
139 asmlinkage long sys_oabi_lstat64(char __user * filename,
140                                  struct oldabi_stat64 __user * statbuf)
141 {
142         struct kstat stat;
143         int error = vfs_lstat(filename, &stat);
144         if (!error)
145                 error = cp_oldabi_stat64(&stat, statbuf);
146         return error;
147 }
148
149 asmlinkage long sys_oabi_fstat64(unsigned long fd,
150                                  struct oldabi_stat64 __user * statbuf)
151 {
152         struct kstat stat;
153         int error = vfs_fstat(fd, &stat);
154         if (!error)
155                 error = cp_oldabi_stat64(&stat, statbuf);
156         return error;
157 }
158
159 struct oabi_flock64 {
160         short   l_type;
161         short   l_whence;
162         loff_t  l_start;
163         loff_t  l_len;
164         pid_t   l_pid;
165 } __attribute__ ((packed,aligned(4)));
166
167 asmlinkage long sys_oabi_fcntl64(unsigned int fd, unsigned int cmd,
168                                  unsigned long arg)
169 {
170         struct oabi_flock64 user;
171         struct flock64 kernel;
172         mm_segment_t fs = USER_DS; /* initialized to kill a warning */
173         unsigned long local_arg = arg;
174         int ret;
175
176         switch (cmd) {
177         case F_GETLK64:
178         case F_SETLK64:
179         case F_SETLKW64:
180                 if (copy_from_user(&user, (struct oabi_flock64 __user *)arg,
181                                    sizeof(user)))
182                         return -EFAULT;
183                 kernel.l_type   = user.l_type;
184                 kernel.l_whence = user.l_whence;
185                 kernel.l_start  = user.l_start;
186                 kernel.l_len    = user.l_len;
187                 kernel.l_pid    = user.l_pid;
188                 local_arg = (unsigned long)&kernel;
189                 fs = get_fs();
190                 set_fs(KERNEL_DS);
191         }
192
193         ret = sys_fcntl64(fd, cmd, local_arg);
194
195         switch (cmd) {
196         case F_GETLK64:
197                 if (!ret) {
198                         user.l_type     = kernel.l_type;
199                         user.l_whence   = kernel.l_whence;
200                         user.l_start    = kernel.l_start;
201                         user.l_len      = kernel.l_len;
202                         user.l_pid      = kernel.l_pid;
203                         if (copy_to_user((struct oabi_flock64 __user *)arg,
204                                          &user, sizeof(user)))
205                                 ret = -EFAULT;
206                 }
207         case F_SETLK64:
208         case F_SETLKW64:
209                 set_fs(fs);
210         }
211
212         return ret;
213 }
214
215 struct oabi_epoll_event {
216         __u32 events;
217         __u64 data;
218 } __attribute__ ((packed,aligned(4)));
219
220 asmlinkage long sys_oabi_epoll_ctl(int epfd, int op, int fd,
221                                    struct oabi_epoll_event __user *event)
222 {
223         struct oabi_epoll_event user;
224         struct epoll_event kernel;
225         mm_segment_t fs;
226         long ret;
227
228         if (op == EPOLL_CTL_DEL)
229                 return sys_epoll_ctl(epfd, op, fd, NULL);
230         if (copy_from_user(&user, event, sizeof(user)))
231                 return -EFAULT;
232         kernel.events = user.events;
233         kernel.data   = user.data;
234         fs = get_fs();
235         set_fs(KERNEL_DS);
236         ret = sys_epoll_ctl(epfd, op, fd, &kernel);
237         set_fs(fs);
238         return ret;
239 }
240
241 asmlinkage long sys_oabi_epoll_wait(int epfd,
242                                     struct oabi_epoll_event __user *events,
243                                     int maxevents, int timeout)
244 {
245         struct epoll_event *kbuf;
246         mm_segment_t fs;
247         long ret, err, i;
248
249         if (maxevents <= 0 || maxevents > (INT_MAX/sizeof(struct epoll_event)))
250                 return -EINVAL;
251         kbuf = kmalloc(sizeof(*kbuf) * maxevents, GFP_KERNEL);
252         if (!kbuf)
253                 return -ENOMEM;
254         fs = get_fs();
255         set_fs(KERNEL_DS);
256         ret = sys_epoll_wait(epfd, kbuf, maxevents, timeout);
257         set_fs(fs);
258         err = 0;
259         for (i = 0; i < ret; i++) {
260                 __put_user_error(kbuf[i].events, &events->events, err);
261                 __put_user_error(kbuf[i].data,   &events->data,   err);
262                 events++;
263         }
264         kfree(kbuf);
265         return err ? -EFAULT : ret;
266 }
267
268 struct oabi_sembuf {
269         unsigned short  sem_num;
270         short           sem_op;
271         short           sem_flg;
272         unsigned short  __pad;
273 };
274
275 asmlinkage long sys_oabi_semtimedop(int semid,
276                                     struct oabi_sembuf __user *tsops,
277                                     unsigned nsops,
278                                     const struct timespec __user *timeout)
279 {
280         struct sembuf *sops;
281         struct timespec local_timeout;
282         long err;
283         int i;
284
285         if (nsops < 1)
286                 return -EINVAL;
287         sops = kmalloc(sizeof(*sops) * nsops, GFP_KERNEL);
288         if (!sops)
289                 return -ENOMEM;
290         err = 0;
291         for (i = 0; i < nsops; i++) {
292                 __get_user_error(sops[i].sem_num, &tsops->sem_num, err);
293                 __get_user_error(sops[i].sem_op,  &tsops->sem_op,  err);
294                 __get_user_error(sops[i].sem_flg, &tsops->sem_flg, err);
295                 tsops++;
296         }
297         if (timeout) {
298                 /* copy this as well before changing domain protection */
299                 err |= copy_from_user(&local_timeout, timeout, sizeof(*timeout));
300                 timeout = &local_timeout;
301         }
302         if (err) {
303                 err = -EFAULT;
304         } else {
305                 mm_segment_t fs = get_fs();
306                 set_fs(KERNEL_DS);
307                 err = sys_semtimedop(semid, sops, nsops, timeout);
308                 set_fs(fs);
309         }
310         kfree(sops);
311         return err;
312 }
313
314 asmlinkage long sys_oabi_semop(int semid, struct oabi_sembuf __user *tsops,
315                                unsigned nsops)
316 {
317         return sys_oabi_semtimedop(semid, tsops, nsops, NULL);
318 }
319
320 extern asmlinkage int sys_ipc(uint call, int first, int second, int third,
321                               void __user *ptr, long fifth);
322
323 asmlinkage int sys_oabi_ipc(uint call, int first, int second, int third,
324                             void __user *ptr, long fifth)
325 {
326         switch (call & 0xffff) {
327         case SEMOP:
328                 return  sys_oabi_semtimedop(first,
329                                             (struct oabi_sembuf __user *)ptr,
330                                             second, NULL);
331         case SEMTIMEDOP:
332                 return  sys_oabi_semtimedop(first,
333                                             (struct oabi_sembuf __user *)ptr,
334                                             second,
335                                             (const struct timespec __user *)fifth);
336         default:
337                 return sys_ipc(call, first, second, third, ptr, fifth);
338         }
339 }