xref: /kvm-unit-tests/x86/apic.c (revision 1bde9127da4c103603972cdf60b6c6c383866447)
1 #include "libcflat.h"
2 #include "apic.h"
3 #include "vm.h"
4 #include "smp.h"
5 #include "desc.h"
6 #include "isr.h"
7 #include "msr.h"
8 
9 static void test_lapic_existence(void)
10 {
11     u32 lvr;
12 
13     lvr = apic_read(APIC_LVR);
14     printf("apic version: %x\n", lvr);
15     report("apic existence", (u16)lvr == 0x14);
16 }
17 
18 #define TSC_DEADLINE_TIMER_VECTOR 0xef
19 
20 static int tdt_count;
21 
22 static void tsc_deadline_timer_isr(isr_regs_t *regs)
23 {
24     ++tdt_count;
25     eoi();
26 }
27 
28 static void __test_tsc_deadline_timer(void)
29 {
30     handle_irq(TSC_DEADLINE_TIMER_VECTOR, tsc_deadline_timer_isr);
31     irq_enable();
32 
33     wrmsr(MSR_IA32_TSCDEADLINE, rdmsr(MSR_IA32_TSC));
34     asm volatile ("nop");
35     report("tsc deadline timer", tdt_count == 1);
36     report("tsc deadline timer clearing", rdmsr(MSR_IA32_TSCDEADLINE) == 0);
37 }
38 
39 static int enable_tsc_deadline_timer(void)
40 {
41     uint32_t lvtt;
42 
43     if (cpuid(1).c & (1 << 24)) {
44         lvtt = APIC_LVT_TIMER_TSCDEADLINE | TSC_DEADLINE_TIMER_VECTOR;
45         apic_write(APIC_LVTT, lvtt);
46         return 1;
47     } else {
48         return 0;
49     }
50 }
51 
52 static void test_tsc_deadline_timer(void)
53 {
54     if(enable_tsc_deadline_timer()) {
55         __test_tsc_deadline_timer();
56     } else {
57         report_skip("tsc deadline timer not detected");
58     }
59 }
60 
61 static void do_write_apicbase(void *data)
62 {
63     wrmsr(MSR_IA32_APICBASE, *(u64 *)data);
64 }
65 
66 void test_enable_x2apic(void)
67 {
68     u64 invalid_state = APIC_DEFAULT_PHYS_BASE | APIC_BSP | APIC_EXTD;
69     u64 apic_enabled = APIC_DEFAULT_PHYS_BASE | APIC_BSP | APIC_EN;
70     u64 x2apic_enabled =
71         APIC_DEFAULT_PHYS_BASE | APIC_BSP | APIC_EN | APIC_EXTD;
72 
73     if (enable_x2apic()) {
74         printf("x2apic enabled\n");
75 
76         report("x2apic enabled to invalid state",
77                test_for_exception(GP_VECTOR, do_write_apicbase,
78                                   &invalid_state));
79         report("x2apic enabled to apic enabled",
80                test_for_exception(GP_VECTOR, do_write_apicbase,
81                                   &apic_enabled));
82 
83         wrmsr(MSR_IA32_APICBASE, APIC_DEFAULT_PHYS_BASE | APIC_BSP);
84         report("disabled to invalid state",
85                test_for_exception(GP_VECTOR, do_write_apicbase,
86                                   &invalid_state));
87         report("disabled to x2apic enabled",
88                test_for_exception(GP_VECTOR, do_write_apicbase,
89                                   &x2apic_enabled));
90 
91         wrmsr(MSR_IA32_APICBASE, apic_enabled);
92         report("apic enabled to invalid state",
93                test_for_exception(GP_VECTOR, do_write_apicbase,
94                                   &invalid_state));
95 
96         wrmsr(MSR_IA32_APICBASE, x2apic_enabled);
97         apic_write(APIC_SPIV, 0x1ff);
98     } else {
99         printf("x2apic not detected\n");
100 
101         report("enable unsupported x2apic",
102                test_for_exception(GP_VECTOR, do_write_apicbase,
103                                   &x2apic_enabled));
104     }
105 }
106 
107 static void test_apic_disable(void)
108 {
109     u64 orig_apicbase = rdmsr(MSR_IA32_APICBASE);
110 
111     report_prefix_push("apic_disable");
112 
113     report("Local apic enabled", orig_apicbase & APIC_EN);
114     report("CPUID.1H:EDX.APIC[bit 9] is set", cpuid(1).d & (1 << 9));
115 
116     wrmsr(MSR_IA32_APICBASE, orig_apicbase & ~(APIC_EN | APIC_EXTD));
117     report("Local apic disabled", !(rdmsr(MSR_IA32_APICBASE) & APIC_EN));
118     report("CPUID.1H:EDX.APIC[bit 9] is clear", !(cpuid(1).d & (1 << 9)));
119 
120     wrmsr(MSR_IA32_APICBASE, orig_apicbase & ~APIC_EXTD);
121     wrmsr(MSR_IA32_APICBASE, orig_apicbase);
122     apic_write(APIC_SPIV, 0x1ff);
123     report("Local apic enabled", rdmsr(MSR_IA32_APICBASE) & APIC_EN);
124     report("CPUID.1H:EDX.APIC[bit 9] is set", cpuid(1).d & (1 << 9));
125 
126     report_prefix_pop();
127 }
128 
129 #define ALTERNATE_APIC_BASE	0x42000000
130 
131 static void test_apicbase(void)
132 {
133     u64 orig_apicbase = rdmsr(MSR_IA32_APICBASE);
134     u32 lvr = apic_read(APIC_LVR);
135     u64 value;
136 
137     wrmsr(MSR_IA32_APICBASE, orig_apicbase & ~(APIC_EN | APIC_EXTD));
138     wrmsr(MSR_IA32_APICBASE, ALTERNATE_APIC_BASE | APIC_BSP | APIC_EN);
139 
140     report_prefix_push("apicbase");
141 
142     report("relocate apic",
143            *(volatile u32 *)(ALTERNATE_APIC_BASE + APIC_LVR) == lvr);
144 
145     value = orig_apicbase | (1UL << cpuid_maxphyaddr());
146     report("reserved physaddr bits",
147            test_for_exception(GP_VECTOR, do_write_apicbase, &value));
148 
149     value = orig_apicbase | 1;
150     report("reserved low bits",
151            test_for_exception(GP_VECTOR, do_write_apicbase, &value));
152 
153     wrmsr(MSR_IA32_APICBASE, orig_apicbase);
154     apic_write(APIC_SPIV, 0x1ff);
155 
156     report_prefix_pop();
157 }
158 
159 static void do_write_apic_id(void *id)
160 {
161     apic_write(APIC_ID, *(u32 *)id);
162 }
163 
164 static void __test_apic_id(void * unused)
165 {
166     u32 id, newid;
167     u8  initial_xapic_id = cpuid(1).b >> 24;
168     u32 initial_x2apic_id = cpuid(0xb).d;
169     bool x2apic_mode = rdmsr(MSR_IA32_APICBASE) & APIC_EXTD;
170 
171     if (x2apic_mode)
172         reset_apic();
173 
174     id = apic_id();
175     report("xapic id matches cpuid", initial_xapic_id == id);
176 
177     newid = (id + 1) << 24;
178     report("writeable xapic id",
179             !test_for_exception(GP_VECTOR, do_write_apic_id, &newid) &&
180             id + 1 == apic_id());
181 
182     if (!enable_x2apic())
183         goto out;
184 
185     report("non-writeable x2apic id",
186             test_for_exception(GP_VECTOR, do_write_apic_id, &newid));
187     report("sane x2apic id", initial_xapic_id == (apic_id() & 0xff));
188 
189     /* old QEMUs do not set initial x2APIC ID */
190     report("x2apic id matches cpuid",
191            initial_xapic_id == (initial_x2apic_id & 0xff) &&
192            initial_x2apic_id == apic_id());
193 
194 out:
195     reset_apic();
196 
197     report("correct xapic id after reset", initial_xapic_id == apic_id());
198 
199     /* old KVMs do not reset xAPIC ID */
200     if (id != apic_id())
201         apic_write(APIC_ID, id << 24);
202 
203     if (x2apic_mode)
204         enable_x2apic();
205 }
206 
207 static void test_apic_id(void)
208 {
209     if (cpu_count() < 2)
210         return;
211 
212     on_cpu(1, __test_apic_id, NULL);
213 }
214 
215 static int ipi_count;
216 
217 static void self_ipi_isr(isr_regs_t *regs)
218 {
219     ++ipi_count;
220     eoi();
221 }
222 
223 static void test_self_ipi(void)
224 {
225     int vec = 0xf1;
226 
227     handle_irq(vec, self_ipi_isr);
228     irq_enable();
229     apic_icr_write(APIC_DEST_SELF | APIC_DEST_PHYSICAL | APIC_DM_FIXED | vec,
230                    0);
231     asm volatile ("nop");
232     report("self ipi", ipi_count == 1);
233 }
234 
235 volatile int nmi_counter_private, nmi_counter, nmi_hlt_counter, sti_loop_active;
236 
237 void sti_nop(char *p)
238 {
239     asm volatile (
240 		  ".globl post_sti \n\t"
241 		  "sti \n"
242 		  /*
243 		   * vmx won't exit on external interrupt if blocked-by-sti,
244 		   * so give it a reason to exit by accessing an unmapped page.
245 		   */
246 		  "post_sti: testb $0, %0 \n\t"
247 		  "nop \n\t"
248 		  "cli"
249 		  : : "m"(*p)
250 		  );
251     nmi_counter = nmi_counter_private;
252 }
253 
254 static void sti_loop(void *ignore)
255 {
256     unsigned k = 0;
257 
258     while (sti_loop_active) {
259 	sti_nop((char *)(ulong)((k++ * 4096) % (128 * 1024 * 1024)));
260     }
261 }
262 
263 static void nmi_handler(isr_regs_t *regs)
264 {
265     extern void post_sti(void);
266     ++nmi_counter_private;
267     nmi_hlt_counter += regs->rip == (ulong)post_sti;
268 }
269 
270 static void update_cr3(void *cr3)
271 {
272     write_cr3((ulong)cr3);
273 }
274 
275 static void test_sti_nmi(void)
276 {
277     unsigned old_counter;
278 
279     if (cpu_count() < 2) {
280 	return;
281     }
282 
283     handle_irq(2, nmi_handler);
284     on_cpu(1, update_cr3, (void *)read_cr3());
285 
286     sti_loop_active = 1;
287     on_cpu_async(1, sti_loop, 0);
288     while (nmi_counter < 30000) {
289 	old_counter = nmi_counter;
290 	apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, 1);
291 	while (nmi_counter == old_counter) {
292 	    ;
293 	}
294     }
295     sti_loop_active = 0;
296     report("nmi-after-sti", nmi_hlt_counter == 0);
297 }
298 
299 static volatile bool nmi_done, nmi_flushed;
300 static volatile int nmi_received;
301 static volatile int cpu0_nmi_ctr1, cpu1_nmi_ctr1;
302 static volatile int cpu0_nmi_ctr2, cpu1_nmi_ctr2;
303 
304 static void multiple_nmi_handler(isr_regs_t *regs)
305 {
306     ++nmi_received;
307 }
308 
309 static void kick_me_nmi(void *blah)
310 {
311     while (!nmi_done) {
312 	++cpu1_nmi_ctr1;
313 	while (cpu1_nmi_ctr1 != cpu0_nmi_ctr1 && !nmi_done) {
314 	    pause();
315 	}
316 	if (nmi_done) {
317 	    return;
318 	}
319 	apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, 0);
320 	/* make sure the NMI has arrived by sending an IPI after it */
321 	apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_FIXED | APIC_INT_ASSERT
322 		       | 0x44, 0);
323 	++cpu1_nmi_ctr2;
324 	while (cpu1_nmi_ctr2 != cpu0_nmi_ctr2 && !nmi_done) {
325 	    pause();
326 	}
327     }
328 }
329 
330 static void flush_nmi(isr_regs_t *regs)
331 {
332     nmi_flushed = true;
333     apic_write(APIC_EOI, 0);
334 }
335 
336 static void test_multiple_nmi(void)
337 {
338     int i;
339     bool ok = true;
340 
341     if (cpu_count() < 2) {
342 	return;
343     }
344 
345     sti();
346     handle_irq(2, multiple_nmi_handler);
347     handle_irq(0x44, flush_nmi);
348     on_cpu_async(1, kick_me_nmi, 0);
349     for (i = 0; i < 1000000; ++i) {
350 	nmi_flushed = false;
351 	nmi_received = 0;
352 	++cpu0_nmi_ctr1;
353 	while (cpu1_nmi_ctr1 != cpu0_nmi_ctr1) {
354 	    pause();
355 	}
356 	apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, 0);
357 	while (!nmi_flushed) {
358 	    pause();
359 	}
360 	if (nmi_received != 2) {
361 	    ok = false;
362 	    break;
363 	}
364 	++cpu0_nmi_ctr2;
365 	while (cpu1_nmi_ctr2 != cpu0_nmi_ctr2) {
366 	    pause();
367 	}
368     }
369     nmi_done = true;
370     report("multiple nmi", ok);
371 }
372 
373 static volatile int lvtt_counter = 0;
374 
375 static void lvtt_handler(isr_regs_t *regs)
376 {
377     lvtt_counter++;
378     eoi();
379 }
380 
381 static void test_apic_timer_one_shot(void)
382 {
383     uint64_t tsc1, tsc2;
384     static const uint32_t interval = 0x10000;
385 
386 #define APIC_LVT_TIMER_VECTOR    (0xee)
387 
388     handle_irq(APIC_LVT_TIMER_VECTOR, lvtt_handler);
389     irq_enable();
390 
391     /* One shot mode */
392     apic_write(APIC_LVTT, APIC_LVT_TIMER_ONESHOT |
393                APIC_LVT_TIMER_VECTOR);
394     /* Divider == 1 */
395     apic_write(APIC_TDCR, 0x0000000b);
396 
397     tsc1 = rdtsc();
398     /* Set "Initial Counter Register", which starts the timer */
399     apic_write(APIC_TMICT, interval);
400     while (!lvtt_counter);
401     tsc2 = rdtsc();
402 
403     /*
404      * For LVT Timer clock, SDM vol 3 10.5.4 says it should be
405      * derived from processor's bus clock (IIUC which is the same
406      * as TSC), however QEMU seems to be using nanosecond. In all
407      * cases, the following should satisfy on all modern
408      * processors.
409      */
410     report("APIC LVT timer one shot", (lvtt_counter == 1) &&
411            (tsc2 - tsc1 >= interval));
412 }
413 
414 int main()
415 {
416     setup_vm();
417     smp_init();
418 
419     test_lapic_existence();
420 
421     mask_pic_interrupts();
422     test_apic_id();
423     test_apic_disable();
424     test_enable_x2apic();
425     test_apicbase();
426 
427     test_self_ipi();
428 
429     test_sti_nmi();
430     test_multiple_nmi();
431 
432     test_apic_timer_one_shot();
433     test_tsc_deadline_timer();
434 
435     return report_summary();
436 }
437