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

Next file: linux-2.4.21/arch/x86_64/ia32/ia32entry.S
Previous file: linux-2.4.21/arch/x86_64/ia32/ia32_ioctl.c
Back to the patch index
Back to the overall index

diff -urN linux-2.4.20/arch/x86_64/ia32/ia32_signal.c linux-2.4.21/arch/x86_64/ia32/ia32_signal.c
@@ -7,7 +7,7 @@
  *  2000-06-20  Pentium III FXSR, SSE support by Gareth Hughes
  *  2000-12-*   x86-64 compatibility mode signal handling by Andi Kleen
  * 
- *  $Id: ia32_signal.c,v 1.22 2002/07/29 10:34:03 ak Exp $
+ *  $Id: ia32_signal.c,v 1.31 2003/03/12 08:22:12 ak Exp $
  */
 
 #include <linux/sched.h>
@@ -33,6 +33,7 @@
 #include <asm/fpu32.h>
 
 #define ptr_to_u32(x) ((u32)(u64)(x))	/* avoid gcc warning */ 
+#define u32_to_ptr(x) ((void *)(u64)(x))
 
 #define DEBUG_SIG 0
 
@@ -45,9 +46,16 @@
 {
 	if (!access_ok (VERIFY_WRITE, to, sizeof(siginfo_t)))
 		return -EFAULT;
-	if (from->si_code < 0)
-		return __copy_to_user(to, from, sizeof(siginfo_t));
-	else {
+	if (from->si_code < 0) { 
+		/* the only field that's different is the alignment
+		   of the pointer in sigval_t. Move that 4 bytes down including
+		   padding. */
+		memmove(&((siginfo_t32 *)&from)->si_int,
+			&from->si_int, 
+			sizeof(siginfo_t) - offsetof(siginfo_t, si_int));
+		/* last 4 bytes stay the same */
+		return __copy_to_user(to, from, sizeof(siginfo_t32));
+	} else {
 		int err;
 
 		/* If you change siginfo_t structure, please be sure
@@ -57,7 +65,7 @@
 		   3 ints plus the relevant union member.  */
 		err = __put_user(from->si_signo, &to->si_signo);
 		err |= __put_user(from->si_errno, &to->si_errno);
-		err |= __put_user((short)from->si_code, &to->si_code);
+		err |= __put_user(from->si_code, &to->si_code);
 		/* First 32bits of unions are always present.  */
 		err |= __put_user(from->si_pid, &to->si_pid);
 		switch (from->si_code >> 16) {
@@ -76,7 +84,7 @@
 	}
 }
 
-asmlinkage int
+asmlinkage long
 sys32_sigsuspend(int history0, int history1, old_sigset_t mask, struct pt_regs regs)
 {
 	sigset_t saveset;
@@ -97,27 +105,32 @@
 	}
 }
 
-asmlinkage int
+asmlinkage long
 sys32_sigaltstack(const stack_ia32_t *uss_ptr, stack_ia32_t *uoss_ptr, 
 				  struct pt_regs regs)
 {
 	stack_t uss,uoss; 
 	int ret;
 	mm_segment_t seg; 
+	if (uss_ptr) {
+		u32 val32;
+		memset(&uss, 0, sizeof(stack_t));
 	if (!access_ok(VERIFY_READ,uss_ptr,sizeof(stack_ia32_t)) ||
-	    __get_user(ptr_to_u32(uss.ss_sp), &uss_ptr->ss_sp) ||
-	    __get_user((u32)uss.ss_flags, &uss_ptr->ss_flags) ||
-	    __get_user((u32)uss.ss_size, &uss_ptr->ss_size))
+		    __get_user(val32, &uss_ptr->ss_sp) ||
+		    __get_user(uss.ss_flags, &uss_ptr->ss_flags) ||
+		    __get_user(uss.ss_size, &uss_ptr->ss_size))
 		return -EFAULT;
+		uss.ss_sp = u32_to_ptr(val32);	
+	}
 	seg = get_fs(); 
 	set_fs(KERNEL_DS); 
-	ret = do_sigaltstack(&uss, &uoss, regs.rsp);
+	ret = do_sigaltstack(uss_ptr ? &uss : NULL, &uoss, regs.rsp);
 	set_fs(seg); 
 	if (ret >= 0 && uoss_ptr)  {
-		if (!access_ok(VERIFY_WRITE,uss_ptr,sizeof(stack_ia32_t)) ||
-		    __put_user(ptr_to_u32(uss.ss_sp), &uss_ptr->ss_sp) ||
-		    __put_user((u32)uss.ss_flags, &uss_ptr->ss_flags) ||
-		    __put_user((u32)uss.ss_size, &uss_ptr->ss_size))
+		if (!access_ok(VERIFY_WRITE,uoss_ptr,sizeof(stack_ia32_t)) ||
+		    __put_user(ptr_to_u32(uoss.ss_sp), &uoss_ptr->ss_sp) ||
+		    __put_user((u32)uoss.ss_flags, &uoss_ptr->ss_flags) ||
+		    __put_user((u32)uoss.ss_size, &uoss_ptr->ss_size))
 			ret = -EFAULT;
 	} 	
 	return ret;	
@@ -173,31 +186,29 @@
 	  if (pre != cur) loadsegment(seg,pre); }
 
 	/* Reload fs and gs if they have changed in the signal handler.
-	   This does not handle long fs/gs base changes in the handler, but 
-	   does not clobber them at least in the normal case. */ 
+	   This does not handle long gs base changes in the handler, but 
+	   does not clobber it at least in the normal case. */ 
 	
 	{
 		unsigned short gs; 
+		unsigned int curgs;
 		err |= __get_user(gs, &sc->gs);
-		load_gs_index(gs); 
+		asm volatile("movl %%gs,%0" : "=r" (curgs)); 		
+		if (gs != curgs) 
+			load_gs_index(gs | 3); 
 	} 
