On Fri, Apr 12, 2013 at 01:14:47PM +0200, Kevin Wolf wrote: > This adds a test case that jumps into VM86 by iret-ing to a TSS and back > to Protected Mode using a task gate in the IDT. > > Signed-off-by: Kevin Wolf <kwolf@xxxxxxxxxx> Applied, thanks. Found a bug with it and emulate_invalid_guest_state=1 which is default. Are you running with emulate_invalid_guest_state=0? > --- > lib/x86/desc.c | 41 ++++------------------------------ > lib/x86/desc.h | 36 ++++++++++++++++++++++++++++++ > lib/x86/vm.c | 4 ++-- > lib/x86/vm.h | 1 + > x86/taskswitch2.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++----- > 5 files changed, 104 insertions(+), 45 deletions(-) > > diff --git a/lib/x86/desc.c b/lib/x86/desc.c > index 770c250..7c5c721 100644 > --- a/lib/x86/desc.c > +++ b/lib/x86/desc.c > @@ -27,41 +27,6 @@ typedef struct { > u8 base_high; > } gdt_entry_t; > > -typedef struct { > - u16 prev; > - u16 res1; > - u32 esp0; > - u16 ss0; > - u16 res2; > - u32 esp1; > - u16 ss1; > - u16 res3; > - u32 esp2; > - u16 ss2; > - u16 res4; > - u32 cr3; > - u32 eip; > - u32 eflags; > - u32 eax, ecx, edx, ebx, esp, ebp, esi, edi; > - u16 es; > - u16 res5; > - u16 cs; > - u16 res6; > - u16 ss; > - u16 res7; > - u16 ds; > - u16 res8; > - u16 fs; > - u16 res9; > - u16 gs; > - u16 res10; > - u16 ldt; > - u16 res11; > - u16 t:1; > - u16 res12:15; > - u16 iomap_base; > -} tss32_t; > - > extern idt_entry_t boot_idt[256]; > > void set_idt_entry(int vec, void *addr, int dpl) > @@ -268,9 +233,11 @@ unsigned exception_error_code(void) > * 0x18 - Not presend code segment > * 0x20 - Primery task > * 0x28 - Interrupt task > + * > + * 0x30 to 0x48 - Free to use for test cases > */ > > -static gdt_entry_t gdt[6]; > +static gdt_entry_t gdt[10]; > #define TSS_GDT_OFFSET 4 > > void set_gdt_entry(int num, u32 base, u32 limit, u8 access, u8 gran) > @@ -327,7 +294,7 @@ void setup_gdt(void) > ".Lflush2: "::"r"(0x10)); > } > > -static void set_idt_task_gate(int vec, u16 sel) > +void set_idt_task_gate(int vec, u16 sel) > { > idt_entry_t *e = &boot_idt[vec]; > > diff --git a/lib/x86/desc.h b/lib/x86/desc.h > index 0b4897c..f819452 100644 > --- a/lib/x86/desc.h > +++ b/lib/x86/desc.h > @@ -24,6 +24,41 @@ struct ex_regs { > unsigned long rflags; > }; > > +typedef struct { > + u16 prev; > + u16 res1; > + u32 esp0; > + u16 ss0; > + u16 res2; > + u32 esp1; > + u16 ss1; > + u16 res3; > + u32 esp2; > + u16 ss2; > + u16 res4; > + u32 cr3; > + u32 eip; > + u32 eflags; > + u32 eax, ecx, edx, ebx, esp, ebp, esi, edi; > + u16 es; > + u16 res5; > + u16 cs; > + u16 res6; > + u16 ss; > + u16 res7; > + u16 ds; > + u16 res8; > + u16 fs; > + u16 res9; > + u16 gs; > + u16 res10; > + u16 ldt; > + u16 res11; > + u16 t:1; > + u16 res12:15; > + u16 iomap_base; > +} tss32_t; > + > #define ASM_TRY(catch) \ > "movl $0, %%gs:4 \n\t" \ > ".pushsection .data.ex \n\t" \ > @@ -44,6 +79,7 @@ unsigned exception_error_code(void); > void set_idt_entry(int vec, void *addr, int dpl); > void set_idt_sel(int vec, u16 sel); > void set_gdt_entry(int num, u32 base, u32 limit, u8 access, u8 gran); > +void set_idt_task_gate(int vec, u16 sel); > void set_intr_task_gate(int e, void *fn); > void print_current_tss_info(void); > void handle_exception(u8 v, void (*func)(struct ex_regs *regs)); > diff --git a/lib/x86/vm.c b/lib/x86/vm.c > index 2852c6c..260ec45 100644 > --- a/lib/x86/vm.c > +++ b/lib/x86/vm.c > @@ -109,14 +109,14 @@ void install_large_page(unsigned long *cr3, > unsigned long phys, > void *virt) > { > - install_pte(cr3, 2, virt, phys | PTE_PRESENT | PTE_WRITE | PTE_PSE, 0); > + install_pte(cr3, 2, virt, phys | PTE_PRESENT | PTE_WRITE | PTE_USER | PTE_PSE, 0); > } > > void install_page(unsigned long *cr3, > unsigned long phys, > void *virt) > { > - install_pte(cr3, 1, virt, phys | PTE_PRESENT | PTE_WRITE, 0); > + install_pte(cr3, 1, virt, phys | PTE_PRESENT | PTE_WRITE | PTE_USER, 0); > } > > > diff --git a/lib/x86/vm.h b/lib/x86/vm.h > index 3473f8d..0b5b5c7 100644 > --- a/lib/x86/vm.h > +++ b/lib/x86/vm.h > @@ -13,6 +13,7 @@ > #define PTE_PRESENT (1ull << 0) > #define PTE_PSE (1ull << 7) > #define PTE_WRITE (1ull << 1) > +#define PTE_USER (1ull << 2) > #define PTE_ADDR (0xffffffffff000ull) > > void setup_vm(); > diff --git a/x86/taskswitch2.c b/x86/taskswitch2.c > index 683834e..6573696 100644 > --- a/x86/taskswitch2.c > +++ b/x86/taskswitch2.c > @@ -5,6 +5,10 @@ > #include "processor.h" > #include "vm.h" > > +#define FREE_GDT_INDEX 6 > +#define MAIN_TSS_INDEX (FREE_GDT_INDEX + 0) > +#define VM86_TSS_INDEX (FREE_GDT_INDEX + 1) > + > #define xstr(s) str(s) > #define str(s) #s > > @@ -113,15 +117,10 @@ start: > goto start; > } > > -int main() > +void test_kernel_mode_int() > { > unsigned int res; > > - setup_vm(); > - setup_idt(); > - setup_gdt(); > - setup_tss32(); > - > /* test that int $2 triggers task gate */ > test_count = 0; > set_intr_task_gate(2, nmi_tss); > @@ -217,6 +216,62 @@ int main() > asm volatile ("ljmp $" xstr(TSS_INTR) ", $0xf4f4f4f4"); > printf("Jump back succeeded\n"); > report("ljmp", test_count == 1); > +} > + > +void test_vm86_switch(void) > +{ > + static tss32_t main_tss; > + static tss32_t vm86_tss; > + > + u8 *vm86_start; > + > + /* Write a 'ud2' instruction somewhere below 1 MB */ > + vm86_start = (void*) 0x42000; > + vm86_start[0] = 0x0f; > + vm86_start[1] = 0x0b; > + > + /* Main TSS */ > + set_gdt_entry(MAIN_TSS_INDEX, (u32)&main_tss, sizeof(tss32_t) - 1, 0x89, 0); > + ltr(MAIN_TSS_INDEX << 3); > + main_tss = (tss32_t) { > + .prev = VM86_TSS_INDEX << 3, > + .cr3 = read_cr3(), > + }; > + > + /* VM86 TSS (marked as busy, so we can iret to it) */ > + set_gdt_entry(VM86_TSS_INDEX, (u32)&vm86_tss, sizeof(tss32_t) - 1, 0x8b, 0); > + vm86_tss = (tss32_t) { > + .eflags = 0x20002, > + .cr3 = read_cr3(), > + .eip = (u32) vm86_start & 0x0f, > + .cs = (u32) vm86_start >> 4, > + .ds = 0x1234, > + .es = 0x2345, > + }; > + > + /* Setup task gate to main TSS for #UD */ > + set_idt_task_gate(6, MAIN_TSS_INDEX << 3); > + > + /* Jump into VM86 task with iret, #UD lets it come back immediately */ > + printf("Switch to VM86 task and back\n"); > + asm volatile( > + "pushf\n" > + "orw $0x4000, (%esp)\n" > + "popf\n" > + "iret\n" > + ); > + report("VM86", 1); > +} > + > +int main() > +{ > + setup_vm(); > + setup_idt(); > + setup_gdt(); > + setup_tss32(); > + > + test_kernel_mode_int(); > + test_vm86_switch(); > > printf("\nsummary: %d tests, %d failures\n", g_tests, g_fail); > > -- > 1.8.1.4 -- Gleb. -- To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html