xref: /kvm-unit-tests/x86/apic.c (revision d51bd17e5a5643abb55839ba7df179d07cd9e424)
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