1d21b4f12SGleb Natapov #include "libcflat.h" 2d21b4f12SGleb Natapov #include "desc.h" 3d21b4f12SGleb Natapov #include "apic-defs.h" 4d21b4f12SGleb Natapov #include "apic.h" 5d21b4f12SGleb Natapov #include "processor.h" 6ce71ddc7SGleb Natapov #include "vm.h" 7efd8e5aaSPaolo Bonzini #include "vmalloc.h" 85aca024eSPaolo Bonzini #include "alloc_page.h" 9e0e5509bSNadav Amit #include "delay.h" 10d21b4f12SGleb Natapov 1167961d18SPaolo Bonzini #define MAIN_TSS_SEL (FIRST_SPARE_SEL + 0) 1267961d18SPaolo Bonzini #define VM86_TSS_SEL (FIRST_SPARE_SEL + 8) 133cdcf179SNadav Amit #define CONFORM_CS_SEL (FIRST_SPARE_SEL + 16) 14b01c8823SKevin Wolf 15d21b4f12SGleb Natapov static volatile int test_count; 16d21b4f12SGleb Natapov static volatile unsigned int test_divider; 17d21b4f12SGleb Natapov 18ce71ddc7SGleb Natapov static char *fault_addr; 19ce71ddc7SGleb Natapov static ulong fault_phys; 20ce71ddc7SGleb Natapov 2106846df5SThomas Huth void do_pf_tss(ulong *error_code); 2206846df5SThomas Huth 23d21b4f12SGleb Natapov static void nmi_tss(void) 24d21b4f12SGleb Natapov { 25d21b4f12SGleb Natapov start: 26d21b4f12SGleb Natapov printf("NMI task is running\n"); 27d21b4f12SGleb Natapov print_current_tss_info(); 28d21b4f12SGleb Natapov test_count++; 29d21b4f12SGleb Natapov asm volatile ("iret"); 30d21b4f12SGleb Natapov goto start; 31d21b4f12SGleb Natapov } 32d21b4f12SGleb Natapov 33d21b4f12SGleb Natapov static void de_tss(void) 34d21b4f12SGleb Natapov { 35d21b4f12SGleb Natapov start: 36d21b4f12SGleb Natapov printf("DE task is running\n"); 37d21b4f12SGleb Natapov print_current_tss_info(); 38d21b4f12SGleb Natapov test_divider = 10; 39d21b4f12SGleb Natapov test_count++; 40d21b4f12SGleb Natapov asm volatile ("iret"); 41d21b4f12SGleb Natapov goto start; 42d21b4f12SGleb Natapov } 43d21b4f12SGleb Natapov 44d21b4f12SGleb Natapov static void of_tss(void) 45d21b4f12SGleb Natapov { 46d21b4f12SGleb Natapov start: 47d21b4f12SGleb Natapov printf("OF task is running\n"); 48d21b4f12SGleb Natapov print_current_tss_info(); 49d21b4f12SGleb Natapov test_count++; 50d21b4f12SGleb Natapov asm volatile ("iret"); 51d21b4f12SGleb Natapov goto start; 52d21b4f12SGleb Natapov } 53d21b4f12SGleb Natapov 54d21b4f12SGleb Natapov static void bp_tss(void) 55d21b4f12SGleb Natapov { 56d21b4f12SGleb Natapov start: 57d21b4f12SGleb Natapov printf("BP task is running\n"); 58d21b4f12SGleb Natapov print_current_tss_info(); 59d21b4f12SGleb Natapov test_count++; 60d21b4f12SGleb Natapov asm volatile ("iret"); 61d21b4f12SGleb Natapov goto start; 62d21b4f12SGleb Natapov } 63d21b4f12SGleb Natapov 64ce71ddc7SGleb Natapov void do_pf_tss(ulong *error_code) 65ce71ddc7SGleb Natapov { 66b006d7ebSAndrew Jones printf("PF task is running %p %lx\n", error_code, *error_code); 67ce71ddc7SGleb Natapov print_current_tss_info(); 68b006d7ebSAndrew Jones if (*error_code == 0x2) /* write access, not present */ 69ce71ddc7SGleb Natapov test_count++; 70ce71ddc7SGleb Natapov install_pte(phys_to_virt(read_cr3()), 1, fault_addr, 71d10d16e1SAlexander Gordeev fault_phys | PT_PRESENT_MASK | PT_WRITABLE_MASK, 0); 72ce71ddc7SGleb Natapov } 73ce71ddc7SGleb Natapov 74ce71ddc7SGleb Natapov extern void pf_tss(void); 75ce71ddc7SGleb Natapov 76ce71ddc7SGleb Natapov asm ( 77ce71ddc7SGleb Natapov "pf_tss: \n\t" 78ce71ddc7SGleb Natapov "push %esp \n\t" 79ce71ddc7SGleb Natapov "call do_pf_tss \n\t" 80ce71ddc7SGleb Natapov "add $4, %esp \n\t" 81ce71ddc7SGleb Natapov "iret\n\t" 82ce71ddc7SGleb Natapov "jmp pf_tss\n\t" 83ce71ddc7SGleb Natapov ); 84ce71ddc7SGleb Natapov 85d21b4f12SGleb Natapov static void jmp_tss(void) 86d21b4f12SGleb Natapov { 87d21b4f12SGleb Natapov start: 88d21b4f12SGleb Natapov printf("JMP to task succeeded\n"); 89d21b4f12SGleb Natapov print_current_tss_info(); 90d21b4f12SGleb Natapov test_count++; 91d21b4f12SGleb Natapov asm volatile ("ljmp $" xstr(TSS_MAIN) ", $0"); 92d21b4f12SGleb Natapov goto start; 93d21b4f12SGleb Natapov } 94d21b4f12SGleb Natapov 95d21b4f12SGleb Natapov static void irq_tss(void) 96d21b4f12SGleb Natapov { 97d21b4f12SGleb Natapov start: 98d21b4f12SGleb Natapov printf("IRQ task is running\n"); 99d21b4f12SGleb Natapov print_current_tss_info(); 100d21b4f12SGleb Natapov test_count++; 101d21b4f12SGleb Natapov asm volatile ("iret"); 102d21b4f12SGleb Natapov test_count++; 103d21b4f12SGleb Natapov printf("IRQ task restarts after iret.\n"); 104d21b4f12SGleb Natapov goto start; 105d21b4f12SGleb Natapov } 106d21b4f12SGleb Natapov 1073cdcf179SNadav Amit static void user_tss(void) 1083cdcf179SNadav Amit { 1093cdcf179SNadav Amit start: 110aa5d8048SPaolo Bonzini printf("Conforming task is running\n"); 111aa5d8048SPaolo Bonzini print_current_tss_info(); 1123cdcf179SNadav Amit test_count++; 1133cdcf179SNadav Amit asm volatile ("iret"); 1143cdcf179SNadav Amit goto start; 1153cdcf179SNadav Amit } 1163cdcf179SNadav Amit 11706846df5SThomas Huth static void test_kernel_mode_int(void) 118d21b4f12SGleb Natapov { 119d21b4f12SGleb Natapov unsigned int res; 120d21b4f12SGleb Natapov 121d21b4f12SGleb Natapov /* test that int $2 triggers task gate */ 122d21b4f12SGleb Natapov test_count = 0; 123d21b4f12SGleb Natapov set_intr_task_gate(2, nmi_tss); 124d21b4f12SGleb Natapov printf("Triggering nmi 2\n"); 125d21b4f12SGleb Natapov asm volatile ("int $2"); 126d21b4f12SGleb Natapov printf("Return from nmi %d\n", test_count); 127*a299895bSThomas Huth report(test_count == 1, "NMI int $2"); 128d21b4f12SGleb Natapov 129d21b4f12SGleb Natapov /* test that external NMI triggers task gate */ 130d21b4f12SGleb Natapov test_count = 0; 131d21b4f12SGleb Natapov set_intr_task_gate(2, nmi_tss); 132d21b4f12SGleb Natapov printf("Triggering nmi through APIC\n"); 133d21b4f12SGleb Natapov apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, 0); 134d21b4f12SGleb Natapov io_delay(); 135d21b4f12SGleb Natapov printf("Return from APIC nmi\n"); 136*a299895bSThomas Huth report(test_count == 1, "NMI external"); 137d21b4f12SGleb Natapov 138d21b4f12SGleb Natapov /* test that external interrupt triggesr task gate */ 139d21b4f12SGleb Natapov test_count = 0; 140d21b4f12SGleb Natapov printf("Trigger IRQ from APIC\n"); 141d21b4f12SGleb Natapov set_intr_task_gate(0xf0, irq_tss); 142d21b4f12SGleb Natapov irq_enable(); 143d21b4f12SGleb Natapov apic_icr_write(APIC_DEST_SELF | APIC_DEST_PHYSICAL | APIC_DM_FIXED | APIC_INT_ASSERT | 0xf0, 0); 144d21b4f12SGleb Natapov io_delay(); 145d21b4f12SGleb Natapov irq_disable(); 146d21b4f12SGleb Natapov printf("Return from APIC IRQ\n"); 147*a299895bSThomas Huth report(test_count == 1, "IRQ external"); 148d21b4f12SGleb Natapov 149d21b4f12SGleb Natapov /* test that HW exception triggesr task gate */ 150d21b4f12SGleb Natapov set_intr_task_gate(0, de_tss); 151d21b4f12SGleb Natapov printf("Try to devide by 0\n"); 152d21b4f12SGleb Natapov asm volatile ("divl %3": "=a"(res) 153d21b4f12SGleb Natapov : "d"(0), "a"(1500), "m"(test_divider)); 154d21b4f12SGleb Natapov printf("Result is %d\n", res); 155*a299895bSThomas Huth report(res == 150, "DE exeption"); 156d21b4f12SGleb Natapov 157d21b4f12SGleb Natapov /* test if call HW exeption DE by int $0 triggers task gate */ 158d21b4f12SGleb Natapov test_count = 0; 159d21b4f12SGleb Natapov set_intr_task_gate(0, de_tss); 160d21b4f12SGleb Natapov printf("Call int 0\n"); 161d21b4f12SGleb Natapov asm volatile ("int $0"); 162d21b4f12SGleb Natapov printf("Return from int 0\n"); 163*a299895bSThomas Huth report(test_count == 1, "int $0"); 164d21b4f12SGleb Natapov 165d21b4f12SGleb Natapov /* test if HW exception OF triggers task gate */ 166d21b4f12SGleb Natapov test_count = 0; 167d21b4f12SGleb Natapov set_intr_task_gate(4, of_tss); 168d21b4f12SGleb Natapov printf("Call into\n"); 169d21b4f12SGleb Natapov asm volatile ("addb $127, %b0\ninto"::"a"(127)); 170d21b4f12SGleb Natapov printf("Return from into\n"); 171*a299895bSThomas Huth report(test_count, "OF exeption"); 172d21b4f12SGleb Natapov 173d21b4f12SGleb Natapov /* test if HW exception BP triggers task gate */ 174d21b4f12SGleb Natapov test_count = 0; 175d21b4f12SGleb Natapov set_intr_task_gate(3, bp_tss); 176d21b4f12SGleb Natapov printf("Call int 3\n"); 177d21b4f12SGleb Natapov asm volatile ("int $3"); 178d21b4f12SGleb Natapov printf("Return from int 3\n"); 179*a299895bSThomas Huth report(test_count == 1, "BP exeption"); 180d21b4f12SGleb Natapov 181ce71ddc7SGleb Natapov /* 182ce71ddc7SGleb Natapov * test that PF triggers task gate and error code is placed on 183ce71ddc7SGleb Natapov * exception task's stack 184ce71ddc7SGleb Natapov */ 185ce71ddc7SGleb Natapov fault_addr = alloc_vpage(); 186ce71ddc7SGleb Natapov fault_phys = (ulong)virt_to_phys(alloc_page()); 187ce71ddc7SGleb Natapov test_count = 0; 188ce71ddc7SGleb Natapov set_intr_task_gate(14, pf_tss); 189ce71ddc7SGleb Natapov printf("Access unmapped page\n"); 190ce71ddc7SGleb Natapov *fault_addr = 0; 191ce71ddc7SGleb Natapov printf("Return from pf tss\n"); 192*a299895bSThomas Huth report(test_count == 1, "PF exeption"); 19367af69b7SPaolo Bonzini } 194ce71ddc7SGleb Natapov 19506846df5SThomas Huth static void test_gdt_task_gate(void) 19667af69b7SPaolo Bonzini { 197d21b4f12SGleb Natapov /* test that calling a task by lcall works */ 198d21b4f12SGleb Natapov test_count = 0; 19967af69b7SPaolo Bonzini tss_intr.eip = (u32)irq_tss; 200d21b4f12SGleb Natapov printf("Calling task by lcall\n"); 201d21b4f12SGleb Natapov /* hlt opcode is 0xf4 I use destination IP 0xf4f4f4f4 to catch 202d21b4f12SGleb Natapov incorrect instruction length calculation */ 203d21b4f12SGleb Natapov asm volatile("lcall $" xstr(TSS_INTR) ", $0xf4f4f4f4"); 204d21b4f12SGleb Natapov printf("Return from call\n"); 205*a299895bSThomas Huth report(test_count == 1, "lcall"); 206d21b4f12SGleb Natapov 207d21b4f12SGleb Natapov /* call the same task again and check that it restarted after iret */ 208d21b4f12SGleb Natapov test_count = 0; 209d21b4f12SGleb Natapov asm volatile("lcall $" xstr(TSS_INTR) ", $0xf4f4f4f4"); 210*a299895bSThomas Huth report(test_count == 2, "lcall2"); 211d21b4f12SGleb Natapov 212d21b4f12SGleb Natapov /* test that calling a task by ljmp works */ 213d21b4f12SGleb Natapov test_count = 0; 21467af69b7SPaolo Bonzini tss_intr.eip = (u32)jmp_tss; 215d21b4f12SGleb Natapov printf("Jumping to a task by ljmp\n"); 216d21b4f12SGleb Natapov asm volatile ("ljmp $" xstr(TSS_INTR) ", $0xf4f4f4f4"); 217d21b4f12SGleb Natapov printf("Jump back succeeded\n"); 218*a299895bSThomas Huth report(test_count == 1, "ljmp"); 219b01c8823SKevin Wolf } 220b01c8823SKevin Wolf 22106846df5SThomas Huth static void test_vm86_switch(void) 222b01c8823SKevin Wolf { 223b01c8823SKevin Wolf static tss32_t main_tss; 224b01c8823SKevin Wolf static tss32_t vm86_tss; 225b01c8823SKevin Wolf 226b01c8823SKevin Wolf u8 *vm86_start; 227b01c8823SKevin Wolf 228b01c8823SKevin Wolf /* Write a 'ud2' instruction somewhere below 1 MB */ 229b01c8823SKevin Wolf vm86_start = (void*) 0x42000; 230b01c8823SKevin Wolf vm86_start[0] = 0x0f; 231b01c8823SKevin Wolf vm86_start[1] = 0x0b; 232b01c8823SKevin Wolf 233b01c8823SKevin Wolf /* Main TSS */ 23467961d18SPaolo Bonzini set_gdt_entry(MAIN_TSS_SEL, (u32)&main_tss, sizeof(tss32_t) - 1, 0x89, 0); 23567961d18SPaolo Bonzini ltr(MAIN_TSS_SEL); 236b01c8823SKevin Wolf main_tss = (tss32_t) { 23767961d18SPaolo Bonzini .prev = VM86_TSS_SEL, 238b01c8823SKevin Wolf .cr3 = read_cr3(), 239b01c8823SKevin Wolf }; 240b01c8823SKevin Wolf 241b01c8823SKevin Wolf /* VM86 TSS (marked as busy, so we can iret to it) */ 24267961d18SPaolo Bonzini set_gdt_entry(VM86_TSS_SEL, (u32)&vm86_tss, sizeof(tss32_t) - 1, 0x8b, 0); 243b01c8823SKevin Wolf vm86_tss = (tss32_t) { 244b01c8823SKevin Wolf .eflags = 0x20002, 245b01c8823SKevin Wolf .cr3 = read_cr3(), 246b01c8823SKevin Wolf .eip = (u32) vm86_start & 0x0f, 247b01c8823SKevin Wolf .cs = (u32) vm86_start >> 4, 248b01c8823SKevin Wolf .ds = 0x1234, 249b01c8823SKevin Wolf .es = 0x2345, 250b01c8823SKevin Wolf }; 251b01c8823SKevin Wolf 252b01c8823SKevin Wolf /* Setup task gate to main TSS for #UD */ 25367961d18SPaolo Bonzini set_idt_task_gate(6, MAIN_TSS_SEL); 254b01c8823SKevin Wolf 255b01c8823SKevin Wolf /* Jump into VM86 task with iret, #UD lets it come back immediately */ 256b01c8823SKevin Wolf printf("Switch to VM86 task and back\n"); 257b01c8823SKevin Wolf asm volatile( 258b01c8823SKevin Wolf "pushf\n" 259b01c8823SKevin Wolf "orw $0x4000, (%esp)\n" 260b01c8823SKevin Wolf "popf\n" 261b01c8823SKevin Wolf "iret\n" 262b01c8823SKevin Wolf ); 263*a299895bSThomas Huth report(1, "VM86"); 264b01c8823SKevin Wolf } 265b01c8823SKevin Wolf 266aa5d8048SPaolo Bonzini #define IOPL_SHIFT 12 267aa5d8048SPaolo Bonzini 26806846df5SThomas Huth static void test_conforming_switch(void) 2693cdcf179SNadav Amit { 2703cdcf179SNadav Amit /* test lcall with conforming segment, cs.dpl != cs.rpl */ 2713cdcf179SNadav Amit test_count = 0; 2723cdcf179SNadav Amit 2733cdcf179SNadav Amit tss_intr.cs = CONFORM_CS_SEL | 3; 2743cdcf179SNadav Amit tss_intr.eip = (u32)user_tss; 2751034d60aSNadav Amit tss_intr.ss = USER_DS; 2761034d60aSNadav Amit tss_intr.ds = tss_intr.gs = tss_intr.es = tss_intr.fs = tss_intr.ss; 277aa5d8048SPaolo Bonzini tss_intr.eflags |= 3 << IOPL_SHIFT; 2783cdcf179SNadav Amit set_gdt_entry(CONFORM_CS_SEL, 0, 0xffffffff, 0x9f, 0xc0); 2793cdcf179SNadav Amit asm volatile("lcall $" xstr(TSS_INTR) ", $0xf4f4f4f4"); 280*a299895bSThomas Huth report(test_count == 1, "lcall with cs.rpl != cs.dpl"); 2813cdcf179SNadav Amit } 2823cdcf179SNadav Amit 28306846df5SThomas Huth int main(void) 284b01c8823SKevin Wolf { 285b01c8823SKevin Wolf setup_vm(); 286b01c8823SKevin Wolf setup_idt(); 287b01c8823SKevin Wolf setup_tss32(); 288b01c8823SKevin Wolf 28967af69b7SPaolo Bonzini test_gdt_task_gate(); 290b01c8823SKevin Wolf test_kernel_mode_int(); 291b01c8823SKevin Wolf test_vm86_switch(); 2923cdcf179SNadav Amit test_conforming_switch(); 293d21b4f12SGleb Natapov 294f3cdd159SJan Kiszka return report_summary(); 295d21b4f12SGleb Natapov } 296