git: 09ea88fde992 - stable/15 - arm64/vmm: Add a feature flag and use it for HCRX

From: Andrew Turner <andrew_at_FreeBSD.org>
Date: Tue, 13 Jan 2026 16:27:16 UTC
The branch stable/15 has been updated by andrew:

URL: https://cgit.FreeBSD.org/src/commit/?id=09ea88fde992cfe52ac9e03e36e0088a98ebd44c

commit 09ea88fde992cfe52ac9e03e36e0088a98ebd44c
Author:     Andrew Turner <andrew@FreeBSD.org>
AuthorDate: 2025-09-22 17:07:57 +0000
Commit:     Andrew Turner <andrew@FreeBSD.org>
CommitDate: 2026-01-13 14:06:18 +0000

    arm64/vmm: Add a feature flag and use it for HCRX
    
    Add a field to hold the features the hardware supports that need to be
    handled when switching to a guest and use it to handle FEAT_HCX that
    adds the HRX_EL2 register.
    
    This reduces the number of times we read ID registers in guest
    switching which may be trapped when running under nested virtualisation.
    
    Sponsored by:   Arm Ltd
    Differential Revision:  https://reviews.freebsd.org/D51816
    
    (cherry picked from commit 0f455824d0abdcf09d2e96cf97f99c542bbde877)
---
 sys/arm64/vmm/arm64.h     |  2 ++
 sys/arm64/vmm/vmm_arm64.c |  6 ++++++
 sys/arm64/vmm/vmm_hyp.c   | 28 +++++++++++++++-------------
 3 files changed, 23 insertions(+), 13 deletions(-)

diff --git a/sys/arm64/vmm/arm64.h b/sys/arm64/vmm/arm64.h
index 20cb3c312df5..c0bcbe822ec0 100644
--- a/sys/arm64/vmm/arm64.h
+++ b/sys/arm64/vmm/arm64.h
@@ -128,6 +128,8 @@ struct hyp {
 	uint64_t	vmid_generation;
 	uint64_t	vttbr_el2;
 	uint64_t	el2_addr;	/* The address of this in el2 space */
+	uint64_t	feats;		/* Which features are enabled */
+#define	HYP_FEAT_HCX		(0x1ul << 0)
 	bool		vgic_attached;
 	struct vgic_v3	*vgic;
 	struct hypctx	*ctx[];
diff --git a/sys/arm64/vmm/vmm_arm64.c b/sys/arm64/vmm/vmm_arm64.c
index e4deb052d396..8e3d8d5220ec 100644
--- a/sys/arm64/vmm/vmm_arm64.c
+++ b/sys/arm64/vmm/vmm_arm64.c
@@ -517,6 +517,7 @@ vmmops_init(struct vm *vm, pmap_t pmap)
 {
 	struct hyp *hyp;
 	vm_size_t size;
+	uint64_t idreg;
 
 	size = el2_hyp_size(vm);
 	hyp = malloc_aligned(size, PAGE_SIZE, M_HYP, M_WAITOK | M_ZERO);
@@ -524,6 +525,11 @@ vmmops_init(struct vm *vm, pmap_t pmap)
 	hyp->vm = vm;
 	hyp->vgic_attached = false;
 
+	if (get_kernel_reg(ID_AA64MMFR1_EL1, &idreg)) {
+		if (ID_AA64MMFR1_HCX_VAL(idreg) >= ID_AA64MMFR1_HCX_IMPL)
+			hyp->feats |= HYP_FEAT_HCX;
+	}
+
 	vtimer_vminit(hyp);
 	vgic_vminit(hyp);
 
diff --git a/sys/arm64/vmm/vmm_hyp.c b/sys/arm64/vmm/vmm_hyp.c
index 636247c48080..eb066e23363b 100644
--- a/sys/arm64/vmm/vmm_hyp.c
+++ b/sys/arm64/vmm/vmm_hyp.c
@@ -264,14 +264,6 @@ vmm_hyp_reg_store(struct hypctx *hypctx, struct hyp *hyp, bool guest)
 	hypctx->hcr_el2 = READ_SPECIALREG(hcr_el2);
 	hypctx->vpidr_el2 = READ_SPECIALREG(vpidr_el2);
 	hypctx->vmpidr_el2 = READ_SPECIALREG(vmpidr_el2);
-
-#ifndef VMM_VHE
-	/* hcrx_el2 depends on feat_hcx */
-	uint64_t mmfr1 = READ_SPECIALREG(id_aa64mmfr1_el1);
-	if (ID_AA64MMFR1_HCX_VAL(mmfr1) >> ID_AA64MMFR1_HCX_SHIFT) {
-		hypctx->hcrx_el2 = READ_SPECIALREG(MRS_REG_ALT_NAME(HCRX_EL2));
-	}
-#endif
 }
 
 static void
@@ -282,11 +274,9 @@ vmm_hyp_reg_restore(struct hypctx *hypctx, struct hyp *hyp, bool guest)
 	/* Restore the special registers */
 	WRITE_SPECIALREG(hcr_el2, hypctx->hcr_el2);
 
-	if (guest_or_nonvhe(guest)) {
-		uint64_t mmfr1 = READ_SPECIALREG(id_aa64mmfr1_el1);
-		if (ID_AA64MMFR1_HCX_VAL(mmfr1) >> ID_AA64MMFR1_HCX_SHIFT) {
-			WRITE_SPECIALREG(MRS_REG_ALT_NAME(HCRX_EL2), hypctx->hcrx_el2);
-		}
+	if (guest) {
+		if ((hyp->feats & HYP_FEAT_HCX) != 0)
+			WRITE_SPECIALREG(HCRX_EL2_REG, hypctx->hcrx_el2);
 	}
 	isb();
 
@@ -513,11 +503,18 @@ vmm_hyp_call_guest(struct hyp *hyp, struct hypctx *hypctx)
 	struct hypctx host_hypctx;
 	uint64_t cntvoff_el2;
 	uint64_t ich_hcr_el2, ich_vmcr_el2, cnthctl_el2, cntkctl_el1;
+#ifndef VMM_VHE
+	uint64_t hcrx_el2;
+#endif
 	uint64_t ret;
 	uint64_t s1e1r, hpfar_el2;
 	bool hpfar_valid;
 
 	vmm_hyp_reg_store(&host_hypctx, NULL, false);
+#ifndef VMM_VHE
+	if ((hyp->feats & HYP_FEAT_HCX) != 0)
+		hcrx_el2 = READ_SPECIALREG(MRS_REG_ALT_NAME(HCRX_EL2));
+#endif
 
 	/* Save the host special registers */
 	cnthctl_el2 = READ_SPECIALREG(cnthctl_el2);
@@ -595,6 +592,11 @@ vmm_hyp_call_guest(struct hyp *hyp, struct hypctx *hypctx)
 
 	vmm_hyp_reg_restore(&host_hypctx, NULL, false);
 
+#ifndef VMM_VHE
+	if ((hyp->feats & HYP_FEAT_HCX) != 0)
+		WRITE_SPECIALREG(MRS_REG_ALT_NAME(HCRX_EL2), hcrx_el2);
+#endif
+
 	/* Restore the host special registers */
 	WRITE_SPECIALREG(ich_hcr_el2, ich_hcr_el2);
 	WRITE_SPECIALREG(ich_vmcr_el2, ich_vmcr_el2);