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
This commit is contained in:
Yongbok Kim 2018-02-20 21:56:31 -05:00 committed by Lioncash
parent abb0408274
commit 46284f1a41
No known key found for this signature in database
GPG key ID: 4E3C3CC1031BA9C7
11 changed files with 161 additions and 1 deletions

View file

@ -3682,6 +3682,8 @@ mips_symbols = (
'helper_mttdsp', 'helper_mttdsp',
'helper_dmt', 'helper_dmt',
'helper_emt', 'helper_emt',
'helper_evp',
'helper_dvp',
'helper_dvpe', 'helper_dvpe',
'helper_evpe', 'helper_evpe',
'helper_fork', 'helper_fork',

View file

@ -3629,6 +3629,8 @@
#define helper_mttdsp helper_mttdsp_mips #define helper_mttdsp helper_mttdsp_mips
#define helper_dmt helper_dmt_mips #define helper_dmt helper_dmt_mips
#define helper_emt helper_emt_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_dvpe helper_dvpe_mips
#define helper_evpe helper_evpe_mips #define helper_evpe helper_evpe_mips
#define helper_fork helper_fork_mips #define helper_fork helper_fork_mips

View file

@ -3629,6 +3629,8 @@
#define helper_mttdsp helper_mttdsp_mips64 #define helper_mttdsp helper_mttdsp_mips64
#define helper_dmt helper_dmt_mips64 #define helper_dmt helper_dmt_mips64
#define helper_emt helper_emt_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_dvpe helper_dvpe_mips64
#define helper_evpe helper_evpe_mips64 #define helper_evpe helper_evpe_mips64
#define helper_fork helper_fork_mips64 #define helper_fork helper_fork_mips64

View file

@ -3629,6 +3629,8 @@
#define helper_mttdsp helper_mttdsp_mips64el #define helper_mttdsp helper_mttdsp_mips64el
#define helper_dmt helper_dmt_mips64el #define helper_dmt helper_dmt_mips64el
#define helper_emt helper_emt_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_dvpe helper_dvpe_mips64el
#define helper_evpe helper_evpe_mips64el #define helper_evpe helper_evpe_mips64el
#define helper_fork helper_fork_mips64el #define helper_fork helper_fork_mips64el

View file

@ -3629,6 +3629,8 @@
#define helper_mttdsp helper_mttdsp_mipsel #define helper_mttdsp helper_mttdsp_mipsel
#define helper_dmt helper_dmt_mipsel #define helper_dmt helper_dmt_mipsel
#define helper_emt helper_emt_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_dvpe helper_dvpe_mipsel
#define helper_evpe helper_evpe_mipsel #define helper_evpe helper_evpe_mipsel
#define helper_fork helper_fork_mipsel #define helper_fork helper_fork_mipsel

View file

@ -76,6 +76,15 @@ static bool mips_cpu_has_work(CPUState *cs)
has_work = false; 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; return has_work;
} }

View file

