From 2473ddf9b4f991859353a2a9641ff19af1dffd93 Mon Sep 17 00:00:00 2001 From: Oleksii Kurochko Date: Fri, 6 Mar 2026 17:33:22 +0100 Subject: [PATCH] xen/riscv: introduce basic vtimer infrastructure for guests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Lay the groundwork for guest timer support by introducing a per-vCPU virtual timer backed by Xen’s common timer infrastructure. The virtual timer is programmed in response to the guest SBI sbi_set_timer() call and injects a virtual supervisor timer interrupt into the vCPU when it expires. While a dedicated struct vtimer is not strictly required at present, it is expected to become necessary once SSTC support is introduced. In particular, it will need to carry additional state such as whether SSTC is enabled, the next compare value (e.g. for the VSTIMECMP CSR) to be saved and restored across context switches, and time delta state (e.g. HTIMEDELTA) required for use cases such as migration. Introducing struct vtimer now avoids a later refactoring. Signed-off-by: Oleksii Kurochko Acked-by: Jan Beulich --- xen/arch/riscv/Makefile | 1 + xen/arch/riscv/domain.c | 10 +++- xen/arch/riscv/include/asm/domain.h | 3 ++ xen/arch/riscv/include/asm/vtimer.h | 17 +++++++ xen/arch/riscv/vtimer.c | 71 +++++++++++++++++++++++++++++ 5 files changed, 100 insertions(+), 2 deletions(-) create mode 100644 xen/arch/riscv/include/asm/vtimer.h create mode 100644 xen/arch/riscv/vtimer.c diff --git a/xen/arch/riscv/Makefile b/xen/arch/riscv/Makefile index bc47e83b26..ffbd7062e2 100644 --- a/xen/arch/riscv/Makefile +++ b/xen/arch/riscv/Makefile @@ -22,6 +22,7 @@ obj-y += traps.o obj-y += vmid.o obj-y += vm_event.o obj-y += vsbi/ +obj-y += vtimer.o $(TARGET): $(TARGET)-syms $(OBJCOPY) -O binary -S $< $@ diff --git a/xen/arch/riscv/domain.c b/xen/arch/riscv/domain.c index 9c86581ea2..d25938200f 100644 --- a/xen/arch/riscv/domain.c +++ b/xen/arch/riscv/domain.c @@ -10,6 +10,7 @@ #include #include #include +#include struct csr_masks { register_t hedeleg; @@ -148,11 +149,14 @@ int arch_vcpu_create(struct vcpu *v) vcpu_csr_init(v); + if ( (rc = vcpu_vtimer_init(v)) ) + goto fail; + /* - * As the vtimer and interrupt controller (IC) are not yet implemented, + * As interrupt controller (IC) is not yet implemented, * return an error. * - * TODO: Drop this once the vtimer and IC are implemented. + * TODO: Drop this once IC is implemented. */ rc = -EOPNOTSUPP; goto fail; @@ -166,6 +170,8 @@ int arch_vcpu_create(struct vcpu *v) void arch_vcpu_destroy(struct vcpu *v) { + vcpu_timer_destroy(v); + vfree((void *)&v->arch.cpu_info[1] - STACK_SIZE); } diff --git a/xen/arch/riscv/include/asm/domain.h b/xen/arch/riscv/include/asm/domain.h index 59d23e4f92..6c48bf1311 100644 --- a/xen/arch/riscv/include/asm/domain.h +++ b/xen/arch/riscv/include/asm/domain.h @@ -8,6 +8,7 @@ #include #include +#include struct vcpu_vmid { uint64_t generation; @@ -49,6 +50,8 @@ struct arch_vcpu { struct cpu_info *cpu_info; + struct vtimer vtimer; + register_t hcounteren; register_t hedeleg; register_t hideleg; diff --git a/xen/arch/riscv/include/asm/vtimer.h b/xen/arch/riscv/include/asm/vtimer.h new file mode 100644 index 0000000000..111863610a --- /dev/null +++ b/xen/arch/riscv/include/asm/vtimer.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef ASM__RISCV__VTIMER_H +#define ASM__RISCV__VTIMER_H + +#include + +struct vtimer { + struct timer timer; +}; + +int vcpu_vtimer_init(struct vcpu *v); +void vcpu_timer_destroy(struct vcpu *v); + +void vtimer_set_timer(struct vtimer *t, uint64_t ticks); + +#endif /* ASM__RISCV__VTIMER_H */ diff --git a/xen/arch/riscv/vtimer.c b/xen/arch/riscv/vtimer.c new file mode 100644 index 0000000000..32d142bcdf --- /dev/null +++ b/xen/arch/riscv/vtimer.c @@ -0,0 +1,71 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include +#include + +#include + +static void vtimer_expired(void *data) +{ + struct vtimer *t = data; + struct vcpu *v = container_of(t, struct vcpu, arch.vtimer); + + vcpu_set_interrupt(v, IRQ_VS_TIMER); +} + +int vcpu_vtimer_init(struct vcpu *v) +{ + struct vtimer *t = &v->arch.vtimer; + + init_timer(&t->timer, vtimer_expired, t, v->processor); + + return 0; +} + +void vcpu_timer_destroy(struct vcpu *v) +{ + struct vtimer *t = &v->arch.vtimer; + + if ( t->timer.status == TIMER_STATUS_invalid ) + return; + + kill_timer(&v->arch.vtimer.timer); +} + +void vtimer_set_timer(struct vtimer *t, const uint64_t ticks) +{ + struct vcpu *v = container_of(t, struct vcpu, arch.vtimer); + s_time_t expires = ticks_to_ns(ticks - boot_clock_cycles); + + vcpu_unset_interrupt(v, IRQ_VS_TIMER); + + /* + * According to the RISC-V sbi spec: + * If the supervisor wishes to clear the timer interrupt without + * scheduling the next timer event, it can either request a timer + * interrupt infinitely far into the future (i.e., (uint64_t)-1), + * or it can instead mask the timer interrupt by clearing sie.STIE CSR + * bit. + */ + if ( ticks == ((uint64_t)~0) ) + { + stop_timer(&t->timer); + + return; + } + + if ( expires < NOW() ) + { + /* + * Simplify the logic if the timeout has already expired and just + * inject the event. + */ + stop_timer(&t->timer); + vcpu_set_interrupt(v, IRQ_VS_TIMER); + + return; + } + + migrate_timer(&t->timer, smp_processor_id()); + set_timer(&t->timer, expires); +}