diff options
Diffstat (limited to 'target')
39 files changed, 2428 insertions, 982 deletions
diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 3609de0888..e3f8215203 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -51,7 +51,7 @@ static void arm_cpu_set_pc(CPUState *cs, vaddr value) if (is_a64(env)) { env->pc = value; - env->thumb = 0; + env->thumb = false; } else { env->regs[15] = value & ~1; env->thumb = value & 1; @@ -189,7 +189,7 @@ static void arm_cpu_reset(DeviceState *dev) if (arm_feature(env, ARM_FEATURE_AARCH64)) { /* 64 bit CPUs always start in 64 bit mode */ - env->aarch64 = 1; + env->aarch64 = true; #if defined(CONFIG_USER_ONLY) env->pstate = PSTATE_MODE_EL0t; /* Userspace expects access to DC ZVA, CTL_EL0 and the cache ops */ @@ -694,6 +694,16 @@ static void arm_cpu_set_irq(void *opaque, int irq, int level) [ARM_CPU_VFIQ] = CPU_INTERRUPT_VFIQ }; + if (!arm_feature(env, ARM_FEATURE_EL2) && + (irq == ARM_CPU_VIRQ || irq == ARM_CPU_VFIQ)) { + /* + * The GIC might tell us about VIRQ and VFIQ state, but if we don't + * have EL2 support we don't care. (Unless the guest is doing something + * silly this will only be calls saying "level is still 0".) + */ + return; + } + if (level) { env->irq_line_state |= mask[irq]; } else { @@ -702,11 +712,9 @@ static void arm_cpu_set_irq(void *opaque, int irq, int level) switch (irq) { case ARM_CPU_VIRQ: - assert(arm_feature(env, ARM_FEATURE_EL2)); arm_cpu_update_virq(cpu); break; case ARM_CPU_VFIQ: - assert(arm_feature(env, ARM_FEATURE_EL2)); arm_cpu_update_vfiq(cpu); break; case ARM_CPU_IRQ: diff --git a/target/arm/cpu.h b/target/arm/cpu.h index cb5359a747..db8ff04449 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -259,7 +259,8 @@ typedef struct CPUArchState { * all other bits are stored in their correct places in env->pstate */ uint32_t pstate; - uint32_t aarch64; /* 1 if CPU is in aarch64 state; inverse of PSTATE.nRW */ + bool aarch64; /* True if CPU is in aarch64 state; inverse of PSTATE.nRW */ + bool thumb; /* True if CPU is in thumb mode; cpsr[5] */ /* Cached TBFLAGS state. See below for which bits are included. */ CPUARMTBFlags hflags; @@ -286,7 +287,6 @@ typedef struct CPUArchState { uint32_t ZF; /* Z set if zero. */ uint32_t QF; /* 0 or 1 */ uint32_t GE; /* cpsr[19:16] */ - uint32_t thumb; /* cpsr[5]. 0 = arm mode, 1 = thumb mode. */ uint32_t condexec_bits; /* IT bits. cpsr[15:10,26:25]. */ uint32_t btype; /* BTI branch type. spsr[11:10]. */ uint64_t daif; /* exception masks, in the bits they are in PSTATE */ @@ -1233,6 +1233,20 @@ void pmu_init(ARMCPU *cpu); #define SCTLR_ATA0 (1ULL << 42) /* v8.5-MemTag */ #define SCTLR_ATA (1ULL << 43) /* v8.5-MemTag */ #define SCTLR_DSSBS_64 (1ULL << 44) /* v8.5, AArch64 only */ +#define SCTLR_TWEDEn (1ULL << 45) /* FEAT_TWED */ +#define SCTLR_TWEDEL MAKE_64_MASK(46, 4) /* FEAT_TWED */ +#define SCTLR_TMT0 (1ULL << 50) /* FEAT_TME */ +#define SCTLR_TMT (1ULL << 51) /* FEAT_TME */ +#define SCTLR_TME0 (1ULL << 52) /* FEAT_TME */ +#define SCTLR_TME (1ULL << 53) /* FEAT_TME */ +#define SCTLR_EnASR (1ULL << 54) /* FEAT_LS64_V */ +#define SCTLR_EnAS0 (1ULL << 55) /* FEAT_LS64_ACCDATA */ +#define SCTLR_EnALS (1ULL << 56) /* FEAT_LS64 */ +#define SCTLR_EPAN (1ULL << 57) /* FEAT_PAN3 */ +#define SCTLR_EnTP2 (1ULL << 60) /* FEAT_SME */ +#define SCTLR_NMI (1ULL << 61) /* FEAT_NMI */ +#define SCTLR_SPINTMASK (1ULL << 62) /* FEAT_NMI */ +#define SCTLR_TIDCP (1ULL << 63) /* FEAT_TIDCP1 */ #define CPTR_TCPAC (1U << 31) #define CPTR_TTA (1U << 20) @@ -1545,6 +1559,18 @@ static inline void xpsr_write(CPUARMState *env, uint32_t val, uint32_t mask) #define SCR_FIEN (1U << 21) #define SCR_ENSCXT (1U << 25) #define SCR_ATA (1U << 26) +#define SCR_FGTEN (1U << 27) +#define SCR_ECVEN (1U << 28) +#define SCR_TWEDEN (1U << 29) +#define SCR_TWEDEL MAKE_64BIT_MASK(30, 4) +#define SCR_TME (1ULL << 34) +#define SCR_AMVOFFEN (1ULL << 35) +#define SCR_ENAS0 (1ULL << 36) +#define SCR_ADEN (1ULL << 37) +#define SCR_HXEN (1ULL << 38) +#define SCR_TRNDR (1ULL << 40) +#define SCR_ENTP2 (1ULL << 41) +#define SCR_GPF (1ULL << 48) #define HSTR_TTEE (1 << 16) #define HSTR_TJDBX (1 << 17) @@ -1934,6 +1960,7 @@ FIELD(ID_MMFR4, CCIDX, 24, 4) FIELD(ID_MMFR4, EVT, 28, 4) FIELD(ID_MMFR5, ETS, 0, 4) +FIELD(ID_MMFR5, NTLBPA, 4, 4) FIELD(ID_PFR0, STATE0, 0, 4) FIELD(ID_PFR0, STATE1, 4, 4) @@ -1986,6 +2013,16 @@ FIELD(ID_AA64ISAR1, SPECRES, 40, 4) FIELD(ID_AA64ISAR1, BF16, 44, 4) FIELD(ID_AA64ISAR1, DGH, 48, 4) FIELD(ID_AA64ISAR1, I8MM, 52, 4) +FIELD(ID_AA64ISAR1, XS, 56, 4) +FIELD(ID_AA64ISAR1, LS64, 60, 4) + +FIELD(ID_AA64ISAR2, WFXT, 0, 4) +FIELD(ID_AA64ISAR2, RPRES, 4, 4) +FIELD(ID_AA64ISAR2, GPA3, 8, 4) +FIELD(ID_AA64ISAR2, APA3, 12, 4) +FIELD(ID_AA64ISAR2, MOPS, 16, 4) +FIELD(ID_AA64ISAR2, BC, 20, 4) +FIELD(ID_AA64ISAR2, PAC_FRAC, 24, 4) FIELD(ID_AA64PFR0, EL0, 0, 4) FIELD(ID_AA64PFR0, EL1, 4, 4) @@ -2008,6 +2045,10 @@ FIELD(ID_AA64PFR1, SSBS, 4, 4) FIELD(ID_AA64PFR1, MTE, 8, 4) FIELD(ID_AA64PFR1, RAS_FRAC, 12, 4) FIELD(ID_AA64PFR1, MPAM_FRAC, 16, 4) +FIELD(ID_AA64PFR1, SME, 24, 4) +FIELD(ID_AA64PFR1, RNDR_TRAP, 28, 4) +FIELD(ID_AA64PFR1, CSV2_FRAC, 32, 4) +FIELD(ID_AA64PFR1, NMI, 36, 4) FIELD(ID_AA64MMFR0, PARANGE, 0, 4) FIELD(ID_AA64MMFR0, ASIDBITS, 4, 4) @@ -2034,6 +2075,11 @@ FIELD(ID_AA64MMFR1, SPECSEI, 24, 4) FIELD(ID_AA64MMFR1, XNX, 28, 4) FIELD(ID_AA64MMFR1, TWED, 32, 4) FIELD(ID_AA64MMFR1, ETS, 36, 4) +FIELD(ID_AA64MMFR1, HCX, 40, 4) +FIELD(ID_AA64MMFR1, AFP, 44, 4) +FIELD(ID_AA64MMFR1, NTLBPA, 48, 4) +FIELD(ID_AA64MMFR1, TIDCP1, 52, 4) +FIELD(ID_AA64MMFR1, CMOW, 56, 4) FIELD(ID_AA64MMFR2, CNP, 0, 4) FIELD(ID_AA64MMFR2, UAO, 4, 4) @@ -2060,7 +2106,10 @@ FIELD(ID_AA64DFR0, CTX_CMPS, 28, 4) FIELD(ID_AA64DFR0, PMSVER, 32, 4) FIELD(ID_AA64DFR0, DOUBLELOCK, 36, 4) FIELD(ID_AA64DFR0, TRACEFILT, 40, 4) +FIELD(ID_AA64DFR0, TRACEBUFFER, 44, 4) FIELD(ID_AA64DFR0, MTPMU, 48, 4) +FIELD(ID_AA64DFR0, BRBE, 52, 4) +FIELD(ID_AA64DFR0, HPMN0, 60, 4) FIELD(ID_AA64ZFR0, SVEVER, 0, 4) FIELD(ID_AA64ZFR0, AES, 4, 4) @@ -2082,6 +2131,7 @@ FIELD(ID_DFR0, PERFMON, 24, 4) FIELD(ID_DFR0, TRACEFILT, 28, 4) FIELD(ID_DFR1, MTPMU, 0, 4) +FIELD(ID_DFR1, HPMN0, 4, 4) FIELD(DBGDIDR, SE_IMP, 12, 1) FIELD(DBGDIDR, NSUHD_IMP, 14, 1) @@ -2757,11 +2807,6 @@ typedef enum CPAccessResult { /* As CP_ACCESS_UNCATEGORIZED, but for traps directly to EL2 or EL3 */ CP_ACCESS_TRAP_UNCATEGORIZED_EL2 = 5, CP_ACCESS_TRAP_UNCATEGORIZED_EL3 = 6, - /* Access fails and results in an exception syndrome for an FP access, - * trapped directly to EL2 or EL3 - */ - CP_ACCESS_TRAP_FP_EL2 = 7, - CP_ACCESS_TRAP_FP_EL3 = 8, } CPAccessResult; /* Access functions for coprocessor registers. These cannot fail and diff --git a/target/arm/helper-a64.c b/target/arm/helper-a64.c index 7cf953b1e6..77a8502b6b 100644 --- a/target/arm/helper-a64.c +++ b/target/arm/helper-a64.c @@ -952,7 +952,7 @@ void HELPER(exception_return)(CPUARMState *env, uint64_t new_pc) qemu_mutex_unlock_iothread(); if (!return_to_aa64) { - env->aarch64 = 0; + env->aarch64 = false; /* We do a raw CPSR write because aarch64_sync_64_to_32() * will sort the register banks out for us, and we've already * caught all the bad-mode cases in el_from_spsr(). @@ -975,7 +975,7 @@ void HELPER(exception_return)(CPUARMState *env, uint64_t new_pc) } else { int tbii; - env->aarch64 = 1; + env->aarch64 = true; spsr &= aarch64_pstate_valid_mask(&env_archcpu(env)->isar); pstate_write(env, spsr); if (!arm_singlestep_active(env)) { diff --git a/target/arm/helper.c b/target/arm/helper.c index d7715c911a..63397bbac1 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -4784,18 +4784,6 @@ static void sctlr_write(CPUARMState *env, const ARMCPRegInfo *ri, } } -static CPAccessResult fpexc32_access(CPUARMState *env, const ARMCPRegInfo *ri, - bool isread) -{ - if ((env->cp15.cptr_el[2] & CPTR_TFP) && arm_current_el(env) == 2) { - return CP_ACCESS_TRAP_FP_EL2; - } - if (env->cp15.cptr_el[3] & CPTR_TFP) { - return CP_ACCESS_TRAP_FP_EL3; - } - return CP_ACCESS_OK; -} - static void sdcr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { @@ -5097,9 +5085,8 @@ static const ARMCPRegInfo v8_cp_reginfo[] = { .access = PL1_RW, .readfn = spsel_read, .writefn = spsel_write }, { .name = "FPEXC32_EL2", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 4, .crn = 5, .crm = 3, .opc2 = 0, - .type = ARM_CP_ALIAS, - .fieldoffset = offsetof(CPUARMState, vfp.xregs[ARM_VFP_FPEXC]), - .access = PL2_RW, .accessfn = fpexc32_access }, + .access = PL2_RW, .type = ARM_CP_ALIAS | ARM_CP_FPU, + .fieldoffset = offsetof(CPUARMState, vfp.xregs[ARM_VFP_FPEXC]) }, { .name = "DACR32_EL2", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 4, .crn = 3, .crm = 0, .opc2 = 0, .access = PL2_RW, .resetvalue = 0, @@ -10181,7 +10168,7 @@ static void arm_cpu_do_interrupt_aarch64(CPUState *cs) } pstate_write(env, PSTATE_DAIF | new_mode); - env->aarch64 = 1; + env->aarch64 = true; aarch64_restore_sp(env, new_el); helper_rebuild_hflags_a64(env, new_el); diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c index 567e296b21..b11a8b9a18 100644 --- a/target/arm/hvf/hvf.c +++ b/target/arm/hvf/hvf.c @@ -564,7 +564,7 @@ int hvf_arch_init_vcpu(CPUState *cpu) hv_return_t ret; int i; - env->aarch64 = 1; + env->aarch64 = true; asm volatile("mrs %0, cntfrq_el0" : "=r"(arm_cpu->gt_cntfrq_hz)); /* Allocate enough space for our sysreg sync */ diff --git a/target/arm/m_helper.c b/target/arm/m_helper.c index b7a0fe0114..a740c3e160 100644 --- a/target/arm/m_helper.c +++ b/target/arm/m_helper.c @@ -564,7 +564,7 @@ void HELPER(v7m_bxns)(CPUARMState *env, uint32_t dest) env->v7m.control[M_REG_S] &= ~R_V7M_CONTROL_SFPA_MASK; } switch_v7m_security_state(env, dest & 1); - env->thumb = 1; + env->thumb = true; env->regs[15] = dest & ~1; arm_rebuild_hflags(env); } @@ -590,7 +590,7 @@ void HELPER(v7m_blxns)(CPUARMState *env, uint32_t dest) * except that the low bit doesn't indicate Thumb/not. */ env->regs[14] = nextinst; - env->thumb = 1; + env->thumb = true; env->regs[15] = dest & ~1; return; } @@ -626,7 +626,7 @@ void HELPER(v7m_blxns)(CPUARMState *env, uint32_t dest) } env->v7m.control[M_REG_S] &= ~R_V7M_CONTROL_SFPA_MASK; switch_v7m_security_state(env, 0); - env->thumb = 1; + env->thumb = true; env->regs[15] = dest; arm_rebuild_hflags(env); } diff --git a/target/arm/op_helper.c b/target/arm/op_helper.c index 70b42b55fd..2b87e8808b 100644 --- a/target/arm/op_helper.c +++ b/target/arm/op_helper.c @@ -691,19 +691,6 @@ void HELPER(access_check_cp_reg)(CPUARMState *env, void *rip, uint32_t syndrome, target_el = 3; syndrome = syn_uncategorized(); break; - case CP_ACCESS_TRAP_FP_EL2: - target_el = 2; - /* Since we are an implementation that takes exceptions on a trapped - * conditional insn only if the insn has passed its condition code - * check, we take the IMPDEF choice to always report CV=1 COND=0xe - * (which is also the required value for AArch64 traps). - */ - syndrome = syn_fp_access_trap(1, 0xe, false); - break; - case CP_ACCESS_TRAP_FP_EL3: - target_el = 3; - syndrome = syn_fp_access_trap(1, 0xe, false); - break; default: g_assert_not_reached(); } diff --git a/target/arm/translate-a32.h b/target/arm/translate-a32.h index 5be4b9b834..09010ad2da 100644 --- a/target/arm/translate-a32.h +++ b/target/arm/translate-a32.h @@ -61,17 +61,14 @@ static inline TCGv_i32 load_cpu_offset(int offset) #define load_cpu_field(name) load_cpu_offset(offsetof(CPUARMState, name)) -static inline void store_cpu_offset(TCGv_i32 var, int offset) -{ - tcg_gen_st_i32(var, cpu_env, offset); - tcg_temp_free_i32(var); -} +void store_cpu_offset(TCGv_i32 var, int offset, int size); -#define store_cpu_field(var, name) \ - store_cpu_offset(var, offsetof(CPUARMState, name)) +#define store_cpu_field(var, name) \ + store_cpu_offset(var, offsetof(CPUARMState, name), \ + sizeof_field(CPUARMState, name)) #define store_cpu_field_constant(val, name) \ - tcg_gen_st_i32(tcg_constant_i32(val), cpu_env, offsetof(CPUARMState, name)) + store_cpu_field(tcg_constant_i32(val), name) /* Create a new temporary and set it to the value of a CPU register. */ static inline TCGv_i32 load_reg(DisasContext *s, int reg) diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c index 19c09c3b53..adbcd99941 100644 --- a/target/arm/translate-a64.c +++ b/target/arm/translate-a64.c @@ -128,29 +128,28 @@ static int get_a64_user_mem_index(DisasContext *s) return arm_to_core_mmu_idx(useridx); } -static void reset_btype(DisasContext *s) +static void set_btype_raw(int val) { - if (s->btype != 0) { - TCGv_i32 zero = tcg_const_i32(0); - tcg_gen_st_i32(zero, cpu_env, offsetof(CPUARMState, btype)); - tcg_temp_free_i32(zero); - s->btype = 0; - } + tcg_gen_st_i32(tcg_constant_i32(val), cpu_env, + offsetof(CPUARMState, btype)); } static void set_btype(DisasContext *s, int val) { - TCGv_i32 tcg_val; - /* BTYPE is a 2-bit field, and 0 should be done with reset_btype. */ tcg_debug_assert(val >= 1 && val <= 3); - - tcg_val = tcg_const_i32(val); - tcg_gen_st_i32(tcg_val, cpu_env, offsetof(CPUARMState, btype)); - tcg_temp_free_i32(tcg_val); + set_btype_raw(val); s->btype = -1; } +static void reset_btype(DisasContext *s) +{ + if (s->btype != 0) { + set_btype_raw(0); + s->btype = 0; + } +} + void gen_a64_set_pc_im(uint64_t val) { tcg_gen_movi_i64(cpu_pc, val); @@ -342,6 +341,11 @@ static void a64_free_cc(DisasCompare64 *c64) tcg_temp_free_i64(c64->value); } +static void gen_rebuild_hflags(DisasContext *s) +{ + gen_helper_rebuild_hflags_a64(cpu_env, tcg_constant_i32(s->current_el)); +} + static void gen_exception_internal(int excp) { TCGv_i32 tcg_excp = tcg_const_i32(excp); @@ -1668,9 +1672,7 @@ static void handle_msr_i(DisasContext *s, uint32_t insn, } else { clear_pstate_bits(PSTATE_UAO); } - t1 = tcg_const_i32(s->current_el); - gen_helper_rebuild_hflags_a64(cpu_env, t1); - tcg_temp_free_i32(t1); + gen_rebuild_hflags(s); break; case 0x04: /* PAN */ @@ -1682,9 +1684,7 @@ static void handle_msr_i(DisasContext *s, uint32_t insn, } else { clear_pstate_bits(PSTATE_PAN); } - t1 = tcg_const_i32(s->current_el); - gen_helper_rebuild_hflags_a64(cpu_env, t1); - tcg_temp_free_i32(t1); + gen_rebuild_hflags(s); break; case 0x05: /* SPSel */ @@ -1742,9 +1742,7 @@ static void handle_msr_i(DisasContext *s, uint32_t insn, } else { clear_pstate_bits(PSTATE_TCO); } - t1 = tcg_const_i32(s->current_el); - gen_helper_rebuild_hflags_a64(cpu_env, t1); - tcg_temp_free_i32(t1); + gen_rebuild_hflags(s); /* Many factors, including TCO, go into MTE_ACTIVE. */ s->base.is_jmp = DISAS_UPDATE_NOCHAIN; } else if (dc_isar_feature(aa64_mte_insn_reg, s)) { @@ -1991,9 +1989,7 @@ static void handle_sys(DisasContext *s, uint32_t insn, bool isread, * A write to any coprocessor regiser that ends a TB * must rebuild the hflags for the next TB. */ - TCGv_i32 tcg_el = tcg_const_i32(s->current_el); - gen_helper_rebuild_hflags_a64(cpu_env, tcg_el); - tcg_temp_free_i32(tcg_el); + gen_rebuild_hflags(s); /* * We default to ending the TB on a coprocessor register write, * but allow this to be suppressed by the register definition @@ -14664,13 +14660,13 @@ static void aarch64_tr_init_disas_context(DisasContextBase *dcbase, dc->isar = &arm_cpu->isar; dc->condjmp = 0; - dc->aarch64 = 1; + dc->aarch64 = true; /* If we are coming from secure EL0 in a system with a 32-bit EL3, then * there is no secure EL1, so we route exceptions to EL3. */ dc->secure_routed_to_el3 = arm_feature(env, ARM_FEATURE_EL3) && !arm_el_is_aa64(env, 3); - dc->thumb = 0; + dc->thumb = false; dc->sctlr_b = 0; dc->be_data = EX_TBFLAG_ANY(tb_flags, BE_DATA) ? MO_BE : MO_LE; dc->condexec_mask = 0; diff --git a/target/arm/translate-m-nocp.c b/target/arm/translate-m-nocp.c index d9e144e8eb..27363a7b4e 100644 --- a/target/arm/translate-m-nocp.c +++ b/target/arm/translate-m-nocp.c @@ -173,7 +173,7 @@ static bool trans_VSCCLRM(DisasContext *s, arg_VSCCLRM *a) } /* Zero the Sregs from btmreg to topreg inclusive. */ - zero = tcg_const_i64(0); + zero = tcg_constant_i64(0); if (btmreg & 1) { write_neon_element64(zero, btmreg >> 1, 1, MO_32); btmreg++; @@ -187,8 +187,7 @@ static bool trans_VSCCLRM(DisasContext *s, arg_VSCCLRM *a) } assert(btmreg == topreg + 1); if (dc_isar_feature(aa32_mve, s)) { - TCGv_i32 z32 = tcg_const_i32(0); - store_cpu_field(z32, v7m.vpr); + store_cpu_field(tcg_constant_i32(0), v7m.vpr); } clear_eci_state(s); @@ -512,7 +511,7 @@ static bool gen_M_fp_sysreg_read(DisasContext *s, int regno, } case ARM_VFP_FPCXT_NS: { - TCGv_i32 control, sfpa, fpscr, fpdscr, zero; + TCGv_i32 control, sfpa, fpscr, fpdscr; TCGLabel *lab_active = gen_new_label(); lookup_tb = true; @@ -552,10 +551,9 @@ static bool gen_M_fp_sysreg_read(DisasContext *s, int regno, storefn(s, opaque, tmp, true); /* If SFPA is zero then set FPSCR from FPDSCR_NS */ fpdscr = load_cpu_field(v7m.fpdscr[M_REG_NS]); - zero = tcg_const_i32(0); - tcg_gen_movcond_i32(TCG_COND_EQ, fpscr, sfpa, zero, fpdscr, fpscr); + tcg_gen_movcond_i32(TCG_COND_EQ, fpscr, sfpa, tcg_constant_i32(0), + fpdscr, fpscr); gen_helper_vfp_set_fpscr(cpu_env, fpscr); - tcg_temp_free_i32(zero); tcg_temp_free_i32(sfpa); tcg_temp_free_i32(fpdscr); tcg_temp_free_i32(fpscr); diff --git a/target/arm/translate-neon.c b/target/arm/translate-neon.c index 384604c009..2e4d1ec87d 100644 --- a/target/arm/translate-neon.c +++ b/target/arm/translate-neon.c @@ -447,7 +447,7 @@ static bool trans_VLDST_multiple(DisasContext *s, arg_VLDST_multiple *a) int mmu_idx = get_mem_index(s); int size = a->size; TCGv_i64 tmp64; - TCGv_i32 addr, tmp; + TCGv_i32 addr; if (!arm_dc_feature(s, ARM_FEATURE_NEON)) { return false; @@ -513,7 +513,6 @@ static bool trans_VLDST_multiple(DisasContext *s, arg_VLDST_multiple *a) tmp64 = tcg_temp_new_i64(); addr = tcg_temp_new_i32(); - tmp = tcg_const_i32(1 << size); load_reg_var(s, addr, a->rn); mop = endian | size | align; @@ -530,7 +529,7 @@ static bool trans_VLDST_multiple(DisasContext *s, arg_VLDST_multiple *a) neon_load_element64(tmp64, tt, n, size); gen_aa32_st_internal_i64(s, tmp64, addr, mmu_idx, mop); } - tcg_gen_add_i32(addr, addr, tmp); + tcg_gen_addi_i32(addr, addr, 1 << size); /* Subsequent memory operations inherit alignment */ mop &= ~MO_AMASK; @@ -538,7 +537,6 @@ static bool trans_VLDST_multiple(DisasContext *s, arg_VLDST_multiple *a) } } tcg_temp_free_i32(addr); - tcg_temp_free_i32(tmp); tcg_temp_free_i64(tmp64); gen_neon_ldst_base_update(s, a->rm, a->rn, nregs * interleave * 8); @@ -1348,7 +1346,7 @@ static bool do_2shift_env_64(DisasContext *s, arg_2reg_shift *a, * To avoid excessive duplication of ops we implement shift * by immediate using the variable shift operations. */ - constimm = tcg_const_i64(dup_const(a->size, a->shift)); + constimm = tcg_constant_i64(dup_const(a->size, a->shift)); for (pass = 0; pass < a->q + 1; pass++) { TCGv_i64 tmp = tcg_temp_new_i64(); @@ -1358,7 +1356,6 @@ static bool do_2shift_env_64(DisasContext *s, arg_2reg_shift *a, write_neon_element64(tmp, a->vd, pass, MO_64); tcg_temp_free_i64(tmp); } - tcg_temp_free_i64(constimm); return true; } @@ -1394,7 +1391,7 @@ static bool do_2shift_env_32(DisasContext *s, arg_2reg_shift *a, * To avoid excessive duplication of ops we implement shift * by immediate using the variable shift operations. */ - constimm = tcg_const_i32(dup_const(a->size, a->shift)); + constimm = tcg_constant_i32(dup_const(a->size, a->shift)); tmp = tcg_temp_new_i32(); for (pass = 0; pass < (a->q ? 4 : 2); pass++) { @@ -1403,7 +1400,6 @@ static bool do_2shift_env_32(DisasContext *s, arg_2reg_shift *a, write_neon_element32(tmp, a->vd, pass, MO_32); } tcg_temp_free_i32(tmp); - tcg_temp_free_i32(constimm); return true; } @@ -1457,7 +1453,7 @@ static bool do_2shift_narrow_64(DisasContext *s, arg_2reg_shift *a, * This is always a right shift, and the shiftfn is always a * left-shift helper, which thus needs the negated shift count. */ - constimm = tcg_const_i64(-a->shift); + constimm = tcg_constant_i64(-a->shift); rm1 = tcg_temp_new_i64(); rm2 = tcg_temp_new_i64(); rd = tcg_temp_new_i32(); @@ -1477,7 +1473,6 @@ static bool do_2shift_narrow_64(DisasContext *s, arg_2reg_shift *a, tcg_temp_free_i32(rd); tcg_temp_free_i64(rm1); tcg_temp_free_i64(rm2); - tcg_temp_free_i64(constimm); return true; } @@ -1521,7 +1516,7 @@ static bool do_2shift_narrow_32(DisasContext *s, arg_2reg_shift *a, /* size == 2 */ imm = -a->shift; } - constimm = tcg_const_i32(imm); + constimm = tcg_constant_i32(imm); /* Load all inputs first to avoid potential overwrite */ rm1 = tcg_temp_new_i32(); @@ -1546,7 +1541,6 @@ static bool do_2shift_narrow_32(DisasContext *s, arg_2reg_shift *a, shiftfn(rm3, rm3, constimm); shiftfn(rm4, rm4, constimm); - tcg_temp_free_i32(constimm); tcg_gen_concat_i32_i64(rtmp, rm3, rm4); tcg_temp_free_i32(rm4); @@ -2911,7 +2905,7 @@ static bool trans_VTBL(DisasContext *s, arg_VTBL *a) return true; } - desc = tcg_const_i32((a->vn << 2) | a->len); + desc = tcg_constant_i32((a->vn << 2) | a->len); def = tcg_temp_new_i64(); if (a->op) { read_neon_element64(def, a->vd, 0, MO_64); @@ -2926,7 +2920,6 @@ static bool trans_VTBL(DisasContext *s, arg_VTBL *a) tcg_temp_free_i64(def); tcg_temp_free_i64(val); - tcg_temp_free_i32(desc); return true; } diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index 180e14d9f8..726cf88d7c 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -1916,8 +1916,6 @@ static bool trans_PNEXT(DisasContext *s, arg_rr_esz *a) static void do_sat_addsub_32(TCGv_i64 reg, TCGv_i64 val, bool u, bool d) { int64_t ibound; - TCGv_i64 bound; - TCGCond cond; /* Use normal 64-bit arithmetic to detect 32-bit overflow. */ if (u) { @@ -1928,15 +1926,12 @@ static void do_sat_addsub_32(TCGv_i64 reg, TCGv_i64 val, bool u, bool d) if (d) { tcg_gen_sub_i64(reg, reg, val); ibound = (u ? 0 : INT32_MIN); - cond = TCG_COND_LT; + tcg_gen_smax_i64(reg, reg, tcg_constant_i64(ibound)); } else { tcg_gen_add_i64(reg, reg, val); ibound = (u ? UINT32_MAX : INT32_MAX); - cond = TCG_COND_GT; + tcg_gen_smin_i64(reg, reg, tcg_constant_i64(ibound)); } - bound = tcg_const_i64(ibound); - tcg_gen_movcond_i64(cond, reg, reg, bound, bound, reg); - tcg_temp_free_i64(bound); } /* Similarly with 64-bit values. */ diff --git a/target/arm/translate-vfp.c b/target/arm/translate-vfp.c index 6a95a67a69..40a513b822 100644 --- a/target/arm/translate-vfp.c +++ b/target/arm/translate-vfp.c @@ -180,8 +180,7 @@ static void gen_update_fp_context(DisasContext *s) gen_helper_vfp_set_fpscr(cpu_env, fpscr); tcg_temp_free_i32(fpscr); if (dc_isar_feature(aa32_mve, s)) { - TCGv_i32 z32 = tcg_const_i32(0); - store_cpu_field(z32, v7m.vpr); + store_cpu_field(tcg_constant_i32(0), v7m.vpr); } /* * We just updated the FPSCR and VPR. Some of this state is cached @@ -317,7 +316,7 @@ static bool trans_VSEL(DisasContext *s, arg_VSEL *a) TCGv_i64 frn, frm, dest; TCGv_i64 tmp, zero, zf, nf, vf; - zero = tcg_const_i64(0); + zero = tcg_constant_i64(0); frn = tcg_temp_new_i64(); frm = tcg_temp_new_i64(); @@ -335,27 +334,22 @@ static bool trans_VSEL(DisasContext *s, arg_VSEL *a) vfp_load_reg64(frm, rm); switch (a->cc) { case 0: /* eq: Z */ - tcg_gen_movcond_i64(TCG_COND_EQ, dest, zf, zero, - frn, frm); + tcg_gen_movcond_i64(TCG_COND_EQ, dest, zf, zero, frn, frm); break; case 1: /* vs: V */ - tcg_gen_movcond_i64(TCG_COND_LT, dest, vf, zero, - frn, frm); + tcg_gen_movcond_i64(TCG_COND_LT, dest, vf, zero, frn, frm); break; case 2: /* ge: N == V -> N ^ V == 0 */ tmp = tcg_temp_new_i64(); tcg_gen_xor_i64(tmp, vf, nf); - tcg_gen_movcond_i64(TCG_COND_GE, dest, tmp, zero, - frn, frm); + tcg_gen_movcond_i64(TCG_COND_GE, dest, tmp, zero, frn, frm); tcg_temp_free_i64(tmp); break; case 3: /* gt: !Z && N == V */ - tcg_gen_movcond_i64(TCG_COND_NE, dest, zf, zero, - frn, frm); + tcg_gen_movcond_i64(TCG_COND_NE, dest, zf, zero, frn, frm); tmp = tcg_temp_new_i64(); tcg_gen_xor_i64(tmp, vf, nf); - tcg_gen_movcond_i64(TCG_COND_GE, dest, tmp, zero, - dest, frm); + tcg_gen_movcond_i64(TCG_COND_GE, dest, tmp, zero, dest, frm); tcg_temp_free_i64(tmp); break; } @@ -367,13 +361,11 @@ static bool trans_VSEL(DisasContext *s, arg_VSEL *a) tcg_temp_free_i64(zf); tcg_temp_free_i64(nf); tcg_temp_free_i64(vf); - - tcg_temp_free_i64(zero); } else { TCGv_i32 frn, frm, dest; TCGv_i32 tmp, zero; - zero = tcg_const_i32(0); + zero = tcg_constant_i32(0); frn = tcg_temp_new_i32(); frm = tcg_temp_new_i32(); @@ -382,27 +374,22 @@ static bool trans_VSEL(DisasContext *s, arg_VSEL *a) vfp_load_reg32(frm, rm); switch (a->cc) { case 0: /* eq: Z */ - tcg_gen_movcond_i32(TCG_COND_EQ, dest, cpu_ZF, zero, - frn, frm); + tcg_gen_movcond_i32(TCG_COND_EQ, dest, cpu_ZF, zero, frn, frm); break; case 1: /* vs: V */ - tcg_gen_movcond_i32(TCG_COND_LT, dest, cpu_VF, zero, - frn, frm); + tcg_gen_movcond_i32(TCG_COND_LT, dest, cpu_VF, zero, frn, frm); break; case 2: /* ge: N == V -> N ^ V == 0 */ tmp = tcg_temp_new_i32(); tcg_gen_xor_i32(tmp, cpu_VF, cpu_NF); - tcg_gen_movcond_i32(TCG_COND_GE, dest, tmp, zero, - frn, frm); + tcg_gen_movcond_i32(TCG_COND_GE, dest, tmp, zero, frn, frm); tcg_temp_free_i32(tmp); break; case 3: /* gt: !Z && N == V */ - tcg_gen_movcond_i32(TCG_COND_NE, dest, cpu_ZF, zero, - frn, frm); + tcg_gen_movcond_i32(TCG_COND_NE, dest, cpu_ZF, zero, frn, frm); tmp = tcg_temp_new_i32(); tcg_gen_xor_i32(tmp, cpu_VF, cpu_NF); - tcg_gen_movcond_i32(TCG_COND_GE, dest, tmp, zero, - dest, frm); + tcg_gen_movcond_i32(TCG_COND_GE, dest, tmp, zero, dest, frm); tcg_temp_free_i32(tmp); break; } @@ -414,8 +401,6 @@ static bool trans_VSEL(DisasContext *s, arg_VSEL *a) tcg_temp_free_i32(frn); tcg_temp_free_i32(frm); tcg_temp_free_i32(dest); - - tcg_temp_free_i32(zero); } return true; @@ -547,7 +532,7 @@ static bool trans_VCVT(DisasContext *s, arg_VCVT *a) fpst = fpstatus_ptr(FPST_FPCR); } - tcg_shift = tcg_const_i32(0); + tcg_shift = tcg_constant_i32(0); tcg_rmode = tcg_const_i32(arm_rmode_to_sf(rounding)); gen_helper_set_rmode(tcg_rmode, tcg_rmode, fpst); @@ -595,8 +580,6 @@ static bool trans_VCVT(DisasContext *s, arg_VCVT *a) gen_helper_set_rmode(tcg_rmode, tcg_rmode, fpst); tcg_temp_free_i32(tcg_rmode); - tcg_temp_free_i32(tcg_shift); - tcg_temp_free_ptr(fpst); return true; @@ -850,15 +833,11 @@ static bool trans_VMSR_VMRS(DisasContext *s, arg_VMSR_VMRS *a) case ARM_VFP_MVFR2: case ARM_VFP_FPSID: if (s->current_el == 1) { - TCGv_i32 tcg_reg, tcg_rt; - gen_set_condexec(s); gen_set_pc_im(s, s->pc_curr); - tcg_reg = tcg_const_i32(a->reg); - tcg_rt = tcg_const_i32(a->rt); - gen_helper_check_hcr_el2_trap(cpu_env, tcg_rt, tcg_reg); - tcg_temp_free_i32(tcg_reg); - tcg_temp_free_i32(tcg_rt); + gen_helper_check_hcr_el2_trap(cpu_env, + tcg_constant_i32(a->rt), + tcg_constant_i32(a->reg)); } /* fall through */ case ARM_VFP_FPEXC: @@ -2388,8 +2367,6 @@ MAKE_VFM_TRANS_FNS(dp) static bool trans_VMOV_imm_hp(DisasContext *s, arg_VMOV_imm_sp *a) { - TCGv_i32 fd; - if (!dc_isar_feature(aa32_fp16_arith, s)) { return false; } @@ -2402,9 +2379,7 @@ static bool trans_VMOV_imm_hp(DisasContext *s, arg_VMOV_imm_sp *a) return true; } - fd = tcg_const_i32(vfp_expand_imm(MO_16, a->imm)); - vfp_store_reg32(fd, a->vd); - tcg_temp_free_i32(fd); + vfp_store_reg32(tcg_constant_i32(vfp_expand_imm(MO_16, a->imm)), a->vd); return true; } @@ -2440,7 +2415,7 @@ static bool trans_VMOV_imm_sp(DisasContext *s, arg_VMOV_imm_sp *a) } } - fd = tcg_const_i32(vfp_expand_imm(MO_32, a->imm)); + fd = tcg_constant_i32(vfp_expand_imm(MO_32, a->imm)); for (;;) { vfp_store_reg32(fd, vd); @@ -2454,7 +2429,6 @@ static bool trans_VMOV_imm_sp(DisasContext *s, arg_VMOV_imm_sp *a) vd = vfp_advance_sreg(vd, delta_d); } - tcg_temp_free_i32(fd); return true; } @@ -2495,7 +2469,7 @@ static bool trans_VMOV_imm_dp(DisasContext *s, arg_VMOV_imm_dp *a) } } - fd = tcg_const_i64(vfp_expand_imm(MO_64, a->imm)); + fd = tcg_constant_i64(vfp_expand_imm(MO_64, a->imm)); for (;;) { vfp_store_reg64(fd, vd); @@ -2509,7 +2483,6 @@ static bool trans_VMOV_imm_dp(DisasContext *s, arg_VMOV_imm_dp *a) vd = vfp_advance_dreg(vd, delta_d); } - tcg_temp_free_i64(fd); return true; } @@ -3294,7 +3267,7 @@ static bool trans_VCVT_fix_hp(DisasContext *s, arg_VCVT_fix_sp *a) vfp_load_reg32(vd, a->vd); fpst = fpstatus_ptr(FPST_FPCR_F16); - shift = tcg_const_i32(frac_bits); + shift = tcg_constant_i32(frac_bits); /* Switch on op:U:sx bits */ switch (a->opc) { @@ -3328,7 +3301,6 @@ static bool trans_VCVT_fix_hp(DisasContext *s, arg_VCVT_fix_sp *a) vfp_store_reg32(vd, a->vd); tcg_temp_free_i32(vd); - tcg_temp_free_i32(shift); tcg_temp_free_ptr(fpst); return true; } @@ -3353,7 +3325,7 @@ static bool trans_VCVT_fix_sp(DisasContext *s, arg_VCVT_fix_sp *a) vfp_load_reg32(vd, a->vd); fpst = fpstatus_ptr(FPST_FPCR); - shift = tcg_const_i32(frac_bits); + shift = tcg_constant_i32(frac_bits); /* Switch on op:U:sx bits */ switch (a->opc) { @@ -3387,7 +3359,6 @@ static bool trans_VCVT_fix_sp(DisasContext *s, arg_VCVT_fix_sp *a) vfp_store_reg32(vd, a->vd); tcg_temp_free_i32(vd); - tcg_temp_free_i32(shift); tcg_temp_free_ptr(fpst); return true; } @@ -3418,7 +3389,7 @@ static bool trans_VCVT_fix_dp(DisasContext *s, arg_VCVT_fix_dp *a) vfp_load_reg64(vd, a->vd); fpst = fpstatus_ptr(FPST_FPCR); - shift = tcg_const_i32(frac_bits); + shift = tcg_constant_i32(frac_bits); /* Switch on op:U:sx bits */ switch (a->opc) { @@ -3452,7 +3423,6 @@ static bool trans_VCVT_fix_dp(DisasContext *s, arg_VCVT_fix_dp *a) vfp_store_reg64(vd, a->vd); tcg_temp_free_i64(vd); - tcg_temp_free_i32(shift); tcg_temp_free_ptr(fpst); return true; } diff --git a/target/arm/translate.c b/target/arm/translate.c index 38e7a38f28..d09692c125 100644 --- a/target/arm/translate.c +++ b/target/arm/translate.c @@ -180,6 +180,25 @@ typedef enum ISSInfo { ISSIs16Bit = (1 << 8), } ISSInfo; +/* + * Store var into env + offset to a member with size bytes. + * Free var after use. + */ +void store_cpu_offset(TCGv_i32 var, int offset, int size) +{ + switch (size) { + case 1: + tcg_gen_st8_i32(var, cpu_env, offset); + break; + case 4: + tcg_gen_st_i32(var, cpu_env, offset); + break; + default: + g_assert_not_reached(); + } + tcg_temp_free_i32(var); +} + /* Save the syndrome information for a Data Abort */ static void disas_set_da_iss(DisasContext *s, MemOp memop, ISSInfo issinfo) { @@ -332,6 +351,26 @@ void gen_set_cpsr(TCGv_i32 var, uint32_t mask) tcg_temp_free_i32(tmp_mask); } +static void gen_rebuild_hflags(DisasContext *s, bool new_el) +{ + bool m_profile = arm_dc_feature(s, ARM_FEATURE_M); + + if (new_el) { + if (m_profile) { + gen_helper_rebuild_hflags_m32_newel(cpu_env); + } else { + gen_helper_rebuild_hflags_a32_newel(cpu_env); + } + } else { + TCGv_i32 tcg_el = tcg_constant_i32(s->current_el); + if (m_profile) { + gen_helper_rebuild_hflags_m32(cpu_env, tcg_el); + } else { + gen_helper_rebuild_hflags_a32(cpu_env, tcg_el); + } + } +} + static void gen_exception_internal(int excp) { TCGv_i32 tcg_excp = tcg_const_i32(excp); @@ -513,16 +552,14 @@ static void gen_sbc_CC(TCGv_i32 dest, TCGv_i32 t0, TCGv_i32 t1) #define GEN_SHIFT(name) \ static void gen_##name(TCGv_i32 dest, TCGv_i32 t0, TCGv_i32 t1) \ { \ - TCGv_i32 tmp1, tmp2, tmp3; \ - tmp1 = tcg_temp_new_i32(); \ - tcg_gen_andi_i32(tmp1, t1, 0xff); \ - tmp2 = tcg_const_i32(0); \ - tmp3 = tcg_const_i32(0x1f); \ - tcg_gen_movcond_i32(TCG_COND_GTU, tmp2, tmp1, tmp3, tmp2, t0); \ - tcg_temp_free_i32(tmp3); \ - tcg_gen_andi_i32(tmp1, tmp1, 0x1f); \ - tcg_gen_##name##_i32(dest, tmp2, tmp1); \ - tcg_temp_free_i32(tmp2); \ + TCGv_i32 tmpd = tcg_temp_new_i32(); \ + TCGv_i32 tmp1 = tcg_temp_new_i32(); \ + TCGv_i32 zero = tcg_constant_i32(0); \ + tcg_gen_andi_i32(tmp1, t1, 0x1f); \ + tcg_gen_##name##_i32(tmpd, t0, tmp1); \ + tcg_gen_andi_i32(tmp1, t1, 0xe0); \ + tcg_gen_movcond_i32(TCG_COND_NE, dest, tmp1, zero, zero, tmpd); \ + tcg_temp_free_i32(tmpd); \ tcg_temp_free_i32(tmp1); \ } GEN_SHIFT(shl) @@ -531,12 +568,10 @@ GEN_SHIFT(shr) static void gen_sar(TCGv_i32 dest, TCGv_i32 t0, TCGv_i32 t1) { - TCGv_i32 tmp1, tmp2; - tmp1 = tcg_temp_new_i32(); + TCGv_i32 tmp1 = tcg_temp_new_i32(); + tcg_gen_andi_i32(tmp1, t1, 0xff); - tmp2 = tcg_const_i32(0x1f); - tcg_gen_movcond_i32(TCG_COND_GTU, tmp1, tmp1, tmp2, tmp2, tmp1); - tcg_temp_free_i32(tmp2); + tcg_gen_umin_i32(tmp1, tmp1, tcg_constant_i32(31)); tcg_gen_sar_i32(dest, t0, tmp1); tcg_temp_free_i32(tmp1); } @@ -4852,7 +4887,7 @@ static void do_coproc_insn(DisasContext *s, int cpnum, int is64, tcg_temp_free_i32(tmp); } else { TCGv_i32 tmp = load_reg(s, rt); - store_cpu_offset(tmp, ri->fieldoffset); + store_cpu_offset(tmp, ri->fieldoffset, 4); } } } @@ -4866,17 +4901,7 @@ static void do_coproc_insn(DisasContext *s, int cpnum, int is64, * A write to any coprocessor register that ends a TB * must rebuild the hflags for the next TB. */ - TCGv_i32 tcg_el = tcg_const_i32(s->current_el); - if (arm_dc_feature(s, ARM_FEATURE_M)) { - gen_helper_rebuild_hflags_m32(cpu_env, tcg_el); - } else { - if (ri->type & ARM_CP_NEWEL) { - gen_helper_rebuild_hflags_a32_newel(cpu_env); - } else { - gen_helper_rebuild_hflags_a32(cpu_env, tcg_el); - } - } - tcg_temp_free_i32(tcg_el); + gen_rebuild_hflags(s, ri->type & ARM_CP_NEWEL); /* * We default to ending the TB on a coprocessor register write, * but allow this to be suppressed by the register definition @@ -6426,7 +6451,7 @@ static bool trans_MSR_v7m(DisasContext *s, arg_MSR_v7m *a) tcg_temp_free_i32(addr); tcg_temp_free_i32(reg); /* If we wrote to CONTROL, the EL might have changed */ - gen_helper_rebuild_hflags_m32_newel(cpu_env); + gen_rebuild_hflags(s, true); gen_lookup_tb(s); return true; } @@ -8878,7 +8903,7 @@ static bool trans_CPS(DisasContext *s, arg_CPS *a) static bool trans_CPS_v7m(DisasContext *s, arg_CPS_v7m *a) { - TCGv_i32 tmp, addr, el; + TCGv_i32 tmp, addr; if (!arm_dc_feature(s, ARM_FEATURE_M)) { return false; @@ -8901,9 +8926,7 @@ static bool trans_CPS_v7m(DisasContext *s, arg_CPS_v7m *a) gen_helper_v7m_msr(cpu_env, addr, tmp); tcg_temp_free_i32(addr); } - el = tcg_const_i32(s->current_el); - gen_helper_rebuild_hflags_m32(cpu_env, el); - tcg_temp_free_i32(el); + gen_rebuild_hflags(s, false); tcg_temp_free_i32(tmp); gen_lookup_tb(s); return true; @@ -9334,7 +9357,7 @@ static void arm_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) dc->isar = &cpu->isar; dc->condjmp = 0; - dc->aarch64 = 0; + dc->aarch64 = false; /* If we are coming from secure EL0 in a system with a 32-bit EL3, then * there is no secure EL1, so we route exceptions to EL3. */ @@ -9847,18 +9870,14 @@ static void arm_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) /* nothing more to generate */ break; case DISAS_WFI: - { - TCGv_i32 tmp = tcg_const_i32((dc->thumb && - !(dc->insn & (1U << 31))) ? 2 : 4); - - gen_helper_wfi(cpu_env, tmp); - tcg_temp_free_i32(tmp); - /* The helper doesn't necessarily throw an exception, but we + gen_helper_wfi(cpu_env, + tcg_constant_i32(dc->base.pc_next - dc->pc_curr)); + /* + * The helper doesn't necessarily throw an exception, but we * must go back to the main loop to check for interrupts anyway. */ tcg_gen_exit_tb(NULL, 0); break; - } case DISAS_WFE: gen_helper_wfe(cpu_env); break; diff --git a/target/arm/translate.h b/target/arm/translate.h index 3a0db801d3..6f0ebdc88e 100644 --- a/target/arm/translate.h +++ b/target/arm/translate.h @@ -30,7 +30,6 @@ typedef struct DisasContext { bool eci_handled; /* TCG op to rewind to if this turns out to be an invalid ECI state */ TCGOp *insn_eci_rewind; - int thumb; int sctlr_b; MemOp be_data; #if !defined(CONFIG_USER_ONLY) @@ -59,12 +58,13 @@ typedef struct DisasContext { * so that top level loop can generate correct syndrome information. */ uint32_t svc_imm; - int aarch64; int current_el; /* Debug target exception level for single-step exceptions */ int debug_target_el; GHashTable *cp_regs; uint64_t features; /* CPU features bits */ + bool aarch64; + bool thumb; /* Because unallocated encodings generate different exception syndrome * information from traps due to FP being disabled, we can't do a single * "is fp access disabled" check at a high level in the decode tree. @@ -332,16 +332,9 @@ static inline void gen_ss_advance(DisasContext *s) static inline void gen_exception(int excp, uint32_t syndrome, uint32_t target_el) { - TCGv_i32 tcg_excp = tcg_const_i32(excp); - TCGv_i32 tcg_syn = tcg_const_i32(syndrome); - TCGv_i32 tcg_el = tcg_const_i32(target_el); - - gen_helper_exception_with_syndrome(cpu_env, tcg_excp, - tcg_syn, tcg_el); - - tcg_temp_free_i32(tcg_el); - tcg_temp_free_i32(tcg_syn); - tcg_temp_free_i32(tcg_excp); + gen_helper_exception_with_syndrome(cpu_env, tcg_constant_i32(excp), + tcg_constant_i32(syndrome), + tcg_constant_i32(target_el)); } /* Generate an architectural singlestep exception */ diff --git a/target/i386/tcg/fpu_helper.c b/target/i386/tcg/fpu_helper.c index ebf5e73df9..30bc44fcf8 100644 --- a/target/i386/tcg/fpu_helper.c +++ b/target/i386/tcg/fpu_helper.c @@ -2466,7 +2466,7 @@ static void do_fsave(CPUX86State *env, target_ulong ptr, int data32, do_fstenv(env, ptr, data32, retaddr); - ptr += (14 << data32); + ptr += (target_ulong)14 << data32; for (i = 0; i < 8; i++) { tmp = ST(i); do_fstt(env, tmp, ptr, retaddr); @@ -2488,7 +2488,7 @@ static void do_frstor(CPUX86State *env, target_ulong ptr, int data32, int i; do_fldenv(env, ptr, data32, retaddr); - ptr += (14 << data32); + ptr += (target_ulong)14 << data32; for (i = 0; i < 8; i++) { tmp = do_fldt(env, ptr, retaddr); diff --git a/target/mips/TODO b/target/mips/TODO deleted file mode 100644 index 1d782d8027..0000000000 --- a/target/mips/TODO +++ /dev/null @@ -1,51 +0,0 @@ -Unsolved issues/bugs in the mips/mipsel backend ------------------------------------------------ - -General -------- -- Unimplemented ASEs: - - MDMX - - SmartMIPS - - microMIPS DSP r1 & r2 encodings -- MT ASE only partially implemented and not functional -- Shadow register support only partially implemented, - lacks set switching on interrupt/exception. -- 34K ITC not implemented. -- A general lack of documentation, especially for technical internals. - Existing documentation is x86-centric. -- Reverse endianness bit not implemented -- The TLB emulation is very inefficient: - QEMU's softmmu implements a x86-style MMU, with separate entries - for read/write/execute, a TLB index which is just a modulo of the - virtual address, and a set of TLBs for each user/kernel/supervisor - MMU mode. - MIPS has a single entry for read/write/execute and only one MMU mode. - But it is fully associative with randomized entry indices, and uses - up to 256 ASID tags as additional matching criterion (which roughly - equates to 256 MMU modes). It also has a global flag which causes - entries to match regardless of ASID. - To cope with these differences, QEMU currently flushes the TLB at - each ASID change. Using the MMU modes to implement ASIDs hinges on - implementing the global bit efficiently. -- save/restore of the CPU state is not implemented (see machine.c). - -MIPS64 ------- -- Userland emulation (both n32 and n64) not functional. - -"Generic" 4Kc system emulation ------------------------------- -- Doesn't correspond to any real hardware. Should be removed some day, - U-Boot is the last remaining user. - -PICA 61 system emulation ------------------------- -- No framebuffer support yet. - -MALTA system emulation ----------------------- -- We fake firmware support instead of doing the real thing -- Real firmware (YAMON) falls over when trying to init RAM, presumably - due to lacking system controller emulation. -- Bonito system controller not implemented -- MSC1 system controller not implemented diff --git a/target/nios2/cpu.c b/target/nios2/cpu.c index b0877cb39e..19b2409974 100644 --- a/target/nios2/cpu.c +++ b/target/nios2/cpu.c @@ -31,12 +31,12 @@ static void nios2_cpu_set_pc(CPUState *cs, vaddr value) Nios2CPU *cpu = NIOS2_CPU(cs); CPUNios2State *env = &cpu->env; - env->regs[R_PC] = value; + env->pc = value; } static bool nios2_cpu_has_work(CPUState *cs) { - return cs->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_NMI); + return cs->interrupt_request & CPU_INTERRUPT_HARD; } static void nios2_cpu_reset(DeviceState *dev) @@ -48,27 +48,42 @@ static void nios2_cpu_reset(DeviceState *dev) ncc->parent_reset(dev); - memset(env->regs, 0, sizeof(uint32_t) * NUM_CORE_REGS); - env->regs[R_PC] = cpu->reset_addr; + memset(env->ctrl, 0, sizeof(env->ctrl)); + env->pc = cpu->reset_addr; #if defined(CONFIG_USER_ONLY) /* Start in user mode with interrupts enabled. */ - env->regs[CR_STATUS] = CR_STATUS_U | CR_STATUS_PIE; + env->ctrl[CR_STATUS] = CR_STATUS_RSIE | CR_STATUS_U | CR_STATUS_PIE; + memset(env->regs, 0, sizeof(env->regs)); #else - env->regs[CR_STATUS] = 0; + env->ctrl[CR_STATUS] = CR_STATUS_RSIE; + nios2_update_crs(env); + memset(env->shadow_regs, 0, sizeof(env->shadow_regs)); #endif } #ifndef CONFIG_USER_ONLY -static void nios2_cpu_set_irq(void *opaque, int irq, int level) +static void eic_set_irq(void *opaque, int irq, int level) +{ + Nios2CPU *cpu = opaque; + CPUState *cs = CPU(cpu); + + if (level) { + cpu_interrupt(cs, CPU_INTERRUPT_HARD); + } else { + cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); + } +} + +static void iic_set_irq(void *opaque, int irq, int level) { Nios2CPU *cpu = opaque; CPUNios2State *env = &cpu->env; CPUState *cs = CPU(cpu); - env->regs[CR_IPENDING] = deposit32(env->regs[CR_IPENDING], irq, 1, !!level); + env->ctrl[CR_IPENDING] = deposit32(env->ctrl[CR_IPENDING], irq, 1, !!level); - if (env->regs[CR_IPENDING]) { + if (env->ctrl[CR_IPENDING]) { cpu_interrupt(cs, CPU_INTERRUPT_HARD); } else { cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); @@ -84,15 +99,6 @@ static void nios2_cpu_initfn(Object *obj) #if !defined(CONFIG_USER_ONLY) mmu_init(&cpu->env); - - /* - * These interrupt lines model the IIC (internal interrupt - * controller). QEMU does not currently support the EIC - * (external interrupt controller) -- if we did it would be - * a separate device in hw/intc with a custom interface to - * the CPU, and boards using it would not wire up these IRQ lines. - */ - qdev_init_gpio_in_named(DEVICE(cpu), nios2_cpu_set_irq, "IRQ", 32); #endif } @@ -101,37 +107,148 @@ static ObjectClass *nios2_cpu_class_by_name(const char *cpu_model) return object_class_by_name(TYPE_NIOS2_CPU); } +static void realize_cr_status(CPUState *cs) +{ + Nios2CPU *cpu = NIOS2_CPU(cs); + + /* Begin with all fields of all registers are reserved. */ + memset(cpu->cr_state, 0, sizeof(cpu->cr_state)); + + /* + * The combination of writable and readonly is the set of all + * non-reserved fields. We apply writable as a mask to bits, + * and merge in existing readonly bits, before storing. + */ +#define WR_REG(C) cpu->cr_state[C].writable = -1 +#define RO_REG(C) cpu->cr_state[C].readonly = -1 +#define WR_FIELD(C, F) cpu->cr_state[C].writable |= R_##C##_##F##_MASK +#define RO_FIELD(C, F) cpu->cr_state[C].readonly |= R_##C##_##F##_MASK + + WR_FIELD(CR_STATUS, PIE); + WR_REG(CR_ESTATUS); + WR_REG(CR_BSTATUS); + RO_REG(CR_CPUID); + RO_REG(CR_EXCEPTION); + WR_REG(CR_BADADDR); + + if (cpu->eic_present) { + WR_FIELD(CR_STATUS, RSIE); + RO_FIELD(CR_STATUS, NMI); + WR_FIELD(CR_STATUS, PRS); + RO_FIELD(CR_STATUS, CRS); + WR_FIELD(CR_STATUS, IL); + WR_FIELD(CR_STATUS, IH); + } else { + RO_FIELD(CR_STATUS, RSIE); + WR_REG(CR_IENABLE); + RO_REG(CR_IPENDING); + } + + if (cpu->mmu_present) { + WR_FIELD(CR_STATUS, U); + WR_FIELD(CR_STATUS, EH); + + WR_FIELD(CR_PTEADDR, VPN); + WR_FIELD(CR_PTEADDR, PTBASE); + + RO_FIELD(CR_TLBMISC, D); + RO_FIELD(CR_TLBMISC, PERM); + RO_FIELD(CR_TLBMISC, BAD); + RO_FIELD(CR_TLBMISC, DBL); + WR_FIELD(CR_TLBMISC, PID); + WR_FIELD(CR_TLBMISC, WE); + WR_FIELD(CR_TLBMISC, RD); + WR_FIELD(CR_TLBMISC, WAY); + + WR_REG(CR_TLBACC); + } + + /* + * TODO: ECC (config, eccinj) and MPU (config, mpubase, mpuacc) are + * unimplemented, so their corresponding control regs remain reserved. + */ + +#undef WR_REG +#undef RO_REG +#undef WR_FIELD +#undef RO_FIELD +} + static void nios2_cpu_realizefn(DeviceState *dev, Error **errp) { CPUState *cs = CPU(dev); + Nios2CPU *cpu = NIOS2_CPU(cs); Nios2CPUClass *ncc = NIOS2_CPU_GET_CLASS(dev); Error *local_err = NULL; +#ifndef CONFIG_USER_ONLY + if (cpu->eic_present) { + qdev_init_gpio_in_named(DEVICE(cpu), eic_set_irq, "EIC", 1); + } else { + qdev_init_gpio_in_named(DEVICE(cpu), iic_set_irq, "IRQ", 32); + } +#endif + cpu_exec_realizefn(cs, &local_err); if (local_err != NULL) { error_propagate(errp, local_err); return; } + realize_cr_status(cs); qemu_init_vcpu(cs); cpu_reset(cs); + /* We have reserved storage for cpuid; might as well use it. */ + cpu->env.ctrl[CR_CPUID] = cs->cpu_index; + ncc->parent_realize(dev, errp); } #ifndef CONFIG_USER_ONLY -static bool nios2_cpu_exec_interrupt(CPUState *cs, int interrupt_request) +static bool eic_take_interrupt(Nios2CPU *cpu) { - Nios2CPU *cpu = NIOS2_CPU(cs); CPUNios2State *env = &cpu->env; + const uint32_t status = env->ctrl[CR_STATUS]; - if ((interrupt_request & CPU_INTERRUPT_HARD) && - (env->regs[CR_STATUS] & CR_STATUS_PIE) && - (env->regs[CR_IPENDING] & env->regs[CR_IENABLE])) { - cs->exception_index = EXCP_IRQ; - nios2_cpu_do_interrupt(cs); + if (cpu->rnmi) { + return !(status & CR_STATUS_NMI); + } + if (!(status & CR_STATUS_PIE)) { + return false; + } + if (cpu->ril <= FIELD_EX32(status, CR_STATUS, IL)) { + return false; + } + if (cpu->rrs != FIELD_EX32(status, CR_STATUS, CRS)) { return true; } + return status & CR_STATUS_RSIE; +} + +static bool iic_take_interrupt(Nios2CPU *cpu) +{ + CPUNios2State *env = &cpu->env; + + if (!(env->ctrl[CR_STATUS] & CR_STATUS_PIE)) { + return false; + } + return env->ctrl[CR_IPENDING] & env->ctrl[CR_IENABLE]; +} + +static bool nios2_cpu_exec_interrupt(CPUState *cs, int interrupt_request) +{ + Nios2CPU *cpu = NIOS2_CPU(cs); + + if (interrupt_request & CPU_INTERRUPT_HARD) { + if (cpu->eic_present + ? eic_take_interrupt(cpu) + : iic_take_interrupt(cpu)) { + cs->exception_index = EXCP_IRQ; + nios2_cpu_do_interrupt(cs); + return true; + } + } return false; } #endif /* !CONFIG_USER_ONLY */ @@ -146,23 +263,26 @@ static void nios2_cpu_disas_set_info(CPUState *cpu, disassemble_info *info) static int nios2_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { Nios2CPU *cpu = NIOS2_CPU(cs); - CPUClass *cc = CPU_GET_CLASS(cs); CPUNios2State *env = &cpu->env; - - if (n > cc->gdb_num_core_regs) { - return 0; - } + uint32_t val; if (n < 32) { /* GP regs */ - return gdb_get_reg32(mem_buf, env->regs[n]); + val = env->regs[n]; } else if (n == 32) { /* PC */ - return gdb_get_reg32(mem_buf, env->regs[R_PC]); + val = env->pc; } else if (n < 49) { /* Status regs */ - return gdb_get_reg32(mem_buf, env->regs[n - 1]); + unsigned cr = n - 33; + if (nios2_cr_reserved(&cpu->cr_state[cr])) { + val = 0; + } else { + val = env->ctrl[n - 33]; + } + } else { + /* Invalid regs */ + return 0; } - /* Invalid regs */ - return 0; + return gdb_get_reg32(mem_buf, val); } static int nios2_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) @@ -170,23 +290,32 @@ static int nios2_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) Nios2CPU *cpu = NIOS2_CPU(cs); CPUClass *cc = CPU_GET_CLASS(cs); CPUNios2State *env = &cpu->env; + uint32_t val; if (n > cc->gdb_num_core_regs) { return 0; } + val = ldl_p(mem_buf); if (n < 32) { /* GP regs */ - env->regs[n] = ldl_p(mem_buf); + env->regs[n] = val; } else if (n == 32) { /* PC */ - env->regs[R_PC] = ldl_p(mem_buf); + env->pc = val; } else if (n < 49) { /* Status regs */ - env->regs[n - 1] = ldl_p(mem_buf); + unsigned cr = n - 33; + /* ??? Maybe allow the debugger to write to readonly fields. */ + val &= cpu->cr_state[cr].writable; + val |= cpu->cr_state[cr].readonly & env->ctrl[cr]; + env->ctrl[cr] = val; + } else { + g_assert_not_reached(); } return 4; } static Property nios2_properties[] = { + DEFINE_PROP_BOOL("diverr_present", Nios2CPU, diverr_present, true), DEFINE_PROP_BOOL("mmu_present", Nios2CPU, mmu_present, true), /* ALTR,pid-num-bits */ DEFINE_PROP_UINT32("mmu_pid_num_bits", Nios2CPU, pid_num_bits, 8), @@ -210,9 +339,7 @@ static const struct SysemuCPUOps nios2_sysemu_ops = { static const struct TCGCPUOps nios2_tcg_ops = { .initialize = nios2_tcg_init, -#ifdef CONFIG_USER_ONLY - .record_sigsegv = nios2_cpu_record_sigsegv, -#else +#ifndef CONFIG_USER_ONLY .tlb_fill = nios2_cpu_tlb_fill, .cpu_exec_interrupt = nios2_cpu_exec_interrupt, .do_interrupt = nios2_cpu_do_interrupt, diff --git a/target/nios2/cpu.h b/target/nios2/cpu.h index 1bab805bb0..f85581ee56 100644 --- a/target/nios2/cpu.h +++ b/target/nios2/cpu.h @@ -23,6 +23,7 @@ #include "exec/cpu-defs.h" #include "hw/core/cpu.h" +#include "hw/registerfields.h" #include "qom/object.h" typedef struct CPUArchState CPUNios2State; @@ -56,83 +57,114 @@ struct Nios2CPUClass { #define EXCEPTION_ADDRESS 0x00000004 #define FAST_TLB_MISS_ADDRESS 0x00000008 +#define NUM_GP_REGS 32 +#define NUM_CR_REGS 32 -/* GP regs + CR regs + PC */ -#define NUM_CORE_REGS (32 + 32 + 1) +#ifndef CONFIG_USER_ONLY +/* 63 shadow register sets; index 0 is the primary register set. */ +#define NUM_REG_SETS 64 +#endif /* General purpose register aliases */ -#define R_ZERO 0 -#define R_AT 1 -#define R_RET0 2 -#define R_RET1 3 -#define R_ARG0 4 -#define R_ARG1 5 -#define R_ARG2 6 -#define R_ARG3 7 -#define R_ET 24 -#define R_BT 25 -#define R_GP 26 -#define R_SP 27 -#define R_FP 28 -#define R_EA 29 -#define R_BA 30 -#define R_RA 31 +enum { + R_ZERO = 0, + R_AT = 1, + R_RET0 = 2, + R_RET1 = 3, + R_ARG0 = 4, + R_ARG1 = 5, + R_ARG2 = 6, + R_ARG3 = 7, + R_ET = 24, + R_BT = 25, + R_GP = 26, + R_SP = 27, + R_FP = 28, + R_EA = 29, + R_BA = 30, + R_SSTATUS = 30, + R_RA = 31, +}; /* Control register aliases */ -#define CR_BASE 32 -#define CR_STATUS (CR_BASE + 0) -#define CR_STATUS_PIE (1 << 0) -#define CR_STATUS_U (1 << 1) -#define CR_STATUS_EH (1 << 2) -#define CR_STATUS_IH (1 << 3) -#define CR_STATUS_IL (63 << 4) -#define CR_STATUS_CRS (63 << 10) -#define CR_STATUS_PRS (63 << 16) -#define CR_STATUS_NMI (1 << 22) -#define CR_STATUS_RSIE (1 << 23) -#define CR_ESTATUS (CR_BASE + 1) -#define CR_BSTATUS (CR_BASE + 2) -#define CR_IENABLE (CR_BASE + 3) -#define CR_IPENDING (CR_BASE + 4) -#define CR_CPUID (CR_BASE + 5) -#define CR_CTL6 (CR_BASE + 6) -#define CR_EXCEPTION (CR_BASE + 7) -#define CR_PTEADDR (CR_BASE + 8) -#define CR_PTEADDR_PTBASE_SHIFT 22 -#define CR_PTEADDR_PTBASE_MASK (0x3FF << CR_PTEADDR_PTBASE_SHIFT) -#define CR_PTEADDR_VPN_SHIFT 2 -#define CR_PTEADDR_VPN_MASK (0xFFFFF << CR_PTEADDR_VPN_SHIFT) -#define CR_TLBACC (CR_BASE + 9) -#define CR_TLBACC_IGN_SHIFT 25 -#define CR_TLBACC_IGN_MASK (0x7F << CR_TLBACC_IGN_SHIFT) -#define CR_TLBACC_C (1 << 24) -#define CR_TLBACC_R (1 << 23) -#define CR_TLBACC_W (1 << 22) -#define CR_TLBACC_X (1 << 21) -#define CR_TLBACC_G (1 << 20) -#define CR_TLBACC_PFN_MASK 0x000FFFFF -#define CR_TLBMISC (CR_BASE + 10) -#define CR_TLBMISC_WAY_SHIFT 20 -#define CR_TLBMISC_WAY_MASK (0xF << CR_TLBMISC_WAY_SHIFT) -#define CR_TLBMISC_RD (1 << 19) -#define CR_TLBMISC_WR (1 << 18) -#define CR_TLBMISC_PID_SHIFT 4 -#define CR_TLBMISC_PID_MASK (0x3FFF << CR_TLBMISC_PID_SHIFT) -#define CR_TLBMISC_DBL (1 << 3) -#define CR_TLBMISC_BAD (1 << 2) -#define CR_TLBMISC_PERM (1 << 1) -#define CR_TLBMISC_D (1 << 0) -#define CR_ENCINJ (CR_BASE + 11) -#define CR_BADADDR (CR_BASE + 12) -#define CR_CONFIG (CR_BASE + 13) -#define CR_MPUBASE (CR_BASE + 14) -#define CR_MPUACC (CR_BASE + 15) - -/* Other registers */ -#define R_PC 64 +enum { + CR_STATUS = 0, + CR_ESTATUS = 1, + CR_BSTATUS = 2, + CR_IENABLE = 3, + CR_IPENDING = 4, + CR_CPUID = 5, + CR_EXCEPTION = 7, + CR_PTEADDR = 8, + CR_TLBACC = 9, + CR_TLBMISC = 10, + CR_ENCINJ = 11, + CR_BADADDR = 12, + CR_CONFIG = 13, + CR_MPUBASE = 14, + CR_MPUACC = 15, +}; + +FIELD(CR_STATUS, PIE, 0, 1) +FIELD(CR_STATUS, U, 1, 1) +FIELD(CR_STATUS, EH, 2, 1) +FIELD(CR_STATUS, IH, 3, 1) +FIELD(CR_STATUS, IL, 4, 6) +FIELD(CR_STATUS, CRS, 10, 6) +FIELD(CR_STATUS, PRS, 16, 6) +FIELD(CR_STATUS, NMI, 22, 1) +FIELD(CR_STATUS, RSIE, 23, 1) +FIELD(CR_STATUS, SRS, 31, 1) /* only in sstatus */ + +#define CR_STATUS_PIE R_CR_STATUS_PIE_MASK +#define CR_STATUS_U R_CR_STATUS_U_MASK +#define CR_STATUS_EH R_CR_STATUS_EH_MASK +#define CR_STATUS_IH R_CR_STATUS_IH_MASK +#define CR_STATUS_NMI R_CR_STATUS_NMI_MASK +#define CR_STATUS_RSIE R_CR_STATUS_RSIE_MASK +#define CR_STATUS_SRS R_CR_STATUS_SRS_MASK + +FIELD(CR_EXCEPTION, CAUSE, 2, 5) +FIELD(CR_EXCEPTION, ECCFTL, 31, 1) + +FIELD(CR_PTEADDR, VPN, 2, 20) +FIELD(CR_PTEADDR, PTBASE, 22, 10) + +FIELD(CR_TLBACC, PFN, 0, 20) +FIELD(CR_TLBACC, G, 20, 1) +FIELD(CR_TLBACC, X, 21, 1) +FIELD(CR_TLBACC, W, 22, 1) +FIELD(CR_TLBACC, R, 23, 1) +FIELD(CR_TLBACC, C, 24, 1) +FIELD(CR_TLBACC, IG, 25, 7) + +#define CR_TLBACC_C R_CR_TLBACC_C_MASK +#define CR_TLBACC_R R_CR_TLBACC_R_MASK +#define CR_TLBACC_W R_CR_TLBACC_W_MASK +#define CR_TLBACC_X R_CR_TLBACC_X_MASK +#define CR_TLBACC_G R_CR_TLBACC_G_MASK + +FIELD(CR_TLBMISC, D, 0, 1) +FIELD(CR_TLBMISC, PERM, 1, 1) +FIELD(CR_TLBMISC, BAD, 2, 1) +FIELD(CR_TLBMISC, DBL, 3, 1) +FIELD(CR_TLBMISC, PID, 4, 14) +FIELD(CR_TLBMISC, WE, 18, 1) +FIELD(CR_TLBMISC, RD, 19, 1) +FIELD(CR_TLBMISC, WAY, 20, 4) +FIELD(CR_TLBMISC, EE, 24, 1) + +#define CR_TLBMISC_EE R_CR_TLBMISC_EE_MASK +#define CR_TLBMISC_RD R_CR_TLBMISC_RD_MASK +#define CR_TLBMISC_WE R_CR_TLBMISC_WE_MASK +#define CR_TLBMISC_DBL R_CR_TLBMISC_DBL_MASK +#define CR_TLBMISC_BAD R_CR_TLBMISC_BAD_MASK +#define CR_TLBMISC_PERM R_CR_TLBMISC_PERM_MASK +#define CR_TLBMISC_D R_CR_TLBMISC_D_MASK /* Exceptions */ #define EXCP_BREAK 0x1000 +#define EXCP_SEMIHOST 0x1001 #define EXCP_RESET 0 #define EXCP_PRESET 1 #define EXCP_IRQ 2 @@ -142,20 +174,27 @@ struct Nios2CPUClass { #define EXCP_UNALIGN 6 #define EXCP_UNALIGND 7 #define EXCP_DIV 8 -#define EXCP_SUPERA 9 +#define EXCP_SUPERA_X 9 #define EXCP_SUPERI 10 -#define EXCP_SUPERD 11 -#define EXCP_TLBD 12 -#define EXCP_TLBX 13 -#define EXCP_TLBR 14 -#define EXCP_TLBW 15 +#define EXCP_SUPERA_D 11 +#define EXCP_TLB_X 12 +#define EXCP_TLB_D (0x1000 | EXCP_TLB_X) +#define EXCP_PERM_X 13 +#define EXCP_PERM_R 14 +#define EXCP_PERM_W 15 #define EXCP_MPUI 16 #define EXCP_MPUD 17 -#define CPU_INTERRUPT_NMI CPU_INTERRUPT_TGT_EXT_3 - struct CPUArchState { - uint32_t regs[NUM_CORE_REGS]; +#ifdef CONFIG_USER_ONLY + uint32_t regs[NUM_GP_REGS]; +#else + uint32_t shadow_regs[NUM_REG_SETS][NUM_GP_REGS]; + /* Pointer into shadow_regs for the current register set. */ + uint32_t *regs; +#endif + uint32_t ctrl[NUM_CR_REGS]; + uint32_t pc; #if !defined(CONFIG_USER_ONLY) Nios2MMU mmu; @@ -163,6 +202,11 @@ struct CPUArchState { int error_code; }; +typedef struct { + uint32_t writable; + uint32_t readonly; +} ControlRegState; + /** * Nios2CPU: * @env: #CPUNios2State @@ -177,7 +221,10 @@ struct ArchCPU { CPUNegativeOffsetState neg; CPUNios2State env; + bool diverr_present; bool mmu_present; + bool eic_present; + uint32_t pid_num_bits; uint32_t tlb_num_ways; uint32_t tlb_num_entries; @@ -186,9 +233,31 @@ struct ArchCPU { uint32_t reset_addr; uint32_t exception_addr; uint32_t fast_tlb_miss_addr; + + /* Bits within each control register which are reserved or readonly. */ + ControlRegState cr_state[NUM_CR_REGS]; + + /* External Interrupt Controller Interface */ + uint32_t rha; /* Requested handler address */ + uint32_t ril; /* Requested interrupt level */ + uint32_t rrs; /* Requested register set */ + bool rnmi; /* Requested nonmaskable interrupt */ }; +static inline bool nios2_cr_reserved(const ControlRegState *s) +{ + return (s->writable | s->readonly) == 0; +} + +static inline void nios2_update_crs(CPUNios2State *env) +{ +#ifndef CONFIG_USER_ONLY + unsigned crs = FIELD_EX32(env->ctrl[CR_STATUS], CR_STATUS, CRS); + env->regs = env->shadow_regs[crs]; +#endif +} + void nios2_tcg_init(void); void nios2_cpu_do_interrupt(CPUState *cs); void dump_mmu(CPUNios2State *env); @@ -197,6 +266,8 @@ hwaddr nios2_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); G_NORETURN void nios2_cpu_do_unaligned_access(CPUState *cpu, vaddr addr, MMUAccessType access_type, int mmu_idx, uintptr_t retaddr); +G_NORETURN void nios2_cpu_loop_exit_advance(CPUNios2State *env, + uintptr_t retaddr); void do_nios2_semihosting(CPUNios2State *env); @@ -212,36 +283,35 @@ void do_nios2_semihosting(CPUNios2State *env); static inline int cpu_mmu_index(CPUNios2State *env, bool ifetch) { - return (env->regs[CR_STATUS] & CR_STATUS_U) ? MMU_USER_IDX : + return (env->ctrl[CR_STATUS] & CR_STATUS_U) ? MMU_USER_IDX : MMU_SUPERVISOR_IDX; } -#ifdef CONFIG_USER_ONLY -void nios2_cpu_record_sigsegv(CPUState *cpu, vaddr addr, - MMUAccessType access_type, - bool maperr, uintptr_t ra); -#else +#ifndef CONFIG_USER_ONLY bool nios2_cpu_tlb_fill(CPUState *cs, vaddr address, int size, MMUAccessType access_type, int mmu_idx, bool probe, uintptr_t retaddr); #endif -static inline int cpu_interrupts_enabled(CPUNios2State *env) -{ - return env->regs[CR_STATUS] & CR_STATUS_PIE; -} - typedef CPUNios2State CPUArchState; typedef Nios2CPU ArchCPU; #include "exec/cpu-all.h" +FIELD(TBFLAGS, CRS0, 0, 1) /* Set if CRS == 0. */ +FIELD(TBFLAGS, U, 1, 1) /* Overlaps CR_STATUS_U */ +FIELD(TBFLAGS, R0_0, 2, 1) /* Set if R0 == 0. */ + static inline void cpu_get_tb_cpu_state(CPUNios2State *env, target_ulong *pc, target_ulong *cs_base, uint32_t *flags) { - *pc = env->regs[R_PC]; + unsigned crs = FIELD_EX32(env->ctrl[CR_STATUS], CR_STATUS, CRS); + + *pc = env->pc; *cs_base = 0; - *flags = (env->regs[CR_STATUS] & (CR_STATUS_EH | CR_STATUS_U)); + *flags = (env->ctrl[CR_STATUS] & CR_STATUS_U) + | (crs ? 0 : R_TBFLAGS_CRS0_MASK) + | (env->regs[0] ? 0 : R_TBFLAGS_R0_0_MASK); } #endif /* NIOS2_CPU_H */ diff --git a/target/nios2/helper.c b/target/nios2/helper.c index e5c98650e1..bb3b09e5a7 100644 --- a/target/nios2/helper.c +++ b/target/nios2/helper.c @@ -28,176 +28,234 @@ #include "exec/helper-proto.h" #include "semihosting/semihost.h" -#if defined(CONFIG_USER_ONLY) -void nios2_cpu_do_interrupt(CPUState *cs) +static void do_exception(Nios2CPU *cpu, uint32_t exception_addr, + uint32_t tlbmisc_set, bool is_break) { - Nios2CPU *cpu = NIOS2_CPU(cs); CPUNios2State *env = &cpu->env; - cs->exception_index = -1; - env->regs[R_EA] = env->regs[R_PC] + 4; -} + CPUState *cs = CPU(cpu); + uint32_t old_status = env->ctrl[CR_STATUS]; + uint32_t new_status = old_status; -void nios2_cpu_record_sigsegv(CPUState *cs, vaddr addr, - MMUAccessType access_type, - bool maperr, uintptr_t retaddr) -{ - /* FIXME: Disentangle kuser page from linux-user sigsegv handling. */ - cs->exception_index = 0xaa; - cpu_loop_exit_restore(cs, retaddr); -} + /* With shadow regs, exceptions are always taken into CRS 0. */ + new_status &= ~R_CR_STATUS_CRS_MASK; + env->regs = env->shadow_regs[0]; -#else /* !CONFIG_USER_ONLY */ + if ((old_status & CR_STATUS_EH) == 0) { + int r_ea = R_EA, cr_es = CR_ESTATUS; -void nios2_cpu_do_interrupt(CPUState *cs) -{ - Nios2CPU *cpu = NIOS2_CPU(cs); - CPUNios2State *env = &cpu->env; + if (is_break) { + r_ea = R_BA; + cr_es = CR_BSTATUS; + } + env->ctrl[cr_es] = old_status; + env->regs[r_ea] = env->pc; + + if (cpu->mmu_present) { + new_status |= CR_STATUS_EH; + + /* + * There are 4 bits that are always written. + * Explicitly clear them, to be set via the argument. + */ + env->ctrl[CR_TLBMISC] &= ~(CR_TLBMISC_D | + CR_TLBMISC_PERM | + CR_TLBMISC_BAD | + CR_TLBMISC_DBL); + env->ctrl[CR_TLBMISC] |= tlbmisc_set; + } - switch (cs->exception_index) { - case EXCP_IRQ: - assert(env->regs[CR_STATUS] & CR_STATUS_PIE); + /* + * With shadow regs, and EH == 0, PRS is set from CRS. + * At least, so says Table 3-9, and some other text, + * though Table 3-38 says otherwise. + */ + new_status = FIELD_DP32(new_status, CR_STATUS, PRS, + FIELD_EX32(old_status, CR_STATUS, CRS)); + } - qemu_log_mask(CPU_LOG_INT, "interrupt at pc=%x\n", env->regs[R_PC]); + new_status &= ~(CR_STATUS_PIE | CR_STATUS_U); - env->regs[CR_ESTATUS] = env->regs[CR_STATUS]; - env->regs[CR_STATUS] |= CR_STATUS_IH; - env->regs[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U); + env->ctrl[CR_STATUS] = new_status; + if (!is_break) { + env->ctrl[CR_EXCEPTION] = FIELD_DP32(0, CR_EXCEPTION, CAUSE, + cs->exception_index); + } + env->pc = exception_addr; +} - env->regs[CR_EXCEPTION] &= ~(0x1F << 2); - env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2; +static void do_iic_irq(Nios2CPU *cpu) +{ + do_exception(cpu, cpu->exception_addr, 0, false); +} - env->regs[R_EA] = env->regs[R_PC] + 4; - env->regs[R_PC] = cpu->exception_addr; - break; +static void do_eic_irq(Nios2CPU *cpu) +{ + CPUNios2State *env = &cpu->env; + uint32_t old_status = env->ctrl[CR_STATUS]; + uint32_t new_status = old_status; + uint32_t old_rs = FIELD_EX32(old_status, CR_STATUS, CRS); + uint32_t new_rs = cpu->rrs; + + new_status = FIELD_DP32(new_status, CR_STATUS, CRS, new_rs); + new_status = FIELD_DP32(new_status, CR_STATUS, IL, cpu->ril); + new_status = FIELD_DP32(new_status, CR_STATUS, NMI, cpu->rnmi); + new_status &= ~(CR_STATUS_RSIE | CR_STATUS_U); + new_status |= CR_STATUS_IH; + + if (!(new_status & CR_STATUS_EH)) { + new_status = FIELD_DP32(new_status, CR_STATUS, PRS, old_rs); + if (new_rs == 0) { + env->ctrl[CR_ESTATUS] = old_status; + } else { + if (new_rs != old_rs) { + old_status |= CR_STATUS_SRS; + } + env->shadow_regs[new_rs][R_SSTATUS] = old_status; + } + env->shadow_regs[new_rs][R_EA] = env->pc; + } - case EXCP_TLBD: - if ((env->regs[CR_STATUS] & CR_STATUS_EH) == 0) { - qemu_log_mask(CPU_LOG_INT, "TLB MISS (fast) at pc=%x\n", - env->regs[R_PC]); + env->ctrl[CR_STATUS] = new_status; + nios2_update_crs(env); - /* Fast TLB miss */ - /* Variation from the spec. Table 3-35 of the cpu reference shows - * estatus not being changed for TLB miss but this appears to - * be incorrect. */ - env->regs[CR_ESTATUS] = env->regs[CR_STATUS]; - env->regs[CR_STATUS] |= CR_STATUS_EH; - env->regs[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U); + env->pc = cpu->rha; +} - env->regs[CR_EXCEPTION] &= ~(0x1F << 2); - env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2; +void nios2_cpu_do_interrupt(CPUState *cs) +{ + Nios2CPU *cpu = NIOS2_CPU(cs); + CPUNios2State *env = &cpu->env; + uint32_t tlbmisc_set = 0; - env->regs[CR_TLBMISC] &= ~CR_TLBMISC_DBL; - env->regs[CR_TLBMISC] |= CR_TLBMISC_WR; + if (qemu_loglevel_mask(CPU_LOG_INT)) { + const char *name = NULL; - env->regs[R_EA] = env->regs[R_PC] + 4; - env->regs[R_PC] = cpu->fast_tlb_miss_addr; + switch (cs->exception_index) { + case EXCP_IRQ: + name = "interrupt"; + break; + case EXCP_TLB_X: + case EXCP_TLB_D: + if (env->ctrl[CR_STATUS] & CR_STATUS_EH) { + name = "TLB MISS (double)"; + } else { + name = "TLB MISS (fast)"; + } + break; + case EXCP_PERM_R: + case EXCP_PERM_W: + case EXCP_PERM_X: + name = "TLB PERM"; + break; + case EXCP_SUPERA_X: + case EXCP_SUPERA_D: + name = "SUPERVISOR (address)"; + break; + case EXCP_SUPERI: + name = "SUPERVISOR (insn)"; + break; + case EXCP_ILLEGAL: + name = "ILLEGAL insn"; + break; + case EXCP_UNALIGN: + name = "Misaligned (data)"; + break; + case EXCP_UNALIGND: + name = "Misaligned (destination)"; + break; + case EXCP_DIV: + name = "DIV error"; + break; + case EXCP_TRAP: + name = "TRAP insn"; + break; + case EXCP_BREAK: + name = "BREAK insn"; + break; + case EXCP_SEMIHOST: + name = "SEMIHOST insn"; + break; + } + if (name) { + qemu_log("%s at pc=0x%08x\n", name, env->pc); } else { - qemu_log_mask(CPU_LOG_INT, "TLB MISS (double) at pc=%x\n", - env->regs[R_PC]); - - /* Double TLB miss */ - env->regs[CR_STATUS] |= CR_STATUS_EH; - env->regs[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U); - - env->regs[CR_EXCEPTION] &= ~(0x1F << 2); - env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2; - - env->regs[CR_TLBMISC] |= CR_TLBMISC_DBL; + qemu_log("Unknown exception %d at pc=0x%08x\n", + cs->exception_index, env->pc); + } + } - env->regs[R_PC] = cpu->exception_addr; + switch (cs->exception_index) { + case EXCP_IRQ: + /* Note that PC is advanced for interrupts as well. */ + env->pc += 4; + if (cpu->eic_present) { + do_eic_irq(cpu); + } else { + do_iic_irq(cpu); } break; - case EXCP_TLBR: - case EXCP_TLBW: - case EXCP_TLBX: - qemu_log_mask(CPU_LOG_INT, "TLB PERM at pc=%x\n", env->regs[R_PC]); - - env->regs[CR_ESTATUS] = env->regs[CR_STATUS]; - env->regs[CR_STATUS] |= CR_STATUS_EH; - env->regs[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U); - - env->regs[CR_EXCEPTION] &= ~(0x1F << 2); - env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2; - - if ((env->regs[CR_STATUS] & CR_STATUS_EH) == 0) { - env->regs[CR_TLBMISC] |= CR_TLBMISC_WR; + case EXCP_TLB_D: + tlbmisc_set = CR_TLBMISC_D; + /* fall through */ + case EXCP_TLB_X: + if (env->ctrl[CR_STATUS] & CR_STATUS_EH) { + tlbmisc_set |= CR_TLBMISC_DBL; + /* + * Normally, we don't write to tlbmisc unless !EH, + * so do it manually for the double-tlb miss exception. + */ + env->ctrl[CR_TLBMISC] &= ~(CR_TLBMISC_D | + CR_TLBMISC_PERM | + CR_TLBMISC_BAD); + env->ctrl[CR_TLBMISC] |= tlbmisc_set; + do_exception(cpu, cpu->exception_addr, 0, false); + } else { + tlbmisc_set |= CR_TLBMISC_WE; + do_exception(cpu, cpu->fast_tlb_miss_addr, tlbmisc_set, false); } - - env->regs[R_EA] = env->regs[R_PC] + 4; - env->regs[R_PC] = cpu->exception_addr; break; - case EXCP_SUPERA: - case EXCP_SUPERI: - case EXCP_SUPERD: - qemu_log_mask(CPU_LOG_INT, "SUPERVISOR exception at pc=%x\n", - env->regs[R_PC]); - - if ((env->regs[CR_STATUS] & CR_STATUS_EH) == 0) { - env->regs[CR_ESTATUS] = env->regs[CR_STATUS]; - env->regs[R_EA] = env->regs[R_PC] + 4; + case EXCP_PERM_R: + case EXCP_PERM_W: + tlbmisc_set = CR_TLBMISC_D; + /* fall through */ + case EXCP_PERM_X: + tlbmisc_set |= CR_TLBMISC_PERM; + if (!(env->ctrl[CR_STATUS] & CR_STATUS_EH)) { + tlbmisc_set |= CR_TLBMISC_WE; } + do_exception(cpu, cpu->exception_addr, tlbmisc_set, false); + break; - env->regs[CR_STATUS] |= CR_STATUS_EH; - env->regs[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U); - - env->regs[CR_EXCEPTION] &= ~(0x1F << 2); - env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2; - - env->regs[R_PC] = cpu->exception_addr; + case EXCP_SUPERA_D: + case EXCP_UNALIGN: + tlbmisc_set = CR_TLBMISC_D; + /* fall through */ + case EXCP_SUPERA_X: + case EXCP_UNALIGND: + tlbmisc_set |= CR_TLBMISC_BAD; + do_exception(cpu, cpu->exception_addr, tlbmisc_set, false); break; + case EXCP_SUPERI: case EXCP_ILLEGAL: + case EXCP_DIV: case EXCP_TRAP: - qemu_log_mask(CPU_LOG_INT, "TRAP exception at pc=%x\n", - env->regs[R_PC]); - - if ((env->regs[CR_STATUS] & CR_STATUS_EH) == 0) { - env->regs[CR_ESTATUS] = env->regs[CR_STATUS]; - env->regs[R_EA] = env->regs[R_PC] + 4; - } - - env->regs[CR_STATUS] |= CR_STATUS_EH; - env->regs[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U); - - env->regs[CR_EXCEPTION] &= ~(0x1F << 2); - env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2; - - env->regs[R_PC] = cpu->exception_addr; + do_exception(cpu, cpu->exception_addr, 0, false); break; case EXCP_BREAK: - qemu_log_mask(CPU_LOG_INT, "BREAK exception at pc=%x\n", - env->regs[R_PC]); - /* The semihosting instruction is "break 1". */ - if (semihosting_enabled() && - cpu_ldl_code(env, env->regs[R_PC]) == 0x003da07a) { - qemu_log_mask(CPU_LOG_INT, "Entering semihosting\n"); - env->regs[R_PC] += 4; - do_nios2_semihosting(env); - break; - } - - if ((env->regs[CR_STATUS] & CR_STATUS_EH) == 0) { - env->regs[CR_BSTATUS] = env->regs[CR_STATUS]; - env->regs[R_BA] = env->regs[R_PC] + 4; - } - - env->regs[CR_STATUS] |= CR_STATUS_EH; - env->regs[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U); - - env->regs[CR_EXCEPTION] &= ~(0x1F << 2); - env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2; + do_exception(cpu, cpu->exception_addr, 0, true); + break; - env->regs[R_PC] = cpu->exception_addr; + case EXCP_SEMIHOST: + do_nios2_semihosting(env); break; default: - cpu_abort(cs, "unhandled exception type=%d\n", - cs->exception_index); - break; + cpu_abort(cs, "unhandled exception type=%d\n", cs->exception_index); } } @@ -232,9 +290,9 @@ void nios2_cpu_do_unaligned_access(CPUState *cs, vaddr addr, Nios2CPU *cpu = NIOS2_CPU(cs); CPUNios2State *env = &cpu->env; - env->regs[CR_BADADDR] = addr; - env->regs[CR_EXCEPTION] = EXCP_UNALIGN << 2; - helper_raise_exception(env, EXCP_UNALIGN); + env->ctrl[CR_BADADDR] = addr; + cs->exception_index = EXCP_UNALIGN; + nios2_cpu_loop_exit_advance(env, retaddr); } bool nios2_cpu_tlb_fill(CPUState *cs, vaddr address, int size, @@ -243,7 +301,7 @@ bool nios2_cpu_tlb_fill(CPUState *cs, vaddr address, int size, { Nios2CPU *cpu = NIOS2_CPU(cs); CPUNios2State *env = &cpu->env; - unsigned int excp = EXCP_TLBD; + unsigned int excp; target_ulong vaddr, paddr; Nios2MMULookup lu; unsigned int hit; @@ -270,9 +328,10 @@ bool nios2_cpu_tlb_fill(CPUState *cs, vaddr address, int size, if (probe) { return false; } - cs->exception_index = EXCP_SUPERA; - env->regs[CR_BADADDR] = address; - cpu_loop_exit_restore(cs, retaddr); + cs->exception_index = (access_type == MMU_INST_FETCH + ? EXCP_SUPERA_X : EXCP_SUPERA_D); + env->ctrl[CR_BADADDR] = address; + nios2_cpu_loop_exit_advance(env, retaddr); } } @@ -291,25 +350,23 @@ bool nios2_cpu_tlb_fill(CPUState *cs, vaddr address, int size, } /* Permission violation */ - excp = (access_type == MMU_DATA_LOAD ? EXCP_TLBR : - access_type == MMU_DATA_STORE ? EXCP_TLBW : EXCP_TLBX); + excp = (access_type == MMU_DATA_LOAD ? EXCP_PERM_R : + access_type == MMU_DATA_STORE ? EXCP_PERM_W : EXCP_PERM_X); + } else { + excp = (access_type == MMU_INST_FETCH ? EXCP_TLB_X: EXCP_TLB_D); } if (probe) { return false; } - if (access_type == MMU_INST_FETCH) { - env->regs[CR_TLBMISC] &= ~CR_TLBMISC_D; - } else { - env->regs[CR_TLBMISC] |= CR_TLBMISC_D; - } - env->regs[CR_PTEADDR] &= CR_PTEADDR_PTBASE_MASK; - env->regs[CR_PTEADDR] |= (address >> 10) & CR_PTEADDR_VPN_MASK; - env->mmu.pteaddr_wr = env->regs[CR_PTEADDR]; + env->ctrl[CR_TLBMISC] = FIELD_DP32(env->ctrl[CR_TLBMISC], CR_TLBMISC, D, + access_type != MMU_INST_FETCH); + env->ctrl[CR_PTEADDR] = FIELD_DP32(env->ctrl[CR_PTEADDR], CR_PTEADDR, VPN, + address >> TARGET_PAGE_BITS); + env->mmu.pteaddr_wr = env->ctrl[CR_PTEADDR]; cs->exception_index = excp; - env->regs[CR_BADADDR] = address; - cpu_loop_exit_restore(cs, retaddr); + env->ctrl[CR_BADADDR] = address; + nios2_cpu_loop_exit_advance(env, retaddr); } -#endif /* !CONFIG_USER_ONLY */ diff --git a/target/nios2/helper.h b/target/nios2/helper.h index a44ecfdf7a..1648d76ade 100644 --- a/target/nios2/helper.h +++ b/target/nios2/helper.h @@ -19,8 +19,13 @@ */ DEF_HELPER_FLAGS_2(raise_exception, TCG_CALL_NO_WG, noreturn, env, i32) +DEF_HELPER_FLAGS_3(divs, TCG_CALL_NO_WG, s32, env, s32, s32) +DEF_HELPER_FLAGS_3(divu, TCG_CALL_NO_WG, i32, env, i32, i32) #if !defined(CONFIG_USER_ONLY) +DEF_HELPER_3(eret, noreturn, env, i32, i32) +DEF_HELPER_FLAGS_2(rdprs, TCG_CALL_NO_WG, i32, env, i32) +DEF_HELPER_3(wrprs, void, env, i32, i32) DEF_HELPER_2(mmu_write_tlbacc, void, env, i32) DEF_HELPER_2(mmu_write_tlbmisc, void, env, i32) DEF_HELPER_2(mmu_write_pteaddr, void, env, i32) diff --git a/target/nios2/meson.build b/target/nios2/meson.build index 62b384702d..2bd60ba306 100644 --- a/target/nios2/meson.build +++ b/target/nios2/meson.build @@ -1,14 +1,17 @@ nios2_ss = ss.source_set() nios2_ss.add(files( 'cpu.c', - 'helper.c', 'nios2-semi.c', 'op_helper.c', 'translate.c', )) nios2_softmmu_ss = ss.source_set() -nios2_softmmu_ss.add(files('monitor.c', 'mmu.c')) +nios2_softmmu_ss.add(files( + 'helper.c', + 'monitor.c', + 'mmu.c' +)) target_arch += {'nios2': nios2_ss} target_softmmu_arch += {'nios2': nios2_softmmu_ss} diff --git a/target/nios2/mmu.c b/target/nios2/mmu.c index 4daab2a7ab..d9b690b78e 100644 --- a/target/nios2/mmu.c +++ b/target/nios2/mmu.c @@ -33,7 +33,7 @@ unsigned int mmu_translate(CPUNios2State *env, target_ulong vaddr, int rw, int mmu_idx) { Nios2CPU *cpu = env_archcpu(env); - int pid = (env->mmu.tlbmisc_wr & CR_TLBMISC_PID_MASK) >> 4; + int pid = FIELD_EX32(env->mmu.tlbmisc_wr, CR_TLBMISC, PID); int vpn = vaddr >> 12; int way, n_ways = cpu->tlb_num_ways; @@ -49,7 +49,7 @@ unsigned int mmu_translate(CPUNios2State *env, } lu->vaddr = vaddr & TARGET_PAGE_MASK; - lu->paddr = (entry->data & CR_TLBACC_PFN_MASK) << TARGET_PAGE_BITS; + lu->paddr = FIELD_EX32(entry->data, CR_TLBACC, PFN) << TARGET_PAGE_BITS; lu->prot = ((entry->data & CR_TLBACC_R) ? PAGE_READ : 0) | ((entry->data & CR_TLBACC_W) ? PAGE_WRITE : 0) | ((entry->data & CR_TLBACC_X) ? PAGE_EXEC : 0); @@ -86,27 +86,27 @@ void helper_mmu_write_tlbacc(CPUNios2State *env, uint32_t v) CPUState *cs = env_cpu(env); Nios2CPU *cpu = env_archcpu(env); - trace_nios2_mmu_write_tlbacc(v >> CR_TLBACC_IGN_SHIFT, + trace_nios2_mmu_write_tlbacc(FIELD_EX32(v, CR_TLBACC, IG), (v & CR_TLBACC_C) ? 'C' : '.', (v & CR_TLBACC_R) ? 'R' : '.', (v & CR_TLBACC_W) ? 'W' : '.', (v & CR_TLBACC_X) ? 'X' : '.', (v & CR_TLBACC_G) ? 'G' : '.', - v & CR_TLBACC_PFN_MASK); + FIELD_EX32(v, CR_TLBACC, PFN)); /* if tlbmisc.WE == 1 then trigger a TLB write on writes to TLBACC */ - if (env->regs[CR_TLBMISC] & CR_TLBMISC_WR) { - int way = (env->regs[CR_TLBMISC] >> CR_TLBMISC_WAY_SHIFT); - int vpn = (env->mmu.pteaddr_wr & CR_PTEADDR_VPN_MASK) >> 2; - int pid = (env->mmu.tlbmisc_wr & CR_TLBMISC_PID_MASK) >> 4; - int g = (v & CR_TLBACC_G) ? 1 : 0; - int valid = ((vpn & CR_TLBACC_PFN_MASK) < 0xC0000) ? 1 : 0; + if (env->ctrl[CR_TLBMISC] & CR_TLBMISC_WE) { + int way = FIELD_EX32(env->ctrl[CR_TLBMISC], CR_TLBMISC, WAY); + int vpn = FIELD_EX32(env->mmu.pteaddr_wr, CR_PTEADDR, VPN); + int pid = FIELD_EX32(env->mmu.tlbmisc_wr, CR_TLBMISC, PID); + int g = FIELD_EX32(v, CR_TLBACC, G); + int valid = FIELD_EX32(vpn, CR_TLBACC, PFN) < 0xC0000; Nios2TLBEntry *entry = &env->mmu.tlb[(way * cpu->tlb_num_ways) + (vpn & env->mmu.tlb_entry_mask)]; uint32_t newTag = (vpn << 12) | (g << 11) | (valid << 10) | pid; uint32_t newData = v & (CR_TLBACC_C | CR_TLBACC_R | CR_TLBACC_W | - CR_TLBACC_X | CR_TLBACC_PFN_MASK); + CR_TLBACC_X | R_CR_TLBACC_PFN_MASK); if ((entry->tag != newTag) || (entry->data != newData)) { if (entry->tag & (1 << 10)) { @@ -117,10 +117,9 @@ void helper_mmu_write_tlbacc(CPUNios2State *env, uint32_t v) entry->data = newData; } /* Auto-increment tlbmisc.WAY */ - env->regs[CR_TLBMISC] = - (env->regs[CR_TLBMISC] & ~CR_TLBMISC_WAY_MASK) | - (((way + 1) & (cpu->tlb_num_ways - 1)) << - CR_TLBMISC_WAY_SHIFT); + env->ctrl[CR_TLBMISC] = FIELD_DP32(env->ctrl[CR_TLBMISC], + CR_TLBMISC, WAY, + (way + 1) & (cpu->tlb_num_ways - 1)); } /* Writes to TLBACC don't change the read-back value */ @@ -130,40 +129,41 @@ void helper_mmu_write_tlbacc(CPUNios2State *env, uint32_t v) void helper_mmu_write_tlbmisc(CPUNios2State *env, uint32_t v) { Nios2CPU *cpu = env_archcpu(env); + uint32_t new_pid = FIELD_EX32(v, CR_TLBMISC, PID); + uint32_t old_pid = FIELD_EX32(env->mmu.tlbmisc_wr, CR_TLBMISC, PID); + uint32_t way = FIELD_EX32(v, CR_TLBMISC, WAY); - trace_nios2_mmu_write_tlbmisc(v >> CR_TLBMISC_WAY_SHIFT, + trace_nios2_mmu_write_tlbmisc(way, (v & CR_TLBMISC_RD) ? 'R' : '.', - (v & CR_TLBMISC_WR) ? 'W' : '.', + (v & CR_TLBMISC_WE) ? 'W' : '.', (v & CR_TLBMISC_DBL) ? '2' : '.', (v & CR_TLBMISC_BAD) ? 'B' : '.', (v & CR_TLBMISC_PERM) ? 'P' : '.', (v & CR_TLBMISC_D) ? 'D' : '.', - (v & CR_TLBMISC_PID_MASK) >> 4); + new_pid); - if ((v & CR_TLBMISC_PID_MASK) != - (env->mmu.tlbmisc_wr & CR_TLBMISC_PID_MASK)) { - mmu_flush_pid(env, (env->mmu.tlbmisc_wr & CR_TLBMISC_PID_MASK) >> - CR_TLBMISC_PID_SHIFT); + if (new_pid != old_pid) { + mmu_flush_pid(env, old_pid); } + /* if tlbmisc.RD == 1 then trigger a TLB read on writes to TLBMISC */ if (v & CR_TLBMISC_RD) { - int way = (v >> CR_TLBMISC_WAY_SHIFT); - int vpn = (env->mmu.pteaddr_wr & CR_PTEADDR_VPN_MASK) >> 2; + int vpn = FIELD_EX32(env->mmu.pteaddr_wr, CR_PTEADDR, VPN); Nios2TLBEntry *entry = &env->mmu.tlb[(way * cpu->tlb_num_ways) + (vpn & env->mmu.tlb_entry_mask)]; - env->regs[CR_TLBACC] &= CR_TLBACC_IGN_MASK; - env->regs[CR_TLBACC] |= entry->data; - env->regs[CR_TLBACC] |= (entry->tag & (1 << 11)) ? CR_TLBACC_G : 0; - env->regs[CR_TLBMISC] = - (v & ~CR_TLBMISC_PID_MASK) | - ((entry->tag & ((1 << cpu->pid_num_bits) - 1)) << - CR_TLBMISC_PID_SHIFT); - env->regs[CR_PTEADDR] &= ~CR_PTEADDR_VPN_MASK; - env->regs[CR_PTEADDR] |= (entry->tag >> 12) << CR_PTEADDR_VPN_SHIFT; + env->ctrl[CR_TLBACC] &= R_CR_TLBACC_IG_MASK; + env->ctrl[CR_TLBACC] |= entry->data; + env->ctrl[CR_TLBACC] |= (entry->tag & (1 << 11)) ? CR_TLBACC_G : 0; + env->ctrl[CR_TLBMISC] = FIELD_DP32(v, CR_TLBMISC, PID, + entry->tag & + ((1 << cpu->pid_num_bits) - 1)); + env->ctrl[CR_PTEADDR] = FIELD_DP32(env->ctrl[CR_PTEADDR], + CR_PTEADDR, VPN, + entry->tag >> TARGET_PAGE_BITS); } else { - env->regs[CR_TLBMISC] = v; + env->ctrl[CR_TLBMISC] = v; } env->mmu.tlbmisc_wr = v; @@ -171,12 +171,12 @@ void helper_mmu_write_tlbmisc(CPUNios2State *env, uint32_t v) void helper_mmu_write_pteaddr(CPUNios2State *env, uint32_t v) { - trace_nios2_mmu_write_pteaddr(v >> CR_PTEADDR_PTBASE_SHIFT, - (v & CR_PTEADDR_VPN_MASK) >> CR_PTEADDR_VPN_SHIFT); + trace_nios2_mmu_write_pteaddr(FIELD_EX32(v, CR_PTEADDR, PTBASE), + FIELD_EX32(v, CR_PTEADDR, VPN)); /* Writes to PTEADDR don't change the read-back VPN value */ - env->regs[CR_PTEADDR] = (v & ~CR_PTEADDR_VPN_MASK) | - (env->regs[CR_PTEADDR] & CR_PTEADDR_VPN_MASK); + env->ctrl[CR_PTEADDR] = ((v & ~R_CR_PTEADDR_VPN_MASK) | + (env->ctrl[CR_PTEADDR] & R_CR_PTEADDR_VPN_MASK)); env->mmu.pteaddr_wr = v; } @@ -207,7 +207,7 @@ void dump_mmu(CPUNios2State *env) entry->tag >> 12, entry->tag & ((1 << cpu->pid_num_bits) - 1), (entry->tag & (1 << 11)) ? 'G' : '-', - entry->data & CR_TLBACC_PFN_MASK, + FIELD_EX32(entry->data, CR_TLBACC, PFN), (entry->data & CR_TLBACC_C) ? 'C' : '-', (entry->data & CR_TLBACC_R) ? 'R' : '-', (entry->data & CR_TLBACC_W) ? 'W' : '-', diff --git a/target/nios2/op_helper.c b/target/nios2/op_helper.c index caa885f7b4..2e30d0a908 100644 --- a/target/nios2/op_helper.c +++ b/target/nios2/op_helper.c @@ -30,3 +30,91 @@ void helper_raise_exception(CPUNios2State *env, uint32_t index) cs->exception_index = index; cpu_loop_exit(cs); } + +void nios2_cpu_loop_exit_advance(CPUNios2State *env, uintptr_t retaddr) +{ + CPUState *cs = env_cpu(env); + + /* + * Note that PC is advanced for all hardware exceptions. + * Do this here, rather than in restore_state_to_opc(), + * lest we affect QEMU internal exceptions, like EXCP_DEBUG. + */ + cpu_restore_state(cs, retaddr, true); + env->pc += 4; + cpu_loop_exit(cs); +} + +static void maybe_raise_div(CPUNios2State *env, uintptr_t ra) +{ + Nios2CPU *cpu = env_archcpu(env); + CPUState *cs = env_cpu(env); + + if (cpu->diverr_present) { + cs->exception_index = EXCP_DIV; + nios2_cpu_loop_exit_advance(env, ra); + } +} + +int32_t helper_divs(CPUNios2State *env, int32_t num, int32_t den) +{ + if (unlikely(den == 0) || unlikely(den == -1 && num == INT32_MIN)) { + maybe_raise_div(env, GETPC()); + return num; /* undefined */ + } + return num / den; +} + +uint32_t helper_divu(CPUNios2State *env, uint32_t num, uint32_t den) +{ + if (unlikely(den == 0)) { + maybe_raise_div(env, GETPC()); + return num; /* undefined */ + } + return num / den; +} + +#ifndef CONFIG_USER_ONLY +void helper_eret(CPUNios2State *env, uint32_t new_status, uint32_t new_pc) +{ + Nios2CPU *cpu = env_archcpu(env); + CPUState *cs = env_cpu(env); + + if (unlikely(new_pc & 3)) { + env->ctrl[CR_BADADDR] = new_pc; + cs->exception_index = EXCP_UNALIGND; + nios2_cpu_loop_exit_advance(env, GETPC()); + } + + /* + * None of estatus, bstatus, or sstatus have constraints on write; + * do not allow reserved fields in status to be set. + * When shadow registers are enabled, eret *does* restore CRS. + * Rather than testing eic_present to decide, mask CRS out of + * the set of readonly fields. + */ + new_status &= cpu->cr_state[CR_STATUS].writable | + (cpu->cr_state[CR_STATUS].readonly & R_CR_STATUS_CRS_MASK); + + env->ctrl[CR_STATUS] = new_status; + env->pc = new_pc; + nios2_update_crs(env); + cpu_loop_exit(cs); +} + +/* + * RDPRS and WRPRS are implemented out of line so that if PRS == CRS, + * all of the tcg global temporaries are synced back to ENV. + */ +uint32_t helper_rdprs(CPUNios2State *env, uint32_t regno) +{ + unsigned prs = FIELD_EX32(env->ctrl[CR_STATUS], CR_STATUS, PRS); + return env->shadow_regs[prs][regno]; +} + +void helper_wrprs(CPUNios2State *env, uint32_t regno, uint32_t val) +{ + unsigned prs = FIELD_EX32(env->ctrl[CR_STATUS], CR_STATUS, PRS); + env->shadow_regs[prs][regno] = val; +} +#endif /* !CONFIG_USER_ONLY */ diff --git a/target/nios2/translate.c b/target/nios2/translate.c index 89b97ef520..3a037a68cc 100644 --- a/target/nios2/translate.c +++ b/target/nios2/translate.c @@ -33,9 +33,9 @@ #include "exec/translator.h" #include "qemu/qemu-print.h" #include "exec/gen-icount.h" +#include "semihosting/semihost.h" /* is_jmp field values */ -#define DISAS_JUMP DISAS_TARGET_0 /* only pc was modified dynamically */ #define DISAS_UPDATE DISAS_TARGET_1 /* cpu state was modified dynamically */ #define INSTRUCTION_FLG(func, flags) { (func), (flags) } @@ -52,32 +52,53 @@ #define INSN_R_TYPE 0x3A /* I-Type instruction parsing */ +typedef struct { + uint8_t op; + union { + uint16_t u; + int16_t s; + } imm16; + uint8_t b; + uint8_t a; +} InstrIType; + #define I_TYPE(instr, code) \ - struct { \ - uint8_t op; \ - union { \ - uint16_t u; \ - int16_t s; \ - } imm16; \ - uint8_t b; \ - uint8_t a; \ - } (instr) = { \ + InstrIType (instr) = { \ .op = extract32((code), 0, 6), \ .imm16.u = extract32((code), 6, 16), \ .b = extract32((code), 22, 5), \ .a = extract32((code), 27, 5), \ } +typedef target_ulong ImmFromIType(const InstrIType *); + +static target_ulong imm_unsigned(const InstrIType *i) +{ + return i->imm16.u; +} + +static target_ulong imm_signed(const InstrIType *i) +{ + return i->imm16.s; +} + +static target_ulong imm_shifted(const InstrIType *i) +{ + return i->imm16.u << 16; +} + /* R-Type instruction parsing */ +typedef struct { + uint8_t op; + uint8_t imm5; + uint8_t opx; + uint8_t c; + uint8_t b; + uint8_t a; +} InstrRType; + #define R_TYPE(instr, code) \ - struct { \ - uint8_t op; \ - uint8_t imm5; \ - uint8_t opx; \ - uint8_t c; \ - uint8_t b; \ - uint8_t a; \ - } (instr) = { \ + InstrRType (instr) = { \ .op = extract32((code), 0, 6), \ .imm5 = extract32((code), 6, 5), \ .opx = extract32((code), 11, 6), \ @@ -87,23 +108,36 @@ } /* J-Type instruction parsing */ +typedef struct { + uint8_t op; + uint32_t imm26; +} InstrJType; + #define J_TYPE(instr, code) \ - struct { \ - uint8_t op; \ - uint32_t imm26; \ - } (instr) = { \ + InstrJType (instr) = { \ .op = extract32((code), 0, 6), \ .imm26 = extract32((code), 6, 26), \ } +typedef void GenFn2i(TCGv, TCGv, target_long); +typedef void GenFn3(TCGv, TCGv, TCGv); +typedef void GenFn4(TCGv, TCGv, TCGv, TCGv); + typedef struct DisasContext { DisasContextBase base; - TCGv_i32 zero; target_ulong pc; int mem_idx; + uint32_t tb_flags; + TCGv sink; + const ControlRegState *cr_state; + bool eic_present; } DisasContext; -static TCGv cpu_R[NUM_CORE_REGS]; +static TCGv cpu_R[NUM_GP_REGS]; +static TCGv cpu_pc; +#ifndef CONFIG_USER_ONLY +static TCGv cpu_crs_R[NUM_GP_REGS]; +#endif typedef struct Nios2Instruction { void (*handler)(DisasContext *dc, uint32_t code, uint32_t flags); @@ -122,31 +156,57 @@ static uint8_t get_opxcode(uint32_t code) return instr.opx; } -static TCGv load_zero(DisasContext *dc) +static TCGv load_gpr(DisasContext *dc, unsigned reg) { - if (!dc->zero) { - dc->zero = tcg_const_i32(0); + assert(reg < NUM_GP_REGS); + + /* + * With shadow register sets, register r0 does not necessarily contain 0, + * but it is overwhelmingly likely that it does -- software is supposed + * to have set r0 to 0 in every shadow register set before use. + */ + if (unlikely(reg == R_ZERO) && FIELD_EX32(dc->tb_flags, TBFLAGS, R0_0)) { + return tcg_constant_tl(0); + } + if (FIELD_EX32(dc->tb_flags, TBFLAGS, CRS0)) { + return cpu_R[reg]; } - return dc->zero; +#ifdef CONFIG_USER_ONLY + g_assert_not_reached(); +#else + return cpu_crs_R[reg]; +#endif } -static TCGv load_gpr(DisasContext *dc, uint8_t reg) +static TCGv dest_gpr(DisasContext *dc, unsigned reg) { - if (likely(reg != R_ZERO)) { + assert(reg < NUM_GP_REGS); + + /* + * The spec for shadow register sets isn't clear, but we assume that + * writes to r0 are discarded regardless of CRS. + */ + if (unlikely(reg == R_ZERO)) { + if (dc->sink == NULL) { + dc->sink = tcg_temp_new(); + } + return dc->sink; + } + if (FIELD_EX32(dc->tb_flags, TBFLAGS, CRS0)) { return cpu_R[reg]; - } else { - return load_zero(dc); } +#ifdef CONFIG_USER_ONLY + g_assert_not_reached(); +#else + return cpu_crs_R[reg]; +#endif } -static void t_gen_helper_raise_exception(DisasContext *dc, - uint32_t index) +static void t_gen_helper_raise_exception(DisasContext *dc, uint32_t index) { - TCGv_i32 tmp = tcg_const_i32(index); - - tcg_gen_movi_tl(cpu_R[R_PC], dc->pc); - gen_helper_raise_exception(cpu_env, tmp); - tcg_temp_free_i32(tmp); + /* Note that PC is advanced for all hardware exceptions. */ + tcg_gen_movi_tl(cpu_pc, dc->base.pc_next); + gen_helper_raise_exception(cpu_env, tcg_constant_i32(index)); dc->base.is_jmp = DISAS_NORETURN; } @@ -156,12 +216,36 @@ static void gen_goto_tb(DisasContext *dc, int n, uint32_t dest) if (translator_use_goto_tb(&dc->base, dest)) { tcg_gen_goto_tb(n); - tcg_gen_movi_tl(cpu_R[R_PC], dest); + tcg_gen_movi_tl(cpu_pc, dest); tcg_gen_exit_tb(tb, n); } else { - tcg_gen_movi_tl(cpu_R[R_PC], dest); - tcg_gen_exit_tb(NULL, 0); + tcg_gen_movi_tl(cpu_pc, dest); + tcg_gen_lookup_and_goto_ptr(); } + dc->base.is_jmp = DISAS_NORETURN; +} + +static void gen_jumpr(DisasContext *dc, int regno, bool is_call) +{ + TCGLabel *l = gen_new_label(); + TCGv test = tcg_temp_new(); + TCGv dest = load_gpr(dc, regno); + + tcg_gen_andi_tl(test, dest, 3); + tcg_gen_brcondi_tl(TCG_COND_NE, test, 0, l); + tcg_temp_free(test); + + tcg_gen_mov_tl(cpu_pc, dest); + if (is_call) { + tcg_gen_movi_tl(dest_gpr(dc, R_RA), dc->base.pc_next); + } + tcg_gen_lookup_and_goto_ptr(); + + gen_set_label(l); + tcg_gen_st_tl(dest, cpu_env, offsetof(CPUNios2State, ctrl[CR_BADADDR])); + t_gen_helper_raise_exception(dc, EXCP_UNALIGND); + + dc->base.is_jmp = DISAS_NORETURN; } static void gen_excp(DisasContext *dc, uint32_t code, uint32_t flags) @@ -169,12 +253,14 @@ static void gen_excp(DisasContext *dc, uint32_t code, uint32_t flags) t_gen_helper_raise_exception(dc, flags); } -static void gen_check_supervisor(DisasContext *dc) +static bool gen_check_supervisor(DisasContext *dc) { - if (dc->base.tb->flags & CR_STATUS_U) { + if (FIELD_EX32(dc->tb_flags, TBFLAGS, U)) { /* CPU in user mode, privileged instruction called, stop. */ t_gen_helper_raise_exception(dc, EXCP_SUPERI); + return false; } + return true; } /* @@ -193,12 +279,11 @@ static void jmpi(DisasContext *dc, uint32_t code, uint32_t flags) { J_TYPE(instr, code); gen_goto_tb(dc, 0, (dc->pc & 0xF0000000) | (instr.imm26 << 2)); - dc->base.is_jmp = DISAS_NORETURN; } static void call(DisasContext *dc, uint32_t code, uint32_t flags) { - tcg_gen_movi_tl(cpu_R[R_RA], dc->base.pc_next); + tcg_gen_movi_tl(dest_gpr(dc, R_RA), dc->base.pc_next); jmpi(dc, code, flags); } @@ -211,27 +296,10 @@ static void gen_ldx(DisasContext *dc, uint32_t code, uint32_t flags) I_TYPE(instr, code); TCGv addr = tcg_temp_new(); - TCGv data; - - /* - * WARNING: Loads into R_ZERO are ignored, but we must generate the - * memory access itself to emulate the CPU precisely. Load - * from a protected page to R_ZERO will cause SIGSEGV on - * the Nios2 CPU. - */ - if (likely(instr.b != R_ZERO)) { - data = cpu_R[instr.b]; - } else { - data = tcg_temp_new(); - } + TCGv data = dest_gpr(dc, instr.b); tcg_gen_addi_tl(addr, load_gpr(dc, instr.a), instr.imm16.s); tcg_gen_qemu_ld_tl(data, addr, dc->mem_idx, flags); - - if (unlikely(instr.b == R_ZERO)) { - tcg_temp_free(data); - } - tcg_temp_free(addr); } @@ -253,7 +321,6 @@ static void br(DisasContext *dc, uint32_t code, uint32_t flags) I_TYPE(instr, code); gen_goto_tb(dc, 0, dc->base.pc_next + (instr.imm16.s & -4)); - dc->base.is_jmp = DISAS_NORETURN; } static void gen_bxx(DisasContext *dc, uint32_t code, uint32_t flags) @@ -261,48 +328,86 @@ static void gen_bxx(DisasContext *dc, uint32_t code, uint32_t flags) I_TYPE(instr, code); TCGLabel *l1 = gen_new_label(); - tcg_gen_brcond_tl(flags, cpu_R[instr.a], cpu_R[instr.b], l1); + tcg_gen_brcond_tl(flags, load_gpr(dc, instr.a), load_gpr(dc, instr.b), l1); gen_goto_tb(dc, 0, dc->base.pc_next); gen_set_label(l1); gen_goto_tb(dc, 1, dc->base.pc_next + (instr.imm16.s & -4)); - dc->base.is_jmp = DISAS_NORETURN; } /* Comparison instructions */ -#define gen_i_cmpxx(fname, op3) \ -static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \ -{ \ - I_TYPE(instr, (code)); \ - tcg_gen_setcondi_tl(flags, cpu_R[instr.b], cpu_R[instr.a], (op3)); \ +static void do_i_cmpxx(DisasContext *dc, uint32_t insn, + TCGCond cond, ImmFromIType *imm) +{ + I_TYPE(instr, insn); + tcg_gen_setcondi_tl(cond, dest_gpr(dc, instr.b), + load_gpr(dc, instr.a), imm(&instr)); } -gen_i_cmpxx(gen_cmpxxsi, instr.imm16.s) -gen_i_cmpxx(gen_cmpxxui, instr.imm16.u) +#define gen_i_cmpxx(fname, imm) \ + static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \ + { do_i_cmpxx(dc, code, flags, imm); } + +gen_i_cmpxx(gen_cmpxxsi, imm_signed) +gen_i_cmpxx(gen_cmpxxui, imm_unsigned) /* Math/logic instructions */ -#define gen_i_math_logic(fname, insn, resimm, op3) \ -static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \ -{ \ - I_TYPE(instr, (code)); \ - if (unlikely(instr.b == R_ZERO)) { /* Store to R_ZERO is ignored */ \ - return; \ - } else if (instr.a == R_ZERO) { /* MOVxI optimizations */ \ - tcg_gen_movi_tl(cpu_R[instr.b], (resimm) ? (op3) : 0); \ - } else { \ - tcg_gen_##insn##_tl(cpu_R[instr.b], cpu_R[instr.a], (op3)); \ - } \ -} - -gen_i_math_logic(addi, addi, 1, instr.imm16.s) -gen_i_math_logic(muli, muli, 0, instr.imm16.s) - -gen_i_math_logic(andi, andi, 0, instr.imm16.u) -gen_i_math_logic(ori, ori, 1, instr.imm16.u) -gen_i_math_logic(xori, xori, 1, instr.imm16.u) - -gen_i_math_logic(andhi, andi, 0, instr.imm16.u << 16) -gen_i_math_logic(orhi , ori, 1, instr.imm16.u << 16) -gen_i_math_logic(xorhi, xori, 1, instr.imm16.u << 16) +static void do_i_math_logic(DisasContext *dc, uint32_t insn, + GenFn2i *fn, ImmFromIType *imm, + bool x_op_0_eq_x) +{ + I_TYPE(instr, insn); + target_ulong val; + + if (unlikely(instr.b == R_ZERO)) { + /* Store to R_ZERO is ignored -- this catches the canonical NOP. */ + return; + } + + val = imm(&instr); + + if (instr.a == R_ZERO && FIELD_EX32(dc->tb_flags, TBFLAGS, R0_0)) { + /* This catches the canonical expansions of movi and movhi. */ + tcg_gen_movi_tl(dest_gpr(dc, instr.b), x_op_0_eq_x ? val : 0); + } else { + fn(dest_gpr(dc, instr.b), load_gpr(dc, instr.a), val); + } +} + +#define gen_i_math_logic(fname, insn, x_op_0, imm) \ + static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \ + { do_i_math_logic(dc, code, tcg_gen_##insn##_tl, imm, x_op_0); } + +gen_i_math_logic(addi, addi, 1, imm_signed) +gen_i_math_logic(muli, muli, 0, imm_signed) + +gen_i_math_logic(andi, andi, 0, imm_unsigned) +gen_i_math_logic(ori, ori, 1, imm_unsigned) +gen_i_math_logic(xori, xori, 1, imm_unsigned) + +gen_i_math_logic(andhi, andi, 0, imm_shifted) +gen_i_math_logic(orhi , ori, 1, imm_shifted) +gen_i_math_logic(xorhi, xori, 1, imm_shifted) + +/* rB <- prs.rA + sigma(IMM16) */ +static void rdprs(DisasContext *dc, uint32_t code, uint32_t flags) +{ + if (!dc->eic_present) { + t_gen_helper_raise_exception(dc, EXCP_ILLEGAL); + return; + } + if (!gen_check_supervisor(dc)) { + return; + } + +#ifdef CONFIG_USER_ONLY + g_assert_not_reached(); +#else + I_TYPE(instr, code); + TCGv dest = dest_gpr(dc, instr.b); + gen_helper_rdprs(dest, cpu_env, tcg_constant_i32(instr.a)); + tcg_gen_addi_tl(dest, dest, instr.imm16.s); +#endif +} /* Prototype only, defined below */ static void handle_r_type_instr(DisasContext *dc, uint32_t code, @@ -365,7 +470,7 @@ static const Nios2Instruction i_type_instructions[] = { INSTRUCTION_FLG(gen_stx, MO_SL), /* stwio */ INSTRUCTION_FLG(gen_bxx, TCG_COND_LTU), /* bltu */ INSTRUCTION_FLG(gen_ldx, MO_UL), /* ldwio */ - INSTRUCTION_UNIMPLEMENTED(), /* rdprs */ + INSTRUCTION(rdprs), /* rdprs */ INSTRUCTION_ILLEGAL(), INSTRUCTION_FLG(handle_r_type_instr, 0), /* R-Type */ INSTRUCTION_NOP(), /* flushd */ @@ -384,26 +489,51 @@ static const Nios2Instruction i_type_instructions[] = { */ static void eret(DisasContext *dc, uint32_t code, uint32_t flags) { - tcg_gen_mov_tl(cpu_R[CR_STATUS], cpu_R[CR_ESTATUS]); - tcg_gen_mov_tl(cpu_R[R_PC], cpu_R[R_EA]); + if (!gen_check_supervisor(dc)) { + return; + } - dc->base.is_jmp = DISAS_JUMP; +#ifdef CONFIG_USER_ONLY + g_assert_not_reached(); +#else + if (FIELD_EX32(dc->tb_flags, TBFLAGS, CRS0)) { + TCGv tmp = tcg_temp_new(); + tcg_gen_ld_tl(tmp, cpu_env, offsetof(CPUNios2State, ctrl[CR_ESTATUS])); + gen_helper_eret(cpu_env, tmp, load_gpr(dc, R_EA)); + tcg_temp_free(tmp); + } else { + gen_helper_eret(cpu_env, load_gpr(dc, R_SSTATUS), load_gpr(dc, R_EA)); + } + dc->base.is_jmp = DISAS_NORETURN; +#endif } /* PC <- ra */ static void ret(DisasContext *dc, uint32_t code, uint32_t flags) { - tcg_gen_mov_tl(cpu_R[R_PC], cpu_R[R_RA]); - - dc->base.is_jmp = DISAS_JUMP; + gen_jumpr(dc, R_RA, false); } -/* PC <- ba */ +/* + * status <- bstatus + * PC <- ba + */ static void bret(DisasContext *dc, uint32_t code, uint32_t flags) { - tcg_gen_mov_tl(cpu_R[R_PC], cpu_R[R_BA]); + if (!gen_check_supervisor(dc)) { + return; + } - dc->base.is_jmp = DISAS_JUMP; +#ifdef CONFIG_USER_ONLY + g_assert_not_reached(); +#else + TCGv tmp = tcg_temp_new(); + tcg_gen_ld_tl(tmp, cpu_env, offsetof(CPUNios2State, ctrl[CR_BSTATUS])); + gen_helper_eret(cpu_env, tmp, load_gpr(dc, R_BA)); + tcg_temp_free(tmp); + + dc->base.is_jmp = DISAS_NORETURN; +#endif } /* PC <- rA */ @@ -411,9 +541,7 @@ static void jmp(DisasContext *dc, uint32_t code, uint32_t flags) { R_TYPE(instr, code); - tcg_gen_mov_tl(cpu_R[R_PC], load_gpr(dc, instr.a)); - - dc->base.is_jmp = DISAS_JUMP; + gen_jumpr(dc, instr.a, false); } /* rC <- PC + 4 */ @@ -421,9 +549,7 @@ static void nextpc(DisasContext *dc, uint32_t code, uint32_t flags) { R_TYPE(instr, code); - if (likely(instr.c != R_ZERO)) { - tcg_gen_movi_tl(cpu_R[instr.c], dc->base.pc_next); - } + tcg_gen_movi_tl(dest_gpr(dc, instr.c), dc->base.pc_next); } /* @@ -434,24 +560,29 @@ static void callr(DisasContext *dc, uint32_t code, uint32_t flags) { R_TYPE(instr, code); - tcg_gen_mov_tl(cpu_R[R_PC], load_gpr(dc, instr.a)); - tcg_gen_movi_tl(cpu_R[R_RA], dc->base.pc_next); - - dc->base.is_jmp = DISAS_JUMP; + gen_jumpr(dc, instr.a, true); } /* rC <- ctlN */ static void rdctl(DisasContext *dc, uint32_t code, uint32_t flags) { - R_TYPE(instr, code); + if (!gen_check_supervisor(dc)) { + return; + } - gen_check_supervisor(dc); +#ifdef CONFIG_USER_ONLY + g_assert_not_reached(); +#else + R_TYPE(instr, code); + TCGv t1, t2, dest = dest_gpr(dc, instr.c); - if (unlikely(instr.c == R_ZERO)) { + /* Reserved registers read as zero. */ + if (nios2_cr_reserved(&dc->cr_state[instr.imm5])) { + tcg_gen_movi_tl(dest, 0); return; } - switch (instr.imm5 + CR_BASE) { + switch (instr.imm5) { case CR_IPENDING: /* * The value of the ipending register is synthetic. @@ -461,24 +592,44 @@ static void rdctl(DisasContext *dc, uint32_t code, uint32_t flags) * must perform the AND here, and anywhere else we need the * guest value of ipending. */ - tcg_gen_and_tl(cpu_R[instr.c], cpu_R[CR_IPENDING], cpu_R[CR_IENABLE]); + t1 = tcg_temp_new(); + t2 = tcg_temp_new(); + tcg_gen_ld_tl(t1, cpu_env, offsetof(CPUNios2State, ctrl[CR_IPENDING])); + tcg_gen_ld_tl(t2, cpu_env, offsetof(CPUNios2State, ctrl[CR_IENABLE])); + tcg_gen_and_tl(dest, t1, t2); + tcg_temp_free(t1); + tcg_temp_free(t2); break; default: - tcg_gen_mov_tl(cpu_R[instr.c], cpu_R[instr.imm5 + CR_BASE]); + tcg_gen_ld_tl(dest, cpu_env, + offsetof(CPUNios2State, ctrl[instr.imm5])); break; } +#endif } /* ctlN <- rA */ static void wrctl(DisasContext *dc, uint32_t code, uint32_t flags) { - gen_check_supervisor(dc); + if (!gen_check_supervisor(dc)) { + return; + } -#ifndef CONFIG_USER_ONLY +#ifdef CONFIG_USER_ONLY + g_assert_not_reached(); +#else R_TYPE(instr, code); TCGv v = load_gpr(dc, instr.a); + uint32_t ofs = offsetof(CPUNios2State, ctrl[instr.imm5]); + uint32_t wr = dc->cr_state[instr.imm5].writable; + uint32_t ro = dc->cr_state[instr.imm5].readonly; + + /* Skip reserved or readonly registers. */ + if (wr == 0) { + return; + } - switch (instr.imm5 + CR_BASE) { + switch (instr.imm5) { case CR_PTEADDR: gen_helper_mmu_write_pteaddr(cpu_env, v); break; @@ -488,145 +639,163 @@ static void wrctl(DisasContext *dc, uint32_t code, uint32_t flags) case CR_TLBMISC: gen_helper_mmu_write_tlbmisc(cpu_env, v); break; - case CR_IPENDING: - /* ipending is read only, writes ignored. */ - break; case CR_STATUS: case CR_IENABLE: /* If interrupts were enabled using WRCTL, trigger them. */ dc->base.is_jmp = DISAS_UPDATE; /* fall through */ default: - tcg_gen_mov_tl(cpu_R[instr.imm5 + CR_BASE], v); + if (wr == -1) { + /* The register is entirely writable. */ + tcg_gen_st_tl(v, cpu_env, ofs); + } else { + /* + * The register is partially read-only or reserved: + * merge the value. + */ + TCGv n = tcg_temp_new(); + + tcg_gen_andi_tl(n, v, wr); + + if (ro != 0) { + TCGv o = tcg_temp_new(); + tcg_gen_ld_tl(o, cpu_env, ofs); + tcg_gen_andi_tl(o, o, ro); + tcg_gen_or_tl(n, n, o); + tcg_temp_free(o); + } + + tcg_gen_st_tl(n, cpu_env, ofs); + tcg_temp_free(n); + } break; } #endif } +/* prs.rC <- rA */ +static void wrprs(DisasContext *dc, uint32_t code, uint32_t flags) +{ + if (!dc->eic_present) { + t_gen_helper_raise_exception(dc, EXCP_ILLEGAL); + return; + } + if (!gen_check_supervisor(dc)) { + return; + } + +#ifdef CONFIG_USER_ONLY + g_assert_not_reached(); +#else + R_TYPE(instr, code); + gen_helper_wrprs(cpu_env, tcg_constant_i32(instr.c), + load_gpr(dc, instr.a)); + /* + * The expected write to PRS[r0] is 0, from CRS[r0]. + * If not, and CRS == PRS (which we cannot tell from here), + * we may now have a non-zero value in our current r0. + * By ending the TB, we re-evaluate tb_flags and find out. + */ + if (instr.c == 0 + && (instr.a != 0 || !FIELD_EX32(dc->tb_flags, TBFLAGS, R0_0))) { + dc->base.is_jmp = DISAS_UPDATE; + } +#endif +} + /* Comparison instructions */ static void gen_cmpxx(DisasContext *dc, uint32_t code, uint32_t flags) { R_TYPE(instr, code); - if (likely(instr.c != R_ZERO)) { - tcg_gen_setcond_tl(flags, cpu_R[instr.c], cpu_R[instr.a], - cpu_R[instr.b]); - } + tcg_gen_setcond_tl(flags, dest_gpr(dc, instr.c), + load_gpr(dc, instr.a), load_gpr(dc, instr.b)); } /* Math/logic instructions */ -#define gen_r_math_logic(fname, insn, op3) \ -static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \ -{ \ - R_TYPE(instr, (code)); \ - if (likely(instr.c != R_ZERO)) { \ - tcg_gen_##insn(cpu_R[instr.c], load_gpr((dc), instr.a), (op3)); \ - } \ -} - -gen_r_math_logic(add, add_tl, load_gpr(dc, instr.b)) -gen_r_math_logic(sub, sub_tl, load_gpr(dc, instr.b)) -gen_r_math_logic(mul, mul_tl, load_gpr(dc, instr.b)) - -gen_r_math_logic(and, and_tl, load_gpr(dc, instr.b)) -gen_r_math_logic(or, or_tl, load_gpr(dc, instr.b)) -gen_r_math_logic(xor, xor_tl, load_gpr(dc, instr.b)) -gen_r_math_logic(nor, nor_tl, load_gpr(dc, instr.b)) - -gen_r_math_logic(srai, sari_tl, instr.imm5) -gen_r_math_logic(srli, shri_tl, instr.imm5) -gen_r_math_logic(slli, shli_tl, instr.imm5) -gen_r_math_logic(roli, rotli_tl, instr.imm5) - -#define gen_r_mul(fname, insn) \ -static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \ -{ \ - R_TYPE(instr, (code)); \ - if (likely(instr.c != R_ZERO)) { \ - TCGv t0 = tcg_temp_new(); \ - tcg_gen_##insn(t0, cpu_R[instr.c], \ - load_gpr(dc, instr.a), load_gpr(dc, instr.b)); \ - tcg_temp_free(t0); \ - } \ -} - -gen_r_mul(mulxss, muls2_tl) -gen_r_mul(mulxuu, mulu2_tl) -gen_r_mul(mulxsu, mulsu2_tl) - -#define gen_r_shift_s(fname, insn) \ -static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \ -{ \ - R_TYPE(instr, (code)); \ - if (likely(instr.c != R_ZERO)) { \ - TCGv t0 = tcg_temp_new(); \ - tcg_gen_andi_tl(t0, load_gpr((dc), instr.b), 31); \ - tcg_gen_##insn(cpu_R[instr.c], load_gpr((dc), instr.a), t0); \ - tcg_temp_free(t0); \ - } \ -} - -gen_r_shift_s(sra, sar_tl) -gen_r_shift_s(srl, shr_tl) -gen_r_shift_s(sll, shl_tl) -gen_r_shift_s(rol, rotl_tl) -gen_r_shift_s(ror, rotr_tl) +static void do_ri_math_logic(DisasContext *dc, uint32_t insn, GenFn2i *fn) +{ + R_TYPE(instr, insn); + fn(dest_gpr(dc, instr.c), load_gpr(dc, instr.a), instr.imm5); +} -static void divs(DisasContext *dc, uint32_t code, uint32_t flags) +static void do_rr_math_logic(DisasContext *dc, uint32_t insn, GenFn3 *fn) { - R_TYPE(instr, (code)); + R_TYPE(instr, insn); + fn(dest_gpr(dc, instr.c), load_gpr(dc, instr.a), load_gpr(dc, instr.b)); +} - /* Stores into R_ZERO are ignored */ - if (unlikely(instr.c == R_ZERO)) { - return; - } +#define gen_ri_math_logic(fname, insn) \ + static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \ + { do_ri_math_logic(dc, code, tcg_gen_##insn##_tl); } + +#define gen_rr_math_logic(fname, insn) \ + static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \ + { do_rr_math_logic(dc, code, tcg_gen_##insn##_tl); } + +gen_rr_math_logic(add, add) +gen_rr_math_logic(sub, sub) +gen_rr_math_logic(mul, mul) - TCGv t0 = tcg_temp_new(); - TCGv t1 = tcg_temp_new(); - TCGv t2 = tcg_temp_new(); - TCGv t3 = tcg_temp_new(); +gen_rr_math_logic(and, and) +gen_rr_math_logic(or, or) +gen_rr_math_logic(xor, xor) +gen_rr_math_logic(nor, nor) - tcg_gen_ext32s_tl(t0, load_gpr(dc, instr.a)); - tcg_gen_ext32s_tl(t1, load_gpr(dc, instr.b)); - tcg_gen_setcondi_tl(TCG_COND_EQ, t2, t0, INT_MIN); - tcg_gen_setcondi_tl(TCG_COND_EQ, t3, t1, -1); - tcg_gen_and_tl(t2, t2, t3); - tcg_gen_setcondi_tl(TCG_COND_EQ, t3, t1, 0); - tcg_gen_or_tl(t2, t2, t3); - tcg_gen_movi_tl(t3, 0); - tcg_gen_movcond_tl(TCG_COND_NE, t1, t2, t3, t2, t1); - tcg_gen_div_tl(cpu_R[instr.c], t0, t1); - tcg_gen_ext32s_tl(cpu_R[instr.c], cpu_R[instr.c]); +gen_ri_math_logic(srai, sari) +gen_ri_math_logic(srli, shri) +gen_ri_math_logic(slli, shli) +gen_ri_math_logic(roli, rotli) - tcg_temp_free(t3); - tcg_temp_free(t2); - tcg_temp_free(t1); - tcg_temp_free(t0); +static void do_rr_mul_high(DisasContext *dc, uint32_t insn, GenFn4 *fn) +{ + R_TYPE(instr, insn); + TCGv discard = tcg_temp_new(); + + fn(discard, dest_gpr(dc, instr.c), + load_gpr(dc, instr.a), load_gpr(dc, instr.b)); + tcg_temp_free(discard); } -static void divu(DisasContext *dc, uint32_t code, uint32_t flags) +#define gen_rr_mul_high(fname, insn) \ + static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \ + { do_rr_mul_high(dc, code, tcg_gen_##insn##_tl); } + +gen_rr_mul_high(mulxss, muls2) +gen_rr_mul_high(mulxuu, mulu2) +gen_rr_mul_high(mulxsu, mulsu2) + +static void do_rr_shift(DisasContext *dc, uint32_t insn, GenFn3 *fn) { - R_TYPE(instr, (code)); + R_TYPE(instr, insn); + TCGv sh = tcg_temp_new(); - /* Stores into R_ZERO are ignored */ - if (unlikely(instr.c == R_ZERO)) { - return; - } + tcg_gen_andi_tl(sh, load_gpr(dc, instr.b), 31); + fn(dest_gpr(dc, instr.c), load_gpr(dc, instr.a), sh); + tcg_temp_free(sh); +} - TCGv t0 = tcg_temp_new(); - TCGv t1 = tcg_temp_new(); - TCGv t2 = tcg_const_tl(0); - TCGv t3 = tcg_const_tl(1); +#define gen_rr_shift(fname, insn) \ + static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \ + { do_rr_shift(dc, code, tcg_gen_##insn##_tl); } - tcg_gen_ext32u_tl(t0, load_gpr(dc, instr.a)); - tcg_gen_ext32u_tl(t1, load_gpr(dc, instr.b)); - tcg_gen_movcond_tl(TCG_COND_EQ, t1, t1, t2, t3, t1); - tcg_gen_divu_tl(cpu_R[instr.c], t0, t1); - tcg_gen_ext32s_tl(cpu_R[instr.c], cpu_R[instr.c]); +gen_rr_shift(sra, sar) +gen_rr_shift(srl, shr) +gen_rr_shift(sll, shl) +gen_rr_shift(rol, rotl) +gen_rr_shift(ror, rotr) - tcg_temp_free(t3); - tcg_temp_free(t2); - tcg_temp_free(t1); - tcg_temp_free(t0); +static void divs(DisasContext *dc, uint32_t code, uint32_t flags) +{ + R_TYPE(instr, (code)); + gen_helper_divs(dest_gpr(dc, instr.c), cpu_env, + load_gpr(dc, instr.a), load_gpr(dc, instr.b)); +} + +static void divu(DisasContext *dc, uint32_t code, uint32_t flags) +{ + R_TYPE(instr, (code)); + gen_helper_divu(dest_gpr(dc, instr.c), cpu_env, + load_gpr(dc, instr.a), load_gpr(dc, instr.b)); } static void trap(DisasContext *dc, uint32_t code, uint32_t flags) @@ -644,6 +813,20 @@ static void trap(DisasContext *dc, uint32_t code, uint32_t flags) t_gen_helper_raise_exception(dc, EXCP_TRAP); } +static void gen_break(DisasContext *dc, uint32_t code, uint32_t flags) +{ +#ifndef CONFIG_USER_ONLY + /* The semihosting instruction is "break 1". */ + R_TYPE(instr, code); + if (semihosting_enabled() && instr.imm5 == 1) { + t_gen_helper_raise_exception(dc, EXCP_SEMIHOST); + return; + } +#endif + + t_gen_helper_raise_exception(dc, EXCP_BREAK); +} + static const Nios2Instruction r_type_instructions[] = { INSTRUCTION_ILLEGAL(), INSTRUCTION(eret), /* eret */ @@ -665,7 +848,7 @@ static const Nios2Instruction r_type_instructions[] = { INSTRUCTION_ILLEGAL(), INSTRUCTION(slli), /* slli */ INSTRUCTION(sll), /* sll */ - INSTRUCTION_UNIMPLEMENTED(), /* wrprs */ + INSTRUCTION(wrprs), /* wrprs */ INSTRUCTION_ILLEGAL(), INSTRUCTION(or), /* or */ INSTRUCTION(mulxsu), /* mulxsu */ @@ -697,7 +880,7 @@ static const Nios2Instruction r_type_instructions[] = { INSTRUCTION(add), /* add */ INSTRUCTION_ILLEGAL(), INSTRUCTION_ILLEGAL(), - INSTRUCTION_FLG(gen_excp, EXCP_BREAK), /* break */ + INSTRUCTION(gen_break), /* break */ INSTRUCTION_ILLEGAL(), INSTRUCTION(nop), /* nop */ INSTRUCTION_ILLEGAL(), @@ -730,7 +913,7 @@ illegal_op: t_gen_helper_raise_exception(dc, EXCP_ILLEGAL); } -static const char * const regnames[] = { +static const char * const gr_regnames[NUM_GP_REGS] = { "zero", "at", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", @@ -739,16 +922,20 @@ static const char * const regnames[] = { "r20", "r21", "r22", "r23", "et", "bt", "gp", "sp", "fp", "ea", "ba", "ra", +}; + +#ifndef CONFIG_USER_ONLY +static const char * const cr_regnames[NUM_CR_REGS] = { "status", "estatus", "bstatus", "ienable", - "ipending", "cpuid", "reserved0", "exception", + "ipending", "cpuid", "res6", "exception", "pteaddr", "tlbacc", "tlbmisc", "reserved1", "badaddr", "config", "mpubase", "mpuacc", - "reserved2", "reserved3", "reserved4", "reserved5", - "reserved6", "reserved7", "reserved8", "reserved9", - "reserved10", "reserved11", "reserved12", "reserved13", - "reserved14", "reserved15", "reserved16", "reserved17", - "rpc" + "res16", "res17", "res18", "res19", + "res20", "res21", "res22", "res23", + "res24", "res25", "res26", "res27", + "res28", "res29", "res30", "res31", }; +#endif #include "exec/gen-icount.h" @@ -757,9 +944,13 @@ static void nios2_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) { DisasContext *dc = container_of(dcbase, DisasContext, base); CPUNios2State *env = cs->env_ptr; + Nios2CPU *cpu = env_archcpu(env); int page_insns; dc->mem_idx = cpu_mmu_index(env, false); + dc->cr_state = cpu->cr_state; + dc->tb_flags = dc->base.tb->flags; + dc->eic_present = cpu->eic_present; /* Bound the number of insns to execute to those left on the page. */ page_insns = -(dc->base.pc_first | TARGET_PAGE_MASK) / 4; @@ -796,13 +987,13 @@ static void nios2_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) return; } - dc->zero = NULL; + dc->sink = NULL; instr = &i_type_instructions[op]; instr->handler(dc, code, instr->flags); - if (dc->zero) { - tcg_temp_free(dc->zero); + if (dc->sink) { + tcg_temp_free(dc->sink); } } @@ -813,14 +1004,12 @@ static void nios2_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) /* Indicate where the next block should start */ switch (dc->base.is_jmp) { case DISAS_TOO_MANY: - case DISAS_UPDATE: - /* Save the current PC back into the CPU register */ - tcg_gen_movi_tl(cpu_R[R_PC], dc->base.pc_next); - tcg_gen_exit_tb(NULL, 0); + gen_goto_tb(dc, 0, dc->base.pc_next); break; - case DISAS_JUMP: - /* The jump will already have updated the PC register */ + case DISAS_UPDATE: + /* Save the current PC, and return to the main loop. */ + tcg_gen_movi_tl(cpu_pc, dc->base.pc_next); tcg_gen_exit_tb(NULL, 0); break; @@ -861,41 +1050,67 @@ void nios2_cpu_dump_state(CPUState *cs, FILE *f, int flags) CPUNios2State *env = &cpu->env; int i; - if (!env) { - return; - } - - qemu_fprintf(f, "IN: PC=%x %s\n", - env->regs[R_PC], lookup_symbol(env->regs[R_PC])); + qemu_fprintf(f, "IN: PC=%x %s\n", env->pc, lookup_symbol(env->pc)); - for (i = 0; i < NUM_CORE_REGS; i++) { - qemu_fprintf(f, "%9s=%8.8x ", regnames[i], env->regs[i]); + for (i = 0; i < NUM_GP_REGS; i++) { + qemu_fprintf(f, "%9s=%8.8x ", gr_regnames[i], env->regs[i]); if ((i + 1) % 4 == 0) { qemu_fprintf(f, "\n"); } } + #if !defined(CONFIG_USER_ONLY) - qemu_fprintf(f, " mmu write: VPN=%05X PID %02X TLBACC %08X\n", - env->mmu.pteaddr_wr & CR_PTEADDR_VPN_MASK, - (env->mmu.tlbmisc_wr & CR_TLBMISC_PID_MASK) >> 4, - env->mmu.tlbacc_wr); + int j; + + for (i = j = 0; i < NUM_CR_REGS; i++) { + if (!nios2_cr_reserved(&cpu->cr_state[i])) { + qemu_fprintf(f, "%9s=%8.8x ", cr_regnames[i], env->ctrl[i]); + if (++j % 4 == 0) { + qemu_fprintf(f, "\n"); + } + } + } + if (j % 4 != 0) { + qemu_fprintf(f, "\n"); + } + if (cpu->mmu_present) { + qemu_fprintf(f, " mmu write: VPN=%05X PID %02X TLBACC %08X\n", + env->mmu.pteaddr_wr & R_CR_PTEADDR_VPN_MASK, + FIELD_EX32(env->mmu.tlbmisc_wr, CR_TLBMISC, PID), + env->mmu.tlbacc_wr); + } #endif qemu_fprintf(f, "\n\n"); } void nios2_tcg_init(void) { - int i; +#ifndef CONFIG_USER_ONLY + TCGv_ptr crs = tcg_global_mem_new_ptr(cpu_env, + offsetof(CPUNios2State, regs), "crs"); - for (i = 0; i < NUM_CORE_REGS; i++) { - cpu_R[i] = tcg_global_mem_new(cpu_env, - offsetof(CPUNios2State, regs[i]), - regnames[i]); + for (int i = 0; i < NUM_GP_REGS; i++) { + cpu_crs_R[i] = tcg_global_mem_new(crs, 4 * i, gr_regnames[i]); } + +#define offsetof_regs0(N) offsetof(CPUNios2State, shadow_regs[0][N]) +#else +#define offsetof_regs0(N) offsetof(CPUNios2State, regs[N]) +#endif + + for (int i = 0; i < NUM_GP_REGS; i++) { + cpu_R[i] = tcg_global_mem_new(cpu_env, offsetof_regs0(i), + gr_regnames[i]); + } + +#undef offsetof_regs0 + + cpu_pc = tcg_global_mem_new(cpu_env, + offsetof(CPUNios2State, pc), "pc"); } void restore_state_to_opc(CPUNios2State *env, TranslationBlock *tb, target_ulong *data) { - env->regs[R_PC] = data[0]; + env->pc = data[0]; } diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index ddda4906ff..0c774056c5 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -34,7 +34,12 @@ /* RISC-V CPU definitions */ -static const char riscv_exts[26] = "IEMAFDQCLBJTPVNSUHKORWXYZG"; +static const char riscv_single_letter_exts[] = "IEMAFDQCPVH"; + +struct isa_ext_data { + const char *name; + bool enabled; +}; const char * const riscv_int_regnames[] = { "x0/zero", "x1/ra", "x2/sp", "x3/gp", "x4/tp", "x5/t0", "x6/t1", @@ -150,7 +155,7 @@ static void riscv_any_cpu_init(Object *obj) #elif defined(TARGET_RISCV64) set_misa(env, MXL_RV64, RVI | RVM | RVA | RVF | RVD | RVC | RVU); #endif - set_priv_version(env, PRIV_VERSION_1_11_0); + set_priv_version(env, PRIV_VERSION_1_12_0); } #if defined(TARGET_RISCV64) @@ -461,6 +466,10 @@ static void riscv_cpu_reset(DeviceState *dev) set_default_nan_mode(1, &env->fp_status); #ifndef CONFIG_USER_ONLY + if (riscv_feature(env, RISCV_FEATURE_DEBUG)) { + riscv_trigger_init(env); + } + if (kvm_enabled()) { kvm_riscv_reset_vcpu(cpu); } @@ -503,7 +512,9 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp) } if (cpu->cfg.priv_spec) { - if (!g_strcmp0(cpu->cfg.priv_spec, "v1.11.0")) { + if (!g_strcmp0(cpu->cfg.priv_spec, "v1.12.0")) { + priv_version = PRIV_VERSION_1_12_0; + } else if (!g_strcmp0(cpu->cfg.priv_spec, "v1.11.0")) { priv_version = PRIV_VERSION_1_11_0; } else if (!g_strcmp0(cpu->cfg.priv_spec, "v1.10.0")) { priv_version = PRIV_VERSION_1_10_0; @@ -518,7 +529,7 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp) if (priv_version) { set_priv_version(env, priv_version); } else if (!env->priv_ver) { - set_priv_version(env, PRIV_VERSION_1_11_0); + set_priv_version(env, PRIV_VERSION_1_12_0); } if (cpu->cfg.mmu) { @@ -541,6 +552,10 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp) riscv_set_feature(env, RISCV_FEATURE_AIA); } + if (cpu->cfg.debug) { + riscv_set_feature(env, RISCV_FEATURE_DEBUG); + } + set_resetvec(env, cpu->cfg.resetvec); /* Validate that MISA_MXL is set properly. */ @@ -567,18 +582,18 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp) if (cpu->cfg.ext_i && cpu->cfg.ext_e) { error_setg(errp, "I and E extensions are incompatible"); - return; - } + return; + } if (!cpu->cfg.ext_i && !cpu->cfg.ext_e) { error_setg(errp, "Either I or E extension must be set"); - return; - } + return; + } - if (cpu->cfg.ext_g && !(cpu->cfg.ext_i & cpu->cfg.ext_m & - cpu->cfg.ext_a & cpu->cfg.ext_f & - cpu->cfg.ext_d)) { + if (cpu->cfg.ext_g && !(cpu->cfg.ext_i & cpu->cfg.ext_m & + cpu->cfg.ext_a & cpu->cfg.ext_f & + cpu->cfg.ext_d)) { warn_report("Setting G will also set IMAFD"); cpu->cfg.ext_i = true; cpu->cfg.ext_m = true; @@ -706,15 +721,23 @@ static void riscv_cpu_set_irq(void *opaque, int irq, int level) case IRQ_VS_TIMER: case IRQ_M_TIMER: case IRQ_U_EXT: - case IRQ_S_EXT: case IRQ_VS_EXT: case IRQ_M_EXT: - if (kvm_enabled()) { + if (kvm_enabled()) { kvm_riscv_set_irq(cpu, irq, level); - } else { + } else { riscv_cpu_update_mip(cpu, 1 << irq, BOOL_TO_MASK(level)); - } + } break; + case IRQ_S_EXT: + if (kvm_enabled()) { + kvm_riscv_set_irq(cpu, irq, level); + } else { + env->external_seip = level; + riscv_cpu_update_mip(cpu, 1 << irq, + BOOL_TO_MASK(level | env->software_seip)); + } + break; default: g_assert_not_reached(); } @@ -780,6 +803,7 @@ static Property riscv_cpu_properties[] = { DEFINE_PROP_BOOL("Zve64f", RISCVCPU, cfg.ext_zve64f, false), DEFINE_PROP_BOOL("mmu", RISCVCPU, cfg.mmu, true), DEFINE_PROP_BOOL("pmp", RISCVCPU, cfg.pmp, true), + DEFINE_PROP_BOOL("debug", RISCVCPU, cfg.debug, true), DEFINE_PROP_STRING("priv_spec", RISCVCPU, cfg.priv_spec), DEFINE_PROP_STRING("vext_spec", RISCVCPU, cfg.vext_spec), @@ -865,6 +889,9 @@ static const struct TCGCPUOps riscv_tcg_ops = { .do_interrupt = riscv_cpu_do_interrupt, .do_transaction_failed = riscv_cpu_do_transaction_failed, .do_unaligned_access = riscv_cpu_do_unaligned_access, + .debug_excp_handler = riscv_cpu_debug_excp_handler, + .debug_check_breakpoint = riscv_cpu_debug_check_breakpoint, + .debug_check_watchpoint = riscv_cpu_debug_check_watchpoint, #endif /* !CONFIG_USER_ONLY */ }; @@ -898,18 +925,73 @@ static void riscv_cpu_class_init(ObjectClass *c, void *data) device_class_set_props(dc, riscv_cpu_properties); } +#define ISA_EDATA_ENTRY(name, prop) {#name, cpu->cfg.prop} + +static void riscv_isa_string_ext(RISCVCPU *cpu, char **isa_str, int max_str_len) +{ + char *old = *isa_str; + char *new = *isa_str; + int i; + + /** + * Here are the ordering rules of extension naming defined by RISC-V + * specification : + * 1. All extensions should be separated from other multi-letter extensions + * by an underscore. + * 2. The first letter following the 'Z' conventionally indicates the most + * closely related alphabetical extension category, IMAFDQLCBKJTPVH. + * If multiple 'Z' extensions are named, they should be ordered first + * by category, then alphabetically within a category. + * 3. Standard supervisor-level extensions (starts with 'S') should be + * listed after standard unprivileged extensions. If multiple + * supervisor-level extensions are listed, they should be ordered + * alphabetically. + * 4. Non-standard extensions (starts with 'X') must be listed after all + * standard extensions. They must be separated from other multi-letter + * extensions by an underscore. + */ + struct isa_ext_data isa_edata_arr[] = { + ISA_EDATA_ENTRY(zfh, ext_zfh), + ISA_EDATA_ENTRY(zfhmin, ext_zfhmin), + ISA_EDATA_ENTRY(zfinx, ext_zfinx), + ISA_EDATA_ENTRY(zhinx, ext_zhinx), + ISA_EDATA_ENTRY(zhinxmin, ext_zhinxmin), + ISA_EDATA_ENTRY(zdinx, ext_zdinx), + ISA_EDATA_ENTRY(zba, ext_zba), + ISA_EDATA_ENTRY(zbb, ext_zbb), + ISA_EDATA_ENTRY(zbc, ext_zbc), + ISA_EDATA_ENTRY(zbs, ext_zbs), + ISA_EDATA_ENTRY(zve32f, ext_zve32f), + ISA_EDATA_ENTRY(zve64f, ext_zve64f), + ISA_EDATA_ENTRY(svinval, ext_svinval), + ISA_EDATA_ENTRY(svnapot, ext_svnapot), + ISA_EDATA_ENTRY(svpbmt, ext_svpbmt), + }; + + for (i = 0; i < ARRAY_SIZE(isa_edata_arr); i++) { + if (isa_edata_arr[i].enabled) { + new = g_strconcat(old, "_", isa_edata_arr[i].name, NULL); + g_free(old); + old = new; + } + } + + *isa_str = new; +} + char *riscv_isa_string(RISCVCPU *cpu) { int i; - const size_t maxlen = sizeof("rv128") + sizeof(riscv_exts) + 1; + const size_t maxlen = sizeof("rv128") + sizeof(riscv_single_letter_exts); char *isa_str = g_new(char, maxlen); char *p = isa_str + snprintf(isa_str, maxlen, "rv%d", TARGET_LONG_BITS); - for (i = 0; i < sizeof(riscv_exts); i++) { - if (cpu->env.misa_ext & RV(riscv_exts[i])) { - *p++ = qemu_tolower(riscv_exts[i]); + for (i = 0; i < sizeof(riscv_single_letter_exts) - 1; i++) { + if (cpu->env.misa_ext & RV(riscv_single_letter_exts[i])) { + *p++ = qemu_tolower(riscv_single_letter_exts[i]); } } *p = '\0'; + riscv_isa_string_ext(cpu, &isa_str, maxlen); return isa_str; } diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 72f1c9451e..34c22d5d3b 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -79,11 +79,16 @@ enum { RISCV_FEATURE_PMP, RISCV_FEATURE_EPMP, RISCV_FEATURE_MISA, - RISCV_FEATURE_AIA + RISCV_FEATURE_AIA, + RISCV_FEATURE_DEBUG }; -#define PRIV_VERSION_1_10_0 0x00011000 -#define PRIV_VERSION_1_11_0 0x00011100 +/* Privileged specification version */ +enum { + PRIV_VERSION_1_10_0 = 0, + PRIV_VERSION_1_11_0, + PRIV_VERSION_1_12_0, +}; #define VEXT_VERSION_1_00_0 0x00010000 @@ -102,6 +107,7 @@ typedef struct CPUArchState CPURISCVState; #if !defined(CONFIG_USER_ONLY) #include "pmp.h" +#include "debug.h" #endif #define RV_VLEN_MAX 1024 @@ -173,6 +179,14 @@ struct CPUArchState { uint64_t mstatus; uint64_t mip; + /* + * MIP contains the software writable version of SEIP ORed with the + * external interrupt value. The MIP register is always up-to-date. + * To keep track of the current source, we also save booleans of the values + * here. + */ + bool external_seip; + bool software_seip; uint64_t miclaim; @@ -267,9 +281,13 @@ struct CPUArchState { pmp_table_t pmp_state; target_ulong mseccfg; + /* trigger module */ + target_ulong trigger_cur; + type2_trigger_t type2_trig[TRIGGER_TYPE2_NUM]; + /* machine specific rdtime callback */ - uint64_t (*rdtime_fn)(uint32_t); - uint32_t rdtime_fn_arg; + uint64_t (*rdtime_fn)(void *); + void *rdtime_fn_arg; /* machine specific AIA ireg read-modify-write callback */ #define AIA_MAKE_IREG(__isel, __priv, __virt, __vgein, __xlen) \ @@ -300,6 +318,11 @@ struct CPUArchState { target_ulong spmbase; target_ulong upmmask; target_ulong upmbase; + + /* CSRs for execution enviornment configuration */ + uint64_t menvcfg; + target_ulong senvcfg; + uint64_t henvcfg; #endif target_ulong cur_pmmask; target_ulong cur_pmbase; @@ -383,6 +406,7 @@ struct RISCVCPUConfig { bool pmp; bool epmp; bool aia; + bool debug; uint64_t resetvec; }; @@ -474,8 +498,8 @@ void riscv_cpu_swap_hypervisor_regs(CPURISCVState *env); int riscv_cpu_claim_interrupts(RISCVCPU *cpu, uint64_t interrupts); uint64_t riscv_cpu_update_mip(RISCVCPU *cpu, uint64_t mask, uint64_t value); #define BOOL_TO_MASK(x) (-!!(x)) /* helper for riscv_cpu_update_mip value */ -void riscv_cpu_set_rdtime_fn(CPURISCVState *env, uint64_t (*fn)(uint32_t), - uint32_t arg); +void riscv_cpu_set_rdtime_fn(CPURISCVState *env, uint64_t (*fn)(void *), + void *arg); void riscv_cpu_set_aia_ireg_rmw_fn(CPURISCVState *env, uint32_t priv, int (*rmw_fn)(void *arg, target_ulong reg, @@ -654,6 +678,8 @@ typedef struct { riscv_csr_op_fn op; riscv_csr_read128_fn read128; riscv_csr_write128_fn write128; + /* The default priv spec version should be PRIV_VERSION_1_10_0 (i.e 0) */ + uint32_t min_priv_ver; } riscv_csr_operations; /* CSR function table constants */ diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h index 0fe01d7da5..4a9e4f7d09 100644 --- a/target/riscv/cpu_bits.h +++ b/target/riscv/cpu_bits.h @@ -148,6 +148,7 @@ #define CSR_MARCHID 0xf12 #define CSR_MIMPID 0xf13 #define CSR_MHARTID 0xf14 +#define CSR_MCONFIGPTR 0xf15 /* Machine Trap Setup */ #define CSR_MSTATUS 0x300 @@ -201,6 +202,9 @@ #define CSR_STVEC 0x105 #define CSR_SCOUNTEREN 0x106 +/* Supervisor Configuration CSRs */ +#define CSR_SENVCFG 0x10A + /* Supervisor Trap Handling */ #define CSR_SSCRATCH 0x140 #define CSR_SEPC 0x141 @@ -246,6 +250,10 @@ #define CSR_HTIMEDELTA 0x605 #define CSR_HTIMEDELTAH 0x615 +/* Hypervisor Configuration CSRs */ +#define CSR_HENVCFG 0x60A +#define CSR_HENVCFGH 0x61A + /* Virtual CSRs */ #define CSR_VSSTATUS 0x200 #define CSR_VSIE 0x204 @@ -289,6 +297,10 @@ #define CSR_VSIEH 0x214 #define CSR_VSIPH 0x254 +/* Machine Configuration CSRs */ +#define CSR_MENVCFG 0x30A +#define CSR_MENVCFGH 0x31A + /* Enhanced Physical Memory Protection (ePMP) */ #define CSR_MSECCFG 0x747 #define CSR_MSECCFGH 0x757 @@ -662,6 +674,34 @@ typedef enum RISCVException { #define PM_EXT_CLEAN 0x00000002ULL #define PM_EXT_DIRTY 0x00000003ULL +/* Execution enviornment configuration bits */ +#define MENVCFG_FIOM BIT(0) +#define MENVCFG_CBIE (3UL << 4) +#define MENVCFG_CBCFE BIT(6) +#define MENVCFG_CBZE BIT(7) +#define MENVCFG_PBMTE (1ULL << 62) +#define MENVCFG_STCE (1ULL << 63) + +/* For RV32 */ +#define MENVCFGH_PBMTE BIT(30) +#define MENVCFGH_STCE BIT(31) + +#define SENVCFG_FIOM MENVCFG_FIOM +#define SENVCFG_CBIE MENVCFG_CBIE +#define SENVCFG_CBCFE MENVCFG_CBCFE +#define SENVCFG_CBZE MENVCFG_CBZE + +#define HENVCFG_FIOM MENVCFG_FIOM +#define HENVCFG_CBIE MENVCFG_CBIE +#define HENVCFG_CBCFE MENVCFG_CBCFE +#define HENVCFG_CBZE MENVCFG_CBZE +#define HENVCFG_PBMTE MENVCFG_PBMTE +#define HENVCFG_STCE MENVCFG_STCE + +/* For RV32 */ +#define HENVCFGH_PBMTE MENVCFGH_PBMTE +#define HENVCFGH_STCE MENVCFGH_STCE + /* Offsets for every pair of control bits per each priv level */ #define XS_OFFSET 0ULL #define U_OFFSET 2ULL diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 1c60fb2e80..e1aa4f2097 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -632,8 +632,8 @@ uint64_t riscv_cpu_update_mip(RISCVCPU *cpu, uint64_t mask, uint64_t value) return old; } -void riscv_cpu_set_rdtime_fn(CPURISCVState *env, uint64_t (*fn)(uint32_t), - uint32_t arg) +void riscv_cpu_set_rdtime_fn(CPURISCVState *env, uint64_t (*fn)(void *), + void *arg) { env->rdtime_fn = fn; env->rdtime_fn_arg = arg; @@ -1150,7 +1150,7 @@ void riscv_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr, env->badaddr = addr; env->two_stage_lookup = riscv_cpu_virt_enabled(env) || riscv_cpu_two_stage_lookup(mmu_idx); - riscv_raise_exception(&cpu->env, cs->exception_index, retaddr); + cpu_loop_exit_restore(cs, retaddr); } void riscv_cpu_do_unaligned_access(CPUState *cs, vaddr addr, @@ -1175,7 +1175,7 @@ void riscv_cpu_do_unaligned_access(CPUState *cs, vaddr addr, env->badaddr = addr; env->two_stage_lookup = riscv_cpu_virt_enabled(env) || riscv_cpu_two_stage_lookup(mmu_idx); - riscv_raise_exception(env, cs->exception_index, retaddr); + cpu_loop_exit_restore(cs, retaddr); } bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, @@ -1311,7 +1311,7 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, first_stage_error, riscv_cpu_virt_enabled(env) || riscv_cpu_two_stage_lookup(mmu_idx)); - riscv_raise_exception(env, cs->exception_index, retaddr); + cpu_loop_exit_restore(cs, retaddr); } return true; diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 341c2e6f23..6ba85e7b5d 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -290,6 +290,15 @@ static RISCVException epmp(CPURISCVState *env, int csrno) return RISCV_EXCP_ILLEGAL_INST; } + +static RISCVException debug(CPURISCVState *env, int csrno) +{ + if (riscv_feature(env, RISCV_FEATURE_DEBUG)) { + return RISCV_EXCP_NONE; + } + + return RISCV_EXCP_ILLEGAL_INST; +} #endif /* User Floating-Point CSRs */ @@ -1398,15 +1407,114 @@ static RISCVException write_mtval(CPURISCVState *env, int csrno, return RISCV_EXCP_NONE; } +/* Execution environment configuration setup */ +static RISCVException read_menvcfg(CPURISCVState *env, int csrno, + target_ulong *val) +{ + *val = env->menvcfg; + return RISCV_EXCP_NONE; +} + +static RISCVException write_menvcfg(CPURISCVState *env, int csrno, + target_ulong val) +{ + uint64_t mask = MENVCFG_FIOM | MENVCFG_CBIE | MENVCFG_CBCFE | MENVCFG_CBZE; + + if (riscv_cpu_mxl(env) == MXL_RV64) { + mask |= MENVCFG_PBMTE | MENVCFG_STCE; + } + env->menvcfg = (env->menvcfg & ~mask) | (val & mask); + + return RISCV_EXCP_NONE; +} + +static RISCVException read_menvcfgh(CPURISCVState *env, int csrno, + target_ulong *val) +{ + *val = env->menvcfg >> 32; + return RISCV_EXCP_NONE; +} + +static RISCVException write_menvcfgh(CPURISCVState *env, int csrno, + target_ulong val) +{ + uint64_t mask = MENVCFG_PBMTE | MENVCFG_STCE; + uint64_t valh = (uint64_t)val << 32; + + env->menvcfg = (env->menvcfg & ~mask) | (valh & mask); + + return RISCV_EXCP_NONE; +} + +static RISCVException read_senvcfg(CPURISCVState *env, int csrno, + target_ulong *val) +{ + *val = env->senvcfg; + return RISCV_EXCP_NONE; +} + +static RISCVException write_senvcfg(CPURISCVState *env, int csrno, + target_ulong val) +{ + uint64_t mask = SENVCFG_FIOM | SENVCFG_CBIE | SENVCFG_CBCFE | SENVCFG_CBZE; + + env->senvcfg = (env->senvcfg & ~mask) | (val & mask); + + return RISCV_EXCP_NONE; +} + +static RISCVException read_henvcfg(CPURISCVState *env, int csrno, + target_ulong *val) +{ + *val = env->henvcfg; + return RISCV_EXCP_NONE; +} + +static RISCVException write_henvcfg(CPURISCVState *env, int csrno, + target_ulong val) +{ + uint64_t mask = HENVCFG_FIOM | HENVCFG_CBIE | HENVCFG_CBCFE | HENVCFG_CBZE; + + if (riscv_cpu_mxl(env) == MXL_RV64) { + mask |= HENVCFG_PBMTE | HENVCFG_STCE; + } + + env->henvcfg = (env->henvcfg & ~mask) | (val & mask); + + return RISCV_EXCP_NONE; +} + +static RISCVException read_henvcfgh(CPURISCVState *env, int csrno, + target_ulong *val) +{ + *val = env->henvcfg >> 32; + return RISCV_EXCP_NONE; +} + +static RISCVException write_henvcfgh(CPURISCVState *env, int csrno, + target_ulong val) +{ + uint64_t mask = HENVCFG_PBMTE | HENVCFG_STCE; + uint64_t valh = (uint64_t)val << 32; + + env->henvcfg = (env->henvcfg & ~mask) | (valh & mask); + + return RISCV_EXCP_NONE; +} + static RISCVException rmw_mip64(CPURISCVState *env, int csrno, uint64_t *ret_val, uint64_t new_val, uint64_t wr_mask) { RISCVCPU *cpu = env_archcpu(env); - /* Allow software control of delegable interrupts not claimed by hardware */ - uint64_t old_mip, mask = wr_mask & delegable_ints & ~env->miclaim; + uint64_t old_mip, mask = wr_mask & delegable_ints; uint32_t gin; + if (mask & MIP_SEIP) { + env->software_seip = new_val & MIP_SEIP; + new_val |= env->external_seip * MIP_SEIP; + } + if (mask) { old_mip = riscv_cpu_update_mip(cpu, mask, (new_val & mask)); } else { @@ -2578,6 +2686,48 @@ static RISCVException write_pmpaddr(CPURISCVState *env, int csrno, return RISCV_EXCP_NONE; } +static RISCVException read_tselect(CPURISCVState *env, int csrno, + target_ulong *val) +{ + *val = tselect_csr_read(env); + return RISCV_EXCP_NONE; +} + +static RISCVException write_tselect(CPURISCVState *env, int csrno, + target_ulong val) +{ + tselect_csr_write(env, val); + return RISCV_EXCP_NONE; +} + +static RISCVException read_tdata(CPURISCVState *env, int csrno, + target_ulong *val) +{ + /* return 0 in tdata1 to end the trigger enumeration */ + if (env->trigger_cur >= TRIGGER_NUM && csrno == CSR_TDATA1) { + *val = 0; + return RISCV_EXCP_NONE; + } + + if (!tdata_available(env, csrno - CSR_TDATA1)) { + return RISCV_EXCP_ILLEGAL_INST; + } + + *val = tdata_csr_read(env, csrno - CSR_TDATA1); + return RISCV_EXCP_NONE; +} + +static RISCVException write_tdata(CPURISCVState *env, int csrno, + target_ulong val) +{ + if (!tdata_available(env, csrno - CSR_TDATA1)) { + return RISCV_EXCP_ILLEGAL_INST; + } + + tdata_csr_write(env, csrno - CSR_TDATA1, val); + return RISCV_EXCP_NONE; +} + /* * Functions to access Pointer Masking feature registers * We have to check if current priv lvl could modify @@ -2880,6 +3030,7 @@ static inline RISCVException riscv_csrrw_check(CPURISCVState *env, { /* check privileges and return RISCV_EXCP_ILLEGAL_INST if check fails */ int read_only = get_field(csrno, 0xC00) == 3; + int csr_min_priv = csr_ops[csrno].min_priv_ver; #if !defined(CONFIG_USER_ONLY) int effective_priv = env->priv; @@ -2912,6 +3063,10 @@ static inline RISCVException riscv_csrrw_check(CPURISCVState *env, return RISCV_EXCP_ILLEGAL_INST; } + if (env->priv_ver < csr_min_priv) { + return RISCV_EXCP_ILLEGAL_INST; + } + return csr_ops[csrno].predicate(env, csrno); } @@ -3070,13 +3225,20 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_FRM] = { "frm", fs, read_frm, write_frm }, [CSR_FCSR] = { "fcsr", fs, read_fcsr, write_fcsr }, /* Vector CSRs */ - [CSR_VSTART] = { "vstart", vs, read_vstart, write_vstart }, - [CSR_VXSAT] = { "vxsat", vs, read_vxsat, write_vxsat }, - [CSR_VXRM] = { "vxrm", vs, read_vxrm, write_vxrm }, - [CSR_VCSR] = { "vcsr", vs, read_vcsr, write_vcsr }, - [CSR_VL] = { "vl", vs, read_vl }, - [CSR_VTYPE] = { "vtype", vs, read_vtype }, - [CSR_VLENB] = { "vlenb", vs, read_vlenb }, + [CSR_VSTART] = { "vstart", vs, read_vstart, write_vstart, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_VXSAT] = { "vxsat", vs, read_vxsat, write_vxsat, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_VXRM] = { "vxrm", vs, read_vxrm, write_vxrm, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_VCSR] = { "vcsr", vs, read_vcsr, write_vcsr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_VL] = { "vl", vs, read_vl, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_VTYPE] = { "vtype", vs, read_vtype, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_VLENB] = { "vlenb", vs, read_vlenb, + .min_priv_ver = PRIV_VERSION_1_12_0 }, /* User Timers and Counters */ [CSR_CYCLE] = { "cycle", ctr, read_instret }, [CSR_INSTRET] = { "instret", ctr, read_instret }, @@ -3103,6 +3265,8 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_MIMPID] = { "mimpid", any, read_zero }, [CSR_MHARTID] = { "mhartid", any, read_mhartid }, + [CSR_MCONFIGPTR] = { "mconfigptr", any, read_zero, + .min_priv_ver = PRIV_VERSION_1_12_0 }, /* Machine Trap Setup */ [CSR_MSTATUS] = { "mstatus", any, read_mstatus, write_mstatus, NULL, read_mstatus_i128 }, @@ -3149,6 +3313,18 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_MVIPH] = { "mviph", aia_any32, read_zero, write_ignore }, [CSR_MIPH] = { "miph", aia_any32, NULL, NULL, rmw_miph }, + /* Execution environment configuration */ + [CSR_MENVCFG] = { "menvcfg", any, read_menvcfg, write_menvcfg, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_MENVCFGH] = { "menvcfgh", any32, read_menvcfgh, write_menvcfgh, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_SENVCFG] = { "senvcfg", smode, read_senvcfg, write_senvcfg, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_HENVCFG] = { "henvcfg", hmode, read_henvcfg, write_henvcfg, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_HENVCFGH] = { "henvcfgh", hmode32, read_henvcfgh, write_henvcfgh, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + /* Supervisor Trap Setup */ [CSR_SSTATUS] = { "sstatus", smode, read_sstatus, write_sstatus, NULL, read_sstatus_i128 }, @@ -3185,33 +3361,58 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_SIEH] = { "sieh", aia_smode32, NULL, NULL, rmw_sieh }, [CSR_SIPH] = { "siph", aia_smode32, NULL, NULL, rmw_siph }, - [CSR_HSTATUS] = { "hstatus", hmode, read_hstatus, write_hstatus }, - [CSR_HEDELEG] = { "hedeleg", hmode, read_hedeleg, write_hedeleg }, - [CSR_HIDELEG] = { "hideleg", hmode, NULL, NULL, rmw_hideleg }, - [CSR_HVIP] = { "hvip", hmode, NULL, NULL, rmw_hvip }, - [CSR_HIP] = { "hip", hmode, NULL, NULL, rmw_hip }, - [CSR_HIE] = { "hie", hmode, NULL, NULL, rmw_hie }, - [CSR_HCOUNTEREN] = { "hcounteren", hmode, read_hcounteren, write_hcounteren }, - [CSR_HGEIE] = { "hgeie", hmode, read_hgeie, write_hgeie }, - [CSR_HTVAL] = { "htval", hmode, read_htval, write_htval }, - [CSR_HTINST] = { "htinst", hmode, read_htinst, write_htinst }, - [CSR_HGEIP] = { "hgeip", hmode, read_hgeip, NULL }, - [CSR_HGATP] = { "hgatp", hmode, read_hgatp, write_hgatp }, - [CSR_HTIMEDELTA] = { "htimedelta", hmode, read_htimedelta, write_htimedelta }, - [CSR_HTIMEDELTAH] = { "htimedeltah", hmode32, read_htimedeltah, write_htimedeltah }, - - [CSR_VSSTATUS] = { "vsstatus", hmode, read_vsstatus, write_vsstatus }, - [CSR_VSIP] = { "vsip", hmode, NULL, NULL, rmw_vsip }, - [CSR_VSIE] = { "vsie", hmode, NULL, NULL, rmw_vsie }, - [CSR_VSTVEC] = { "vstvec", hmode, read_vstvec, write_vstvec }, - [CSR_VSSCRATCH] = { "vsscratch", hmode, read_vsscratch, write_vsscratch }, - [CSR_VSEPC] = { "vsepc", hmode, read_vsepc, write_vsepc }, - [CSR_VSCAUSE] = { "vscause", hmode, read_vscause, write_vscause }, - [CSR_VSTVAL] = { "vstval", hmode, read_vstval, write_vstval }, - [CSR_VSATP] = { "vsatp", hmode, read_vsatp, write_vsatp }, - - [CSR_MTVAL2] = { "mtval2", hmode, read_mtval2, write_mtval2 }, - [CSR_MTINST] = { "mtinst", hmode, read_mtinst, write_mtinst }, + [CSR_HSTATUS] = { "hstatus", hmode, read_hstatus, write_hstatus, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_HEDELEG] = { "hedeleg", hmode, read_hedeleg, write_hedeleg, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_HIDELEG] = { "hideleg", hmode, NULL, NULL, rmw_hideleg, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_HVIP] = { "hvip", hmode, NULL, NULL, rmw_hvip, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_HIP] = { "hip", hmode, NULL, NULL, rmw_hip, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_HIE] = { "hie", hmode, NULL, NULL, rmw_hie, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_HCOUNTEREN] = { "hcounteren", hmode, read_hcounteren, write_hcounteren, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_HGEIE] = { "hgeie", hmode, read_hgeie, write_hgeie, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_HTVAL] = { "htval", hmode, read_htval, write_htval, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_HTINST] = { "htinst", hmode, read_htinst, write_htinst, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_HGEIP] = { "hgeip", hmode, read_hgeip, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_HGATP] = { "hgatp", hmode, read_hgatp, write_hgatp, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_HTIMEDELTA] = { "htimedelta", hmode, read_htimedelta, write_htimedelta, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_HTIMEDELTAH] = { "htimedeltah", hmode32, read_htimedeltah, write_htimedeltah, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + + [CSR_VSSTATUS] = { "vsstatus", hmode, read_vsstatus, write_vsstatus, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_VSIP] = { "vsip", hmode, NULL, NULL, rmw_vsip, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_VSIE] = { "vsie", hmode, NULL, NULL, rmw_vsie , + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_VSTVEC] = { "vstvec", hmode, read_vstvec, write_vstvec, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_VSSCRATCH] = { "vsscratch", hmode, read_vsscratch, write_vsscratch, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_VSEPC] = { "vsepc", hmode, read_vsepc, write_vsepc, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_VSCAUSE] = { "vscause", hmode, read_vscause, write_vscause, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_VSTVAL] = { "vstval", hmode, read_vstval, write_vstval, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_VSATP] = { "vsatp", hmode, read_vsatp, write_vsatp, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + + [CSR_MTVAL2] = { "mtval2", hmode, read_mtval2, write_mtval2, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_MTINST] = { "mtinst", hmode, read_mtinst, write_mtinst, + .min_priv_ver = PRIV_VERSION_1_12_0 }, /* Virtual Interrupts and Interrupt Priorities (H-extension with AIA) */ [CSR_HVIEN] = { "hvien", aia_hmode, read_zero, write_ignore }, @@ -3245,7 +3446,8 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_VSIPH] = { "vsiph", aia_hmode32, NULL, NULL, rmw_vsiph }, /* Physical Memory Protection */ - [CSR_MSECCFG] = { "mseccfg", epmp, read_mseccfg, write_mseccfg }, + [CSR_MSECCFG] = { "mseccfg", epmp, read_mseccfg, write_mseccfg, + .min_priv_ver = PRIV_VERSION_1_12_0 }, [CSR_PMPCFG0] = { "pmpcfg0", pmp, read_pmpcfg, write_pmpcfg }, [CSR_PMPCFG1] = { "pmpcfg1", pmp, read_pmpcfg, write_pmpcfg }, [CSR_PMPCFG2] = { "pmpcfg2", pmp, read_pmpcfg, write_pmpcfg }, @@ -3267,6 +3469,12 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_PMPADDR14] = { "pmpaddr14", pmp, read_pmpaddr, write_pmpaddr }, [CSR_PMPADDR15] = { "pmpaddr15", pmp, read_pmpaddr, write_pmpaddr }, + /* Debug CSRs */ + [CSR_TSELECT] = { "tselect", debug, read_tselect, write_tselect }, + [CSR_TDATA1] = { "tdata1", debug, read_tdata, write_tdata }, + [CSR_TDATA2] = { "tdata2", debug, read_tdata, write_tdata }, + [CSR_TDATA3] = { "tdata3", debug, read_tdata, write_tdata }, + /* User Pointer Masking */ [CSR_UMTE] = { "umte", pointer_masking, read_umte, write_umte }, [CSR_UPMMASK] = { "upmmask", pointer_masking, read_upmmask, write_upmmask }, diff --git a/target/riscv/debug.c b/target/riscv/debug.c new file mode 100644 index 0000000000..2f2a51c732 --- /dev/null +++ b/target/riscv/debug.c @@ -0,0 +1,441 @@ +/* + * QEMU RISC-V Native Debug Support + * + * Copyright (c) 2022 Wind River Systems, Inc. + * + * Author: + * Bin Meng <bin.meng@windriver.com> + * + * This provides the native debug support via the Trigger Module, as defined + * in the RISC-V Debug Specification: + * https://github.com/riscv/riscv-debug-spec/raw/master/riscv-debug-stable.pdf + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qapi/error.h" +#include "cpu.h" +#include "trace.h" +#include "exec/exec-all.h" + +/* + * The following M-mode trigger CSRs are implemented: + * + * - tselect + * - tdata1 + * - tdata2 + * - tdata3 + * + * We don't support writable 'type' field in the tdata1 register, so there is + * no need to implement the "tinfo" CSR. + * + * The following triggers are implemented: + * + * Index | Type | tdata mapping | Description + * ------+------+------------------------+------------ + * 0 | 2 | tdata1, tdata2 | Address / Data Match + * 1 | 2 | tdata1, tdata2 | Address / Data Match + */ + +/* tdata availability of a trigger */ +typedef bool tdata_avail[TDATA_NUM]; + +static tdata_avail tdata_mapping[TRIGGER_NUM] = { + [TRIGGER_TYPE2_IDX_0 ... TRIGGER_TYPE2_IDX_1] = { true, true, false }, +}; + +/* only breakpoint size 1/2/4/8 supported */ +static int access_size[SIZE_NUM] = { + [SIZE_ANY] = 0, + [SIZE_1B] = 1, + [SIZE_2B] = 2, + [SIZE_4B] = 4, + [SIZE_6B] = -1, + [SIZE_8B] = 8, + [6 ... 15] = -1, +}; + +static inline target_ulong trigger_type(CPURISCVState *env, + trigger_type_t type) +{ + target_ulong tdata1; + + switch (riscv_cpu_mxl(env)) { + case MXL_RV32: + tdata1 = RV32_TYPE(type); + break; + case MXL_RV64: + tdata1 = RV64_TYPE(type); + break; + default: + g_assert_not_reached(); + } + + return tdata1; +} + +bool tdata_available(CPURISCVState *env, int tdata_index) +{ + if (unlikely(tdata_index >= TDATA_NUM)) { + return false; + } + + if (unlikely(env->trigger_cur >= TRIGGER_NUM)) { + return false; + } + + return tdata_mapping[env->trigger_cur][tdata_index]; +} + +target_ulong tselect_csr_read(CPURISCVState *env) +{ + return env->trigger_cur; +} + +void tselect_csr_write(CPURISCVState *env, target_ulong val) +{ + /* all target_ulong bits of tselect are implemented */ + env->trigger_cur = val; +} + +static target_ulong tdata1_validate(CPURISCVState *env, target_ulong val, + trigger_type_t t) +{ + uint32_t type, dmode; + target_ulong tdata1; + + switch (riscv_cpu_mxl(env)) { + case MXL_RV32: + type = extract32(val, 28, 4); + dmode = extract32(val, 27, 1); + tdata1 = RV32_TYPE(t); + break; + case MXL_RV64: + type = extract64(val, 60, 4); + dmode = extract64(val, 59, 1); + tdata1 = RV64_TYPE(t); + break; + default: + g_assert_not_reached(); + } + + if (type != t) { + qemu_log_mask(LOG_GUEST_ERROR, + "ignoring type write to tdata1 register\n"); + } + if (dmode != 0) { + qemu_log_mask(LOG_UNIMP, "debug mode is not supported\n"); + } + + return tdata1; +} + +static inline void warn_always_zero_bit(target_ulong val, target_ulong mask, + const char *msg) +{ + if (val & mask) { + qemu_log_mask(LOG_UNIMP, "%s bit is always zero\n", msg); + } +} + +static uint32_t type2_breakpoint_size(CPURISCVState *env, target_ulong ctrl) +{ + uint32_t size, sizelo, sizehi = 0; + + if (riscv_cpu_mxl(env) == MXL_RV64) { + sizehi = extract32(ctrl, 21, 2); + } + sizelo = extract32(ctrl, 16, 2); + size = (sizehi << 2) | sizelo; + + return size; +} + +static inline bool type2_breakpoint_enabled(target_ulong ctrl) +{ + bool mode = !!(ctrl & (TYPE2_U | TYPE2_S | TYPE2_M)); + bool rwx = !!(ctrl & (TYPE2_LOAD | TYPE2_STORE | TYPE2_EXEC)); + + return mode && rwx; +} + +static target_ulong type2_mcontrol_validate(CPURISCVState *env, + target_ulong ctrl) +{ + target_ulong val; + uint32_t size; + + /* validate the generic part first */ + val = tdata1_validate(env, ctrl, TRIGGER_TYPE_AD_MATCH); + + /* validate unimplemented (always zero) bits */ + warn_always_zero_bit(ctrl, TYPE2_MATCH, "match"); + warn_always_zero_bit(ctrl, TYPE2_CHAIN, "chain"); + warn_always_zero_bit(ctrl, TYPE2_ACTION, "action"); + warn_always_zero_bit(ctrl, TYPE2_TIMING, "timing"); + warn_always_zero_bit(ctrl, TYPE2_SELECT, "select"); + warn_always_zero_bit(ctrl, TYPE2_HIT, "hit"); + + /* validate size encoding */ + size = type2_breakpoint_size(env, ctrl); + if (access_size[size] == -1) { + qemu_log_mask(LOG_UNIMP, "access size %d is not supported, using SIZE_ANY\n", + size); + } else { + val |= (ctrl & TYPE2_SIZELO); + if (riscv_cpu_mxl(env) == MXL_RV64) { + val |= (ctrl & TYPE2_SIZEHI); + } + } + + /* keep the mode and attribute bits */ + val |= (ctrl & (TYPE2_U | TYPE2_S | TYPE2_M | + TYPE2_LOAD | TYPE2_STORE | TYPE2_EXEC)); + + return val; +} + +static void type2_breakpoint_insert(CPURISCVState *env, target_ulong index) +{ + target_ulong ctrl = env->type2_trig[index].mcontrol; + target_ulong addr = env->type2_trig[index].maddress; + bool enabled = type2_breakpoint_enabled(ctrl); + CPUState *cs = env_cpu(env); + int flags = BP_CPU | BP_STOP_BEFORE_ACCESS; + uint32_t size; + + if (!enabled) { + return; + } + + if (ctrl & TYPE2_EXEC) { + cpu_breakpoint_insert(cs, addr, flags, &env->type2_trig[index].bp); + } + + if (ctrl & TYPE2_LOAD) { + flags |= BP_MEM_READ; + } + if (ctrl & TYPE2_STORE) { + flags |= BP_MEM_WRITE; + } + + if (flags & BP_MEM_ACCESS) { + size = type2_breakpoint_size(env, ctrl); + if (size != 0) { + cpu_watchpoint_insert(cs, addr, size, flags, + &env->type2_trig[index].wp); + } else { + cpu_watchpoint_insert(cs, addr, 8, flags, + &env->type2_trig[index].wp); + } + } +} + +static void type2_breakpoint_remove(CPURISCVState *env, target_ulong index) +{ + CPUState *cs = env_cpu(env); + + if (env->type2_trig[index].bp) { + cpu_breakpoint_remove_by_ref(cs, env->type2_trig[index].bp); + env->type2_trig[index].bp = NULL; + } + + if (env->type2_trig[index].wp) { + cpu_watchpoint_remove_by_ref(cs, env->type2_trig[index].wp); + env->type2_trig[index].wp = NULL; + } +} + +static target_ulong type2_reg_read(CPURISCVState *env, + target_ulong trigger_index, int tdata_index) +{ + uint32_t index = trigger_index - TRIGGER_TYPE2_IDX_0; + target_ulong tdata; + + switch (tdata_index) { + case TDATA1: + tdata = env->type2_trig[index].mcontrol; + break; + case TDATA2: + tdata = env->type2_trig[index].maddress; + break; + default: + g_assert_not_reached(); + } + + return tdata; +} + +static void type2_reg_write(CPURISCVState *env, target_ulong trigger_index, + int tdata_index, target_ulong val) +{ + uint32_t index = trigger_index - TRIGGER_TYPE2_IDX_0; + target_ulong new_val; + + switch (tdata_index) { + case TDATA1: + new_val = type2_mcontrol_validate(env, val); + if (new_val != env->type2_trig[index].mcontrol) { + env->type2_trig[index].mcontrol = new_val; + type2_breakpoint_remove(env, index); + type2_breakpoint_insert(env, index); + } + break; + case TDATA2: + if (val != env->type2_trig[index].maddress) { + env->type2_trig[index].maddress = val; + type2_breakpoint_remove(env, index); + type2_breakpoint_insert(env, index); + } + break; + default: + g_assert_not_reached(); + } + + return; +} + +typedef target_ulong (*tdata_read_func)(CPURISCVState *env, + target_ulong trigger_index, + int tdata_index); + +static tdata_read_func trigger_read_funcs[TRIGGER_NUM] = { + [TRIGGER_TYPE2_IDX_0 ... TRIGGER_TYPE2_IDX_1] = type2_reg_read, +}; + +typedef void (*tdata_write_func)(CPURISCVState *env, + target_ulong trigger_index, + int tdata_index, + target_ulong val); + +static tdata_write_func trigger_write_funcs[TRIGGER_NUM] = { + [TRIGGER_TYPE2_IDX_0 ... TRIGGER_TYPE2_IDX_1] = type2_reg_write, +}; + +target_ulong tdata_csr_read(CPURISCVState *env, int tdata_index) +{ + tdata_read_func read_func = trigger_read_funcs[env->trigger_cur]; + + return read_func(env, env->trigger_cur, tdata_index); +} + +void tdata_csr_write(CPURISCVState *env, int tdata_index, target_ulong val) +{ + tdata_write_func write_func = trigger_write_funcs[env->trigger_cur]; + + return write_func(env, env->trigger_cur, tdata_index, val); +} + +void riscv_cpu_debug_excp_handler(CPUState *cs) +{ + RISCVCPU *cpu = RISCV_CPU(cs); + CPURISCVState *env = &cpu->env; + + if (cs->watchpoint_hit) { + if (cs->watchpoint_hit->flags & BP_CPU) { + cs->watchpoint_hit = NULL; + riscv_raise_exception(env, RISCV_EXCP_BREAKPOINT, 0); + } + } else { + if (cpu_breakpoint_test(cs, env->pc, BP_CPU)) { + riscv_raise_exception(env, RISCV_EXCP_BREAKPOINT, 0); + } + } +} + +bool riscv_cpu_debug_check_breakpoint(CPUState *cs) +{ + RISCVCPU *cpu = RISCV_CPU(cs); + CPURISCVState *env = &cpu->env; + CPUBreakpoint *bp; + target_ulong ctrl; + target_ulong pc; + int i; + + QTAILQ_FOREACH(bp, &cs->breakpoints, entry) { + for (i = 0; i < TRIGGER_TYPE2_NUM; i++) { + ctrl = env->type2_trig[i].mcontrol; + pc = env->type2_trig[i].maddress; + + if ((ctrl & TYPE2_EXEC) && (bp->pc == pc)) { + /* check U/S/M bit against current privilege level */ + if ((ctrl >> 3) & BIT(env->priv)) { + return true; + } + } + } + } + + return false; +} + +bool riscv_cpu_debug_check_watchpoint(CPUState *cs, CPUWatchpoint *wp) +{ + RISCVCPU *cpu = RISCV_CPU(cs); + CPURISCVState *env = &cpu->env; + target_ulong ctrl; + target_ulong addr; + int flags; + int i; + + for (i = 0; i < TRIGGER_TYPE2_NUM; i++) { + ctrl = env->type2_trig[i].mcontrol; + addr = env->type2_trig[i].maddress; + flags = 0; + + if (ctrl & TYPE2_LOAD) { + flags |= BP_MEM_READ; + } + if (ctrl & TYPE2_STORE) { + flags |= BP_MEM_WRITE; + } + + if ((wp->flags & flags) && (wp->vaddr == addr)) { + /* check U/S/M bit against current privilege level */ + if ((ctrl >> 3) & BIT(env->priv)) { + return true; + } + } + } + + return false; +} + +void riscv_trigger_init(CPURISCVState *env) +{ + target_ulong type2 = trigger_type(env, TRIGGER_TYPE_AD_MATCH); + int i; + + /* type 2 triggers */ + for (i = 0; i < TRIGGER_TYPE2_NUM; i++) { + /* + * type = TRIGGER_TYPE_AD_MATCH + * dmode = 0 (both debug and M-mode can write tdata) + * maskmax = 0 (unimplemented, always 0) + * sizehi = 0 (match against any size, RV64 only) + * hit = 0 (unimplemented, always 0) + * select = 0 (always 0, perform match on address) + * timing = 0 (always 0, trigger before instruction) + * sizelo = 0 (match against any size) + * action = 0 (always 0, raise a breakpoint exception) + * chain = 0 (unimplemented, always 0) + * match = 0 (always 0, when any compare value equals tdata2) + */ + env->type2_trig[i].mcontrol = type2; + env->type2_trig[i].maddress = 0; + env->type2_trig[i].bp = NULL; + env->type2_trig[i].wp = NULL; + } +} diff --git a/target/riscv/debug.h b/target/riscv/debug.h new file mode 100644 index 0000000000..27b9cac6b4 --- /dev/null +++ b/target/riscv/debug.h @@ -0,0 +1,114 @@ +/* + * QEMU RISC-V Native Debug Support + * + * Copyright (c) 2022 Wind River Systems, Inc. + * + * Author: + * Bin Meng <bin.meng@windriver.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef RISCV_DEBUG_H +#define RISCV_DEBUG_H + +/* trigger indexes implemented */ +enum { + TRIGGER_TYPE2_IDX_0 = 0, + TRIGGER_TYPE2_IDX_1, + TRIGGER_TYPE2_NUM, + TRIGGER_NUM = TRIGGER_TYPE2_NUM +}; + +/* register index of tdata CSRs */ +enum { + TDATA1 = 0, + TDATA2, + TDATA3, + TDATA_NUM +}; + +typedef enum { + TRIGGER_TYPE_NO_EXIST = 0, /* trigger does not exist */ + TRIGGER_TYPE_AD_MATCH = 2, /* address/data match trigger */ + TRIGGER_TYPE_INST_CNT = 3, /* instruction count trigger */ + TRIGGER_TYPE_INT = 4, /* interrupt trigger */ + TRIGGER_TYPE_EXCP = 5, /* exception trigger */ + TRIGGER_TYPE_AD_MATCH6 = 6, /* new address/data match trigger */ + TRIGGER_TYPE_EXT_SRC = 7, /* external source trigger */ + TRIGGER_TYPE_UNAVAIL = 15 /* trigger exists, but unavailable */ +} trigger_type_t; + +typedef struct { + target_ulong mcontrol; + target_ulong maddress; + struct CPUBreakpoint *bp; + struct CPUWatchpoint *wp; +} type2_trigger_t; + +/* tdata field masks */ + +#define RV32_TYPE(t) ((uint32_t)(t) << 28) +#define RV32_TYPE_MASK (0xf << 28) +#define RV32_DMODE BIT(27) +#define RV64_TYPE(t) ((uint64_t)(t) << 60) +#define RV64_TYPE_MASK (0xfULL << 60) +#define RV64_DMODE BIT_ULL(59) + +/* mcontrol field masks */ + +#define TYPE2_LOAD BIT(0) +#define TYPE2_STORE BIT(1) +#define TYPE2_EXEC BIT(2) +#define TYPE2_U BIT(3) +#define TYPE2_S BIT(4) +#define TYPE2_M BIT(6) +#define TYPE2_MATCH (0xf << 7) +#define TYPE2_CHAIN BIT(11) +#define TYPE2_ACTION (0xf << 12) +#define TYPE2_SIZELO (0x3 << 16) +#define TYPE2_TIMING BIT(18) +#define TYPE2_SELECT BIT(19) +#define TYPE2_HIT BIT(20) +#define TYPE2_SIZEHI (0x3 << 21) /* RV64 only */ + +/* access size */ +enum { + SIZE_ANY = 0, + SIZE_1B, + SIZE_2B, + SIZE_4B, + SIZE_6B, + SIZE_8B, + SIZE_10B, + SIZE_12B, + SIZE_14B, + SIZE_16B, + SIZE_NUM = 16 +}; + +bool tdata_available(CPURISCVState *env, int tdata_index); + +target_ulong tselect_csr_read(CPURISCVState *env); +void tselect_csr_write(CPURISCVState *env, target_ulong val); + +target_ulong tdata_csr_read(CPURISCVState *env, int tdata_index); +void tdata_csr_write(CPURISCVState *env, int tdata_index, target_ulong val); + +void riscv_cpu_debug_excp_handler(CPUState *cs); +bool riscv_cpu_debug_check_breakpoint(CPUState *cs); +bool riscv_cpu_debug_check_watchpoint(CPUState *cs, CPUWatchpoint *wp); + +void riscv_trigger_init(CPURISCVState *env); + +#endif /* RISCV_DEBUG_H */ diff --git a/target/riscv/helper.h b/target/riscv/helper.h index 26bbab2fab..a669d0187b 100644 --- a/target/riscv/helper.h +++ b/target/riscv/helper.h @@ -1086,10 +1086,7 @@ DEF_HELPER_6(vcompress_vm_h, void, ptr, ptr, ptr, ptr, env, i32) DEF_HELPER_6(vcompress_vm_w, void, ptr, ptr, ptr, ptr, env, i32) DEF_HELPER_6(vcompress_vm_d, void, ptr, ptr, ptr, ptr, env, i32) -DEF_HELPER_4(vmv1r_v, void, ptr, ptr, env, i32) -DEF_HELPER_4(vmv2r_v, void, ptr, ptr, env, i32) -DEF_HELPER_4(vmv4r_v, void, ptr, ptr, env, i32) -DEF_HELPER_4(vmv8r_v, void, ptr, ptr, env, i32) +DEF_HELPER_4(vmvr_v, void, ptr, ptr, env, i32) DEF_HELPER_5(vzext_vf2_h, void, ptr, ptr, ptr, env, i32) DEF_HELPER_5(vzext_vf2_w, void, ptr, ptr, ptr, env, i32) diff --git a/target/riscv/insn_trans/trans_rvv.c.inc b/target/riscv/insn_trans/trans_rvv.c.inc index 8d675db9a2..90327509f7 100644 --- a/target/riscv/insn_trans/trans_rvv.c.inc +++ b/target/riscv/insn_trans/trans_rvv.c.inc @@ -1198,7 +1198,7 @@ GEN_LDST_WHOLE_TRANS(vs8r_v, 8, true) static inline uint32_t MAXSZ(DisasContext *s) { int scale = s->lmul - 3; - return scale < 0 ? s->cfg_ptr->vlen >> -scale : s->cfg_ptr->vlen << scale; + return s->cfg_ptr->vlen >> -scale; } static bool opivv_check(DisasContext *s, arg_rmrr *a) @@ -3597,8 +3597,7 @@ static bool trans_vrgather_vx(DisasContext *s, arg_rmrr *a) if (a->vm && s->vl_eq_vlmax) { int scale = s->lmul - (s->sew + 3); - int vlmax = scale < 0 ? - s->cfg_ptr->vlen >> -scale : s->cfg_ptr->vlen << scale; + int vlmax = s->cfg_ptr->vlen >> -scale; TCGv_i64 dest = tcg_temp_new_i64(); if (a->rs1 == 0) { @@ -3630,8 +3629,7 @@ static bool trans_vrgather_vi(DisasContext *s, arg_rmrr *a) if (a->vm && s->vl_eq_vlmax) { int scale = s->lmul - (s->sew + 3); - int vlmax = scale < 0 ? - s->cfg_ptr->vlen >> -scale : s->cfg_ptr->vlen << scale; + int vlmax = s->cfg_ptr->vlen >> -scale; if (a->rs1 >= vlmax) { tcg_gen_gvec_dup_imm(MO_64, vreg_ofs(s, a->rd), MAXSZ(s), MAXSZ(s), 0); @@ -3697,7 +3695,7 @@ static bool trans_vcompress_vm(DisasContext *s, arg_r *a) * Whole Vector Register Move Instructions ignore vtype and vl setting. * Thus, we don't need to check vill bit. (Section 16.6) */ -#define GEN_VMV_WHOLE_TRANS(NAME, LEN, SEQ) \ +#define GEN_VMV_WHOLE_TRANS(NAME, LEN) \ static bool trans_##NAME(DisasContext *s, arg_##NAME * a) \ { \ if (require_rvv(s) && \ @@ -3712,13 +3710,8 @@ static bool trans_##NAME(DisasContext *s, arg_##NAME * a) \ } else { \ TCGLabel *over = gen_new_label(); \ tcg_gen_brcondi_tl(TCG_COND_GEU, cpu_vstart, maxsz, over); \ - \ - static gen_helper_gvec_2_ptr * const fns[4] = { \ - gen_helper_vmv1r_v, gen_helper_vmv2r_v, \ - gen_helper_vmv4r_v, gen_helper_vmv8r_v, \ - }; \ tcg_gen_gvec_2_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, a->rs2), \ - cpu_env, maxsz, maxsz, 0, fns[SEQ]); \ + cpu_env, maxsz, maxsz, 0, gen_helper_vmvr_v); \ mark_vs_dirty(s); \ gen_set_label(over); \ } \ @@ -3727,10 +3720,10 @@ static bool trans_##NAME(DisasContext *s, arg_##NAME * a) \ return false; \ } -GEN_VMV_WHOLE_TRANS(vmv1r_v, 1, 0) -GEN_VMV_WHOLE_TRANS(vmv2r_v, 2, 1) -GEN_VMV_WHOLE_TRANS(vmv4r_v, 4, 2) -GEN_VMV_WHOLE_TRANS(vmv8r_v, 8, 3) +GEN_VMV_WHOLE_TRANS(vmv1r_v, 1) +GEN_VMV_WHOLE_TRANS(vmv2r_v, 2) +GEN_VMV_WHOLE_TRANS(vmv4r_v, 4) +GEN_VMV_WHOLE_TRANS(vmv8r_v, 8) static bool int_ext_check(DisasContext *s, arg_rmr *a, uint8_t div) { diff --git a/target/riscv/machine.c b/target/riscv/machine.c index 5178b3fec9..2a437b29a1 100644 --- a/target/riscv/machine.c +++ b/target/riscv/machine.c @@ -216,7 +216,38 @@ static const VMStateDescription vmstate_kvmtimer = { VMSTATE_UINT64(env.kvm_timer_time, RISCVCPU), VMSTATE_UINT64(env.kvm_timer_compare, RISCVCPU), VMSTATE_UINT64(env.kvm_timer_state, RISCVCPU), + VMSTATE_END_OF_LIST() + } +}; + +static bool debug_needed(void *opaque) +{ + RISCVCPU *cpu = opaque; + CPURISCVState *env = &cpu->env; + + return riscv_feature(env, RISCV_FEATURE_DEBUG); +} + +static const VMStateDescription vmstate_debug_type2 = { + .name = "cpu/debug/type2", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINTTL(mcontrol, type2_trigger_t), + VMSTATE_UINTTL(maddress, type2_trigger_t), + VMSTATE_END_OF_LIST() + } +}; +static const VMStateDescription vmstate_debug = { + .name = "cpu/debug", + .version_id = 1, + .minimum_version_id = 1, + .needed = debug_needed, + .fields = (VMStateField[]) { + VMSTATE_UINTTL(env.trigger_cur, RISCVCPU), + VMSTATE_STRUCT_ARRAY(env.type2_trig, RISCVCPU, TRIGGER_TYPE2_NUM, + 0, vmstate_debug_type2, type2_trigger_t), VMSTATE_END_OF_LIST() } }; @@ -231,6 +262,28 @@ static int riscv_cpu_post_load(void *opaque, int version_id) return 0; } +static bool envcfg_needed(void *opaque) +{ + RISCVCPU *cpu = opaque; + CPURISCVState *env = &cpu->env; + + return (env->priv_ver >= PRIV_VERSION_1_12_0 ? 1 : 0); +} + +static const VMStateDescription vmstate_envcfg = { + .name = "cpu/envcfg", + .version_id = 1, + .minimum_version_id = 1, + .needed = envcfg_needed, + .fields = (VMStateField[]) { + VMSTATE_UINT64(env.menvcfg, RISCVCPU), + VMSTATE_UINTTL(env.senvcfg, RISCVCPU), + VMSTATE_UINT64(env.henvcfg, RISCVCPU), + + VMSTATE_END_OF_LIST() + } +}; + const VMStateDescription vmstate_riscv_cpu = { .name = "cpu", .version_id = 3, @@ -292,6 +345,8 @@ const VMStateDescription vmstate_riscv_cpu = { &vmstate_pointermasking, &vmstate_rv128, &vmstate_kvmtimer, + &vmstate_envcfg, + &vmstate_debug, NULL } }; diff --git a/target/riscv/meson.build b/target/riscv/meson.build index 91f0ac32ff..2c20f3dd8e 100644 --- a/target/riscv/meson.build +++ b/target/riscv/meson.build @@ -27,6 +27,7 @@ riscv_softmmu_ss = ss.source_set() riscv_softmmu_ss.add(files( 'arch_dump.c', 'pmp.c', + 'debug.c', 'monitor.c', 'machine.c' )) diff --git a/target/riscv/pmp.c b/target/riscv/pmp.c index 81b61bb65c..151da3fa08 100644 --- a/target/riscv/pmp.c +++ b/target/riscv/pmp.c @@ -141,17 +141,9 @@ static void pmp_decode_napot(target_ulong a, target_ulong *sa, target_ulong *ea) 0111...1111 2^(XLEN+2)-byte NAPOT range 1111...1111 Reserved */ - if (a == -1) { - *sa = 0u; - *ea = -1; - return; - } else { - target_ulong t1 = ctz64(~a); - target_ulong base = (a & ~(((target_ulong)1 << t1) - 1)) << 2; - target_ulong range = ((target_ulong)1 << (t1 + 3)) - 1; - *sa = base; - *ea = base + range; - } + a = (a << 2) | 0x3; + *sa = a & (a + 1); + *ea = a | (a + 1); } void pmp_update_rule_addr(CPURISCVState *env, uint32_t pmp_index) diff --git a/target/riscv/vector_helper.c b/target/riscv/vector_helper.c index 7a6ce0a3bc..576b14e5a3 100644 --- a/target/riscv/vector_helper.c +++ b/target/riscv/vector_helper.c @@ -4888,25 +4888,20 @@ GEN_VEXT_VCOMPRESS_VM(vcompress_vm_w, uint32_t, H4) GEN_VEXT_VCOMPRESS_VM(vcompress_vm_d, uint64_t, H8) /* Vector Whole Register Move */ -#define GEN_VEXT_VMV_WHOLE(NAME, LEN) \ -void HELPER(NAME)(void *vd, void *vs2, CPURISCVState *env, \ - uint32_t desc) \ -{ \ - /* EEW = 8 */ \ - uint32_t maxsz = simd_maxsz(desc); \ - uint32_t i = env->vstart; \ - \ - memcpy((uint8_t *)vd + H1(i), \ - (uint8_t *)vs2 + H1(i), \ - maxsz - env->vstart); \ - \ - env->vstart = 0; \ -} +void HELPER(vmvr_v)(void *vd, void *vs2, CPURISCVState *env, uint32_t desc) +{ + /* EEW = SEW */ + uint32_t maxsz = simd_maxsz(desc); + uint32_t sewb = 1 << FIELD_EX64(env->vtype, VTYPE, VSEW); + uint32_t startb = env->vstart * sewb; + uint32_t i = startb; + + memcpy((uint8_t *)vd + H1(i), + (uint8_t *)vs2 + H1(i), + maxsz - startb); -GEN_VEXT_VMV_WHOLE(vmv1r_v, 1) -GEN_VEXT_VMV_WHOLE(vmv2r_v, 2) -GEN_VEXT_VMV_WHOLE(vmv4r_v, 4) -GEN_VEXT_VMV_WHOLE(vmv8r_v, 8) + env->vstart = 0; +} /* Vector Integer Extension */ #define GEN_VEXT_INT_EXT(NAME, ETYPE, DTYPE, HD, HS1) \ diff --git a/target/s390x/tcg/translate.c b/target/s390x/tcg/translate.c index ae70368966..8f092dab95 100644 --- a/target/s390x/tcg/translate.c +++ b/target/s390x/tcg/translate.c @@ -2622,7 +2622,7 @@ static DisasJumpType op_icm(DisasContext *s, DisasOps *o) tcg_gen_qemu_ld8u(tmp, o->in2, get_mem_index(s)); tcg_gen_addi_i64(o->in2, o->in2, 1); tcg_gen_deposit_i64(o->out, o->out, tmp, pos, 8); - ccm |= 0xff << pos; + ccm |= 0xffull << pos; } m3 = (m3 << 1) & 0xf; pos -= 8; |