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 <kw...@redhat.com> --- 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 -- To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html