|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [Xen-devel] [PATCH RFC v2 12/12] x86: activate per-vcpu stacks in case of xpti
>>> On 22.01.18 at 13:32, <jgross@xxxxxxxx> wrote:
> 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;
const
> + 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;
Ugly open coding of what previously was 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)
const
> @@ -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);
Why is this not done in the if() after lgdt()?
> 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);
Why is this not - like below - &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);
> + }
So on a switch from PV to PV you add two LTR and 6 WRMSR. Quite
a lot, and I'm not at all convinced that this double writing is all really
needed in such a case.
> --- 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;
> }
Since you fiddle with the GDT anyway during context switch - do
you really need to allocate another page here, rather than simply
mapping the pCPU's GDT page into the vCPU's per-domain area?
That would also eliminate a concern regarding changes being made
to the GDT after a domain was created.
Jan
_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxxx
https://lists.xenproject.org/mailman/listinfo/xen-devel
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |