xref: /kvm-unit-tests/x86/apic.c (revision 598f9fc46e7b9c63f2c3fd5985cf195fbedb7ff1)
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 
28d423ca36SLiu, Jinsong #define TSC_DEADLINE_TIMER_MODE (2 << 17)
29d423ca36SLiu, Jinsong #define TSC_DEADLINE_TIMER_VECTOR 0xef
30d423ca36SLiu, Jinsong #define MSR_IA32_TSC            0x00000010
31d423ca36SLiu, Jinsong #define MSR_IA32_TSCDEADLINE    0x000006e0
32d423ca36SLiu, Jinsong 
33d423ca36SLiu, Jinsong static int tdt_count;
34d423ca36SLiu, Jinsong 
35d423ca36SLiu, Jinsong static void tsc_deadline_timer_isr(isr_regs_t *regs)
36d423ca36SLiu, Jinsong {
37d423ca36SLiu, Jinsong     ++tdt_count;
38d423ca36SLiu, Jinsong }
39d423ca36SLiu, Jinsong 
40d423ca36SLiu, Jinsong static void start_tsc_deadline_timer(void)
41d423ca36SLiu, Jinsong {
42d423ca36SLiu, Jinsong     handle_irq(TSC_DEADLINE_TIMER_VECTOR, tsc_deadline_timer_isr);
43d423ca36SLiu, Jinsong     irq_enable();
44d423ca36SLiu, Jinsong 
45d423ca36SLiu, Jinsong     wrmsr(MSR_IA32_TSCDEADLINE, rdmsr(MSR_IA32_TSC));
46d423ca36SLiu, Jinsong     asm volatile ("nop");
47d423ca36SLiu, Jinsong     report("tsc deadline timer", tdt_count == 1);
48d423ca36SLiu, Jinsong }
49d423ca36SLiu, Jinsong 
50d423ca36SLiu, Jinsong static int enable_tsc_deadline_timer(void)
51d423ca36SLiu, Jinsong {
52d423ca36SLiu, Jinsong     uint32_t lvtt;
53d423ca36SLiu, Jinsong 
54d423ca36SLiu, Jinsong     if (cpuid(1).c & (1 << 24)) {
55d423ca36SLiu, Jinsong         lvtt = TSC_DEADLINE_TIMER_MODE | TSC_DEADLINE_TIMER_VECTOR;
56d423ca36SLiu, Jinsong         apic_write(APIC_LVTT, lvtt);
57d423ca36SLiu, Jinsong         start_tsc_deadline_timer();
58d423ca36SLiu, Jinsong         return 1;
59d423ca36SLiu, Jinsong     } else {
60d423ca36SLiu, Jinsong         return 0;
61d423ca36SLiu, Jinsong     }
62d423ca36SLiu, Jinsong }
63d423ca36SLiu, Jinsong 
64d423ca36SLiu, Jinsong static void test_tsc_deadline_timer(void)
65d423ca36SLiu, Jinsong {
66d423ca36SLiu, Jinsong     if(enable_tsc_deadline_timer()) {
67d423ca36SLiu, Jinsong         printf("tsc deadline timer enabled\n");
68d423ca36SLiu, Jinsong     } else {
69d423ca36SLiu, Jinsong         printf("tsc deadline timer not detected\n");
70d423ca36SLiu, Jinsong     }
71d423ca36SLiu, Jinsong }
72d423ca36SLiu, Jinsong 
737d36db35SAvi Kivity #define MSR_APIC_BASE 0x0000001b
747d36db35SAvi Kivity 
757d36db35SAvi Kivity void test_enable_x2apic(void)
767d36db35SAvi Kivity {
777d36db35SAvi Kivity     if (enable_x2apic()) {
787d36db35SAvi Kivity         printf("x2apic enabled\n");
797d36db35SAvi Kivity     } else {
807d36db35SAvi Kivity         printf("x2apic not detected\n");
817d36db35SAvi Kivity     }
827d36db35SAvi Kivity }
837d36db35SAvi Kivity 
847d36db35SAvi Kivity static void eoi(void)
857d36db35SAvi Kivity {
867d36db35SAvi Kivity     apic_write(APIC_EOI, 0);
877d36db35SAvi Kivity }
887d36db35SAvi Kivity 
897d36db35SAvi Kivity static int ipi_count;
907d36db35SAvi Kivity 
917d36db35SAvi Kivity static void self_ipi_isr(isr_regs_t *regs)
927d36db35SAvi Kivity {
937d36db35SAvi Kivity     ++ipi_count;
947d36db35SAvi Kivity     eoi();
957d36db35SAvi Kivity }
967d36db35SAvi Kivity 
977d36db35SAvi Kivity static void test_self_ipi(void)
987d36db35SAvi Kivity {
997d36db35SAvi Kivity     int vec = 0xf1;
1007d36db35SAvi Kivity 
101d51bd17eSGleb Natapov     handle_irq(vec, self_ipi_isr);
1027d36db35SAvi Kivity     irq_enable();
1037d36db35SAvi Kivity     apic_icr_write(APIC_DEST_SELF | APIC_DEST_PHYSICAL | APIC_DM_FIXED | vec,
1047d36db35SAvi Kivity                    0);
1057d36db35SAvi Kivity     asm volatile ("nop");
1067d36db35SAvi Kivity     report("self ipi", ipi_count == 1);
1077d36db35SAvi Kivity }
1087d36db35SAvi Kivity 
1097d36db35SAvi Kivity static void set_ioapic_redir(unsigned line, unsigned vec)
1107d36db35SAvi Kivity {
1117d36db35SAvi Kivity     ioapic_redir_entry_t e = {
1127d36db35SAvi Kivity         .vector = vec,
1137d36db35SAvi Kivity         .delivery_mode = 0,
1147d36db35SAvi Kivity         .trig_mode = 0,
1157d36db35SAvi Kivity     };
1167d36db35SAvi Kivity 
1177d36db35SAvi Kivity     ioapic_write_redir(line, e);
1187d36db35SAvi Kivity }
1197d36db35SAvi Kivity 
1207d36db35SAvi Kivity static void set_irq_line(unsigned line, int val)
1217d36db35SAvi Kivity {
1227d36db35SAvi Kivity     asm volatile("out %0, %1" : : "a"((u8)val), "d"((u16)(0x2000 + line)));
1237d36db35SAvi Kivity }
1247d36db35SAvi Kivity 
1257d36db35SAvi Kivity static void toggle_irq_line(unsigned line)
1267d36db35SAvi Kivity {
1277d36db35SAvi Kivity     set_irq_line(line, 1);
1287d36db35SAvi Kivity     set_irq_line(line, 0);
1297d36db35SAvi Kivity }
1307d36db35SAvi Kivity 
1317d36db35SAvi Kivity static int g_isr_77;
1327d36db35SAvi Kivity 
1337d36db35SAvi Kivity static void ioapic_isr_77(isr_regs_t *regs)
1347d36db35SAvi Kivity {
1357d36db35SAvi Kivity     ++g_isr_77;
1367d36db35SAvi Kivity     eoi();
1377d36db35SAvi Kivity }
1387d36db35SAvi Kivity 
1397d36db35SAvi Kivity static void test_ioapic_intr(void)
1407d36db35SAvi Kivity {
141d51bd17eSGleb Natapov     handle_irq(0x77, ioapic_isr_77);
142*598f9fc4SJoerg Roedel     set_ioapic_redir(0x0e, 0x77);
143*598f9fc4SJoerg Roedel     toggle_irq_line(0x0e);
1447d36db35SAvi Kivity     asm volatile ("nop");
1457d36db35SAvi Kivity     report("ioapic interrupt", g_isr_77 == 1);
1467d36db35SAvi Kivity }
1477d36db35SAvi Kivity 
1487d36db35SAvi Kivity static int g_78, g_66, g_66_after_78;
1497d36db35SAvi Kivity static ulong g_66_rip, g_78_rip;
1507d36db35SAvi Kivity 
1517d36db35SAvi Kivity static void ioapic_isr_78(isr_regs_t *regs)
1527d36db35SAvi Kivity {
1537d36db35SAvi Kivity     ++g_78;
1547d36db35SAvi Kivity     g_78_rip = regs->rip;
1557d36db35SAvi Kivity     eoi();
1567d36db35SAvi Kivity }
1577d36db35SAvi Kivity 
1587d36db35SAvi Kivity static void ioapic_isr_66(isr_regs_t *regs)
1597d36db35SAvi Kivity {
1607d36db35SAvi Kivity     ++g_66;
1617d36db35SAvi Kivity     if (g_78)
1627d36db35SAvi Kivity         ++g_66_after_78;
1637d36db35SAvi Kivity     g_66_rip = regs->rip;
1647d36db35SAvi Kivity     eoi();
1657d36db35SAvi Kivity }
1667d36db35SAvi Kivity 
1677d36db35SAvi Kivity static void test_ioapic_simultaneous(void)
1687d36db35SAvi Kivity {
169d51bd17eSGleb Natapov     handle_irq(0x78, ioapic_isr_78);
170d51bd17eSGleb Natapov     handle_irq(0x66, ioapic_isr_66);
171*598f9fc4SJoerg Roedel     set_ioapic_redir(0x0e, 0x78);
172*598f9fc4SJoerg Roedel     set_ioapic_redir(0x0f, 0x66);
1737d36db35SAvi Kivity     irq_disable();
174*598f9fc4SJoerg Roedel     toggle_irq_line(0x0f);
175*598f9fc4SJoerg Roedel     toggle_irq_line(0x0e);
1767d36db35SAvi Kivity     irq_enable();
1777d36db35SAvi Kivity     asm volatile ("nop");
1787d36db35SAvi Kivity     report("ioapic simultaneous interrupt",
1797d36db35SAvi Kivity            g_66 && g_78 && g_66_after_78 && g_66_rip == g_78_rip);
1807d36db35SAvi Kivity }
1817d36db35SAvi Kivity 
182f2d2b7c7SAvi Kivity volatile int nmi_counter_private, nmi_counter, nmi_hlt_counter, sti_loop_active;
183f2d2b7c7SAvi Kivity 
184f2d2b7c7SAvi Kivity void sti_nop(char *p)
185f2d2b7c7SAvi Kivity {
186f2d2b7c7SAvi Kivity     asm volatile (
187f2d2b7c7SAvi Kivity 		  ".globl post_sti \n\t"
188f2d2b7c7SAvi Kivity 		  "sti \n"
189f2d2b7c7SAvi Kivity 		  /*
190f2d2b7c7SAvi Kivity 		   * vmx won't exit on external interrupt if blocked-by-sti,
191f2d2b7c7SAvi Kivity 		   * so give it a reason to exit by accessing an unmapped page.
192f2d2b7c7SAvi Kivity 		   */
193f2d2b7c7SAvi Kivity 		  "post_sti: testb $0, %0 \n\t"
194f2d2b7c7SAvi Kivity 		  "nop \n\t"
195f2d2b7c7SAvi Kivity 		  "cli"
196f2d2b7c7SAvi Kivity 		  : : "m"(*p)
197f2d2b7c7SAvi Kivity 		  );
198f2d2b7c7SAvi Kivity     nmi_counter = nmi_counter_private;
199f2d2b7c7SAvi Kivity }
200f2d2b7c7SAvi Kivity 
201f2d2b7c7SAvi Kivity static void sti_loop(void *ignore)
202f2d2b7c7SAvi Kivity {
203f2d2b7c7SAvi Kivity     unsigned k = 0;
204f2d2b7c7SAvi Kivity 
205f2d2b7c7SAvi Kivity     while (sti_loop_active) {
206f2d2b7c7SAvi Kivity 	sti_nop((char *)(ulong)((k++ * 4096) % (128 * 1024 * 1024)));
207f2d2b7c7SAvi Kivity     }
208f2d2b7c7SAvi Kivity }
209f2d2b7c7SAvi Kivity 
210f2d2b7c7SAvi Kivity static void nmi_handler(isr_regs_t *regs)
211f2d2b7c7SAvi Kivity {
212f2d2b7c7SAvi Kivity     extern void post_sti(void);
213f2d2b7c7SAvi Kivity     ++nmi_counter_private;
214f2d2b7c7SAvi Kivity     nmi_hlt_counter += regs->rip == (ulong)post_sti;
215f2d2b7c7SAvi Kivity }
216f2d2b7c7SAvi Kivity 
217f2d2b7c7SAvi Kivity static void update_cr3(void *cr3)
218f2d2b7c7SAvi Kivity {
219f2d2b7c7SAvi Kivity     write_cr3((ulong)cr3);
220f2d2b7c7SAvi Kivity }
221f2d2b7c7SAvi Kivity 
222f2d2b7c7SAvi Kivity static void test_sti_nmi(void)
223f2d2b7c7SAvi Kivity {
224f2d2b7c7SAvi Kivity     unsigned old_counter;
225f2d2b7c7SAvi Kivity 
226f2d2b7c7SAvi Kivity     if (cpu_count() < 2) {
227f2d2b7c7SAvi Kivity 	return;
228f2d2b7c7SAvi Kivity     }
229f2d2b7c7SAvi Kivity 
230d51bd17eSGleb Natapov     handle_irq(2, nmi_handler);
231f2d2b7c7SAvi Kivity     on_cpu(1, update_cr3, (void *)read_cr3());
232f2d2b7c7SAvi Kivity 
233f2d2b7c7SAvi Kivity     sti_loop_active = 1;
234f2d2b7c7SAvi Kivity     on_cpu_async(1, sti_loop, 0);
235f2d2b7c7SAvi Kivity     while (nmi_counter < 30000) {
236f2d2b7c7SAvi Kivity 	old_counter = nmi_counter;
237f2d2b7c7SAvi Kivity 	apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, 1);
238f2d2b7c7SAvi Kivity 	while (nmi_counter == old_counter) {
239f2d2b7c7SAvi Kivity 	    ;
240f2d2b7c7SAvi Kivity 	}
241f2d2b7c7SAvi Kivity     }
242f2d2b7c7SAvi Kivity     sti_loop_active = 0;
243f2d2b7c7SAvi Kivity     report("nmi-after-sti", nmi_hlt_counter == 0);
244f2d2b7c7SAvi Kivity }
245f2d2b7c7SAvi Kivity 
246173e7eacSAvi Kivity static volatile bool nmi_done, nmi_flushed;
247173e7eacSAvi Kivity static volatile int nmi_received;
248173e7eacSAvi Kivity static volatile int cpu0_nmi_ctr1, cpu1_nmi_ctr1;
249173e7eacSAvi Kivity static volatile int cpu0_nmi_ctr2, cpu1_nmi_ctr2;
250173e7eacSAvi Kivity 
251173e7eacSAvi Kivity static void multiple_nmi_handler(isr_regs_t *regs)
252173e7eacSAvi Kivity {
253173e7eacSAvi Kivity     ++nmi_received;
254173e7eacSAvi Kivity }
255173e7eacSAvi Kivity 
256173e7eacSAvi Kivity static void kick_me_nmi(void *blah)
257173e7eacSAvi Kivity {
258173e7eacSAvi Kivity     while (!nmi_done) {
259173e7eacSAvi Kivity 	++cpu1_nmi_ctr1;
260173e7eacSAvi Kivity 	while (cpu1_nmi_ctr1 != cpu0_nmi_ctr1 && !nmi_done) {
261173e7eacSAvi Kivity 	    pause();
262173e7eacSAvi Kivity 	}
263173e7eacSAvi Kivity 	if (nmi_done) {
264173e7eacSAvi Kivity 	    return;
265173e7eacSAvi Kivity 	}
266173e7eacSAvi Kivity 	apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, 0);
267173e7eacSAvi Kivity 	/* make sure the NMI has arrived by sending an IPI after it */
268173e7eacSAvi Kivity 	apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_FIXED | APIC_INT_ASSERT
269173e7eacSAvi Kivity 		       | 0x44, 0);
270173e7eacSAvi Kivity 	++cpu1_nmi_ctr2;
271173e7eacSAvi Kivity 	while (cpu1_nmi_ctr2 != cpu0_nmi_ctr2 && !nmi_done) {
272173e7eacSAvi Kivity 	    pause();
273173e7eacSAvi Kivity 	}
274173e7eacSAvi Kivity     }
275173e7eacSAvi Kivity }
276173e7eacSAvi Kivity 
277173e7eacSAvi Kivity static void flush_nmi(isr_regs_t *regs)
278173e7eacSAvi Kivity {
279173e7eacSAvi Kivity     nmi_flushed = true;
280173e7eacSAvi Kivity     apic_write(APIC_EOI, 0);
281173e7eacSAvi Kivity }
282173e7eacSAvi Kivity 
283173e7eacSAvi Kivity static void test_multiple_nmi(void)
284173e7eacSAvi Kivity {
285173e7eacSAvi Kivity     int i;
286173e7eacSAvi Kivity     bool ok = true;
287173e7eacSAvi Kivity 
288173e7eacSAvi Kivity     if (cpu_count() < 2) {
289173e7eacSAvi Kivity 	return;
290173e7eacSAvi Kivity     }
291173e7eacSAvi Kivity 
292173e7eacSAvi Kivity     sti();
293173e7eacSAvi Kivity     handle_irq(2, multiple_nmi_handler);
294173e7eacSAvi Kivity     handle_irq(0x44, flush_nmi);
295173e7eacSAvi Kivity     on_cpu_async(1, kick_me_nmi, 0);
296173e7eacSAvi Kivity     for (i = 0; i < 1000000; ++i) {
297173e7eacSAvi Kivity 	nmi_flushed = false;
298173e7eacSAvi Kivity 	nmi_received = 0;
299173e7eacSAvi Kivity 	++cpu0_nmi_ctr1;
300173e7eacSAvi Kivity 	while (cpu1_nmi_ctr1 != cpu0_nmi_ctr1) {
301173e7eacSAvi Kivity 	    pause();
302173e7eacSAvi Kivity 	}
303173e7eacSAvi Kivity 	apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, 0);
304173e7eacSAvi Kivity 	while (!nmi_flushed) {
305173e7eacSAvi Kivity 	    pause();
306173e7eacSAvi Kivity 	}
307173e7eacSAvi Kivity 	if (nmi_received != 2) {
308173e7eacSAvi Kivity 	    ok = false;
309173e7eacSAvi Kivity 	    break;
310173e7eacSAvi Kivity 	}
311173e7eacSAvi Kivity 	++cpu0_nmi_ctr2;
312173e7eacSAvi Kivity 	while (cpu1_nmi_ctr2 != cpu0_nmi_ctr2) {
313173e7eacSAvi Kivity 	    pause();
314173e7eacSAvi Kivity 	}
315173e7eacSAvi Kivity     }
316173e7eacSAvi Kivity     nmi_done = true;
317173e7eacSAvi Kivity     report("multiple nmi", ok);
318173e7eacSAvi Kivity }
319173e7eacSAvi Kivity 
3207d36db35SAvi Kivity int main()
3217d36db35SAvi Kivity {
3227d36db35SAvi Kivity     setup_vm();
323f2d2b7c7SAvi Kivity     smp_init();
324d51bd17eSGleb Natapov     setup_idt();
3257d36db35SAvi Kivity 
3267d36db35SAvi Kivity     test_lapic_existence();
3277d36db35SAvi Kivity 
3287d36db35SAvi Kivity     mask_pic_interrupts();
3297d36db35SAvi Kivity     enable_apic();
3307d36db35SAvi Kivity     test_enable_x2apic();
3317d36db35SAvi Kivity 
3327d36db35SAvi Kivity     test_self_ipi();
3337d36db35SAvi Kivity 
3347d36db35SAvi Kivity     test_ioapic_intr();
3357d36db35SAvi Kivity     test_ioapic_simultaneous();
336f2d2b7c7SAvi Kivity     test_sti_nmi();
337173e7eacSAvi Kivity     test_multiple_nmi();
3387d36db35SAvi Kivity 
339d423ca36SLiu, Jinsong     test_tsc_deadline_timer();
340d423ca36SLiu, Jinsong 
3417d36db35SAvi Kivity     printf("\nsummary: %d tests, %d failures\n", g_tests, g_fail);
3427d36db35SAvi Kivity 
3437d36db35SAvi Kivity     return g_fail != 0;
3447d36db35SAvi Kivity }
345