summaryrefslogtreecommitdiff
path: root/arch/mips/kernel/traps.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/mips/kernel/traps.c')
-rw-r--r--arch/mips/kernel/traps.c133
1 files changed, 72 insertions, 61 deletions
diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c
index 3f4c52b73f15..cc436793c14e 100644
--- a/arch/mips/kernel/traps.c
+++ b/arch/mips/kernel/traps.c
@@ -707,29 +707,60 @@ asmlinkage void do_ov(struct pt_regs *regs)
exception_exit(prev_state);
}
-int process_fpemu_return(int sig, void __user *fault_addr)
+int process_fpemu_return(int sig, void __user *fault_addr, unsigned long fcr31)
{
- if (sig == SIGSEGV || sig == SIGBUS) {
- struct siginfo si = {0};
+ struct siginfo si = { 0 };
+
+ switch (sig) {
+ case 0:
+ return 0;
+
+ case SIGFPE:
si.si_addr = fault_addr;
si.si_signo = sig;
- if (sig == SIGSEGV) {
- down_read(&current->mm->mmap_sem);
- if (find_vma(current->mm, (unsigned long)fault_addr))
- si.si_code = SEGV_ACCERR;
- else
- si.si_code = SEGV_MAPERR;
- up_read(&current->mm->mmap_sem);
- } else {
- si.si_code = BUS_ADRERR;
- }
+ /*
+ * 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);
return 1;
- } else if (sig) {
+
+ case SIGBUS:
+ si.si_addr = fault_addr;
+ si.si_signo = sig;
+ si.si_code = BUS_ADRERR;
+ force_sig_info(sig, &si, current);
+ return 1;
+
+ case SIGSEGV:
+ si.si_addr = fault_addr;
+ si.si_signo = sig;
+ down_read(&current->mm->mmap_sem);
+ if (find_vma(current->mm, (unsigned long)fault_addr))
+ si.si_code = SEGV_ACCERR;
+ else
+ si.si_code = SEGV_MAPERR;
+ up_read(&current->mm->mmap_sem);
+ force_sig_info(sig, &si, current);
+ return 1;
+
+ default:
force_sig(sig, current);
return 1;
- } else {
- return 0;
}
}
@@ -739,7 +770,8 @@ int process_fpemu_return(int sig, void __user *fault_addr)
asmlinkage void do_fpe(struct pt_regs *regs, unsigned long fcr31)
{
enum ctx_state prev_state;
- siginfo_t info = {0};
+ void __user *fault_addr;
+ int sig;
prev_state = exception_enter();
if (notify_die(DIE_FP, "FP exception", regs, 0, regs_to_trapnr(regs),
@@ -753,9 +785,6 @@ asmlinkage void do_fpe(struct pt_regs *regs, unsigned long fcr31)
die_if_kernel("FP exception in kernel code", regs);
if (fcr31 & FPU_CSR_UNI_X) {
- int sig;
- void __user *fault_addr = NULL;
-
/*
* Unimplemented operation exception. If we've got the full
* software emulator on-board, let's use it...
@@ -772,6 +801,7 @@ asmlinkage void do_fpe(struct pt_regs *regs, unsigned long fcr31)
/* Run the emulator */
sig = fpu_emulator_cop1Handler(regs, &current->thread.fpu, 1,
&fault_addr);
+ fcr31 = current->thread.fpu.fcr31;
/*
* We can't allow the emulated instruction to leave any of
@@ -781,35 +811,13 @@ asmlinkage void do_fpe(struct pt_regs *regs, unsigned long fcr31)
/* Restore the hardware register state */
own_fpu(1); /* Using the FPU again. */
-
- /* If something went wrong, signal */
- process_fpemu_return(sig, fault_addr);
-
- goto out;
+ } else {
+ sig = SIGFPE;
+ fault_addr = (void __user *) regs->cp0_epc;
}
- /*
- * 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)
- info.si_code = FPE_FLTINV;
- else if (fcr31 & FPU_CSR_DIV_X)
- info.si_code = FPE_FLTDIV;
- else if (fcr31 & FPU_CSR_OVF_X)
- info.si_code = FPE_FLTOVF;
- else if (fcr31 & FPU_CSR_UDF_X)
- info.si_code = FPE_FLTUND;
- else if (fcr31 & FPU_CSR_INE_X)
- info.si_code = FPE_FLTRES;
- else
- info.si_code = __SI_FAULT;
- info.si_signo = SIGFPE;
- info.si_errno = 0;
- info.si_addr = (void __user *) regs->cp0_epc;
- force_sig_info(SIGFPE, &info, current);
+ /* Send a signal if required. */
+ process_fpemu_return(sig, fault_addr, fcr31);
out:
exception_exit(prev_state);
@@ -1210,10 +1218,13 @@ asmlinkage void do_cpu(struct pt_regs *regs)
enum ctx_state prev_state;
unsigned int __user *epc;
unsigned long old_epc, old31;
+ void __user *fault_addr;
unsigned int opcode;
+ unsigned long fcr31;
unsigned int cpid;
int status, err;
unsigned long __maybe_unused flags;
+ int sig;
prev_state = exception_enter();
cpid = (regs->cp0_cause >> CAUSEB_CE) & 3;
@@ -1286,22 +1297,22 @@ asmlinkage void do_cpu(struct pt_regs *regs)
case 1:
err = enable_restore_fp_context(0);
- if (!raw_cpu_has_fpu || err) {
- int sig;
- void __user *fault_addr = NULL;
- sig = fpu_emulator_cop1Handler(regs,
- &current->thread.fpu,
- 0, &fault_addr);
+ if (raw_cpu_has_fpu && !err)
+ break;
- /*
- * We can't allow the emulated instruction to leave
- * any of the cause bits set in $fcr31.
- */
- current->thread.fpu.fcr31 &= ~FPU_CSR_ALL_X;
+ sig = fpu_emulator_cop1Handler(regs, &current->thread.fpu, 0,
+ &fault_addr);
+ fcr31 = current->thread.fpu.fcr31;
- if (!process_fpemu_return(sig, fault_addr) && !err)
- mt_ase_fp_affinity();
- }
+ /*
+ * We can't allow the emulated instruction to leave
+ * any of the cause bits set in $fcr31.
+ */
+ current->thread.fpu.fcr31 &= ~FPU_CSR_ALL_X;
+
+ /* Send a signal if required. */
+ if (!process_fpemu_return(sig, fault_addr, fcr31) && !err)
+ mt_ase_fp_affinity();
goto out;