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

[Xen-devel] [PATCH v3 12/17] x86: activate per-vcpu stacks in case of xpti



When scheduling a vcpu subject to xpti activate the per-vcpu stacks
by loading the vcpu specific gdt and tss. When de-scheduling such a
vcpu switch back to the per physical cpu gdt and tss.

Accessing the user registers on the stack is done via helpers as
depending on XPTI active or not the registers are located either on
the per-vcpu stack or on the default stack.

Signed-off-by: Juergen Gross <jgross@xxxxxxxx>
---
V3:
- moved some code to xpti.c
- fix error for HVM domains: reset MSRs of XPTI domain before setting
  them for HVM
- fix error for 32 bit pv domains: setup GDT in case it was reset after
  descheduling a XPTI domain
- reuse percpu GDT for XPTI domains by writing TSS entry (Jan Beulich)
- avoid LTRs and WRMSRs if possible (Jan Beulich)
---
 xen/arch/x86/domain.c      | 99 +++++++++++++++++++++++++++++++++++++++++++---
 xen/include/asm-x86/regs.h |  2 +
 2 files changed, 95 insertions(+), 6 deletions(-)

diff --git a/xen/arch/x86/domain.c b/xen/arch/x86/domain.c
index 6dd47bb2bb..8d6dc73881 100644
--- a/xen/arch/x86/domain.c
+++ b/xen/arch/x86/domain.c
@@ -1594,24 +1594,55 @@ static inline bool need_full_gdt(const struct domain *d)
     return is_pv_domain(d) && !is_idle_domain(d);
 }
 
+/*
+ * Get address of registers on stack: this is either on the per physical cpu
+ * stack (XPTI is off) or on the per-vcpu stack (XPTI is on)
+ */
+static inline struct cpu_user_regs *get_regs_on_stack(const struct vcpu *v)
+{
+    return is_vcpu_xpti_active(v) ? v->arch.pv_vcpu.stack_regs
+                                  : &get_cpu_info()->guest_cpu_user_regs;
+}
+
+static inline void copy_user_regs_from_stack(struct vcpu *v)
+{
+    memcpy(&v->arch.user_regs, get_regs_on_stack(v), CTXT_SWITCH_STACK_BYTES);
+}
+
+static inline void copy_user_regs_to_stack(const struct vcpu *v)
+{
+    memcpy(get_regs_on_stack(v), &v->arch.user_regs, CTXT_SWITCH_STACK_BYTES);
+}
+
 static void __context_switch(void)
 {
-    struct cpu_user_regs *stack_regs = guest_cpu_user_regs();
     unsigned int          cpu = smp_processor_id();
     struct vcpu          *p = per_cpu(curr_vcpu, cpu);
     struct vcpu          *n = current;
     struct domain        *pd = p->domain, *nd = n->domain;
     struct desc_struct   *gdt;
     struct desc_ptr       gdt_desc;
+    bool                  is_pv_gdt;
 
     ASSERT(p != n);
     ASSERT(!vcpu_cpu_dirty(n));
 
     if ( !is_idle_domain(pd) )
     {
-        memcpy(&p->arch.user_regs, stack_regs, CTXT_SWITCH_STACK_BYTES);
+        copy_user_regs_from_stack(p);
         vcpu_save_fpu(p);
         pd->arch.ctxt_switch->from(p);
+        if ( is_domain_xpti_active(pd) && !is_domain_xpti_active(nd) )
+        {
+            unsigned long stub_va = this_cpu(stubs.addr);
+
+            wrmsrl(MSR_LSTAR, stub_va);
+            wrmsrl(MSR_CSTAR, stub_va + STUB_TRAMPOLINE_SIZE_PERCPU);
+            if ( boot_cpu_data.x86_vendor == X86_VENDOR_INTEL ||
+                 boot_cpu_data.x86_vendor == X86_VENDOR_CENTAUR )
+                wrmsrl(MSR_IA32_SYSENTER_ESP,
+                       (unsigned long)&get_cpu_info()->guest_cpu_user_regs.es);
+        }
     }
 
     /*
@@ -1625,7 +1656,7 @@ static void __context_switch(void)
 
     if ( !is_idle_domain(nd) )
     {
-        memcpy(stack_regs, &n->arch.user_regs, CTXT_SWITCH_STACK_BYTES);
+        copy_user_regs_to_stack(n);
         if ( cpu_has_xsave )
         {
             u64 xcr0 = n->arch.xcr0 ?: XSTATE_FP_SSE;
@@ -1655,24 +1686,80 @@ static void __context_switch(void)
                       l1e_from_pfn(mfn + i, __PAGE_HYPERVISOR_RW));
     }
 
-    if ( need_full_gdt(pd) &&
-         ((p->vcpu_id != n->vcpu_id) || !need_full_gdt(nd)) )
+    is_pv_gdt = need_full_gdt(pd);
+    if ( is_pv_gdt &&
+         ((p->vcpu_id != n->vcpu_id) || !need_full_gdt(nd) ||
+          (pd->arch.pv_domain.xpti && !is_domain_xpti_active(nd))) )
     {
         gdt_desc.limit = LAST_RESERVED_GDT_BYTE;
         gdt_desc.base  = (unsigned long)(gdt - FIRST_RESERVED_GDT_ENTRY);
 
         lgdt(&gdt_desc);
+        is_pv_gdt = false;
+
+        /*
+         * When switching from XPTI domain to non-XPTI domain or when changing
+         * vcpu_id of XPTI domains we need to switch to the per physical cpu
+         * TSS in order to avoid either unmapped stacks or stacks being in use
+         * on multiple cpus at the same time.
+         */
+        if ( pd->arch.pv_domain.xpti )
+        {
+            _set_tss_desc(gdt + TSS_ENTRY - FIRST_RESERVED_GDT_ENTRY,
+                          (unsigned long)&this_cpu(init_tss),
+                          offsetof(struct tss_struct, __cacheline_filler) - 1,
+                          SYS_DESC_tss_avail);
+            ltr(TSS_ENTRY << 3);
+            get_cpu_info()->flags &= ~VCPUSTACK_ACTIVE;
+        }
     }
 
     write_ptbase(n);
 
