aboutsummaryrefslogtreecommitdiff
path: root/target-mips
diff options
context:
space:
mode:
authorYongbok Kim <yongbok.kim@imgtec.com>2016-02-03 12:31:07 +0000
committerLeon Alrae <leon.alrae@imgtec.com>2016-02-26 08:59:17 +0000
commit01bc435b44b8802cc4697faa07d908684afbce4e (patch)
treeeb377fa81c980fba2f2c08941e43ba526d395041 /target-mips
parentbee62662a312b99b4418b558a99b3963a4cbff07 (diff)
downloadqemu-01bc435b44b8802cc4697faa07d908684afbce4e.zip
qemu-01bc435b44b8802cc4697faa07d908684afbce4e.tar.gz
qemu-01bc435b44b8802cc4697faa07d908684afbce4e.tar.bz2
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. Signed-off-by: Yongbok Kim <yongbok.kim@imgtec.com> Signed-off-by: Leon Alrae <leon.alrae@imgtec.com>
Diffstat (limited to 'target-mips')
-rw-r--r--target-mips/cpu.c9
-rw-r--r--target-mips/cpu.h25
-rw-r--r--target-mips/helper.h4
-rw-r--r--target-mips/op_helper.c48
-rw-r--r--target-mips/translate.c59
-rw-r--r--target-mips/translate_init.c3
6 files changed, 147 insertions, 1 deletions
diff --git a/target-mips/cpu.c b/target-mips/cpu.c
index 0b3f130..7dc3a44 100644
--- a/target-mips/cpu.c
+++ b/target-mips/cpu.c
@@ -77,6 +77,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/target-mips/cpu.h b/target-mips/cpu.h
index bd23c2a..1e2b070 100644
--- a/target-mips/cpu.h
+++ b/target-mips/cpu.h
@@ -237,6 +237,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
@@ -286,6 +288,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;
@@ -471,6 +475,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
@@ -858,6 +863,26 @@ static inline int mips_vpe_active(CPUMIPSState *env)
return active;
}
+static inline int mips_vp_active(CPUMIPSState *env)
+{
+ 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 */
+ 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/target-mips/helper.h b/target-mips/helper.h
index 95b9149..1bc8bb2 100644
--- a/target-mips/helper.h
+++ b/target-mips/helper.h
@@ -176,6 +176,10 @@ DEF_HELPER_0(dmt, tl)
DEF_HELPER_0(emt, tl)
DEF_HELPER_1(dvpe, tl, env)
DEF_HELPER_1(evpe, tl, env)
+
+/* R6 Multi-threading */
+DEF_HELPER_1(dvp, tl, env)
+DEF_HELPER_1(evp, tl, env)
#endif /* !CONFIG_USER_ONLY */
/* microMIPS functions */
diff --git a/target-mips/op_helper.c b/target-mips/op_helper.c
index 684ec92..7c5669c 100644
--- a/target-mips/op_helper.c
+++ b/target-mips/op_helper.c
@@ -571,6 +571,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
@@ -1840,6 +1848,46 @@ 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)) {
+ CPU_FOREACH(other_cs) {
+ MIPSCPU *other_cpu = MIPS_CPU(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) {
+ CPU_FOREACH(other_cs) {
+ MIPSCPU *other_cpu = MIPS_CPU(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/target-mips/translate.c b/target-mips/translate.c
index 658926d..a166569 100644
--- a/target-mips/translate.c
+++ b/target-mips/translate.c
@@ -894,6 +894,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) */
@@ -1429,6 +1431,7 @@ typedef struct DisasContext {
bool mvh;
int CP0_LLAddr_shift;
bool ps;
+ bool vp;
} DisasContext;
enum {
@@ -4950,6 +4953,11 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel)
gen_helper_mfc0_mvpconf1(arg, cpu_env);
rn = "MVPConf1";
break;
+ case 4:
+ CP0_CHECK(ctx->vp);
+ gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_VPControl));
+ rn = "VPControl";
+ break;
default:
goto cp0_unimplemented;
}
@@ -5077,6 +5085,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(arg, offsetof(CPUMIPSState, CP0_GlobalNumber));
+ rn = "GlobalNumber";
+ break;
default:
goto cp0_unimplemented;
}
@@ -5597,6 +5610,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;
}
@@ -5699,6 +5717,11 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel)
gen_helper_mtc0_entrylo1(cpu_env, arg);
rn = "EntryLo1";
break;
+ case 1:
+ CP0_CHECK(ctx->vp);
+ /* ignored */
+ rn = "GlobalNumber";
+ break;
default:
goto cp0_unimplemented;
}
@@ -6234,6 +6257,11 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel)
gen_helper_mfc0_mvpconf1(arg, cpu_env);
rn = "MVPConf1";
break;
+ case 4:
+ CP0_CHECK(ctx->vp);
+ gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_VPControl));
+ rn = "VPControl";
+ break;
default:
goto cp0_unimplemented;
}
@@ -6335,6 +6363,11 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel)
tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_EntryLo1));
rn = "EntryLo1";
break;
+ case 1:
+ CP0_CHECK(ctx->vp);
+ gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_GlobalNumber));
+ rn = "GlobalNumber";
+ break;
default:
goto cp0_unimplemented;
}
@@ -6841,6 +6874,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;
}
@@ -6941,6 +6979,11 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel)
gen_helper_dmtc0_entrylo1(cpu_env, arg);
rn = "EntryLo1";
break;
+ case 1:
+ CP0_CHECK(ctx->vp);
+ /* ignored */
+ rn = "GlobalNumber";
+ break;
default:
goto cp0_unimplemented;
}
@@ -19080,6 +19123,20 @@ static void decode_opc(CPUMIPSState *env, DisasContext *ctx)
gen_helper_evpe(t0, cpu_env);
gen_store_gpr(t0, rt);
break;
+ case OPC_DVP:
+ check_insn(ctx, ISA_MIPS32R6);
+ if (ctx->vp) {
+ gen_helper_dvp(t0, cpu_env);
+ gen_store_gpr(t0, rt);
+ }
+ break;
+ case OPC_EVP:
+ check_insn(ctx, ISA_MIPS32R6);
+ if (ctx->vp) {
+ gen_helper_evp(t0, cpu_env);
+ gen_store_gpr(t0, rt);
+ }
+ break;
case OPC_DI:
check_insn(ctx, ISA_MIPS32R2);
save_cpu_state(ctx, 1);
@@ -19611,6 +19668,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;
@@ -19996,6 +20054,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);
if (kvm_enabled()) {
env->CP0_EBase |= 0x40000000;
diff --git a/target-mips/translate_init.c b/target-mips/translate_init.c
index bb33c7c..cdef59d 100644
--- a/target-mips/translate_init.c
+++ b/target-mips/translate_init.c
@@ -665,7 +665,8 @@ static const mips_def_t mips_defs[] =
(1 << CP0C3_RXI) | (1 << CP0C3_LPA),
.CP0_Config4 = MIPS_CONFIG4 | (1U << CP0C4_M) | (3 << CP0C4_IE) |
(0xfc << CP0C4_KScrExist),
- .CP0_Config5 = MIPS_CONFIG5 | (1 << CP0C5_XNP) | (1 << CP0C5_LLB),
+ .CP0_Config5 = MIPS_CONFIG5 | (1 << CP0C5_XNP) | (1 << CP0C5_VP) |
+ (1 << CP0C5_LLB),
.CP0_Config5_rw_bitmask = (1 << CP0C5_MSAEn) | (1 << CP0C5_SBRI) |
(1 << CP0C5_FRE) | (1 << CP0C5_UFE),
.CP0_LLAddr_rw_bitmask = 0,