patch-1.3.91 linux/arch/i386/kernel/signal.c

Next file: linux/arch/i386/kernel/traps.c
Previous file: linux/arch/alpha/kernel/signal.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v1.3.90/linux/arch/i386/kernel/signal.c linux/arch/i386/kernel/signal.c
@@ -4,6 +4,8 @@
  *  Copyright (C) 1991, 1992  Linus Torvalds
  */
 
+#include <linux/config.h>
+
 #include <linux/sched.h>
 #include <linux/mm.h>
 #include <linux/kernel.h>
@@ -42,6 +44,42 @@
 }
 
 /*
+ * FIXME. We don't currently restore emulator state
+ */
+#define restore_i387_soft(x) do { } while (0)
+
+static inline void restore_i387_hard(struct _fpstate *buf)
+{
+#ifdef __SMP__
+	if (current->flags & PF_USEDFPU) {
+		stts();
+	}
+#else
+	if (current == last_task_used_math) {
+		last_task_used_math = NULL;
+		stts();
+	}
+#endif
+	current->used_math = 1;
+	current->flags &= PF_USEDFPU;
+	memcpy_fromfs(&current->tss.i387.hard, buf, sizeof(*buf));
+}
+
+static void restore_i387(struct _fpstate *buf)
+{
+#ifndef CONFIG_MATH_EMULATION
+	restore_i387_hard(buf);
+#else
+	if (hard_math) {
+		restore_i387_hard(buf);
+		return;
+	}
+	restore_i387_soft(buf);
+#endif	
+}
+	
+
+/*
  * This sets regs->esp even though we don't actually use sigstacks yet..
  */
 asmlinkage int sys_sigreturn(unsigned long __unused)
@@ -73,12 +111,56 @@
 	regs->eflags &= ~0x40DD5;
 	regs->eflags |= context.eflags & 0x40DD5;
 	regs->orig_eax = -1;		/* disable syscall checks */
+	if (context.fpstate) {
+		struct _fpstate * buf = context.fpstate;
+		if (verify_area(VERIFY_READ, buf, sizeof(*buf)))
+			goto badframe;
+		restore_i387(buf);
+	}
 	return context.eax;
 badframe:
 	do_exit(SIGSEGV);
 }
 
 /*
+ * FIXME. We currently don't save 387 state if we use emulation
+ */
+#define save_i387_soft(x) NULL
+
+static inline struct _fpstate * save_i387_hard(struct _fpstate * buf)
+{
+#ifdef __SMP__
+	if (current->flags & PF_USEDFPU) {
+		__asm__ __volatile__("fnsave %0":"=m" (current->tss.i387.hard));
+		stts();
+		current->flags &= PF_USEDFPU;
+	}
+#else
+	if (current == last_task_used_math) {
+		__asm__ __volatile__("fnsave %0":"=m" (current->tss.i387.hard));
+		last_task_used_math = NULL;
+		__asm__ __volatile__("fwait");	/* not needed on 486+ */
+		stts();
+	}
+#endif
+	current->tss.i387.hard.status = current->tss.i387.hard.swd;
+	memcpy_tofs(buf, &current->tss.i387.hard, sizeof(*buf));
+	current->used_math = 0;
+	return buf;
+}
+
+static struct _fpstate * save_i387(struct _fpstate * buf)
+{
+#ifndef CONFIG_MATH_EMULATION
+	return save_i387_hard(buf);
+#else
+	if (hard_math)
+		return save_i387_hard(buf);
+	return save_i387_soft(buf);
+#endif
+}
+
+/*
  * Set up a signal frame... Make the stack look the way iBCS2 expects
  * it to look.
  */
@@ -91,8 +173,8 @@
 	frame = (unsigned long *) regs->esp;
 	if (regs->ss != USER_DS && sa->sa_restorer)
 		frame = (unsigned long *) sa->sa_restorer;
-	frame -= 32;
-	if (verify_area(VERIFY_WRITE,frame,32*4))
+	frame -= 64;
+	if (verify_area(VERIFY_WRITE,frame,64*4))
 		do_exit(SIGSEGV);
 
 /* set up the "normal" stack seen by the signal handler (iBCS2) */
@@ -122,7 +204,7 @@
 	put_user(regs->eflags, frame+18);
 	put_user(regs->esp, frame+19);
 	put_user(regs->ss, frame+20);
-	put_user(NULL,frame+21);
+	put_user(save_i387((struct _fpstate *)(frame+32)),frame+21);
 /* non-iBCS2 extensions.. */
 	put_user(oldmask, frame+22);
 	put_user(current->tss.cr2, frame+23);

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov with Sam's (original) version
of this