summaryrefslogtreecommitdiffstats
path: root/arch/mips
diff options
context:
space:
mode:
authorMaciej W. Rozycki <macro@imgtec.com>2016-10-28 03:21:03 -0400
committerRalf Baechle <ralf@linux-mips.org>2016-11-03 20:28:41 -0400
commit5a1aca4469fdccd5b74ba0b4e490173b2b447895 (patch)
treebdfad3f7a9ce39cc48edd2a1cff8c89a0549c60a /arch/mips
parentc9e5603974573367c4d80964a845237a2297228c (diff)
MIPS: Fix FCSR Cause bit handling for correct SIGFPE issue
Sanitize FCSR Cause bit handling, following a trail of past attempts: * commit 4249548454f7 ("MIPS: ptrace: Fix FP context restoration FCSR regression"), * commit 443c44032a54 ("MIPS: Always clear FCSR cause bits after emulation"), * commit 64bedffe4968 ("MIPS: Clear [MSA]FPE CSR.Cause after notify_die()"), * commit b1442d39fac2 ("MIPS: Prevent user from setting FCSR cause bits"), * commit b54d2901517d ("Properly handle branch delay slots in connection with signals."). Specifically do not mask these bits out in ptrace(2) processing and send a SIGFPE signal instead whenever a matching pair of an FCSR Cause and Enable bit is seen as execution of an affected context is about to resume. Only then clear Cause bits, and even then do not clear any bits that are set but masked with the respective Enable bits. Adjust Cause bit clearing throughout code likewise, except within the FPU emulator proper where they are set according to IEEE 754 exceptions raised as the operation emulated executed. Do so so that any IEEE 754 exceptions subject to their default handling are recorded like with operations executed by FPU hardware. Signed-off-by: Maciej W. Rozycki <macro@imgtec.com> Cc: Paul Burton <paul.burton@imgtec.com> Cc: James Hogan <james.hogan@imgtec.com> Cc: linux-mips@linux-mips.org Cc: linux-kernel@vger.kernel.org Patchwork: https://patchwork.linux-mips.org/patch/14460/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
Diffstat (limited to 'arch/mips')
-rw-r--r--arch/mips/include/asm/fpu_emulator.h13
-rw-r--r--arch/mips/include/asm/switch_to.h18
-rw-r--r--arch/mips/kernel/mips-r2-to-r6-emul.c10
-rw-r--r--arch/mips/kernel/ptrace.c7
-rw-r--r--arch/mips/kernel/traps.c72
5 files changed, 78 insertions, 42 deletions
diff --git a/arch/mips/include/asm/fpu_emulator.h b/arch/mips/include/asm/fpu_emulator.h
index 355dc25172e7..c05369e0b8d6 100644
--- a/arch/mips/include/asm/fpu_emulator.h
+++ b/arch/mips/include/asm/fpu_emulator.h
@@ -63,6 +63,8 @@ do { \
63extern int fpu_emulator_cop1Handler(struct pt_regs *xcp, 63extern int fpu_emulator_cop1Handler(struct pt_regs *xcp,
64 struct mips_fpu_struct *ctx, int has_fpu, 64 struct mips_fpu_struct *ctx, int has_fpu,
65 void *__user *fault_addr); 65 void *__user *fault_addr);
66void force_fcr31_sig(unsigned long fcr31, void __user *fault_addr,
67 struct task_struct *tsk);
66int process_fpemu_return(int sig, void __user *fault_addr, 68int process_fpemu_return(int sig, void __user *fault_addr,
67 unsigned long fcr31); 69 unsigned long fcr31);
68int isBranchInstr(struct pt_regs *regs, struct mm_decoded_insn dec_insn, 70int isBranchInstr(struct pt_regs *regs, struct mm_decoded_insn dec_insn,
@@ -81,4 +83,15 @@ static inline void fpu_emulator_init_fpu(void)
81 set_fpr64(&t->thread.fpu.fpr[i], 0, SIGNALLING_NAN); 83 set_fpr64(&t->thread.fpu.fpr[i], 0, SIGNALLING_NAN);
82} 84}
83 85
86/*
87 * Mask the FCSR Cause bits according to the Enable bits, observing
88 * that Unimplemented is always enabled.
89 */
90static inline unsigned long mask_fcr31_x(unsigned long fcr31)
91{
92 return fcr31 & (FPU_CSR_UNI_X |
93 ((fcr31 & FPU_CSR_ALL_E) <<
94 (ffs(FPU_CSR_ALL_X) - ffs(FPU_CSR_ALL_E))));
95}
96
84#endif /* _ASM_FPU_EMULATOR_H */ 97#endif /* _ASM_FPU_EMULATOR_H */
diff --git a/arch/mips/include/asm/switch_to.h b/arch/mips/include/asm/switch_to.h
index ebb5c0f2f90d..c0ae27971e31 100644
--- a/arch/mips/include/asm/switch_to.h
+++ b/arch/mips/include/asm/switch_to.h
@@ -76,6 +76,22 @@ do { if (cpu_has_rw_llb) { \
76} while (0) 76} while (0)
77 77
78/* 78/*
79 * Check FCSR for any unmasked exceptions pending set with `ptrace',
80 * clear them and send a signal.
81 */
82#define __sanitize_fcr31(next) \
83do { \
84 unsigned long fcr31 = mask_fcr31_x(next->thread.fpu.fcr31); \
85 void __user *pc; \
86 \
87 if (unlikely(fcr31)) { \
88 pc = (void __user *)task_pt_regs(next)->cp0_epc; \
89 next->thread.fpu.fcr31 &= ~fcr31; \
90 force_fcr31_sig(fcr31, pc, next); \
91 } \
92} while (0)
93
94/*
79 * For newly created kernel threads switch_to() will return to 95 * For newly created kernel threads switch_to() will return to
80 * ret_from_kernel_thread, newly created user threads to ret_from_fork. 96 * ret_from_kernel_thread, newly created user threads to ret_from_fork.
81 * That is, everything following resume() will be skipped for new threads. 97 * That is, everything following resume() will be skipped for new threads.
@@ -85,6 +101,8 @@ do { if (cpu_has_rw_llb) { \
85do { \ 101do { \
86 __mips_mt_fpaff_switch_to(prev); \ 102 __mips_mt_fpaff_switch_to(prev); \
87 lose_fpu_inatomic(1, prev); \ 103 lose_fpu_inatomic(1, prev); \
104 if (tsk_used_math(next)) \
105 __sanitize_fcr31(next); \
88 if (cpu_has_dsp) { \ 106 if (cpu_has_dsp) { \
89 __save_dsp(prev); \ 107 __save_dsp(prev); \
90 __restore_dsp(next); \ 108 __restore_dsp(next); \
diff --git a/arch/mips/kernel/mips-r2-to-r6-emul.c b/arch/mips/kernel/mips-r2-to-r6-emul.c
index 22dedd62818a..bd09853aecdf 100644
--- a/arch/mips/kernel/mips-r2-to-r6-emul.c
+++ b/arch/mips/kernel/mips-r2-to-r6-emul.c
@@ -899,7 +899,7 @@ static inline int mipsr2_find_op_func(struct pt_regs *regs, u32 inst,
899 * mipsr2_decoder: Decode and emulate a MIPS R2 instruction 899 * mipsr2_decoder: Decode and emulate a MIPS R2 instruction
900 * @regs: Process register set 900 * @regs: Process register set
901 * @inst: Instruction to decode and emulate 901 * @inst: Instruction to decode and emulate
902 * @fcr31: Floating Point Control and Status Register returned 902 * @fcr31: Floating Point Control and Status Register Cause bits returned
903 */ 903 */
904int mipsr2_decoder(struct pt_regs *regs, u32 inst, unsigned long *fcr31) 904int mipsr2_decoder(struct pt_regs *regs, u32 inst, unsigned long *fcr31)
905{ 905{
@@ -1172,13 +1172,13 @@ fpu_emul:
1172 1172
1173 err = fpu_emulator_cop1Handler(regs, &current->thread.fpu, 0, 1173 err = fpu_emulator_cop1Handler(regs, &current->thread.fpu, 0,
1174 &fault_addr); 1174 &fault_addr);
1175 *fcr31 = current->thread.fpu.fcr31;
1176 1175
1177 /* 1176 /*
1178 * We can't allow the emulated instruction to leave any of 1177 * We can't allow the emulated instruction to leave any
1179 * the cause bits set in $fcr31. 1178 * enabled Cause bits set in $fcr31.
1180 */ 1179 */
1181 current->thread.fpu.fcr31 &= ~FPU_CSR_ALL_X; 1180 *fcr31 = res = mask_fcr31_x(current->thread.fpu.fcr31);
1181 current->thread.fpu.fcr31 &= ~res;
1182 1182
1183 /* 1183 /*
1184 * this is a tricky issue - lose_fpu() uses LL/SC atomics 1184 * this is a tricky issue - lose_fpu() uses LL/SC atomics
diff --git a/arch/mips/kernel/ptrace.c b/arch/mips/kernel/ptrace.c
index fd3a9580661a..a92994d60e91 100644
--- a/arch/mips/kernel/ptrace.c
+++ b/arch/mips/kernel/ptrace.c
@@ -79,16 +79,15 @@ void ptrace_disable(struct task_struct *child)
79} 79}
80 80
81/* 81/*
82 * Poke at FCSR according to its mask. Don't set the cause bits as 82 * Poke at FCSR according to its mask. Set the Cause bits even
83 * this is currently not handled correctly in FP context restoration 83 * if a corresponding Enable bit is set. This will be noticed at
84 * and will cause an oops if a corresponding enable bit is set. 84 * the time the thread is switched to and SIGFPE thrown accordingly.
85 */ 85 */
86static void ptrace_setfcr31(struct task_struct *child, u32 value) 86static void ptrace_setfcr31(struct task_struct *child, u32 value)
87{ 87{
88 u32 fcr31; 88 u32 fcr31;
89 u32 mask; 89 u32 mask;
90 90
91 value &= ~FPU_CSR_ALL_X;
92 fcr31 = child->thread.fpu.fcr31; 91 fcr31 = child->thread.fpu.fcr31;
93 mask = boot_cpu_data.fpu_msk31; 92 mask = boot_cpu_data.fpu_msk31;
94 child->thread.fpu.fcr31 = (value & ~mask) | (fcr31 & mask); 93 child->thread.fpu.fcr31 = (value & ~mask) | (fcr31 & mask);
diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c
index b9a910b208f9..3905003dfe2b 100644
--- a/arch/mips/kernel/traps.c
+++ b/arch/mips/kernel/traps.c
@@ -708,6 +708,32 @@ asmlinkage void do_ov(struct pt_regs *regs)
708 exception_exit(prev_state); 708 exception_exit(prev_state);
709} 709}
710 710
711/*
712 * Send SIGFPE according to FCSR Cause bits, which must have already
713 * been masked against Enable bits. This is impotant as Inexact can
714 * happen together with Overflow or Underflow, and `ptrace' can set
715 * any bits.
716 */
717void force_fcr31_sig(unsigned long fcr31, void __user *fault_addr,
718 struct task_struct *tsk)
719{
720 struct siginfo si = { .si_addr = fault_addr, .si_signo = SIGFPE };
721
722 if (fcr31 & FPU_CSR_INV_X)
723 si.si_code = FPE_FLTINV;
724 else if (fcr31 & FPU_CSR_DIV_X)
725 si.si_code = FPE_FLTDIV;
726 else if (fcr31 & FPU_CSR_OVF_X)
727 si.si_code = FPE_FLTOVF;
728 else if (fcr31 & FPU_CSR_UDF_X)
729 si.si_code = FPE_FLTUND;
730 else if (fcr31 & FPU_CSR_INE_X)
731 si.si_code = FPE_FLTRES;
732 else
733 si.si_code = __SI_FAULT;
734 force_sig_info(SIGFPE, &si, tsk);
735}
736
711int process_fpemu_return(int sig, void __user *fault_addr, unsigned long fcr31) 737int process_fpemu_return(int sig, void __user *fault_addr, unsigned long fcr31)
712{ 738{
713 struct siginfo si = { 0 }; 739 struct siginfo si = { 0 };
@@ -718,27 +744,7 @@ int process_fpemu_return(int sig, void __user *fault_addr, unsigned long fcr31)
718 return 0; 744 return 0;
719 745
720 case SIGFPE: 746 case SIGFPE:
721 si.si_addr = fault_addr; 747 force_fcr31_sig(fcr31, fault_addr, current);
722 si.si_signo = sig;
723 /*
724 * Inexact can happen together with Overflow or Underflow.
725 * Respect the mask to deliver the correct exception.
726 */
727 fcr31 &= (fcr31 & FPU_CSR_ALL_E) <<
728 (ffs(FPU_CSR_ALL_X) - ffs(FPU_CSR_ALL_E));
729 if (fcr31 & FPU_CSR_INV_X)
730 si.si_code = FPE_FLTINV;
731 else if (fcr31 & FPU_CSR_DIV_X)
732 si.si_code = FPE_FLTDIV;
733 else if (fcr31 & FPU_CSR_OVF_X)
734 si.si_code = FPE_FLTOVF;
735 else if (fcr31 & FPU_CSR_UDF_X)
736 si.si_code = FPE_FLTUND;
737 else if (fcr31 & FPU_CSR_INE_X)
738 si.si_code = FPE_FLTRES;
739 else
740 si.si_code = __SI_FAULT;
741 force_sig_info(sig, &si, current);
742 return 1; 748 return 1;
743 749
744 case SIGBUS: 750 case SIGBUS:
@@ -802,13 +808,13 @@ static int simulate_fp(struct pt_regs *regs, unsigned int opcode,
802 /* Run the emulator */ 808 /* Run the emulator */
803 sig = fpu_emulator_cop1Handler(regs, &current->thread.fpu, 1, 809 sig = fpu_emulator_cop1Handler(regs, &current->thread.fpu, 1,
804 &fault_addr); 810 &fault_addr);
805 fcr31 = current->thread.fpu.fcr31;
806 811
807 /* 812 /*
808 * We can't allow the emulated instruction to leave any of 813 * We can't allow the emulated instruction to leave any
809 * the cause bits set in $fcr31. 814 * enabled Cause bits set in $fcr31.
810 */ 815 */
811 current->thread.fpu.fcr31 &= ~FPU_CSR_ALL_X; 816 fcr31 = mask_fcr31_x(current->thread.fpu.fcr31);
817 current->thread.fpu.fcr31 &= ~fcr31;
812 818
813 /* Restore the hardware register state */ 819 /* Restore the hardware register state */
814 own_fpu(1); 820 own_fpu(1);
@@ -834,7 +840,7 @@ asmlinkage void do_fpe(struct pt_regs *regs, unsigned long fcr31)
834 goto out; 840 goto out;
835 841
836 /* Clear FCSR.Cause before enabling interrupts */ 842 /* Clear FCSR.Cause before enabling interrupts */
837 write_32bit_cp1_register(CP1_STATUS, fcr31 & ~FPU_CSR_ALL_X); 843 write_32bit_cp1_register(CP1_STATUS, fcr31 & ~mask_fcr31_x(fcr31));
838 local_irq_enable(); 844 local_irq_enable();
839 845
840 die_if_kernel("FP exception in kernel code", regs); 846 die_if_kernel("FP exception in kernel code", regs);
@@ -856,13 +862,13 @@ asmlinkage void do_fpe(struct pt_regs *regs, unsigned long fcr31)
856 /* Run the emulator */ 862 /* Run the emulator */
857 sig = fpu_emulator_cop1Handler(regs, &current->thread.fpu, 1, 863 sig = fpu_emulator_cop1Handler(regs, &current->thread.fpu, 1,
858 &fault_addr); 864 &fault_addr);
859 fcr31 = current->thread.fpu.fcr31;
860 865
861 /* 866 /*
862 * We can't allow the emulated instruction to leave any of 867 * We can't allow the emulated instruction to leave any
863 * the cause bits set in $fcr31. 868 * enabled Cause bits set in $fcr31.
864 */ 869 */
865 current->thread.fpu.fcr31 &= ~FPU_CSR_ALL_X; 870 fcr31 = mask_fcr31_x(current->thread.fpu.fcr31);
871 current->thread.fpu.fcr31 &= ~fcr31;
866 872
867 /* Restore the hardware register state */ 873 /* Restore the hardware register state */
868 own_fpu(1); /* Using the FPU again. */ 874 own_fpu(1); /* Using the FPU again. */
@@ -1427,13 +1433,13 @@ asmlinkage void do_cpu(struct pt_regs *regs)
1427 1433
1428 sig = fpu_emulator_cop1Handler(regs, &current->thread.fpu, 0, 1434 sig = fpu_emulator_cop1Handler(regs, &current->thread.fpu, 0,
1429 &fault_addr); 1435 &fault_addr);
1430 fcr31 = current->thread.fpu.fcr31;
1431 1436
1432 /* 1437 /*
1433 * We can't allow the emulated instruction to leave 1438 * We can't allow the emulated instruction to leave
1434 * any of the cause bits set in $fcr31. 1439 * any enabled Cause bits set in $fcr31.
1435 */ 1440 */
1436 current->thread.fpu.fcr31 &= ~FPU_CSR_ALL_X; 1441 fcr31 = mask_fcr31_x(current->thread.fpu.fcr31);
1442 current->thread.fpu.fcr31 &= ~fcr31;
1437 1443
1438 /* Send a signal if required. */ 1444 /* Send a signal if required. */
1439 if (!process_fpemu_return(sig, fault_addr, fcr31) && !err) 1445 if (!process_fpemu_return(sig, fault_addr, fcr31) && !err)