@ -238,6 +238,8 @@ struct CPUMIPSState {
int32_t CP0_Index; int32_t CP0_Index;
/* CP0_MVP* are per MVP registers. */ /* CP0_MVP* are per MVP registers. */
int32_t CP0_VPControl;
#define CP0VPCtl_DIS 0
int32_t CP0_Random; int32_t CP0_Random;
int32_t CP0_VPEControl; int32_t CP0_VPEControl;
#define CP0VPECo_YSI 21 #define CP0VPECo_YSI 21
@ -287,6 +289,8 @@ struct CPUMIPSState {
# define CP0EnLo_RI 31 # define CP0EnLo_RI 31
# define CP0EnLo_XI 30 # define CP0EnLo_XI 30
#endif #endif
int32_t CP0_GlobalNumber;
#define CP0GN_VPId 0
target_ulong CP0_Context; target_ulong CP0_Context;
target_ulong CP0_KScratch[MIPS_KSCRATCH_NUM]; target_ulong CP0_KScratch[MIPS_KSCRATCH_NUM];
int32_t CP0_PageMask; int32_t CP0_PageMask;
@ -472,6 +476,7 @@ struct CPUMIPSState {
#define CP0C5_XNP 13 #define CP0C5_XNP 13
#define CP0C5_UFE 9 #define CP0C5_UFE 9
#define CP0C5_FRE 8 #define CP0C5_FRE 8
#define CP0C5_VP 7
#define CP0C5_SBRI 6 #define CP0C5_SBRI 6
#define CP0C5_MVH 5 #define CP0C5_MVH 5
#define CP0C5_LLB 4 #define CP0C5_LLB 4
@ -863,6 +868,28 @@ static inline int mips_vpe_active(CPUMIPSState *env)
return active; 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" #include "exec/exec-all.h"
static inline void compute_hflags(CPUMIPSState *env) static inline void compute_hflags(CPUMIPSState *env)

View file

@ -178,6 +178,10 @@ DEF_HELPER_1(dvpe, tl, env)
DEF_HELPER_1(evpe, tl, env) DEF_HELPER_1(evpe, tl, env)
#endif /* !CONFIG_USER_ONLY */ #endif /* !CONFIG_USER_ONLY */
/* R6 Multi-threading */
DEF_HELPER_1(dvp, tl, env)
DEF_HELPER_1(evp, tl, env)
/* microMIPS functions */ /* microMIPS functions */
DEF_HELPER_4(lwm, void, env, tl, tl, i32) DEF_HELPER_4(lwm, void, env, tl, tl, i32)
DEF_HELPER_4(swm, void, env, tl, tl, i32) DEF_HELPER_4(swm, void, env, tl, tl, i32)

View file

@ -569,6 +569,14 @@ static bool mips_vpe_is_wfi(MIPSCPU *c)
return cpu->halted && mips_vpe_active(env); 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) static inline void mips_vpe_wake(MIPSCPU *c)
{ {
/* Dont set ->halted = 0 directly, let it be done via cpu_has_work /* 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; 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 #ifndef CONFIG_USER_ONLY
/* TLB management */ /* TLB management */
static void r4k_mips_tlb_flush_extra (CPUMIPSState *env, int first) static void r4k_mips_tlb_flush_extra (CPUMIPSState *env, int first)

View file

@ -890,6 +890,8 @@ enum {
OPC_EVPE = 0x01 | (1 << 5) | OPC_MFMC0, OPC_EVPE = 0x01 | (1 << 5) | OPC_MFMC0,
OPC_DI = (0 << 5) | (0x0C << 11) | OPC_MFMC0, OPC_DI = (0 << 5) | (0x0C << 11) | OPC_MFMC0,
OPC_EI = (1 << 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) */ /* Coprocessor 0 (with rs == C0) */
@ -1414,6 +1416,7 @@ typedef struct DisasContext {
bool mvh; bool mvh;
int CP0_LLAddr_shift; int CP0_LLAddr_shift;
bool ps; bool ps;
bool vp;
// Unicorn engine // Unicorn engine
struct uc_struct *uc; struct uc_struct *uc;
} DisasContext; } 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); gen_helper_mfc0_mvpconf1(tcg_ctx, arg, tcg_ctx->cpu_env);
rn = "MVPConf1"; rn = "MVPConf1";
break; break;
case 4:
CP0_CHECK(ctx->vp);
gen_mfc0_load32(ctx, arg, offsetof(CPUMIPSState, CP0_VPControl));
rn = "VPControl";
break;
default: default:
goto cp0_unimplemented; goto cp0_unimplemented;
} }
@ -5150,6 +5158,11 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel)
} }
rn = "EntryLo1"; rn = "EntryLo1";
break; break;
case 1:
CP0_CHECK(ctx->vp);
gen_mfc0_load32(ctx, arg, offsetof(CPUMIPSState, CP0_GlobalNumber));
rn = "GlobalNumber";
break;
default: default:
goto cp0_unimplemented; goto cp0_unimplemented;
} }
@ -5672,6 +5685,11 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel)
/* ignored */ /* ignored */
rn = "MVPConf1"; rn = "MVPConf1";
break; break;
case 4:
CP0_CHECK(ctx->vp);
/* ignored */
rn = "VPControl";
break;
default: default:
goto cp0_unimplemented; 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); gen_helper_mtc0_entrylo1(tcg_ctx, tcg_ctx->cpu_env, arg);
rn = "EntryLo1"; rn = "EntryLo1";
break; break;
case 1:
CP0_CHECK(ctx->vp);
/* ignored */
rn = "GlobalNumber";
break;
default: default:
goto cp0_unimplemented; 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); gen_helper_mfc0_mvpconf1(tcg_ctx, arg, tcg_ctx->cpu_env);
rn = "MVPConf1"; rn = "MVPConf1";
break; break;
case 4:
CP0_CHECK(ctx->vp);
gen_mfc0_load32(ctx, arg, offsetof(CPUMIPSState, CP0_VPControl));
rn = "VPControl";
break;
default: default:
goto cp0_unimplemented; 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)); tcg_gen_ld_tl(tcg_ctx, arg, tcg_ctx->cpu_env, offsetof(CPUMIPSState, CP0_EntryLo1));
rn = "EntryLo1"; rn = "EntryLo1";
break; break;
case 1:
CP0_CHECK(ctx->vp);
gen_mfc0_load32(ctx, arg, offsetof(CPUMIPSState, CP0_GlobalNumber));
rn = "GlobalNumber";
break;
default: default:
goto cp0_unimplemented; goto cp0_unimplemented;
} }
@ -6917,6 +6950,11 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel)
/* ignored */ /* ignored */
rn = "MVPConf1"; rn = "MVPConf1";
break; break;
case 4:
CP0_CHECK(ctx->vp);
/* ignored */
rn = "VPControl";
break;
default: default:
goto cp0_unimplemented; 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); gen_helper_dmtc0_entrylo1(tcg_ctx, tcg_ctx->cpu_env, arg);
rn = "EntryLo1"; rn = "EntryLo1";
break; break;
case 1:
CP0_CHECK(ctx->vp);
/* ignored */
rn = "GlobalNumber";
break;
default: default:
goto cp0_unimplemented; 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_helper_evpe(tcg_ctx, t0, tcg_ctx->cpu_env);
gen_store_gpr(tcg_ctx, t0, rt); gen_store_gpr(tcg_ctx, t0, rt);
break; 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: case OPC_DI:
check_insn(ctx, ISA_MIPS32R2); check_insn(ctx, ISA_MIPS32R2);
save_cpu_state(ctx, 1); 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.ulri = (env->CP0_Config3 >> CP0C3_ULRI) & 1;
ctx.ps = ((env->active_fpu.fcr0 >> FCR0_PS) & 1) || ctx.ps = ((env->active_fpu.fcr0 >> FCR0_PS) & 1) ||
(env->insn_flags & (INSN_LOONGSON2E | INSN_LOONGSON2F)); (env->insn_flags & (INSN_LOONGSON2E | INSN_LOONGSON2F));
ctx.vp = (env->CP0_Config5 >> CP0C5_VP) & 1;
restore_cpu_state(env, &ctx); restore_cpu_state(env, &ctx);
#ifdef CONFIG_USER_ONLY #ifdef CONFIG_USER_ONLY
ctx.mem_idx = MIPS_HFLAG_UM; 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->CP0_Random = env->tlb->nb_tlb - 1;
env->tlb->tlb_in_use = env->tlb->nb_tlb; env->tlb->tlb_in_use = env->tlb->nb_tlb;
env->CP0_Wired = 0; env->CP0_Wired = 0;
env->CP0_GlobalNumber = (cs->cpu_index & 0xFF) << CP0GN_VPId;
env->CP0_EBase = (cs->cpu_index & 0x3FF); env->CP0_EBase = (cs->cpu_index & 0x3FF);
env->CP0_EBase |= 0x80000000; env->CP0_EBase |= 0x80000000;
env->CP0_Status = (1 << CP0St_BEV) | (1 << CP0St_ERL); env->CP0_Status = (1 << CP0St_BEV) | (1 << CP0St_ERL);

View file

@ -788,7 +788,8 @@ static const mips_def_t mips_defs[] =
(1 << CP0C3_RXI) | (1 << CP0C3_LPA), (1 << CP0C3_RXI) | (1 << CP0C3_LPA),
MIPS_CONFIG4 | (1U << CP0C4_M) | (3 << CP0C4_IE) | MIPS_CONFIG4 | (1U << CP0C4_M) | (3 << CP0C4_IE) |
(0xfc << CP0C4_KScrExist), (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_MSAEn) | (1 << CP0C5_SBRI) |
(1 << CP0C5_FRE) | (1 << CP0C5_UFE), (1 << CP0C5_FRE) | (1 << CP0C5_UFE),
0, 0,