git: 32111003c308 - main - arm64/vmm: save and restore HCRX_EL2 register

From: Andrew Turner <andrew_at_FreeBSD.org>
Date: Tue, 04 Feb 2025 12:26:34 UTC
The branch main has been updated by andrew:

URL: https://cgit.FreeBSD.org/src/commit/?id=32111003c3087389cc5e50949ee3a26c4e7b26c4

commit 32111003c3087389cc5e50949ee3a26c4e7b26c4
Author:     Harry Moulton <harry.moulton@arm.com>
AuthorDate: 2025-01-23 12:40:05 +0000
Commit:     Andrew Turner <andrew@FreeBSD.org>
CommitDate: 2025-02-04 11:24:41 +0000

    arm64/vmm: save and restore HCRX_EL2 register
    
    With the addition of the Extended Hypervisor Configuration Register
    (HCRX_EL2), this change ensures that it is both set to 0 before entering
    a vm, and that it is properly saved/restored.
    
    Reviewed by:    andrew
    Sponsored by:   Arm Ltd
    Differential Revision:  https://reviews.freebsd.org/D48584
    Signed-off-by: Harry Moulton <harry.moulton@arm.com>
---
 sys/arm64/vmm/arm64.h     |  1 +
 sys/arm64/vmm/vmm_hyp.c   | 15 +++++++++++++++
 sys/arm64/vmm/vmm_reset.c |  2 ++
 3 files changed, 18 insertions(+)

diff --git a/sys/arm64/vmm/arm64.h b/sys/arm64/vmm/arm64.h
index 8cfe77dcde6f..6a0c4c78e568 100644
--- a/sys/arm64/vmm/arm64.h
+++ b/sys/arm64/vmm/arm64.h
@@ -94,6 +94,7 @@ struct hypctx {
 	/* EL2 control registers */
 	uint64_t	cptr_el2;	/* Architectural Feature Trap Register */
 	uint64_t	hcr_el2;	/* Hypervisor Configuration Register */
+	uint64_t	hcrx_el2;	/* Extended Hypervisor Configuration Register */
 	uint64_t	mdcr_el2;	/* Monitor Debug Configuration Register */
 	uint64_t	vpidr_el2;	/* Virtualization Processor ID Register */
 	uint64_t	vmpidr_el2;	/* Virtualization Multiprocessor ID Register */
diff --git a/sys/arm64/vmm/vmm_hyp.c b/sys/arm64/vmm/vmm_hyp.c
index bd119c80139b..d61885c15871 100644
--- a/sys/arm64/vmm/vmm_hyp.c
+++ b/sys/arm64/vmm/vmm_hyp.c
@@ -259,6 +259,14 @@ 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
@@ -268,6 +276,13 @@ 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);
+		}
+	}
 	isb();
 
 	WRITE_SPECIALREG(sp_el0, hypctx->sp_el0);
diff --git a/sys/arm64/vmm/vmm_reset.c b/sys/arm64/vmm/vmm_reset.c
index 8ccb83e7a0ea..79d022cf33e8 100644
--- a/sys/arm64/vmm/vmm_reset.c
+++ b/sys/arm64/vmm/vmm_reset.c
@@ -140,6 +140,8 @@ reset_vm_el2_regs(void *vcpu)
 		el2ctx->hcr_el2 |= HCR_E2H;
 	}
 
+	/* Set the Extended Hypervisor Configuration Register */
+	el2ctx->hcrx_el2 = 0;
 	/* TODO: Trap all extensions we don't support */
 	el2ctx->mdcr_el2 = 0;
 	/* PMCR_EL0.N is read from MDCR_EL2.HPMN */