xref: /kvm-unit-tests/x86/apic.c (revision 39ac3f8494beb048a121f44bf626275b18c66da5)
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_MODE (2 << 17)
19 #define TSC_DEADLINE_TIMER_VECTOR 0xef
20 #define MSR_IA32_TSC            0x00000010
21 #define MSR_IA32_TSCDEADLINE    0x000006e0
22 
23 static int tdt_count;
24 
25 static void tsc_deadline_timer_isr(isr_regs_t *regs)
26 {
27     ++tdt_count;
28 }
29 
30 static void start_tsc_deadline_timer(void)
31 {
32     handle_irq(TSC_DEADLINE_TIMER_VECTOR, tsc_deadline_timer_isr);
33     irq_enable();
34 
35     wrmsr(MSR_IA32_TSCDEADLINE, rdmsr(MSR_IA32_TSC));
36     asm volatile ("nop");
37     report("tsc deadline timer", tdt_count == 1);
38     report("tsc deadline timer clearing", rdmsr(MSR_IA32_TSCDEADLINE) == 0);
39 }
40 
41 static int enable_tsc_deadline_timer(void)
42 {
43     uint32_t lvtt;
44 
45     if (cpuid(1).c & (1 << 24)) {
46         lvtt = TSC_DEADLINE_TIMER_MODE | TSC_DEADLINE_TIMER_VECTOR;
47         apic_write(APIC_LVTT, lvtt);
48         start_tsc_deadline_timer();
49         return 1;
50     } else {
51         return 0;
52     }
53 }
54 
55 static void test_tsc_deadline_timer(void)
56 {
57     if(enable_tsc_deadline_timer()) {
58         printf("tsc deadline timer enabled\n");
59     } else {
60         printf("tsc deadline timer not detected\n");
61     }
62 }
63 
64 static void do_write_apicbase(void *data)
65 {
66     set_exception_return(&&resume);
67     wrmsr(MSR_IA32_APICBASE, *(u64 *)data);
68 resume:
69     barrier();
70 }
71 
72 void test_enable_x2apic(void)
73 {
74     u64 invalid_state = APIC_DEFAULT_PHYS_BASE | APIC_BSP | APIC_EXTD;
75     u64 apic_enabled = APIC_DEFAULT_PHYS_BASE | APIC_BSP | APIC_EN;
76     u64 x2apic_enabled =
77         APIC_DEFAULT_PHYS_BASE | APIC_BSP | APIC_EN | APIC_EXTD;
78 
79     if (enable_x2apic()) {
80         printf("x2apic enabled\n");
81 
82         report("x2apic enabled to invalid state",
83                test_for_exception(GP_VECTOR, do_write_apicbase,
84                                   &invalid_state));
85         report("x2apic enabled to apic enabled",
86                test_for_exception(GP_VECTOR, do_write_apicbase,
87                                   &apic_enabled));
88 
89         wrmsr(MSR_IA32_APICBASE, APIC_DEFAULT_PHYS_BASE | APIC_BSP);
90         report("disabled to invalid state",
91                test_for_exception(GP_VECTOR, do_write_apicbase,
92                                   &invalid_state));
93         report("disabled to x2apic enabled",
94                test_for_exception(GP_VECTOR, do_write_apicbase,
95                                   &x2apic_enabled));
96 
97         wrmsr(MSR_IA32_APICBASE, apic_enabled);
98         report("apic enabled to invalid state",
99                test_for_exception(GP_VECTOR, do_write_apicbase,
100                                   &invalid_state));
101 
102         wrmsr(MSR_IA32_APICBASE, x2apic_enabled);
103         apic_write(APIC_SPIV, 0x1ff);
104     } else {
105         printf("x2apic not detected\n");
106 
107         report("enable unsupported x2apic",
108                test_for_exception(GP_VECTOR, do_write_apicbase,
109                                   &x2apic_enabled));
110     }
111 }
112 
113 #define ALTERNATE_APIC_BASE	0x42000000
114 
115 static void test_apicbase(void)
116 {
117     u64 orig_apicbase = rdmsr(MSR_IA32_APICBASE);
118     u32 lvr = apic_read(APIC_LVR);
119     u64 value;
120 
121     wrmsr(MSR_IA32_APICBASE, orig_apicbase & ~(APIC_EN | APIC_EXTD));
122     wrmsr(MSR_IA32_APICBASE, ALTERNATE_APIC_BASE | APIC_BSP | APIC_EN);
123 
124     report_prefix_push("apicbase");
125 
126     report("relocate apic",
127            *(volatile u32 *)(ALTERNATE_APIC_BASE + APIC_LVR) == lvr);
128 
129     value = orig_apicbase | (1UL << cpuid_maxphyaddr());
130     report("reserved physaddr bits",
131            test_for_exception(GP_VECTOR, do_write_apicbase, &value));
132 
133     value = orig_apicbase | 1;
134     report("reserved low bits",
135            test_for_exception(GP_VECTOR, do_write_apicbase, &value));
136 
137     wrmsr(MSR_IA32_APICBASE, orig_apicbase);
138     apic_write(APIC_SPIV, 0x1ff);
139 
140     report_prefix_pop();
141 }
142 
143 static void eoi(void)
144 {
145     apic_write(APIC_EOI, 0);
146 }
147 
148 static int ipi_count;
149 
150 static void self_ipi_isr(isr_regs_t *regs)
151 {
152     ++ipi_count;
153     eoi();
154 }
155 
156 static void test_self_ipi(void)
157 {
158     int vec = 0xf1;
159 
160     handle_irq(vec, self_ipi_isr);
161     irq_enable();
162     apic_icr_write(APIC_DEST_SELF | APIC_DEST_PHYSICAL | APIC_DM_FIXED | vec,
163                    0);
164     asm volatile ("nop");
165     report("self ipi", ipi_count == 1);
166 }
167 
168 static void set_ioapic_redir(unsigned line, unsigned vec)
169 {
170     ioapic_redir_entry_t e = {
171         .vector = vec,
172         .delivery_mode = 0,
173         .trig_mode = 0,
174     };
175 
176     ioapic_write_redir(line, e);
177 }
178 
179 static void set_irq_line(unsigned line, int val)
180 {
181     asm volatile("out %0, %1" : : "a"((u8)val), "d"((u16)(0x2000 + line)));
182 }
183 
184 static void toggle_irq_line(unsigned line)
185 {
186     set_irq_line(line, 1);
187     set_irq_line(line, 0);
188 }
189 
190 static int g_isr_77;
191 
192 static void ioapic_isr_77(isr_regs_t *regs)
193 {
194     ++g_isr_77;
195     eoi();
196 }
197 
198 static void test_ioapic_intr(void)
199 {
200     handle_irq(0x77, ioapic_isr_77);
201     set_ioapic_redir(0x0e, 0x77);
202     toggle_irq_line(0x0e);
203     asm volatile ("nop");
204     report("ioapic interrupt", g_isr_77 == 1);
205 }
206 
207 static int g_78, g_66, g_66_after_78;
208 static ulong g_66_rip, g_78_rip;
209 
210 static void ioapic_isr_78(isr_regs_t *regs)
211 {
212     ++g_78;
213     g_78_rip = regs->rip;
214     eoi();
215 }
216 
217 static void ioapic_isr_66(isr_regs_t *regs)
218 {
219     ++g_66;
220     if (g_78)
221         ++g_66_after_78;
222     g_66_rip = regs->rip;
223     eoi();
224 }
225 
226 static void test_ioapic_simultaneous(void)
227 {
228     handle_irq(0x78, ioapic_isr_78);
229     handle_irq(0x66, ioapic_isr_66);
230     set_ioapic_redir(0x0e, 0x78);
231     set_ioapic_redir(0x0f, 0x66);
232     irq_disable();
233     toggle_irq_line(0x0f);
234     toggle_irq_line(0x0e);
235     irq_enable();
236     asm volatile ("nop");
237     report("ioapic simultaneous interrupt",
238            g_66 && g_78 && g_66_after_78 && g_66_rip == g_78_rip);
239 }
240 
241 volatile int nmi_counter_private, nmi_counter, nmi_hlt_counter, sti_loop_active;
242 
243 void sti_nop(char *p)
244 {
245     asm volatile (
246 		  ".globl post_sti \n\t"
247 		  "sti \n"
248 		  /*
249 		   * vmx won't exit on external interrupt if blocked-by-sti,
250 		   * so give it a reason to exit by accessing an unmapped page.
251 		   */
252 		  "post_sti: testb $0, %0 \n\t"
253 		  "nop \n\t"
254 		  "cli"
255 		  : : "m"(*p)
256 		  );
257     nmi_counter = nmi_counter_private;
258 }
259 
260 static void sti_loop(void *ignore)
261 {
262     unsigned k = 0;
263 
264     while (sti_loop_active) {
265 	sti_nop((char *)(ulong)((k++ * 4096) % (128 * 1024 * 1024)));
266     }
267 }
268 
269 static void nmi_handler(isr_regs_t *regs)
270 {
271     extern void post_sti(void);
272     ++nmi_counter_private;
273     nmi_hlt_counter += regs->rip == (ulong)post_sti;
274 }
275 
276 static void update_cr3(void *cr3)
277 {
278     write_cr3((ulong)cr3);
279 }
280 
281 static void test_sti_nmi(void)
282 {
283     unsigned old_counter;
284 
285     if (cpu_count() < 2) {
286 	return;
287     }
288 
289     handle_irq(2, nmi_handler);
290     on_cpu(1, update_cr3, (void *)read_cr3());
291 
292     sti_loop_active = 1;
293     on_cpu_async(1, sti_loop, 0);
294     while (nmi_counter < 30000) {
295 	old_counter = nmi_counter;
296 	apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, 1);
297 	while (nmi_counter == old_counter) {
298 	    ;
299 	}
300     }
301     sti_loop_active = 0;
302     report("nmi-after-sti", nmi_hlt_counter == 0);
303 }
304 
305 static volatile bool nmi_done, nmi_flushed;
306 static volatile int nmi_received;
307 static volatile int cpu0_nmi_ctr1, cpu1_nmi_ctr1;
308 static volatile int cpu0_nmi_ctr2, cpu1_nmi_ctr2;
309 
310 static void multiple_nmi_handler(isr_regs_t *regs)
311 {
312     ++nmi_received;
313 }
314 
315 static void kick_me_nmi(void *blah)
316 {
317     while (!nmi_done) {
318 	++cpu1_nmi_ctr1;
319 	while (cpu1_nmi_ctr1 != cpu0_nmi_ctr1 && !nmi_done) {
320 	    pause();
321 	}
322 	if (nmi_done) {
323 	    return;
324 	}
325 	apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, 0);
326 	/* make sure the NMI has arrived by sending an IPI after it */
327 	apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_FIXED | APIC_INT_ASSERT
328 		       | 0x44, 0);
329 	++cpu1_nmi_ctr2;
330 	while (cpu1_nmi_ctr2 != cpu0_nmi_ctr2 && !nmi_done) {
331 	    pause();
332 	}
333     }
334 }
335 
336 static void flush_nmi(isr_regs_t *regs)
337 {
338     nmi_flushed = true;
339     apic_write(APIC_EOI, 0);
340 }
341 
342 static void test_multiple_nmi(void)
343 {
344     int i;
345     bool ok = true;
346 
347     if (cpu_count() < 2) {
348 	return;
349     }
350 
351     sti();
352     handle_irq(2, multiple_nmi_handler);
353     handle_irq(0x44, flush_nmi);
354     on_cpu_async(1, kick_me_nmi, 0);
355     for (i = 0; i < 1000000; ++i) {
356 	nmi_flushed = false;
357 	nmi_received = 0;
358 	++cpu0_nmi_ctr1;
359 	while (cpu1_nmi_ctr1 != cpu0_nmi_ctr1) {
360 	    pause();
361 	}
362 	apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, 0);
363 	while (!nmi_flushed) {
364 	    pause();
365 	}
366 	if (nmi_received != 2) {
367 	    ok = false;
368 	    break;
369 	}
370 	++cpu0_nmi_ctr2;
371 	while (cpu1_nmi_ctr2 != cpu0_nmi_ctr2) {
372 	    pause();
373 	}
374     }
375     nmi_done = true;
376     report("multiple nmi", ok);
377 }
378 
379 int main()
380 {
381     setup_vm();
382     smp_init();
383     setup_idt();
384 
385     test_lapic_existence();
386 
387     mask_pic_interrupts();
388     test_enable_x2apic();
389     test_apicbase();
390 
391     test_self_ipi();
392 
393     test_ioapic_intr();
394     test_ioapic_simultaneous();
395     test_sti_nmi();
396     test_multiple_nmi();
397 
398     test_tsc_deadline_timer();
399 
400     return report_summary();
401 }
402