diff mbox

[08/12] target-mips: add BadInstr and BadInstrP support

Message ID 1403189143-54609-9-git-send-email-leon.alrae@imgtec.com
State New
Headers show

Commit Message

Leon Alrae June 19, 2014, 2:45 p.m. UTC
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.

The BadInstr and BadInstrP registers are provided to allow acceleration of
instruction emulation.

Signed-off-by: Leon Alrae <leon.alrae@imgtec.com>
---
 target-mips/cpu.h       |    6 +++
 target-mips/helper.c    |   23 +++++++++++
 target-mips/translate.c |  102 ++++++++++++++++++++++++++++++++++++++++++++---
 3 files changed, 125 insertions(+), 6 deletions(-)

Comments

Aurelien Jarno June 19, 2014, 10:13 p.m. UTC | #1
On Thu, Jun 19, 2014 at 03:45:39PM +0100, Leon Alrae wrote:
> 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.
> 
> The BadInstr and BadInstrP registers are provided to allow acceleration of
> instruction emulation.
> 
> Signed-off-by: Leon Alrae <leon.alrae@imgtec.com>
> ---
>  target-mips/cpu.h       |    6 +++
>  target-mips/helper.c    |   23 +++++++++++
>  target-mips/translate.c |  102 ++++++++++++++++++++++++++++++++++++++++++++---
>  3 files changed, 125 insertions(+), 6 deletions(-)

I don't think this should implemented that way, as it would have a
significant impact on the performances. Given we have the fault address
(we fill EPC), we can fetch the corresponding opcode. There might be
some code change to do for the branches, so that we can get the
informations we need from re-translation (this might also simplify the
current branches code).

