@@ -118,6 +118,11 @@ enum {
* ARM pseudocode function CheckSMEAccess().
*/
ARM_CP_SME = 1 << 19,
+ /*
+ * Flag: one of the four EL2 registers which redirect to the
+ * equivalent EL1 register when FEAT_NV2 is enabled.
+ */
+ ARM_CP_NV2_REDIRECT = 1 << 20,
};
/*
@@ -3239,6 +3239,8 @@ FIELD(TBFLAG_A64, TRAP_ERET, 29, 1)
FIELD(TBFLAG_A64, NAA, 30, 1)
FIELD(TBFLAG_A64, ATA0, 31, 1)
FIELD(TBFLAG_A64, NV, 32, 1)
+FIELD(TBFLAG_A64, NV1, 33, 1)
+FIELD(TBFLAG_A64, NV2, 34, 1)
/*
* Helpers for using the above. Note that only the A64 accessors use
@@ -146,6 +146,10 @@ typedef struct DisasContext {
bool naa;
/* True if FEAT_NV HCR_EL2.NV is enabled */
bool nv;
+ /* True if NV enabled and HCR_EL2.NV1 is set */
+ bool nv1;
+ /* True if NV enabled and HCR_EL2.NV2 is set */
+ bool nv2;
/*
* >= 0, a copy of PSTATE.BTYPE, which will be 0 without v8.5-BTI.
* < 0, set by the current instruction.
@@ -6135,14 +6135,16 @@ static const ARMCPRegInfo el2_cp_reginfo[] = {
.opc0 = 3, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 7,
.access = PL2_RW, .type = ARM_CP_CONST, .resetvalue = 0 },
{ .name = "ELR_EL2", .state = ARM_CP_STATE_AA64,
- .type = ARM_CP_ALIAS,
+ .type = ARM_CP_ALIAS | ARM_CP_NV2_REDIRECT,
.opc0 = 3, .opc1 = 4, .crn = 4, .crm = 0, .opc2 = 1,
.access = PL2_RW,
.fieldoffset = offsetof(CPUARMState, elr_el[2]) },
{ .name = "ESR_EL2", .state = ARM_CP_STATE_BOTH,
+ .type = ARM_CP_NV2_REDIRECT,
.opc0 = 3, .opc1 = 4, .crn = 5, .crm = 2, .opc2 = 0,
.access = PL2_RW, .fieldoffset = offsetof(CPUARMState, cp15.esr_el[2]) },
{ .name = "FAR_EL2", .state = ARM_CP_STATE_BOTH,
+ .type = ARM_CP_NV2_REDIRECT,
.opc0 = 3, .opc1 = 4, .crn = 6, .crm = 0, .opc2 = 0,
.access = PL2_RW, .fieldoffset = offsetof(CPUARMState, cp15.far_el[2]) },
{ .name = "HIFAR", .state = ARM_CP_STATE_AA32,
@@ -6151,7 +6153,7 @@ static const ARMCPRegInfo el2_cp_reginfo[] = {
.access = PL2_RW,
.fieldoffset = offsetofhigh32(CPUARMState, cp15.far_el[2]) },
{ .name = "SPSR_EL2", .state = ARM_CP_STATE_AA64,
- .type = ARM_CP_ALIAS,
+ .type = ARM_CP_ALIAS | ARM_CP_NV2_REDIRECT,
.opc0 = 3, .opc1 = 4, .crn = 4, .crm = 0, .opc2 = 0,
.access = PL2_RW,
.fieldoffset = offsetof(CPUARMState, banked_spsr[BANK_HYP]) },
@@ -7876,11 +7878,13 @@ static CPAccessResult access_tfsr_el2(CPUARMState *env, const ARMCPRegInfo *ri,
/*
* TFSR_EL2: similar to generic access_mte(), but we need to
* account for FEAT_NV. At EL1 this must be a FEAT_NV access;
- * we will trap to EL2 and the HCR/SCR traps do not apply.
+ * if NV2 is enabled then we will redirect this to TFSR_EL1
+ * after doing the HCR and SCR ATA traps; otherwise this will
+ * be a trap to EL2 and the HCR/SCR traps do not apply.
*/
int el = arm_current_el(env);
- if (el == 1) {
+ if (el == 1 && (arm_hcr_el2_eff(env) & HCR_NV2)) {
return CP_ACCESS_OK;
}
if (el < 2 && arm_is_el2_enabled(env)) {
@@ -7917,6 +7921,7 @@ static const ARMCPRegInfo mte_reginfo[] = {
.access = PL1_RW, .accessfn = access_tfsr_el1,
.fieldoffset = offsetof(CPUARMState, cp15.tfsr_el[1]) },
{ .name = "TFSR_EL2", .state = ARM_CP_STATE_AA64,
+ .type = ARM_CP_NV2_REDIRECT,
.opc0 = 3, .opc1 = 4, .crn = 5, .crm = 6, .opc2 = 0,
.access = PL2_RW, .accessfn = access_tfsr_el2,
.fieldoffset = offsetof(CPUARMState, cp15.tfsr_el[2]) },
@@ -302,6 +302,12 @@ static CPUARMTBFlags rebuild_hflags_a64(CPUARMState *env, int el, int fp_el,
if (el == 1 && (hcr & HCR_NV)) {
DP_TBFLAG_A64(flags, TRAP_ERET, 1);
DP_TBFLAG_A64(flags, NV, 1);
+ if (hcr & HCR_NV1) {
+ DP_TBFLAG_A64(flags, NV1, 1);
+ }
+ if (hcr & HCR_NV2) {
+ DP_TBFLAG_A64(flags, NV2, 1);
+ }
}
if (cpu_isar_feature(aa64_mte, env_archcpu(env))) {
@@ -2133,6 +2133,7 @@ static void handle_sys(DisasContext *s, bool isread,
const ARMCPRegInfo *ri = get_arm_cp_reginfo(s->cp_regs, key);
bool need_exit_tb = false;
bool nv_trap_to_el2 = false;
+ bool nv_redirect_reg = false;
bool skip_fp_access_checks = false;
TCGv_ptr tcg_ri = NULL;
TCGv_i64 tcg_rt;
@@ -2174,7 +2175,14 @@ static void handle_sys(DisasContext *s, bool isread,
* for registers accessible at EL1).
*/
skip_fp_access_checks = true;
- if (s->nv && arm_cpreg_traps_in_nv(ri)) {
+ if (s->nv2 && (ri->type & ARM_CP_NV2_REDIRECT)) {
+ /*
+ * This is one of the few EL2 registers which should redirect
+ * to the equivalent EL1 register. We do that after running
+ * the EL2 register's accessfn.
+ */
+ nv_redirect_reg = true;
+ } else if (s->nv && arm_cpreg_traps_in_nv(ri)) {
/*
* This register / instruction exists and is an EL2 register, so
* we must trap to EL2 if accessed in nested virtualization EL1
@@ -2226,6 +2234,27 @@ static void handle_sys(DisasContext *s, bool isread,
return;
}
+ if (nv_redirect_reg) {
+ /*
+ * FEAT_NV2 redirection of an EL2 register to an EL1 register.
+ * Conveniently in all cases the encoding of the EL1 register is
+ * identical to the EL2 register except that opc1 is 0.
+ * Get the reginfo for the EL1 register to use for the actual access.
+ * We don't use the EL1 register's access function, and
+ * fine-grained-traps on EL1 also do not apply here.
+ */
+ key = ENCODE_AA64_CP_REG(CP_REG_ARM64_SYSREG_CP,
+ crn, crm, op0, 0, op2);
+ ri = get_arm_cp_reginfo(s->cp_regs, key);
+ assert(ri);
+ assert(cp_access_ok(s->current_el, ri, isread));
+ /*
+ * We might not have done an update_pc earlier, so check we don't
+ * need it. We could support this in future if necessary.
+ */
+ assert(!(ri->type & ARM_CP_RAISES_EXC));
+ }
+
/* Handle special cases first */
switch (ri->type & ARM_CP_SPECIAL_MASK) {
case 0:
@@ -14032,6 +14061,8 @@ static void aarch64_tr_init_disas_context(DisasContextBase *dcbase,
dc->sme_trap_nonstreaming = EX_TBFLAG_A64(tb_flags, SME_TRAP_NONSTREAMING);
dc->naa = EX_TBFLAG_A64(tb_flags, NAA);
dc->nv = EX_TBFLAG_A64(tb_flags, NV);
+ dc->nv1 = EX_TBFLAG_A64(tb_flags, NV1);
+ dc->nv2 = EX_TBFLAG_A64(tb_flags, NV2);
dc->vec_len = 0;
dc->vec_stride = 0;
dc->cp_regs = arm_cpu->cp_regs;