1 #include "libcflat.h" 2 #include "vm.h" 3 #include "processor.h" 4 #include "desc.h" 5 #include "isr.h" 6 #include "apic.h" 7 #include "apic-defs.h" 8 #include "vmalloc.h" 9 #include "alloc_page.h" 10 11 #ifdef __x86_64__ 12 # define R "r" 13 #else 14 # define R "e" 15 #endif 16 17 static inline void io_delay(void) 18 { 19 } 20 21 void apic_self_ipi(u8 v) 22 { 23 apic_icr_write(APIC_DEST_SELF | APIC_DEST_PHYSICAL | APIC_DM_FIXED | 24 APIC_INT_ASSERT | v, 0); 25 } 26 27 void apic_self_nmi(void) 28 { 29 apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, 0); 30 } 31 32 #define flush_phys_addr(__s) outl(__s, 0xe4) 33 #define flush_stack() do { \ 34 int __l; \ 35 flush_phys_addr(virt_to_phys(&__l)); \ 36 } while (0) 37 38 extern char isr_iret_ip[]; 39 40 static void flush_idt_page() 41 { 42 struct descriptor_table_ptr ptr; 43 sidt(&ptr); 44 flush_phys_addr(virt_to_phys((void*)ptr.base)); 45 } 46 47 static volatile unsigned int test_divider; 48 static volatile int test_count; 49 50 ulong stack_phys; 51 void *stack_va; 52 53 void do_pf_tss(void) 54 { 55 printf("PF running\n"); 56 install_pte(phys_to_virt(read_cr3()), 1, stack_va, 57 stack_phys | PT_PRESENT_MASK | PT_WRITABLE_MASK, 0); 58 invlpg(stack_va); 59 } 60 61 extern void pf_tss(void); 62 63 asm ("pf_tss: \n\t" 64 #ifdef __x86_64__ 65 // no task on x86_64, save/restore caller-save regs 66 "push %rax; push %rcx; push %rdx; push %rsi; push %rdi\n" 67 "push %r8; push %r9; push %r10; push %r11\n" 68 #endif 69 "call do_pf_tss \n\t" 70 #ifdef __x86_64__ 71 "pop %r11; pop %r10; pop %r9; pop %r8\n" 72 "pop %rdi; pop %rsi; pop %rdx; pop %rcx; pop %rax\n" 73 #endif 74 "add $"S", %"R "sp\n\t" // discard error code 75 "iret"W" \n\t" 76 "jmp pf_tss\n\t" 77 ); 78 79 80 #ifndef __x86_64__ 81 static void of_isr(struct ex_regs *r) 82 { 83 printf("OF isr running\n"); 84 test_count++; 85 } 86 #endif 87 88 static void np_isr(struct ex_regs *r) 89 { 90 printf("NP isr running %lx err=%lx\n", r->rip, r->error_code); 91 set_idt_sel(33, read_cs()); 92 test_count++; 93 } 94 95 static void de_isr(struct ex_regs *r) 96 { 97 printf("DE isr running divider is %d\n", test_divider); 98 test_divider = 10; 99 } 100 101 static void bp_isr(struct ex_regs *r) 102 { 103 printf("BP isr running\n"); 104 test_count++; 105 } 106 107 static void nested_nmi_isr(struct ex_regs *r) 108 { 109 printf("Nested NMI isr running rip=%lx\n", r->rip); 110 111 if (r->rip != (ulong)&isr_iret_ip) 112 test_count++; 113 } 114 static void nmi_isr(struct ex_regs *r) 115 { 116 printf("NMI isr running %p\n", &isr_iret_ip); 117 test_count++; 118 handle_exception(2, nested_nmi_isr); 119 printf("Sending nested NMI to self\n"); 120 apic_self_nmi(); 121 io_delay(); 122 printf("After nested NMI to self\n"); 123 } 124 125 unsigned long *iret_stack; 126 127 static void nested_nmi_iret_isr(struct ex_regs *r) 128 { 129 printf("Nested NMI isr running rip=%lx\n", r->rip); 130 131 if (r->rip == iret_stack[-3]) 132 test_count++; 133 } 134 135 extern void do_iret(ulong phys_stack, void *virt_stack); 136 137 // Return to same privilege level won't pop SS or SP, so 138 // save it in RDX while we run on the nested stack 139 140 asm("do_iret:" 141 #ifdef __x86_64__ 142 "mov %rdi, %rax \n\t" // phys_stack 143 "mov %rsi, %rdx \n\t" // virt_stack 144 #else 145 "mov 4(%esp), %eax \n\t" // phys_stack 146 "mov 8(%esp), %edx \n\t" // virt_stack 147 #endif 148 "xchg %"R "dx, %"R "sp \n\t" // point to new stack 149 "pushf"W" \n\t" 150 "mov %cs, %ecx \n\t" 151 "push"W" %"R "cx \n\t" 152 "push"W" $1f \n\t" 153 "outl %eax, $0xe4 \n\t" // flush page 154 "iret"W" \n\t" 155 "1: xchg %"R "dx, %"R "sp \n\t" // point to old stack 156 "ret\n\t" 157 ); 158 159 static void nmi_iret_isr(struct ex_regs *r) 160 { 161 unsigned long *s = alloc_page(); 162 test_count++; 163 printf("NMI isr running stack %p\n", s); 164 handle_exception(2, nested_nmi_iret_isr); 165 printf("Sending nested NMI to self\n"); 166 apic_self_nmi(); 167 printf("After nested NMI to self\n"); 168 iret_stack = &s[128]; 169 do_iret(virt_to_phys(s), iret_stack); 170 printf("After iret\n"); 171 } 172 173 static void tirq0(isr_regs_t *r) 174 { 175 printf("irq0 running\n"); 176 if (test_count == 1) 177 test_count++; 178 eoi(); 179 } 180 181 static void tirq1(isr_regs_t *r) 182 { 183 printf("irq1 running\n"); 184 test_count++; 185 eoi(); 186 } 187 188 ulong saved_stack; 189 190 #define switch_stack(S) do { \ 191 asm volatile ("mov %%" R "sp, %0":"=r"(saved_stack)); \ 192 asm volatile ("mov %0, %%" R "sp"::"r"(S)); \ 193 } while(0) 194 195 #define restore_stack() do { \ 196 asm volatile ("mov %0, %%" R "sp"::"r"(saved_stack)); \ 197 } while(0) 198 199 int main() 200 { 201 unsigned int res; 202 ulong *pt, *cr3, i; 203 204 setup_vm(); 205 setup_idt(); 206 setup_alt_stack(); 207 208 handle_irq(32, tirq0); 209 handle_irq(33, tirq1); 210 211 /* generate HW exception that will fault on IDT and stack */ 212 handle_exception(0, de_isr); 213 printf("Try to divide by 0\n"); 214 flush_idt_page(); 215 flush_stack(); 216 asm volatile ("divl %3": "=a"(res) 217 : "d"(0), "a"(1500), "m"(test_divider)); 218 printf("Result is %d\n", res); 219 report("DE exception", res == 150); 220 221 /* generate soft exception (BP) that will fault on IDT and stack */ 222 test_count = 0; 223 handle_exception(3, bp_isr); 224 printf("Try int 3\n"); 225 flush_idt_page(); 226 flush_stack(); 227 asm volatile ("int $3"); 228 printf("After int 3\n"); 229 report("BP exception", test_count == 1); 230 231 #ifndef __x86_64__ 232 /* generate soft exception (OF) that will fault on IDT */ 233 test_count = 0; 234 handle_exception(4, of_isr); 235 flush_idt_page(); 236 printf("Try into\n"); 237 asm volatile ("addb $127, %b0\ninto"::"a"(127)); 238 printf("After into\n"); 239 report("OF exception", test_count == 1); 240 241 /* generate soft exception (OF) using two bit instruction that will 242 fault on IDT */ 243 test_count = 0; 244 handle_exception(4, of_isr); 245 flush_idt_page(); 246 printf("Try into\n"); 247 asm volatile ("addb $127, %b0\naddr16 into"::"a"(127)); 248 printf("After into\n"); 249 report("2 byte OF exception", test_count == 1); 250 #endif 251 252 /* generate HW interrupt that will fault on IDT */ 253 test_count = 0; 254 flush_idt_page(); 255 printf("Sending vec 33 to self\n"); 256 irq_enable(); 257 apic_self_ipi(33); 258 io_delay(); 259 irq_disable(); 260 printf("After vec 33 to self\n"); 261 report("vec 33", test_count == 1); 262 263 /* generate soft interrupt that will fault on IDT and stack */ 264 test_count = 0; 265 flush_idt_page(); 266 printf("Try int $33\n"); 267 flush_stack(); 268 asm volatile ("int $33"); 269 printf("After int $33\n"); 270 report("int $33", test_count == 1); 271 272 /* Inject two HW interrupt than open iterrupt windows. Both interrupt 273 will fault on IDT access */ 274 test_count = 0; 275 flush_idt_page(); 276 printf("Sending vec 32 and 33 to self\n"); 277 apic_self_ipi(32); 278 apic_self_ipi(33); 279 io_delay(); 280 irq_enable(); 281 asm volatile("nop"); 282 irq_disable(); 283 printf("After vec 32 and 33 to self\n"); 284 report("vec 32/33", test_count == 2); 285 286 287 /* Inject HW interrupt, do sti and than (while in irq shadow) inject 288 soft interrupt. Fault during soft interrupt. Soft interrup shoud be 289 handled before HW interrupt */ 290 test_count = 0; 291 flush_idt_page(); 292 printf("Sending vec 32 and int $33\n"); 293 apic_self_ipi(32); 294 flush_stack(); 295 io_delay(); 296 asm volatile ("sti; int $33"); 297 irq_disable(); 298 printf("After vec 32 and int $33\n"); 299 report("vec 32/int $33", test_count == 2); 300 301 /* test that TPR is honored */ 302 test_count = 0; 303 handle_irq(62, tirq1); 304 flush_idt_page(); 305 printf("Sending vec 33 and 62 and mask one with TPR\n"); 306 apic_write(APIC_TASKPRI, 0xf << 4); 307 irq_enable(); 308 apic_self_ipi(32); 309 apic_self_ipi(62); 310 io_delay(); 311 apic_write(APIC_TASKPRI, 0x2 << 4); 312 printf("After 33/62 TPR test\n"); 313 report("TPR", test_count == 1); 314 apic_write(APIC_TASKPRI, 0x0); 315 while(test_count != 2); /* wait for second irq */ 316 irq_disable(); 317 318 /* test fault durint NP delivery */ 319 printf("Before NP test\n"); 320 test_count = 0; 321 handle_exception(11, np_isr); 322 set_idt_sel(33, NP_SEL); 323 flush_idt_page(); 324 flush_stack(); 325 asm volatile ("int $33"); 326 printf("After int33\n"); 327 report("NP exception", test_count == 2); 328 329 /* generate NMI that will fault on IDT */ 330 test_count = 0; 331 handle_exception(2, nmi_isr); 332 flush_idt_page(); 333 printf("Sending NMI to self\n"); 334 apic_self_nmi(); 335 printf("After NMI to self\n"); 336 /* this is needed on VMX without NMI window notification. 337 Interrupt windows is used instead, so let pending NMI 338 to be injected */ 339 irq_enable(); 340 asm volatile ("nop"); 341 irq_disable(); 342 report("NMI", test_count == 2); 343 344 /* generate NMI that will fault on IRET */ 345 printf("Before NMI IRET test\n"); 346 test_count = 0; 347 handle_exception(2, nmi_iret_isr); 348 printf("Sending NMI to self\n"); 349 apic_self_nmi(); 350 /* this is needed on VMX without NMI window notification. 351 Interrupt windows is used instead, so let pending NMI 352 to be injected */ 353 irq_enable(); 354 asm volatile ("nop"); 355 irq_disable(); 356 printf("After NMI to self\n"); 357 report("NMI", test_count == 2); 358 stack_phys = (ulong)virt_to_phys(alloc_page()); 359 stack_va = alloc_vpage(); 360 361 /* Generate DE and PF exceptions serially */ 362 test_divider = 0; 363 set_intr_alt_stack(14, pf_tss); 364 handle_exception(0, de_isr); 365 printf("Try to divide by 0\n"); 366 /* install read only pte */ 367 install_pte(phys_to_virt(read_cr3()), 1, stack_va, 368 stack_phys | PT_PRESENT_MASK, 0); 369 invlpg(stack_va); 370 flush_phys_addr(stack_phys); 371 switch_stack(stack_va + 4095); 372 flush_idt_page(); 373 asm volatile ("divl %3": "=a"(res) 374 : "d"(0), "a"(1500), "m"(test_divider)); 375 restore_stack(); 376 printf("Result is %d\n", res); 377 report("DE PF exceptions", res == 150); 378 379 /* Generate NP and PF exceptions serially */ 380 printf("Before NP test\n"); 381 test_count = 0; 382 set_intr_alt_stack(14, pf_tss); 383 handle_exception(11, np_isr); 384 set_idt_sel(33, NP_SEL); 385 /* install read only pte */ 386 install_pte(phys_to_virt(read_cr3()), 1, stack_va, 387 stack_phys | PT_PRESENT_MASK, 0); 388 invlpg(stack_va); 389 flush_idt_page(); 390 flush_phys_addr(stack_phys); 391 switch_stack(stack_va + 4095); 392 asm volatile ("int $33"); 393 restore_stack(); 394 printf("After int33\n"); 395 report("NP PF exceptions", test_count == 2); 396 397 pt = alloc_page(); 398 cr3 = (void*)read_cr3(); 399 memset(pt, 0, 4096); 400 /* use shadowed stack during interrupt delivery */ 401 for (i = 0; i < 4096/sizeof(ulong); i++) { 402 if (!cr3[i]) { 403 cr3[i] = virt_to_phys(pt) | PT_PRESENT_MASK | PT_WRITABLE_MASK; 404 pt[0] = virt_to_phys(pt) | PT_PRESENT_MASK | PT_WRITABLE_MASK; 405 #ifndef __x86_64__ 406 ((ulong*)(i<<22))[1] = 0; 407 #else 408 ((ulong*)(i<<39))[1] = 0; 409 #endif 410 write_cr3(virt_to_phys(cr3)); 411 break; 412 } 413 } 414 test_count = 0; 415 printf("Try int 33 with shadowed stack\n"); 416 switch_stack(((char*)pt) + 4095); 417 asm volatile("int $33"); 418 restore_stack(); 419 printf("After int 33 with shadowed stack\n"); 420 report("int 33 with shadowed stack", test_count == 1); 421 422 return report_summary(); 423 } 424