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

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

diff -urN linux-2.4.19/arch/mips64/kernel/signal32.c linux-2.4.20/arch/mips64/kernel/signal32.c
@@ -31,8 +31,8 @@
 #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
 
 extern asmlinkage int do_signal32(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);
 
@@ -63,38 +63,6 @@
 	int ss_flags;
 } stack32_t;
 
-
-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;
-}
-
 extern void __put_sigset_unknown_nsig(void);
 extern void __get_sigset_unknown_nsig(void);
 
@@ -289,6 +257,54 @@
 	return ret;
 }
 
+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;
+}
+
 static asmlinkage int restore_sigcontext(struct pt_regs *regs,
 					 struct sigcontext *sc)
 {
@@ -316,15 +332,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;
 }
 
@@ -335,13 +360,55 @@
 	sigset_t sf_mask;
 };
 
-struct rt_sigframe {
+struct rt_sigframe32 {
 	u32 rs_ass[4];			/* argument save space for o32 */
 	u32 rs_code[2];			/* signal trampoline */
-	struct siginfo rs_info;
+	struct siginfo32 rs_info;
 	struct ucontext rs_uc;
 };
 
+static int copy_siginfo_to_user32(siginfo_t32 *to, siginfo_t *from)
+{
+	int err;
+
+	if (!access_ok (VERIFY_WRITE, to, sizeof(siginfo_t32)))
+		return -EFAULT;
+
+	/* If you change siginfo_t structure, please be sure
+	   this code is fixed accordingly.
+	   It should never copy any pad contained in the structure
+	   to avoid security leaks, but must copy the generic
+	   3 ints plus the relevant union member.
+	   This routine must convert siginfo from 64bit to 32bit as well
+	   at the same time.  */
+	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);
+	if (from->si_code < 0)
+		err |= __copy_to_user(&to->_sifields._pad, &from->_sifields._pad, SI_PAD_SIZE);
+	else {
+		switch (from->si_code >> 16) {
+		case __SI_CHLD >> 16:
+			err |= __put_user(from->si_utime, &to->si_utime);
+			err |= __put_user(from->si_stime, &to->si_stime);
+			err |= __put_user(from->si_status, &to->si_status);
+		default:
+			err |= __put_user(from->si_pid, &to->si_pid);
+			err |= __put_user(from->si_uid, &to->si_uid);
+			break;
+		case __SI_FAULT >> 16:
+			err |= __put_user((long)from->si_addr, &to->si_addr);
+			break;
+		case __SI_POLL >> 16:
+			err |= __put_user(from->si_band, &to->si_band);
+			err |= __put_user(from->si_fd, &to->si_fd);
+			break;
+		/* case __SI_RT: This is not generated by the kernel as of now.  */
+		}
+	}
+	return err;
+}
+
 asmlinkage void sys32_sigreturn(abi64_no_regargs, struct pt_regs regs)
 {
 	struct sigframe *frame;
@@ -369,7 +436,7 @@
 		syscall_trace();
 	__asm__ __volatile__(
 		"move\t$29, %0\n\t"
-		"j\to32_ret_from_sys_call"
+		"j\tret_from_sys_call"
 		:/* no outputs */
 		:"r" (&regs));
 	/* Unreached */
@@ -380,11 +447,11 @@
 
 asmlinkage void sys32_rt_sigreturn(abi64_no_regargs, struct pt_regs regs)
 {
-	struct rt_sigframe *frame;
+	struct rt_sigframe32 *frame;
 	sigset_t set;
 	stack_t st;
 
-	frame = (struct rt_sigframe *) regs.regs[29];
+	frame = (struct rt_sigframe32 *) regs.regs[29];
 	if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
 		goto badframe;
 	if (__copy_from_user(&set, &frame->rs_uc.uc_sigmask, sizeof(set)))
@@ -410,7 +477,7 @@
 	 */
 	__asm__ __volatile__(
 		"move\t$29, %0\n\t"
-		"j\to32_ret_from_sys_call"
+		"j\tret_from_sys_call"
 		:/* no outputs */
 		:"r" (&regs));
 	/* Unreached */
@@ -422,9 +489,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) {						\
 	err |= __put_user(regs->regs[i], &sc->sc_regs[i]);		\
@@ -445,20 +514,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;
 }
 
@@ -473,6 +549,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;
@@ -547,7 +630,7 @@
 				  struct pt_regs *regs, int signr,
 				  sigset_t *set, siginfo_t *info)
 {
-	struct rt_sigframe *frame;
+	struct rt_sigframe32 *frame;
 	int err = 0;
 
 	frame = get_sigframe(ka, regs, sizeof(*frame));
@@ -572,8 +655,8 @@
 		flush_cache_sigtramp((unsigned long) frame->rs_code);
 	}
 
-	/* Create siginfo.  */
-	err |= __copy_to_user(&frame->rs_info, info, sizeof(*info));
+	/* Convert (siginfo_t -> siginfo_t32) and copy to user. */
+	err |= copy_siginfo_to_user32(&frame->rs_info, info);
 
 	/* Create the ucontext.  */
 	err |= __put_user(0, &frame->rs_uc.uc_flags);
@@ -598,7 +681,7 @@
 	 *   a2 = pointer to ucontext
 	 *
 	 * $25 and c0_epc point to the signal handler, $29 points to
-	 * the struct rt_sigframe.
+	 * the struct rt_sigframe32.
 	 */
 	regs->regs[ 4] = signr;
 	regs->regs[ 5] = (unsigned long) &frame->rs_info;
@@ -784,7 +867,7 @@
 extern asmlinkage int sys_sigprocmask(int how, old_sigset_t *set,
 						old_sigset_t *oset);
 
-asmlinkage int sys32_sigprocmask(int how, old_sigset_t32 *set, 
+asmlinkage int sys32_sigprocmask(int how, old_sigset_t32 *set,
 				 old_sigset_t32 *oset)
 {
 	old_sigset_t s;
@@ -878,7 +961,7 @@
 
 	if (set && get_sigset(&new_set, set))
 		return -EFAULT;
-	
+
 	set_fs (KERNEL_DS);
 	ret = sys_rt_sigprocmask(how, set ? &new_set : NULL,
 				 oset ? &old_set : NULL, sigsetsize);

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