|
|
@@ -708,6 +708,32 @@ asmlinkage void do_ov(struct pt_regs *regs)
|
|
|
exception_exit(prev_state);
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * Send SIGFPE according to FCSR Cause bits, which must have already
|
|
|
+ * been masked against Enable bits. This is impotant as Inexact can
|
|
|
+ * happen together with Overflow or Underflow, and `ptrace' can set
|
|
|
+ * any bits.
|
|
|
+ */
|
|
|
+void force_fcr31_sig(unsigned long fcr31, void __user *fault_addr,
|
|
|
+ struct task_struct *tsk)
|
|
|
+{
|
|
|
+ struct siginfo si = { .si_addr = fault_addr, .si_signo = SIGFPE };
|
|
|
+
|
|
|
+ if (fcr31 & FPU_CSR_INV_X)
|
|
|
+ si.si_code = FPE_FLTINV;
|
|
|
+ else if (fcr31 & FPU_CSR_DIV_X)
|
|
|
+ si.si_code = FPE_FLTDIV;
|
|
|
+ else if (fcr31 & FPU_CSR_OVF_X)
|
|
|
+ si.si_code = FPE_FLTOVF;
|
|
|
+ else if (fcr31 & FPU_CSR_UDF_X)
|
|
|
+ si.si_code = FPE_FLTUND;
|
|
|
+ else if (fcr31 & FPU_CSR_INE_X)
|
|
|
+ si.si_code = FPE_FLTRES;
|
|
|
+ else
|
|
|
+ si.si_code = __SI_FAULT;
|
|
|
+ force_sig_info(SIGFPE, &si, tsk);
|
|
|
+}
|
|
|
+
|
|
|
int process_fpemu_return(int sig, void __user *fault_addr, unsigned long fcr31)
|
|
|
{
|
|
|
struct siginfo si = { 0 };
|
|
|
@@ -718,27 +744,7 @@ int process_fpemu_return(int sig, void __user *fault_addr, unsigned long fcr31)
|
|
|
return 0;
|
|
|
|
|
|
case SIGFPE:
|
|
|
- si.si_addr = fault_addr;
|
|
|
- si.si_signo = sig;
|
|
|
- /*
|
|
|
- * Inexact can happen together with Overflow or Underflow.
|
|
|
- * Respect the mask to deliver the correct exception.
|
|
|
- */
|
|
|
- fcr31 &= (fcr31 & FPU_CSR_ALL_E) <<
|
|
|
- (ffs(FPU_CSR_ALL_X) - ffs(FPU_CSR_ALL_E));
|
|
|
- if (fcr31 & FPU_CSR_INV_X)
|
|
|
- si.si_code = FPE_FLTINV;
|
|
|
- else if (fcr31 & FPU_CSR_DIV_X)
|
|
|
- si.si_code = FPE_FLTDIV;
|
|
|
- else if (fcr31 & FPU_CSR_OVF_X)
|
|
|
- si.si_code = FPE_FLTOVF;
|
|
|
- else if (fcr31 & FPU_CSR_UDF_X)
|
|
|
- si.si_code = FPE_FLTUND;
|
|
|
- else if (fcr31 & FPU_CSR_INE_X)
|
|
|
- si.si_code = FPE_FLTRES;
|
|
|
- else
|
|
|
- si.si_code = __SI_FAULT;
|
|
|
- force_sig_info(sig, &si, current);
|
|
|
+ force_fcr31_sig(fcr31, fault_addr, current);
|
|
|
return 1;
|
|
|
|
|
|
case SIGBUS:
|
|
|
@@ -802,13 +808,13 @@ static int simulate_fp(struct pt_regs *regs, unsigned int opcode,
|
|
|
/* Run the emulator */
|
|
|
sig = fpu_emulator_cop1Handler(regs, ¤t->thread.fpu, 1,
|
|
|
&fault_addr);
|
|
|
- fcr31 = current->thread.fpu.fcr31;
|
|
|
|
|
|
/*
|
|
|
- * We can't allow the emulated instruction to leave any of
|
|
|
- * the cause bits set in $fcr31.
|
|
|
+ * We can't allow the emulated instruction to leave any
|
|
|
+ * enabled Cause bits set in $fcr31.
|
|
|
*/
|
|
|
- current->thread.fpu.fcr31 &= ~FPU_CSR_ALL_X;
|
|
|
+ fcr31 = mask_fcr31_x(current->thread.fpu.fcr31);
|
|
|
+ current->thread.fpu.fcr31 &= ~fcr31;
|
|
|
|
|
|
/* Restore the hardware register state */
|
|
|
own_fpu(1);
|
|
|
@@ -834,7 +840,7 @@ asmlinkage void do_fpe(struct pt_regs *regs, unsigned long fcr31)
|
|
|
goto out;
|
|
|
|
|
|
/* Clear FCSR.Cause before enabling interrupts */
|
|
|
- write_32bit_cp1_register(CP1_STATUS, fcr31 & ~FPU_CSR_ALL_X);
|
|
|
+ write_32bit_cp1_register(CP1_STATUS, fcr31 & ~mask_fcr31_x(fcr31));
|
|
|
local_irq_enable();
|
|
|
|
|
|
die_if_kernel("FP exception in kernel code", regs);
|
|
|
@@ -856,13 +862,13 @@ asmlinkage void do_fpe(struct pt_regs *regs, unsigned long fcr31)
|
|
|
/* Run the emulator */
|
|
|
sig = fpu_emulator_cop1Handler(regs, ¤t->thread.fpu, 1,
|
|
|
&fault_addr);
|
|
|
- fcr31 = current->thread.fpu.fcr31;
|
|
|
|
|
|
/*
|
|
|
- * We can't allow the emulated instruction to leave any of
|
|
|
- * the cause bits set in $fcr31.
|
|
|
+ * We can't allow the emulated instruction to leave any
|
|
|
+ * enabled Cause bits set in $fcr31.
|
|
|
*/
|
|
|
- current->thread.fpu.fcr31 &= ~FPU_CSR_ALL_X;
|
|
|
+ fcr31 = mask_fcr31_x(current->thread.fpu.fcr31);
|
|
|
+ current->thread.fpu.fcr31 &= ~fcr31;
|
|
|
|
|
|
/* Restore the hardware register state */
|
|
|
own_fpu(1); /* Using the FPU again. */
|
|
|
@@ -1427,13 +1433,13 @@ asmlinkage void do_cpu(struct pt_regs *regs)
|
|
|
|
|
|
sig = fpu_emulator_cop1Handler(regs, ¤t->thread.fpu, 0,
|
|
|
&fault_addr);
|
|
|
- fcr31 = current->thread.fpu.fcr31;
|
|
|
|
|
|
/*
|
|
|
* We can't allow the emulated instruction to leave
|
|
|
- * any of the cause bits set in $fcr31.
|
|
|
+ * any enabled Cause bits set in $fcr31.
|
|
|
*/
|
|
|
- current->thread.fpu.fcr31 &= ~FPU_CSR_ALL_X;
|
|
|
+ fcr31 = mask_fcr31_x(current->thread.fpu.fcr31);
|
|
|
+ current->thread.fpu.fcr31 &= ~fcr31;
|
|
|
|
|
|
/* Send a signal if required. */
|
|
|
if (!process_fpemu_return(sig, fault_addr, fcr31) && !err)
|