1 #include "libcflat.h" 2 #include "desc.h" 3 #include "apic-defs.h" 4 #include "apic.h" 5 #include "processor.h" 6 #include "vm.h" 7 8 #define xstr(s) str(s) 9 #define str(s) #s 10 11 static volatile int test_count; 12 static volatile unsigned int test_divider; 13 14 static char *fault_addr; 15 static ulong fault_phys; 16 17 static int g_fail; 18 static int g_tests; 19 20 static inline void io_delay(void) 21 { 22 } 23 24 static void report(const char *msg, int pass) 25 { 26 ++g_tests; 27 printf("%s: %s\n", msg, (pass ? "PASS" : "FAIL")); 28 if (!pass) 29 ++g_fail; 30 } 31 32 static void nmi_tss(void) 33 { 34 start: 35 printf("NMI task is running\n"); 36 print_current_tss_info(); 37 test_count++; 38 asm volatile ("iret"); 39 goto start; 40 } 41 42 static void de_tss(void) 43 { 44 start: 45 printf("DE task is running\n"); 46 print_current_tss_info(); 47 test_divider = 10; 48 test_count++; 49 asm volatile ("iret"); 50 goto start; 51 } 52 53 static void of_tss(void) 54 { 55 start: 56 printf("OF task is running\n"); 57 print_current_tss_info(); 58 test_count++; 59 asm volatile ("iret"); 60 goto start; 61 } 62 63 static void bp_tss(void) 64 { 65 start: 66 printf("BP task is running\n"); 67 print_current_tss_info(); 68 test_count++; 69 asm volatile ("iret"); 70 goto start; 71 } 72 73 void do_pf_tss(ulong *error_code) 74 { 75 printf("PF task is running %x %x\n", error_code, *(ulong*)error_code); 76 print_current_tss_info(); 77 if (*(ulong*)error_code == 0x2) /* write access, not present */ 78 test_count++; 79 install_pte(phys_to_virt(read_cr3()), 1, fault_addr, 80 fault_phys | PTE_PRESENT | PTE_WRITE, 0); 81 } 82 83 extern void pf_tss(void); 84 85 asm ( 86 "pf_tss: \n\t" 87 "push %esp \n\t" 88 "call do_pf_tss \n\t" 89 "add $4, %esp \n\t" 90 "iret\n\t" 91 "jmp pf_tss\n\t" 92 ); 93 94 static void jmp_tss(void) 95 { 96 start: 97 printf("JMP to task succeeded\n"); 98 print_current_tss_info(); 99 test_count++; 100 asm volatile ("ljmp $" xstr(TSS_MAIN) ", $0"); 101 goto start; 102 } 103 104 static void irq_tss(void) 105 { 106 start: 107 printf("IRQ task is running\n"); 108 print_current_tss_info(); 109 test_count++; 110 asm volatile ("iret"); 111 test_count++; 112 printf("IRQ task restarts after iret.\n"); 113 goto start; 114 } 115 116 int main() 117 { 118 unsigned int res; 119 120 setup_vm(); 121 setup_idt(); 122 setup_gdt(); 123 setup_tss32(); 124 125 /* test that int $2 triggers task gate */ 126 test_count = 0; 127 set_intr_task_gate(2, nmi_tss); 128 printf("Triggering nmi 2\n"); 129 asm volatile ("int $2"); 130 printf("Return from nmi %d\n", test_count); 131 report("NMI int $2", test_count == 1); 132 133 /* test that external NMI triggers task gate */ 134 test_count = 0; 135 set_intr_task_gate(2, nmi_tss); 136 printf("Triggering nmi through APIC\n"); 137 apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, 0); 138 io_delay(); 139 printf("Return from APIC nmi\n"); 140 report("NMI external", test_count == 1); 141 142 /* test that external interrupt triggesr task gate */ 143 test_count = 0; 144 printf("Trigger IRQ from APIC\n"); 145 set_intr_task_gate(0xf0, irq_tss); 146 irq_enable(); 147 apic_icr_write(APIC_DEST_SELF | APIC_DEST_PHYSICAL | APIC_DM_FIXED | APIC_INT_ASSERT | 0xf0, 0); 148 io_delay(); 149 irq_disable(); 150 printf("Return from APIC IRQ\n"); 151 report("IRQ external", test_count == 1); 152 153 /* test that HW exception triggesr task gate */ 154 set_intr_task_gate(0, de_tss); 155 printf("Try to devide by 0\n"); 156 asm volatile ("divl %3": "=a"(res) 157 : "d"(0), "a"(1500), "m"(test_divider)); 158 printf("Result is %d\n", res); 159 report("DE exeption", res == 150); 160 161 /* test if call HW exeption DE by int $0 triggers task gate */ 162 test_count = 0; 163 set_intr_task_gate(0, de_tss); 164 printf("Call int 0\n"); 165 asm volatile ("int $0"); 166 printf("Return from int 0\n"); 167 report("int $0", test_count == 1); 168 169 /* test if HW exception OF triggers task gate */ 170 test_count = 0; 171 set_intr_task_gate(4, of_tss); 172 printf("Call into\n"); 173 asm volatile ("addb $127, %b0\ninto"::"a"(127)); 174 printf("Return from into\n"); 175 report("OF exeption", test_count); 176 177 /* test if HW exception BP triggers task gate */ 178 test_count = 0; 179 set_intr_task_gate(3, bp_tss); 180 printf("Call int 3\n"); 181 asm volatile ("int $3"); 182 printf("Return from int 3\n"); 183 report("BP exeption", test_count == 1); 184 185 /* 186 * test that PF triggers task gate and error code is placed on 187 * exception task's stack 188 */ 189 fault_addr = alloc_vpage(); 190 fault_phys = (ulong)virt_to_phys(alloc_page()); 191 test_count = 0; 192 set_intr_task_gate(14, pf_tss); 193 printf("Access unmapped page\n"); 194 *fault_addr = 0; 195 printf("Return from pf tss\n"); 196 report("PF exeption", test_count == 1); 197 198 /* test that calling a task by lcall works */ 199 test_count = 0; 200 set_intr_task_gate(0, irq_tss); 201 printf("Calling task by lcall\n"); 202 /* hlt opcode is 0xf4 I use destination IP 0xf4f4f4f4 to catch 203 incorrect instruction length calculation */ 204 asm volatile("lcall $" xstr(TSS_INTR) ", $0xf4f4f4f4"); 205 printf("Return from call\n"); 206 report("lcall", test_count == 1); 207 208 /* call the same task again and check that it restarted after iret */ 209 test_count = 0; 210 asm volatile("lcall $" xstr(TSS_INTR) ", $0xf4f4f4f4"); 211 report("lcall2", test_count == 2); 212 213 /* test that calling a task by ljmp works */ 214 test_count = 0; 215 set_intr_task_gate(0, jmp_tss); 216 printf("Jumping to a task by ljmp\n"); 217 asm volatile ("ljmp $" xstr(TSS_INTR) ", $0xf4f4f4f4"); 218 printf("Jump back succeeded\n"); 219 report("ljmp", test_count == 1); 220 221 printf("\nsummary: %d tests, %d failures\n", g_tests, g_fail); 222 223 return g_fail != 0; 224 } 225