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 <oleksii.kurochko@gmail.com>
---
Changes in v2:
- Drop domain_vtimer_init() as it does nothing.
- Drop "struct vcpu *v;" from struct vtimer as it could be taken
from arch_vcpu using container_of().
- Drop vtimer_initialized, use t->status == TIMER_STATUS_invalid
instead to understand if timer was or wasn't initialized.
- Drop inclusion of xen/domain.h as xen/sched.h already includes it.
- s/ xen/time.h/ xen.timer.h in vtimer.c.
- Drop ULL in if-conidtion in vtimer_set_timer() as with the cast
it isn't necessary to have suffix ULL.
- Add migrate timer to vtimer_set_timer() to be sure that vtimer
will occur on pCPU it was ran, so the signalling to that vCPU
will (commonly) be cheaper.
- Check if the timeout has already expired and just inject the event
in vtimer_vtimer_set_timer().
- Drop const for ticks argument of vtimer_set_timer().
- Merge two patches to one:
- xen/riscv: introduce vtimer
- xen/riscv: introduce vtimer_set_timer() and vtimer_expired()
---
xen/arch/riscv/Makefile | 1 +
xen/arch/riscv/domain.c | 8 +++-
xen/arch/riscv/include/asm/domain.h | 3 ++
xen/arch/riscv/include/asm/vtimer.h | 20 ++++++++
xen/arch/riscv/vtimer.c | 73 +++++++++++++++++++++++++++++
5 files changed, 103 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 8863d4b15605..5bd180130165 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 c078d595df9c..e38c0db62cac 100644
--- a/xen/arch/riscv/domain.c
+++ b/xen/arch/riscv/domain.c
@@ -9,6 +9,7 @@
#include <asm/cpufeature.h>
#include <asm/csr.h>
#include <asm/riscv_encoding.h>
+#include <asm/vtimer.h>
#define HEDELEG_DEFAULT (BIT(CAUSE_MISALIGNED_FETCH, U) | \
BIT(CAUSE_FETCH_ACCESS, U) | \
@@ -111,11 +112,14 @@ int arch_vcpu_create(struct vcpu *v)
if ( is_idle_vcpu(v) )
return rc;
+ 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;
diff --git a/xen/arch/riscv/include/asm/domain.h b/xen/arch/riscv/include/asm/domain.h
index fa083094b43e..482429d4ef33 100644
--- a/xen/arch/riscv/include/asm/domain.h
+++ b/xen/arch/riscv/include/asm/domain.h
@@ -8,6 +8,7 @@
#include <public/hvm/params.h>
#include <asm/p2m.h>
+#include <asm/vtimer.h>
struct vcpu_vmid {
uint64_t generation;
@@ -51,6 +52,8 @@ struct arch_vcpu
struct cpu_info *cpu_info;
+ struct vtimer vtimer;
+
/* CSRs */
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 000000000000..0d1555511755
--- /dev/null
+++ b/xen/arch/riscv/include/asm/vtimer.h
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * (c) 2023-2024 Vates
+ */
+
+#ifndef ASM__RISCV__VTIMER_H
+#define ASM__RISCV__VTIMER_H
+
+#include <xen/timer.h>
+
+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 000000000000..b6599fa383b8
--- /dev/null
+++ b/xen/arch/riscv/vtimer.c
@@ -0,0 +1,73 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <xen/sched.h>
+#include <xen/timer.h>
+
+#include <asm/vtimer.h>
+
+static void vtimer_expired(void *data)
+{
+ struct vtimer *t = data;
+ struct arch_vcpu *avcpu = container_of(t, struct arch_vcpu, vtimer);
+ struct vcpu *v = container_of(avcpu, struct vcpu, arch);
+
+ 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 arch_vcpu *avcpu = container_of(t, struct arch_vcpu, vtimer);
+ struct vcpu *v = container_of(avcpu, struct vcpu, arch);
+ 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);
+}
--
2.52.0