xref: /kvm-unit-tests/x86/apic.c (revision 173e7eac73fedd65aed406437d4709e19ca0e92d)
17d36db35SAvi Kivity #include "libcflat.h"
27d36db35SAvi Kivity #include "apic.h"
37d36db35SAvi Kivity #include "vm.h"
4f2d2b7c7SAvi Kivity #include "smp.h"
5e7c37968SGleb Natapov #include "desc.h"
6110f0d93SGleb Natapov #include "isr.h"
77d36db35SAvi Kivity 
87d36db35SAvi Kivity static int g_fail;
97d36db35SAvi Kivity static int g_tests;
107d36db35SAvi Kivity 
117d36db35SAvi Kivity static void report(const char *msg, int pass)
127d36db35SAvi Kivity {
137d36db35SAvi Kivity     ++g_tests;
147d36db35SAvi Kivity     printf("%s: %s\n", msg, (pass ? "PASS" : "FAIL"));
157d36db35SAvi Kivity     if (!pass)
167d36db35SAvi Kivity         ++g_fail;
177d36db35SAvi Kivity }
187d36db35SAvi Kivity 
197d36db35SAvi Kivity static void test_lapic_existence(void)
207d36db35SAvi Kivity {
217d36db35SAvi Kivity     u32 lvr;
227d36db35SAvi Kivity 
237d36db35SAvi Kivity     lvr = apic_read(APIC_LVR);
247d36db35SAvi Kivity     printf("apic version: %x\n", lvr);
257d36db35SAvi Kivity     report("apic existence", (u16)lvr == 0x14);
267d36db35SAvi Kivity }
277d36db35SAvi Kivity 
287d36db35SAvi Kivity #define MSR_APIC_BASE 0x0000001b
297d36db35SAvi Kivity 
307d36db35SAvi Kivity void test_enable_x2apic(void)
317d36db35SAvi Kivity {
327d36db35SAvi Kivity     if (enable_x2apic()) {
337d36db35SAvi Kivity         printf("x2apic enabled\n");
347d36db35SAvi Kivity     } else {
357d36db35SAvi Kivity         printf("x2apic not detected\n");
367d36db35SAvi Kivity     }
377d36db35SAvi Kivity }
387d36db35SAvi Kivity 
397d36db35SAvi Kivity static void eoi(void)
407d36db35SAvi Kivity {
417d36db35SAvi Kivity     apic_write(APIC_EOI, 0);
427d36db35SAvi Kivity }
437d36db35SAvi Kivity 
447d36db35SAvi Kivity static int ipi_count;
457d36db35SAvi Kivity 
467d36db35SAvi Kivity static void self_ipi_isr(isr_regs_t *regs)
477d36db35SAvi Kivity {
487d36db35SAvi Kivity     ++ipi_count;
497d36db35SAvi Kivity     eoi();
507d36db35SAvi Kivity }
517d36db35SAvi Kivity 
527d36db35SAvi Kivity static void test_self_ipi(void)
537d36db35SAvi Kivity {
547d36db35SAvi Kivity     int vec = 0xf1;
557d36db35SAvi Kivity 
56d51bd17eSGleb Natapov     handle_irq(vec, self_ipi_isr);
577d36db35SAvi Kivity     irq_enable();
587d36db35SAvi Kivity     apic_icr_write(APIC_DEST_SELF | APIC_DEST_PHYSICAL | APIC_DM_FIXED | vec,
597d36db35SAvi Kivity                    0);
607d36db35SAvi Kivity     asm volatile ("nop");
617d36db35SAvi Kivity     report("self ipi", ipi_count == 1);
627d36db35SAvi Kivity }
637d36db35SAvi Kivity 
647d36db35SAvi Kivity static void set_ioapic_redir(unsigned line, unsigned vec)
657d36db35SAvi Kivity {
667d36db35SAvi Kivity     ioapic_redir_entry_t e = {
677d36db35SAvi Kivity         .vector = vec,
687d36db35SAvi Kivity         .delivery_mode = 0,
697d36db35SAvi Kivity         .trig_mode = 0,
707d36db35SAvi Kivity     };
717d36db35SAvi Kivity 
727d36db35SAvi Kivity     ioapic_write_redir(line, e);
737d36db35SAvi Kivity }
747d36db35SAvi Kivity 
757d36db35SAvi Kivity static void set_irq_line(unsigned line, int val)
767d36db35SAvi Kivity {
777d36db35SAvi Kivity     asm volatile("out %0, %1" : : "a"((u8)val), "d"((u16)(0x2000 + line)));
787d36db35SAvi Kivity }
797d36db35SAvi Kivity 
807d36db35SAvi Kivity static void toggle_irq_line(unsigned line)
817d36db35SAvi Kivity {
827d36db35SAvi Kivity     set_irq_line(line, 1);
837d36db35SAvi Kivity     set_irq_line(line, 0);
847d36db35SAvi Kivity }
857d36db35SAvi Kivity 
867d36db35SAvi Kivity static int g_isr_77;
877d36db35SAvi Kivity 
887d36db35SAvi Kivity static void ioapic_isr_77(isr_regs_t *regs)
897d36db35SAvi Kivity {
907d36db35SAvi Kivity     ++g_isr_77;
917d36db35SAvi Kivity     eoi();
927d36db35SAvi Kivity }
937d36db35SAvi Kivity 
947d36db35SAvi Kivity static void test_ioapic_intr(void)
957d36db35SAvi Kivity {
96d51bd17eSGleb Natapov     handle_irq(0x77, ioapic_isr_77);
977d36db35SAvi Kivity     set_ioapic_redir(0x10, 0x77);
987d36db35SAvi Kivity     toggle_irq_line(0x10);
997d36db35SAvi Kivity     asm volatile ("nop");
1007d36db35SAvi Kivity     report("ioapic interrupt", g_isr_77 == 1);
1017d36db35SAvi Kivity }
1027d36db35SAvi Kivity 
1037d36db35SAvi Kivity static int g_78, g_66, g_66_after_78;
1047d36db35SAvi Kivity static ulong g_66_rip, g_78_rip;
1057d36db35SAvi Kivity 
1067d36db35SAvi Kivity static void ioapic_isr_78(isr_regs_t *regs)
1077d36db35SAvi Kivity {
1087d36db35SAvi Kivity     ++g_78;
1097d36db35SAvi Kivity     g_78_rip = regs->rip;
1107d36db35SAvi Kivity     eoi();
1117d36db35SAvi Kivity }
1127d36db35SAvi Kivity 
1137d36db35SAvi Kivity static void ioapic_isr_66(isr_regs_t *regs)
1147d36db35SAvi Kivity {
1157d36db35SAvi Kivity     ++g_66;
1167d36db35SAvi Kivity     if (g_78)
1177d36db35SAvi Kivity         ++g_66_after_78;
1187d36db35SAvi Kivity     g_66_rip = regs->rip;
1197d36db35SAvi Kivity     eoi();
1207d36db35SAvi Kivity }
1217d36db35SAvi Kivity 
1227d36db35SAvi Kivity static void test_ioapic_simultaneous(void)
1237d36db35SAvi Kivity {
124d51bd17eSGleb Natapov     handle_irq(0x78, ioapic_isr_78);
125d51bd17eSGleb Natapov     handle_irq(0x66, ioapic_isr_66);
1267d36db35SAvi Kivity     set_ioapic_redir(0x10, 0x78);
1277d36db35SAvi Kivity     set_ioapic_redir(0x11, 0x66);
1287d36db35SAvi Kivity     irq_disable();
1297d36db35SAvi Kivity     toggle_irq_line(0x11);
1307d36db35SAvi Kivity     toggle_irq_line(0x10);
1317d36db35SAvi Kivity     irq_enable();
1327d36db35SAvi Kivity     asm volatile ("nop");
1337d36db35SAvi Kivity     report("ioapic simultaneous interrupt",
1347d36db35SAvi Kivity            g_66 && g_78 && g_66_after_78 && g_66_rip == g_78_rip);
1357d36db35SAvi Kivity }
1367d36db35SAvi Kivity 
137f2d2b7c7SAvi Kivity volatile int nmi_counter_private, nmi_counter, nmi_hlt_counter, sti_loop_active;
138f2d2b7c7SAvi Kivity 
139f2d2b7c7SAvi Kivity void sti_nop(char *p)
140f2d2b7c7SAvi Kivity {
141f2d2b7c7SAvi Kivity     asm volatile (
142f2d2b7c7SAvi Kivity 		  ".globl post_sti \n\t"
143f2d2b7c7SAvi Kivity 		  "sti \n"
144f2d2b7c7SAvi Kivity 		  /*
145f2d2b7c7SAvi Kivity 		   * vmx won't exit on external interrupt if blocked-by-sti,
146f2d2b7c7SAvi Kivity 		   * so give it a reason to exit by accessing an unmapped page.
147f2d2b7c7SAvi Kivity 		   */
148f2d2b7c7SAvi Kivity 		  "post_sti: testb $0, %0 \n\t"
149f2d2b7c7SAvi Kivity 		  "nop \n\t"
150f2d2b7c7SAvi Kivity 		  "cli"
151f2d2b7c7SAvi Kivity 		  : : "m"(*p)
152f2d2b7c7SAvi Kivity 		  );
153f2d2b7c7SAvi Kivity     nmi_counter = nmi_counter_private;
154f2d2b7c7SAvi Kivity }
155f2d2b7c7SAvi Kivity 
156f2d2b7c7SAvi Kivity static void sti_loop(void *ignore)
157f2d2b7c7SAvi Kivity {
158f2d2b7c7SAvi Kivity     unsigned k = 0;
159f2d2b7c7SAvi Kivity 
160f2d2b7c7SAvi Kivity     while (sti_loop_active) {
161f2d2b7c7SAvi Kivity 	sti_nop((char *)(ulong)((k++ * 4096) % (128 * 1024 * 1024)));
162f2d2b7c7SAvi Kivity     }
163f2d2b7c7SAvi Kivity }
164f2d2b7c7SAvi Kivity 
165f2d2b7c7SAvi Kivity static void nmi_handler(isr_regs_t *regs)
166f2d2b7c7SAvi Kivity {
167f2d2b7c7SAvi Kivity     extern void post_sti(void);
168f2d2b7c7SAvi Kivity     ++nmi_counter_private;
169f2d2b7c7SAvi Kivity     nmi_hlt_counter += regs->rip == (ulong)post_sti;
170f2d2b7c7SAvi Kivity }
171f2d2b7c7SAvi Kivity 
172f2d2b7c7SAvi Kivity static void update_cr3(void *cr3)
173f2d2b7c7SAvi Kivity {
174f2d2b7c7SAvi Kivity     write_cr3((ulong)cr3);
175f2d2b7c7SAvi Kivity }
176f2d2b7c7SAvi Kivity 
177f2d2b7c7SAvi Kivity static void test_sti_nmi(void)
178f2d2b7c7SAvi Kivity {
179f2d2b7c7SAvi Kivity     unsigned old_counter;
180f2d2b7c7SAvi Kivity 
181f2d2b7c7SAvi Kivity     if (cpu_count() < 2) {
182f2d2b7c7SAvi Kivity 	return;
183f2d2b7c7SAvi Kivity     }
184f2d2b7c7SAvi Kivity 
185d51bd17eSGleb Natapov     handle_irq(2, nmi_handler);
186f2d2b7c7SAvi Kivity     on_cpu(1, update_cr3, (void *)read_cr3());
187f2d2b7c7SAvi Kivity 
188f2d2b7c7SAvi Kivity     sti_loop_active = 1;
189f2d2b7c7SAvi Kivity     on_cpu_async(1, sti_loop, 0);
190f2d2b7c7SAvi Kivity     while (nmi_counter < 30000) {
191f2d2b7c7SAvi Kivity 	old_counter = nmi_counter;
192f2d2b7c7SAvi Kivity 	apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, 1);
193f2d2b7c7SAvi Kivity 	while (nmi_counter == old_counter) {
194f2d2b7c7SAvi Kivity 	    ;
195f2d2b7c7SAvi Kivity 	}
196f2d2b7c7SAvi Kivity     }
197f2d2b7c7SAvi Kivity     sti_loop_active = 0;
198f2d2b7c7SAvi Kivity     report("nmi-after-sti", nmi_hlt_counter == 0);
199f2d2b7c7SAvi Kivity }
200f2d2b7c7SAvi Kivity 
201*173e7eacSAvi Kivity static volatile bool nmi_done, nmi_flushed;
202*173e7eacSAvi Kivity static volatile int nmi_received;
203*173e7eacSAvi Kivity static volatile int cpu0_nmi_ctr1, cpu1_nmi_ctr1;
204*173e7eacSAvi Kivity static volatile int cpu0_nmi_ctr2, cpu1_nmi_ctr2;
205*173e7eacSAvi Kivity 
206*173e7eacSAvi Kivity static void multiple_nmi_handler(isr_regs_t *regs)
207*173e7eacSAvi Kivity {
208*173e7eacSAvi Kivity     ++nmi_received;
209*173e7eacSAvi Kivity }
210*173e7eacSAvi Kivity 
211*173e7eacSAvi Kivity static void kick_me_nmi(void *blah)
212*173e7eacSAvi Kivity {
213*173e7eacSAvi Kivity     while (!nmi_done) {
214*173e7eacSAvi Kivity 	++cpu1_nmi_ctr1;
215*173e7eacSAvi Kivity 	while (cpu1_nmi_ctr1 != cpu0_nmi_ctr1 && !nmi_done) {
216*173e7eacSAvi Kivity 	    pause();
217*173e7eacSAvi Kivity 	}
218*173e7eacSAvi Kivity 	if (nmi_done) {
219*173e7eacSAvi Kivity 	    return;
220*173e7eacSAvi Kivity 	}
221*173e7eacSAvi Kivity 	apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, 0);
222*173e7eacSAvi Kivity 	/* make sure the NMI has arrived by sending an IPI after it */
223*173e7eacSAvi Kivity 	apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_FIXED | APIC_INT_ASSERT
224*173e7eacSAvi Kivity 		       | 0x44, 0);
225*173e7eacSAvi Kivity 	++cpu1_nmi_ctr2;
226*173e7eacSAvi Kivity 	while (cpu1_nmi_ctr2 != cpu0_nmi_ctr2 && !nmi_done) {
227*173e7eacSAvi Kivity 	    pause();
228*173e7eacSAvi Kivity 	}
229*173e7eacSAvi Kivity     }
230*173e7eacSAvi Kivity }
231*173e7eacSAvi Kivity 
232*173e7eacSAvi Kivity static void flush_nmi(isr_regs_t *regs)
233*173e7eacSAvi Kivity {
234*173e7eacSAvi Kivity     nmi_flushed = true;
235*173e7eacSAvi Kivity     apic_write(APIC_EOI, 0);
236*173e7eacSAvi Kivity }
237*173e7eacSAvi Kivity 
238*173e7eacSAvi Kivity static void test_multiple_nmi(void)
239*173e7eacSAvi Kivity {
240*173e7eacSAvi Kivity     int i;
241*173e7eacSAvi Kivity     bool ok = true;
242*173e7eacSAvi Kivity 
243*173e7eacSAvi Kivity     if (cpu_count() < 2) {
244*173e7eacSAvi Kivity 	return;
245*173e7eacSAvi Kivity     }
246*173e7eacSAvi Kivity 
247*173e7eacSAvi Kivity     sti();
248*173e7eacSAvi Kivity     handle_irq(2, multiple_nmi_handler);
249*173e7eacSAvi Kivity     handle_irq(0x44, flush_nmi);
250*173e7eacSAvi Kivity     on_cpu_async(1, kick_me_nmi, 0);
251*173e7eacSAvi Kivity     for (i = 0; i < 1000000; ++i) {
252*173e7eacSAvi Kivity 	nmi_flushed = false;
253*173e7eacSAvi Kivity 	nmi_received = 0;
254*173e7eacSAvi Kivity 	++cpu0_nmi_ctr1;
255*173e7eacSAvi Kivity 	while (cpu1_nmi_ctr1 != cpu0_nmi_ctr1) {
256*173e7eacSAvi Kivity 	    pause();
257*173e7eacSAvi Kivity 	}
258*173e7eacSAvi Kivity 	apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, 0);
259*173e7eacSAvi Kivity 	while (!nmi_flushed) {
260*173e7eacSAvi Kivity 	    pause();
261*173e7eacSAvi Kivity 	}
262*173e7eacSAvi Kivity 	if (nmi_received != 2) {
263*173e7eacSAvi Kivity 	    ok = false;
264*173e7eacSAvi Kivity 	    break;
265*173e7eacSAvi Kivity 	}
266*173e7eacSAvi Kivity 	++cpu0_nmi_ctr2;
267*173e7eacSAvi Kivity 	while (cpu1_nmi_ctr2 != cpu0_nmi_ctr2) {
268*173e7eacSAvi Kivity 	    pause();
269*173e7eacSAvi Kivity 	}
270*173e7eacSAvi Kivity     }
271*173e7eacSAvi Kivity     nmi_done = true;
272*173e7eacSAvi Kivity     report("multiple nmi", ok);
273*173e7eacSAvi Kivity }
274*173e7eacSAvi Kivity 
2757d36db35SAvi Kivity int main()
2767d36db35SAvi Kivity {
2777d36db35SAvi Kivity     setup_vm();
278f2d2b7c7SAvi Kivity     smp_init();
279d51bd17eSGleb Natapov     setup_idt();
2807d36db35SAvi Kivity 
2817d36db35SAvi Kivity     test_lapic_existence();
2827d36db35SAvi Kivity 
2837d36db35SAvi Kivity     mask_pic_interrupts();
2847d36db35SAvi Kivity     enable_apic();
2857d36db35SAvi Kivity     test_enable_x2apic();
2867d36db35SAvi Kivity 
2877d36db35SAvi Kivity     test_self_ipi();
2887d36db35SAvi Kivity 
2897d36db35SAvi Kivity     test_ioapic_intr();
2907d36db35SAvi Kivity     test_ioapic_simultaneous();
291f2d2b7c7SAvi Kivity     test_sti_nmi();
292*173e7eacSAvi Kivity     test_multiple_nmi();
2937d36db35SAvi Kivity 
2947d36db35SAvi Kivity     printf("\nsummary: %d tests, %d failures\n", g_tests, g_fail);
2957d36db35SAvi Kivity 
2967d36db35SAvi Kivity     return g_fail != 0;
2977d36db35SAvi Kivity }
298