git: 278111b8a541 - main - Allow the kernel to emulate the physical counter on arm64

From: Andrew Turner <andrew_at_FreeBSD.org>
Date: Thu, 25 Aug 2022 11:17:39 UTC
The branch main has been updated by andrew:

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

commit 278111b8a54141bdaf4c4a045539db21febf053f
Author:     Andrew Turner <andrew@FreeBSD.org>
AuthorDate: 2022-06-27 12:37:40 +0000
Commit:     Andrew Turner <andrew@FreeBSD.org>
CommitDate: 2022-08-25 11:17:23 +0000

    Allow the kernel to emulate the physical counter on arm64
    
    When running under a VM we don't have access to the physical counter.
    Add support to emulate this instruction by handling the trap in the
    kernel. As it is slow only enable when the hw.emulate_phys_counter
    tunable is set on boot.
    
    Sponsored by:   The FreeBSD Foundation
    Differential Revision: https://reviews.freebsd.org/D35613
---
 sys/arm/arm/generic_timer.c | 46 ++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 45 insertions(+), 1 deletion(-)

diff --git a/sys/arm/arm/generic_timer.c b/sys/arm/arm/generic_timer.c
index 0e0e8a53bcce..0c242a31d9a7 100644
--- a/sys/arm/arm/generic_timer.c
+++ b/sys/arm/arm/generic_timer.c
@@ -63,6 +63,10 @@ __FBSDID("$FreeBSD$");
 #include <machine/machdep.h> /* For arm_set_delay */
 #endif
 
+#if defined(__aarch64__)
+#include <machine/undefined.h>
+#endif
+
 #ifdef FDT
 #include <dev/ofw/openfirm.h>
 #include <dev/ofw/ofw_bus.h>
@@ -235,12 +239,52 @@ setup_user_access(void *arg __unused)
 	isb();
 }
 
+#ifdef __aarch64__
+static int
+cntpct_handler(vm_offset_t va, uint32_t insn, struct trapframe *frame,
+    uint32_t esr)
+{
+	uint64_t val;
+	int reg;
+
+	if ((insn & MRS_MASK) != MRS_VALUE)
+		return (0);
+
+	if (MRS_SPECIAL(insn) != MRS_SPECIAL(CNTPCT_EL0))
+		return (0);
+
+	reg = MRS_REGISTER(insn);
+	val = READ_SPECIALREG(cntvct_el0);
+	if (reg < nitems(frame->tf_x)) {
+		frame->tf_x[reg] = val;
+	} else if (reg == 30) {
+		frame->tf_lr = val;
+	}
+
+	/*
+	 * We will handle this instruction, move to the next so we
+	 * don't trap here again.
+	 */
+	frame->tf_elr += INSN_SIZE;
+
+	return (1);
+}
+#endif
+
 static void
 tmr_setup_user_access(void *arg __unused)
 {
+	int emulate;
 
-	if (arm_tmr_sc != NULL)
+	if (arm_tmr_sc != NULL) {
 		smp_rendezvous(NULL, setup_user_access, NULL, NULL);
+#ifdef __aarch64__
+		if (TUNABLE_INT_FETCH("hw.emulate_phys_counter", &emulate) &&
+		    emulate != 0) {
+			install_undef_handler(true, cntpct_handler);
+		}
+#endif
+	}
 }
 SYSINIT(tmr_ua, SI_SUB_SMP, SI_ORDER_ANY, tmr_setup_user_access, NULL);