Subject: [PATCH] o32 sigcontext fixes for mips64

From: "Kip Walker" <[email-masked]>
Date: Mon, 14 Jul 2003 09:48:58 -0700

This 2.4 patch fixes some issues with the signal context save and
restore for o32 binaries running on the mips64 kernel.

Any comments?

Kip

Index: include/asm-mips64/fpu.h
===================================================================
RCS file: /home/cvs/linux/include/asm-mips64/fpu.h,v
retrieving revision 1.1.2.4
diff -u -r1.1.2.4 fpu.h
--- include/asm-mips64/fpu.h	9 Apr 2003 00:46:24 -0000	1.1.2.4
+++ include/asm-mips64/fpu.h	14 Jul 2003 16:45:05 -0000
@@ -21,9 +21,13 @@
 #include <asm/current.h>
 
 struct sigcontext;
+struct sigcontext32;
 
 extern asmlinkage int (*save_fp_context)(struct sigcontext *sc);
 extern asmlinkage int (*restore_fp_context)(struct sigcontext *sc);
+
+extern asmlinkage int (*save_fp_context32)(struct sigcontext32 *sc);
+extern asmlinkage int (*restore_fp_context32)(struct sigcontext32 *sc);
 
 extern void fpu_emulator_init_fpu(void);
 extern void _init_fpu(void);
Index: include/asm-mips64/sigcontext.h
===================================================================
RCS file: /home/cvs/linux/include/asm-mips64/sigcontext.h,v
retrieving revision 1.3.2.2
diff -u -r1.3.2.2 sigcontext.h
--- include/asm-mips64/sigcontext.h	4 Nov 2002 19:39:56 -0000	1.3.2.2
+++ include/asm-mips64/sigcontext.h	14 Jul 2003 16:45:05 -0000
@@ -27,4 +27,24 @@
 	unsigned int       sc_badvaddr;
 };
 
+struct sigcontext32 {
+	u32 sc_regmask;		/* Unused */
+	u32 sc_status;
+	u64 sc_pc;
+	u64 sc_regs[32];
+	u64 sc_fpregs[32];
+	u32 sc_ownedfp;		/* Unused */
+	u32 sc_fpc_csr;
+	u32 sc_fpc_eir;		/* Unused */
+	u32 sc_used_math;
+	u32 sc_ssflags;		/* Unused */
+	u64 sc_mdhi;
+	u64 sc_mdlo;
+
+	u32 sc_cause;		/* Unused */
+	u32 sc_badvaddr;	/* Unused */
+
+	u32 sc_sigset[4];	/* kernel's sigset_t */
+};
+
 #endif /* _ASM_SIGCONTEXT_H */
Index: arch/mips/tools/offset.c
===================================================================
RCS file: /home/cvs/linux/arch/mips/tools/Attic/offset.c,v
retrieving revision 1.16.4.6
diff -u -r1.16.4.6 offset.c
--- arch/mips/tools/offset.c	4 Nov 2002 19:39:56 -0000	1.16.4.6
+++ arch/mips/tools/offset.c	14 Jul 2003 16:45:05 -0000
@@ -8,6 +8,7 @@
  * Kevin Kissell, [email-masked] and Carsten Langgaard, [email-masked]
  * Copyright (C) 2000 MIPS Technologies, Inc.
  */
+#include <linux/config.h>
 #include <linux/types.h>
 #include <linux/sched.h>
 #include <linux/mm.h>
@@ -156,6 +157,14 @@
 	offset("#define SC_CAUSE      ", struct sigcontext, sc_cause);
 	offset("#define SC_BADVADDR   ", struct sigcontext, sc_badvaddr);
 	linefeed;
+
+#ifdef CONFIG_MIPS64
+	text("/* Linux 32-bit sigcontext offsets. */");
+	offset("#define SC32_FPREGS     ", struct sigcontext32, sc_fpregs);
+	offset("#define SC32_FPC_CSR    ", struct sigcontext32, sc_fpc_csr);
+	offset("#define SC32_FPC_EIR    ", struct sigcontext32, sc_fpc_eir);
+	linefeed;
+#endif
 }
 
 void output_signal_defined(void)
