Index | Thread | Search

From:
Nick Owens <mischief@offblast.org>
Subject:
riscv64: use sstc for clocks when available
To:
tech@openbsd.org
Date:
Mon, 13 Jul 2026 09:18:20 -0700

Download raw body.

Thread
  • Nick Owens:

    riscv64: use sstc for clocks when available

hi,

this implements support for sstc extension. with sstc, we can write the
stimecmp csr directly instead of needing an sbi ecall to set the timer
comparison register.

both the k1 and k3 support it, and since it's a direct register write it
takes only ~10ns instead of several hundred ns for the ecall and
possible hypervisor emulation in the case of linux kvm.

i've tested on orangepi rv2 (k1) directly, and on the k3 under linux
kvm. fallback path tested on kvm by disabling sstc in the advertised
features of qemu kvm as well. without sstc, the k1 takes about 250ns and
the k3 + kvm takes about 600ns. with this change, both are around 10ns
per timer write.

this also conveniently works around a bug in upstream kvm where timer
writes via sbi ecall can get lost if there's a context switch at the
wrong time, which makes kvm guest timers permanently stop firing. see
https://lore.kernel.org/all/20260609183921.42043-1-jsun@junsun.net/

diff --git a/sys/arch/riscv64/riscv64/clock.c b/sys/arch/riscv64/riscv64/clock.c
index f9775297737..66e300560bc 100644
--- a/sys/arch/riscv64/riscv64/clock.c
+++ b/sys/arch/riscv64/riscv64/clock.c
@@ -25,6 +25,7 @@
 #include <sys/stdint.h>
 #include <sys/timetc.h>
 
+#include <machine/elf.h>
 #include <machine/cpufunc.h>
 #include <machine/sbi.h>
 
@@ -68,13 +69,20 @@ timer_rearm(void *unused, uint64_t nsecs)
 	if (nsecs > timer_nsec_max)
 		nsecs = timer_nsec_max;
 	cycles = (nsecs * timer_nsec_cycle_ratio) >> 32;
-	sbi_set_timer(rdtime() + cycles);
+
+	if (riscv_hwcap & HWCAP_ISA_SSTC)
+		csr_write(stimecmp, rdtime() + cycles);
+	else
+		sbi_set_timer(rdtime() + cycles);
 }
 
 void
 timer_trigger(void *unused)
 {
-	sbi_set_timer(0);
+	if (riscv_hwcap & HWCAP_ISA_SSTC)
+		csr_write(stimecmp, 0);
+	else
+		sbi_set_timer(0);
 }
 
 u_int
@@ -125,7 +133,11 @@ clock_intr(void *frame)
 	struct cpu_info *ci = curcpu();
 	int s;
 
-	sbi_set_timer(UINT64_MAX);	/* clear timer interrupt */
+	/* clear timer interrupt */
+	if (riscv_hwcap & HWCAP_ISA_SSTC)
+		csr_write(stimecmp, UINT64_MAX);
+	else
+		sbi_set_timer(UINT64_MAX);
 
 	/*
 	 * If the clock interrupt is masked, defer all clock interrupt