git: 67242cc4284e - main - arm64/vmm: vtimer: Read CNTPCT_EL0 through a dedicated macro

From: Andrew Turner <andrew_at_FreeBSD.org>
Date: Thu, 17 Sep 2026 09:56:06 UTC
The branch main has been updated by andrew:

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

commit 67242cc4284e44936068945d9960c8f1fef136c1
Author:     Kajetan Puchalski <kajetan.puchalski@arm.com>
AuthorDate: 2026-05-12 16:12:04 +0000
Commit:     Andrew Turner <andrew@FreeBSD.org>
CommitDate: 2026-09-17 09:20:22 +0000

    arm64/vmm: vtimer: Read CNTPCT_EL0 through a dedicated macro
    
    CNTPCT_EL0 needs to be read after an ISB in order to ensure a consistent
    value irrespective of speculative execution.
    
    Make existing reads of CNTPCT_EL0 use a new dedicated accessor macro
    that expands to the correct instruction sequence.
    
    Signed-off-by: Kajetan Puchalski <kajetan.puchalski@arm.com>
    Reviewed by:    andrew
    Sponsored by:   Arm Ltd
    Pull Request:   https://github.com/freebsd/freebsd-src/pull/2423
---
 sys/arm64/include/_armreg.h |  7 +++++++
 sys/arm64/vmm/io/vtimer.c   | 14 +++++++-------
 sys/arm64/vmm/vmm_arm64.c   |  2 +-
 3 files changed, 15 insertions(+), 8 deletions(-)

diff --git a/sys/arm64/include/_armreg.h b/sys/arm64/include/_armreg.h
index 0ade9e6168c9..e00a2b2033af 100644
--- a/sys/arm64/include/_armreg.h
+++ b/sys/arm64/include/_armreg.h
@@ -80,6 +80,13 @@
 	_val;								\
 })
 
+/* TODO: Use CNTPCTSS when FEAT_ECV is available */
+#define	READ_CNTPCT()							\
+({	uint64_t _val;							\
+	__asm __volatile("isb\nmrs	%0, cntpct_el0" : "=&r" (_val));\
+	_val;								\
+})
+
 #define	UL(x)	UINT64_C(x)
 
 #endif /* !_MACHINE__ARMREG_H_ */
diff --git a/sys/arm64/vmm/io/vtimer.c b/sys/arm64/vmm/io/vtimer.c
index 509e52947524..00767d3b59b5 100644
--- a/sys/arm64/vmm/io/vtimer.c
+++ b/sys/arm64/vmm/io/vtimer.c
@@ -96,7 +96,7 @@ vtimer_virtual_timer_intr(void *arg)
 		goto out;
 	}
 
