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> --- config-i386.mak | 3 +- lib/x86/desc.c | 37 +----------------------------- lib/x86/desc.h | 36 +++++++++++++++++++++++++++++ lib/x86/vm.c | 4 +- lib/x86/vm.h | 1 + x86/taskswitch_vm86.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++ x86/unittests.cfg | 6 +++++ 7 files changed, 107 insertions(+), 39 deletions(-) create mode 100644 x86/taskswitch_vm86.c diff --git a/config-i386.mak b/config-i386.mak index de52f3d..b5c3b9c 100644 --- a/config-i386.mak +++ b/config-i386.mak @@ -5,9 +5,10 @@ ldarch = elf32-i386 CFLAGS += -D__i386__ CFLAGS += -I $(KERNELDIR)/include -tests = $(TEST_DIR)/taskswitch.flat $(TEST_DIR)/taskswitch2.flat +tests = $(TEST_DIR)/taskswitch.flat $(TEST_DIR)/taskswitch2.flat $(TEST_DIR)/taskswitch_vm86.flat include config-x86-common.mak $(TEST_DIR)/taskswitch.elf: $(cstart.o) $(TEST_DIR)/taskswitch.o $(TEST_DIR)/taskswitch2.elf: $(cstart.o) $(TEST_DIR)/taskswitch2.o +$(TEST_DIR)/taskswitch_vm86.elf: $(cstart.o) $(TEST_DIR)/taskswitch_vm86.o diff --git a/lib/x86/desc.c b/lib/x86/desc.c index 770c250..c4a3607 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) @@ -327,7 +292,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 abbb0c9..aae044a 100644 --- a/lib/x86/vm.c +++ b/lib/x86/vm.c @@ -108,14 +108,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 bf8fd52..aebc5c3 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/taskswitch_vm86.c b/x86/taskswitch_vm86.c new file mode 100644 index 0000000..363cb00 --- /dev/null +++ b/x86/taskswitch_vm86.c @@ -0,0 +1,59 @@ +#include "libcflat.h" +#include "desc.h" +#include "processor.h" +#include "vm.h" + +static tss32_t main_tss; +static tss32_t vm86_tss; + +#define FREE_GDT_INDEX 4 +#define MAIN_TSS_INDEX (FREE_GDT_INDEX + 0) +#define VM86_TSS_INDEX (FREE_GDT_INDEX + 1) + +extern void vm86_start(void); + +int main(void) +{ + u8 *vm86_start; + + setup_vm(); + setup_idt(); + setup_gdt(); + + /* 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 */ + asm volatile( + "pushf\n" + "orw $0x4000, (%esp)\n" + "popf\n" + "iret\n" + ); + + return 0; +} diff --git a/x86/unittests.cfg b/x86/unittests.cfg index dac7d44..194850b 100644 --- a/x86/unittests.cfg +++ b/x86/unittests.cfg @@ -76,6 +76,12 @@ smp = 2 extra_params = -cpu qemu64,-svm groups = task +[taskswitch_vm86] +file = taskswitch_vm86.flat +smp = 2 +extra_params = -cpu qemu64,-svm +groups = task + [kvmclock_test] file = kvmclock_test.flat smp = 2 -- 1.7.6.5 -- 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