MIPS: Set `si_code' for SIGFPE signals sent from emulation too
authorMaciej W. Rozycki <macro@linux-mips.org>
Fri, 3 Apr 2015 22:27:15 +0000 (23:27 +0100)
committerRalf Baechle <ralf@linux-mips.org>
Tue, 7 Apr 2015 23:10:19 +0000 (01:10 +0200)
Rework `process_fpemu_return' and move IEEE 754 exception interpretation
there, from `do_fpe'.  Record the cause bits set in FCSR before they are
cleared and pass them through to `process_fpemu_return' so as to set
`si_code' correctly too for SIGFPE signals sent from emulation rather
than those issued by hardware with the FPE processor exception only.

For simplicity `mipsr2_decoder' assumes `*fcr31' has been preinitialised
and only sets it to anything if an FPU instruction has been emulated,
which in turn is the only case SIGFPE can be issued for here.

Signed-off-by: Maciej W. Rozycki <macro@linux-mips.org>
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/9705/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
arch/mips/include/asm/fpu_emulator.h
arch/mips/include/asm/mips-r2-to-r6-emul.h
arch/mips/kernel/mips-r2-to-r6-emul.c
arch/mips/kernel/traps.c
arch/mips/kernel/unaligned.c

index 6370c82eb5e1a11053b4f619c810818172552f36..bfcc5c64b7b09745a822f78719b171841071d960 100644 (file)
@@ -66,7 +66,8 @@ extern int do_dsemulret(struct pt_regs *xcp);
 extern int fpu_emulator_cop1Handler(struct pt_regs *xcp,
                                    struct mips_fpu_struct *ctx, int has_fpu,
                                    void *__user *fault_addr);
-int process_fpemu_return(int sig, void __user *fault_addr);
+int process_fpemu_return(int sig, void __user *fault_addr,
+                        unsigned long fcr31);
 int mm_isBranchInstr(struct pt_regs *regs, struct mm_decoded_insn dec_insn,
                     unsigned long *contpc);
 
index dd6f1de5b621b2248dc80bbb74c9142ef95f3323..4b89f28047f7118588acf2152ba43e44a5d353e2 100644 (file)
@@ -84,11 +84,16 @@ extern void do_trap_or_bp(struct pt_regs *regs, unsigned int code,
 
 #ifndef CONFIG_MIPSR2_TO_R6_EMULATOR
 static int mipsr2_emulation;
-static inline int mipsr2_decoder(struct pt_regs *regs, u32 inst) { return 0; };
+static inline int mipsr2_decoder(struct pt_regs *regs, u32 inst,
+                                unsigned long *fcr31)
+{
+       return 0;
+};
 #else
 /* MIPS R2 Emulator ON/OFF */
 extern int mipsr2_emulation;
-extern int mipsr2_decoder(struct pt_regs *regs, u32 inst);
+extern int mipsr2_decoder(struct pt_regs *regs, u32 inst,
+                         unsigned long *fcr31);
 #endif /* CONFIG_MIPSR2_TO_R6_EMULATOR */
 
 #define NO_R6EMU       (cpu_has_mips_r6 && !mipsr2_emulation)
index 6633fa97d4316244ac59997428ac35c4a3a5556a..f2977f00911b303177fe086f72c0ae6be4a39313 100644 (file)
@@ -898,8 +898,9 @@ static inline int mipsr2_find_op_func(struct pt_regs *regs, u32 inst,
  * mipsr2_decoder: Decode and emulate a MIPS R2 instruction
  * @regs: Process register set
  * @inst: Instruction to decode and emulate
+ * @fcr31: Floating Point Control and Status Register returned
  */
-int mipsr2_decoder(struct pt_regs *regs, u32 inst)
+int mipsr2_decoder(struct pt_regs *regs, u32 inst, unsigned long *fcr31)
 {
        int err = 0;
        unsigned long vaddr;
@@ -1168,6 +1169,7 @@ fpu_emul:
 
                err = 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
index 4a0552dbcf4a7a756522e37f1cdac0f8c91a47bc..7d5532adc890bb2e502771e4b013a835ab46a51a 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,6 +792,7 @@ 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;
 
        /*
         * 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, &current->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,
-                                            &current->thread.cp0_baduaddr);
+                                            &current->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,
-                                                      &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();
 
                break;
 
index bbb69695a0a10765f4c6bdf300da304667c021f5..cf51ad36f2138f4a37cac539da22baccc1552974 100644 (file)
@@ -1076,7 +1076,7 @@ static void emulate_load_store_insn(struct pt_regs *regs,
                own_fpu(1);     /* Restore FPU state. */
 
                /* Signal if something went wrong. */
-               process_fpemu_return(res, fault_addr);
+               process_fpemu_return(res, fault_addr, 0);
 
                if (res == 0)
                        break;
@@ -1511,7 +1511,7 @@ fpu_emul:
                own_fpu(1);     /* restore FPU state */
 
                /* If something went wrong, signal */
-               process_fpemu_return(res, fault_addr);
+               process_fpemu_return(res, fault_addr, 0);
 
                if (res == 0)
                        goto success;