From 46284f1a418a82f515ceb097d7bac23687aa0f74 Mon Sep 17 00:00:00 2001 From: Yongbok Kim Date: Tue, 20 Feb 2018 21:56:31 -0500 Subject: [PATCH] target-mips: implement R6 multi-threading MIPS Release 6 provides multi-threading features which replace pre-R6 MT Module. CP0.Config3.MT is always 0 in R6, instead there is new CP0.Config5.VP (Virtual Processor) bit which indicates presence of multi-threading support which includes CP0.GlobalNumber register and DVP/EVP instructions. Backports commit 01bc435b44b8802cc4697faa07d908684afbce4e from qemu --- qemu/header_gen.py | 2 ++ qemu/mips.h | 2 ++ qemu/mips64.h | 2 ++ qemu/mips64el.h | 2 ++ qemu/mipsel.h | 2 ++ qemu/target-mips/cpu.c | 9 +++++ qemu/target-mips/cpu.h | 27 ++++++++++++++ qemu/target-mips/helper.h | 4 +++ qemu/target-mips/op_helper.c | 50 ++++++++++++++++++++++++++ qemu/target-mips/translate.c | 59 +++++++++++++++++++++++++++++++ qemu/target-mips/translate_init.c | 3 +- 11 files changed, 161 insertions(+), 1 deletion(-) diff --git a/qemu/header_gen.py b/qemu/header_gen.py index 549fd72c..1877eebd 100644 --- a/qemu/header_gen.py +++ b/qemu/header_gen.py @@ -3682,6 +3682,8 @@ mips_symbols = ( 'helper_mttdsp', 'helper_dmt', 'helper_emt', + 'helper_evp', + 'helper_dvp', 'helper_dvpe', 'helper_evpe', 'helper_fork', diff --git a/qemu/mips.h b/qemu/mips.h index ff6939d7..448b1bc9 100644 --- a/qemu/mips.h +++ b/qemu/mips.h @@ -3629,6 +3629,8 @@ #define helper_mttdsp helper_mttdsp_mips #define helper_dmt helper_dmt_mips #define helper_emt helper_emt_mips +#define helper_evp helper_evp_mips +#define helper_dvp helper_dvp_mips #define helper_dvpe helper_dvpe_mips #define helper_evpe helper_evpe_mips #define helper_fork helper_fork_mips diff --git a/qemu/mips64.h b/qemu/mips64.h index a4e7b0b4..f76c9b81 100644 --- a/qemu/mips64.h +++ b/qemu/mips64.h @@ -3629,6 +3629,8 @@ #define helper_mttdsp helper_mttdsp_mips64 #define helper_dmt helper_dmt_mips64 #define helper_emt helper_emt_mips64 +#define helper_evp helper_evp_mips64 +#define helper_dvp helper_dvp_mips64 #define helper_dvpe helper_dvpe_mips64 #define helper_evpe helper_evpe_mips64 #define helper_fork helper_fork_mips64 diff --git a/qemu/mips64el.h b/qemu/mips64el.h index 47942f4c..0d42ad24 100644 --- a/qemu/mips64el.h +++ b/qemu/mips64el.h @@ -3629,6 +3629,8 @@ #define helper_mttdsp helper_mttdsp_mips64el #define helper_dmt helper_dmt_mips64el #define helper_emt helper_emt_mips64el +#define helper_evp helper_evp_mips64el +#define helper_dvp helper_dvp_mips64el #define helper_dvpe helper_dvpe_mips64el #define helper_evpe helper_evpe_mips64el #define helper_fork helper_fork_mips64el diff --git a/qemu/mipsel.h b/qemu/mipsel.h index a0e12926..31d4c891 100644 --- a/qemu/mipsel.h +++ b/qemu/mipsel.h @@ -3629,6 +3629,8 @@ #define helper_mttdsp helper_mttdsp_mipsel #define helper_dmt helper_dmt_mipsel #define helper_emt helper_emt_mipsel +#define helper_evp helper_evp_mipsel +#define helper_dvp helper_dvp_mipsel #define helper_dvpe helper_dvpe_mipsel #define helper_evpe helper_evpe_mipsel #define helper_fork helper_fork_mipsel diff --git a/qemu/target-mips/cpu.c b/qemu/target-mips/cpu.c index a7514e49..47ab0f65 100644 --- a/qemu/target-mips/cpu.c +++ b/qemu/target-mips/cpu.c @@ -76,6 +76,15 @@ static bool mips_cpu_has_work(CPUState *cs) has_work = false; } } + /* MIPS Release 6 has the ability to halt the CPU. */ + if (env->CP0_Config5 & (1 << CP0C5_VP)) { + if (cs->interrupt_request & CPU_INTERRUPT_WAKE) { + has_work = true; + } + if (!mips_vp_active(env)) { + has_work = false; + } + } return has_work; } diff --git a/qemu/target-mips/cpu.h b/qemu/target-mips/cpu.h index e0c3ed5d..0ea01c32 100644 --- a/qemu/target-mips/cpu.h +++ b/qemu/target-mips/cpu.h @@ -238,6 +238,8 @@ struct CPUMIPSState { int32_t CP0_Index; /* CP0_MVP* are per MVP registers. */ + int32_t CP0_VPControl; +#define CP0VPCtl_DIS 0 int32_t CP0_Random; int32_t CP0_VPEControl; #define CP0VPECo_YSI 21 @@ -287,6 +289,8 @@ struct CPUMIPSState { # define CP0EnLo_RI 31 # define CP0EnLo_XI 30 #endif + int32_t CP0_GlobalNumber; +#define CP0GN_VPId 0 target_ulong CP0_Context; target_ulong CP0_KScratch[MIPS_KSCRATCH_NUM]; int32_t CP0_PageMask; @@ -472,6 +476,7 @@ struct CPUMIPSState { #define CP0C5_XNP 13 #define CP0C5_UFE 9 #define CP0C5_FRE 8 +#define CP0C5_VP 7 #define CP0C5_SBRI 6 #define CP0C5_MVH 5 #define CP0C5_LLB 4 @@ -863,6 +868,28 @@ static inline int mips_vpe_active(CPUMIPSState *env) return active; } +static inline int mips_vp_active(CPUMIPSState *env) +{ + // Unicorn: commented out + //CPUState *other_cs = first_cpu; + + /* Check if the VP disabled other VPs (which means the VP is enabled) */ + if ((env->CP0_VPControl >> CP0VPCtl_DIS) & 1) { + return 1; + } + + /* Check if the virtual processor is disabled due to a DVP */ + /* Unicorn: commented out (CPU_FOREACH doesn't exist) + CPU_FOREACH(other_cs) { + MIPSCPU *other_cpu = MIPS_CPU(other_cs); + if ((&other_cpu->env != env) && + ((other_cpu->env.CP0_VPControl >> CP0VPCtl_DIS) & 1)) { + return 0; + } + }*/ + return 1; +} + #include "exec/exec-all.h" static inline void compute_hflags(CPUMIPSState *env) diff --git a/qemu/target-mips/helper.h b/qemu/target-mips/helper.h index 34df721a..5dd0bdb3 100644 --- a/qemu/target-mips/helper.h +++ b/qemu/target-mips/helper.h @@ -178,6 +178,10 @@ DEF_HELPER_1(dvpe, tl, env) DEF_HELPER_1(evpe, tl, env) #endif /* !CONFIG_USER_ONLY */ +/* R6 Multi-threading */ +DEF_HELPER_1(dvp, tl, env) +DEF_HELPER_1(evp, tl, env) + /* microMIPS functions */ DEF_HELPER_4(lwm, void, env, tl, tl, i32) DEF_HELPER_4(swm, void, env, tl, tl, i32) diff --git a/qemu/target-mips/op_helper.c b/qemu/target-mips/op_helper.c index 7809dcbb..f6641472 100644 --- a/qemu/target-mips/op_helper.c +++ b/qemu/target-mips/op_helper.c @@ -569,6 +569,14 @@ static bool mips_vpe_is_wfi(MIPSCPU *c) return cpu->halted && mips_vpe_active(env); } +static bool mips_vp_is_wfi(MIPSCPU *c) +{ + CPUState *cpu = CPU(c); + CPUMIPSState *env = &c->env; + + return cpu->halted && mips_vp_active(env); +} + static inline void mips_vpe_wake(MIPSCPU *c) { /* Dont set ->halted = 0 directly, let it be done via cpu_has_work @@ -1847,6 +1855,48 @@ target_ulong helper_yield(CPUMIPSState *env, target_ulong arg) return env->CP0_YQMask; } +/* R6 Multi-threading */ +#ifndef CONFIG_USER_ONLY +target_ulong helper_dvp(CPUMIPSState *env) +{ + //CPUState *other_cs = first_cpu; + target_ulong prev = env->CP0_VPControl; + + if (!((env->CP0_VPControl >> CP0VPCtl_DIS) & 1)) { + // Unicorn: commented out (CPU_FOREACH doesn't exist) + //CPU_FOREACH(other_cs) { + // MIPSCPU *other_cpu = MIPS_CPU(env->uc, other_cs); + // /* Turn off all VPs except the one executing the dvp. */ + // if (&other_cpu->env != env) { + // mips_vpe_sleep(other_cpu); + // } + //} + env->CP0_VPControl |= (1 << CP0VPCtl_DIS); + } + return prev; +} + +target_ulong helper_evp(CPUMIPSState *env) +{ + //CPUState *other_cs = first_cpu; + target_ulong prev = env->CP0_VPControl; + + if ((env->CP0_VPControl >> CP0VPCtl_DIS) & 1) { + // Unicorn: Commented out (CPU_FOREACH doesn't exist) + //CPU_FOREACH(other_cs) { + // MIPSCPU *other_cpu = MIPS_CPU(env->uc, other_cs); + // if ((&other_cpu->env != env) && !mips_vp_is_wfi(other_cpu)) { + // /* If the VP is WFI, don't disturb its sleep. + // * Otherwise, wake it up. */ + // mips_vpe_wake(other_cpu); + // } + //} + env->CP0_VPControl &= ~(1 << CP0VPCtl_DIS); + } + return prev; +} +#endif /* !CONFIG_USER_ONLY */ + #ifndef CONFIG_USER_ONLY /* TLB management */ static void r4k_mips_tlb_flush_extra (CPUMIPSState *env, int first) diff --git a/qemu/target-mips/translate.c b/qemu/target-mips/translate.c index 52b15241..4a098d8e 100644 --- a/qemu/target-mips/translate.c +++ b/qemu/target-mips/translate.c @@ -890,6 +890,8 @@ enum { OPC_EVPE = 0x01 | (1 << 5) | OPC_MFMC0, OPC_DI = (0 << 5) | (0x0C << 11) | OPC_MFMC0, OPC_EI = (1 << 5) | (0x0C << 11) | OPC_MFMC0, + OPC_DVP = 0x04 | (0 << 3) | (1 << 5) | (0 << 11) | OPC_MFMC0, + OPC_EVP = 0x04 | (0 << 3) | (0 << 5) | (0 << 11) | OPC_MFMC0, }; /* Coprocessor 0 (with rs == C0) */ @@ -1414,6 +1416,7 @@ typedef struct DisasContext { bool mvh; int CP0_LLAddr_shift; bool ps; + bool vp; // Unicorn engine struct uc_struct *uc; } DisasContext; @@ -5022,6 +5025,11 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) gen_helper_mfc0_mvpconf1(tcg_ctx, arg, tcg_ctx->cpu_env); rn = "MVPConf1"; break; + case 4: + CP0_CHECK(ctx->vp); + gen_mfc0_load32(ctx, arg, offsetof(CPUMIPSState, CP0_VPControl)); + rn = "VPControl"; + break; default: goto cp0_unimplemented; } @@ -5150,6 +5158,11 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) } rn = "EntryLo1"; break; + case 1: + CP0_CHECK(ctx->vp); + gen_mfc0_load32(ctx, arg, offsetof(CPUMIPSState, CP0_GlobalNumber)); + rn = "GlobalNumber"; + break; default: goto cp0_unimplemented; } @@ -5672,6 +5685,11 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) /* ignored */ rn = "MVPConf1"; break; + case 4: + CP0_CHECK(ctx->vp); + /* ignored */ + rn = "VPControl"; + break; default: goto cp0_unimplemented; } @@ -5774,6 +5792,11 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) gen_helper_mtc0_entrylo1(tcg_ctx, tcg_ctx->cpu_env, arg); rn = "EntryLo1"; break; + case 1: + CP0_CHECK(ctx->vp); + /* ignored */ + rn = "GlobalNumber"; + break; default: goto cp0_unimplemented; } @@ -6309,6 +6332,11 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel) gen_helper_mfc0_mvpconf1(tcg_ctx, arg, tcg_ctx->cpu_env); rn = "MVPConf1"; break; + case 4: + CP0_CHECK(ctx->vp); + gen_mfc0_load32(ctx, arg, offsetof(CPUMIPSState, CP0_VPControl)); + rn = "VPControl"; + break; default: goto cp0_unimplemented; } @@ -6410,6 +6438,11 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel) tcg_gen_ld_tl(tcg_ctx, arg, tcg_ctx->cpu_env, offsetof(CPUMIPSState, CP0_EntryLo1)); rn = "EntryLo1"; break; + case 1: + CP0_CHECK(ctx->vp); + gen_mfc0_load32(ctx, arg, offsetof(CPUMIPSState, CP0_GlobalNumber)); + rn = "GlobalNumber"; + break; default: goto cp0_unimplemented; } @@ -6917,6 +6950,11 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) /* ignored */ rn = "MVPConf1"; break; + case 4: + CP0_CHECK(ctx->vp); + /* ignored */ + rn = "VPControl"; + break; default: goto cp0_unimplemented; } @@ -7017,6 +7055,11 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) gen_helper_dmtc0_entrylo1(tcg_ctx, tcg_ctx->cpu_env, arg); rn = "EntryLo1"; break; + case 1: + CP0_CHECK(ctx->vp); + /* ignored */ + rn = "GlobalNumber"; + break; default: goto cp0_unimplemented; } @@ -19242,6 +19285,20 @@ static void decode_opc(CPUMIPSState *env, DisasContext *ctx, bool *insn_need_pat gen_helper_evpe(tcg_ctx, t0, tcg_ctx->cpu_env); gen_store_gpr(tcg_ctx, t0, rt); break; + case OPC_DVP: + check_insn(ctx, ISA_MIPS32R6); + if (ctx->vp) { + gen_helper_dvp(tcg_ctx, t0, tcg_ctx->cpu_env); + gen_store_gpr(tcg_ctx, t0, rt); + } + break; + case OPC_EVP: + check_insn(ctx, ISA_MIPS32R6); + if (ctx->vp) { + gen_helper_evp(tcg_ctx, t0, tcg_ctx->cpu_env); + gen_store_gpr(tcg_ctx, t0, rt); + } + break; case OPC_DI: check_insn(ctx, ISA_MIPS32R2); save_cpu_state(ctx, 1); @@ -19773,6 +19830,7 @@ void gen_intermediate_code(CPUMIPSState *env, struct TranslationBlock *tb) ctx.ulri = (env->CP0_Config3 >> CP0C3_ULRI) & 1; ctx.ps = ((env->active_fpu.fcr0 >> FCR0_PS) & 1) || (env->insn_flags & (INSN_LOONGSON2E | INSN_LOONGSON2F)); + ctx.vp = (env->CP0_Config5 >> CP0C5_VP) & 1; restore_cpu_state(env, &ctx); #ifdef CONFIG_USER_ONLY ctx.mem_idx = MIPS_HFLAG_UM; @@ -20196,6 +20254,7 @@ void cpu_state_reset(CPUMIPSState *env) env->CP0_Random = env->tlb->nb_tlb - 1; env->tlb->tlb_in_use = env->tlb->nb_tlb; env->CP0_Wired = 0; + env->CP0_GlobalNumber = (cs->cpu_index & 0xFF) << CP0GN_VPId; env->CP0_EBase = (cs->cpu_index & 0x3FF); env->CP0_EBase |= 0x80000000; env->CP0_Status = (1 << CP0St_BEV) | (1 << CP0St_ERL); diff --git a/qemu/target-mips/translate_init.c b/qemu/target-mips/translate_init.c index 521d97c8..d3e9a1ad 100644 --- a/qemu/target-mips/translate_init.c +++ b/qemu/target-mips/translate_init.c @@ -788,7 +788,8 @@ static const mips_def_t mips_defs[] = (1 << CP0C3_RXI) | (1 << CP0C3_LPA), MIPS_CONFIG4 | (1U << CP0C4_M) | (3 << CP0C4_IE) | (0xfc << CP0C4_KScrExist), - MIPS_CONFIG5 | (1 << CP0C5_XNP) | (1 << CP0C5_LLB), + MIPS_CONFIG5 | (1 << CP0C5_XNP) | (1 << CP0C5_VP) | + (1 << CP0C5_LLB), (1 << CP0C5_MSAEn) | (1 << CP0C5_SBRI) | (1 << CP0C5_FRE) | (1 << CP0C5_UFE), 0,