patch-2.4.21 linux-2.4.21/arch/x86_64/ia32/sys_ia32.c

Next file: linux-2.4.21/arch/x86_64/kernel/acpitable.c
Previous file: linux-2.4.21/arch/x86_64/ia32/socket32.c
Back to the patch index
Back to the overall index

diff -urN linux-2.4.20/arch/x86_64/ia32/sys_ia32.c linux-2.4.21/arch/x86_64/ia32/sys_ia32.c
@@ -9,13 +9,14 @@
  * Copyright (C) 1997 		David S. Miller (davem@caip.rutgers.edu)
  * Copyright (C) 2000		Hewlett-Packard Co.
  * Copyright (C) 2000		David Mosberger-Tang <davidm@hpl.hp.com>
- * Copyright (C) 2000,2001	Andi Kleen, SuSE Labs (x86-64 port) 
+ * Copyright (C) 2000,2001,2002	Andi Kleen, SuSE Labs (x86-64 port) 
  *
  * These routines maintain argument size conversion between 32bit and 64bit
  * environment. In 2.5 most of this should be moved to a generic directory. 
  *
  * This file assumes that there is a hole at the end of user address space.
- * $Id: sys_ia32.c,v 1.42 2002/09/17 15:23:41 ak Exp $
+ *
+ * $Id: sys_ia32.c,v 1.54 2003/03/24 09:28:26 ak Exp $
  */
 
 #include <linux/config.h>
@@ -61,6 +62,7 @@
 #include <asm/semaphore.h>
 #include <asm/ipc.h>
 #include <asm/atomic.h>
+#include <asm/ldt.h>
 
 #include <net/scm.h>
 #include <net/sock.h>
@@ -225,7 +227,33 @@
 	return ret;
 }
 
+/* Don't set O_LARGEFILE implicitely. */
+asmlinkage long sys32_open(const char * filename, int flags, int mode)
+{
+	char * tmp;
+	int fd, error;
+
+	tmp = getname(filename);
+	fd = PTR_ERR(tmp);
+	if (!IS_ERR(tmp)) {
+		fd = get_unused_fd();
+		if (fd >= 0) {
+			struct file *f = filp_open(tmp, flags, mode);
+			error = PTR_ERR(f);
+			if (IS_ERR(f))
+				goto out_error;
+			fd_install(fd, f);
+		}
+out:
+		putname(tmp);
+	}
+	return fd;
 
+out_error:
+	put_unused_fd(fd);
+	fd = error;
+	goto out;
+}
 
 /*
  * Linux/i386 didn't use to be able to handle more than
@@ -242,7 +270,7 @@
 	unsigned int offset;
 };
 
-asmlinkage __u32
+asmlinkage long
 sys32_mmap(struct mmap_arg_struct *arg)
 {
 	struct mmap_arg_struct a;
@@ -261,22 +289,16 @@
 		if (!file)
 			return -EBADF;
 	}
+	
 	if (a.prot & PROT_READ) 
 		a.prot |= PROT_EXEC; 
 
-	a.flags |= MAP_32BIT;
-
 	mm = current->mm; 
 	down_write(&mm->mmap_sem); 
 	retval = do_mmap_pgoff(file, a.addr, a.len, a.prot, a.flags, a.offset>>PAGE_SHIFT);
 	if (file)
 		fput(file);
 
-	/* Should not happen */
-	if (retval >= 0xFFFFFFFF && (long)retval > 0) { 
-		do_munmap(mm, retval, a.len); 
-		retval = -ENOMEM; 
-	} 
 	up_write(&mm->mmap_sem); 
 
 	return retval;
@@ -284,7 +306,7 @@
 
 extern asmlinkage long sys_mprotect(unsigned long start,size_t len,unsigned long prot);
 
-asmlinkage int sys32_mprotect(unsigned long start, size_t len, unsigned long prot)
+asmlinkage long sys32_mprotect(unsigned long start, size_t len, unsigned long prot)
 {
 	if (prot & PROT_READ) 
 		prot |= PROT_EXEC; 
@@ -503,7 +525,7 @@
     struct timeval32 it_value;
 };
 
-static inline long
+static long
 get_tv32(struct timeval *o, struct timeval32 *i)
 {
 	int err = -EFAULT; 
@@ -514,7 +536,7 @@
 	return err; 
 }
 
