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