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

[Xen-devel] [PATCH RFC v2 12/12] 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>
---
 xen/arch/x86/domain.c              | 76 +++++++++++++++++++++++++++++++++++---
 xen/arch/x86/pv/domain.c           | 34 +++++++++++++++--
 xen/include/asm-x86/desc.h         |  5 +++
 xen/include/asm-x86/regs.h         |  2 +
 4 files changed, 107 insertions(+), 10 deletions(-)

diff --git a/xen/arch/x86/domain.c b/xen/arch/x86/domain.c
index da1bf1a97b..d75234ca35 100644
--- a/xen/arch/x86/domain.c
+++ b/xen/arch/x86/domain.c
@@ -1585,9 +1585,28 @@ static inline bool need_full_gdt(const struct domain *d)
     return is_pv_domain(d) && !is_idle_domain(d);
 }
 
+static void copy_user_regs_from_stack(struct vcpu *v)
+{
+    struct cpu_user_regs *stack_regs;
+
+    stack_regs = (is_pv_vcpu(v) && v->domain->arch.pv_domain.xpti)
+                 ? v->arch.pv_vcpu.stack_regs
+                 : &get_cpu_info()->guest_cpu_user_regs;
+    memcpy(&v->arch.user_regs, stack_regs, CTXT_SWITCH_STACK_BYTES);
+}
+
+static void copy_user_regs_to_stack(struct vcpu *v)
+{
+    struct cpu_user_regs *stack_regs;
+
+    stack_regs = (is_pv_vcpu(v) && v->domain->arch.pv_domain.xpti)
+                 ? v->arch.pv_vcpu.stack_regs
+                 : &get_cpu_info()->guest_cpu_user_regs;
+    memcpy(stack_regs, &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;
@@ -1600,7 +1619,7 @@ static void __context_switch(void)
 
     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);
     }
@@ -1616,7 +1635,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;
@@ -1635,7 +1654,7 @@ static void __context_switch(void)
 
     gdt = !is_pv_32bit_domain(nd) ? per_cpu(gdt_table, cpu) :
                                     per_cpu(compat_gdt_table, cpu);
