MIPS: math-emu: Correct delay-slot exception propagation
authorMaciej W. Rozycki <macro@linux-mips.org>
Fri, 3 Apr 2015 22:26:56 +0000 (23:26 +0100)
committerRalf Baechle <ralf@linux-mips.org>
Tue, 7 Apr 2015 23:10:09 +0000 (01:10 +0200)
Restore EPC at the branch whose delay slot is emulated if the delay-slot
instruction signals.  This is so that code in `fpu_emulator_cop1Handler'
does not see EPC having advanced and mistakenly successfully resume
userland execution from the location at the branch target in that case.
Restoring EPC guarantees an immediate exit from the emulation loop and
if EPC hasn't advanced at all since entering the loop, also issuing the
signal reported by the delay-slot instruction.

Signed-off-by: Maciej W. Rozycki <macro@linux-mips.org>
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/9701/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
arch/mips/math-emu/cp1emu.c
arch/mips/math-emu/dsemul.c

index 732c3a37d7b9dea3325293530be70ac144d3a10a..acfef06b83112b88a43d913eced9ac5b1d94659a 100644 (file)
@@ -1134,6 +1134,14 @@ emul:
                                /*
                                 * Branch taken: emulate dslot instruction
                                 */
+                               unsigned long bcpc;
+
+                               /*
+                                * Remember EPC at the branch to point back
+                                * at so that any delay-slot instruction
+                                * signal is not silently ignored.
+                                */
+                               bcpc = xcp->cp0_epc;
                                xcp->cp0_epc += dec_insn.pc_inc;
 
                                contpc = MIPSInst_SIMM(ir);
@@ -1159,7 +1167,15 @@ emul:
                                                 * Single step the non-CP1
                                                 * instruction in the dslot.
                                                 */
-                                               return mips_dsemul(xcp, ir, contpc);
+                                               sig = mips_dsemul(xcp, ir,
+                                                                 contpc);
+                                               if (sig)
+                                                       xcp->cp0_epc = bcpc;
+                                               /*
+                                                * SIGILL forces out of
+                                                * the emulation loop.
+                                                */
+                                               return sig ? sig : SIGILL;
                                        }
                                } else
                                        contpc = (xcp->cp0_epc + (contpc << 2));
@@ -1174,7 +1190,7 @@ emul:
                                        if (cpu_has_mips_2_3_4_5_r)
                                                goto emul;
 
-                                       return SIGILL;
+                                       goto bc_sigill;
 
                                case cop1_op:
                                        goto emul;
@@ -1184,7 +1200,7 @@ emul:
                                                /* its one of ours */
                                                goto emul;
 
-                                       return SIGILL;
+                                       goto bc_sigill;
 
                                case spec_op:
                                        switch (MIPSInst_FUNC(ir)) {
@@ -1192,16 +1208,24 @@ emul:
                                                if (cpu_has_mips_4_5_r)
                                                        goto emul;
 
-                                               return SIGILL;
+                                               goto bc_sigill;
                                        }
                                        break;
+
+                               bc_sigill:
+                                       xcp->cp0_epc = bcpc;
+                                       return SIGILL;
                                }
 
                                /*
                                 * Single step the non-cp1
                                 * instruction in the dslot
                                 */
-                               return mips_dsemul(xcp, ir, contpc);
+                               sig = mips_dsemul(xcp, ir, contpc);
+                               if (sig)
+                                       xcp->cp0_epc = bcpc;
+                               /* SIGILL forces out of the emulation loop.  */
+                               return sig ? sig : SIGILL;
                        } else if (likely) {    /* branch not taken */
                                /*
                                 * branch likely nullifies
index 00ad7365e453c8d06a50c4b2abf69352dcbb51cb..e0b5cc27d78b0dd906ec1248b670346116219b37 100644 (file)
@@ -96,7 +96,7 @@ int mips_dsemul(struct pt_regs *regs, mips_instruction ir, unsigned long cpc)
 
        flush_cache_sigtramp((unsigned long)&fr->emul);
 
-       return SIGILL;          /* force out of emulation loop */
+       return 0;
 }
 
 int do_dsemulret(struct pt_regs *xcp)