-static inline long
+static long
 put_tv32(struct timeval32 *o, struct timeval *i)
 {
 	int err = -EFAULT;
@@ -525,7 +547,7 @@
 	return err; 
 }
 
-static inline long
+static long
 get_it32(struct itimerval *o, struct itimerval32 *i)
 {
 	int err = -EFAULT; 
@@ -538,7 +560,7 @@
 	return err;
 }
 
-static inline long
+static long
 put_it32(struct itimerval32 *o, struct itimerval *i)
 {
 	int err = -EFAULT;
@@ -589,7 +611,8 @@
 	return 0;
 
 }
-asmlinkage unsigned long 
+
+asmlinkage long 
 sys32_alarm(unsigned int seconds)
 {
 	struct itimerval it_new, it_old;
@@ -912,21 +935,23 @@
 asmlinkage long
 sys32_nanosleep(struct timespec32 *rqtp, struct timespec32 *rmtp)
 {
-	struct timespec t;
+	struct timespec t, tout;
 	int ret;
 	mm_segment_t old_fs = get_fs ();
 	
+	if (rqtp) { 
 	if (verify_area(VERIFY_READ, rqtp, sizeof(struct timespec32)) ||
 	    __get_user (t.tv_sec, &rqtp->tv_sec) ||
 	    __get_user (t.tv_nsec, &rqtp->tv_nsec))
 		return -EFAULT;
+	}
 	set_fs (KERNEL_DS);
-	ret = sys_nanosleep(&t, rmtp ? &t : NULL);
+	ret = sys_nanosleep(rqtp ? &t : NULL, rmtp ? &tout : NULL);
 	set_fs (old_fs);
 	if (rmtp && ret == -EINTR) {
 		if (verify_area(VERIFY_WRITE, rmtp, sizeof(struct timespec32)) ||
-		    __put_user (t.tv_sec, &rmtp->tv_sec) ||
-	    	    __put_user (t.tv_nsec, &rmtp->tv_nsec))
+		    __put_user (tout.tv_sec, &rmtp->tv_sec) ||
+	    	    __put_user (tout.tv_nsec, &rmtp->tv_nsec))
 			return -EFAULT;
 	}
 	return ret;
@@ -935,47 +960,66 @@
 asmlinkage ssize_t sys_readv(unsigned long,const struct iovec *,unsigned long);
 asmlinkage ssize_t sys_writev(unsigned long,const struct iovec *,unsigned long);
 
-struct iovec *
-get_iovec32(struct iovec32 *iov32, struct iovec *iov_buf, u32 count, int type)
+static struct iovec *
+get_iovec32(struct iovec32 *iov32, struct iovec *iov_buf, u32 *count, int type, int *errp)
 {
 	int i;
 	u32 buf, len;
 	struct iovec *ivp, *iov;
+	unsigned long totlen; 
 
 	/* Get the "struct iovec" from user memory */
 
-	if (!count)
+	*errp = 0; 
+	if (!*count)
 		return 0;
-	if(verify_area(VERIFY_READ, iov32, sizeof(struct iovec32)*count))
+	*errp = -EINVAL;
+	if (*count > UIO_MAXIOV)
 		return(struct iovec *)0;
-	if (count > UIO_MAXIOV)
+	*errp = -EFAULT;
+	if(verify_area(VERIFY_READ, iov32, sizeof(struct iovec32)**count))
 		return(struct iovec *)0;
-	if (count > UIO_FASTIOV) {
-		iov = kmalloc(count*sizeof(struct iovec), GFP_KERNEL);
+	if (*count > UIO_FASTIOV) {
+		*errp = -ENOMEM; 
+		iov = kmalloc(*count*sizeof(struct iovec), GFP_KERNEL);
 		if (!iov)
 			return((struct iovec *)0);
 	} else
 		iov = iov_buf;
 
 	ivp = iov;
-	for (i = 0; i < count; i++) {
-		if (__get_user(len, &iov32->iov_len) ||
-		    __get_user(buf, &iov32->iov_base)) {
-			if (iov != iov_buf)
-				kfree(iov);
-			return((struct iovec *)0);
-		}
-		if (verify_area(type, (void *)A(buf), len)) {
-			if (iov != iov_buf)
-				kfree(iov);
-			return((struct iovec *)0);
+	totlen = 0;
+	for (i = 0; i < *count; i++) {
+		*errp = __get_user(len, &iov32->iov_len) |
+		  	__get_user(buf, &iov32->iov_base);	
+		if (*errp)
+			goto error;
+		*errp = verify_area(type, (void *)A(buf), len);
+		if (*errp) { 
+			if (i > 0) { 
+				*count = i;
+				break;
+			}	
+			goto error;
 		}
+		/* SuS checks: */
+		*errp = -EINVAL; 
+		if ((int)len < 0)
+			goto error;
+		if ((totlen += len) >= 0x7fffffff)
+			goto error;			
 		ivp->iov_base = (void *)A(buf);
 		ivp->iov_len = (__kernel_size_t)len;
 		iov32++;
 		ivp++;
 	}
+	*errp = 0;
 	return(iov);
+
+error:
+	if (iov != iov_buf)
+		kfree(iov);
+	return NULL;
 }
 
 asmlinkage long
@@ -986,8 +1030,8 @@
 	int ret;
 	mm_segment_t old_fs = get_fs();
 
-	if ((iov = get_iovec32(vector, iovstack, count, VERIFY_WRITE)) == (struct iovec *)0)
-		return -EFAULT;
+	if ((iov = get_iovec32(vector, iovstack, &count, VERIFY_WRITE, &ret)) == NULL)
+		return ret;
 	set_fs(KERNEL_DS);
 	ret = sys_readv(fd, iov, count);
 	set_fs(old_fs);
@@ -1004,8 +1048,8 @@
 	int ret;
 	mm_segment_t old_fs = get_fs();
 
-	if ((iov = get_iovec32(vector, iovstack, count, VERIFY_READ)) == (struct iovec *)0)
-		return -EFAULT;
+	if ((iov = get_iovec32(vector, iovstack, &count, VERIFY_READ, &ret)) == NULL)
+		return ret;
 	set_fs(KERNEL_DS);
 	ret = sys_writev(fd, iov, count);
 	set_fs(old_fs);
@@ -1092,7 +1136,7 @@
 
 /*
  * sys_time() can be implemented in user-level using
- * sys_gettimeofday().  IA64 did this but i386 Linux did not
+ * sys_gettimeofday().  x86-64 did this but i386 Linux did not
  * so we have to implement this system call here.
  */
 asmlinkage long sys32_time(int * tloc)
@@ -1354,7 +1398,7 @@
 	return ret; 
 }
 
-int sys32_ni_syscall(int call)
+asmlinkage long sys32_ni_syscall(int call)
 { 
 	printk(KERN_INFO "IA32 syscall %d from %s not implemented\n", call,
 	       current->comm);
@@ -1572,8 +1616,8 @@
 	if (s->si_signo >= SIGRTMIN) {
 		d->si_pid = s->si_pid;
 		d->si_uid = s->si_uid;
-		/* XXX: Ouch, how to find this out??? */
-		d->si_int = s->si_int;
+		memcpy(&d->si_int, &s->si_int, 
+		       sizeof(siginfo_t) - offsetof(siginfo_t,si_int));
 	} else switch (s->si_signo) {
 	/* XXX: What about POSIX1.b timers */
 	case SIGCHLD:
@@ -1610,8 +1654,9 @@
 	if (s->si_signo >= SIGRTMIN) {
 		d->si_pid = s->si_pid;
 		d->si_uid = s->si_uid;
-		/* XXX: Ouch, how to find this out??? */
-		d->si_int = s->si_int;
+		memcpy(&d->si_int,
+		       &s->si_int,
+		       sizeof(siginfo_t) - offsetof(siginfo_t, si_int)); 
 	} else switch (s->si_signo) {
 	/* XXX: What about POSIX1.b timers */
 	case SIGCHLD:
@@ -1812,7 +1857,7 @@
 
 
 /* warning: next two assume little endian */ 
-asmlinkage ssize_t32
+asmlinkage long
 sys32_pread(unsigned int fd, char *ubuf, __kernel_size_t32 count,
 	    u32 poslo, u32 poshi)
 {
@@ -1820,7 +1865,7 @@
 			 ((loff_t)AA(poshi) << 32) | AA(poslo));
 }
 
-asmlinkage ssize_t32
+asmlinkage long
 sys32_pwrite(unsigned int fd, char *ubuf, __kernel_size_t32 count,
 	     u32 poslo, u32 poshi)
 {
@@ -1867,6 +1912,30 @@
 	return ret;
 }
 
+extern long sys_modify_ldt(int,void*,unsigned long);
+
+asmlinkage long sys32_modify_ldt(int func, void *ptr, unsigned long bytecount)
+{
+        long ret;
+        if (func == 0x1 || func == 0x11) { 
+				struct modify_ldt_ldt_s info;
+                mm_segment_t old_fs = get_fs();
+                if (bytecount != sizeof(struct modify_ldt_ldt_s))
+                        return -EINVAL;
+                if (copy_from_user(&info, ptr, sizeof(struct modify_ldt_ldt_s)))
+                        return -EFAULT;
+                /* lm bit was undefined in the 32bit ABI and programs
+                   give it random values. Force it to zero here. */
+                info.lm = 0; 
+                set_fs(KERNEL_DS);
+                ret = sys_modify_ldt(func, &info, bytecount);
+                set_fs(old_fs);
+        }  else { 
+                ret = sys_modify_ldt(func, ptr, bytecount); 
+        }
+        return ret;
+}
+
 /* Handle adjtimex compatability. */
 
 struct timex32 {
@@ -1943,41 +2012,33 @@
 	return ret;
 }
 
-
-/* common code for old and new mmaps */
-static inline long do_mmap2(
-	unsigned long addr, unsigned long len,
+asmlinkage long sys32_mmap2(unsigned long addr, unsigned long len,
 	unsigned long prot, unsigned long flags,
 	unsigned long fd, unsigned long pgoff)
 {
-	int error = -EBADF;
+	struct mm_struct *mm = current->mm;
+	unsigned long error;
 	struct file * file = NULL;
 
 	flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
 	if (!(flags & MAP_ANONYMOUS)) {
 		file = fget(fd);
 		if (!file)
-			goto out;
+			return -EBADF;
 	}
 
-	down_write(&current->mm->mmap_sem);
-	error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
-	up_write(&current->mm->mmap_sem);
+	if (prot & PROT_READ)
+		prot |= PROT_EXEC;
+
+	down_write(&mm->mmap_sem);
+	error = do_mmap_pgoff(file, addr, len, prot, flags|MAP_32BIT, pgoff);
+	up_write(&mm->mmap_sem);
 
 	if (file)
 		fput(file);
-out:
 	return error;
 }
 
-asmlinkage long sys32_mmap2(unsigned long addr, unsigned long len,
-	unsigned long prot, unsigned long flags,
-	unsigned long fd, unsigned long pgoff)
-{
-	return do_mmap2(addr, len, prot, flags, fd, pgoff);
-}
-
-
 asmlinkage long sys32_olduname(struct oldold_utsname * name)
 {
 	int error;
@@ -2024,7 +2085,7 @@
 
 extern int sys_ustat(dev_t, struct ustat *);
 
-int sys32_ustat(dev_t dev, struct ustat32 *u32p)
+asmlinkage long sys32_ustat(dev_t dev, struct ustat32 *u32p)
 {
 	struct ustat u;
 	mm_segment_t seg;
@@ -2067,21 +2128,26 @@
 	return cnt; 
 } 
 
-int sys32_execve(char *name, u32 argv, u32 envp, struct pt_regs regs)
+asmlinkage long sys32_execve(char *name, u32 argv, u32 envp, struct pt_regs regs)
 { 
 	mm_segment_t oldseg; 
-	char **buf; 
-	int na,ne;
+	char **buf = NULL; 
+	int na = 0,ne = 0;
 	int ret;
-	unsigned sz; 
+	unsigned sz = 0; 
 	
+	if (argv) {
 	na = nargs(argv, NULL); 
 	if (na < 0) 
 		return -EFAULT; 
+	} 	
+	if (envp) { 
 	ne = nargs(envp, NULL); 
 	if (ne < 0) 
 		return -EFAULT; 
+	}
 
+	if (argv || envp) { 
 	sz = (na+ne)*sizeof(void *); 
 	if (sz > PAGE_SIZE) 
 		buf = vmalloc(sz); 
@@ -2089,14 +2155,19 @@
 		buf = kmalloc(sz, GFP_KERNEL); 
 	if (!buf)
 		return -ENOMEM; 
+	} 
 	
+	if (argv) { 
 	ret = nargs(argv, buf);
 	if (ret < 0)
 		goto free;
+	}
 
+	if (envp) { 
 	ret = nargs(envp, buf + na); 
 	if (ret < 0)
 		goto free; 
+	}
 
 	name = getname(name); 
 	ret = PTR_ERR(name); 
@@ -2105,7 +2176,7 @@
 
 	oldseg = get_fs(); 
 	set_fs(KERNEL_DS);
-	ret = do_execve(name, buf, buf+na, &regs);  
+	ret = do_execve(name, argv ? buf : NULL, envp ? buf+na : NULL, &regs);  
 	set_fs(oldseg); 
 
 	if (ret == 0)
@@ -2114,19 +2185,21 @@
 	putname(name);
  
 free:
+	if (argv || envp) { 
 	if (sz > PAGE_SIZE)
 		vfree(buf); 
 	else
 		kfree(buf);
+	}
 	return ret; 
 } 
 
-asmlinkage int sys32_fork(struct pt_regs regs)
+asmlinkage long sys32_fork(struct pt_regs regs)
 {
 	return do_fork(SIGCHLD, regs.rsp, &regs, 0);
 }
 
-asmlinkage int sys32_clone(unsigned int clone_flags, unsigned int newsp, struct pt_regs regs)
+asmlinkage long sys32_clone(unsigned int clone_flags, unsigned int newsp, struct pt_regs regs)
 {
 	if (!newsp)
 		newsp = regs.rsp;
@@ -2143,7 +2216,7 @@
  * do not have enough call-clobbered registers to hold all
  * the information you need.
  */
-asmlinkage int sys32_vfork(struct pt_regs regs)
+asmlinkage long sys32_vfork(struct pt_regs regs)
 {
 	return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs.rsp, &regs, 0);
 }
@@ -2154,14 +2227,14 @@
 
 extern off_t sys_lseek (unsigned int fd, off_t offset, unsigned int origin);
 
-int sys32_lseek (unsigned int fd, int offset, unsigned int whence)
+asmlinkage long sys32_lseek (unsigned int fd, int offset, unsigned int whence)
 {
 	return sys_lseek(fd, offset, whence);
 }
 
 extern int sys_kill(pid_t pid, int sig); 
 
-int sys32_kill(int pid, int sig)
+asmlinkage long sys32_kill(int pid, int sig)
 {
 	return sys_kill(pid, sig);
 }
@@ -2306,54 +2379,6 @@
 	return err;
 }
 
-static int nfs_uud32_trans(struct nfsctl_arg *karg, struct nfsctl_arg32 *arg32)
-{
-	u32 uaddr;
-	int i;
-	int err;
-
-	memset(karg, 0, sizeof(*karg));
-	if(get_user(karg->ca_version, &arg32->ca32_version))
-		return -EFAULT;
-	karg->ca_umap.ug_ident = (char *)get_free_page(GFP_USER);
-	if(!karg->ca_umap.ug_ident)
-		return -ENOMEM;
-	err = get_user(uaddr, &arg32->ca32_umap.ug32_ident);
-	if(strncpy_from_user(karg->ca_umap.ug_ident,
-			     (char *)A(uaddr), PAGE_SIZE) <= 0)
-		return -EFAULT;
-	err |= __get_user(karg->ca_umap.ug_uidbase,
-		      &arg32->ca32_umap.ug32_uidbase);
-	err |= __get_user(karg->ca_umap.ug_uidlen,
-		      &arg32->ca32_umap.ug32_uidlen);
-	err |= __get_user(uaddr, &arg32->ca32_umap.ug32_udimap);
-	if (err)
-		return -EFAULT;
-	karg->ca_umap.ug_udimap = kmalloc((sizeof(uid_t) * karg->ca_umap.ug_uidlen),
-					  GFP_USER);
-	if(!karg->ca_umap.ug_udimap)
-		return -ENOMEM;
-	for(i = 0; i < karg->ca_umap.ug_uidlen; i++)
-		err |= __get_user(karg->ca_umap.ug_udimap[i],
-			      &(((__kernel_uid_t32 *)A(uaddr))[i]));
-	err |= __get_user(karg->ca_umap.ug_gidbase,
-		      &arg32->ca32_umap.ug32_gidbase);
-	err |= __get_user(karg->ca_umap.ug_uidlen,
-		      &arg32->ca32_umap.ug32_gidlen);
-	err |= __get_user(uaddr, &arg32->ca32_umap.ug32_gdimap);
-	if (err)
-		return -EFAULT;
-	karg->ca_umap.ug_gdimap = kmalloc((sizeof(gid_t) * karg->ca_umap.ug_uidlen),
-					  GFP_USER);
-	if(!karg->ca_umap.ug_gdimap)
-		return -ENOMEM;
-	for(i = 0; i < karg->ca_umap.ug_gidlen; i++)
-		err |= __get_user(karg->ca_umap.ug_gdimap[i],
-			      &(((__kernel_gid_t32 *)A(uaddr))[i]));
-
-	return err;
-}
-
 static int nfs_getfh32_trans(struct nfsctl_arg *karg, struct nfsctl_arg32 *arg32)
 {
 	int err;
@@ -2411,7 +2436,7 @@
 	return copy_to_user(res32, kres, sizeof(*res32));
 }
 
-int asmlinkage sys32_nfsservctl(int cmd, struct nfsctl_arg32 *arg32, union nfsctl_res32 *res32)
+long asmlinkage sys32_nfsservctl(int cmd, struct nfsctl_arg32 *arg32, union nfsctl_res32 *res32)
 {
 	struct nfsctl_arg *karg = NULL;
 	union nfsctl_res *kres = NULL;
@@ -2442,10 +2467,6 @@
 	case NFSCTL_UNEXPORT:
 		err = nfs_exp32_trans(karg, arg32);
 		break;
-	/* This one is unimplemented, be we're ready for it. */
-	case NFSCTL_UGIDUPDATE:
-		err = nfs_uud32_trans(karg, arg32);
-		break;
 	case NFSCTL_GETFH:
 		err = nfs_getfh32_trans(karg, arg32);
 		break;
@@ -2492,13 +2513,13 @@
 }
 #else /* !NFSD */
 extern asmlinkage long sys_ni_syscall(void);
-int asmlinkage sys32_nfsservctl(int cmd, void *notused, void *notused2)
+long asmlinkage sys32_nfsservctl(int cmd, void *notused, void *notused2)
 {
 	return sys_ni_syscall();
 }
 #endif
 
-int sys32_module_warning(void)
+long sys32_module_warning(void)
 { 
 	static long warn_time = -(60*HZ); 
 	if (time_before(warn_time + 60*HZ,jiffies) && strcmp(current->comm,"klogd")) { 
@@ -2509,15 +2530,27 @@
 	return -ENOSYS ;
 } 
 
+long sys32_vm86_warning(void)
+{ 
+	static long warn_time = -(60*HZ); 
+	if (time_before(warn_time + 60*HZ,jiffies)) { 
+		printk(KERN_INFO "%s: vm86 mode not supported on 64 bit kernel\n",
+		       current->comm);
+		warn_time = jiffies;
+	} 
+	return -ENOSYS ;
+} 
+
+/* This only triggers an i686 uname */
 struct exec_domain ia32_exec_domain = { 
-	name: "linux/x86",
+	name: "linux/uname-i686",
 	pers_low: PER_LINUX32,
 	pers_high: PER_LINUX32,
 };      
 
 static int __init ia32_init (void)
 {
-	printk("IA32 emulation $Id: sys_ia32.c,v 1.42 2002/09/17 15:23:41 ak Exp $\n");  
+	printk("IA32 emulation $Id: sys_ia32.c,v 1.54 2003/03/24 09:28:26 ak Exp $\n");  
 	ia32_exec_domain.signal_map = default_exec_domain.signal_map;
 	ia32_exec_domain.signal_invmap = default_exec_domain.signal_invmap;
 	register_exec_domain(&ia32_exec_domain);

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)