]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - arch/mips/kernel/traps.c
Merge remote-tracking branch 'kvm/linux-next'
[karo-tx-linux.git] / arch / mips / kernel / traps.c
index dbfa47cdc8c1a8e04fbfd9a5243dcea265d7aade..e334c641a81b3d30bc9a90e2de98604f76993dc4 100644 (file)
@@ -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(&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;
        }
 }
 
@@ -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,13 +792,20 @@ static int simulate_fp(struct pt_regs *regs, unsigned int opcode,
        /* Run the emulator */
        sig = fpu_emulator_cop1Handler(regs, &current->thread.fpu, 1,
                                       &fault_addr);
+       fcr31 = current->thread.fpu.fcr31;
 
-       /* If something went wrong, signal */
-       process_fpemu_return(sig, fault_addr);
+       /*
+        * 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;
 
        /* Restore the hardware register state */
        own_fpu(1);
 
+       /* Send a signal if required.  */
+       process_fpemu_return(sig, fault_addr, fcr31);
+
        return 0;
 }
 
@@ -776,18 +815,21 @@ 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),
                       SIGFPE) == NOTIFY_STOP)
                goto out;
+
+       /* Clear FCSR.Cause before enabling interrupts */
+       write_32bit_cp1_register(CP1_STATUS, fcr31 & ~FPU_CSR_ALL_X);
+       local_irq_enable();
+
        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...
@@ -804,44 +846,23 @@ 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
-                * the cause bit set in $fcr31.
+                * the cause bits set in $fcr31.
                 */
                current->thread.fpu.fcr31 &= ~FPU_CSR_ALL_X;
 
                /* 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);
@@ -1044,7 +1065,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:
@@ -1054,7 +1077,8 @@ asmlinkage void do_ri(struct pt_regs *regs)
                        goto no_r2_instr;
                default:
                        process_fpemu_return(status,
-                                            &current->thread.cp0_baduaddr);
+                                            &current->thread.cp0_baduaddr,
+                                            fcr31);
                        task_thread_info(current)->r2_emul_return = 1;
                        return;
                }
@@ -1301,10 +1325,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;
@@ -1378,15 +1405,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 (!process_fpemu_return(sig, fault_addr) && !err)
-                               mt_ase_fp_affinity();
-               }
+               if (raw_cpu_has_fpu && !err)
+                       break;
+
+               sig = fpu_emulator_cop1Handler(regs, &current->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.
+                */
+               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;
 
@@ -1398,13 +1432,22 @@ asmlinkage void do_cpu(struct pt_regs *regs)
        exception_exit(prev_state);
 }
 
-asmlinkage void do_msa_fpe(struct pt_regs *regs)
+asmlinkage void do_msa_fpe(struct pt_regs *regs, unsigned int msacsr)
 {
        enum ctx_state prev_state;
 
        prev_state = exception_enter();
+       if (notify_die(DIE_MSAFP, "MSA FP exception", regs, 0,
+                      regs_to_trapnr(regs), SIGFPE) == NOTIFY_STOP)
+               goto out;
+
+       /* Clear MSACSR.Cause before enabling interrupts */
+       write_msa_csr(msacsr & ~MSA_CSR_CAUSEF);
+       local_irq_enable();
+
        die_if_kernel("do_msa_fpe invoked from kernel context!", regs);
        force_sig(SIGFPE, current);
+out:
        exception_exit(prev_state);
 }