patch-2.4.20 linux-2.4.20/arch/mips64/kernel/signal.c

Next file: linux-2.4.20/arch/mips64/kernel/signal32.c
Previous file: linux-2.4.20/arch/mips64/kernel/setup.c
Back to the patch index
Back to the overall index

diff -urN linux-2.4.19/arch/mips64/kernel/signal.c linux-2.4.20/arch/mips64/kernel/signal.c
@@ -32,8 +32,8 @@
 #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
 
 extern asmlinkage int do_signal(sigset_t *oldset, struct pt_regs *regs);
-extern asmlinkage int save_fp_context(struct sigcontext *sc);
-extern asmlinkage int restore_fp_context(struct sigcontext *sc);
+extern asmlinkage int (*save_fp_context)(struct sigcontext *sc);
+extern asmlinkage int (*restore_fp_context)(struct sigcontext *sc);
 
 extern asmlinkage void syscall_trace(void);
 
@@ -72,35 +72,6 @@
 	}
 }
 
-static inline int store_fp_context(struct sigcontext *sc)
-{
-	unsigned int fcr0;
-	int err = 0;
-
-	err |= __copy_to_user(&sc->sc_fpregs[0], 
-		&current->thread.fpu.hard.fp_regs[0], NUM_FPU_REGS * 
-						sizeof(unsigned long));
-	err |= __copy_to_user(&sc->sc_fpc_csr, &current->thread.fpu.hard.control,
-						sizeof(unsigned int));
-	__asm__ __volatile__("cfc1 %0, $0\n\t" : "=r" (fcr0));
-	err |= __copy_to_user(&sc->sc_fpc_eir, &fcr0, sizeof(unsigned int));
-
-	return err;
-}
-
-static inline int refill_fp_context(struct sigcontext *sc)
-{
-	int err = 0;
-
-	if (verify_area(VERIFY_READ, sc, sizeof(*sc)))
-		return -EFAULT;
-	err |= __copy_from_user(&current->thread.fpu.hard.fp_regs[0], 
-			&sc->sc_fpregs[0], NUM_FPU_REGS * sizeof(unsigned long));
-	err |= __copy_from_user(&current->thread.fpu.hard.control, &sc->sc_fpc_csr,
-							sizeof(unsigned int));
-	return err;
-}
-
 /*
  * Atomically swap in the new signal mask, and wait for a signal.
  */
@@ -165,7 +136,7 @@
 	}
 }
 
-asmlinkage int 
+asmlinkage int
 sys_sigaction(int sig, const struct sigaction *act, struct sigaction *oact)
 {
 	struct k_sigaction new_ka, old_ka;
@@ -214,6 +185,54 @@
 	return do_sigaltstack(uss, uoss, usp);
 }
 
+static inline int restore_thread_fp_context(struct sigcontext *sc)
+{
+     u64 *pfreg = &current->thread.fpu.soft.regs[0];
+     int err = 0;
+
+     /*
+      * Copy all 32 64-bit values.
+      */
+
+#define restore_fpr(i)                          \
+     do { err |= __get_user(pfreg[i], &sc->sc_fpregs[i]); } while(0)
+
+     restore_fpr( 0); restore_fpr( 1); restore_fpr( 2); restore_fpr( 3);
+     restore_fpr( 4); restore_fpr( 5); restore_fpr( 6); restore_fpr( 7);
+     restore_fpr( 8); restore_fpr( 9); restore_fpr(10); restore_fpr(11);
+     restore_fpr(12); restore_fpr(13); restore_fpr(14); restore_fpr(15);
+     restore_fpr(16); restore_fpr(17); restore_fpr(18); restore_fpr(19);
+     restore_fpr(20); restore_fpr(21); restore_fpr(22); restore_fpr(23);
+     restore_fpr(24); restore_fpr(25); restore_fpr(26); restore_fpr(27);
+     restore_fpr(28); restore_fpr(29); restore_fpr(30); restore_fpr(31);
+
+     err |= __get_user(current->thread.fpu.soft.sr, &sc->sc_fpc_csr);
+
+     return err;
+}
+
+static inline int save_thread_fp_context(struct sigcontext *sc)
+{
+     u64 *pfreg = &current->thread.fpu.soft.regs[0];
+     int err = 0;
+
+#define save_fpr(i)                                     \
+     do { err |= __put_user(pfreg[i], &sc->sc_fpregs[i]); } while(0)
+
+     save_fpr( 0); save_fpr( 1); save_fpr( 2); save_fpr( 3);
+     save_fpr( 4); save_fpr( 5); save_fpr( 6); save_fpr( 7);
+     save_fpr( 8); save_fpr( 9); save_fpr(10); save_fpr(11);
+     save_fpr(12); save_fpr(13); save_fpr(14); save_fpr(15);
+     save_fpr(16); save_fpr(17); save_fpr(18); save_fpr(19);
+     save_fpr(20); save_fpr(21); save_fpr(22); save_fpr(23);
+     save_fpr(24); save_fpr(25); save_fpr(26); save_fpr(27);
+     save_fpr(28); save_fpr(29); save_fpr(30); save_fpr(31);
+
+     err |= __put_user(current->thread.fpu.soft.sr, &sc->sc_fpc_csr);
+
+     return err;
+}
+
 asmlinkage int
 restore_sigcontext(struct pt_regs *regs, struct sigcontext *sc)
 {
@@ -241,15 +260,24 @@
 #undef restore_gp_reg
 
 	err |= __get_user(owned_fp, &sc->sc_ownedfp);
+	err |= __get_user(current->used_math, &sc->sc_used_math);
+
 	if (owned_fp) {
-		if (IS_FPU_OWNER()) {
-			CLEAR_FPU_OWNER();
-			regs->cp0_status &= ~ST0_CU1;
-		}
-		current->used_math = 1;
-		err |= refill_fp_context(sc);
+		err |= restore_fp_context(sc);
+		goto out;
+	}
+
+	if (IS_FPU_OWNER()) {
+		/* Signal handler acquired FPU - give it back */
+		CLEAR_FPU_OWNER();
+		regs->cp0_status &= ~ST0_CU1;
+	}
+	if (current->used_math) {
+		/* Undo possible contamination of thread state */
+		err |= restore_thread_fp_context(sc);
 	}
 
+out:
 	return err;
 }
 
