diff options
Diffstat (limited to 'target/mips/cpu.c')
-rw-r--r-- | target/mips/cpu.c | 255 |
1 files changed, 224 insertions, 31 deletions
diff --git a/target/mips/cpu.c b/target/mips/cpu.c index b2864d67d7..4c4548233c 100644 --- a/target/mips/cpu.c +++ b/target/mips/cpu.c @@ -33,6 +33,216 @@ #include "hw/qdev-clock.h" #include "hw/semihosting/semihost.h" #include "qapi/qapi-commands-machine-target.h" +#include "fpu_helper.h" + +#if !defined(CONFIG_USER_ONLY) + +/* Called for updates to CP0_Status. */ +void sync_c0_status(CPUMIPSState *env, CPUMIPSState *cpu, int tc) +{ + int32_t tcstatus, *tcst; + uint32_t v = cpu->CP0_Status; + uint32_t cu, mx, asid, ksu; + uint32_t mask = ((1 << CP0TCSt_TCU3) + | (1 << CP0TCSt_TCU2) + | (1 << CP0TCSt_TCU1) + | (1 << CP0TCSt_TCU0) + | (1 << CP0TCSt_TMX) + | (3 << CP0TCSt_TKSU) + | (0xff << CP0TCSt_TASID)); + + cu = (v >> CP0St_CU0) & 0xf; + mx = (v >> CP0St_MX) & 0x1; + ksu = (v >> CP0St_KSU) & 0x3; + asid = env->CP0_EntryHi & env->CP0_EntryHi_ASID_mask; + + tcstatus = cu << CP0TCSt_TCU0; + tcstatus |= mx << CP0TCSt_TMX; + tcstatus |= ksu << CP0TCSt_TKSU; + tcstatus |= asid; + + if (tc == cpu->current_tc) { + tcst = &cpu->active_tc.CP0_TCStatus; + } else { + tcst = &cpu->tcs[tc].CP0_TCStatus; + } + + *tcst &= ~mask; + *tcst |= tcstatus; + compute_hflags(cpu); +} + +void cpu_mips_store_status(CPUMIPSState *env, target_ulong val) +{ + uint32_t mask = env->CP0_Status_rw_bitmask; + target_ulong old = env->CP0_Status; + + if (env->insn_flags & ISA_MIPS_R6) { + bool has_supervisor = extract32(mask, CP0St_KSU, 2) == 0x3; +#if defined(TARGET_MIPS64) + uint32_t ksux = (1 << CP0St_KX) & val; + ksux |= (ksux >> 1) & val; /* KX = 0 forces SX to be 0 */ + ksux |= (ksux >> 1) & val; /* SX = 0 forces UX to be 0 */ + val = (val & ~(7 << CP0St_UX)) | ksux; +#endif + if (has_supervisor && extract32(val, CP0St_KSU, 2) == 0x3) { + mask &= ~(3 << CP0St_KSU); + } + mask &= ~(((1 << CP0St_SR) | (1 << CP0St_NMI)) & val); + } + + env->CP0_Status = (old & ~mask) | (val & mask); +#if defined(TARGET_MIPS64) + if ((env->CP0_Status ^ old) & (old & (7 << CP0St_UX))) { + /* Access to at least one of the 64-bit segments has been disabled */ + tlb_flush(env_cpu(env)); + } +#endif + if (ase_mt_available(env)) { + sync_c0_status(env, env, env->current_tc); + } else { + compute_hflags(env); + } +} + +void cpu_mips_store_cause(CPUMIPSState *env, target_ulong val) +{ + uint32_t mask = 0x00C00300; + uint32_t old = env->CP0_Cause; + int i; + + if (env->insn_flags & ISA_MIPS_R2) { + mask |= 1 << CP0Ca_DC; + } + if (env->insn_flags & ISA_MIPS_R6) { + mask &= ~((1 << CP0Ca_WP) & val); + } + + env->CP0_Cause = (env->CP0_Cause & ~mask) | (val & mask); + + if ((old ^ env->CP0_Cause) & (1 << CP0Ca_DC)) { + if (env->CP0_Cause & (1 << CP0Ca_DC)) { + cpu_mips_stop_count(env); + } else { + cpu_mips_start_count(env); + } + } + + /* Set/reset software interrupts */ + for (i = 0 ; i < 2 ; i++) { + if ((old ^ env->CP0_Cause) & (1 << (CP0Ca_IP + i))) { + cpu_mips_soft_irq(env, i, env->CP0_Cause & (1 << (CP0Ca_IP + i))); + } + } +} + +#endif /* !CONFIG_USER_ONLY */ + +static const char * const excp_names[EXCP_LAST + 1] = { + [EXCP_RESET] = "reset", + [EXCP_SRESET] = "soft reset", + [EXCP_DSS] = "debug single step", + [EXCP_DINT] = "debug interrupt", + [EXCP_NMI] = "non-maskable interrupt", + [EXCP_MCHECK] = "machine check", + [EXCP_EXT_INTERRUPT] = "interrupt", + [EXCP_DFWATCH] = "deferred watchpoint", + [EXCP_DIB] = "debug instruction breakpoint", + [EXCP_IWATCH] = "instruction fetch watchpoint", + [EXCP_AdEL] = "address error load", + [EXCP_AdES] = "address error store", + [EXCP_TLBF] = "TLB refill", + [EXCP_IBE] = "instruction bus error", + [EXCP_DBp] = "debug breakpoint", + [EXCP_SYSCALL] = "syscall", + [EXCP_BREAK] = "break", + [EXCP_CpU] = "coprocessor unusable", + [EXCP_RI] = "reserved instruction", + [EXCP_OVERFLOW] = "arithmetic overflow", + [EXCP_TRAP] = "trap", + [EXCP_FPE] = "floating point", + [EXCP_DDBS] = "debug data break store", + [EXCP_DWATCH] = "data watchpoint", + [EXCP_LTLBL] = "TLB modify", + [EXCP_TLBL] = "TLB load", + [EXCP_TLBS] = "TLB store", + [EXCP_DBE] = "data bus error", + [EXCP_DDBL] = "debug data break load", + [EXCP_THREAD] = "thread", + [EXCP_MDMX] = "MDMX", + [EXCP_C2E] = "precise coprocessor 2", + [EXCP_CACHE] = "cache error", + [EXCP_TLBXI] = "TLB execute-inhibit", + [EXCP_TLBRI] = "TLB read-inhibit", + [EXCP_MSADIS] = "MSA disabled", + [EXCP_MSAFPE] = "MSA floating point", +}; + +const char *mips_exception_name(int32_t exception) +{ + if (exception < 0 || exception > EXCP_LAST) { + return "unknown"; + } + return excp_names[exception]; +} + +void cpu_set_exception_base(int vp_index, target_ulong address) +{ + MIPSCPU *vp = MIPS_CPU(qemu_get_cpu(vp_index)); + vp->env.exception_base = address; +} + +target_ulong exception_resume_pc(CPUMIPSState *env) +{ + target_ulong bad_pc; + target_ulong isa_mode; + + isa_mode = !!(env->hflags & MIPS_HFLAG_M16); + bad_pc = env->active_tc.PC | isa_mode; + if (env->hflags & MIPS_HFLAG_BMASK) { + /* + * If the exception was raised from a delay slot, come back to + * the jump. + */ + bad_pc -= (env->hflags & MIPS_HFLAG_B16 ? 2 : 4); + } + + return bad_pc; +} + +bool mips_cpu_exec_interrupt(CPUState *cs, int interrupt_request) +{ + if (interrupt_request & CPU_INTERRUPT_HARD) { + MIPSCPU *cpu = MIPS_CPU(cs); + CPUMIPSState *env = &cpu->env; + + if (cpu_mips_hw_interrupts_enabled(env) && + cpu_mips_hw_interrupts_pending(env)) { + /* Raise it */ + cs->exception_index = EXCP_EXT_INTERRUPT; + env->error_code = 0; + mips_cpu_do_interrupt(cs); + return true; + } + } + return false; +} + +void QEMU_NORETURN do_raise_exception_err(CPUMIPSState *env, + uint32_t exception, + int error_code, + uintptr_t pc) +{ + CPUState *cs = env_cpu(env); + + qemu_log_mask(CPU_LOG_INT, "%s: %d (%s) %d\n", + __func__, exception, mips_exception_name(exception), + error_code); + cs->exception_index = exception; + env->error_code = error_code; + + cpu_loop_exit_restore(cs, pc); +} static void mips_cpu_set_pc(CPUState *cs, vaddr value) { @@ -72,7 +282,7 @@ static bool mips_cpu_has_work(CPUState *cs) if ((cs->interrupt_request & CPU_INTERRUPT_HARD) && cpu_mips_hw_interrupts_pending(env)) { if (cpu_mips_hw_interrupts_enabled(env) || - (env->insn_flags & ISA_MIPS32R6)) { + (env->insn_flags & ISA_MIPS_R6)) { has_work = true; } } @@ -103,12 +313,18 @@ static bool mips_cpu_has_work(CPUState *cs) return has_work; } -#include "translate_init.c.inc" +#include "cpu-defs.c.inc" -/* TODO QOM'ify CPU reset and remove */ -static void cpu_state_reset(CPUMIPSState *env) +static void mips_cpu_reset(DeviceState *dev) { - CPUState *cs = env_cpu(env); + CPUState *cs = CPU(dev); + MIPSCPU *cpu = MIPS_CPU(cs); + MIPSCPUClass *mcc = MIPS_CPU_GET_CLASS(cpu); + CPUMIPSState *env = &cpu->env; + + mcc->parent_reset(dev); + + memset(env, 0, offsetof(CPUMIPSState, end_reset_fields)); /* Reset registers to their default values */ env->CP0_PRid = env->cpu_model->CP0_PRid; @@ -288,13 +504,13 @@ static void cpu_state_reset(CPUMIPSState *env) /* XKPhys (note, SegCtl2.XR = 0, so XAM won't be used) */ env->CP0_SegCtl1 |= (CP0SC_AM_UK << CP0SC1_XAM); #endif /* !CONFIG_USER_ONLY */ - if ((env->insn_flags & ISA_MIPS32R6) && + if ((env->insn_flags & ISA_MIPS_R6) && (env->active_fpu.fcr0 & (1 << FCR0_F64))) { /* Status.FR = 0 mode in 64-bit FPU not allowed in R6 */ env->CP0_Status |= (1 << CP0St_FR); } - if (env->insn_flags & ISA_MIPS32R6) { + if (env->insn_flags & ISA_MIPS_R6) { /* PTW = 1 */ env->CP0_PWSize = 0x40; /* GDI = 12 */ @@ -317,10 +533,7 @@ static void cpu_state_reset(CPUMIPSState *env) env->hflags |= MIPS_HFLAG_M16; } - /* MSA */ - if (env->CP0_Config3 & (1 << CP0C3_MSAP)) { - msa_reset(env); - } + msa_reset(env); compute_hflags(env); restore_fp_status(env); @@ -331,20 +544,6 @@ static void cpu_state_reset(CPUMIPSState *env) /* UHI interface can be used to obtain argc and argv */ env->active_tc.gpr[4] = -1; } -} - -static void mips_cpu_reset(DeviceState *dev) -{ - CPUState *s = CPU(dev); - MIPSCPU *cpu = MIPS_CPU(s); - MIPSCPUClass *mcc = MIPS_CPU_GET_CLASS(cpu); - CPUMIPSState *env = &cpu->env; - - mcc->parent_reset(dev); - - memset(env, 0, offsetof(CPUMIPSState, end_reset_fields)); - - cpu_state_reset(env); #ifndef CONFIG_USER_ONLY if (kvm_enabled()) { @@ -596,9 +795,3 @@ bool cpu_type_supports_cps_smp(const char *cpu_type) const MIPSCPUClass *mcc = MIPS_CPU_CLASS(object_class_by_name(cpu_type)); return (mcc->cpu_def->CP0_Config3 & (1 << CP0C3_CMGCR)) != 0; } - -void cpu_set_exception_base(int vp_index, target_ulong address) -{ - MIPSCPU *vp = MIPS_CPU(qemu_get_cpu(vp_index)); - vp->env.exception_base = address; -} |