-    if ( need_full_gdt(nd) )
+    if ( need_full_gdt(nd) && !nd->arch.pv_domain.xpti )
     {
         unsigned long mfn = virt_to_mfn(gdt);
         l1_pgentry_t *pl1e = pv_gdt_ptes(n);
@@ -1647,23 +1666,68 @@ static void __context_switch(void)
     }
 
     if ( need_full_gdt(pd) &&
-         ((p->vcpu_id != n->vcpu_id) || !need_full_gdt(nd)) )
+         ((p->vcpu_id != n->vcpu_id) || !need_full_gdt(nd) ||
+          pd->arch.pv_domain.xpti) )
     {
         gdt_desc.limit = LAST_RESERVED_GDT_BYTE;
         gdt_desc.base  = (unsigned long)(gdt - FIRST_RESERVED_GDT_ENTRY);
 
+        if ( pd->arch.pv_domain.xpti )
+            _set_tssldt_type(gdt + TSS_ENTRY - FIRST_RESERVED_GDT_ENTRY,
+                             SYS_DESC_tss_avail);
+
         lgdt(&gdt_desc);
+
+        if ( pd->arch.pv_domain.xpti )
+        {
+            unsigned long stub_va = this_cpu(stubs.addr);
+
+            ltr(TSS_ENTRY << 3);
+            get_cpu_info()->flags &= ~VCPUSTACK_ACTIVE;
+            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);
+        }
     }
 
     write_ptbase(n);
 
     if ( need_full_gdt(nd) &&
-         ((p->vcpu_id != n->vcpu_id) || !need_full_gdt(pd)) )
+         ((p->vcpu_id != n->vcpu_id) || !need_full_gdt(pd) ||
+          nd->arch.pv_domain.xpti) )
     {
         gdt_desc.limit = LAST_RESERVED_GDT_BYTE;
         gdt_desc.base = GDT_VIRT_START(n);
 
+        if ( nd->arch.pv_domain.xpti )
+        {
+            struct cpu_info *info;
+
+            gdt = (struct desc_struct *)GDT_VIRT_START(n);
+            gdt[PER_CPU_GDT_ENTRY].a = cpu;
+            _set_tssldt_type(gdt + TSS_ENTRY, SYS_DESC_tss_avail);
+            info = (struct cpu_info *)(XPTI_START(n) + STACK_SIZE) - 1;
+            info->stack_bottom_cpu = (unsigned long)guest_cpu_user_regs();
+        }
+
         lgdt(&gdt_desc);
+
+        if ( nd->arch.pv_domain.xpti )
+        {
+            unsigned long stub_va = XPTI_TRAMPOLINE(n);
+
+            ltr(TSS_ENTRY << 3);
+            get_cpu_info()->flags |= VCPUSTACK_ACTIVE;
+            wrmsrl(MSR_LSTAR, stub_va);
+            wrmsrl(MSR_CSTAR, stub_va + STUB_TRAMPOLINE_SIZE_PERVCPU);
+            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/arch/x86/pv/domain.c b/xen/arch/x86/pv/domain.c
index 834be96ed8..6158086087 100644
--- a/xen/arch/x86/pv/domain.c
+++ b/xen/arch/x86/pv/domain.c
@@ -133,10 +133,36 @@ int switch_compat(struct domain *d)
 
 static int pv_create_gdt_ldt_l1tab(struct vcpu *v)
 {
-    return create_perdomain_mapping(v->domain, GDT_VIRT_START(v),
-                                    1U << GDT_LDT_VCPU_SHIFT,
-                                    v->domain->arch.pv_domain.gdt_ldt_l1tab,
-                                    NULL);
+    int rc;
+
+    rc = create_perdomain_mapping(v->domain, GDT_VIRT_START(v),
+                                  1U << GDT_LDT_VCPU_SHIFT,
+                                  v->domain->arch.pv_domain.gdt_ldt_l1tab,
+                                  NULL);
+    if ( !rc && v->domain->arch.pv_domain.xpti )
+    {
+        struct desc_struct *gdt;
+        struct page_info *gdt_pg;
+
+        BUILD_BUG_ON(NR_RESERVED_GDT_PAGES > 1);
+        gdt = (struct desc_struct *)GDT_VIRT_START(v) +
+              FIRST_RESERVED_GDT_ENTRY;
+        rc = create_perdomain_mapping(v->domain, (unsigned long)gdt,
+                                      NR_RESERVED_GDT_PAGES,
+                                      NULL, &gdt_pg);
+        if ( !rc )
+        {
+            gdt = __map_domain_page(gdt_pg);
+            memcpy(gdt, boot_cpu_gdt_table, NR_RESERVED_GDT_BYTES);
+            _set_tssldt_desc(gdt + TSS_ENTRY - FIRST_RESERVED_GDT_ENTRY,
+                         XPTI_TSS(v),
+                         offsetof(struct tss_struct, __cacheline_filler) - 1,
+                         SYS_DESC_tss_avail);
+            unmap_domain_page(gdt);
+        }
+    }
+
+    return rc;
 }
 
 static void pv_destroy_gdt_ldt_l1tab(struct vcpu *v)
diff --git a/xen/include/asm-x86/desc.h b/xen/include/asm-x86/desc.h
index 4093c65faa..d5fff4cce5 100644
--- a/xen/include/asm-x86/desc.h
+++ b/xen/include/asm-x86/desc.h
@@ -185,6 +185,11 @@ do {                                                     \
         (((u32)(addr) & 0x00FF0000U) >> 16);             \
 } while (0)
 
+#define _set_tssldt_type(desc,type)                      \
+do {                                                     \
+    ((u8 *)&(desc)[0].b)[1] = (type) | 0x80;             \
+} while (0)
+
 struct __packed desc_ptr {
        unsigned short limit;
        unsigned long base;
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®.