target-mips: add MTHC0 and MFHC0 instructions

Implement MTHC0 and MFHC0 instructions. In MIPS32 they are used to access
upper word of extended to 64-bits CP0 registers.

In MIPS64, when CP0 destination register specified is the EntryLo0 or
EntryLo1, bits 1:0 of the GPR appear at bits 31:30 of EntryLo0 or
EntryLo1. This is to compensate for RI and XI, which were shifted to bits
63:62 by MTC0 to EntryLo0 or EntryLo1. Therefore creating separate
functions for EntryLo0 and EntryLo1.

Backports commit 5204ea79ea739b557f47fc4db96c94edcb33a5d6 from qemu
This commit is contained in:
Leon Alrae 2018-02-13 14:01:56 -05:00 committed by Lioncash
parent 59865351e0
commit 8743ec8b6d
No known key found for this signature in database
GPG key ID: 4E3C3CC1031BA9C7
2 changed files with 224 additions and 0 deletions

View file

@ -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

View file

@ -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) {