17d36db35SAvi Kivity #include "libcflat.h" 27d36db35SAvi Kivity #include "apic.h" 37d36db35SAvi Kivity #include "vm.h" 4f2d2b7c7SAvi Kivity #include "smp.h" 5*d51bd17eSGleb Natapov #include "idt.h" 67d36db35SAvi Kivity 77d36db35SAvi Kivity typedef struct { 87d36db35SAvi Kivity ulong regs[sizeof(ulong)*2]; 97d36db35SAvi Kivity ulong func; 107d36db35SAvi Kivity ulong rip; 117d36db35SAvi Kivity ulong cs; 127d36db35SAvi Kivity ulong rflags; 137d36db35SAvi Kivity } isr_regs_t; 147d36db35SAvi Kivity 157d36db35SAvi Kivity #ifdef __x86_64__ 167d36db35SAvi Kivity # define R "r" 177d36db35SAvi Kivity #else 187d36db35SAvi Kivity # define R "e" 197d36db35SAvi Kivity #endif 207d36db35SAvi Kivity 217d36db35SAvi Kivity extern char isr_entry_point[]; 227d36db35SAvi Kivity 237d36db35SAvi Kivity asm ( 247d36db35SAvi Kivity "isr_entry_point: \n" 257d36db35SAvi Kivity #ifdef __x86_64__ 267d36db35SAvi Kivity "push %r15 \n\t" 277d36db35SAvi Kivity "push %r14 \n\t" 287d36db35SAvi Kivity "push %r13 \n\t" 297d36db35SAvi Kivity "push %r12 \n\t" 307d36db35SAvi Kivity "push %r11 \n\t" 317d36db35SAvi Kivity "push %r10 \n\t" 327d36db35SAvi Kivity "push %r9 \n\t" 337d36db35SAvi Kivity "push %r8 \n\t" 347d36db35SAvi Kivity #endif 357d36db35SAvi Kivity "push %"R "di \n\t" 367d36db35SAvi Kivity "push %"R "si \n\t" 377d36db35SAvi Kivity "push %"R "bp \n\t" 387d36db35SAvi Kivity "push %"R "sp \n\t" 397d36db35SAvi Kivity "push %"R "bx \n\t" 407d36db35SAvi Kivity "push %"R "dx \n\t" 417d36db35SAvi Kivity "push %"R "cx \n\t" 427d36db35SAvi Kivity "push %"R "ax \n\t" 437d36db35SAvi Kivity #ifdef __x86_64__ 447d36db35SAvi Kivity "mov %rsp, %rdi \n\t" 457d36db35SAvi Kivity "callq *8*16(%rsp) \n\t" 467d36db35SAvi Kivity #else 477d36db35SAvi Kivity "push %esp \n\t" 487d36db35SAvi Kivity "calll *4+4*8(%esp) \n\t" 497d36db35SAvi Kivity "add $4, %esp \n\t" 507d36db35SAvi Kivity #endif 517d36db35SAvi Kivity "pop %"R "ax \n\t" 527d36db35SAvi Kivity "pop %"R "cx \n\t" 537d36db35SAvi Kivity "pop %"R "dx \n\t" 547d36db35SAvi Kivity "pop %"R "bx \n\t" 557d36db35SAvi Kivity "pop %"R "bp \n\t" 567d36db35SAvi Kivity "pop %"R "bp \n\t" 577d36db35SAvi Kivity "pop %"R "si \n\t" 587d36db35SAvi Kivity "pop %"R "di \n\t" 597d36db35SAvi Kivity #ifdef __x86_64__ 607d36db35SAvi Kivity "pop %r8 \n\t" 617d36db35SAvi Kivity "pop %r9 \n\t" 627d36db35SAvi Kivity "pop %r10 \n\t" 637d36db35SAvi Kivity "pop %r11 \n\t" 647d36db35SAvi Kivity "pop %r12 \n\t" 657d36db35SAvi Kivity "pop %r13 \n\t" 667d36db35SAvi Kivity "pop %r14 \n\t" 677d36db35SAvi Kivity "pop %r15 \n\t" 687d36db35SAvi Kivity #endif 697d36db35SAvi Kivity #ifdef __x86_64__ 707d36db35SAvi Kivity "add $8, %rsp \n\t" 717d36db35SAvi Kivity "iretq \n\t" 727d36db35SAvi Kivity #else 737d36db35SAvi Kivity "add $4, %esp \n\t" 747d36db35SAvi Kivity "iretl \n\t" 757d36db35SAvi Kivity #endif 767d36db35SAvi Kivity ); 777d36db35SAvi Kivity 787d36db35SAvi Kivity static int g_fail; 797d36db35SAvi Kivity static int g_tests; 807d36db35SAvi Kivity 817d36db35SAvi Kivity static void outb(unsigned char data, unsigned short port) 827d36db35SAvi Kivity { 837d36db35SAvi Kivity asm volatile ("out %0, %1" : : "a"(data), "d"(port)); 847d36db35SAvi Kivity } 857d36db35SAvi Kivity 867d36db35SAvi Kivity static void report(const char *msg, int pass) 877d36db35SAvi Kivity { 887d36db35SAvi Kivity ++g_tests; 897d36db35SAvi Kivity printf("%s: %s\n", msg, (pass ? "PASS" : "FAIL")); 907d36db35SAvi Kivity if (!pass) 917d36db35SAvi Kivity ++g_fail; 927d36db35SAvi Kivity } 937d36db35SAvi Kivity 947d36db35SAvi Kivity static void test_lapic_existence(void) 957d36db35SAvi Kivity { 967d36db35SAvi Kivity u32 lvr; 977d36db35SAvi Kivity 987d36db35SAvi Kivity lvr = apic_read(APIC_LVR); 997d36db35SAvi Kivity printf("apic version: %x\n", lvr); 1007d36db35SAvi Kivity report("apic existence", (u16)lvr == 0x14); 1017d36db35SAvi Kivity } 1027d36db35SAvi Kivity 1037d36db35SAvi Kivity #define MSR_APIC_BASE 0x0000001b 1047d36db35SAvi Kivity 1057d36db35SAvi Kivity void test_enable_x2apic(void) 1067d36db35SAvi Kivity { 1077d36db35SAvi Kivity if (enable_x2apic()) { 1087d36db35SAvi Kivity printf("x2apic enabled\n"); 1097d36db35SAvi Kivity } else { 1107d36db35SAvi Kivity printf("x2apic not detected\n"); 1117d36db35SAvi Kivity } 1127d36db35SAvi Kivity } 1137d36db35SAvi Kivity 114*d51bd17eSGleb Natapov static void handle_irq(unsigned vec, void (*func)(isr_regs_t *regs)) 1157d36db35SAvi Kivity { 1167d36db35SAvi Kivity u8 *thunk = vmalloc(50); 117*d51bd17eSGleb Natapov 118*d51bd17eSGleb Natapov set_idt_entry(vec, thunk, 0); 119*d51bd17eSGleb Natapov 1207d36db35SAvi Kivity #ifdef __x86_64__ 1217d36db35SAvi Kivity /* sub $8, %rsp */ 1227d36db35SAvi Kivity *thunk++ = 0x48; *thunk++ = 0x83; *thunk++ = 0xec; *thunk++ = 0x08; 1237d36db35SAvi Kivity /* mov $func_low, %(rsp) */ 1247d36db35SAvi Kivity *thunk++ = 0xc7; *thunk++ = 0x04; *thunk++ = 0x24; 1257d36db35SAvi Kivity *(u32 *)thunk = (ulong)func; thunk += 4; 1267d36db35SAvi Kivity /* mov $func_high, %(rsp+4) */ 1277d36db35SAvi Kivity *thunk++ = 0xc7; *thunk++ = 0x44; *thunk++ = 0x24; *thunk++ = 0x04; 1287d36db35SAvi Kivity *(u32 *)thunk = (ulong)func >> 32; thunk += 4; 1297d36db35SAvi Kivity /* jmp isr_entry_point */ 1307d36db35SAvi Kivity *thunk ++ = 0xe9; 1317d36db35SAvi Kivity *(u32 *)thunk = (ulong)isr_entry_point - (ulong)(thunk + 4); 1327d36db35SAvi Kivity #else 1337d36db35SAvi Kivity /* push $func */ 1347d36db35SAvi Kivity *thunk++ = 0x68; 1357d36db35SAvi Kivity *(u32 *)thunk = (ulong)func; 1367d36db35SAvi Kivity /* jmp isr_entry_point */ 1377d36db35SAvi Kivity *thunk ++ = 0xe9; 1387d36db35SAvi Kivity *(u32 *)thunk = (ulong)isr_entry_point - (ulong)(thunk + 4); 1397d36db35SAvi Kivity #endif 1407d36db35SAvi Kivity } 1417d36db35SAvi Kivity 1427d36db35SAvi Kivity static void irq_disable(void) 1437d36db35SAvi Kivity { 1447d36db35SAvi Kivity asm volatile("cli"); 1457d36db35SAvi Kivity } 1467d36db35SAvi Kivity 1477d36db35SAvi Kivity static void irq_enable(void) 1487d36db35SAvi Kivity { 1497d36db35SAvi Kivity asm volatile("sti"); 1507d36db35SAvi Kivity } 1517d36db35SAvi Kivity 1527d36db35SAvi Kivity static void eoi(void) 1537d36db35SAvi Kivity { 1547d36db35SAvi Kivity apic_write(APIC_EOI, 0); 1557d36db35SAvi Kivity } 1567d36db35SAvi Kivity 1577d36db35SAvi Kivity static int ipi_count; 1587d36db35SAvi Kivity 1597d36db35SAvi Kivity static void self_ipi_isr(isr_regs_t *regs) 1607d36db35SAvi Kivity { 1617d36db35SAvi Kivity ++ipi_count; 1627d36db35SAvi Kivity eoi(); 1637d36db35SAvi Kivity } 1647d36db35SAvi Kivity 1657d36db35SAvi Kivity static void test_self_ipi(void) 1667d36db35SAvi Kivity { 1677d36db35SAvi Kivity int vec = 0xf1; 1687d36db35SAvi Kivity 169*d51bd17eSGleb Natapov handle_irq(vec, self_ipi_isr); 1707d36db35SAvi Kivity irq_enable(); 1717d36db35SAvi Kivity apic_icr_write(APIC_DEST_SELF | APIC_DEST_PHYSICAL | APIC_DM_FIXED | vec, 1727d36db35SAvi Kivity 0); 1737d36db35SAvi Kivity asm volatile ("nop"); 1747d36db35SAvi Kivity report("self ipi", ipi_count == 1); 1757d36db35SAvi Kivity } 1767d36db35SAvi Kivity 1777d36db35SAvi Kivity static void set_ioapic_redir(unsigned line, unsigned vec) 1787d36db35SAvi Kivity { 1797d36db35SAvi Kivity ioapic_redir_entry_t e = { 1807d36db35SAvi Kivity .vector = vec, 1817d36db35SAvi Kivity .delivery_mode = 0, 1827d36db35SAvi Kivity .trig_mode = 0, 1837d36db35SAvi Kivity }; 1847d36db35SAvi Kivity 1857d36db35SAvi Kivity ioapic_write_redir(line, e); 1867d36db35SAvi Kivity } 1877d36db35SAvi Kivity 1887d36db35SAvi Kivity static void set_irq_line(unsigned line, int val) 1897d36db35SAvi Kivity { 1907d36db35SAvi Kivity asm volatile("out %0, %1" : : "a"((u8)val), "d"((u16)(0x2000 + line))); 1917d36db35SAvi Kivity } 1927d36db35SAvi Kivity 1937d36db35SAvi Kivity static void toggle_irq_line(unsigned line) 1947d36db35SAvi Kivity { 1957d36db35SAvi Kivity set_irq_line(line, 1); 1967d36db35SAvi Kivity set_irq_line(line, 0); 1977d36db35SAvi Kivity } 1987d36db35SAvi Kivity 1997d36db35SAvi Kivity static int g_isr_77; 2007d36db35SAvi Kivity 2017d36db35SAvi Kivity static void ioapic_isr_77(isr_regs_t *regs) 2027d36db35SAvi Kivity { 2037d36db35SAvi Kivity ++g_isr_77; 2047d36db35SAvi Kivity eoi(); 2057d36db35SAvi Kivity } 2067d36db35SAvi Kivity 2077d36db35SAvi Kivity static void test_ioapic_intr(void) 2087d36db35SAvi Kivity { 209*d51bd17eSGleb Natapov handle_irq(0x77, ioapic_isr_77); 2107d36db35SAvi Kivity set_ioapic_redir(0x10, 0x77); 2117d36db35SAvi Kivity toggle_irq_line(0x10); 2127d36db35SAvi Kivity asm volatile ("nop"); 2137d36db35SAvi Kivity report("ioapic interrupt", g_isr_77 == 1); 2147d36db35SAvi Kivity } 2157d36db35SAvi Kivity 2167d36db35SAvi Kivity static int g_78, g_66, g_66_after_78; 2177d36db35SAvi Kivity static ulong g_66_rip, g_78_rip; 2187d36db35SAvi Kivity 2197d36db35SAvi Kivity static void ioapic_isr_78(isr_regs_t *regs) 2207d36db35SAvi Kivity { 2217d36db35SAvi Kivity ++g_78; 2227d36db35SAvi Kivity g_78_rip = regs->rip; 2237d36db35SAvi Kivity eoi(); 2247d36db35SAvi Kivity } 2257d36db35SAvi Kivity 2267d36db35SAvi Kivity static void ioapic_isr_66(isr_regs_t *regs) 2277d36db35SAvi Kivity { 2287d36db35SAvi Kivity ++g_66; 2297d36db35SAvi Kivity if (g_78) 2307d36db35SAvi Kivity ++g_66_after_78; 2317d36db35SAvi Kivity g_66_rip = regs->rip; 2327d36db35SAvi Kivity eoi(); 2337d36db35SAvi Kivity } 2347d36db35SAvi Kivity 2357d36db35SAvi Kivity static void test_ioapic_simultaneous(void) 2367d36db35SAvi Kivity { 237*d51bd17eSGleb Natapov handle_irq(0x78, ioapic_isr_78); 238*d51bd17eSGleb Natapov handle_irq(0x66, ioapic_isr_66); 2397d36db35SAvi Kivity set_ioapic_redir(0x10, 0x78); 2407d36db35SAvi Kivity set_ioapic_redir(0x11, 0x66); 2417d36db35SAvi Kivity irq_disable(); 2427d36db35SAvi Kivity toggle_irq_line(0x11); 2437d36db35SAvi Kivity toggle_irq_line(0x10); 2447d36db35SAvi Kivity irq_enable(); 2457d36db35SAvi Kivity asm volatile ("nop"); 2467d36db35SAvi Kivity report("ioapic simultaneous interrupt", 2477d36db35SAvi Kivity g_66 && g_78 && g_66_after_78 && g_66_rip == g_78_rip); 2487d36db35SAvi Kivity } 2497d36db35SAvi Kivity 250f2d2b7c7SAvi Kivity volatile int nmi_counter_private, nmi_counter, nmi_hlt_counter, sti_loop_active; 251f2d2b7c7SAvi Kivity 252f2d2b7c7SAvi Kivity void sti_nop(char *p) 253f2d2b7c7SAvi Kivity { 254f2d2b7c7SAvi Kivity asm volatile ( 255f2d2b7c7SAvi Kivity ".globl post_sti \n\t" 256f2d2b7c7SAvi Kivity "sti \n" 257f2d2b7c7SAvi Kivity /* 258f2d2b7c7SAvi Kivity * vmx won't exit on external interrupt if blocked-by-sti, 259f2d2b7c7SAvi Kivity * so give it a reason to exit by accessing an unmapped page. 260f2d2b7c7SAvi Kivity */ 261f2d2b7c7SAvi Kivity "post_sti: testb $0, %0 \n\t" 262f2d2b7c7SAvi Kivity "nop \n\t" 263f2d2b7c7SAvi Kivity "cli" 264f2d2b7c7SAvi Kivity : : "m"(*p) 265f2d2b7c7SAvi Kivity ); 266f2d2b7c7SAvi Kivity nmi_counter = nmi_counter_private; 267f2d2b7c7SAvi Kivity } 268f2d2b7c7SAvi Kivity 269f2d2b7c7SAvi Kivity static void sti_loop(void *ignore) 270f2d2b7c7SAvi Kivity { 271f2d2b7c7SAvi Kivity unsigned k = 0; 272f2d2b7c7SAvi Kivity 273f2d2b7c7SAvi Kivity while (sti_loop_active) { 274f2d2b7c7SAvi Kivity sti_nop((char *)(ulong)((k++ * 4096) % (128 * 1024 * 1024))); 275f2d2b7c7SAvi Kivity } 276f2d2b7c7SAvi Kivity } 277f2d2b7c7SAvi Kivity 278f2d2b7c7SAvi Kivity static void nmi_handler(isr_regs_t *regs) 279f2d2b7c7SAvi Kivity { 280f2d2b7c7SAvi Kivity extern void post_sti(void); 281f2d2b7c7SAvi Kivity ++nmi_counter_private; 282f2d2b7c7SAvi Kivity nmi_hlt_counter += regs->rip == (ulong)post_sti; 283f2d2b7c7SAvi Kivity } 284f2d2b7c7SAvi Kivity 285f2d2b7c7SAvi Kivity static void update_cr3(void *cr3) 286f2d2b7c7SAvi Kivity { 287f2d2b7c7SAvi Kivity write_cr3((ulong)cr3); 288f2d2b7c7SAvi Kivity } 289f2d2b7c7SAvi Kivity 290f2d2b7c7SAvi Kivity static void test_sti_nmi(void) 291f2d2b7c7SAvi Kivity { 292f2d2b7c7SAvi Kivity unsigned old_counter; 293f2d2b7c7SAvi Kivity 294f2d2b7c7SAvi Kivity if (cpu_count() < 2) { 295f2d2b7c7SAvi Kivity return; 296f2d2b7c7SAvi Kivity } 297f2d2b7c7SAvi Kivity 298*d51bd17eSGleb Natapov handle_irq(2, nmi_handler); 299f2d2b7c7SAvi Kivity on_cpu(1, update_cr3, (void *)read_cr3()); 300f2d2b7c7SAvi Kivity 301f2d2b7c7SAvi Kivity sti_loop_active = 1; 302f2d2b7c7SAvi Kivity on_cpu_async(1, sti_loop, 0); 303f2d2b7c7SAvi Kivity while (nmi_counter < 30000) { 304f2d2b7c7SAvi Kivity old_counter = nmi_counter; 305f2d2b7c7SAvi Kivity apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, 1); 306f2d2b7c7SAvi Kivity while (nmi_counter == old_counter) { 307f2d2b7c7SAvi Kivity ; 308f2d2b7c7SAvi Kivity } 309f2d2b7c7SAvi Kivity } 310f2d2b7c7SAvi Kivity sti_loop_active = 0; 311f2d2b7c7SAvi Kivity report("nmi-after-sti", nmi_hlt_counter == 0); 312f2d2b7c7SAvi Kivity } 313f2d2b7c7SAvi Kivity 3147d36db35SAvi Kivity int main() 3157d36db35SAvi Kivity { 3167d36db35SAvi Kivity setup_vm(); 317f2d2b7c7SAvi Kivity smp_init(); 318*d51bd17eSGleb Natapov setup_idt(); 3197d36db35SAvi Kivity 3207d36db35SAvi Kivity test_lapic_existence(); 3217d36db35SAvi Kivity 3227d36db35SAvi Kivity mask_pic_interrupts(); 3237d36db35SAvi Kivity enable_apic(); 3247d36db35SAvi Kivity test_enable_x2apic(); 3257d36db35SAvi Kivity 3267d36db35SAvi Kivity test_self_ipi(); 3277d36db35SAvi Kivity 3287d36db35SAvi Kivity test_ioapic_intr(); 3297d36db35SAvi Kivity test_ioapic_simultaneous(); 330f2d2b7c7SAvi Kivity test_sti_nmi(); 3317d36db35SAvi Kivity 3327d36db35SAvi Kivity printf("\nsummary: %d tests, %d failures\n", g_tests, g_fail); 3337d36db35SAvi Kivity 3347d36db35SAvi Kivity return g_fail != 0; 3357d36db35SAvi Kivity } 336