Index: arch/mips64/kernel/signal32.c
===================================================================
RCS file: /home/cvs/linux/arch/mips64/kernel/signal32.c,v
retrieving revision 1.20.2.12
diff -u -r1.20.2.12 signal32.c
--- arch/mips64/kernel/signal32.c	12 Mar 2003 15:44:06 -0000	1.20.2.12
+++ arch/mips64/kernel/signal32.c	14 Jul 2003 16:45:06 -0000
@@ -60,6 +60,14 @@
 	int ss_flags;
 } stack32_t;
 
+struct ucontext32 {
+	u32                 uc_flags;
+	s32                 uc_link;
+	stack32_t           uc_stack;
+	struct sigcontext32 uc_mcontext;
+	sigset_t32          uc_sigmask;   /* mask last for extensibility */
+};
+
 extern void __put_sigset_unknown_nsig(void);
 extern void __get_sigset_unknown_nsig(void);
 
@@ -250,8 +258,8 @@
 	return ret;
 }
 
-static asmlinkage int restore_sigcontext(struct pt_regs *regs,
-					 struct sigcontext *sc)
+static asmlinkage int restore_sigcontext32(struct pt_regs *regs,
+					   struct sigcontext32 *sc)
 {
 	int err = 0;
 
@@ -280,7 +288,7 @@
 	if (current->used_math) {
 		/* restore fpu context if we have used it before */
 		own_fpu();
-		err |= restore_fp_context(sc);
+		err |= restore_fp_context32(sc);
 	} else {
 		/* signal handler may have used FPU.  Give it up. */
 		loose_fpu();
@@ -292,7 +300,7 @@
 struct sigframe {
 	u32 sf_ass[4];			/* argument save space for o32 */
 	u32 sf_code[2];			/* signal trampoline */
-	struct sigcontext sf_sc;
+	struct sigcontext32 sf_sc;
 	sigset_t sf_mask;
 };
 
@@ -300,7 +308,7 @@
 	u32 rs_ass[4];			/* argument save space for o32 */
 	u32 rs_code[2];			/* signal trampoline */
 	struct siginfo32 rs_info;
-	struct ucontext rs_uc;
+	struct ucontext32 rs_uc;
 };
 
 static int copy_siginfo_to_user32(siginfo_t32 *to, siginfo_t *from)
@@ -362,7 +370,7 @@
 	recalc_sigpending(current);
 	spin_unlock_irq(&current->sigmask_lock);
 
-	if (restore_sigcontext(&regs, &frame->sf_sc))
+	if (restore_sigcontext32(&regs, &frame->sf_sc))
 		goto badframe;
 
 	/*
@@ -386,6 +394,7 @@
 	struct rt_sigframe32 *frame;
 	sigset_t set;
 	stack_t st;
+	s32 sp;
 
 	frame = (struct rt_sigframe32 *) regs.regs[29];
 	if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
@@ -399,11 +408,18 @@
 	recalc_sigpending(current);
 	spin_unlock_irq(&current->sigmask_lock);
 
-	if (restore_sigcontext(&regs, &frame->rs_uc.uc_mcontext))
+	if (restore_sigcontext32(&regs, &frame->rs_uc.uc_mcontext))
 		goto badframe;
 
-	if (__copy_from_user(&st, &frame->rs_uc.uc_stack, sizeof(st)))
+	/* The ucontext contains a stack32_t, so we must convert!  */
+	if (__get_user(sp, &frame->rs_uc.uc_stack.ss_sp))
+		goto badframe;
+	st.ss_size = (long) sp;
+	if (__get_user(st.ss_size, &frame->rs_uc.uc_stack.ss_size))
 		goto badframe;
+	if (__get_user(st.ss_flags, &frame->rs_uc.uc_stack.ss_flags))
+		goto badframe;
+
 	/* It is more difficult to avoid calling this function than to
 	   call it and ignore errors.  */
 	do_sigaltstack(&st, NULL, regs.regs[29]);
@@ -422,8 +438,8 @@
 	force_sig(SIGSEGV, current);
 }
 
-static int inline setup_sigcontext(struct pt_regs *regs,
-				   struct sigcontext *sc)
+static int inline setup_sigcontext32(struct pt_regs *regs,
+				     struct sigcontext32 *sc)
 {
 	int err = 0;
 
@@ -462,7 +478,7 @@
 		own_fpu();
 		restore_fp(current);
 	}