-	cntpct_el0 = READ_SPECIALREG(cntpct_el0) -
+	cntpct_el0 = READ_CNTPCT() -
 	    hypctx_read_sys_reg(hypctx, HOST_CNTVOFF_EL2);
 	if (hypctx->vtimer_cpu.virt_timer.cntx_cval_el0 < cntpct_el0)
 		vgic_inject_irq(hypctx->hyp, vcpu_vcpuid(hypctx->vcpu),
@@ -279,7 +279,7 @@ vtimer_sync_hwstate(struct hypctx *hypctx)
 {
 	uint64_t cntpct_el0;
 
-	cntpct_el0 = READ_SPECIALREG(cntpct_el0) -
+	cntpct_el0 = READ_CNTPCT() -
 	    hypctx_read_sys_reg(hypctx, HOST_CNTVOFF_EL2);
 	vtime_sync_timer(hypctx, &hypctx->vtimer_cpu.virt_timer, cntpct_el0);
 	/* If FEAT_ECV_POFF is in use then we need to sync the physical timer */
@@ -321,7 +321,7 @@ vtimer_schedule_irq(struct hypctx *hypctx, bool phys)
 		timer = &hypctx->vtimer_cpu.phys_timer;
 	else
 		timer = &hypctx->vtimer_cpu.virt_timer;
-	cntpct_el0 = READ_SPECIALREG(cntpct_el0) -
+	cntpct_el0 = READ_CNTPCT() -
 	    hypctx_read_sys_reg(hypctx, HOST_CNTVOFF_EL2);
 	if (timer->cntx_cval_el0 < cntpct_el0) {
 		/* Timer set in the past, trigger interrupt */
@@ -379,7 +379,7 @@ vtimer_phys_ctl_read(struct vcpu *vcpu, uint64_t *rval, void *arg)
 	hypctx = vcpu_get_cookie(vcpu);
 	vtimer_cpu = &hypctx->vtimer_cpu;
 
-	cntpct_el0 = READ_SPECIALREG(cntpct_el0) - hypctx_read_sys_reg(hypctx, HOST_CNTVOFF_EL2);
+	cntpct_el0 = READ_CNTPCT() - hypctx_read_sys_reg(hypctx, HOST_CNTVOFF_EL2);
 	if (vtimer_cpu->phys_timer.cntx_cval_el0 < cntpct_el0)
 		/* Timer condition met */
 		*rval = vtimer_cpu->phys_timer.cntx_ctl_el0 | CNTP_CTL_ISTATUS;
@@ -422,7 +422,7 @@ vtimer_phys_cnt_read(struct vcpu *vcpu, uint64_t *rval, void *arg)
 	struct hypctx *hypctx;
 
 	hypctx = vcpu_get_cookie(vcpu);
-	*rval = READ_SPECIALREG(cntpct_el0) - hypctx_read_sys_reg(hypctx, HOST_CNTVOFF_EL2);
+	*rval = READ_CNTPCT() - hypctx_read_sys_reg(hypctx, HOST_CNTVOFF_EL2);
 	return (0);
 }
 
@@ -484,7 +484,7 @@ vtimer_phys_tval_read(struct vcpu *vcpu, uint64_t *rval, void *arg)
 		 */
 		*rval = (uint32_t)RES1;
 	} else {
-		cntpct_el0 = READ_SPECIALREG(cntpct_el0) -
+		cntpct_el0 = READ_CNTPCT() -
 		    hypctx_read_sys_reg(hypctx, HOST_CNTVOFF_EL2);
 		*rval = vtimer_cpu->phys_timer.cntx_cval_el0 - cntpct_el0;
 	}
@@ -502,7 +502,7 @@ vtimer_phys_tval_write(struct vcpu *vcpu, uint64_t wval, void *arg)
 	hypctx = vcpu_get_cookie(vcpu);
 	vtimer_cpu = &hypctx->vtimer_cpu;
 
-	cntpct_el0 = READ_SPECIALREG(cntpct_el0) - hypctx_read_sys_reg(hypctx, HOST_CNTVOFF_EL2);
+	cntpct_el0 = READ_CNTPCT() - hypctx_read_sys_reg(hypctx, HOST_CNTVOFF_EL2);
 	vtimer_cpu->phys_timer.cntx_cval_el0 = (int32_t)wval + cntpct_el0;
 
 	vtimer_remove_irq(hypctx, vcpu);
diff --git a/sys/arm64/vmm/vmm_arm64.c b/sys/arm64/vmm/vmm_arm64.c
index e2d667575706..8b405b49fc84 100644
--- a/sys/arm64/vmm/vmm_arm64.c
+++ b/sys/arm64/vmm/vmm_arm64.c
@@ -542,7 +542,7 @@ vmmops_init(struct vm *vm, pmap_t pmap)
 	if (ID_AA64MMFR1_HCX_VAL(idreg) >= ID_AA64MMFR1_HCX_IMPL)
 		hyp->feats |= HYP_FEAT_HCX;
 
-	hyp->cntvoff_el2 = READ_SPECIALREG(cntpct_el0);
+	hyp->cntvoff_el2 = READ_CNTPCT();
 	vgic_vminit(hyp);
 
 	if (!in_vhe())