x86_64: basic changes for supporting compatibility mode guest (version 2)
RFC PATCH 1/4 x86_64: basic changes for supporting compatibility mode guest (version 2)
This introduces
- a (generic) config setting to turn on compatibility mode support
- support to load a Dom0 32-bit ELF image on 64-bit
- a compatibility guest mode GDT (requires moving the ring 0 selectors to different GDT slots), and the respective selector handling changes
- some initial code path changes to address the needs of a compatibility mode guest
Signed-off-by Jan Beulich <[email protected]>
config/x86_64.mk +1 -0
@@ -1,4 +1,5 @@ CONFIG_X86 := y +CONFIG_COMPAT := y CONFIG_HVM := y CONFIG_MIGRATE := y CONFIG_XCUTILS := y
xen/arch/x86/boot/x86_64.S +24 -4
@@ -223,15 +223,35 @@ high_start: .align PAGE_SIZE, 0 ENTRY(gdt_table) .quad 0x0000000000000000 /* unused */ - .quad 0x00cf9a000000ffff /* 0xe008 ring 0 code, compatibility */ - .quad 0x00af9a000000ffff /* 0xe010 ring 0 code, 64-bit mode */ - .quad 0x00cf92000000ffff /* 0xe018 ring 0 data */ + .quad 0x00af9a000000ffff /* 0xe008 ring 0 code, 64-bit mode */ + .quad 0x00cf92000000ffff /* 0xe010 ring 0 data */ + .quad 0x0000000000000000 /* reserved */ .quad 0x00cffa000000ffff /* 0xe023 ring 3 code, compatibility */ .quad 0x00cff2000000ffff /* 0xe02b ring 3 data */ .quad 0x00affa000000ffff /* 0xe033 ring 3 code, 64-bit mode */ - .quad 0x0000000000000000 /* unused */ + .quad 0x00cf9a000000ffff /* 0xe038 ring 0 code, compatibility */ + .org gdt_table - FIRST_RESERVED_GDT_BYTE + __TSS(0) * 8 .fill 4*NR_CPUS,8,0 /* space for TSS and LDT per CPU */ +#ifdef CONFIG_COMPAT +# define LIMIT(d) (((d) << 16) \ + | (((MACH2PHYS_COMPAT_VIRT_END - 1) >> PAGE_SHIFT) & 0xffff) \ + | (((MACH2PHYS_COMPAT_VIRT_END - 1) << (32 - PAGE_SHIFT)) & (0xf << 48))) + .align PAGE_SIZE, 0 +ENTRY(compat_gdt_table) + .quad 0x0000000000000000 /* unused */ + .quad 0x00af9a000000ffff /* 0xe008 ring 0 code, 64-bit mode */ + .quad 0x00cf92000000ffff /* 0xe010 ring 0 data */ + .quad LIMIT(0x00cfba000000) /* 0xe019 ring 1 code, compatibility */ + .quad LIMIT(0x00cfb2000000) /* 0xe021 ring 1 data */ + .quad LIMIT(0x00c0fa000000) /* 0xe02b ring 3 code, compatibility */ + .quad LIMIT(0x00c0f2000000) /* 0xe033 ring 3 data */ + .quad 0x00cf9a000000ffff /* 0xe038 ring 0 code, compatibility */ + .org compat_gdt_table - FIRST_RESERVED_GDT_BYTE + __TSS(0) * 8 + .fill 4*NR_CPUS,8,0 /* space for TSS and LDT per CPU */ +# undef LIMIT +#endif + /* Initial PML4 -- level-4 page table. */ .align PAGE_SIZE, 0 ENTRY(idle_pg_table)
xen/arch/x86/domain.c +105 -35
196 lines not on this page · raw message
xen/arch/x86/domain_build.c +65 -6
127 lines not on this page · raw message
xen/arch/x86/mm.c +6 -2
@@ -391,7 +391,7 @@ static int alloc_segdesc_page(struct pag descs =map_domain_page (page_to_mfn (page));for ( i =0 ; i <512 ; i++ ) -if (unlikely (!check_descriptor (&descs[i])) ) +if (unlikely (!check_descriptor (page_get_owner (page), &descs[i])) ) goto fail;unmap_domain_page (descs);
@@ -1746,6 +1746,8 @@ int new_guest_cr3(unsigned long mfn)/* Switch to idle pagetable: this VCPU has no active p.t. now. */ MEM_LOG ("New baseptr %lx: slow path via idle pagetables" , mfn); old_base_mfn =pagetable_get_pfn (v->arch.guest_table); +if ( old_base_mfn ==pagetable_get_pfn (v->arch.guest_table_user) ) + v->arch.guest_table_user =pagetable_null (); v->arch.guest_table =pagetable_null ();update_pagetables (v);write_cr3 (__pa (idle_pg_table));
@@ -1770,6 +1772,8 @@ int new_guest_cr3(unsigned long mfn) old_base_mfn =pagetable_get_pfn (v->arch.guest_table); v->arch.guest_table =pagetable_from_pfn (mfn); +if (IS_COMPAT (d) ) + v->arch.guest_table_user = v->arch.guest_table;update_pagetables (v);/* update shadow_table and monitor_table */ write_ptbase (v);
@@ -2882,7 +2886,7 @@ long do_update_descriptor(u64 pa, u64 deif ( !VALID_MFN (mfn =gmfn_to_mfn (dom, gmfn)) || (((unsigned int )pa %sizeof (struct desc_struct )) !=0 ) || !mfn_valid (mfn) || - !check_descriptor (&d) ) + !check_descriptor (dom, &d) ) {UNLOCK_BIGLOCK (dom);return -EINVAL ;
xen/arch/x86/traps.c +8 -1
@@ -1719,6 +1719,13 @@ void set_tss_desc(unsigned int n, void * (unsigned long )addr, offsetof(struct tss_struct , __cacheline_filler) -1 , 9); +#ifdef CONFIG_COMPAT +_set_tssldt_desc ( + compat_gdt_table +__TSS (n) -FIRST_RESERVED_GDT_ENTRY , + (unsigned long )addr, + offsetof(struct tss_struct , __cacheline_filler) -1 , +11 ); +#endif }void __init trap_init (void )
@@ -1793,7 +1800,7 @@ long do_set_trap_table(XEN_GUEST_HANDLE(if ( cur.address ==0 )break ; -fixup_guest_code_selector (cur.cs); +fixup_guest_code_selector (current->domain, cur.cs);memcpy (&dst[cur.vector], &cur,sizeof (cur));
xen/arch/x86/x86_32/mm.c +6 -6
@@ -226,7 +226,7 @@ long do_stack_switch(unsigned long ss, uint nr =smp_processor_id ();struct tss_struct *t = &init_tss[nr]; -fixup_guest_stack_selector (ss); +fixup_guest_stack_selector (current->domain, ss); current->arch.guest_context.kernel_ss = ss; current->arch.guest_context.kernel_sp = esp;
@@ -237,7 +237,7 @@ long do_stack_switch(unsigned long ss, u }/* Returns TRUE if given descriptor is valid for GDT or LDT. */ -int check_descriptor (struct desc_struct *d) +int check_descriptor (const struct domain *dom,struct desc_struct *d) {unsigned long base, limit;u32 a = d->a, b = d->b;
@@ -257,8 +257,8 @@ int check_descriptor(struct desc_struct* gates (consider a call gate pointing at another kernel descriptor with * DPL 0 -- this would get the OS ring-0 privileges). */ -if ( (b & _SEGMENT_DPL) < (GUEST_KERNEL_RPL <<13 ) ) - d->b = b = (b & ~_SEGMENT_DPL) | (GUEST_KERNEL_RPL <<13 ); +if ( (b & _SEGMENT_DPL) < (GUEST_KERNEL_RPL (dom) <<13 ) ) + d->b = b = (b & ~_SEGMENT_DPL) | (GUEST_KERNEL_RPL (dom) <<13 );if ( !(b & _SEGMENT_S) ) {
@@ -280,8 +280,8 @@ int check_descriptor(struct desc_struct/* Validate and fix up the target code selector. */ cs = a >>16 ; -fixup_guest_code_selector (cs); -if ( !guest_gate_selector_okay (cs) ) +fixup_guest_code_selector (dom, cs); +if ( !guest_gate_selector_okay (dom, cs) ) goto bad; a = d->a = (d->a &0xffffU ) | (cs <<16 );
xen/arch/x86/x86_32/traps.c +2 -2
@@ -284,7 +284,7 @@ void init_int80_direct_trap(struct vcpu* switch to the Xen stack and we need to swap back to the guest * kernel stack before passing control to the system call entry point. */ -if (TI_GET_IF (ti) || !guest_gate_selector_okay (ti->cs) || +if (TI_GET_IF (ti) || !guest_gate_selector_okay (v->domain, ti->cs) || supervisor_mode_kernel ) { v->arch.int80_desc.a = v->arch.int80_desc.b =0 ;
@@ -314,7 +314,7 @@ static long register_guest_callback(strulong ret =0 ;struct vcpu *v = current; -fixup_guest_code_selector (reg->address.cs); +fixup_guest_code_selector (v->domain, reg->address.cs);switch ( reg->type ) {
xen/arch/x86/x86_64/asm-offsets.c +6 -2
@@ -58,12 +58,16 @@ void __dummy__(void) OFFSET(VCPU_thread_flags, struct vcpu, arch.flags); OFFSET(VCPU_event_addr, struct vcpu, arch.guest_context.event_callback_eip); + OFFSET(VCPU_event_sel, struct vcpu, + arch.guest_context.event_callback_cs); OFFSET(VCPU_failsafe_addr, struct vcpu, arch.guest_context.failsafe_callback_eip); + OFFSET(VCPU_failsafe_sel, struct vcpu, + arch.guest_context.failsafe_callback_cs); OFFSET(VCPU_syscall_addr, struct vcpu, arch.guest_context.syscall_callback_eip); - OFFSET(VCPU_kernel_sp, struct vcpu, - arch.guest_context.kernel_sp); + OFFSET(VCPU_kernel_sp, struct vcpu, arch.guest_context.kernel_sp); + OFFSET(VCPU_kernel_ss, struct vcpu, arch.guest_context.kernel_ss); OFFSET(VCPU_guest_context_flags, struct vcpu, arch.guest_context.flags); OFFSET(VCPU_arch_guest_fpu_ctxt, struct vcpu, arch.guest_context.fpu_ctxt); OFFSET(VCPU_flags, struct vcpu, vcpu_flags);
xen/arch/x86/x86_64/mm.c +6 -6
@@ -222,7 +222,7 @@ long subarch_memory_op(int op, XEN_GUESTlong do_stack_switch (unsigned long ss,unsigned long esp) { -fixup_guest_stack_selector (ss); +fixup_guest_stack_selector (current->domain, ss); current->arch.guest_context.kernel_ss = ss; current->arch.guest_context.kernel_sp = esp;return 0 ;
@@ -282,7 +282,7 @@ long do_set_segment_base(unsigned int wh/* Returns TRUE if given descriptor is valid for GDT or LDT. */ -int check_descriptor (struct desc_struct *d) +int check_descriptor (const struct domain *dom,struct desc_struct *d) {u32 a = d->a, b = d->b;u16 cs;
@@ -292,8 +292,8 @@ int check_descriptor(struct desc_struct goto good;/* Check and fix up the DPL. */ -if ( (b & _SEGMENT_DPL) < (GUEST_KERNEL_RPL <<13 ) ) - d->b = b = (b & ~_SEGMENT_DPL) | (GUEST_KERNEL_RPL <<13 ); +if ( (b & _SEGMENT_DPL) < (GUEST_KERNEL_RPL (dom) <<13 ) ) + d->b = b = (b & ~_SEGMENT_DPL) | (GUEST_KERNEL_RPL (dom) <<13 );/* All code and data segments are okay. No base/limit checking. */ if ( (b & _SEGMENT_S) )
@@ -309,8 +309,8 @@ int check_descriptor(struct desc_struct/* Validate and fix up the target code selector. */ cs = a >>16 ; -fixup_guest_code_selector (cs); -if ( !guest_gate_selector_okay (cs) ) +fixup_guest_code_selector (dom, cs); +if ( !guest_gate_selector_okay (dom, cs) ) goto bad; a = d->a = (d->a &0xffffU ) | (cs <<16 );
xen/arch/x86/x86_64/traps.c +2 -0
@@ -172,6 +172,8 @@ asmlinkage void do_double_fault(struct cvoid toggle_guest_mode (struct vcpu *v) { +if (IS_COMPAT (v->domain) ) +return ; v->arch.flags ^= TF_kernel_mode; __asm__ __volatile__ ("swapgs" );update_pagetables (v);
xen/common/Makefile +1 -0
@@ -3,6 +3,7 @@ obj-y += bitmap.o obj-y += dom0_ops.o obj-y += domain.o obj-y += elf.o +obj-$(CONFIG_COMPAT) += elf32.o obj-y += event_channel.o obj-y += grant_table.o obj-y += kernel.o
xen/common/elf.c +1 -1
@@ -29,7 +29,7 @@ int parseelfimage(struct domain_setup_inint h, virt_base_defined, elf_pa_off_defined;if ( !elf_sanity_check (ehdr) ) -return -EINVAL ; +return -ENOSYS ;if ( (ehdr->e_phoff + (ehdr->e_phnum*ehdr->e_phentsize)) > dsi->image_len ) {
xen/common/elf32.c new file +16 -0
@@ -0,0 +1,16 @@ +/****************************************************************************** +* elf32.c +* +* Stub to support 32-bit ELF images on 64-bit platforms. +*/ + +#include <xen/config.h> + +#undef ELFSIZE +#define ELFSIZE 32 + +#define parseelfimage parseelf32image +#define loadelfimage loadelf32image +#define elf_sanity_check elf32_sanity_check + +#include "elf.c"
xen/include/asm-x86/config.h +13 -3
@@ -86,6 +86,7 @@ extern unsigned long _end; /* standard E#if defined(__x86_64__)#define CONFIG_X86_64 1 +#define CONFIG_COMPAT 1#define asmlinkage
@@ -175,13 +176,22 @@ extern unsigned long _end; /* standard E#define DIRECTMAP_VIRT_START (PML4_ADDR(262))#define DIRECTMAP_VIRT_END (DIRECTMAP_VIRT_START + PML4_ENTRY_BYTES*2) +#define __HYPERVISOR_COMPAT_VIRT_START 0xF5800000 +#define __MACH2PHYS_COMPAT_VIRT_START 0xF5800000 +#define __MACH2PHYS_COMPAT_VIRT_END 0xF6800000 +#define HYPERVISOR_COMPAT_VIRT_START mk_unsigned_long(__HYPERVISOR_COMPAT_VIRT_START) +#define MACH2PHYS_COMPAT_VIRT_START mk_unsigned_long(__MACH2PHYS_COMPAT_VIRT_START) +#define MACH2PHYS_COMPAT_VIRT_END mk_unsigned_long(__MACH2PHYS_COMPAT_VIRT_END) +#define MACH2PHYS_COMPAT_NR_ENTRIES ((MACH2PHYS_COMPAT_VIRT_END-MACH2PHYS_COMPAT_VIRT_START)>>2) +#define compat_machine_to_phys_mapping ((unsigned long *)MACH2PHYS_COMPAT_VIRT_START) +#define PGT_base_page_table PGT_l4_page_table -#define __HYPERVISOR_CS64 0xe010 -#define __HYPERVISOR_CS32 0xe008 +#define __HYPERVISOR_CS64 0xe008 +#define __HYPERVISOR_CS32 0xe038#define __HYPERVISOR_CS __HYPERVISOR_CS64#define __HYPERVISOR_DS64 0x0000 -#define __HYPERVISOR_DS32 0xe018 +#define __HYPERVISOR_DS32 0xe010#define __HYPERVISOR_DS __HYPERVISOR_DS64/* For generic assembly code: use macros to define operation/operand sizes. */
xen/include/asm-x86/desc.h +64 -24
138 lines not on this page · raw message
xen/include/asm-x86/ldt.h +2 -1
@@ -17,7 +17,8 @@ static inline void load_LDT(struct vcpuelse { cpu =smp_processor_id (); - desc = gdt_table +__LDT (cpu) -FIRST_RESERVED_GDT_ENTRY ; + desc = (!IS_COMPAT (v->domain) ? gdt_table : compat_gdt_table) + +__LDT (cpu) -FIRST_RESERVED_GDT_ENTRY ;_set_tssldt_desc (desc,LDT_VIRT_START (v), ents*8 -1 ,2 ); __asm__ __volatile__ ("lldt %%ax" : :"a" (__LDT (cpu)<<3 ) ); }
xen/include/asm-x86/mm.h +1 -1
@@ -254,7 +254,7 @@ static inline int page_is_removable(struASSERT (((_p)->count_info & PGC_count_mask) !=0 ); \ ASSERT(page_get_owner(_p) == (_d)) -int check_descriptor (struct desc_struct *d); +int check_descriptor (const struct domain *,struct desc_struct *d);/* * The MPT (machine->physical mapping table) is an array of word-sized
xen/include/asm-x86/regs.h +2 -1
@@ -38,7 +38,8 @@ enum EFLAGS {ASSERT (diff <STACK_SIZE ); \/* If a guest frame, it must be have guest privs (unless HVM guest). */ \/* We permit CS==0 which can come from an uninitialised trap entry. */ \ -ASSERT ((diff !=0 ) ||vm86_mode (r) || ((r->cs&3 ) >=GUEST_KERNEL_RPL ) || \ +ASSERT ((diff !=0 ) ||vm86_mode (r) || \ + ((r->cs&3 ) >=GUEST_KERNEL_RPL (current->domain)) || \ (r->cs ==0 ) ||hvm_guest (current)); \/* If not a guest frame, it must be a hypervisor frame. */ \ASSERT ((diff ==0 ) || (!vm86_mode (r) && (r->cs == __HYPERVISOR_CS))); \
xen/include/asm-x86/x86_64/regs.h +3 -1
@@ -11,7 +11,9 @@#define ring_3 (r) (((r)->cs & 3) == 3)#define guest_kernel_mode (v, r) \ - (ring_3(r) && ((v)->arch.flags & TF_kernel_mode)) + (!IS_COMPAT(v->domain) ? \ + ring_3(r) && ((v)->arch.flags & TF_kernel_mode) : \ + ring_1(r))#define permit_softint (dpl, v, r) \ ((dpl) >= (guest_kernel_mode(v, r) ? 1 : 3))
xen/include/public/arch-x86_64.h +14 -1
@@ -176,7 +176,10 @@ DEFINE_XEN_GUEST_HANDLE(trap_info_t);#ifdef __GNUC__/* Anonymous union includes both 32- and 64-bit names (e.g., eax/rax). */ -#define __DECL_REG (name) union { uint64_t r ## name, e ## name; } +#define __DECL_REG (name) union { \ + uint64_t r ## name, e ## name; \ + uint32_t _e ## name; \ +}#else /* Non-gcc sources must always use the proper 64-bit name (e.g., rax). */ #define __DECL_REG (name) uint64_t r ## name
@@ -249,7 +252,17 @@ struct vcpu_guest_context {unsigned long debugreg[8 ];/* DB0-DB7 (debug registers) */ unsigned long event_callback_eip;unsigned long failsafe_callback_eip; +#ifdef __GNUC__ +union { +unsigned long syscall_callback_eip; +struct { +unsigned int event_callback_cs;/* compat CS of event cb */ +unsigned int failsafe_callback_cs;/* compat CS of failsafe cb */ + }; + }; +#else unsigned long syscall_callback_eip; +#endif unsigned long vm_assist;/* VMASST_TYPE_* bitmap */ /* Segment base addresses. */ uint64_t fs_base;
xen/include/xen/elf.h +6 -0
@@ -529,6 +529,12 @@ struct domain_setup_info;extern int loadelfimage (struct domain_setup_info *);extern int parseelfimage (struct domain_setup_info *); +#ifdef CONFIG_COMPAT +extern int loadelf32image (struct domain_setup_info *); +extern int parseelf32image (struct domain_setup_info *); +extern int elf32_sanity_check (Elf32_Ehdr *ehdr); +#endif +#ifdef Elf_Ehdrextern int elf_sanity_check (Elf_Ehdr *ehdr);#endif
xen/include/xen/sched.h +10 -0
@@ -404,6 +404,9 @@ extern struct domain *domain_list;/* Domain is paused by the hypervisor? */ #define _DOMF_paused 6#define DOMF_paused (1UL<<_DOMF_paused) +/* Domain is a compatibility one? */ +#define _DOMF_compat 7 +#define DOMF_compat (1UL<<_DOMF_compat)static inline int vcpu_runnable (struct vcpu *v) {
@@ -434,6 +437,13 @@ static inline void vcpu_unblock(struct v#define IS_PRIV (_d) \ (test_bit(_DOMF_privileged, &(_d)->domain_flags)) +#ifdef CONFIG_COMPAT +#define IS_COMPAT (_d) \ + (test_bit(_DOMF_compat, &(_d)->domain_flags)) +#else +#define IS_COMPAT (_d) 0 +#endif +#define VM_ASSIST (_d,_t) (test_bit((_t), &(_d)->vm_assist))#endif /* __SCHED_H__ */
_______________________________________________
Xen-devel mailing list
[email protected]
http://lists.xensource.com/xen-devel