From aea14095ea91f792ee43ee52fe6032cd8cdd7190 Mon Sep 17 00:00:00 2001 From: Leon Alrae Date: Mon, 7 Jul 2014 11:24:01 +0100 Subject: target-mips: add BadInstr and BadInstrP support BadInstr Register (CP0 Register 8, Select 1) The BadInstr register is a read-only register that capture the most recent instruction which caused an exception. BadInstrP Register (CP0 Register 8, Select 2) The BadInstrP register contains the prior branch instruction, when the faulting instruction is in a branch delay slot. Using error_code to indicate whether AdEL or TLBL was triggered during instruction fetch, in this case BadInstr is not updated as valid instruction word is not available. Signed-off-by: Leon Alrae Reviewed-by: Yongbok Kim --- target-mips/helper.c | 45 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 42 insertions(+), 3 deletions(-) (limited to 'target-mips/helper.c') 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 { -- cgit v1.2.3