+    if ( is_domain_xpti_active(nd) )
+    {
+        struct cpu_info *info;
+
+        /* Don't use guest_cpu_user_regs(), might point to vcpu stack. */
+        info = (struct cpu_info *)(XPTI_START(n) + STACK_SIZE) - 1;
+        info->stack_bottom_cpu =
+                   (unsigned long)&get_cpu_info()->guest_cpu_user_regs;
+    }
+
     if ( need_full_gdt(nd) &&
-         ((p->vcpu_id != n->vcpu_id) || !need_full_gdt(pd)) )
+         (!is_pv_gdt ||
+          (nd->arch.pv_domain.xpti && !is_domain_xpti_active(pd))) )
     {
         gdt_desc.limit = LAST_RESERVED_GDT_BYTE;
         gdt_desc.base = GDT_VIRT_START(n);
 
         lgdt(&gdt_desc);
+
+        /*
+         * Either we are currently on physical cpu TSS and stacks, or we have
+         * switched vcpu_id. In both cases we need to reload TSS and MSRs with
+         * stub addresses when we enter a XPTI domain.
+         */
+        if ( nd->arch.pv_domain.xpti )
+        {
+            unsigned long stub_va = XPTI_TRAMPOLINE(n);
+
+            _set_tss_desc(gdt + TSS_ENTRY - FIRST_RESERVED_GDT_ENTRY,
+                          XPTI_TSS(n),
+                          offsetof(struct tss_struct, __cacheline_filler) - 1,
+                          SYS_DESC_tss_avail);
+
+            ltr(TSS_ENTRY << 3);
+            get_cpu_info()->flags |= VCPUSTACK_ACTIVE;
+            wrmsrl(MSR_LSTAR,
+                   stub_va + ((unsigned long)&xpti_lstar & ~PAGE_MASK));
+            wrmsrl(MSR_CSTAR,
+                   stub_va + ((unsigned long)&xpti_cstar & ~PAGE_MASK));
+            if ( boot_cpu_data.x86_vendor == X86_VENDOR_INTEL ||
+                 boot_cpu_data.x86_vendor == X86_VENDOR_CENTAUR )
+                wrmsrl(MSR_IA32_SYSENTER_ESP,
+                       (unsigned long)&guest_cpu_user_regs()->es);
+        }
     }
 
     if ( pd != nd )
diff --git a/xen/include/asm-x86/regs.h b/xen/include/asm-x86/regs.h
index 725a664e0a..361de4c54e 100644
--- a/xen/include/asm-x86/regs.h
+++ b/xen/include/asm-x86/regs.h
@@ -7,6 +7,8 @@
 #define guest_mode(r)                                                         \
 ({                                                                            \
     unsigned long diff = (char *)guest_cpu_user_regs() - (char *)(r);         \
+    if ( diff >= STACK_SIZE )                                                 \
+        diff = (char *)&get_cpu_info()->guest_cpu_user_regs - (char *)(r);    \
     /* Frame pointer must point into current CPU stack. */                    \
     ASSERT(diff < STACK_SIZE);                                                \
     /* If not a guest frame, it must be a hypervisor frame. */                \
-- 
2.13.6


_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxxx
https://lists.xenproject.org/mailman/listinfo/xen-devel

 


Rackspace

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