summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/mips/math-emu/cp1emu.c34
-rw-r--r--arch/mips/math-emu/dsemul.c2
2 files changed, 30 insertions, 6 deletions
diff --git a/arch/mips/math-emu/cp1emu.c b/arch/mips/math-emu/cp1emu.c
index 732c3a37d7b9..acfef06b8311 100644
--- a/arch/mips/math-emu/cp1emu.c
+++ b/arch/mips/math-emu/cp1emu.c
@@ -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
diff --git a/arch/mips/math-emu/dsemul.c b/arch/mips/math-emu/dsemul.c
index 00ad7365e453..e0b5cc27d78b 100644
--- a/arch/mips/math-emu/dsemul.c
+++ b/arch/mips/math-emu/dsemul.c
@@ -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)