|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [PATCH v3 37/39] xen/riscv: implement continue_new_vcpu()
continue_new_vcpu() is the arch hook invoked the first time a freshly
created vCPU is scheduled. Implement both cases it has to cover:
- for the idle vCPU, switch to its own stack and jump to idle_loop();
- for a guest vCPU, enter the guest through the new
return_to_new_vcpu() path in entry.S, which restores hstatus and
sepc, passes the hart id in a0 and, in a1, either the DTB address
(boot vCPU, as expected by the RISC-V boot protocol) or the opaque
value passed to SBI HSM hart_start() (secondary vCPUs), clears the
remaining GPRs, sets sstatus.SPP and sstatus.SPIE and executes sret.
The remaining GPRs have to be cleared as sret neither switches stacks
nor touches the GPRs: the guest would otherwise start with Xen's values
in them, sp pointing to this vCPU's Xen stack among them.
Interrupts have to stay disabled across the restore. A trap taken in
HS-mode overwrites hstatus.SPV, sstatus.SPP and sepc (trap entry clears
hstatus.SPV in particular), so an interrupt taken between the write of
hstatus and sret would make sret return to HS-mode instead of VS-mode,
and restoring them afterwards is non-trivial. Instead interrupts are
simply kept off and sstatus.SPIE is set, so that sret re-enables them
by restoring sstatus.SIE from SPIE.
Introduce get_cpu_info() and reset_stack_and_jump() in asm/current.h,
needed by the above. get_cpu_info() is a macro rather than a static
inline because asm/current.h is pulled in by <xen/percpu.h> before
this_cpu() is defined and before <xen/sched.h> completes struct vcpu.
idle_loop() is only introduced as a stub.
Signed-off-by: Oleksii Kurochko <oleksii.kurochko@xxxxxxxxx>
---
Changes in v3:
- Drop unneeded inclusion of asm/aia.h, asm/aplic.h and asm/imsic.h.
- Mark return_to_new_vcpu() declaration with asmlinkage.
- Drop the "else" after reset_stack_and_jump(idle_loop), which cannot
return.
- Move the restore of hstatus and the setting of sstatus.SPIE from
continue_new_vcpu() to return_to_new_vcpu(), so that everything sret
depends on is set up in one place, and shorten the comment in
continue_new_vcpu() accordingly.
- Move the tp/SSCRATCH swap out to a separate patch, as nothing reads
SSCRATCH yet.
- Reword the comments in return_to_new_vcpu(), which suggested that
the values of .a0 and .a1 are established there.
- Clear the GPRs which are not meaningful to the vCPU being started
before sret, so that Xen register values are not exposed to a guest.
- Update the commit message: a1 carries the opaque value passed to
hart_start() for secondary vCPUs, describe the clearing of the GPRs,
drop the comparison with a real CPU and the reference to a later
implementation of idle_loop().
---
Changes in v2:
- New patch.
---
---
xen/arch/riscv/domain.c | 25 +++++++++++++++++-
xen/arch/riscv/entry.S | 39 ++++++++++++++++++++++++++++
xen/arch/riscv/include/asm/current.h | 4 +++
3 files changed, 67 insertions(+), 1 deletion(-)
diff --git a/xen/arch/riscv/domain.c b/xen/arch/riscv/domain.c
index 6cb762bee33c..04be628dcdb4 100644
--- a/xen/arch/riscv/domain.c
+++ b/xen/arch/riscv/domain.c
@@ -138,9 +138,27 @@ static void vcpu_csr_init(struct vcpu *v)
v->arch.hie = BIT(IRQ_S_GEXT, UL);
}
+static void schedule_tail(struct vcpu *prev);
+static void noreturn idle_loop(void);
+void asmlinkage noreturn return_to_new_vcpu(void);
+
static void continue_new_vcpu(struct vcpu *prev)
{
- BUG_ON("unimplemented\n");
+ schedule_tail(prev);
+
+ if ( is_idle_vcpu(current) )
+ reset_stack_and_jump(idle_loop);
+
+ /*
+ * return_to_new_vcpu() sets up hstatus.SPV, sstatus.SPP and sepc so
+ * that sret enters the guest in VS-mode. A trap taken in HS-mode
+ * overwrites all of them (trap entry clears hstatus.SPV in particular),
+ * so interrupts have to stay disabled until sret. They are re-enabled by
+ * sret itself, as return_to_new_vcpu() also sets sstatus.SPIE.
+ */
+ local_irq_disable();
+
+ reset_stack_and_jump(return_to_new_vcpu);
}
int arch_vcpu_create(struct vcpu *v)
@@ -519,3 +537,8 @@ static void __init __maybe_unused build_assertions(void)
#undef CHECK_GPR_INDEX
}
+
+static void noreturn idle_loop(void)
+{
+ BUG_ON("unimplemented");
+}
diff --git a/xen/arch/riscv/entry.S b/xen/arch/riscv/entry.S
index 017fbb06262c..99bc0f4974c2 100644
--- a/xen/arch/riscv/entry.S
+++ b/xen/arch/riscv/entry.S
@@ -147,3 +147,42 @@ FUNC(__context_switch)
ret
END(__context_switch)
+
+/*
+ * Enter a vCPU for the first time. Must be called with interrupts disabled,
+ * see continue_new_vcpu().
+ */
+FUNC(return_to_new_vcpu)
+ REG_L t0, CPU_USER_REGS_HSTATUS(sp)
+ csrw CSR_HSTATUS, t0
+
+ REG_L t0, CPU_USER_REGS_SEPC(sp)
+ csrw CSR_SEPC, t0
+
+ /* .a0 holds the hart id */
+ REG_L a0, CPU_USER_REGS_A0(sp)
+
+ /*
+ * .a1 holds the DTB address for the boot vCPU, or the opaque value
+ * passed to SBI HSM hart_start() for secondary vCPUs
+ */
+ REG_L a1, CPU_USER_REGS_A1(sp)
+
+ /* Return to (V)S-mode, with interrupts re-enabled by sret */
+ li t0, SSTATUS_SPP | SSTATUS_SPIE
+ csrs CSR_SSTATUS, t0
+
+ /*
+ * sret doesn't switch stacks and leaves the GPRs alone, so every
+ * register which isn't meaningful to the vCPU being started has to be
+ * cleared here: otherwise the guest would see Xen's values, sp (this
+ * vCPU's Xen stack) and ra among them.
+ */
+ .irp reg, ra, sp, gp, tp, t0, t1, t2, s0, s1, a2, a3, a4, a5, a6, a7, \
+ s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, t3, t4, t5, t6
+ mv \reg, zero
+ .endr
+
+ /* Enter guest */
+ sret
+END(return_to_new_vcpu)
diff --git a/xen/arch/riscv/include/asm/current.h
b/xen/arch/riscv/include/asm/current.h
index 78ec52fd8a35..f8babcc3d926 100644
--- a/xen/arch/riscv/include/asm/current.h
+++ b/xen/arch/riscv/include/asm/current.h
@@ -47,6 +47,8 @@ DECLARE_PER_CPU(struct vcpu *, curr_vcpu);
#define set_current(vcpu) do { current = (vcpu); } while (0)
#define get_cpu_current(cpu) per_cpu(curr_vcpu, cpu)
+#define get_cpu_info() (current->arch.cpu_info)
+
#define guest_cpu_user_regs() ({ BUG_ON("unimplemented"); NULL; })
#define vcpu_guest_cpu_user_regs(vcpu) \
(&(vcpu)->arch.cpu_info->guest_cpu_user_regs)
@@ -58,6 +60,8 @@ DECLARE_PER_CPU(struct vcpu *, curr_vcpu);
unreachable(); \
} while ( false )
+#define reset_stack_and_jump(fn) switch_stack_and_jump(get_cpu_info(), fn)
+
#define get_per_cpu_offset() __per_cpu_offset[smp_processor_id()]
#endif /* __ASSEMBLER__ */
--
2.55.0
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |