aboutsummaryrefslogtreecommitdiff
path: root/target/arm/tcg/translate-a64.c
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2024-01-09 14:43:53 +0000
committerPeter Maydell <peter.maydell@linaro.org>2024-01-09 14:43:53 +0000
commitc35da11df40678d606064f75a7c2d747efa1b302 (patch)
tree83c17e8123d596ba71b490fccaa357df11c5d50a /target/arm/tcg/translate-a64.c
parentef8a4a8816d166102545e3404a00c4e50b4496ee (diff)
target/arm: Handle FEAT_NV2 redirection of SPSR_EL2, ELR_EL2, ESR_EL2, FAR_EL2
Under FEAT_NV2, when HCR_EL2.{NV,NV2} == 0b11 at EL1, accesses to the registers SPSR_EL2, ELR_EL2, ESR_EL2, FAR_EL2 and TFSR_EL2 (which would UNDEF without FEAT_NV or FEAT_NV2) should instead access the equivalent EL1 registers SPSR_EL1, ELR_EL1, ESR_EL1, FAR_EL1 and TFSR_EL1. Because there are only five registers involved and the encoding for the EL1 register is identical to that of the EL2 register except that opc1 is 0, we handle this by finding the EL1 register in the hash table and using it instead. Note that traps that apply to direct accesses to the EL1 register, such as active fine-grained traps or other trap bits, do not trigger when it is accessed via the EL2 encoding in this way. However, some traps that are defined by the EL2 register may apply. We therefore call the EL2 register's accessfn first. The only one of the five which has such traps is TFSR_EL2: make sure its accessfn correctly handles both FEAT_NV (where we trap to EL2 without checking ATA bits) and FEAT_NV2 (where we check ATA bits and then redirect to TFSR_EL1). (We don't need the NV1 tbflag bit until the next patch, but we introduce it here to avoid putting the NV, NV1, NV2 bits in an odd order.) Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Tested-by: Miguel Luis <miguel.luis@oracle.com>
Diffstat (limited to 'target/arm/tcg/translate-a64.c')
-rw-r--r--target/arm/tcg/translate-a64.c33
1 files changed, 32 insertions, 1 deletions
diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c
index ed1cc019a4..2ada5b7e3f 100644
--- a/target/arm/tcg/translate-a64.c
+++ b/target/arm/tcg/translate-a64.c
@@ -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;