diff --git a/qemu/target-mips/cpu.h b/qemu/target-mips/cpu.h index cf2834d6..d18cadf4 100644 --- a/qemu/target-mips/cpu.h +++ b/qemu/target-mips/cpu.h @@ -474,6 +474,7 @@ struct CPUMIPSState { #define CP0C5_UFE 9 #define CP0C5_FRE 8 #define CP0C5_SBRI 6 +#define CP0C5_MVH 5 #define CP0C5_LLB 4 #define CP0C5_UFR 2 #define CP0C5_NFExists 0 diff --git a/qemu/target-mips/translate.c b/qemu/target-mips/translate.c index 5e28ab9d..a6ba1c67 100644 --- a/qemu/target-mips/translate.c +++ b/qemu/target-mips/translate.c @@ -865,8 +865,10 @@ enum { enum { OPC_MFC0 = (0x00 << 21) | OPC_CP0, OPC_DMFC0 = (0x01 << 21) | OPC_CP0, + OPC_MFHC0 = (0x02 << 21) | OPC_CP0, OPC_MTC0 = (0x04 << 21) | OPC_CP0, OPC_DMTC0 = (0x05 << 21) | OPC_CP0, + OPC_MTHC0 = (0x06 << 21) | OPC_CP0, OPC_MFTR = (0x08 << 21) | OPC_CP0, OPC_RDPGPR = (0x0A << 21) | OPC_CP0, OPC_MFMC0 = (0x0B << 21) | OPC_CP0, @@ -1407,6 +1409,9 @@ typedef struct DisasContext { int ie; bool bi; bool bp; + uint64_t PAMask; + bool mvh; + int CP0_LLAddr_shift; // Unicorn engine struct uc_struct *uc; } DisasContext; @@ -1823,6 +1828,15 @@ static inline void check_mips_64(DisasContext *ctx) } #endif +#ifndef CONFIG_USER_ONLY +static inline void check_mvh(DisasContext *ctx) +{ + if (unlikely(!ctx->mvh)) { + generate_exception(ctx, EXCP_RI); + } +} +#endif + /* Define small wrappers for gen_load_fpr* so that we have a uniform calling interface for 32 and 64-bit FPRs. No sense in changing all callers for gen_load_fpr32 when we need the CTX parameter for @@ -4901,6 +4915,60 @@ static inline void gen_move_low32(TCGContext *s, TCGv ret, TCGv_i64 arg) #endif } +static inline void gen_mthc0_entrylo(TCGContext *s, TCGv arg, target_ulong off) +{ + TCGv_i64 t0 = tcg_temp_new_i64(s); + TCGv_i64 t1 = tcg_temp_new_i64(s); + + tcg_gen_ext_tl_i64(s, t0, arg); + tcg_gen_ld_i64(s, t1, s->cpu_env, off); +#if defined(TARGET_MIPS64) + tcg_gen_deposit_i64(s, t1, t1, t0, 30, 32); +#else + tcg_gen_concat32_i64(s, t1, t1, t0); +#endif + tcg_gen_st_i64(s, t1, s->cpu_env, off); + tcg_temp_free_i64(s, t1); + tcg_temp_free_i64(s, t0); +} + +static inline void gen_mthc0_store64(TCGContext *s, TCGv arg, target_ulong off) +{ + TCGv_i64 t0 = tcg_temp_new_i64(s); + TCGv_i64 t1 = tcg_temp_new_i64(s); + + tcg_gen_ext_tl_i64(s, t0, arg); + tcg_gen_ld_i64(s, t1, s->cpu_env, off); + tcg_gen_concat32_i64(s, t1, t1, t0); + tcg_gen_st_i64(s, t1, s->cpu_env, off); + tcg_temp_free_i64(s, t1); + tcg_temp_free_i64(s, t0); +} + +static inline void gen_mfhc0_entrylo(TCGContext *s, TCGv arg, target_ulong off) +{ + TCGv_i64 t0 = tcg_temp_new_i64(s); + + tcg_gen_ld_i64(s, t0, s->cpu_env, off); +#if defined(TARGET_MIPS64) + tcg_gen_shri_i64(s, t0, t0, 30); +#else + tcg_gen_shri_i64(s, t0, t0, 32); +#endif + gen_move_low32(s, arg, t0); + tcg_temp_free_i64(s, t0); +} + +static inline void gen_mfhc0_load64(TCGContext *s, TCGv arg, target_ulong off, int shift) +{ + TCGv_i64 t0 = tcg_temp_new_i64(s); + + tcg_gen_ld_i64(s, t0, s->cpu_env, off); + tcg_gen_shri_i64(s, t0, t0, 32 + shift); + gen_move_low32(s, arg, t0); + tcg_temp_free_i64(s, t0); +} + static inline void gen_mfc0_load32 (DisasContext *ctx, TCGv arg, target_ulong off) { TCGContext *tcg_ctx = ctx->uc->tcg_ctx; @@ -4935,6 +5003,142 @@ static inline void gen_mtc0_store64 (DisasContext *ctx, TCGv arg, target_ulong o tcg_gen_st_tl(tcg_ctx, arg, tcg_ctx->cpu_env, off); } +static void gen_mfhc0(DisasContext *ctx, TCGv arg, int reg, int sel) +{ + TCGContext *s = ctx->uc->tcg_ctx; + const char *rn = "invalid"; + + if (!(ctx->hflags & MIPS_HFLAG_ELPA)) { + goto mfhc0_read_zero; + } + + switch (reg) { + case 2: + switch (sel) { + case 0: + gen_mfhc0_entrylo(s, arg, offsetof(CPUMIPSState, CP0_EntryLo0)); + rn = "EntryLo0"; + break; + default: + goto mfhc0_read_zero; + } + break; + case 3: + switch (sel) { + case 0: + gen_mfhc0_entrylo(s, arg, offsetof(CPUMIPSState, CP0_EntryLo1)); + rn = "EntryLo1"; + break; + default: + goto mfhc0_read_zero; + } + break; + case 17: + switch (sel) { + case 0: + gen_mfhc0_load64(s, arg, offsetof(CPUMIPSState, lladdr), + ctx->CP0_LLAddr_shift); + rn = "LLAddr"; + break; + default: + goto mfhc0_read_zero; + } + break; + case 28: + switch (sel) { + case 0: + case 2: + case 4: + case 6: + gen_mfhc0_load64(s, arg, offsetof(CPUMIPSState, CP0_TagLo), 0); + rn = "TagLo"; + break; + default: + goto mfhc0_read_zero; + } + break; + default: + goto mfhc0_read_zero; + } + + (void)rn; /* avoid a compiler warning */ + LOG_DISAS("mfhc0 %s (reg %d sel %d)\n", rn, reg, sel); + return; + +mfhc0_read_zero: + LOG_DISAS("mfhc0 %s (reg %d sel %d)\n", rn, reg, sel); + tcg_gen_movi_tl(s, arg, 0); +} + +static void gen_mthc0(DisasContext *ctx, TCGv arg, int reg, int sel) +{ + TCGContext *s = ctx->uc->tcg_ctx; + const char *rn = "invalid"; + uint64_t mask = ctx->PAMask >> 36; + + if (!(ctx->hflags & MIPS_HFLAG_ELPA)) { + goto mthc0_nop; + } + + switch (reg) { + case 2: + switch (sel) { + case 0: + tcg_gen_andi_tl(s, arg, arg, mask); + gen_mthc0_entrylo(s, arg, offsetof(CPUMIPSState, CP0_EntryLo0)); + rn = "EntryLo0"; + break; + default: + goto mthc0_nop; + } + break; + case 3: + switch (sel) { + case 0: + tcg_gen_andi_tl(s, arg, arg, mask); + gen_mthc0_entrylo(s, arg, offsetof(CPUMIPSState, CP0_EntryLo1)); + rn = "EntryLo1"; + break; + default: + goto mthc0_nop; + } + break; + case 17: + switch (sel) { + case 0: + /* LLAddr is read-only (the only exception is bit 0 if LLB is + supported); the CP0_LLAddr_rw_bitmask does not seem to be + relevant for modern MIPS cores supporting MTHC0, therefore + treating MTHC0 to LLAddr as NOP. */ + rn = "LLAddr"; + break; + default: + goto mthc0_nop; + } + break; + case 28: + switch (sel) { + case 0: + case 2: + case 4: + case 6: + tcg_gen_andi_tl(s, arg, arg, mask); + gen_mthc0_store64(s, arg, offsetof(CPUMIPSState, CP0_TagLo)); + rn = "TagLo"; + break; + default: + goto mthc0_nop; + } + break; + default: + goto mthc0_nop; + } + + (void)rn; /* avoid a compiler warning */ +mthc0_nop: + LOG_DISAS("mthc0 %s (reg %d sel %d)\n", rn, reg, sel); +} + static inline void gen_mfc0_unimplemented(DisasContext *ctx, TCGv arg) { TCGContext *tcg_ctx = ctx->uc->tcg_ctx; @@ -7949,6 +8153,25 @@ static void gen_cp0 (CPUMIPSState *env, DisasContext *ctx, uint32_t opc, int rt, opn = "dmtc0"; break; #endif + case OPC_MFHC0: + check_mvh(ctx); + if (rt == 0) { + /* Treat as NOP. */ + return; + } + gen_mfhc0(ctx, *cpu_gpr[rt], rd, ctx->opcode & 0x7); + opn = "mfhc0"; + break; + case OPC_MTHC0: + check_mvh(ctx); + { + TCGv t0 = tcg_temp_new(tcg_ctx); + gen_load_gpr(ctx, t0, rt); + gen_mthc0(ctx, t0, rd, ctx->opcode & 0x7); + tcg_temp_free(tcg_ctx, t0); + } + opn = "mthc0"; + break; case OPC_MFTR: check_insn(ctx, ASE_MT); if (rd == 0) {