-	err |= save_fp_context(sc);
+	err |= save_fp_context32(sc);
 
 out:
 	return err;
@@ -513,7 +529,7 @@
 	err |= __put_user(0x0000000c                     , frame->sf_code + 1);
 	flush_cache_sigtramp((unsigned long) frame->sf_code);
 
-	err |= setup_sigcontext(regs, &frame->sf_sc);
+	err |= setup_sigcontext32(regs, &frame->sf_sc);
 	err |= __copy_to_user(&frame->sf_mask, set, sizeof(*set));
 	if (err)
 		goto give_sigsegv;
@@ -554,6 +570,7 @@
 {
 	struct rt_sigframe32 *frame;
 	int err = 0;
+	s32 sp;
 
 	frame = get_sigframe(ka, regs, sizeof(*frame));
 	if (!access_ok(VERIFY_WRITE, frame, sizeof (*frame)))
@@ -577,13 +594,14 @@
 	/* Create the ucontext.  */
 	err |= __put_user(0, &frame->rs_uc.uc_flags);
 	err |= __put_user(0, &frame->rs_uc.uc_link);
-	err |= __put_user((void *)current->sas_ss_sp,
+	sp = (int) (long) current->sas_ss_sp;
+	err |= __put_user(sp,
 	                  &frame->rs_uc.uc_stack.ss_sp);
 	err |= __put_user(sas_ss_flags(regs->regs[29]),
 	                  &frame->rs_uc.uc_stack.ss_flags);
 	err |= __put_user(current->sas_ss_size,
 	                  &frame->rs_uc.uc_stack.ss_size);
-	err |= setup_sigcontext(regs, &frame->rs_uc.uc_mcontext);
+	err |= setup_sigcontext32(regs, &frame->rs_uc.uc_mcontext);
 	err |= __copy_to_user(&frame->rs_uc.uc_sigmask, set, sizeof(*set));
 
 	if (err)
Index: arch/mips64/kernel/traps.c
===================================================================
RCS file: /home/cvs/linux/arch/mips64/kernel/traps.c,v
retrieving revision 1.30.2.54
diff -u -r1.30.2.54 traps.c
--- arch/mips64/kernel/traps.c	6 Jun 2003 04:36:37 -0000	1.30.2.54
+++ arch/mips64/kernel/traps.c	14 Jul 2003 16:45:06 -0000
@@ -781,12 +781,21 @@
 asmlinkage int (*save_fp_context)(struct sigcontext *sc);
 asmlinkage int (*restore_fp_context)(struct sigcontext *sc);
 
+asmlinkage int (*save_fp_context32)(struct sigcontext32 *sc);
+asmlinkage int (*restore_fp_context32)(struct sigcontext32 *sc);
+
 extern asmlinkage int _save_fp_context(struct sigcontext *sc);
 extern asmlinkage int _restore_fp_context(struct sigcontext *sc);
 
+extern asmlinkage int _save_fp_context32(struct sigcontext32 *sc);
+extern asmlinkage int _restore_fp_context32(struct sigcontext32 *sc);
+
 extern asmlinkage int fpu_emulator_save_context(struct sigcontext *sc);
 extern asmlinkage int fpu_emulator_restore_context(struct sigcontext *sc);
 
+extern asmlinkage int fpu_emulator_save_context32(struct sigcontext32 *sc);
+extern asmlinkage int fpu_emulator_restore_context32(struct sigcontext32 *sc);
+
 void __init per_cpu_trap_init(void)
 {
 	unsigned int cpu = smp_processor_id();
@@ -904,9 +913,13 @@
 	if (cpu_has_fpu) {
 	        save_fp_context = _save_fp_context;
 		restore_fp_context = _restore_fp_context;
+	        save_fp_context32 = _save_fp_context32;
+		restore_fp_context32 = _restore_fp_context32;
 	} else {
 		save_fp_context = fpu_emulator_save_context;
 		restore_fp_context = fpu_emulator_restore_context;
+		save_fp_context32 = fpu_emulator_save_context32;
+		restore_fp_context32 = fpu_emulator_restore_context32;
 	}
 
 	flush_icache_range(KSEG0, KSEG0 + 0x400);
Index: arch/mips64/kernel/r4k_fpu.S
===================================================================
RCS file: /home/cvs/linux/arch/mips64/kernel/r4k_fpu.S,v
retrieving revision 1.3.2.1
diff -u -r1.3.2.1 r4k_fpu.S
--- arch/mips64/kernel/r4k_fpu.S	31 Jul 2002 02:41:21 -0000	1.3.2.1
+++ arch/mips64/kernel/r4k_fpu.S	14 Jul 2003 16:45:06 -0000
@@ -5,6 +5,8 @@
  *
  * Save/restore floating point context for signal handlers.
  *
+ * Copyright (C) 2003 Broadcom Corporation
+ *
  * Copyright (C) 1996, 1998, 1999, 2001 by Ralf Baechle
  *
  * Multi-arch abstraction and asm macros for easier reading:
@@ -32,11 +34,8 @@
 	.set	noreorder
 	/* Save floating point context */
 LEAF(_save_fp_context)
-	mfc0	t1, CP0_STATUS
-	sll	t2, t1,5
+	cfc1	t1, fcr31
 
-	bgez	t2, 1f
-	 cfc1	t1, fcr31
 	/* Store the 16 odd double precision registers */
 	EX	sdc1 $f1, SC_FPREGS+8(a0)
 	EX	sdc1 $f3, SC_FPREGS+24(a0)
@@ -56,7 +55,6 @@
 	EX	sdc1 $f31, SC_FPREGS+248(a0)
 
 	/* Store the 16 even double precision registers */
-1:
 	EX	sdc1 $f0, SC_FPREGS+0(a0)
 	EX	sdc1 $f2, SC_FPREGS+16(a0)
 	EX	sdc1 $f4, SC_FPREGS+32(a0)
@@ -81,24 +79,42 @@
 	 li	v0, 0					# success
 	END(_save_fp_context)
 
+	/* Save 32-bit process floating point context */
+LEAF(_save_fp_context32)
+	cfc1	t1, fcr31
+
+	EX	sdc1 $f0, SC32_FPREGS+0(a0)
+	EX	sdc1 $f2, SC32_FPREGS+16(a0)
+	EX	sdc1 $f4, SC32_FPREGS+32(a0)
+	EX	sdc1 $f6, SC32_FPREGS+48(a0)
+	EX	sdc1 $f8, SC32_FPREGS+64(a0)
+	EX	sdc1 $f10, SC32_FPREGS+80(a0)
+	EX	sdc1 $f12, SC32_FPREGS+96(a0)
+	EX	sdc1 $f14, SC32_FPREGS+112(a0)
+	EX	sdc1 $f16, SC32_FPREGS+128(a0)
+	EX	sdc1 $f18, SC32_FPREGS+144(a0)
+	EX	sdc1 $f20, SC32_FPREGS+160(a0)
+	EX	sdc1 $f22, SC32_FPREGS+176(a0)
+	EX	sdc1 $f24, SC32_FPREGS+192(a0)
+	EX	sdc1 $f26, SC32_FPREGS+208(a0)
+	EX	sdc1 $f28, SC32_FPREGS+224(a0)
+	EX	sdc1 $f30, SC32_FPREGS+240(a0)
+	EX	sw t1, SC32_FPC_CSR(a0)
+	cfc1	t0, $0				# implementation/version
+	EX	sw t0, SC32_FPC_EIR(a0)
+
+	jr	ra
+	 li	v0, 0					# success
+	END(_save_fp_context32)
+
 /*
  * Restore FPU state:
  *  - fp gp registers
  *  - cp1 status/control register
- *
- * We base the decision which registers to restore from the signal stack
- * frame on the current content of c0_status, not on the content of the
- * stack frame which might have been changed by the user.
  */
 LEAF(_restore_fp_context)
-	mfc0	t1, CP0_STATUS
-	sll	t0, t1,5
-	bgez	t0, 1f
-	 EX	lw t0, SC_FPC_CSR(a0)
-
-	/* Restore the 16 odd double precision registers only
-	 * when enabled in the cp0 status register.
-	 */
+	/* Restore an o32/64 sigcontext. */
+	EX	lw t0, SC_FPC_CSR(a0)
 	EX	ldc1 $f1, SC_FPREGS+8(a0)
 	EX	ldc1 $f3, SC_FPREGS+24(a0)
 	EX	ldc1 $f5, SC_FPREGS+40(a0)
@@ -116,11 +132,7 @@
 	EX	ldc1 $f29, SC_FPREGS+232(a0)
 	EX	ldc1 $f31, SC_FPREGS+248(a0)
 
-	/*
-	 * Restore the 16 even double precision registers
-	 * when cp1 was enabled in the cp0 status register.
-	 */
-1:	EX	ldc1 $f0, SC_FPREGS+0(a0)
+	EX	ldc1 $f0, SC_FPREGS+0(a0)
 	EX	ldc1 $f2, SC_FPREGS+16(a0)
 	EX	ldc1 $f4, SC_FPREGS+32(a0)
 	EX	ldc1 $f6, SC_FPREGS+48(a0)
@@ -140,6 +152,30 @@
 	jr	ra
 	 li	v0, 0					# success
 	END(_restore_fp_context)
+
+LEAF(_restore_fp_context32)
+	/* Restore an o32 sigcontext.  */
+	EX	lw t0, SC32_FPC_CSR(a0)
+	EX	ldc1 $f0, SC32_FPREGS+0(a0)
+	EX	ldc1 $f2, SC32_FPREGS+16(a0)
+	EX	ldc1 $f4, SC32_FPREGS+32(a0)
+	EX	ldc1 $f6, SC32_FPREGS+48(a0)
+	EX	ldc1 $f8, SC32_FPREGS+64(a0)
+	EX	ldc1 $f10, SC32_FPREGS+80(a0)
+	EX	ldc1 $f12, SC32_FPREGS+96(a0)
+	EX	ldc1 $f14, SC32_FPREGS+112(a0)
+	EX	ldc1 $f16, SC32_FPREGS+128(a0)
+	EX	ldc1 $f18, SC32_FPREGS+144(a0)
+	EX	ldc1 $f20, SC32_FPREGS+160(a0)
+	EX	ldc1 $f22, SC32_FPREGS+176(a0)
+	EX	ldc1 $f24, SC32_FPREGS+192(a0)
+	EX	ldc1 $f26, SC32_FPREGS+208(a0)
+	EX	ldc1 $f28, SC32_FPREGS+224(a0)
+	EX	ldc1 $f30, SC32_FPREGS+240(a0)
+	ctc1	t0, fcr31
+	jr	ra
+	 li	v0, 0					# success
+	END(_restore_fp_context32)
 
 	.type	fault@function
 	.ent	fault
Index: arch/mips/math-emu/kernel_linkage.c
===================================================================
RCS file: /home/cvs/linux/arch/mips/math-emu/kernel_linkage.c,v
retrieving revision 1.5.2.1
diff -u -r1.5.2.1 kernel_linkage.c
--- arch/mips/math-emu/kernel_linkage.c	5 Aug 2002 23:53:34 -0000	1.5.2.1
+++ arch/mips/math-emu/kernel_linkage.c	14 Jul 2003 16:45:06 -0000
@@ -90,3 +90,40 @@
 	return err;
 }
 
+#ifdef CONFIG_MIPS64
+/*
+ * This is the o32 version
+ */
+
+int fpu_emulator_save_context32(struct sigcontext32 *sc)
+{
+	int i;
+	int err = 0;
+
+	for (i = 0; i < 32; i+=2) {
+		err |=
+		    __put_user(current->thread.fpu.soft.regs[i],
+			       &sc->sc_fpregs[i]);
+	}
+	err |= __put_user(current->thread.fpu.soft.sr, &sc->sc_fpc_csr);
+	err |= __put_user(fpuemuprivate.eir, &sc->sc_fpc_eir);
+
+	return err;
+}
+
+int fpu_emulator_restore_context32(struct sigcontext32 *sc)
+{
+	int i;
+	int err = 0;
+
+	for (i = 0; i < 32; i+=2) {
+		err |=
+		    __get_user(current->thread.fpu.soft.regs[i],
+			       &sc->sc_fpregs[i]);
+	}
+	err |= __get_user(current->thread.fpu.soft.sr, &sc->sc_fpc_csr);
+	err |= __get_user(fpuemuprivate.eir, &sc->sc_fpc_eir);
+
+	return err;
+}
+#endif