From: Jason Wright <[email protected]> Add an .accessfn to the RNDR and RNDRRS system registers that traps reads to EL3 when SCR_EL3.TRNDR is set, as required by FEAT_RNG_TRAP. Mark SCR_EL3.TRNDR (bit 40) as a writable field in scr_write() when the CPU advertises the feature. The pseudocode in DDI0487 revision M.b shows the trap firing from EL0, EL1, EL2, and EL3, so there is no check of arm_current_el().
When FEAT_RNG_TRAP is implemented without FEAT_RNG, an RNDR/RNDRRS read with SCR_EL3.TRNDR=0 should UNDEF rather than succeed; handle that case in access_rndr(). Register the rndr_reginfo CP reg entries whenever either FEAT_RNG or FEAT_RNG_TRAP is implemented, so the accessfn fires even on a FEAT_RNG_TRAP-only CPU. When SCR_EL3.TRNDR is set, ID_AA64ISAR0_EL1.RNDR reads as 1 regardless of whether FEAT_RNG is implemented; give ID_AA64ISAR0_EL1 a readfn so it reports this at runtime, as we already do for ID_AA64PFR0_EL1. Suggested-by: Richard Henderson <[email protected]> Suggested-by: Peter Maydell <[email protected]> Signed-off-by: Jason Wright <[email protected]> Reviewed-by: Richard Henderson <[email protected]> Signed-off-by: Peter Maydell <[email protected]> --- target/arm/cpu-features.h | 5 ++++ target/arm/helper.c | 58 +++++++++++++++++++++++++++++++++++---- 2 files changed, 58 insertions(+), 5 deletions(-) diff --git a/target/arm/cpu-features.h b/target/arm/cpu-features.h index 4e8d844fea..38a695ded7 100644 --- a/target/arm/cpu-features.h +++ b/target/arm/cpu-features.h @@ -908,6 +908,11 @@ static inline bool isar_feature_aa64_rndr(const ARMISARegisters *id) return FIELD_EX64_IDREG(id, ID_AA64ISAR0, RNDR) != 0; } +static inline bool isar_feature_aa64_rng_trap(const ARMISARegisters *id) +{ + return FIELD_EX64_IDREG(id, ID_AA64PFR1, RNDR_TRAP) != 0; +} + static inline bool isar_feature_aa64_tlbirange(const ARMISARegisters *id) { return FIELD_EX64_IDREG(id, ID_AA64ISAR0, TLB) == 2; diff --git a/target/arm/helper.c b/target/arm/helper.c index 34487eeaa3..9dd8fdfa41 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -790,6 +790,9 @@ static void scr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) if (cpu_isar_feature(aa64_fpmr, cpu)) { valid_mask |= SCR_ENFPM; } + if (cpu_isar_feature(aa64_rng_trap, cpu)) { + valid_mask |= SCR_TRNDR; + } } else { valid_mask &= ~(SCR_RW | SCR_ST); if (cpu_isar_feature(aa32_ras, cpu)) { @@ -5170,6 +5173,21 @@ static uint64_t id_aa64pfr0_read(CPUARMState *env, const ARMCPRegInfo *ri) } return pfr0; } + +static uint64_t id_aa64isar0_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + ARMCPU *cpu = env_archcpu(env); + uint64_t isar0 = GET_IDREG(&cpu->isar, ID_AA64ISAR0); + + /* + * When FEAT_RNG_TRAP is active (SCR_EL3.TRNDR set), ID_AA64ISAR0_EL1.RNDR + * reads as 1 regardless of whether FEAT_RNG is implemented. + */ + if (env->cp15.scr_el3 & SCR_TRNDR) { + isar0 = FIELD_DP64(isar0, ID_AA64ISAR0, RNDR, 1); + } + return isar0; +} #endif /* @@ -5304,6 +5322,22 @@ static const ARMCPRegInfo pauth_reginfo[] = { .fieldoffset = offsetof(CPUARMState, keys.apib.hi) }, }; +static CPAccessResult access_rndr(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + if (env->cp15.scr_el3 & SCR_TRNDR) { + return CP_ACCESS_TRAP_EL3; + } + /* + * Note that FEAT_RNG_TRAP may be implemented without FEAT_RNG. + * In that case, if the trap is not enabled, the read undefs. + */ + if (!cpu_isar_feature(aa64_rndr, env_archcpu(env))) { + return CP_ACCESS_UNDEFINED; + } + return CP_ACCESS_OK; +} + static uint64_t rndr_readfn(CPUARMState *env, const ARMCPRegInfo *ri) { Error *err = NULL; @@ -5335,11 +5369,11 @@ static const ARMCPRegInfo rndr_reginfo[] = { { .name = "RNDR", .state = ARM_CP_STATE_AA64, .type = ARM_CP_NO_RAW | ARM_CP_SUPPRESS_TB_END | ARM_CP_IO, .opc0 = 3, .opc1 = 3, .crn = 2, .crm = 4, .opc2 = 0, - .access = PL0_R, .readfn = rndr_readfn }, + .access = PL0_R, .accessfn = access_rndr, .readfn = rndr_readfn }, { .name = "RNDRRS", .state = ARM_CP_STATE_AA64, .type = ARM_CP_NO_RAW | ARM_CP_SUPPRESS_TB_END | ARM_CP_IO, .opc0 = 3, .opc1 = 3, .crn = 2, .crm = 4, .opc2 = 1, - .access = PL0_R, .readfn = rndr_readfn }, + .access = PL0_R, .accessfn = access_rndr, .readfn = rndr_readfn }, }; static void dccvap_writefn(CPUARMState *env, const ARMCPRegInfo *ri, @@ -6522,11 +6556,24 @@ void register_cp_regs_for_features(ARMCPU *cpu) .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_tid3, .resetvalue = 0 }, + /* + * ID_AA64ISAR0_EL1 is not a plain ARM_CP_CONST in system + * emulation because the RNDR field depends on SCR_EL3.TRNDR + * at read time when FEAT_RNG_TRAP is implemented. + */ { .name = "ID_AA64ISAR0_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 6, .opc2 = 0, - .access = PL1_R, .type = ARM_CP_CONST, + .access = PL1_R, +#ifdef CONFIG_USER_ONLY + .type = ARM_CP_CONST, + .resetvalue = GET_IDREG(isar, ID_AA64ISAR0) +#else + .type = ARM_CP_NO_RAW, .accessfn = access_tid3, - .resetvalue = GET_IDREG(isar, ID_AA64ISAR0)}, + .readfn = id_aa64isar0_read, + .writefn = arm_cp_write_ignore +#endif + }, { .name = "ID_AA64ISAR1_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 6, .opc2 = 1, .access = PL1_R, .type = ARM_CP_CONST, @@ -7454,7 +7501,8 @@ void register_cp_regs_for_features(ARMCPU *cpu) if (cpu_isar_feature(aa64_pauth, cpu)) { define_arm_cp_regs(cpu, pauth_reginfo); } - if (cpu_isar_feature(aa64_rndr, cpu)) { + if (cpu_isar_feature(aa64_rndr, cpu) || + cpu_isar_feature(aa64_rng_trap, cpu)) { define_arm_cp_regs(cpu, rndr_reginfo); } /* Data Cache clean instructions up to PoP */ -- 2.43.0
