|
@@ -700,29 +700,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(¤t->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(¤t->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(¤t->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(¤t->mm->mmap_sem);
|
|
|
+ force_sig_info(sig, &si, current);
|
|
|
+ return 1;
|
|
|
+
|
|
|
+ default:
|
|
|
force_sig(sig, current);
|
|
|
return 1;
|
|
|
- } else {
|
|
|
- return 0;
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -730,7 +761,8 @@ static int simulate_fp(struct pt_regs *regs, unsigned int opcode,
|
|
|
unsigned long old_epc, unsigned long old_ra)
|
|
|
{
|
|
|
union mips_instruction inst = { .word = opcode };
|
|
|
- void __user *fault_addr = NULL;
|
|
|
+ void __user *fault_addr;
|
|
|
+ unsigned long fcr31;
|
|
|
int sig;
|
|
|
|
|
|
/* If it's obviously not an FP instruction, skip it */
|
|
@@ -760,6 +792,7 @@ 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
|
|
@@ -767,12 +800,12 @@ static int simulate_fp(struct pt_regs *regs, unsigned int opcode,
|
|
|
*/
|
|
|
current->thread.fpu.fcr31 &= ~FPU_CSR_ALL_X;
|
|
|
|
|
|
- /* If something went wrong, signal */
|
|
|
- process_fpemu_return(sig, fault_addr);
|
|
|
-
|
|
|
/* Restore the hardware register state */
|
|
|
own_fpu(1);
|
|
|
|
|
|
+ /* Send a signal if required. */
|
|
|
+ process_fpemu_return(sig, fault_addr, fcr31);
|
|
|
+
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -782,7 +815,8 @@ static int simulate_fp(struct pt_regs *regs, unsigned int opcode,
|
|
|
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),
|
|
@@ -791,9 +825,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...
|
|
@@ -810,6 +841,7 @@ 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
|
|
@@ -819,35 +851,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);
|
|
@@ -1050,7 +1060,9 @@ asmlinkage void do_ri(struct pt_regs *regs)
|
|
|
if (mipsr2_emulation && cpu_has_mips_r6 &&
|
|
|
likely(user_mode(regs)) &&
|
|
|
likely(get_user(opcode, epc) >= 0)) {
|
|
|
- status = mipsr2_decoder(regs, opcode);
|
|
|
+ unsigned long fcr31 = 0;
|
|
|
+
|
|
|
+ status = mipsr2_decoder(regs, opcode, &fcr31);
|
|
|
switch (status) {
|
|
|
case 0:
|
|
|
case SIGEMT:
|
|
@@ -1060,7 +1072,8 @@ asmlinkage void do_ri(struct pt_regs *regs)
|
|
|
goto no_r2_instr;
|
|
|
default:
|
|
|
process_fpemu_return(status,
|
|
|
- ¤t->thread.cp0_baduaddr);
|
|
|
+ ¤t->thread.cp0_baduaddr,
|
|
|
+ fcr31);
|
|
|
task_thread_info(current)->r2_emul_return = 1;
|
|
|
return;
|
|
|
}
|
|
@@ -1307,10 +1320,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;
|
|
@@ -1384,22 +1400,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,
|
|
|
- ¤t->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, ¤t->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();
|
|
|
|
|
|
break;
|
|
|
|