@@ -347,9 +375,11 @@
 static int inline setup_sigcontext(struct pt_regs *regs,
 				   struct sigcontext *sc)
 {
+	int owned_fp;
 	int err = 0;
 
 	err |= __put_user(regs->cp0_epc, &sc->sc_pc);
+	err |= __put_user(regs->cp0_status, &sc->sc_status);
 
 #define save_gp_reg(i) do {						\
 	err |= __put_user(regs->regs[i], &sc->sc_regs[i]);		\
@@ -370,20 +400,27 @@
 	err |= __put_user(regs->cp0_cause, &sc->sc_cause);
 	err |= __put_user(regs->cp0_badvaddr, &sc->sc_badvaddr);
 
-	if (current->used_math) {	/* fp is active.  */
-		if (IS_FPU_OWNER()) {
-			lazy_fpu_switch(current, 0);
-			CLEAR_FPU_OWNER();
-			regs->cp0_status &= ~ST0_CU1;
-		}
-		err |= __put_user(1, &sc->sc_ownedfp);
-		err |= store_fp_context(sc);
-		current->used_math = 0;
-	} else {
-		err |= __put_user(0, &sc->sc_ownedfp);
+	owned_fp = IS_FPU_OWNER();
+	err |= __put_user(owned_fp, &sc->sc_ownedfp);
+	err |= __put_user(current->used_math, &sc->sc_used_math);
+
+	if (!current->used_math)
+		goto out;
+
+	/* There exists FP thread state that may be trashed by signal */
+	if (owned_fp) {
+		/* fp is active.  Save context from FPU */
+		err |= save_fp_context(sc);
+		goto out;
 	}
-	err |= __put_user(regs->cp0_status, &sc->sc_status);
 
+	/*
+	 * Someone else has FPU.
+	 * Copy Thread context into signal context
+	 */
+	err |= save_thread_fp_context(sc);
+
+out:
 	return err;
 }
 
@@ -398,6 +435,13 @@
 	/* Default to using normal stack */
 	sp = regs->regs[29];
 
+	/*
+ 	 * FPU emulator may have it's own trampoline active just
+ 	 * above the user stack, 16-bytes before the next lowest
+ 	 * 16 byte boundary.  Try to avoid trashing it.
+ 	 */
+ 	sp -= 32;
+
 	/* This is the X/Open sanctioned signal stack switching.  */
 	if ((ka->sa.sa_flags & SA_ONSTACK) && ! on_sig_stack(sp))
 		sp = current->sas_ss_sp + current->sas_ss_size;
@@ -456,8 +500,9 @@
 	regs->cp0_epc = regs->regs[25] = (unsigned long) ka->sa.sa_handler;
 
 #if DEBUG_SIG
-	printk("SIG deliver (%s:%d): sp=0x%p pc=0x%p ra=0x%p\n",
-	       current->comm, current->pid, frame, regs->cp0_epc, frame->code);
+	printk("SIG deliver (%s:%d): sp=0x%p pc=0x%lx ra=0x%p\n",
+	       current->comm, current->pid,
+	       frame, regs->cp0_epc, frame->sf_code);
 #endif
         return;
 
@@ -532,8 +577,9 @@
 	regs->cp0_epc = regs->regs[25] = (unsigned long) ka->sa.sa_handler;
 
 #if DEBUG_SIG
-	printk("SIG deliver (%s:%d): sp=0x%p pc=0x%p ra=0x%p\n",
-	       current->comm, current->pid, frame, regs->cp0_epc, frame->code);
+	printk("SIG deliver (%s:%d): sp=0x%p pc=0x%lx ra=0x%p\n",
+	       current->comm, current->pid,
+	       frame, regs->cp0_epc, frame->rs_code);
 #endif
 	return;
 
@@ -661,7 +707,7 @@
 				continue;
 
 			switch (signr) {
-			case SIGCONT: case SIGCHLD: case SIGWINCH:
+			case SIGCONT: case SIGCHLD: case SIGWINCH: case SIGURG:
 				continue;
 
 			case SIGTSTP: case SIGTTIN: case SIGTTOU:

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