|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [PATCH v3 11/39] xen/riscv: implement vCPU context switching
Implement context_switch() and the helpers it needs: save/restore of
H/VS CSRs, virtual timer and P2M context, and __context_switch() in
assembly, which switches Xen's own callee-saved state (and thereby the
stack) from prev to next. The virtual interrupt controller state isn't
switched here.
Add offsets of struct arch_vcpu's xen_saved_context to asm-offsets.c for
use by __context_switch().
henvcfg, htimedelta and vsie are 64-bit on both RV32 and RV64, so store
them as uint64_t and access them with csr_{read,write}64(). For that
purpose introduce csr_read64().
The VMID has to be claimed on the context switch rather than on guest
entry: once p2m_ctxt_switch_to() writes HGATP, speculation can populate
G-stage entries under whatever VMID is written there. This is done by
p2m_vmid_switch_to(), called right before the HGATP write:
- VMIDs are a per-hart resource, so the (generation, vmid) pair of a
vCPU which last ran on another hart is invalidated before a VMID is
claimed for it. The local flush for a wrapped generation moves along
with the claim.
- Nothing is switched in for the idle vCPU, so HGATP keeps pointing at
the p2m of the domain which ran on the hart last. Track that domain
per hart (hgatp_owner) and keep the hart in its dirty_cpumask until a
vCPU of another domain is switched in: p2m_tlb_flush() then goes on
reaching the hart, and a domain which idles between two runs on the
same hart keeps its VMIDs.
- When the owner changes, the hart leaves the old owner's dirty_cpumask
while its TLB may still hold that domain's G-stage translations, so it
is moved to a new VMID generation, which makes none of them reachable
again. It joins the new owner's dirty_cpumask before HGATP is written;
a full barrier there, paired with one in p2m_tlb_flush(), guarantees
that a concurrent P2M change either reaches the hart with an
HFENCE.GVMA or is visible to it before it walks the p2m.
That leaves p2m_handle_vmenter() with nothing to do, so drop it together
with its call from check_for_pcpu_work(): a VMID is only ever invalidated
while its vCPU isn't running, and p2m_tlb_flush() drops stale entries
with a remote HFENCE.GVMA rather than by retiring VMIDs. Unlike
p2m_handle_vmenter(), HGATP is written unconditionally, as it holds the
G-stage root too, which on a context switch belongs to another domain.
While at it, fix the inclusion order of headers in asm-offsets.c: Xen's
headers go first, then arch specific ones.
Signed-off-by: Oleksii Kurochko <oleksii.kurochko@xxxxxxxxx>
---
Changes in v3:
- Update the commit message.
- Introduce csr_read64(), as vsie is 64-bit on RV32 too, i.e. has to be
accessed as the vsie/vsieh pair there.
- Make vsie declared as uint64_t instead of register_t.
- Rename the parameter of save_csr_regs() to p and of restore_csr_regs()
to n, to match their callers ctxt_switch_from()/ctxt_switch_to() and the
rest of the context switch helpers (p2m_ctxt_switch_{from,to}(),
vtimer_ctxt_switch_{from,to}()).
- s/save_csr_regs/csr_regs_ctxt_switch_from/
- s/restore_csr_regs/csr_regs_ctxt_switch_to/
- Drop the forward declaration of struct vcpu in <asm/system.h>: the
return type of __context_switch() already declares the tag at file
scope.
- Make the next parameter of __context_switch() pointer-to-const, as
next's saved context is only read.
- Extend the comment above __context_switch(): as ra is switched too, it
doesn't return to its caller, but to where next last called it from
(next's own context_switch(), or continue_new_vcpu() for a vCPU which
has never run).
- Call vtimer_ctxt_switch_to() after csr_regs_ctxt_switch_to(), so that
ctxt_switch_to() restores state in the reverse order of
ctxt_switch_from() saving it.
- Rework p2m's VMID handling in the context switch: track the domain
whose p2m HGATP points at in a per-CPU hgatp_owner, so a pass through
idle no longer drops the hart from that domain's dirty_cpumask nor
burns a VMID generation; move the dirty_cpumask update and the
invalidation of a VMID brought from another hart from schedule_tail()
and ctxt_switch_to() to p2m_ctxt_switch_to(), next to the VMID claim;
add the barriers ordering the dirty_cpumask update against
p2m_tlb_flush().
- Initialise arch_vcpu.last_cpu to CPU_NONE instead of NR_CPUS.
- Mark prev's dirty_cpu clean before ctxt_switch_to() and set current's
after it.
---
Changes in v2:
- New patch.
---
---
xen/arch/riscv/domain.c | 127 ++++++++++++++++++++++
xen/arch/riscv/entry.S | 48 ++++++++
xen/arch/riscv/include/asm/csr.h | 13 +++
xen/arch/riscv/include/asm/domain.h | 15 ++-
xen/arch/riscv/include/asm/p2m.h | 1 -
xen/arch/riscv/include/asm/system.h | 2 +
xen/arch/riscv/p2m.c | 157 ++++++++++++++++++---------
xen/arch/riscv/riscv64/asm-offsets.c | 19 +++-
xen/arch/riscv/stubs.c | 5 -
xen/arch/riscv/traps.c | 2 -
10 files changed, 330 insertions(+), 59 deletions(-)
diff --git a/xen/arch/riscv/domain.c b/xen/arch/riscv/domain.c
index fd6229635627..f80643e2d82b 100644
--- a/xen/arch/riscv/domain.c
+++ b/xen/arch/riscv/domain.c
@@ -11,6 +11,7 @@
#include <asm/bitops.h>
#include <asm/cpufeature.h>
#include <asm/csr.h>
+#include <asm/current.h>
#include <asm/intc.h>
#include <asm/mmio.h>
#include <asm/riscv_encoding.h>
@@ -152,6 +153,8 @@ int arch_vcpu_create(struct vcpu *v)
if ( is_idle_vcpu(v) )
return 0;
+ v->arch.last_cpu = CPU_NONE;
+
vcpu_csr_init(v);
if ( (rc = vcpu_vtimer_init(v)) )
@@ -323,6 +326,130 @@ int arch_domain_create(struct domain *d,
return rc;
}
+static void csr_regs_ctxt_switch_from(struct vcpu *p)
+{
+ /*
+ * There is no need to save these CSRs as only hypervisor writes them in
+ * csr_regs_ctxt_switch_to() and guest can't access them so they shouldn't
+ * be stored here. Keep them commented here just for symmetry with the
+ * restore CSRs register part.
+ *
+ * p->arch.hedeleg = csr_read(CSR_HEDELEG);
+ * p->arch.hideleg = csr_read(CSR_HIDELEG);
+ * p->arch.henvcfg = csr_read64(CSR_HENVCFG);
+ * p->arch.hcounteren = csr_read(CSR_HCOUNTEREN);
+ * p->arch.htimedelta = csr_read64(CSR_HTIMEDELTA);
+ *
+ * if ( riscv_isa_extension_available(NULL, RISCV_ISA_EXT_smstateen) )
+ * p->arch.hstateen0 = csr_read(CSR_HSTATEEN0);
+ */
+
+ p->arch.hvip = csr_read(CSR_HVIP);
+
+ p->arch.vsstatus = csr_read(CSR_VSSTATUS);
+ p->arch.vsie = csr_read64(CSR_VSIE);
+ p->arch.vstvec = csr_read(CSR_VSTVEC);
+ p->arch.vsscratch = csr_read(CSR_VSSCRATCH);
+ p->arch.vscause = csr_read(CSR_VSCAUSE);
+ p->arch.vstval = csr_read(CSR_VSTVAL);
+ p->arch.vsepc = csr_read(CSR_VSEPC);
+}
+
+static void csr_regs_ctxt_switch_to(struct vcpu *n)
+{
+ csr_write(CSR_HEDELEG, n->arch.hedeleg);
+ csr_write(CSR_HIDELEG, n->arch.hideleg);
+ csr_write(CSR_HVIP, n->arch.hvip);
+ csr_write64(CSR_HENVCFG, n->arch.henvcfg);
+ csr_write(CSR_HCOUNTEREN, n->arch.hcounteren);
+ csr_write64(CSR_HTIMEDELTA, n->arch.htimedelta);
+
+ if ( riscv_isa_extension_available(NULL, RISCV_ISA_EXT_smstateen) )
+ csr_write(CSR_HSTATEEN0, n->arch.hstateen0);
+
+ csr_write(CSR_VSSTATUS, n->arch.vsstatus);
+ csr_write64(CSR_VSIE, n->arch.vsie);
+ csr_write(CSR_VSTVEC, n->arch.vstvec);
+ csr_write(CSR_VSSCRATCH, n->arch.vsscratch);
+ csr_write(CSR_VSCAUSE, n->arch.vscause);
+ csr_write(CSR_VSTVAL, n->arch.vstval);
+ csr_write(CSR_VSEPC, n->arch.vsepc);
+}
+
+static void ctxt_switch_from(struct vcpu *p)
+{
+ /*
+ * When the idle VCPU is running, Xen will always stay in hypervisor
+ * mode.
+ * Therefore we don't need to save the context of an idle VCPU.
+ */
+ if ( is_idle_vcpu(p) )
+ return;
+
+ p2m_ctxt_switch_from(p);
+
+ vtimer_ctxt_switch_from(p);
+
+ csr_regs_ctxt_switch_from(p);
+}
+
+static void ctxt_switch_to(struct vcpu *n)
+{
+ /*
+ * When the idle VCPU is running, Xen will always stay in hypervisor
+ * mode.
+ * Therefore we don't need to restore the context of an idle VCPU.
+ */
+ if ( is_idle_vcpu(n) )
+ return;
+
+ csr_regs_ctxt_switch_to(n);
+
+ vtimer_ctxt_switch_to(n);
+
+ p2m_ctxt_switch_to(n);
+}
+
+static void schedule_tail(struct vcpu *prev)
+{
+ unsigned int cpu = smp_processor_id();
+
+ ASSERT(prev != current);
+
+ ctxt_switch_from(prev);
+
+ write_atomic(&prev->dirty_cpu, VCPU_CPU_CLEAN);
+
+ ctxt_switch_to(current);
+
+ write_atomic(¤t->dirty_cpu, cpu);
+
+ current->arch.last_cpu = cpu;
+
+ /*
+ * sched_context_switched() internally uses a spinlock,
+ * which requires interrupts to be enabled.
+ */
+ local_irq_enable();
+
+ sched_context_switched(prev, current);
+}
+
+void context_switch(struct vcpu *prev, struct vcpu *next)
+{
+ ASSERT(local_irq_is_enabled());
+ ASSERT(prev != next);
+ ASSERT(!vcpu_cpu_dirty(next));
+
+ local_irq_disable();
+
+ set_current(next);
+
+ prev = __context_switch(prev, next);
+
+ schedule_tail(prev);
+}
+
static void __init __maybe_unused build_assertions(void)
{
/*
diff --git a/xen/arch/riscv/entry.S b/xen/arch/riscv/entry.S
index 202a35fb03a8..017fbb06262c 100644
--- a/xen/arch/riscv/entry.S
+++ b/xen/arch/riscv/entry.S
@@ -99,3 +99,51 @@ restore_registers:
sret
END(handle_trap)
+
+/*
+ * struct vcpu *__context_switch(struct vcpu *prev, const struct vcpu *next)
+ *
+ * This is called on prev's stack, and returns on next's. As ra is
+ * switched too, it doesn't return to its caller: it returns to where
+ * next last called it from, i.e. into next's own context_switch(), or,
+ * for a vCPU which has never run, to continue_new_vcpu() with an empty
+ * stack.
+ *
+ * a0 - prev
+ * a1 - next
+ *
+ * Returns prev in a0
+ */
+FUNC(__context_switch)
+ REG_S s0, VCPU_XEN_SAVED_CONTEXT_S0(a0)
+ REG_S s1, VCPU_XEN_SAVED_CONTEXT_S1(a0)
+ REG_S s2, VCPU_XEN_SAVED_CONTEXT_S2(a0)
+ REG_S s3, VCPU_XEN_SAVED_CONTEXT_S3(a0)
+ REG_S s4, VCPU_XEN_SAVED_CONTEXT_S4(a0)
+ REG_S s5, VCPU_XEN_SAVED_CONTEXT_S5(a0)
+ REG_S s6, VCPU_XEN_SAVED_CONTEXT_S6(a0)
+ REG_S s7, VCPU_XEN_SAVED_CONTEXT_S7(a0)
+ REG_S s8, VCPU_XEN_SAVED_CONTEXT_S8(a0)
+ REG_S s9, VCPU_XEN_SAVED_CONTEXT_S9(a0)
+ REG_S s10, VCPU_XEN_SAVED_CONTEXT_S10(a0)
+ REG_S s11, VCPU_XEN_SAVED_CONTEXT_S11(a0)
+ REG_S sp, VCPU_XEN_SAVED_CONTEXT_SP(a0)
+ REG_S ra, VCPU_XEN_SAVED_CONTEXT_RA(a0)
+
+ REG_L s0, VCPU_XEN_SAVED_CONTEXT_S0(a1)
+ REG_L s1, VCPU_XEN_SAVED_CONTEXT_S1(a1)
+ REG_L s2, VCPU_XEN_SAVED_CONTEXT_S2(a1)
+ REG_L s3, VCPU_XEN_SAVED_CONTEXT_S3(a1)
+ REG_L s4, VCPU_XEN_SAVED_CONTEXT_S4(a1)
+ REG_L s5, VCPU_XEN_SAVED_CONTEXT_S5(a1)
+ REG_L s6, VCPU_XEN_SAVED_CONTEXT_S6(a1)
+ REG_L s7, VCPU_XEN_SAVED_CONTEXT_S7(a1)
+ REG_L s8, VCPU_XEN_SAVED_CONTEXT_S8(a1)
+ REG_L s9, VCPU_XEN_SAVED_CONTEXT_S9(a1)
+ REG_L s10, VCPU_XEN_SAVED_CONTEXT_S10(a1)
+ REG_L s11, VCPU_XEN_SAVED_CONTEXT_S11(a1)
+ REG_L sp, VCPU_XEN_SAVED_CONTEXT_SP(a1)
+ REG_L ra, VCPU_XEN_SAVED_CONTEXT_RA(a1)
+
+ ret
+END(__context_switch)
diff --git a/xen/arch/riscv/include/asm/csr.h b/xen/arch/riscv/include/asm/csr.h
index a5cb24c863dd..aad82c6a6d8a 100644
--- a/xen/arch/riscv/include/asm/csr.h
+++ b/xen/arch/riscv/include/asm/csr.h
@@ -40,6 +40,13 @@
csr_write(csr ## H, v_ >> 32); \
})
+#define csr_read64(csr) \
+({ \
+ uint64_t v_ = csr_read(csr ## H); \
+ \
+ (v_ << 32) | csr_read(csr); \
+})
+
/*
* The two halves are read by separate instructions, so a CSR which hardware
* increments can carry from the low half into the high one in between,
@@ -72,6 +79,12 @@
(void)csr ## H; \
csr_read(csr); \
})
+
+#define csr_read64(csr) \
+({ \
+ (void)csr ## H; \
+ csr_read(csr); \
+})
#endif
#define csr_swap(csr, val) \
diff --git a/xen/arch/riscv/include/asm/domain.h
b/xen/arch/riscv/include/asm/domain.h
index 15e8fa19685e..77ad888a2d6c 100644
--- a/xen/arch/riscv/include/asm/domain.h
+++ b/xen/arch/riscv/include/asm/domain.h
@@ -29,6 +29,11 @@ struct arch_vcpu_io {
struct arch_vcpu {
struct vcpu_vmid vmid;
+ /*
+ * The last CPU this vCPU ran on. Initialised to CPU_NONE.
+ */
+ unsigned int last_cpu;
+
/*
* Callee saved registers for Xen's state used to switch from
* prev's stack to the next's stack during context switch.
@@ -60,11 +65,19 @@ struct arch_vcpu {
register_t hcounteren;
register_t hedeleg;
register_t hideleg;
- register_t henvcfg;
+ uint64_t henvcfg;
register_t hstateen0;
+ uint64_t htimedelta;
register_t hvip;
register_t vsatp;
+ register_t vscause;
+ register_t vsepc;
+ uint64_t vsie;
+ register_t vsscratch;
+ register_t vsstatus;
+ register_t vstval;
+ register_t vstvec;
/*
* VCPU interrupts
diff --git a/xen/arch/riscv/include/asm/p2m.h b/xen/arch/riscv/include/asm/p2m.h
index 0d1dace1a0d8..9edf78377ee5 100644
--- a/xen/arch/riscv/include/asm/p2m.h
+++ b/xen/arch/riscv/include/asm/p2m.h
@@ -262,7 +262,6 @@ struct page_info *p2m_get_page_from_gfn(struct p2m_domain
*p2m, gfn_t gfn,
void p2m_ctxt_switch_from(struct vcpu *p);
void p2m_ctxt_switch_to(struct vcpu *n);
-void p2m_handle_vmenter(void);
#endif /* ASM__RISCV__P2M_H */
diff --git a/xen/arch/riscv/include/asm/system.h
b/xen/arch/riscv/include/asm/system.h
index f33af64fd2ec..d350b9c959c2 100644
--- a/xen/arch/riscv/include/asm/system.h
+++ b/xen/arch/riscv/include/asm/system.h
@@ -76,6 +76,8 @@ static inline bool local_irq_is_enabled(void)
#define arch_fetch_and_add(x, v) __sync_fetch_and_add(x, v)
+struct vcpu *__context_switch(struct vcpu *prev, const struct vcpu *next);
+
#endif /* __ASSEMBLER__ */
#endif /* ASM__RISCV__SYSTEM_H */
diff --git a/xen/arch/riscv/p2m.c b/xen/arch/riscv/p2m.c
index de25607247a6..02e7f6364fad 100644
--- a/xen/arch/riscv/p2m.c
+++ b/xen/arch/riscv/p2m.c
@@ -243,6 +243,15 @@ static void p2m_tlb_flush(struct p2m_domain *p2m)
p2m->need_flush = false;
+ /*
+ * Order the p2m updates above against the read of dirty_cpumask below,
+ * pairing with the barrier in p2m_vmid_switch_to(). Either that hart is
+ * seen here and gets an HFENCE.GVMA, or it adds itself to the mask
+ * afterwards, in which case it starts walking this p2m only once the
+ * updates are visible to it.
+ */
+ smp_mb();
+
sbi_remote_hfence_gvma(d->dirty_cpumask, 0, 0);
}
@@ -1504,16 +1513,110 @@ void p2m_ctxt_switch_from(struct vcpu *p)
* VMID, world-switch code should zero vsatp, then swap hgatp, then
* finally write the new vsatp value what will be done in
* p2m_ctxt_switch_to().
- * Note, that also HGATP update could happen in p2m_handle_vmenter().
*/
p->arch.vsatp = csr_swap(CSR_VSATP, 0);
/*
- * Nothing to do with HGATP as it will be update in p2m_ctxt_switch_to()
- * or/and in p2m_handle_vmenter().
+ * Nothing to do with HGATP as it will be updated in
+ * p2m_ctxt_switch_to().
*/
}
+/*
+ * Domain whose p2m this hart's HGATP points at. ctxt_switch_to() bails out
+ * early for the idle vCPU, so HGATP survives a pass through idle and keeps
+ * pointing at the domain which ran here last. That domain, rather than the
+ * one the scheduler switched away from, is what owns this hart's G-stage
+ * translations. The hart also keeps its place in the owner's dirty_cpumask:
+ * p2m_tlb_flush() goes on reaching it, and a domain which idles between two
+ * runs on the same hart keeps its VMIDs.
+ *
+ * TODO: nothing resets hgatp_owner when its domain is destroyed, so a hart
+ * which stays idle from the domain's last run until it is freed is
+ * left with a dangling pointer. Once domain_relinquish_resources()
+ * tears down the p2m, it has to reset hgatp_owner of every hart still
+ * pointing at the domain (cmpxchg() against d), i.e. before the RCU
+ * grace period of domain_destroy() and so before the domain is freed.
+ */
+static DEFINE_PER_CPU(struct domain *, hgatp_owner);
+
+/*
+ * Update this hart's place in the dirty_cpumask of the domains involved and
+ * claim a VMID for n. Must be called before HGATP is pointed at n's p2m, as
+ * it is written with the VMID claimed here.
+ */
+static void p2m_vmid_switch_to(struct vcpu *n)
+{
+ unsigned int cpu = smp_processor_id();
+ struct domain *owner = this_cpu(hgatp_owner);
+ bool need_flush;
+
+ ASSERT(!is_idle_vcpu(n));
+
+ if ( owner != n->domain )
+ {
+ /*
+ * Once this hart drops out of the owner's dirty_cpumask it stops
+ * being a target of p2m_tlb_flush(), while its TLB may still hold
+ * G-stage translations of that domain: none of the vCPUs of that
+ * domain which ran here has had its VMID invalidated. Move the hart
+ * to a new VMID generation so that none of them can be reached
+ * again.
+ *
+ * This has to precede the VMID claim below, so that n gets a VMID of
+ * the new generation.
+ */
+ if ( owner )
+ {
+ vmid_flush_hart();
+
+ cpumask_clear_cpu(cpu, owner->dirty_cpumask);
+ }
+
+ /*
+ * Mark this hart in the incoming domain's dirty_cpumask before HGATP
+ * is pointed at its p2m: from that write on the hart may cache the
+ * p2m's translations, so p2m_tlb_flush() must already reach it.
+ */
+ cpumask_set_cpu(cpu, n->domain->dirty_cpumask);
+
+ /*
+ * Pairs with the barrier in p2m_tlb_flush(). cpumask_set_cpu() is an
+ * unordered AMO on RISC-V, so without this a concurrent flusher could
+ * read the mask without this hart in it while this hart is already
+ * walking the p2m it is about to be pointed at.
+ */
+ smp_mb();
+
+ this_cpu(hgatp_owner) = n->domain;
+ }
+
+ /*
+ * A VMID is meaningful only on the hart whose pool issued it: generations
+ * are per-hart counters which all start at 1 and advance independently,
+ * so the pair a vCPU brings from another hart may match this hart's
+ * generation by coincidence, leaving the vCPU under a VMID which is live
+ * here for someone else.
+ */
+ if ( n->arch.last_cpu != cpu )
+ vmid_flush_vcpu(n);
+
+ /*
+ * Claim the VMID here rather than leaving it to the next guest entry:
+ * the caller makes HGATP live right after, and a stale VMID there pairs
+ * this domain's G-stage root with a tag which may already have been
+ * re-issued to a vCPU of another domain.
+ */
+ need_flush = vmid_handle_vmenter(&n->arch.vmid);
+
+ /*
+ * A VMID isn't re-used until the generation it was issued in wraps, so a
+ * G-stage flush is needed only when vmid_handle_vmenter() says so.
+ */
+ if ( unlikely(need_flush) )
+ local_hfence_gvma_all();
+}
+
/*
* As speculation may occur at any time, an incorrect set of page tables could
* be used. Therefore, this function must be called only after all other guest
@@ -1528,11 +1631,9 @@ void p2m_ctxt_switch_to(struct vcpu *n)
if ( is_idle_vcpu(n) )
return;
+ p2m_vmid_switch_to(n);
+
csr_write(CSR_HGATP, construct_hgatp(p2m, n->arch.vmid.vmid));
- /*
- * As VMID is unique per vCPU and just re-used here thereby there is no
- * need for G-stage TLB flush here.
- */
csr_write(CSR_VSATP, n->arch.vsatp);
@@ -1548,48 +1649,6 @@ void p2m_ctxt_switch_to(struct vcpu *n)
flush_tlb_guest_local();
}
-void p2m_handle_vmenter(void)
-{
- struct vcpu *curr = current;
- struct p2m_domain *p2m = p2m_get_hostp2m(curr->domain);
- struct vcpu_vmid *p_vmid = &curr->arch.vmid;
- unsigned short old_vmid, new_vmid;
- bool need_flush;
-
- BUG_ON(is_idle_vcpu(curr));
-
- old_vmid = p_vmid->vmid;
- need_flush = vmid_handle_vmenter(p_vmid);
- new_vmid = p_vmid->vmid;
-
-#ifdef P2M_DEBUG
- printk("%pv: oldvmid(%d) new_vmid(%d), need_flush(%d)\n",
- curr, old_vmid, new_vmid, need_flush);
-#endif
-
- /*
- * There is no need to set VSATP to 0 to stop speculation before updating
- * HGATP, as VSATP is not modified here.
- */
- if ( old_vmid != new_vmid )
- csr_write(CSR_HGATP, construct_hgatp(p2m, p_vmid->vmid));
-
- /*
- * There is also no need to flush G-stage TLB unconditionally as old VMID
- * won't be reused until need_flush is set to true.
- */
- if ( unlikely(need_flush) )
- local_hfence_gvma_all();
-
- /*
- * There is also no need to flush the VS-stage TLB: even if speculation
- * occurs (VSATP + old HGATP were used), it will use the old VMID, which
- * won't be reused until need_flush is set to true. When VMIDs aren't
- * available there is no old VMID to rely on, but then need_flush is set
- * on every entry, so the flush above covers that case.
- */
-}
-
struct page_info *get_page_from_gfn(struct domain *d, unsigned long gfn,
p2m_type_t *t, p2m_query_t q)
{
diff --git a/xen/arch/riscv/riscv64/asm-offsets.c
b/xen/arch/riscv/riscv64/asm-offsets.c
index 1290b9dbbe82..c1be1614ce94 100644
--- a/xen/arch/riscv/riscv64/asm-offsets.c
+++ b/xen/arch/riscv/riscv64/asm-offsets.c
@@ -1,8 +1,10 @@
#define COMPILE_OFFSETS
+#include <xen/sched.h>
+#include <xen/types.h>
+
#include <asm/current.h>
#include <asm/processor.h>
-#include <xen/types.h>
#define DEFINE(_sym, _val) \
asm volatile ( "\n.ascii\"==>#define " #_sym " %0 /* " #_val " */<==\""\
@@ -53,4 +55,19 @@ void asm_offsets(void)
BLANK();
DEFINE(PCPU_INFO_SIZE, sizeof(struct pcpu_info));
BLANK();
+ OFFSET(VCPU_XEN_SAVED_CONTEXT_S0, struct vcpu, arch.xen_saved_context.s0);
+ OFFSET(VCPU_XEN_SAVED_CONTEXT_S1, struct vcpu, arch.xen_saved_context.s1);
+ OFFSET(VCPU_XEN_SAVED_CONTEXT_S2, struct vcpu, arch.xen_saved_context.s2);
+ OFFSET(VCPU_XEN_SAVED_CONTEXT_S3, struct vcpu, arch.xen_saved_context.s3);
+ OFFSET(VCPU_XEN_SAVED_CONTEXT_S4, struct vcpu, arch.xen_saved_context.s4);
+ OFFSET(VCPU_XEN_SAVED_CONTEXT_S5, struct vcpu, arch.xen_saved_context.s5);
+ OFFSET(VCPU_XEN_SAVED_CONTEXT_S6, struct vcpu, arch.xen_saved_context.s6);
+ OFFSET(VCPU_XEN_SAVED_CONTEXT_S7, struct vcpu, arch.xen_saved_context.s7);
+ OFFSET(VCPU_XEN_SAVED_CONTEXT_S8, struct vcpu, arch.xen_saved_context.s8);
+ OFFSET(VCPU_XEN_SAVED_CONTEXT_S9, struct vcpu, arch.xen_saved_context.s9);
+ OFFSET(VCPU_XEN_SAVED_CONTEXT_S10, struct vcpu,
arch.xen_saved_context.s10);
+ OFFSET(VCPU_XEN_SAVED_CONTEXT_S11, struct vcpu,
arch.xen_saved_context.s11);
+ OFFSET(VCPU_XEN_SAVED_CONTEXT_SP, struct vcpu, arch.xen_saved_context.sp);
+ OFFSET(VCPU_XEN_SAVED_CONTEXT_RA, struct vcpu, arch.xen_saved_context.ra);
+ BLANK();
}
diff --git a/xen/arch/riscv/stubs.c b/xen/arch/riscv/stubs.c
index 3a7953593d93..e0febae432b2 100644
--- a/xen/arch/riscv/stubs.c
+++ b/xen/arch/riscv/stubs.c
@@ -81,11 +81,6 @@ void smp_send_state_dump(unsigned int cpu)
DEFINE_PER_CPU(struct vcpu *, curr_vcpu);
-void context_switch(struct vcpu *prev, struct vcpu *next)
-{
- BUG_ON("unimplemented");
-}
-
void continue_running(struct vcpu *same)
{
BUG_ON("unimplemented");
diff --git a/xen/arch/riscv/traps.c b/xen/arch/riscv/traps.c
index 10d6855d913b..9bb54d030be7 100644
--- a/xen/arch/riscv/traps.c
+++ b/xen/arch/riscv/traps.c
@@ -178,8 +178,6 @@ static void check_for_pcpu_work(void)
vcpu_sync_interrupts(curr);
vcpu_flush_interrupts(curr);
-
- p2m_handle_vmenter();
}
static void timer_interrupt(void)
--
2.55.0
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |