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