CPAccessResult access_tvm_trvm(CPUARMState *, const ARMCPRegInfo *, bool);
+/**
+ * arm_cpreg_trap_in_nv: Return true if cpreg traps in nested virtualization
+ *
+ * Return true if this cpreg is one which should be trapped to EL2 if
+ * it is executed at EL1 when nested virtualization is enabled via HCR_EL2.NV.
+ */
+static inline bool arm_cpreg_traps_in_nv(const ARMCPRegInfo *ri)
+{
+ /*
+ * The Arm ARM defines the registers to be trapped in terms of
+ * their names (I_TZTZL). However the underlying principle is "if
+ * it would UNDEF at EL1 but work at EL2 then it should trap", and
+ * the way the encoding of sysregs and system instructions is done
+ * means that the right set of registers is exactly those where
+ * the opc1 field is 4 or 5. (You can see this also in the assert
+ * we do that the opc1 field and the permissions mask line up in
+ * define_one_arm_cp_reg_with_opaque().)
+ * Checking the opc1 field is easier for us and avoids the problem
+ * that we do not consistently use the right architectural names
+ * for all sysregs, since we treat the name field as largely for debug.
+ *
+ * However we do this check, it is going to be at least potentially
+ * fragile to future new sysregs, but this seems the least likely
+ * to break.
+ *
+ * In particular, note that the released sysreg XML defines that
+ * the FEAT_MEC sysregs and instructions do not follow this FEAT_NV
+ * trapping rule, so we will need to add an ARM_CP_* flag to indicate
+ * "register does not trap on NV" to handle those if/when we implement
+ * FEAT_MEC.
+ */
+ return ri->opc1 == 4 || ri->opc1 == 5;
+}
+
#endif /* TARGET_ARM_CPREGS_H */
crn, crm, op0, op1, op2);
const ARMCPRegInfo *ri = get_arm_cp_reginfo(s->cp_regs, key);
bool need_exit_tb = false;
+ bool nv_trap_to_el2 = false;
+ bool skip_fp_access_checks = false;
TCGv_ptr tcg_ri = NULL;
TCGv_i64 tcg_rt;
- uint32_t syndrome;
+ uint32_t syndrome = syn_aa64_sysregtrap(op0, op1, op2, crn, crm, rt, isread);
if (crn == 11 || crn == 15) {
/*
* Check for TIDCP trap, which must take precedence over
* the UNDEF for "no such register" etc.
*/
- syndrome = syn_aa64_sysregtrap(op0, op1, op2, crn, crm, rt, isread);
switch (s->current_el) {
case 0:
if (dc_isar_feature(aa64_tidcp1, s)) {
/* Check access permissions */
if (!cp_access_ok(s->current_el, ri, isread)) {
- gen_sysreg_undef(s, isread, op0, op1, op2, crn, crm, rt);
- return;
+ /*
+ * FEAT_NV/NV2 handling does not do the usual FP access checks
+ * for registers only accessible at EL2 (though it *does* do them
+ * for registers accessible at EL1).
+ */
+ skip_fp_access_checks = true;
+ 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
+ * instead of UNDEFing. We'll do that after the usual access checks.
+ * (This makes a difference only for a couple of registers like
+ * VSTTBR_EL2 where the "UNDEF if NonSecure" should take priority
+ * over the trap-to-EL2. Most trapped-by-FEAT_NV registers have
+ * an accessfn which does nothing when called from EL1, because
+ * the trap-to-EL3 controls which would apply to that register
+ * at EL2 don't take priority over the FEAT_NV trap-to-EL2.)
+ */
+ nv_trap_to_el2 = true;
+ } else {
+ gen_sysreg_undef(s, isread, op0, op1, op2, crn, crm, rt);
+ return;
+ }
}
if (ri->accessfn || (ri->fgt && s->fgt_active)) {
/* Emit code to perform further access permissions checks at
* runtime; this may result in an exception.
*/
- syndrome = syn_aa64_sysregtrap(op0, op1, op2, crn, crm, rt, isread);
gen_a64_update_pc(s, 0);
tcg_ri = tcg_temp_new_ptr();
gen_helper_access_check_cp_reg(tcg_ri, tcg_env,
gen_a64_update_pc(s, 0);
}
- if ((ri->type & ARM_CP_FPU) && !fp_access_check_only(s)) {
- return;
- } else if ((ri->type & ARM_CP_SVE) && !sve_access_check(s)) {
- return;
- } else if ((ri->type & ARM_CP_SME) && !sme_access_check(s)) {
+ if (!skip_fp_access_checks) {
+ if ((ri->type & ARM_CP_FPU) && !fp_access_check_only(s)) {
+ return;
+ } else if ((ri->type & ARM_CP_SVE) && !sve_access_check(s)) {
+ return;
+ } else if ((ri->type & ARM_CP_SME) && !sme_access_check(s)) {
+ return;
+ }
+ }
+
+ if (nv_trap_to_el2) {
+ gen_exception_insn_el(s, 0, EXCP_UDEF, syndrome, 2);
return;
}
dc->pstate_za = EX_TBFLAG_A64(tb_flags, PSTATE_ZA);
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->vec_len = 0;
dc->vec_stride = 0;
dc->cp_regs = arm_cpu->cp_regs;