#include "qemu/osdep.h"
#include "cpu.h"
#include "disas/disas.h"
+#include "exec/exec-all.h"
#include "tcg-op.h"
#include "exec/cpu_ldst.h"
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) */
};
/* global register indices */
-static TCGv_ptr cpu_env;
+static TCGv_env cpu_env;
static TCGv cpu_gpr[32], cpu_PC;
static TCGv cpu_HI[MIPS_DSP_ACC], cpu_LO[MIPS_DSP_ACC];
static TCGv cpu_dspctrl, btarget, bcond;
bool mvh;
int CP0_LLAddr_shift;
bool ps;
+ bool vp;
+ bool cmgcr;
+ bool mrp;
} DisasContext;
enum {
tcg_temp_free(t1);
}
+static inline bool use_goto_tb(DisasContext *ctx, target_ulong dest)
+{
+ if (unlikely(ctx->singlestep_enabled)) {
+ return false;
+ }
+
+#ifndef CONFIG_USER_ONLY
+ return (ctx->tb->pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK);
+#else
+ return true;
+#endif
+}
+
static inline void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest)
{
- TranslationBlock *tb;
- tb = ctx->tb;
- if ((tb->pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK) &&
- likely(!ctx->singlestep_enabled)) {
+ if (use_goto_tb(ctx, dest)) {
tcg_gen_goto_tb(n);
gen_save_pc(dest);
- tcg_gen_exit_tb((uintptr_t)tb + n);
+ tcg_gen_exit_tb((uintptr_t)ctx->tb + n);
} else {
gen_save_pc(dest);
if (ctx->singlestep_enabled) {
tcg_temp_free_i32(t0);
}
+#define CP0_CHECK(c) \
+ do { \
+ if (!(c)) { \
+ goto cp0_unimplemented; \
+ } \
+ } while (0)
+
static void gen_mfhc0(DisasContext *ctx, TCGv arg, int reg, int sel)
{
const char *rn = "invalid";
- if (!(ctx->hflags & MIPS_HFLAG_ELPA)) {
- goto mfhc0_read_zero;
- }
+ CP0_CHECK(ctx->hflags & MIPS_HFLAG_ELPA);
switch (reg) {
case 2:
rn = "EntryLo0";
break;
default:
- goto mfhc0_read_zero;
+ goto cp0_unimplemented;
}
break;
case 3:
rn = "EntryLo1";
break;
default:
- goto mfhc0_read_zero;
+ goto cp0_unimplemented;
}
break;
case 17:
ctx->CP0_LLAddr_shift);
rn = "LLAddr";
break;
+ case 1:
+ CP0_CHECK(ctx->mrp);
+ gen_helper_mfhc0_maar(arg, cpu_env);
+ rn = "MAAR";
+ break;
default:
- goto mfhc0_read_zero;
+ goto cp0_unimplemented;
}
break;
case 28:
rn = "TagLo";
break;
default:
- goto mfhc0_read_zero;
+ goto cp0_unimplemented;
}
break;
default:
- goto mfhc0_read_zero;
+ goto cp0_unimplemented;
}
(void)rn; /* avoid a compiler warning */
LOG_DISAS("mfhc0 %s (reg %d sel %d)\n", rn, reg, sel);
return;
-mfhc0_read_zero:
+cp0_unimplemented:
LOG_DISAS("mfhc0 %s (reg %d sel %d)\n", rn, reg, sel);
tcg_gen_movi_tl(arg, 0);
}
const char *rn = "invalid";
uint64_t mask = ctx->PAMask >> 36;
- if (!(ctx->hflags & MIPS_HFLAG_ELPA)) {
- goto mthc0_nop;
- }
+ CP0_CHECK(ctx->hflags & MIPS_HFLAG_ELPA);
switch (reg) {
case 2:
rn = "EntryLo0";
break;
default:
- goto mthc0_nop;
+ goto cp0_unimplemented;
}
break;
case 3:
rn = "EntryLo1";
break;
default:
- goto mthc0_nop;
+ goto cp0_unimplemented;
}
break;
case 17:
treating MTHC0 to LLAddr as NOP. */
rn = "LLAddr";
break;
+ case 1:
+ CP0_CHECK(ctx->mrp);
+ gen_helper_mthc0_maar(cpu_env, arg);
+ rn = "MAAR";
+ break;
default:
- goto mthc0_nop;
+ goto cp0_unimplemented;
}
break;
case 28:
rn = "TagLo";
break;
default:
- goto mthc0_nop;
+ goto cp0_unimplemented;
}
break;
default:
- goto mthc0_nop;
+ goto cp0_unimplemented;
}
(void)rn; /* avoid a compiler warning */
-mthc0_nop:
+cp0_unimplemented:
LOG_DISAS("mthc0 %s (reg %d sel %d)\n", rn, reg, sel);
}
}
}
-#define CP0_CHECK(c) \
- do { \
- if (!(c)) { \
- goto cp0_unimplemented; \
- } \
- } while (0)
-
static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel)
{
const char *rn = "invalid";
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;
}
}
rn = "EntryLo1";
break;
+ case 1:
+ CP0_CHECK(ctx->vp);
+ gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_GlobalNumber));
+ rn = "GlobalNumber";
+ break;
default:
goto cp0_unimplemented;
}
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_EBase));
rn = "EBase";
break;
+ case 3:
+ check_insn(ctx, ISA_MIPS32R2);
+ CP0_CHECK(ctx->cmgcr);
+ tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_CMGCRBase));
+ tcg_gen_ext32s_tl(arg, arg);
+ rn = "CMGCRBase";
+ break;
default:
goto cp0_unimplemented;
}
gen_helper_mfc0_lladdr(arg, cpu_env);
rn = "LLAddr";
break;
+ case 1:
+ CP0_CHECK(ctx->mrp);
+ gen_helper_mfc0_maar(arg, cpu_env);
+ rn = "MAAR";
+ break;
+ case 2:
+ CP0_CHECK(ctx->mrp);
+ gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_MAARI));
+ rn = "MAARI";
+ break;
default:
goto cp0_unimplemented;
}
}
break;
case 26:
- tcg_gen_movi_tl(arg, 0); /* unimplemented */
- rn = "ECC";
+ switch (sel) {
+ case 0:
+ gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_ErrCtl));
+ rn = "ErrCtl";
+ break;
+ default:
+ goto cp0_unimplemented;
+ }
break;
case 27:
switch (sel) {
/* ignored */
rn = "MVPConf1";
break;
+ case 4:
+ CP0_CHECK(ctx->vp);
+ /* ignored */
+ rn = "VPControl";
+ break;
default:
goto cp0_unimplemented;
}
gen_helper_mtc0_entrylo1(cpu_env, arg);
rn = "EntryLo1";
break;
+ case 1:
+ CP0_CHECK(ctx->vp);
+ /* ignored */
+ rn = "GlobalNumber";
+ break;
default:
goto cp0_unimplemented;
}
gen_helper_mtc0_lladdr(cpu_env, arg);
rn = "LLAddr";
break;
+ case 1:
+ CP0_CHECK(ctx->mrp);
+ gen_helper_mtc0_maar(cpu_env, arg);
+ rn = "MAAR";
+ break;
+ case 2:
+ CP0_CHECK(ctx->mrp);
+ gen_helper_mtc0_maari(cpu_env, arg);
+ rn = "MAARI";
+ break;
default:
goto cp0_unimplemented;
}
}
break;
case 26:
- /* ignored */
- rn = "ECC";
+ switch (sel) {
+ case 0:
+ gen_helper_mtc0_errctl(cpu_env, arg);
+ ctx->bstate = BS_STOP;
+ rn = "ErrCtl";
+ break;
+ default:
+ goto cp0_unimplemented;
+ }
break;
case 27:
switch (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;
}
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;
}
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_EBase));
rn = "EBase";
break;
+ case 3:
+ check_insn(ctx, ISA_MIPS32R2);
+ CP0_CHECK(ctx->cmgcr);
+ tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_CMGCRBase));
+ rn = "CMGCRBase";
+ break;
default:
goto cp0_unimplemented;
}
gen_helper_dmfc0_lladdr(arg, cpu_env);
rn = "LLAddr";
break;
+ case 1:
+ CP0_CHECK(ctx->mrp);
+ gen_helper_dmfc0_maar(arg, cpu_env);
+ rn = "MAAR";
+ break;
+ case 2:
+ CP0_CHECK(ctx->mrp);
+ gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_MAARI));
+ rn = "MAARI";
+ break;
default:
goto cp0_unimplemented;
}
}
break;
case 26:
- tcg_gen_movi_tl(arg, 0); /* unimplemented */
- rn = "ECC";
+ switch (sel) {
+ case 0:
+ gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_ErrCtl));
+ rn = "ErrCtl";
+ break;
+ default:
+ goto cp0_unimplemented;
+ }
break;
case 27:
switch (sel) {
/* ignored */
rn = "MVPConf1";
break;
+ case 4:
+ CP0_CHECK(ctx->vp);
+ /* ignored */
+ rn = "VPControl";
+ break;
default:
goto cp0_unimplemented;
}
gen_helper_dmtc0_entrylo1(cpu_env, arg);
rn = "EntryLo1";
break;
+ case 1:
+ CP0_CHECK(ctx->vp);
+ /* ignored */
+ rn = "GlobalNumber";
+ break;
default:
goto cp0_unimplemented;
}
gen_helper_mtc0_lladdr(cpu_env, arg);
rn = "LLAddr";
break;
+ case 1:
+ CP0_CHECK(ctx->mrp);
+ gen_helper_mtc0_maar(cpu_env, arg);
+ rn = "MAAR";
+ break;
+ case 2:
+ CP0_CHECK(ctx->mrp);
+ gen_helper_mtc0_maari(cpu_env, arg);
+ rn = "MAARI";
+ break;
default:
goto cp0_unimplemented;
}
}
break;
case 26:
- /* ignored */
- rn = "ECC";
+ switch (sel) {
+ case 0:
+ gen_helper_mtc0_errctl(cpu_env, arg);
+ ctx->bstate = BS_STOP;
+ rn = "ErrCtl";
+ break;
+ default:
+ goto cp0_unimplemented;
+ }
break;
case 27:
switch (sel) {
tcg_temp_free(t0);
}
+static void gen_cache_operation(DisasContext *ctx, uint32_t op, int base,
+ int16_t offset)
+{
+ TCGv_i32 t0 = tcg_const_i32(op);
+ TCGv t1 = tcg_temp_new();
+ gen_base_offset_addr(ctx, t1, base, offset);
+ gen_helper_cache(cpu_env, t1, t0);
+}
+
#if defined(TARGET_MIPS64)
static void decode_i64_mips16 (DisasContext *ctx,
int ry, int funct, int16_t offset,
switch (minor) {
case CACHE:
check_cp0_enabled(ctx);
- /* Treat as no-op. */
+ if (ctx->hflags & MIPS_HFLAG_ITC_CACHE) {
+ gen_cache_operation(ctx, rt, rs, imm);
+ }
break;
case LWC2:
case SWC2:
/* Treat as NOP. */
break;
case R6_OPC_CACHE:
- /* Treat as NOP. */
+ check_cp0_enabled(ctx);
+ if (ctx->hflags & MIPS_HFLAG_ITC_CACHE) {
+ gen_cache_operation(ctx, rt, rs, imm);
+ }
break;
case R6_OPC_SC:
gen_st_cond(ctx, op1, rt, rs, imm);
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);
check_insn_opc_removed(ctx, ISA_MIPS32R6);
check_cp0_enabled(ctx);
check_insn(ctx, ISA_MIPS3 | ISA_MIPS32);
+ if (ctx->hflags & MIPS_HFLAG_ITC_CACHE) {
+ gen_cache_operation(ctx, rt, rs, imm);
+ }
/* Treat as NOP. */
break;
case OPC_PREF:
ctx.PAMask = env->PAMask;
ctx.mvh = (env->CP0_Config5 >> CP0C5_MVH) & 1;
ctx.CP0_LLAddr_shift = env->CP0_LLAddr_shift;
+ ctx.cmgcr = (env->CP0_Config3 >> CP0C3_CMGCR) & 1;
/* Restore delay slot state from the tb context. */
ctx.hflags = (uint32_t)tb->flags; /* FIXME: maybe use 64 bits here? */
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;
+ ctx.mrp = (env->CP0_Config5 >> CP0C5_MRP) & 1;
restore_cpu_state(env, &ctx);
#ifdef CONFIG_USER_ONLY
ctx.mem_idx = MIPS_HFLAG_UM;
return;
cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env");
+
TCGV_UNUSED(cpu_gpr[0]);
for (i = 1; i < 32; i++)
- cpu_gpr[i] = tcg_global_mem_new(TCG_AREG0,
+ cpu_gpr[i] = tcg_global_mem_new(cpu_env,
offsetof(CPUMIPSState, active_tc.gpr[i]),
regnames[i]);
for (i = 0; i < 32; i++) {
int off = offsetof(CPUMIPSState, active_fpu.fpr[i].wr.d[0]);
msa_wr_d[i * 2] =
- tcg_global_mem_new_i64(TCG_AREG0, off, msaregnames[i * 2]);
+ tcg_global_mem_new_i64(cpu_env, off, msaregnames[i * 2]);
/* The scalar floating-point unit (FPU) registers are mapped on
* the MSA vector registers. */
fpu_f64[i] = msa_wr_d[i * 2];
off = offsetof(CPUMIPSState, active_fpu.fpr[i].wr.d[1]);
msa_wr_d[i * 2 + 1] =
- tcg_global_mem_new_i64(TCG_AREG0, off, msaregnames[i * 2 + 1]);
+ tcg_global_mem_new_i64(cpu_env, off, msaregnames[i * 2 + 1]);
}
- cpu_PC = tcg_global_mem_new(TCG_AREG0,
+ cpu_PC = tcg_global_mem_new(cpu_env,
offsetof(CPUMIPSState, active_tc.PC), "PC");
for (i = 0; i < MIPS_DSP_ACC; i++) {
- cpu_HI[i] = tcg_global_mem_new(TCG_AREG0,
+ cpu_HI[i] = tcg_global_mem_new(cpu_env,
offsetof(CPUMIPSState, active_tc.HI[i]),
regnames_HI[i]);
- cpu_LO[i] = tcg_global_mem_new(TCG_AREG0,
+ cpu_LO[i] = tcg_global_mem_new(cpu_env,
offsetof(CPUMIPSState, active_tc.LO[i]),
regnames_LO[i]);
}
- cpu_dspctrl = tcg_global_mem_new(TCG_AREG0,
+ cpu_dspctrl = tcg_global_mem_new(cpu_env,
offsetof(CPUMIPSState, active_tc.DSPControl),
"DSPControl");
- bcond = tcg_global_mem_new(TCG_AREG0,
+ bcond = tcg_global_mem_new(cpu_env,
offsetof(CPUMIPSState, bcond), "bcond");
- btarget = tcg_global_mem_new(TCG_AREG0,
+ btarget = tcg_global_mem_new(cpu_env,
offsetof(CPUMIPSState, btarget), "btarget");
- hflags = tcg_global_mem_new_i32(TCG_AREG0,
+ hflags = tcg_global_mem_new_i32(cpu_env,
offsetof(CPUMIPSState, hflags), "hflags");
- fpu_fcr0 = tcg_global_mem_new_i32(TCG_AREG0,
+ fpu_fcr0 = tcg_global_mem_new_i32(cpu_env,
offsetof(CPUMIPSState, active_fpu.fcr0),
"fcr0");
- fpu_fcr31 = tcg_global_mem_new_i32(TCG_AREG0,
+ fpu_fcr31 = tcg_global_mem_new_i32(cpu_env,
offsetof(CPUMIPSState, active_fpu.fcr31),
"fcr31");
return cpu;
}
+bool cpu_supports_cps_smp(const char *cpu_model)
+{
+ const mips_def_t *def = cpu_mips_find_by_name(cpu_model);
+ if (!def) {
+ return false;
+ }
+
+ return (def->CP0_Config3 & (1 << CP0C3_CMGCR)) != 0;
+}
+
void cpu_state_reset(CPUMIPSState *env)
{
MIPSCPU *cpu = mips_env_get_cpu(env);
env->CP0_PageGrain_rw_bitmask = env->cpu_model->CP0_PageGrain_rw_bitmask;
env->CP0_PageGrain = env->cpu_model->CP0_PageGrain;
env->active_fpu.fcr0 = env->cpu_model->CP1_fcr0;
+ env->active_fpu.fcr31 = env->cpu_model->CP1_fcr31;
env->msair = env->cpu_model->MSAIR;
env->insn_flags = env->cpu_model->insn_flags;
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;
} else {
env->CP0_EBase |= 0x80000000;
}
+ if (env->CP0_Config3 & (1 << CP0C3_CMGCR)) {
+ env->CP0_CMGCRBase = 0x1fbf8000 >> 4;
+ }
env->CP0_Status = (1 << CP0St_BEV) | (1 << CP0St_ERL);
/* vectored interrupts not implemented, timer on int 7,
no performance counters. */