xref: /kvm-unit-tests/x86/apic.c (revision 32b9603c394e6a53fcb8c9cd724061d0abef787a)
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"
722c7d929SJan Kiszka #include "msr.h"
87d36db35SAvi Kivity 
97d36db35SAvi Kivity static void test_lapic_existence(void)
107d36db35SAvi Kivity {
117d36db35SAvi Kivity     u32 lvr;
127d36db35SAvi Kivity 
137d36db35SAvi Kivity     lvr = apic_read(APIC_LVR);
147d36db35SAvi Kivity     printf("apic version: %x\n", lvr);
157d36db35SAvi Kivity     report("apic existence", (u16)lvr == 0x14);
167d36db35SAvi Kivity }
177d36db35SAvi Kivity 
18d423ca36SLiu, Jinsong #define TSC_DEADLINE_TIMER_MODE (2 << 17)
19d423ca36SLiu, Jinsong #define TSC_DEADLINE_TIMER_VECTOR 0xef
20d423ca36SLiu, Jinsong #define MSR_IA32_TSC            0x00000010
21d423ca36SLiu, Jinsong #define MSR_IA32_TSCDEADLINE    0x000006e0
22d423ca36SLiu, Jinsong 
23d423ca36SLiu, Jinsong static int tdt_count;
24d423ca36SLiu, Jinsong 
25d423ca36SLiu, Jinsong static void tsc_deadline_timer_isr(isr_regs_t *regs)
26d423ca36SLiu, Jinsong {
27d423ca36SLiu, Jinsong     ++tdt_count;
28d423ca36SLiu, Jinsong }
29d423ca36SLiu, Jinsong 
30*32b9603cSRadim Krčmář static void __test_tsc_deadline_timer(void)
31d423ca36SLiu, Jinsong {
32d423ca36SLiu, Jinsong     handle_irq(TSC_DEADLINE_TIMER_VECTOR, tsc_deadline_timer_isr);
33d423ca36SLiu, Jinsong     irq_enable();
34d423ca36SLiu, Jinsong 
35d423ca36SLiu, Jinsong     wrmsr(MSR_IA32_TSCDEADLINE, rdmsr(MSR_IA32_TSC));
36d423ca36SLiu, Jinsong     asm volatile ("nop");
37d423ca36SLiu, Jinsong     report("tsc deadline timer", tdt_count == 1);
38f8833144SNadav Amit     report("tsc deadline timer clearing", rdmsr(MSR_IA32_TSCDEADLINE) == 0);
39d423ca36SLiu, Jinsong }
40d423ca36SLiu, Jinsong 
41d423ca36SLiu, Jinsong static int enable_tsc_deadline_timer(void)
42d423ca36SLiu, Jinsong {
43d423ca36SLiu, Jinsong     uint32_t lvtt;
44d423ca36SLiu, Jinsong 
45d423ca36SLiu, Jinsong     if (cpuid(1).c & (1 << 24)) {
46d423ca36SLiu, Jinsong         lvtt = TSC_DEADLINE_TIMER_MODE | TSC_DEADLINE_TIMER_VECTOR;
47d423ca36SLiu, Jinsong         apic_write(APIC_LVTT, lvtt);
48d423ca36SLiu, Jinsong         return 1;
49d423ca36SLiu, Jinsong     } else {
50d423ca36SLiu, Jinsong         return 0;
51d423ca36SLiu, Jinsong     }
52d423ca36SLiu, Jinsong }
53d423ca36SLiu, Jinsong 
54d423ca36SLiu, Jinsong static void test_tsc_deadline_timer(void)
55d423ca36SLiu, Jinsong {
56d423ca36SLiu, Jinsong     if(enable_tsc_deadline_timer()) {
57*32b9603cSRadim Krčmář         __test_tsc_deadline_timer();
58d423ca36SLiu, Jinsong     } else {
59*32b9603cSRadim Krčmář         report_skip("tsc deadline timer not detected");
60d423ca36SLiu, Jinsong     }
61d423ca36SLiu, Jinsong }
62d423ca36SLiu, Jinsong 
6322c7d929SJan Kiszka static void do_write_apicbase(void *data)
6422c7d929SJan Kiszka {
6522c7d929SJan Kiszka     wrmsr(MSR_IA32_APICBASE, *(u64 *)data);
6603f37ef2SPaolo Bonzini }
677d36db35SAvi Kivity 
687d36db35SAvi Kivity void test_enable_x2apic(void)
697d36db35SAvi Kivity {
7022c7d929SJan Kiszka     u64 invalid_state = APIC_DEFAULT_PHYS_BASE | APIC_BSP | APIC_EXTD;
7122c7d929SJan Kiszka     u64 apic_enabled = APIC_DEFAULT_PHYS_BASE | APIC_BSP | APIC_EN;
7222c7d929SJan Kiszka     u64 x2apic_enabled =
7322c7d929SJan Kiszka         APIC_DEFAULT_PHYS_BASE | APIC_BSP | APIC_EN | APIC_EXTD;
7422c7d929SJan Kiszka 
757d36db35SAvi Kivity     if (enable_x2apic()) {
767d36db35SAvi Kivity         printf("x2apic enabled\n");
7722c7d929SJan Kiszka 
7822c7d929SJan Kiszka         report("x2apic enabled to invalid state",
7922c7d929SJan Kiszka                test_for_exception(GP_VECTOR, do_write_apicbase,
8022c7d929SJan Kiszka                                   &invalid_state));
8122c7d929SJan Kiszka         report("x2apic enabled to apic enabled",
8222c7d929SJan Kiszka                test_for_exception(GP_VECTOR, do_write_apicbase,
8322c7d929SJan Kiszka                                   &apic_enabled));
8422c7d929SJan Kiszka 
8522c7d929SJan Kiszka         wrmsr(MSR_IA32_APICBASE, APIC_DEFAULT_PHYS_BASE | APIC_BSP);
8622c7d929SJan Kiszka         report("disabled to invalid state",
8722c7d929SJan Kiszka                test_for_exception(GP_VECTOR, do_write_apicbase,
8822c7d929SJan Kiszka                                   &invalid_state));
8922c7d929SJan Kiszka         report("disabled to x2apic enabled",
9022c7d929SJan Kiszka                test_for_exception(GP_VECTOR, do_write_apicbase,
9122c7d929SJan Kiszka                                   &x2apic_enabled));
9222c7d929SJan Kiszka 
9322c7d929SJan Kiszka         wrmsr(MSR_IA32_APICBASE, apic_enabled);
9422c7d929SJan Kiszka         report("apic enabled to invalid state",
9522c7d929SJan Kiszka                test_for_exception(GP_VECTOR, do_write_apicbase,
9622c7d929SJan Kiszka                                   &invalid_state));
9722c7d929SJan Kiszka 
9822c7d929SJan Kiszka         wrmsr(MSR_IA32_APICBASE, x2apic_enabled);
9922c7d929SJan Kiszka         apic_write(APIC_SPIV, 0x1ff);
1007d36db35SAvi Kivity     } else {
1017d36db35SAvi Kivity         printf("x2apic not detected\n");
10222c7d929SJan Kiszka 
10322c7d929SJan Kiszka         report("enable unsupported x2apic",
10422c7d929SJan Kiszka                test_for_exception(GP_VECTOR, do_write_apicbase,
10522c7d929SJan Kiszka                                   &x2apic_enabled));
1067d36db35SAvi Kivity     }
1077d36db35SAvi Kivity }
1087d36db35SAvi Kivity 
1099b6bdb3fSJan Kiszka #define ALTERNATE_APIC_BASE	0x42000000
1109b6bdb3fSJan Kiszka 
1119b6bdb3fSJan Kiszka static void test_apicbase(void)
1129b6bdb3fSJan Kiszka {
1139b6bdb3fSJan Kiszka     u64 orig_apicbase = rdmsr(MSR_IA32_APICBASE);
1149b6bdb3fSJan Kiszka     u32 lvr = apic_read(APIC_LVR);
1159b6bdb3fSJan Kiszka     u64 value;
1169b6bdb3fSJan Kiszka 
1179b6bdb3fSJan Kiszka     wrmsr(MSR_IA32_APICBASE, orig_apicbase & ~(APIC_EN | APIC_EXTD));
1189b6bdb3fSJan Kiszka     wrmsr(MSR_IA32_APICBASE, ALTERNATE_APIC_BASE | APIC_BSP | APIC_EN);
1199b6bdb3fSJan Kiszka 
1205bba1769SAndrew Jones     report_prefix_push("apicbase");
1215bba1769SAndrew Jones 
1229b6bdb3fSJan Kiszka     report("relocate apic",
1239b6bdb3fSJan Kiszka            *(volatile u32 *)(ALTERNATE_APIC_BASE + APIC_LVR) == lvr);
1249b6bdb3fSJan Kiszka 
125772befb7SEduardo Habkost     value = orig_apicbase | (1UL << cpuid_maxphyaddr());
1265bba1769SAndrew Jones     report("reserved physaddr bits",
1279b6bdb3fSJan Kiszka            test_for_exception(GP_VECTOR, do_write_apicbase, &value));
1289b6bdb3fSJan Kiszka 
1299b6bdb3fSJan Kiszka     value = orig_apicbase | 1;
1305bba1769SAndrew Jones     report("reserved low bits",
1319b6bdb3fSJan Kiszka            test_for_exception(GP_VECTOR, do_write_apicbase, &value));
1329b6bdb3fSJan Kiszka 
1339b6bdb3fSJan Kiszka     wrmsr(MSR_IA32_APICBASE, orig_apicbase);
1349b6bdb3fSJan Kiszka     apic_write(APIC_SPIV, 0x1ff);
1355bba1769SAndrew Jones 
1365bba1769SAndrew Jones     report_prefix_pop();
1379b6bdb3fSJan Kiszka }
1389b6bdb3fSJan Kiszka 
1397d36db35SAvi Kivity static int ipi_count;
1407d36db35SAvi Kivity 
1417d36db35SAvi Kivity static void self_ipi_isr(isr_regs_t *regs)
1427d36db35SAvi Kivity {
1437d36db35SAvi Kivity     ++ipi_count;
1447d36db35SAvi Kivity     eoi();
1457d36db35SAvi Kivity }
1467d36db35SAvi Kivity 
1477d36db35SAvi Kivity static void test_self_ipi(void)
1487d36db35SAvi Kivity {
1497d36db35SAvi Kivity     int vec = 0xf1;
1507d36db35SAvi Kivity 
151d51bd17eSGleb Natapov     handle_irq(vec, self_ipi_isr);
1527d36db35SAvi Kivity     irq_enable();
1537d36db35SAvi Kivity     apic_icr_write(APIC_DEST_SELF | APIC_DEST_PHYSICAL | APIC_DM_FIXED | vec,
1547d36db35SAvi Kivity                    0);
1557d36db35SAvi Kivity     asm volatile ("nop");
1567d36db35SAvi Kivity     report("self ipi", ipi_count == 1);
1577d36db35SAvi Kivity }
1587d36db35SAvi Kivity 
159f2d2b7c7SAvi Kivity volatile int nmi_counter_private, nmi_counter, nmi_hlt_counter, sti_loop_active;
160f2d2b7c7SAvi Kivity 
161f2d2b7c7SAvi Kivity void sti_nop(char *p)
162f2d2b7c7SAvi Kivity {
163f2d2b7c7SAvi Kivity     asm volatile (
164f2d2b7c7SAvi Kivity 		  ".globl post_sti \n\t"
165f2d2b7c7SAvi Kivity 		  "sti \n"
166f2d2b7c7SAvi Kivity 		  /*
167f2d2b7c7SAvi Kivity 		   * vmx won't exit on external interrupt if blocked-by-sti,
168f2d2b7c7SAvi Kivity 		   * so give it a reason to exit by accessing an unmapped page.
169f2d2b7c7SAvi Kivity 		   */
170f2d2b7c7SAvi Kivity 		  "post_sti: testb $0, %0 \n\t"
171f2d2b7c7SAvi Kivity 		  "nop \n\t"
172f2d2b7c7SAvi Kivity 		  "cli"
173f2d2b7c7SAvi Kivity 		  : : "m"(*p)
174f2d2b7c7SAvi Kivity 		  );
175f2d2b7c7SAvi Kivity     nmi_counter = nmi_counter_private;
176f2d2b7c7SAvi Kivity }
177f2d2b7c7SAvi Kivity 
178f2d2b7c7SAvi Kivity static void sti_loop(void *ignore)
179f2d2b7c7SAvi Kivity {
180f2d2b7c7SAvi Kivity     unsigned k = 0;
181f2d2b7c7SAvi Kivity 
182f2d2b7c7SAvi Kivity     while (sti_loop_active) {
183f2d2b7c7SAvi Kivity 	sti_nop((char *)(ulong)((k++ * 4096) % (128 * 1024 * 1024)));
184f2d2b7c7SAvi Kivity     }
185f2d2b7c7SAvi Kivity }
186f2d2b7c7SAvi Kivity 
187f2d2b7c7SAvi Kivity static void nmi_handler(isr_regs_t *regs)
188f2d2b7c7SAvi Kivity {
189f2d2b7c7SAvi Kivity     extern void post_sti(void);
190f2d2b7c7SAvi Kivity     ++nmi_counter_private;
191f2d2b7c7SAvi Kivity     nmi_hlt_counter += regs->rip == (ulong)post_sti;
192f2d2b7c7SAvi Kivity }
193f2d2b7c7SAvi Kivity 
194f2d2b7c7SAvi Kivity static void update_cr3(void *cr3)
195f2d2b7c7SAvi Kivity {
196f2d2b7c7SAvi Kivity     write_cr3((ulong)cr3);
197f2d2b7c7SAvi Kivity }
198f2d2b7c7SAvi Kivity 
199f2d2b7c7SAvi Kivity static void test_sti_nmi(void)
200f2d2b7c7SAvi Kivity {
201f2d2b7c7SAvi Kivity     unsigned old_counter;
202f2d2b7c7SAvi Kivity 
203f2d2b7c7SAvi Kivity     if (cpu_count() < 2) {
204f2d2b7c7SAvi Kivity 	return;
205f2d2b7c7SAvi Kivity     }
206f2d2b7c7SAvi Kivity 
207d51bd17eSGleb Natapov     handle_irq(2, nmi_handler);
208f2d2b7c7SAvi Kivity     on_cpu(1, update_cr3, (void *)read_cr3());
209f2d2b7c7SAvi Kivity 
210f2d2b7c7SAvi Kivity     sti_loop_active = 1;
211f2d2b7c7SAvi Kivity     on_cpu_async(1, sti_loop, 0);
212f2d2b7c7SAvi Kivity     while (nmi_counter < 30000) {
213f2d2b7c7SAvi Kivity 	old_counter = nmi_counter;
214f2d2b7c7SAvi Kivity 	apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, 1);
215f2d2b7c7SAvi Kivity 	while (nmi_counter == old_counter) {
216f2d2b7c7SAvi Kivity 	    ;
217f2d2b7c7SAvi Kivity 	}
218f2d2b7c7SAvi Kivity     }
219f2d2b7c7SAvi Kivity     sti_loop_active = 0;
220f2d2b7c7SAvi Kivity     report("nmi-after-sti", nmi_hlt_counter == 0);
221f2d2b7c7SAvi Kivity }
222f2d2b7c7SAvi Kivity 
223173e7eacSAvi Kivity static volatile bool nmi_done, nmi_flushed;
224173e7eacSAvi Kivity static volatile int nmi_received;
225173e7eacSAvi Kivity static volatile int cpu0_nmi_ctr1, cpu1_nmi_ctr1;
226173e7eacSAvi Kivity static volatile int cpu0_nmi_ctr2, cpu1_nmi_ctr2;
227173e7eacSAvi Kivity 
228173e7eacSAvi Kivity static void multiple_nmi_handler(isr_regs_t *regs)
229173e7eacSAvi Kivity {
230173e7eacSAvi Kivity     ++nmi_received;
231173e7eacSAvi Kivity }
232173e7eacSAvi Kivity 
233173e7eacSAvi Kivity static void kick_me_nmi(void *blah)
234173e7eacSAvi Kivity {
235173e7eacSAvi Kivity     while (!nmi_done) {
236173e7eacSAvi Kivity 	++cpu1_nmi_ctr1;
237173e7eacSAvi Kivity 	while (cpu1_nmi_ctr1 != cpu0_nmi_ctr1 && !nmi_done) {
238173e7eacSAvi Kivity 	    pause();
239173e7eacSAvi Kivity 	}
240173e7eacSAvi Kivity 	if (nmi_done) {
241173e7eacSAvi Kivity 	    return;
242173e7eacSAvi Kivity 	}
243173e7eacSAvi Kivity 	apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, 0);
244173e7eacSAvi Kivity 	/* make sure the NMI has arrived by sending an IPI after it */
245173e7eacSAvi Kivity 	apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_FIXED | APIC_INT_ASSERT
246173e7eacSAvi Kivity 		       | 0x44, 0);
247173e7eacSAvi Kivity 	++cpu1_nmi_ctr2;
248173e7eacSAvi Kivity 	while (cpu1_nmi_ctr2 != cpu0_nmi_ctr2 && !nmi_done) {
249173e7eacSAvi Kivity 	    pause();
250173e7eacSAvi Kivity 	}
251173e7eacSAvi Kivity     }
252173e7eacSAvi Kivity }
253173e7eacSAvi Kivity 
254173e7eacSAvi Kivity static void flush_nmi(isr_regs_t *regs)
255173e7eacSAvi Kivity {
256173e7eacSAvi Kivity     nmi_flushed = true;
257173e7eacSAvi Kivity     apic_write(APIC_EOI, 0);
258173e7eacSAvi Kivity }
259173e7eacSAvi Kivity 
260173e7eacSAvi Kivity static void test_multiple_nmi(void)
261173e7eacSAvi Kivity {
262173e7eacSAvi Kivity     int i;
263173e7eacSAvi Kivity     bool ok = true;
264173e7eacSAvi Kivity 
265173e7eacSAvi Kivity     if (cpu_count() < 2) {
266173e7eacSAvi Kivity 	return;
267173e7eacSAvi Kivity     }
268173e7eacSAvi Kivity 
269173e7eacSAvi Kivity     sti();
270173e7eacSAvi Kivity     handle_irq(2, multiple_nmi_handler);
271173e7eacSAvi Kivity     handle_irq(0x44, flush_nmi);
272173e7eacSAvi Kivity     on_cpu_async(1, kick_me_nmi, 0);
273173e7eacSAvi Kivity     for (i = 0; i < 1000000; ++i) {
274173e7eacSAvi Kivity 	nmi_flushed = false;
275173e7eacSAvi Kivity 	nmi_received = 0;
276173e7eacSAvi Kivity 	++cpu0_nmi_ctr1;
277173e7eacSAvi Kivity 	while (cpu1_nmi_ctr1 != cpu0_nmi_ctr1) {
278173e7eacSAvi Kivity 	    pause();
279173e7eacSAvi Kivity 	}
280173e7eacSAvi Kivity 	apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, 0);
281173e7eacSAvi Kivity 	while (!nmi_flushed) {
282173e7eacSAvi Kivity 	    pause();
283173e7eacSAvi Kivity 	}
284173e7eacSAvi Kivity 	if (nmi_received != 2) {
285173e7eacSAvi Kivity 	    ok = false;
286173e7eacSAvi Kivity 	    break;
287173e7eacSAvi Kivity 	}
288173e7eacSAvi Kivity 	++cpu0_nmi_ctr2;
289173e7eacSAvi Kivity 	while (cpu1_nmi_ctr2 != cpu0_nmi_ctr2) {
290173e7eacSAvi Kivity 	    pause();
291173e7eacSAvi Kivity 	}
292173e7eacSAvi Kivity     }
293173e7eacSAvi Kivity     nmi_done = true;
294173e7eacSAvi Kivity     report("multiple nmi", ok);
295173e7eacSAvi Kivity }
296173e7eacSAvi Kivity 
2977d36db35SAvi Kivity int main()
2987d36db35SAvi Kivity {
2997d36db35SAvi Kivity     setup_vm();
300f2d2b7c7SAvi Kivity     smp_init();
301d51bd17eSGleb Natapov     setup_idt();
3027d36db35SAvi Kivity 
3037d36db35SAvi Kivity     test_lapic_existence();
3047d36db35SAvi Kivity 
3057d36db35SAvi Kivity     mask_pic_interrupts();
3067d36db35SAvi Kivity     test_enable_x2apic();
3079b6bdb3fSJan Kiszka     test_apicbase();
3087d36db35SAvi Kivity 
3097d36db35SAvi Kivity     test_self_ipi();
3107d36db35SAvi Kivity 
311f2d2b7c7SAvi Kivity     test_sti_nmi();
312173e7eacSAvi Kivity     test_multiple_nmi();
3137d36db35SAvi Kivity 
314d423ca36SLiu, Jinsong     test_tsc_deadline_timer();
315d423ca36SLiu, Jinsong 
316f3cdd159SJan Kiszka     return report_summary();
3177d36db35SAvi Kivity }
318