Message ID | 1365765287-5759-1-git-send-email-kwolf@redhat.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
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@redhat.com> 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@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Am 14.04.2013 um 14:42 hat Gleb Natapov geschrieben: > 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@redhat.com> > 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? Not knowingly at least, I didn't specify any module options. I guess I just have enable_unrestricted_guest == true, which makes guest_state_valid() return true immediately. Kevin -- To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On Mon, Apr 15, 2013 at 10:56:42AM +0200, Kevin Wolf wrote: > Am 14.04.2013 um 14:42 hat Gleb Natapov geschrieben: > > 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@redhat.com> > > 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? > > Not knowingly at least, I didn't specify any module options. I > guess I just have enable_unrestricted_guest == true, which makes > guest_state_valid() return true immediately. > Can you check in /sys/module/kvm_intel/parameters/emulate_invalid_guest_state and /sys/module/kvm_intel/parameters/unrestricted_guest? You shouldn't have failed entry problem with enable_unrestricted_guest == true either. -- Gleb. -- To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Am 15.04.2013 um 17:38 hat Gleb Natapov geschrieben: > On Mon, Apr 15, 2013 at 10:56:42AM +0200, Kevin Wolf wrote: > > Am 14.04.2013 um 14:42 hat Gleb Natapov geschrieben: > > > 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@redhat.com> > > > 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? > > > > Not knowingly at least, I didn't specify any module options. I > > guess I just have enable_unrestricted_guest == true, which makes > > guest_state_valid() return true immediately. > > > Can you check in > /sys/module/kvm_intel/parameters/emulate_invalid_guest_state and > /sys/module/kvm_intel/parameters/unrestricted_guest? You shouldn't have > failed entry problem with enable_unrestricted_guest == true either. $ cat /sys/module/kvm_intel/parameters/emulate_invalid_guest_state Y $ cat /sys/module/kvm_intel/parameters/unrestricted_guest Y Why do you think that I wouldn't have the failed entry check with unrestricted_guest == true? The task switch is always done in software and if it leads to an invalid segment descriptor in the VMCS, then a failed VM entry looks quite expected to me. Kevin -- To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On Mon, Apr 15, 2013 at 05:42:27PM +0200, Kevin Wolf wrote: > Am 15.04.2013 um 17:38 hat Gleb Natapov geschrieben: > > On Mon, Apr 15, 2013 at 10:56:42AM +0200, Kevin Wolf wrote: > > > Am 14.04.2013 um 14:42 hat Gleb Natapov geschrieben: > > > > 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@redhat.com> > > > > 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? > > > > > > Not knowingly at least, I didn't specify any module options. I > > > guess I just have enable_unrestricted_guest == true, which makes > > > guest_state_valid() return true immediately. > > > > > Can you check in > > /sys/module/kvm_intel/parameters/emulate_invalid_guest_state and > > /sys/module/kvm_intel/parameters/unrestricted_guest? You shouldn't have > > failed entry problem with enable_unrestricted_guest == true either. > > $ cat /sys/module/kvm_intel/parameters/emulate_invalid_guest_state > Y > $ cat /sys/module/kvm_intel/parameters/unrestricted_guest > Y > > Why do you think that I wouldn't have the failed entry check with > unrestricted_guest == true? The task switch is always done in software > and if it leads to an invalid segment descriptor in the VMCS, then a > failed VM entry looks quite expected to me. > unrestricted_guest eliminates a lot of check on vcpu state during guest entry since now real mode, along with big real mode, are virtualized directly by the CPU. But you are right that with VM86 we hit the check that is still there: if vcpu is in the VM86 mode segment registers should be in a specific state. -- Gleb. -- To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
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);
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@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(-)