git: e72c41772352 - main - arm: Use the correct irq when in the hypervisor

From: Andrew Turner <andrew_at_FreeBSD.org>
Date: Fri, 17 May 2024 16:12:51 UTC
The branch main has been updated by andrew:

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

commit e72c41772352e1165711900c4f8df6b15b842ba8
Author:     Andrew Turner <andrew@FreeBSD.org>
AuthorDate: 2024-01-11 17:13:12 +0000
Commit:     Andrew Turner <andrew@FreeBSD.org>
CommitDate: 2024-05-17 16:07:16 +0000

    arm: Use the correct irq when in the hypervisor
    
    When booting in the hypervisor state we need to use the hypervisor
    interrupt in the generic timer. In this case the registers we access
    in the kernel are remapped to the EL2 versions, however this causes
    an unexpected interrupt to trigger.
    
    Sponsored by:   Arm Ltd
    Differential Revision:  https://reviews.freebsd.org/D43975
---
 sys/arm/arm/generic_timer.c | 25 ++++++++++++++++++-------
 1 file changed, 18 insertions(+), 7 deletions(-)

diff --git a/sys/arm/arm/generic_timer.c b/sys/arm/arm/generic_timer.c
index bad0939b6ab9..d3e264527a7f 100644
--- a/sys/arm/arm/generic_timer.c
+++ b/sys/arm/arm/generic_timer.c
@@ -171,12 +171,14 @@ static struct timecounter arm_tmr_timecount = {
 #define	set_el0(x, val)	cp15_## x ##_set(val)
 #define	set_el1(x, val)	cp15_## x ##_set(val)
 #define	HAS_PHYS	true
+#define	IN_VHE		false
 #else /* __aarch64__ */
 #define	get_el0(x)	READ_SPECIALREG(x ##_el0)
 #define	get_el1(x)	READ_SPECIALREG(x ##_el1)
 #define	set_el0(x, val)	WRITE_SPECIALREG(x ##_el0, val)
 #define	set_el1(x, val)	WRITE_SPECIALREG(x ##_el1, val)
 #define	HAS_PHYS	has_hyp()
+#define	IN_VHE		in_vhe()
 #endif
 
 static int
@@ -678,13 +680,22 @@ arm_tmr_attach(device_t dev)
 #endif
 
 #ifdef __aarch64__
-	/*
-	 * Use the virtual timer when we can't use the hypervisor.
-	 * A hypervisor guest may change the virtual timer registers while
-	 * executing so any use of the virtual timer interrupt needs to be
-	 * coordinated with the virtual machine manager.
-	 */
-	if (!HAS_PHYS) {
+	if (IN_VHE) {
+		/*
+		 * The kernel is running at EL2. The EL0 timer registers are
+		 * re-mapped to the EL2 version. Because of this we need to
+		 * use the EL2 interrupt.
+		 */
+		sc->physical_sys = true;
+		first_timer = GT_HYP_PHYS;
+		last_timer = GT_HYP_PHYS;
+	} else if (!HAS_PHYS) {
+		/*
+		 * Use the virtual timer when we can't use the hypervisor.
+		 * A hypervisor guest may change the virtual timer registers
+		 * while executing so any use of the virtual timer interrupt
+		 * needs to be coordinated with the virtual machine manager.
+		 */
 		sc->physical_sys = false;
 		first_timer = GT_VIRT;
 		last_timer = GT_VIRT;