-	RELOAD_SEG(fs,0);
-	RELOAD_SEG(ds,0);
-	RELOAD_SEG(es,0);
+	RELOAD_SEG(fs,3);
+	RELOAD_SEG(ds,3);
+	RELOAD_SEG(es,3);
 
 	COPY(di); COPY(si); COPY(bp); COPY(sp); COPY(bx);
 	COPY(dx); COPY(cx); COPY(ip);
 	/* Don't touch extended registers */ 
 
-#if 1	/* not ready for this yet */ 
 	err |= __get_user(regs->cs, &sc->cs); 
-	regs->cs |= 2; 	
+	regs->cs |= 3; 	
 	err |= __get_user(regs->ss, &sc->ss); 
-	regs->ss |= 2; 
-#else
-	regs->cs = __USER32_CS;
-	regs->ss = __USER32_DS; 
-#endif
+	regs->ss |= 3; 
 	
 	{
 		unsigned int tmpflags;
@@ -213,7 +224,7 @@
 		buf = (struct _fpstate_ia32 *) (u64)tmp;
 		if (buf) {
 			if (verify_area(VERIFY_READ, buf, sizeof(*buf)))
-				goto badframe;
+				return 1;
 			err |= restore_i387_ia32(current, buf, 0);
 		}
 	}
@@ -224,12 +235,9 @@
 		*peax = tmp;
 	}
 	return err;
-
-badframe:
-	return 1;
 }
 
-asmlinkage int sys32_sigreturn(struct pt_regs regs)
+asmlinkage long sys32_sigreturn(struct pt_regs regs)
 {
 	struct sigframe *frame = (struct sigframe *)(regs.rsp - 8);
 	sigset_t set;
@@ -258,7 +266,7 @@
 	return 0;
 }	
 
-asmlinkage int sys32_rt_sigreturn(struct pt_regs regs)
+asmlinkage long sys32_rt_sigreturn(struct pt_regs regs)
 {
 	struct rt_sigframe *frame = (struct rt_sigframe *)(regs.rsp - 4);
 	sigset_t set;
@@ -336,8 +344,12 @@
 	tmp = save_i387_ia32(current, fpstate, regs, 0);
 	if (tmp < 0)
 	  err = -EFAULT;
-	else
+	else { 
+		/* trigger finit in signal handler */
+		current->used_math = 0;
+		stts();
 	  err |= __put_user((u32)(u64)(tmp ? fpstate : NULL), &sc->fpstate);
+	} 
 
 	/* non-iBCS2 extensions.. */
 	err |= __put_user(mask, &sc->oldmask);

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