aboutsummaryrefslogtreecommitdiffstats
path: root/arch/mips/math-emu
diff options
context:
space:
mode:
authorMaciej W. Rozycki <macro@linux-mips.org>2015-04-03 18:26:56 -0400
committerRalf Baechle <ralf@linux-mips.org>2015-04-07 19:10:09 -0400
commit9ab4471c9f1b3e986f4d429951492f736c888ff6 (patch)
tree331efb7e2a03576a9e3da4f596f8237e1d98efe1 /arch/mips/math-emu
parent2d83fea786d7aeb5b3b76bd492d9b3bccc0f823c (diff)
MIPS: math-emu: Correct delay-slot exception propagation
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>
Diffstat (limited to 'arch/mips/math-emu')
-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:
1134 /* 1134 /*
1135 * Branch taken: emulate dslot instruction 1135 * Branch taken: emulate dslot instruction
1136 */ 1136 */
1137 unsigned long bcpc;
1138
1139 /*
1140 * Remember EPC at the branch to point back
1141 * at so that any delay-slot instruction
1142 * signal is not silently ignored.
1143 */
1144 bcpc = xcp->cp0_epc;
1137 xcp->cp0_epc += dec_insn.pc_inc; 1145 xcp->cp0_epc += dec_insn.pc_inc;
1138 1146
1139 contpc = MIPSInst_SIMM(ir); 1147 contpc = MIPSInst_SIMM(ir);
@@ -1159,7 +1167,15 @@ emul:
1159 * Single step the non-CP1 1167 * Single step the non-CP1
1160 * instruction in the dslot. 1168 * instruction in the dslot.
1161 */ 1169 */
1162 return mips_dsemul(xcp, ir, contpc); 1170 sig = mips_dsemul(xcp, ir,
1171 contpc);
1172 if (sig)
1173 xcp->cp0_epc = bcpc;
1174 /*
1175 * SIGILL forces out of
1176 * the emulation loop.
1177 */
1178 return sig ? sig : SIGILL;
1163 } 1179 }
1164 } else 1180 } else
1165 contpc = (xcp->cp0_epc + (contpc << 2)); 1181 contpc = (xcp->cp0_epc + (contpc << 2));
@@ -1174,7 +1190,7 @@ emul:
1174 if (cpu_has_mips_2_3_4_5_r) 1190 if (cpu_has_mips_2_3_4_5_r)
1175 goto emul; 1191 goto emul;
1176 1192
1177 return SIGILL; 1193 goto bc_sigill;
1178 1194
1179 case cop1_op: 1195 case cop1_op:
1180 goto emul; 1196 goto emul;
@@ -1184,7 +1200,7 @@ emul:
1184 /* its one of ours */ 1200 /* its one of ours */
1185 goto emul; 1201 goto emul;
1186 1202
1187 return SIGILL; 1203 goto bc_sigill;
1188 1204
1189 case spec_op: 1205 case spec_op:
1190 switch (MIPSInst_FUNC(ir)) { 1206 switch (MIPSInst_FUNC(ir)) {
@@ -1192,16 +1208,24 @@ emul:
1192 if (cpu_has_mips_4_5_r) 1208 if (cpu_has_mips_4_5_r)
1193 goto emul; 1209 goto emul;
1194 1210
1195 return SIGILL; 1211 goto bc_sigill;
1196 } 1212 }
1197 break; 1213 break;
1214
1215 bc_sigill:
1216 xcp->cp0_epc = bcpc;
1217 return SIGILL;
1198 } 1218 }
1199 1219
1200 /* 1220 /*
1201 * Single step the non-cp1 1221 * Single step the non-cp1
1202 * instruction in the dslot 1222 * instruction in the dslot
1203 */ 1223 */
1204 return mips_dsemul(xcp, ir, contpc); 1224 sig = mips_dsemul(xcp, ir, contpc);
1225 if (sig)
1226 xcp->cp0_epc = bcpc;
1227 /* SIGILL forces out of the emulation loop. */
1228 return sig ? sig : SIGILL;
1205 } else if (likely) { /* branch not taken */ 1229 } else if (likely) { /* branch not taken */
1206 /* 1230 /*
1207 * branch likely nullifies 1231 * 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)
96 96
97 flush_cache_sigtramp((unsigned long)&fr->emul); 97 flush_cache_sigtramp((unsigned long)&fr->emul);
98 98
99 return SIGILL; /* force out of emulation loop */ 99 return 0;
100} 100}
101 101
102int do_dsemulret(struct pt_regs *xcp) 102int do_dsemulret(struct pt_regs *xcp)