aboutsummaryrefslogtreecommitdiff
path: root/target-mips/helper.c
diff options
context:
space:
mode:
Diffstat (limited to 'target-mips/helper.c')
-rw-r--r--target-mips/helper.c45
1 files changed, 42 insertions, 3 deletions
diff --git a/target-mips/helper.c b/target-mips/helper.c
index 05325d92e9..c92b25c2a0 100644
--- a/target-mips/helper.c
+++ b/target-mips/helper.c
@@ -25,6 +25,7 @@
#include "cpu.h"
#include "sysemu/kvm.h"
+#include "exec/cpu_ldst.h"
enum {
TLBRET_XI = -6,
@@ -241,6 +242,10 @@ static void raise_mmu_exception(CPUMIPSState *env, target_ulong address,
CPUState *cs = CPU(mips_env_get_cpu(env));
int exception = 0, error_code = 0;
+ if (rw == MMU_INST_FETCH) {
+ error_code |= EXCP_INST_NOTAVAIL;
+ }
+
switch (tlb_error) {
default:
case TLBRET_BADADDR:
@@ -259,7 +264,7 @@ static void raise_mmu_exception(CPUMIPSState *env, target_ulong address,
} else {
exception = EXCP_TLBL;
}
- error_code = 1;
+ error_code |= EXCP_TLB_NOMATCH;
break;
case TLBRET_INVALID:
/* TLB match with no valid bit */
@@ -451,6 +456,21 @@ static void set_hflags_for_handler (CPUMIPSState *env)
<< MIPS_HFLAG_M16_SHIFT);
}
}
+
+static inline void set_badinstr_registers(CPUMIPSState *env)
+{
+ if (env->hflags & MIPS_HFLAG_M16) {
+ /* TODO: add BadInstr support for microMIPS */
+ return;
+ }
+ if (env->CP0_Config3 & (1 << CP0C3_BI)) {
+ env->CP0_BadInstr = cpu_ldl_code(env, env->active_tc.PC);
+ }
+ if ((env->CP0_Config3 & (1 << CP0C3_BP)) &&
+ (env->hflags & MIPS_HFLAG_BMASK)) {
+ env->CP0_BadInstrP = cpu_ldl_code(env, env->active_tc.PC - 4);
+ }
+}
#endif
void mips_cpu_do_interrupt(CPUState *cs)
@@ -458,6 +478,7 @@ void mips_cpu_do_interrupt(CPUState *cs)
#if !defined(CONFIG_USER_ONLY)
MIPSCPU *cpu = MIPS_CPU(cs);
CPUMIPSState *env = &cpu->env;
+ bool update_badinstr = 0;
target_ulong offset;
int cause = -1;
const char *name;
@@ -566,10 +587,13 @@ void mips_cpu_do_interrupt(CPUState *cs)
goto set_EPC;
case EXCP_LTLBL:
cause = 1;
+ update_badinstr = !(env->error_code & EXCP_INST_NOTAVAIL);
goto set_EPC;
case EXCP_TLBL:
cause = 2;
- if (env->error_code == 1 && !(env->CP0_Status & (1 << CP0St_EXL))) {
+ update_badinstr = !(env->error_code & EXCP_INST_NOTAVAIL);
+ if ((env->error_code & EXCP_TLB_NOMATCH) &&
+ !(env->CP0_Status & (1 << CP0St_EXL))) {
#if defined(TARGET_MIPS64)
int R = env->CP0_BadVAddr >> 62;
int UX = (env->CP0_Status & (1 << CP0St_UX)) != 0;
@@ -586,7 +610,9 @@ void mips_cpu_do_interrupt(CPUState *cs)
goto set_EPC;
case EXCP_TLBS:
cause = 3;
- if (env->error_code == 1 && !(env->CP0_Status & (1 << CP0St_EXL))) {
+ update_badinstr = 1;
+ if ((env->error_code & EXCP_TLB_NOMATCH) &&
+ !(env->CP0_Status & (1 << CP0St_EXL))) {
#if defined(TARGET_MIPS64)
int R = env->CP0_BadVAddr >> 62;
int UX = (env->CP0_Status & (1 << CP0St_UX)) != 0;
@@ -603,9 +629,11 @@ void mips_cpu_do_interrupt(CPUState *cs)
goto set_EPC;
case EXCP_AdEL:
cause = 4;
+ update_badinstr = !(env->error_code & EXCP_INST_NOTAVAIL);
goto set_EPC;
case EXCP_AdES:
cause = 5;
+ update_badinstr = 1;
goto set_EPC;
case EXCP_IBE:
cause = 6;
@@ -615,32 +643,40 @@ void mips_cpu_do_interrupt(CPUState *cs)
goto set_EPC;
case EXCP_SYSCALL:
cause = 8;
+ update_badinstr = 1;
goto set_EPC;
case EXCP_BREAK:
cause = 9;
+ update_badinstr = 1;
goto set_EPC;
case EXCP_RI:
cause = 10;
+ update_badinstr = 1;
goto set_EPC;
case EXCP_CpU:
cause = 11;
+ update_badinstr = 1;
env->CP0_Cause = (env->CP0_Cause & ~(0x3 << CP0Ca_CE)) |
(env->error_code << CP0Ca_CE);
goto set_EPC;
case EXCP_OVERFLOW:
cause = 12;
+ update_badinstr = 1;
goto set_EPC;
case EXCP_TRAP:
cause = 13;
+ update_badinstr = 1;
goto set_EPC;
case EXCP_FPE:
cause = 15;
+ update_badinstr = 1;
goto set_EPC;
case EXCP_C2E:
cause = 18;
goto set_EPC;
case EXCP_TLBRI:
cause = 19;
+ update_badinstr = 1;
goto set_EPC;
case EXCP_TLBXI:
cause = 20;
@@ -671,6 +707,9 @@ void mips_cpu_do_interrupt(CPUState *cs)
set_EPC:
if (!(env->CP0_Status & (1 << CP0St_EXL))) {
env->CP0_EPC = exception_resume_pc(env);
+ if (update_badinstr) {
+ set_badinstr_registers(env);
+ }
if (env->hflags & MIPS_HFLAG_BMASK) {
env->CP0_Cause |= (1U << CP0Ca_BD);
} else {