> diff --git a/target-mips/cpu.h b/target-mips/cpu.h
> index 14edf57..785a29b 100644
> --- a/target-mips/cpu.h
> +++ b/target-mips/cpu.h
> @@ -177,6 +177,10 @@ struct TCState {
>      target_ulong CP0_TCScheFBack;
>      int32_t CP0_Debug_tcstatus;
>      target_ulong CP0_UserLocal;
> +    uint32_t last_instr;
> +    uint32_t CP0_BadInstr;
> +    uint32_t last_branch;
> +    uint32_t CP0_BadInstrP;
>  };
>  
>  typedef struct CPUMIPSState CPUMIPSState;
> @@ -383,6 +387,8 @@ struct CPUMIPSState {
>  #define CP0C2_SA   0
>      int32_t CP0_Config3;
>  #define CP0C3_M    31
> +#define CP0C3_BP 27
> +#define CP0C3_BI 26
>  #define CP0C3_ISA_ON_EXC 16
>  #define CP0C3_ULRI 13
>  #define CP0C3_RXI  12
> diff --git a/target-mips/helper.c b/target-mips/helper.c
> index ab9217f..1a3a6af 100644
> --- a/target-mips/helper.c
> +++ b/target-mips/helper.c
> @@ -439,6 +439,7 @@ void mips_cpu_do_interrupt(CPUState *cs)
>  #if !defined(CONFIG_USER_ONLY)
>      MIPSCPU *cpu = MIPS_CPU(cs);
>      CPUMIPSState *env = &cpu->env;
> +    int update_badinstr = 0;
>      target_ulong offset;
>      int cause = -1;
>      const char *name;
> @@ -547,9 +548,11 @@ void mips_cpu_do_interrupt(CPUState *cs)
>          goto set_EPC;
>      case EXCP_LTLBL:
>          cause = 1;
> +        update_badinstr = 1;
>          goto set_EPC;
>      case EXCP_TLBL:
>          cause = 2;
> +        update_badinstr = 1;
>          if (env->error_code == 1 && !(env->CP0_Status & (1 << CP0St_EXL))) {
>  #if defined(TARGET_MIPS64)
>              int R = env->CP0_BadVAddr >> 62;
> @@ -567,6 +570,7 @@ void mips_cpu_do_interrupt(CPUState *cs)
>          goto set_EPC;
>      case EXCP_TLBS:
>          cause = 3;
> +        update_badinstr = 1;
>          if (env->error_code == 1 && !(env->CP0_Status & (1 << CP0St_EXL))) {
>  #if defined(TARGET_MIPS64)
>              int R = env->CP0_BadVAddr >> 62;
> @@ -584,9 +588,11 @@ void mips_cpu_do_interrupt(CPUState *cs)
>          goto set_EPC;
>      case EXCP_AdEL:
>          cause = 4;
> +        update_badinstr = 1;
>          goto set_EPC;
>      case EXCP_AdES:
>          cause = 5;
> +        update_badinstr = 1;
>          goto set_EPC;
>      case EXCP_IBE:
>          cause = 6;
> @@ -596,35 +602,44 @@ 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;
> +        update_badinstr = 1;
>          goto set_EPC;
>      case EXCP_MDMX:
>          cause = 22;
> @@ -650,6 +665,14 @@ void mips_cpu_do_interrupt(CPUState *cs)
>              offset = 0x20000100;
>          }
>   set_EPC:
> +        if (env->CP0_Config3 & (1 << CP0C3_BI) && update_badinstr) {
> +            env->active_tc.CP0_BadInstr = env->active_tc.last_instr;
> +        }
> +        if (env->CP0_Config3 & (1 << CP0C3_BP) && update_badinstr &&
> +            env->hflags & MIPS_HFLAG_BMASK) {
> +            env->active_tc.CP0_BadInstrP = env->active_tc.last_branch;
> +        }
> +
>          if (!(env->CP0_Status & (1 << CP0St_EXL))) {
>              env->CP0_EPC = exception_resume_pc(env);
>              if (env->hflags & MIPS_HFLAG_BMASK) {
> diff --git a/target-mips/translate.c b/target-mips/translate.c
> index e511c75..b27d22e 100644
> --- a/target-mips/translate.c
> +++ b/target-mips/translate.c
> @@ -1116,6 +1116,8 @@ static TCGv cpu_dspctrl, btarget, bcond;
>  static TCGv_i32 hflags;
>  static TCGv_i32 fpu_fcr0, fpu_fcr31;
>  static TCGv_i64 fpu_f64[32];
> +static TCGv_i32 last_instr;
> +static TCGv_i32 last_branch;
>  
>  static uint32_t gen_opc_hflags[OPC_BUF_SIZE];
>  static target_ulong gen_opc_btarget[OPC_BUF_SIZE];
> @@ -1167,7 +1169,8 @@ static target_ulong gen_opc_btarget[OPC_BUF_SIZE];
>  typedef struct DisasContext {
>      struct TranslationBlock *tb;
>      target_ulong pc, saved_pc;
> -    uint32_t opcode;
> +    uint32_t opcode, saved_opcode;
> +    uint32_t opcode_branch, saved_opcode_branch;
>      int singlestep_enabled;
>      int insn_flags;
>      /* Routine used to access memory */
> @@ -1179,6 +1182,8 @@ typedef struct DisasContext {
>      int32_t kscrexist;
>      bool rxi;
>      bool ie;
> +    bool bi;
> +    bool bp;
>  } DisasContext;
>  
>  enum {
> @@ -1391,6 +1396,15 @@ static inline void save_cpu_state (DisasContext *ctx, int do_save_pc)
>          gen_save_pc(ctx->pc);
>          ctx->saved_pc = ctx->pc;
>      }
> +    if (ctx->bi && ctx->opcode != ctx->saved_opcode) {
> +        tcg_gen_movi_i32(last_instr, ctx->opcode);
> +        ctx->saved_opcode = ctx->opcode;
> +    }
> +    if (ctx->bp && ctx->hflags & MIPS_HFLAG_BMASK &&
> +        ctx->opcode_branch != ctx->saved_opcode_branch) {
> +        tcg_gen_movi_i32(last_branch, ctx->opcode_branch);
> +        ctx->saved_opcode_branch = ctx->opcode_branch;
> +    }
>      if (ctx->hflags != ctx->saved_hflags) {
>          tcg_gen_movi_i32(hflags, ctx->hflags);
>          ctx->saved_hflags = ctx->hflags;
> @@ -4160,6 +4174,9 @@ static void gen_compute_branch (DisasContext *ctx, uint32_t opc,
>  #endif
>          generate_exception(ctx, EXCP_RI);
>          goto out;
> +    } else {
> +        /* capture branch opcode prior to delay slot */
> +        ctx->opcode_branch = ctx->opcode;
>      }
>  
>      /* Load needed operands */
> @@ -4868,9 +4885,19 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel)
>              tcg_gen_ext32s_tl(arg, arg);
>              rn = "BadVAddr";
>              break;
> +        case 1:
> +            gen_mfc0_load32(arg, offsetof(CPUMIPSState,
> +                                          active_tc.CP0_BadInstr));
> +            rn = "BadInstr";
> +            break;
> +        case 2:
> +            gen_mfc0_load32(arg, offsetof(CPUMIPSState,
> +                                          active_tc.CP0_BadInstrP));
> +            rn = "BadInstrP";
> +            break;
>          default:
>              goto die;
> -       }
> +        }
>          break;
>      case 9:
>          switch (sel) {
> @@ -5464,8 +5491,22 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel)
>          }
>          break;
>      case 8:
> -        /* ignored */
> -        rn = "BadVAddr";
> +        switch (sel) {
> +        case 0:
> +            /* ignored */
> +            rn = "BadVAddr";
> +            break;
> +        case 1:
> +            /* ignored */
> +            rn = "BadInstr";
> +            break;
> +        case 2:
> +            /* ignored */
> +            rn = "BadInstrP";
> +            break;
> +        default:
> +            goto die;
> +        }
>          break;
>      case 9:
>          switch (sel) {
> @@ -6090,6 +6131,16 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel)
>              tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_BadVAddr));
>              rn = "BadVAddr";
>              break;
> +        case 1:
> +            gen_mfc0_load32(arg, offsetof(CPUMIPSState,
> +                                          active_tc.CP0_BadInstr));
> +            rn = "BadInstr";
> +            break;
> +        case 2:
> +            gen_mfc0_load32(arg, offsetof(CPUMIPSState,
> +                                          active_tc.CP0_BadInstrP));
> +            rn = "BadInstrP";
> +            break;
>          default:
>              goto die;
>          }
> @@ -6671,8 +6722,22 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel)
>          }
>          break;
>      case 8:
> -        /* ignored */
> -        rn = "BadVAddr";
> +        switch (sel) {
> +        case 0:
> +            /* ignored */
> +            rn = "BadVAddr";
> +            break;
> +        case 1:
> +            /* ignored */
> +            rn = "BadInstr";
> +            break;
> +        case 2:
> +            /* ignored */
> +            rn = "BadInstrP";
> +            break;
> +        default:
> +            goto die;
> +        }
>          break;
>      case 9:
>          switch (sel) {
> @@ -7633,6 +7698,11 @@ static void gen_compute_branch1(DisasContext *ctx, uint32_t op,
>      const char *opn = "cp1 cond branch";
>      TCGv_i32 t0 = tcg_temp_new_i32();
>  
> +    if (!(ctx->hflags & MIPS_HFLAG_BMASK)) {
> +        /* capture branch opcode prior to delay slot */
> +        ctx->opcode_branch = ctx->opcode;
> +    }
> +
>      if (cc != 0)
>          check_insn(ctx, ISA_MIPS4 | ISA_MIPS32);
>  
> @@ -7747,6 +7817,12 @@ static void gen_compute_branch1_r6(DisasContext *ctx, uint32_t op,
>      const char *opn = "cp1 cond branch";
>  
>      TCGv_i64 t0 = tcg_temp_new_i64();
> +
> +    if (!(ctx->hflags & MIPS_HFLAG_BMASK)) {
> +        /* capture branch opcode prior to delay slot */
> +        ctx->opcode_branch = ctx->opcode;
> +    }
> +
>      gen_load_fpr64(ctx, t0, ft);
>      tcg_gen_andi_i64(t0, t0, 1);
>  
> @@ -17568,6 +17644,12 @@ gen_intermediate_code_internal(MIPSCPU *cpu, TranslationBlock *tb,
>      ctx.kscrexist = (env->CP0_Config4 >> CP0C4_KScrExist) & 0xff;
>      ctx.rxi = (env->CP0_Config3 >> CP0C3_RXI) & 1;
>      ctx.ie = (env->CP0_Config4 >> CP0C4_IE) & 1;
> +    ctx.bi = (env->CP0_Config3 >> CP0C3_BI) & 1;
> +    ctx.bp = (env->CP0_Config3 >> CP0C3_BP) & 1;
> +    ctx.opcode = 0;
> +    ctx.saved_opcode = 0;
> +    ctx.opcode_branch = 0;
> +    ctx.saved_opcode_branch = 0;
>      /* Restore delay slot state from the tb context.  */
>      ctx.hflags = (uint32_t)tb->flags; /* FIXME: maybe use 64 bits here? */
>      ctx.ulri = env->CP0_Config3 & (1 << CP0C3_ULRI);
> @@ -17869,6 +17951,14 @@ void mips_tcg_init(void)
>      fpu_fcr31 = tcg_global_mem_new_i32(TCG_AREG0,
>                                         offsetof(CPUMIPSState, active_fpu.fcr31),
>                                         "fcr31");
> +    last_instr = tcg_global_mem_new_i32(TCG_AREG0,
> +                                        offsetof(CPUMIPSState,
> +                                                 active_tc.last_instr),
> +                                        "last_instr");
> +    last_branch = tcg_global_mem_new_i32(TCG_AREG0,
> +                                         offsetof(CPUMIPSState,
> +                                                  active_tc.last_branch),
> +                                         "last_branch");
>  
>      inited = 1;
>  }
> -- 
> 1.7.5.4
> 
>
Leon Alrae July 8, 2014, 8:07 a.m. UTC | #2
On 19/06/2014 23:13, Aurelien Jarno wrote:
> I don't think this should implemented that way, as it would have a
> significant impact on the performances. Given we have the fault address
> (we fill EPC), we can fetch the corresponding opcode. There might be
> some code change to do for the branches, so that we can get the
> informations we need from re-translation (this might also simplify the
> current branches code).

I changed the BadInstr implementation in v2. Now the instruction word is
fetched when we have the exception (and the valid instruction word is
available), so we don't have to generate code to save the last
instruction. The same has been done for BadInstrP and the branch prior
to the delay slot.

Thanks,
Leon
diff mbox

Patch

diff --git a/target-mips/cpu.h b/target-mips/cpu.h
index 14edf57..785a29b 100644
--- a/target-mips/cpu.h
+++ b/target-mips/cpu.h
@@ -177,6 +177,10 @@  struct TCState {
     target_ulong CP0_TCScheFBack;
     int32_t CP0_Debug_tcstatus;
     target_ulong CP0_UserLocal;
+    uint32_t last_instr;
+    uint32_t CP0_BadInstr;
+    uint32_t last_branch;
+    uint32_t CP0_BadInstrP;
 };
 
 typedef struct CPUMIPSState CPUMIPSState;
@@ -383,6 +387,8 @@  struct CPUMIPSState {
 #define CP0C2_SA   0
     int32_t CP0_Config3;
 #define CP0C3_M    31
+#define CP0C3_BP 27
+#define CP0C3_BI 26
 #define CP0C3_ISA_ON_EXC 16
 #define CP0C3_ULRI 13
 #define CP0C3_RXI  12
diff --git a/target-mips/helper.c b/target-mips/helper.c
index ab9217f..1a3a6af 100644
--- a/target-mips/helper.c
+++ b/target-mips/helper.c
@@ -439,6 +439,7 @@  void mips_cpu_do_interrupt(CPUState *cs)
 #if !defined(CONFIG_USER_ONLY)
     MIPSCPU *cpu = MIPS_CPU(cs);
     CPUMIPSState *env = &cpu->env;
+    int update_badinstr = 0;
     target_ulong offset;
     int cause = -1;
     const char *name;
@@ -547,9 +548,11 @@  void mips_cpu_do_interrupt(CPUState *cs)
         goto set_EPC;
     case EXCP_LTLBL:
         cause = 1;
+        update_badinstr = 1;
         goto set_EPC;
     case EXCP_TLBL:
         cause = 2;
+        update_badinstr = 1;
         if (env->error_code == 1 && !(env->CP0_Status & (1 << CP0St_EXL))) {
 #if defined(TARGET_MIPS64)
             int R = env->CP0_BadVAddr >> 62;
@@ -567,6 +570,7 @@  void mips_cpu_do_interrupt(CPUState *cs)
         goto set_EPC;
     case EXCP_TLBS:
         cause = 3;
+        update_badinstr = 1;
         if (env->error_code == 1 && !(env->CP0_Status & (1 << CP0St_EXL))) {
 #if defined(TARGET_MIPS64)
             int R = env->CP0_BadVAddr >> 62;
@@ -584,9 +588,11 @@  void mips_cpu_do_interrupt(CPUState *cs)
         goto set_EPC;
     case EXCP_AdEL:
         cause = 4;
+        update_badinstr = 1;
         goto set_EPC;
     case EXCP_AdES:
         cause = 5;
+        update_badinstr = 1;
         goto set_EPC;
     case EXCP_IBE:
         cause = 6;
@@ -596,35 +602,44 @@  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;
+        update_badinstr = 1;
         goto set_EPC;
     case EXCP_MDMX:
         cause = 22;
@@ -650,6 +665,14 @@  void mips_cpu_do_interrupt(CPUState *cs)
             offset = 0x20000100;
         }
  set_EPC:
+        if (env->CP0_Config3 & (1 << CP0C3_BI) && update_badinstr) {
+            env->active_tc.CP0_BadInstr = env->active_tc.last_instr;
+        }
+        if (env->CP0_Config3 & (1 << CP0C3_BP) && update_badinstr &&
+            env->hflags & MIPS_HFLAG_BMASK) {
+            env->active_tc.CP0_BadInstrP = env->active_tc.last_branch;
+        }
+
         if (!(env->CP0_Status & (1 << CP0St_EXL))) {
             env->CP0_EPC = exception_resume_pc(env);
             if (env->hflags & MIPS_HFLAG_BMASK) {
diff --git a/target-mips/translate.c b/target-mips/translate.c
index e511c75..b27d22e 100644
--- a/target-mips/translate.c
+++ b/target-mips/translate.c
@@ -1116,6 +1116,8 @@  static TCGv cpu_dspctrl, btarget, bcond;
 static TCGv_i32 hflags;
 static TCGv_i32 fpu_fcr0, fpu_fcr31;
 static TCGv_i64 fpu_f64[32];
+static TCGv_i32 last_instr;
+static TCGv_i32 last_branch;
 
 static uint32_t gen_opc_hflags[OPC_BUF_SIZE];
 static target_ulong gen_opc_btarget[OPC_BUF_SIZE];
@@ -1167,7 +1169,8 @@  static target_ulong gen_opc_btarget[OPC_BUF_SIZE];
 typedef struct DisasContext {
     struct TranslationBlock *tb;
     target_ulong pc, saved_pc;
-    uint32_t opcode;
+    uint32_t opcode, saved_opcode;
+    uint32_t opcode_branch, saved_opcode_branch;
     int singlestep_enabled;
     int insn_flags;
     /* Routine used to access memory */
@@ -1179,6 +1182,8 @@  typedef struct DisasContext {
     int32_t kscrexist;
     bool rxi;
     bool ie;
+    bool bi;
+    bool bp;
 } DisasContext;
 
 enum {
@@ -1391,6 +1396,15 @@  static inline void save_cpu_state (DisasContext *ctx, int do_save_pc)
         gen_save_pc(ctx->pc);
         ctx->saved_pc = ctx->pc;
     }
+    if (ctx->bi && ctx->opcode != ctx->saved_opcode) {
+        tcg_gen_movi_i32(last_instr, ctx->opcode);
+        ctx->saved_opcode = ctx->opcode;
+    }
+    if (ctx->bp && ctx->hflags & MIPS_HFLAG_BMASK &&
+        ctx->opcode_branch != ctx->saved_opcode_branch) {
+        tcg_gen_movi_i32(last_branch, ctx->opcode_branch);
+        ctx->saved_opcode_branch = ctx->opcode_branch;
+    }
     if (ctx->hflags != ctx->saved_hflags) {
         tcg_gen_movi_i32(hflags, ctx->hflags);
         ctx->saved_hflags = ctx->hflags;
@@ -4160,6 +4174,9 @@  static void gen_compute_branch (DisasContext *ctx, uint32_t opc,
 #endif
         generate_exception(ctx, EXCP_RI);
         goto out;
+    } else {
+        /* capture branch opcode prior to delay slot */
+        ctx->opcode_branch = ctx->opcode;
     }
 
     /* Load needed operands */
@@ -4868,9 +4885,19 @@  static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel)
             tcg_gen_ext32s_tl(arg, arg);
             rn = "BadVAddr";
             break;
+        case 1:
+            gen_mfc0_load32(arg, offsetof(CPUMIPSState,
+                                          active_tc.CP0_BadInstr));
+            rn = "BadInstr";
+            break;
+        case 2:
+            gen_mfc0_load32(arg, offsetof(CPUMIPSState,
+                                          active_tc.CP0_BadInstrP));
+            rn = "BadInstrP";
+            break;
         default:
             goto die;
-       }
+        }
         break;
     case 9:
         switch (sel) {
@@ -5464,8 +5491,22 @@  static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel)
         }
         break;
     case 8:
-        /* ignored */
-        rn = "BadVAddr";
+        switch (sel) {
+        case 0:
+            /* ignored */
+            rn = "BadVAddr";
+            break;
+        case 1:
+            /* ignored */
+            rn = "BadInstr";
+            break;
+        case 2:
+            /* ignored */
+            rn = "BadInstrP";
+            break;
+        default:
+            goto die;
+        }
         break;
     case 9:
         switch (sel) {
@@ -6090,6 +6131,16 @@  static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel)
             tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_BadVAddr));
             rn = "BadVAddr";
             break;
+        case 1:
+            gen_mfc0_load32(arg, offsetof(CPUMIPSState,
+                                          active_tc.CP0_BadInstr));
+            rn = "BadInstr";
+            break;
+        case 2:
+            gen_mfc0_load32(arg, offsetof(CPUMIPSState,
+                                          active_tc.CP0_BadInstrP));
+            rn = "BadInstrP";
+            break;
         default:
             goto die;
         }
@@ -6671,8 +6722,22 @@  static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel)
         }
         break;
     case 8:
-        /* ignored */
-        rn = "BadVAddr";
+        switch (sel) {
+        case 0:
+            /* ignored */
+            rn = "BadVAddr";
+            break;
+        case 1:
+            /* ignored */
+            rn = "BadInstr";
+            break;
+        case 2:
+            /* ignored */
+            rn = "BadInstrP";
+            break;
+        default:
+            goto die;
+        }
         break;
     case 9:
         switch (sel) {
@@ -7633,6 +7698,11 @@  static void gen_compute_branch1(DisasContext *ctx, uint32_t op,
     const char *opn = "cp1 cond branch";
     TCGv_i32 t0 = tcg_temp_new_i32();
 
+    if (!(ctx->hflags & MIPS_HFLAG_BMASK)) {
+        /* capture branch opcode prior to delay slot */
+        ctx->opcode_branch = ctx->opcode;
+    }
+
     if (cc != 0)
         check_insn(ctx, ISA_MIPS4 | ISA_MIPS32);
 
@@ -7747,6 +7817,12 @@  static void gen_compute_branch1_r6(DisasContext *ctx, uint32_t op,
     const char *opn = "cp1 cond branch";
 
     TCGv_i64 t0 = tcg_temp_new_i64();
+
+    if (!(ctx->hflags & MIPS_HFLAG_BMASK)) {
+        /* capture branch opcode prior to delay slot */
+        ctx->opcode_branch = ctx->opcode;
+    }
+
     gen_load_fpr64(ctx, t0, ft);
     tcg_gen_andi_i64(t0, t0, 1);
 
@@ -17568,6 +17644,12 @@  gen_intermediate_code_internal(MIPSCPU *cpu, TranslationBlock *tb,
     ctx.kscrexist = (env->CP0_Config4 >> CP0C4_KScrExist) & 0xff;
     ctx.rxi = (env->CP0_Config3 >> CP0C3_RXI) & 1;
     ctx.ie = (env->CP0_Config4 >> CP0C4_IE) & 1;
+    ctx.bi = (env->CP0_Config3 >> CP0C3_BI) & 1;
+    ctx.bp = (env->CP0_Config3 >> CP0C3_BP) & 1;
+    ctx.opcode = 0;
+    ctx.saved_opcode = 0;
+    ctx.opcode_branch = 0;
+    ctx.saved_opcode_branch = 0;
     /* Restore delay slot state from the tb context.  */
     ctx.hflags = (uint32_t)tb->flags; /* FIXME: maybe use 64 bits here? */
     ctx.ulri = env->CP0_Config3 & (1 << CP0C3_ULRI);
@@ -17869,6 +17951,14 @@  void mips_tcg_init(void)
     fpu_fcr31 = tcg_global_mem_new_i32(TCG_AREG0,
                                        offsetof(CPUMIPSState, active_fpu.fcr31),
                                        "fcr31");
+    last_instr = tcg_global_mem_new_i32(TCG_AREG0,
+                                        offsetof(CPUMIPSState,
+                                                 active_tc.last_instr),
+                                        "last_instr");
+    last_branch = tcg_global_mem_new_i32(TCG_AREG0,
+                                         offsetof(CPUMIPSState,
+                                                  active_tc.last_branch),
+                                         "last_branch");
 
     inited = 1;
 }