From: Nick Owens Subject: riscv64: use sstc for clocks when available To: tech@openbsd.org Date: Mon, 13 Jul 2026 09:18:20 -0700 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 #include +#include #include #include @@ -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