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" 7d21b4f12SGleb Natapov 8*b01c8823SKevin Wolf #define FREE_GDT_INDEX 6 9*b01c8823SKevin Wolf #define MAIN_TSS_INDEX (FREE_GDT_INDEX + 0) 10*b01c8823SKevin Wolf #define VM86_TSS_INDEX (FREE_GDT_INDEX + 1) 11*b01c8823SKevin Wolf 12d21b4f12SGleb Natapov #define xstr(s) str(s) 13d21b4f12SGleb Natapov #define str(s) #s 14d21b4f12SGleb Natapov 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 21d21b4f12SGleb Natapov static int g_fail; 22d21b4f12SGleb Natapov static int g_tests; 23d21b4f12SGleb Natapov 24d21b4f12SGleb Natapov static inline void io_delay(void) 25d21b4f12SGleb Natapov { 26d21b4f12SGleb Natapov } 27d21b4f12SGleb Natapov 28d21b4f12SGleb Natapov static void report(const char *msg, int pass) 29d21b4f12SGleb Natapov { 30d21b4f12SGleb Natapov ++g_tests; 31d21b4f12SGleb Natapov printf("%s: %s\n", msg, (pass ? "PASS" : "FAIL")); 32d21b4f12SGleb Natapov if (!pass) 33d21b4f12SGleb Natapov ++g_fail; 34d21b4f12SGleb Natapov } 35d21b4f12SGleb Natapov 36d21b4f12SGleb Natapov static void nmi_tss(void) 37d21b4f12SGleb Natapov { 38d21b4f12SGleb Natapov start: 39d21b4f12SGleb Natapov printf("NMI task is running\n"); 40d21b4f12SGleb Natapov print_current_tss_info(); 41d21b4f12SGleb Natapov test_count++; 42d21b4f12SGleb Natapov asm volatile ("iret"); 43d21b4f12SGleb Natapov goto start; 44d21b4f12SGleb Natapov } 45d21b4f12SGleb Natapov 46d21b4f12SGleb Natapov static void de_tss(void) 47d21b4f12SGleb Natapov { 48d21b4f12SGleb Natapov start: 49d21b4f12SGleb Natapov printf("DE task is running\n"); 50d21b4f12SGleb Natapov print_current_tss_info(); 51d21b4f12SGleb Natapov test_divider = 10; 52d21b4f12SGleb Natapov test_count++; 53d21b4f12SGleb Natapov asm volatile ("iret"); 54d21b4f12SGleb Natapov goto start; 55d21b4f12SGleb Natapov } 56d21b4f12SGleb Natapov 57d21b4f12SGleb Natapov static void of_tss(void) 58d21b4f12SGleb Natapov { 59d21b4f12SGleb Natapov start: 60d21b4f12SGleb Natapov printf("OF task is running\n"); 61d21b4f12SGleb Natapov print_current_tss_info(); 62d21b4f12SGleb Natapov test_count++; 63d21b4f12SGleb Natapov asm volatile ("iret"); 64d21b4f12SGleb Natapov goto start; 65d21b4f12SGleb Natapov } 66d21b4f12SGleb Natapov 67d21b4f12SGleb Natapov static void bp_tss(void) 68d21b4f12SGleb Natapov { 69d21b4f12SGleb Natapov start: 70d21b4f12SGleb Natapov printf("BP task is running\n"); 71d21b4f12SGleb Natapov print_current_tss_info(); 72d21b4f12SGleb Natapov test_count++; 73d21b4f12SGleb Natapov asm volatile ("iret"); 74d21b4f12SGleb Natapov goto start; 75d21b4f12SGleb Natapov } 76d21b4f12SGleb Natapov 77ce71ddc7SGleb Natapov void do_pf_tss(ulong *error_code) 78ce71ddc7SGleb Natapov { 79ce71ddc7SGleb Natapov printf("PF task is running %x %x\n", error_code, *(ulong*)error_code); 80ce71ddc7SGleb Natapov print_current_tss_info(); 81ce71ddc7SGleb Natapov if (*(ulong*)error_code == 0x2) /* write access, not present */ 82ce71ddc7SGleb Natapov test_count++; 83ce71ddc7SGleb Natapov install_pte(phys_to_virt(read_cr3()), 1, fault_addr, 84ce71ddc7SGleb Natapov fault_phys | PTE_PRESENT | PTE_WRITE, 0); 85ce71ddc7SGleb Natapov } 86ce71ddc7SGleb Natapov 87ce71ddc7SGleb Natapov extern void pf_tss(void); 88ce71ddc7SGleb Natapov 89ce71ddc7SGleb Natapov asm ( 90ce71ddc7SGleb Natapov "pf_tss: \n\t" 91ce71ddc7SGleb Natapov "push %esp \n\t" 92ce71ddc7SGleb Natapov "call do_pf_tss \n\t" 93ce71ddc7SGleb Natapov "add $4, %esp \n\t" 94ce71ddc7SGleb Natapov "iret\n\t" 95ce71ddc7SGleb Natapov "jmp pf_tss\n\t" 96ce71ddc7SGleb Natapov ); 97ce71ddc7SGleb Natapov 98d21b4f12SGleb Natapov static void jmp_tss(void) 99d21b4f12SGleb Natapov { 100d21b4f12SGleb Natapov start: 101d21b4f12SGleb Natapov printf("JMP to task succeeded\n"); 102d21b4f12SGleb Natapov print_current_tss_info(); 103d21b4f12SGleb Natapov test_count++; 104d21b4f12SGleb Natapov asm volatile ("ljmp $" xstr(TSS_MAIN) ", $0"); 105d21b4f12SGleb Natapov goto start; 106d21b4f12SGleb Natapov } 107d21b4f12SGleb Natapov 108d21b4f12SGleb Natapov static void irq_tss(void) 109d21b4f12SGleb Natapov { 110d21b4f12SGleb Natapov start: 111d21b4f12SGleb Natapov printf("IRQ task is running\n"); 112d21b4f12SGleb Natapov print_current_tss_info(); 113d21b4f12SGleb Natapov test_count++; 114d21b4f12SGleb Natapov asm volatile ("iret"); 115d21b4f12SGleb Natapov test_count++; 116d21b4f12SGleb Natapov printf("IRQ task restarts after iret.\n"); 117d21b4f12SGleb Natapov goto start; 118d21b4f12SGleb Natapov } 119d21b4f12SGleb Natapov 120*b01c8823SKevin Wolf void test_kernel_mode_int() 121d21b4f12SGleb Natapov { 122d21b4f12SGleb Natapov unsigned int res; 123d21b4f12SGleb Natapov 124d21b4f12SGleb Natapov /* test that int $2 triggers task gate */ 125d21b4f12SGleb Natapov test_count = 0; 126d21b4f12SGleb Natapov set_intr_task_gate(2, nmi_tss); 127d21b4f12SGleb Natapov printf("Triggering nmi 2\n"); 128d21b4f12SGleb Natapov asm volatile ("int $2"); 129d21b4f12SGleb Natapov printf("Return from nmi %d\n", test_count); 130d21b4f12SGleb Natapov report("NMI int $2", test_count == 1); 131d21b4f12SGleb Natapov 132d21b4f12SGleb Natapov /* test that external NMI triggers task gate */ 133d21b4f12SGleb Natapov test_count = 0; 134d21b4f12SGleb Natapov set_intr_task_gate(2, nmi_tss); 135d21b4f12SGleb Natapov printf("Triggering nmi through APIC\n"); 136d21b4f12SGleb Natapov apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, 0); 137d21b4f12SGleb Natapov io_delay(); 138d21b4f12SGleb Natapov printf("Return from APIC nmi\n"); 139d21b4f12SGleb Natapov report("NMI external", test_count == 1); 140d21b4f12SGleb Natapov 141d21b4f12SGleb Natapov /* test that external interrupt triggesr task gate */ 142d21b4f12SGleb Natapov test_count = 0; 143d21b4f12SGleb Natapov printf("Trigger IRQ from APIC\n"); 144d21b4f12SGleb Natapov set_intr_task_gate(0xf0, irq_tss); 145d21b4f12SGleb Natapov irq_enable(); 146d21b4f12SGleb Natapov apic_icr_write(APIC_DEST_SELF | APIC_DEST_PHYSICAL | APIC_DM_FIXED | APIC_INT_ASSERT | 0xf0, 0); 147d21b4f12SGleb Natapov io_delay(); 148d21b4f12SGleb Natapov irq_disable(); 149d21b4f12SGleb Natapov printf("Return from APIC IRQ\n"); 150d21b4f12SGleb Natapov report("IRQ external", test_count == 1); 151d21b4f12SGleb Natapov 152d21b4f12SGleb Natapov /* test that HW exception triggesr task gate */ 153d21b4f12SGleb Natapov set_intr_task_gate(0, de_tss); 154d21b4f12SGleb Natapov printf("Try to devide by 0\n"); 155d21b4f12SGleb Natapov asm volatile ("divl %3": "=a"(res) 156d21b4f12SGleb Natapov : "d"(0), "a"(1500), "m"(test_divider)); 157d21b4f12SGleb Natapov printf("Result is %d\n", res); 158d21b4f12SGleb Natapov report("DE exeption", res == 150); 159d21b4f12SGleb Natapov 160d21b4f12SGleb Natapov /* test if call HW exeption DE by int $0 triggers task gate */ 161d21b4f12SGleb Natapov test_count = 0; 162d21b4f12SGleb Natapov set_intr_task_gate(0, de_tss); 163d21b4f12SGleb Natapov printf("Call int 0\n"); 164d21b4f12SGleb Natapov asm volatile ("int $0"); 165d21b4f12SGleb Natapov printf("Return from int 0\n"); 166d21b4f12SGleb Natapov report("int $0", test_count == 1); 167d21b4f12SGleb Natapov 168d21b4f12SGleb Natapov /* test if HW exception OF triggers task gate */ 169d21b4f12SGleb Natapov test_count = 0; 170d21b4f12SGleb Natapov set_intr_task_gate(4, of_tss); 171d21b4f12SGleb Natapov printf("Call into\n"); 172d21b4f12SGleb Natapov asm volatile ("addb $127, %b0\ninto"::"a"(127)); 173d21b4f12SGleb Natapov printf("Return from into\n"); 174d21b4f12SGleb Natapov report("OF exeption", test_count); 175d21b4f12SGleb Natapov 176d21b4f12SGleb Natapov /* test if HW exception BP triggers task gate */ 177d21b4f12SGleb Natapov test_count = 0; 178d21b4f12SGleb Natapov set_intr_task_gate(3, bp_tss); 179d21b4f12SGleb Natapov printf("Call int 3\n"); 180d21b4f12SGleb Natapov asm volatile ("int $3"); 181d21b4f12SGleb Natapov printf("Return from int 3\n"); 182d21b4f12SGleb Natapov report("BP exeption", test_count == 1); 183d21b4f12SGleb Natapov 184ce71ddc7SGleb Natapov /* 185ce71ddc7SGleb Natapov * test that PF triggers task gate and error code is placed on 186ce71ddc7SGleb Natapov * exception task's stack 187ce71ddc7SGleb Natapov */ 188ce71ddc7SGleb Natapov fault_addr = alloc_vpage(); 189ce71ddc7SGleb Natapov fault_phys = (ulong)virt_to_phys(alloc_page()); 190ce71ddc7SGleb Natapov test_count = 0; 191ce71ddc7SGleb Natapov set_intr_task_gate(14, pf_tss); 192ce71ddc7SGleb Natapov printf("Access unmapped page\n"); 193ce71ddc7SGleb Natapov *fault_addr = 0; 194ce71ddc7SGleb Natapov printf("Return from pf tss\n"); 195ce71ddc7SGleb Natapov report("PF exeption", test_count == 1); 196ce71ddc7SGleb Natapov 197d21b4f12SGleb Natapov /* test that calling a task by lcall works */ 198d21b4f12SGleb Natapov test_count = 0; 199d21b4f12SGleb Natapov set_intr_task_gate(0, 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"); 205d21b4f12SGleb Natapov report("lcall", test_count == 1); 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"); 210d21b4f12SGleb Natapov report("lcall2", test_count == 2); 211d21b4f12SGleb Natapov 212d21b4f12SGleb Natapov /* test that calling a task by ljmp works */ 213d21b4f12SGleb Natapov test_count = 0; 214d21b4f12SGleb Natapov set_intr_task_gate(0, 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"); 218d21b4f12SGleb Natapov report("ljmp", test_count == 1); 219*b01c8823SKevin Wolf } 220*b01c8823SKevin Wolf 221*b01c8823SKevin Wolf void test_vm86_switch(void) 222*b01c8823SKevin Wolf { 223*b01c8823SKevin Wolf static tss32_t main_tss; 224*b01c8823SKevin Wolf static tss32_t vm86_tss; 225*b01c8823SKevin Wolf 226*b01c8823SKevin Wolf u8 *vm86_start; 227*b01c8823SKevin Wolf 228*b01c8823SKevin Wolf /* Write a 'ud2' instruction somewhere below 1 MB */ 229*b01c8823SKevin Wolf vm86_start = (void*) 0x42000; 230*b01c8823SKevin Wolf vm86_start[0] = 0x0f; 231*b01c8823SKevin Wolf vm86_start[1] = 0x0b; 232*b01c8823SKevin Wolf 233*b01c8823SKevin Wolf /* Main TSS */ 234*b01c8823SKevin Wolf set_gdt_entry(MAIN_TSS_INDEX, (u32)&main_tss, sizeof(tss32_t) - 1, 0x89, 0); 235*b01c8823SKevin Wolf ltr(MAIN_TSS_INDEX << 3); 236*b01c8823SKevin Wolf main_tss = (tss32_t) { 237*b01c8823SKevin Wolf .prev = VM86_TSS_INDEX << 3, 238*b01c8823SKevin Wolf .cr3 = read_cr3(), 239*b01c8823SKevin Wolf }; 240*b01c8823SKevin Wolf 241*b01c8823SKevin Wolf /* VM86 TSS (marked as busy, so we can iret to it) */ 242*b01c8823SKevin Wolf set_gdt_entry(VM86_TSS_INDEX, (u32)&vm86_tss, sizeof(tss32_t) - 1, 0x8b, 0); 243*b01c8823SKevin Wolf vm86_tss = (tss32_t) { 244*b01c8823SKevin Wolf .eflags = 0x20002, 245*b01c8823SKevin Wolf .cr3 = read_cr3(), 246*b01c8823SKevin Wolf .eip = (u32) vm86_start & 0x0f, 247*b01c8823SKevin Wolf .cs = (u32) vm86_start >> 4, 248*b01c8823SKevin Wolf .ds = 0x1234, 249*b01c8823SKevin Wolf .es = 0x2345, 250*b01c8823SKevin Wolf }; 251*b01c8823SKevin Wolf 252*b01c8823SKevin Wolf /* Setup task gate to main TSS for #UD */ 253*b01c8823SKevin Wolf set_idt_task_gate(6, MAIN_TSS_INDEX << 3); 254*b01c8823SKevin Wolf 255*b01c8823SKevin Wolf /* Jump into VM86 task with iret, #UD lets it come back immediately */ 256*b01c8823SKevin Wolf printf("Switch to VM86 task and back\n"); 257*b01c8823SKevin Wolf asm volatile( 258*b01c8823SKevin Wolf "pushf\n" 259*b01c8823SKevin Wolf "orw $0x4000, (%esp)\n" 260*b01c8823SKevin Wolf "popf\n" 261*b01c8823SKevin Wolf "iret\n" 262*b01c8823SKevin Wolf ); 263*b01c8823SKevin Wolf report("VM86", 1); 264*b01c8823SKevin Wolf } 265*b01c8823SKevin Wolf 266*b01c8823SKevin Wolf int main() 267*b01c8823SKevin Wolf { 268*b01c8823SKevin Wolf setup_vm(); 269*b01c8823SKevin Wolf setup_idt(); 270*b01c8823SKevin Wolf setup_gdt(); 271*b01c8823SKevin Wolf setup_tss32(); 272*b01c8823SKevin Wolf 273*b01c8823SKevin Wolf test_kernel_mode_int(); 274*b01c8823SKevin Wolf test_vm86_switch(); 275d21b4f12SGleb Natapov 276d21b4f12SGleb Natapov printf("\nsummary: %d tests, %d failures\n", g_tests, g_fail); 277d21b4f12SGleb Natapov 278d21b4f12SGleb Natapov return g_fail != 0; 279d21b4f12SGleb Natapov } 280