1/*
2 * sys_parisc32.c: Conversion between 32bit and 64bit native syscalls.
3 *
4 * Copyright (C) 2000-2001 Hewlett Packard Company
5 * Copyright (C) 2000 John Marvin
6 * Copyright (C) 2001 Matthew Wilcox
7 *
8 * These routines maintain argument size conversion between 32bit and 64bit
9 * environment. Based heavily on sys_ia32.c and sys_sparc32.c.
10 */
11
12#include <linux/compat.h>
13#include <linux/kernel.h>
14#include <linux/sched.h>
15#include <linux/fs.h>
16#include <linux/mm.h>
17#include <linux/file.h>
18#include <linux/signal.h>
19#include <linux/resource.h>
20#include <linux/times.h>
21#include <linux/utsname.h>
22#include <linux/time.h>
23#include <linux/smp.h>
24#include <linux/smp_lock.h>
25#include <linux/sem.h>
26#include <linux/msg.h>
27#include <linux/shm.h>
28#include <linux/slab.h>
29#include <linux/uio.h>
30#include <linux/nfs_fs.h>
31#include <linux/ncp_fs.h>
32#include <linux/sunrpc/svc.h>
33#include <linux/nfsd/nfsd.h>
34#include <linux/nfsd/cache.h>
35#include <linux/nfsd/xdr.h>
36#include <linux/nfsd/syscall.h>
37#include <linux/poll.h>
38#include <linux/personality.h>
39#include <linux/stat.h>
40#include <linux/highmem.h>
41#include <linux/highuid.h>
42#include <linux/mman.h>
43#include <linux/binfmts.h>
44#include <linux/namei.h>
45#include <linux/vfs.h>
46#include <linux/ptrace.h>
47#include <linux/swap.h>
48#include <linux/syscalls.h>
49
50#include <asm/types.h>
51#include <asm/uaccess.h>
52#include <asm/semaphore.h>
53#include <asm/mmu_context.h>
54
55#include "sys32.h"
56
57#undef DEBUG
58
59#ifdef DEBUG
60#define DBG(x)	printk x
61#else
62#define DBG(x)
63#endif
64
65/*
66 * sys32_execve() executes a new program.
67 */
68
69asmlinkage int sys32_execve(struct pt_regs *regs)
70{
71	int error;
72	char *filename;
73
74	DBG(("sys32_execve(%p) r26 = 0x%lx\n", regs, regs->gr[26]));
75	filename = getname((const char __user *) regs->gr[26]);
76	error = PTR_ERR(filename);
77	if (IS_ERR(filename))
78		goto out;
79	error = compat_do_execve(filename, compat_ptr(regs->gr[25]),
80				 compat_ptr(regs->gr[24]), regs);
81	if (error == 0) {
82		task_lock(current);
83		current->ptrace &= ~PT_DTRACE;
84		task_unlock(current);
85	}
86	putname(filename);
87out:
88
89	return error;
90}
91
92asmlinkage long sys32_unimplemented(int r26, int r25, int r24, int r23,
93	int r22, int r21, int r20)
94{
95    printk(KERN_ERR "%s(%d): Unimplemented 32 on 64 syscall #%d!\n",
96    	current->comm, current->pid, r20);
97    return -ENOSYS;
98}
99
100#ifdef CONFIG_SYSCTL
101
102struct __sysctl_args32 {
103	u32 name;
104	int nlen;
105	u32 oldval;
106	u32 oldlenp;
107	u32 newval;
108	u32 newlen;
109	u32 __unused[4];
110};
111
112asmlinkage long sys32_sysctl(struct __sysctl_args32 __user *args)
113{
114#ifndef CONFIG_SYSCTL_SYSCALL
115	return -ENOSYS;
116#else
117	struct __sysctl_args32 tmp;
118	int error;
119	unsigned int oldlen32;
120	size_t oldlen, __user *oldlenp = NULL;
121	unsigned long addr = (((long __force)&args->__unused[0]) + 7) & ~7;
122
123	DBG(("sysctl32(%p)\n", args));
124
125	if (copy_from_user(&tmp, args, sizeof(tmp)))
126		return -EFAULT;
127
128	if (tmp.oldval && tmp.oldlenp) {
129		/* Duh, this is ugly and might not work if sysctl_args
130		   is in read-only memory, but do_sysctl does indirectly
131		   a lot of uaccess in both directions and we'd have to
132		   basically copy the whole sysctl.c here, and
133		   glibc's __sysctl uses rw memory for the structure
134		   anyway.  */
135		/* a possibly better hack than this, which will avoid the
136		 * problem if the struct is read only, is to push the
137		 * 'oldlen' value out to the user's stack instead. -PB
138		 */
139		if (get_user(oldlen32, (u32 *)(u64)tmp.oldlenp))
140			return -EFAULT;
141		oldlen = oldlen32;
142		if (put_user(oldlen, (size_t *)addr))
143			return -EFAULT;
144		oldlenp = (size_t *)addr;
145	}
146
147	lock_kernel();
148	error = do_sysctl((int __user *)(u64)tmp.name, tmp.nlen,
149			  (void __user *)(u64)tmp.oldval, oldlenp,
150			  (void __user *)(u64)tmp.newval, tmp.newlen);
151	unlock_kernel();
152	if (oldlenp) {
153		if (!error) {
154			if (get_user(oldlen, (size_t *)addr)) {
155				error = -EFAULT;
156			} else {
157				oldlen32 = oldlen;
158				if (put_user(oldlen32, (u32 *)(u64)tmp.oldlenp))
159					error = -EFAULT;
160			}
161		}
162		if (copy_to_user(args->__unused, tmp.__unused, sizeof(tmp.__unused)))
163			error = -EFAULT;
164	}
165	return error;
166#endif
167}
168
169#endif /* CONFIG_SYSCTL */
170
171asmlinkage long sys32_sched_rr_get_interval(pid_t pid,
172	struct compat_timespec __user *interval)
173{
174	struct timespec t;
175	int ret;
176
177	KERNEL_SYSCALL(ret, sys_sched_rr_get_interval, pid, (struct timespec __user *)&t);
178	if (put_compat_timespec(&t, interval))
179		return -EFAULT;
180	return ret;
181}
182
183static int
184put_compat_timeval(struct compat_timeval __user *u, struct timeval *t)
185{
186	struct compat_timeval t32;
187	t32.tv_sec = t->tv_sec;
188	t32.tv_usec = t->tv_usec;
189	return copy_to_user(u, &t32, sizeof t32);
190}
191
192static inline long get_ts32(struct timespec *o, struct compat_timeval __user *i)
193{
194	long usec;
195
196	if (__get_user(o->tv_sec, &i->tv_sec))
197		return -EFAULT;
198	if (__get_user(usec, &i->tv_usec))
199		return -EFAULT;
200	o->tv_nsec = usec * 1000;
201	return 0;
202}
203
204asmlinkage int
205sys32_gettimeofday(struct compat_timeval __user *tv, struct timezone __user *tz)
206{
207    extern void do_gettimeofday(struct timeval *tv);
208
209    if (tv) {
210	    struct timeval ktv;
211	    do_gettimeofday(&ktv);
212	    if (put_compat_timeval(tv, &ktv))
213		    return -EFAULT;
214    }
215    if (tz) {
216	    extern struct timezone sys_tz;
217	    if (copy_to_user(tz, &sys_tz, sizeof(sys_tz)))
218		    return -EFAULT;
219    }
220    return 0;
221}
222
223asmlinkage
224int sys32_settimeofday(struct compat_timeval __user *tv, struct timezone __user *tz)
225{
226	struct timespec kts;
227	struct timezone ktz;
228
229 	if (tv) {
230		if (get_ts32(&kts, tv))
231			return -EFAULT;
232	}
233	if (tz) {
234		if (copy_from_user(&ktz, tz, sizeof(ktz)))
235			return -EFAULT;
236	}
237
238	return do_sys_settimeofday(tv ? &kts : NULL, tz ? &ktz : NULL);
239}
240
241int cp_compat_stat(struct kstat *stat, struct compat_stat __user *statbuf)
242{
243	compat_ino_t ino;
244	int err;
245
246	if (stat->size > MAX_NON_LFS || !new_valid_dev(stat->dev) ||
247	    !new_valid_dev(stat->rdev))
248		return -EOVERFLOW;
249
250	ino = stat->ino;
251	if (sizeof(ino) < sizeof(stat->ino) && ino != stat->ino)
252		return -EOVERFLOW;
253
254	err  = put_user(new_encode_dev(stat->dev), &statbuf->st_dev);
255	err |= put_user(ino, &statbuf->st_ino);
256	err |= put_user(stat->mode, &statbuf->st_mode);
257	err |= put_user(stat->nlink, &statbuf->st_nlink);
258	err |= put_user(0, &statbuf->st_reserved1);
259	err |= put_user(0, &statbuf->st_reserved2);
260	err |= put_user(new_encode_dev(stat->rdev), &statbuf->st_rdev);
261	err |= put_user(stat->size, &statbuf->st_size);
262	err |= put_user(stat->atime.tv_sec, &statbuf->st_atime);
263	err |= put_user(stat->atime.tv_nsec, &statbuf->st_atime_nsec);
264	err |= put_user(stat->mtime.tv_sec, &statbuf->st_mtime);
265	err |= put_user(stat->mtime.tv_nsec, &statbuf->st_mtime_nsec);
266	err |= put_user(stat->ctime.tv_sec, &statbuf->st_ctime);
267	err |= put_user(stat->ctime.tv_nsec, &statbuf->st_ctime_nsec);
268	err |= put_user(stat->blksize, &statbuf->st_blksize);
269	err |= put_user(stat->blocks, &statbuf->st_blocks);
270	err |= put_user(0, &statbuf->__unused1);
271	err |= put_user(0, &statbuf->__unused2);
272	err |= put_user(0, &statbuf->__unused3);
273	err |= put_user(0, &statbuf->__unused4);
274	err |= put_user(0, &statbuf->__unused5);
275	err |= put_user(0, &statbuf->st_fstype); /* not avail */
276	err |= put_user(0, &statbuf->st_realdev); /* not avail */
277	err |= put_user(0, &statbuf->st_basemode); /* not avail */
278	err |= put_user(0, &statbuf->st_spareshort);
279	err |= put_user(stat->uid, &statbuf->st_uid);
280	err |= put_user(stat->gid, &statbuf->st_gid);
281	err |= put_user(0, &statbuf->st_spare4[0]);
282	err |= put_user(0, &statbuf->st_spare4[1]);
283	err |= put_user(0, &statbuf->st_spare4[2]);
284
285	return err;
286}
287
288struct linux32_dirent {
289	u32		d_ino;
290	compat_off_t	d_off;
291	u16		d_reclen;
292	char		d_name[1];
293};
294
295struct old_linux32_dirent {
296	u32	d_ino;
297	u32	d_offset;
298	u16	d_namlen;
299	char	d_name[1];
300};
301
302struct getdents32_callback {
303	struct linux32_dirent __user * current_dir;
304	struct linux32_dirent __user * previous;
305	int count;
306	int error;
307};
308
309struct readdir32_callback {
310	struct old_linux32_dirent __user * dirent;
311	int count;
312};
313
314#define NAME_OFFSET(de) ((int) ((de)->d_name - (char __user *) (de)))
315static int filldir32 (void *__buf, const char *name, int namlen,
316			loff_t offset, u64 ino, unsigned int d_type)
317{
318	struct linux32_dirent __user * dirent;
319	struct getdents32_callback * buf = (struct getdents32_callback *) __buf;
320	int reclen = ALIGN(NAME_OFFSET(dirent) + namlen + 1, 4);
321	u32 d_ino;
322
323	buf->error = -EINVAL;	/* only used if we fail.. */
324	if (reclen > buf->count)
325		return -EINVAL;
326	d_ino = ino;
327	if (sizeof(d_ino) < sizeof(ino) && d_ino != ino)
328		return -EOVERFLOW;
329	dirent = buf->previous;
330	if (dirent)
331		put_user(offset, &dirent->d_off);
332	dirent = buf->current_dir;
333	buf->previous = dirent;
334	put_user(d_ino, &dirent->d_ino);
335	put_user(reclen, &dirent->d_reclen);
336	copy_to_user(dirent->d_name, name, namlen);
337	put_user(0, dirent->d_name + namlen);
338	dirent = ((void __user *)dirent) + reclen;
339	buf->current_dir = dirent;
340	buf->count -= reclen;
341	return 0;
342}
343
344asmlinkage long
345sys32_getdents (unsigned int fd, void __user * dirent, unsigned int count)
346{
347	struct file * file;
348	struct linux32_dirent __user * lastdirent;
349	struct getdents32_callback buf;
350	int error;
351
352	error = -EFAULT;
353	if (!access_ok(VERIFY_WRITE, dirent, count))
354		goto out;
355
356	error = -EBADF;
357	file = fget(fd);
358	if (!file)
359		goto out;
360
361	buf.current_dir = (struct linux32_dirent __user *) dirent;
362	buf.previous = NULL;
363	buf.count = count;
364	buf.error = 0;
365
366	error = vfs_readdir(file, filldir32, &buf);
367	if (error < 0)
368		goto out_putf;
369	error = buf.error;
370	lastdirent = buf.previous;
371	if (lastdirent) {
372		if (put_user(file->f_pos, &lastdirent->d_off))
373			error = -EFAULT;
374		else
375			error = count - buf.count;
376	}
377
378out_putf:
379	fput(file);
380out:
381	return error;
382}
383
384static int fillonedir32(void * __buf, const char * name, int namlen,
385			loff_t offset, u64 ino, unsigned int d_type)
386{
387	struct readdir32_callback * buf = (struct readdir32_callback *) __buf;
388	struct old_linux32_dirent __user * dirent;
389	u32 d_ino;
390
391	if (buf->count)
392		return -EINVAL;
393	d_ino = ino;
394	if (sizeof(d_ino) < sizeof(ino) && d_ino != ino)
395		return -EOVERFLOW;
396	buf->count++;
397	dirent = buf->dirent;
398	put_user(d_ino, &dirent->d_ino);
399	put_user(offset, &dirent->d_offset);
400	put_user(namlen, &dirent->d_namlen);
401	copy_to_user(dirent->d_name, name, namlen);
402	put_user(0, dirent->d_name + namlen);
403	return 0;
404}
405
406asmlinkage long
407sys32_readdir (unsigned int fd, void __user * dirent, unsigned int count)
408{
409	int error;
410	struct file * file;
411	struct readdir32_callback buf;
412
413	error = -EBADF;
414	file = fget(fd);
415	if (!file)
416		goto out;
417
418	buf.count = 0;
419	buf.dirent = dirent;
420
421	error = vfs_readdir(file, fillonedir32, &buf);
422	if (error >= 0)
423		error = buf.count;
424	fput(file);
425out:
426	return error;
427}
428
429/*** copied from mips64 ***/
430/*
431 * Ooo, nasty.  We need here to frob 32-bit unsigned longs to
432 * 64-bit unsigned longs.
433 */
434
435static inline int
436get_fd_set32(unsigned long n, u32 *ufdset, unsigned long *fdset)
437{
438	n = (n + 8*sizeof(u32) - 1) / (8*sizeof(u32));
439	if (ufdset) {
440		unsigned long odd;
441
442		if (!access_ok(VERIFY_WRITE, ufdset, n*sizeof(u32)))
443			return -EFAULT;
444
445		odd = n & 1UL;
446		n &= ~1UL;
447		while (n) {
448			unsigned long h, l;
449			__get_user(l, ufdset);
450			__get_user(h, ufdset+1);
451			ufdset += 2;
452			*fdset++ = h << 32 | l;
453			n -= 2;
454		}
455		if (odd)
456			__get_user(*fdset, ufdset);
457	} else {
458		/* Tricky, must clear full unsigned long in the
459		 * kernel fdset at the end, this makes sure that
460		 * actually happens.
461		 */
462		memset(fdset, 0, ((n + 1) & ~1)*sizeof(u32));
463	}
464	return 0;
465}
466
467static inline void
468set_fd_set32(unsigned long n, u32 *ufdset, unsigned long *fdset)
469{
470	unsigned long odd;
471	n = (n + 8*sizeof(u32) - 1) / (8*sizeof(u32));
472
473	if (!ufdset)
474		return;
475
476	odd = n & 1UL;
477	n &= ~1UL;
478	while (n) {
479		unsigned long h, l;
480		l = *fdset++;
481		h = l >> 32;
482		__put_user(l, ufdset);
483		__put_user(h, ufdset+1);
484		ufdset += 2;
485		n -= 2;
486	}
487	if (odd)
488		__put_user(*fdset, ufdset);
489}
490
491struct msgbuf32 {
492    int mtype;
493    char mtext[1];
494};
495
496asmlinkage long sys32_msgsnd(int msqid,
497				struct msgbuf32 __user *umsgp32,
498				size_t msgsz, int msgflg)
499{
500	struct msgbuf *mb;
501	struct msgbuf32 mb32;
502	int err;
503
504	if ((mb = kmalloc(msgsz + sizeof *mb + 4, GFP_KERNEL)) == NULL)
505		return -ENOMEM;
506
507	err = get_user(mb32.mtype, &umsgp32->mtype);
508	mb->mtype = mb32.mtype;
509	err |= copy_from_user(mb->mtext, &umsgp32->mtext, msgsz);
510
511	if (err)
512		err = -EFAULT;
513	else
514		KERNEL_SYSCALL(err, sys_msgsnd, msqid, (struct msgbuf __user *)mb, msgsz, msgflg);
515
516	kfree(mb);
517	return err;
518}
519
520asmlinkage long sys32_msgrcv(int msqid,
521				struct msgbuf32 __user *umsgp32,
522				size_t msgsz, long msgtyp, int msgflg)
523{
524	struct msgbuf *mb;
525	struct msgbuf32 mb32;
526	int err, len;
527
528	if ((mb = kmalloc(msgsz + sizeof *mb + 4, GFP_KERNEL)) == NULL)
529		return -ENOMEM;
530
531	KERNEL_SYSCALL(err, sys_msgrcv, msqid, (struct msgbuf __user *)mb, msgsz, msgtyp, msgflg);
532
533	if (err >= 0) {
534		len = err;
535		mb32.mtype = mb->mtype;
536		err = put_user(mb32.mtype, &umsgp32->mtype);
537		err |= copy_to_user(&umsgp32->mtext, mb->mtext, len);
538		if (err)
539			err = -EFAULT;
540		else
541			err = len;
542	}
543
544	kfree(mb);
545	return err;
546}
547
548asmlinkage int sys32_sendfile(int out_fd, int in_fd, compat_off_t __user *offset, s32 count)
549{
550        mm_segment_t old_fs = get_fs();
551        int ret;
552        off_t of;
553
554        if (offset && get_user(of, offset))
555                return -EFAULT;
556
557        set_fs(KERNEL_DS);
558        ret = sys_sendfile(out_fd, in_fd, offset ? (off_t __user *)&of : NULL, count);
559        set_fs(old_fs);
560
561        if (offset && put_user(of, offset))
562                return -EFAULT;
563
564        return ret;
565}
566
567asmlinkage int sys32_sendfile64(int out_fd, int in_fd, compat_loff_t __user *offset, s32 count)
568{
569	mm_segment_t old_fs = get_fs();
570	int ret;
571	loff_t lof;
572
573	if (offset && get_user(lof, offset))
574		return -EFAULT;
575
576	set_fs(KERNEL_DS);
577	ret = sys_sendfile64(out_fd, in_fd, offset ? (loff_t __user *)&lof : NULL, count);
578	set_fs(old_fs);
579
580	if (offset && put_user(lof, offset))
581		return -EFAULT;
582
583	return ret;
584}
585
586
587/* lseek() needs a wrapper because 'offset' can be negative, but the top
588 * half of the argument has been zeroed by syscall.S.
589 */
590
591asmlinkage int sys32_lseek(unsigned int fd, int offset, unsigned int origin)
592{
593	return sys_lseek(fd, offset, origin);
594}
595
596asmlinkage long sys32_semctl(int semid, int semnum, int cmd, union semun arg)
597{
598        union semun u;
599
600        if (cmd == SETVAL) {
601                /* Ugh.  arg is a union of int,ptr,ptr,ptr, so is 8 bytes.
602                 * The int should be in the first 4, but our argument
603                 * frobbing has left it in the last 4.
604                 */
605                u.val = *((int *)&arg + 1);
606                return sys_semctl (semid, semnum, cmd, u);
607	}
608	return sys_semctl (semid, semnum, cmd, arg);
609}
610
611long sys32_lookup_dcookie(u32 cookie_high, u32 cookie_low, char __user *buf,
612			  size_t len)
613{
614	return sys_lookup_dcookie((u64)cookie_high << 32 | cookie_low,
615				  buf, len);
616}
617