From ad535118f0f1e5cb4b1dfb2580efcfe728717194 Mon Sep 17 00:00:00 2001 From: Oleksii Kurochko Date: Wed, 18 Feb 2026 10:16:34 +0100 Subject: [PATCH] xen/riscv: implement arch_vcpu_{create,destroy}() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce architecture-specific functions to create and destroy VCPUs. Note that arch_vcpu_create() currently returns -EOPNOTSUPP, as the virtual timer and interrupt controller are not yet implemented. Add callee-saved registers to struct arch_vcpu which are used to preserve Xen’s own execution context when switching between vCPU stacks. It is going to be used in the following way (pseudocode): context_switch(prev_vcpu, next_vcpu): ... /* Switch from previous stack to the next stack. */ __context_switch(prev_vcpu, next_vcpu); ... schedule_tail(prev_vcpu): Save and restore vCPU's CSRs. The Xen-saved context allows __context_switch() to switch execution from the previous vCPU’s stack to the next vCPU’s stack and later resume execution on the original stack when switching back. During vCPU creation, the Xen-saved context is going to be initialized with: - SP pointing to the newly allocated vCPU stack. - RA pointing to a helper that performs final vCPU setup before transferring control to the guest. As part of this change, add continue_new_vcpu(), which will be used after the first context_switch() of a new vCPU. Since this functionality is not yet implemented, continue_new_vcpu() is currently provided as a stub. The prev argument is going to be set by RISC-V ABI (prev will be stored in a0) when __context_swtich() will be introduced and called from context_switch(). Update the STACK_SIZE definition. Introduce struct cpu_info to store per-vCPU state that lives at the top of the vCPU's stack. Signed-off-by: Oleksii Kurochko Acked-by: Jan Beulich --- xen/arch/riscv/Makefile | 1 + xen/arch/riscv/domain.c | 58 ++++++++++++++++++++++++++++ xen/arch/riscv/include/asm/config.h | 2 +- xen/arch/riscv/include/asm/current.h | 6 +++ xen/arch/riscv/include/asm/domain.h | 24 ++++++++++++ xen/arch/riscv/stubs.c | 10 ----- 6 files changed, 90 insertions(+), 11 deletions(-) create mode 100644 xen/arch/riscv/domain.c diff --git a/xen/arch/riscv/Makefile b/xen/arch/riscv/Makefile index 1eb9ab090b..caa1aac5b2 100644 --- a/xen/arch/riscv/Makefile +++ b/xen/arch/riscv/Makefile @@ -1,5 +1,6 @@ obj-y += aplic.o obj-y += cpufeature.o +obj-y += domain.o obj-$(CONFIG_EARLY_PRINTK) += early_printk.o obj-y += entry.o obj-y += imsic.o diff --git a/xen/arch/riscv/domain.c b/xen/arch/riscv/domain.c new file mode 100644 index 0000000000..b60320b90d --- /dev/null +++ b/xen/arch/riscv/domain.c @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include +#include +#include +#include + +static void continue_new_vcpu(struct vcpu *prev) +{ + BUG_ON("unimplemented\n"); +} + +int arch_vcpu_create(struct vcpu *v) +{ + int rc; + void *stack = vzalloc(STACK_SIZE); + + if ( !stack ) + return -ENOMEM; + + v->arch.cpu_info = stack + STACK_SIZE - sizeof(*v->arch.cpu_info); + + v->arch.xen_saved_context.sp = (register_t)v->arch.cpu_info; + v->arch.xen_saved_context.ra = (register_t)continue_new_vcpu; + + /* Idle VCPUs don't need the rest of this setup */ + if ( is_idle_vcpu(v) ) + return 0; + + /* + * As the vtimer and interrupt controller (IC) are not yet implemented, + * return an error. + * + * TODO: Drop this once the vtimer and IC are implemented. + */ + rc = -EOPNOTSUPP; + goto fail; + + return rc; + + fail: + arch_vcpu_destroy(v); + return rc; +} + +void arch_vcpu_destroy(struct vcpu *v) +{ + vfree((void *)&v->arch.cpu_info[1] - STACK_SIZE); +} + +static void __init __maybe_unused build_assertions(void) +{ + /* + * Enforce the requirement documented in struct cpu_info that + * guest_cpu_user_regs must be the first field. + */ + BUILD_BUG_ON(offsetof(struct cpu_info, guest_cpu_user_regs)); +} diff --git a/xen/arch/riscv/include/asm/config.h b/xen/arch/riscv/include/asm/config.h index 1e08d3bf78..0613de008b 100644 --- a/xen/arch/riscv/include/asm/config.h +++ b/xen/arch/riscv/include/asm/config.h @@ -143,7 +143,7 @@ #define SMP_CACHE_BYTES (1 << 6) -#define STACK_SIZE PAGE_SIZE +#define STACK_SIZE (PAGE_SIZE << 3) #define IDENT_AREA_SIZE 64 diff --git a/xen/arch/riscv/include/asm/current.h b/xen/arch/riscv/include/asm/current.h index 0c3ea70c2e..58c9f1506b 100644 --- a/xen/arch/riscv/include/asm/current.h +++ b/xen/arch/riscv/include/asm/current.h @@ -21,6 +21,12 @@ struct pcpu_info { /* tp points to one of these */ extern struct pcpu_info pcpu_info[NR_CPUS]; +/* Per-VCPU state that lives at the top of the stack */ +struct cpu_info { + /* This should be the first member. */ + struct cpu_user_regs guest_cpu_user_regs; +}; + #define set_processor_id(id) do { \ tp->processor_id = (id); \ } while (0) diff --git a/xen/arch/riscv/include/asm/domain.h b/xen/arch/riscv/include/asm/domain.h index 316e7c6c84..f78f145258 100644 --- a/xen/arch/riscv/include/asm/domain.h +++ b/xen/arch/riscv/include/asm/domain.h @@ -24,6 +24,30 @@ struct arch_vcpu_io { struct arch_vcpu { struct vcpu_vmid vmid; + + /* + * Callee saved registers for Xen's state used to switch from + * prev's stack to the next's stack during context switch. + */ + struct + { + register_t s0; + register_t s1; + register_t s2; + register_t s3; + register_t s4; + register_t s5; + register_t s6; + register_t s7; + register_t s8; + register_t s9; + register_t s10; + register_t s11; + register_t sp; + register_t ra; + } xen_saved_context; + + struct cpu_info *cpu_info; }; struct paging_domain { diff --git a/xen/arch/riscv/stubs.c b/xen/arch/riscv/stubs.c index d071c8b86d..daadff0138 100644 --- a/xen/arch/riscv/stubs.c +++ b/xen/arch/riscv/stubs.c @@ -116,16 +116,6 @@ void dump_pageframe_info(struct domain *d) BUG_ON("unimplemented"); } -int arch_vcpu_create(struct vcpu *v) -{ - BUG_ON("unimplemented"); -} - -void arch_vcpu_destroy(struct vcpu *v) -{ - BUG_ON("unimplemented"); -} - int arch_sanitise_domain_config(struct xen_domctl_createdomain *config) { BUG_ON("unimplemented");