[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH v4 24/42] 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. It restores hstatus and
   sepc and passes the hart id in a0. a1 holds the DTB address for
   the boot vCPU (as expected by the RISC-V boot protocol), or the
   opaque value passed to SBI HSM hart_start() for secondary vCPUs.
   It then 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>
Reviewed-by: Baptiste Le Duc <baptiste.le-duc@xxxxxxxxxx>
---
Changes in v4:
 - Split the description of the guest vCPU case in the commit message
   into several sentences.
 - Call vcpu_set_vsxl() before entering the guest, as "xen/riscv: set
   the guest's XLEN explicitly in hstatus.VSXL" now comes first.
 - Add Reviewed-by: Baptiste Le Duc <baptiste.le-duc@xxxxxxxxxx>.
---
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              | 28 +++++++++++++++++---
 xen/arch/riscv/entry.S               | 39 ++++++++++++++++++++++++++++
 xen/arch/riscv/include/asm/current.h |  4 +++
 3 files changed, 68 insertions(+), 3 deletions(-)

diff --git a/xen/arch/riscv/domain.c b/xen/arch/riscv/domain.c
index 66fa14c89eaf..65d582aa8fe0 100644
--- a/xen/arch/riscv/domain.c
+++ b/xen/arch/riscv/domain.c
@@ -252,12 +252,29 @@ static void vcpu_csr_init(struct vcpu *v)
     }
 }
 
+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)
 {
-    /* Avoid an unused-function error until this function is implemented. */
-    (void)vcpu_set_vsxl;
+    schedule_tail(prev);
+
+    if ( is_idle_vcpu(current) )
+        reset_stack_and_jump(idle_loop);
 
-    BUG_ON("unimplemented\n");
+    vcpu_set_vsxl(current);
+
+    /*
+     * 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)
@@ -629,3 +646,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 1f1010e5864f..9cc5e050ced8 100644
--- a/xen/arch/riscv/include/asm/current.h
+++ b/xen/arch/riscv/include/asm/current.h
@@ -49,6 +49,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)
@@ -60,6 +62,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.56.1




 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.