xref: /kvm-unit-tests/x86/apic.c (revision f3bd1e054a97c65c4be2905b3c984d250437ba29)
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     wrmsr(MSR_IA32_APICBASE, *(u64 *)data);
67 }
68 
69 void test_enable_x2apic(void)
70 {
71     u64 invalid_state = APIC_DEFAULT_PHYS_BASE | APIC_BSP | APIC_EXTD;
72     u64 apic_enabled = APIC_DEFAULT_PHYS_BASE | APIC_BSP | APIC_EN;
73     u64 x2apic_enabled =
74         APIC_DEFAULT_PHYS_BASE | APIC_BSP | APIC_EN | APIC_EXTD;
75 
76     if (enable_x2apic()) {
77         printf("x2apic enabled\n");
78 
79         report("x2apic enabled to invalid state",
80                test_for_exception(GP_VECTOR, do_write_apicbase,
81                                   &invalid_state));
82         report("x2apic enabled to apic enabled",
83                test_for_exception(GP_VECTOR, do_write_apicbase,
84                                   &apic_enabled));
85 
86         wrmsr(MSR_IA32_APICBASE, APIC_DEFAULT_PHYS_BASE | APIC_BSP);
87         report("disabled to invalid state",
88                test_for_exception(GP_VECTOR, do_write_apicbase,
89                                   &invalid_state));
90         report("disabled to x2apic enabled",
91                test_for_exception(GP_VECTOR, do_write_apicbase,
92                                   &x2apic_enabled));
93 
94         wrmsr(MSR_IA32_APICBASE, apic_enabled);
95         report("apic enabled to invalid state",
96                test_for_exception(GP_VECTOR, do_write_apicbase,
97                                   &invalid_state));
98 
99         wrmsr(MSR_IA32_APICBASE, x2apic_enabled);
100         apic_write(APIC_SPIV, 0x1ff);
101     } else {
102         printf("x2apic not detected\n");
103 
104         report("enable unsupported x2apic",
105                test_for_exception(GP_VECTOR, do_write_apicbase,
106                                   &x2apic_enabled));
107     }
108 }
109 
110 #define ALTERNATE_APIC_BASE	0x42000000
111 
112 static void test_apicbase(void)
113 {
114     u64 orig_apicbase = rdmsr(MSR_IA32_APICBASE);
115     u32 lvr = apic_read(APIC_LVR);
116     u64 value;
117 
118     wrmsr(MSR_IA32_APICBASE, orig_apicbase & ~(APIC_EN | APIC_EXTD));
119     wrmsr(MSR_IA32_APICBASE, ALTERNATE_APIC_BASE | APIC_BSP | APIC_EN);
120 
121     report_prefix_push("apicbase");
122 
123     report("relocate apic",
124            *(volatile u32 *)(ALTERNATE_APIC_BASE + APIC_LVR) == lvr);
125 
126     value = orig_apicbase | (1UL << cpuid_maxphyaddr());
127     report("reserved physaddr bits",
128            test_for_exception(GP_VECTOR, do_write_apicbase, &value));
129 
130     value = orig_apicbase | 1;
131     report("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     report_prefix_pop();
138 }
139 
140 static int ipi_count;
141 
142 static void self_ipi_isr(isr_regs_t *regs)
143 {
144     ++ipi_count;
145     eoi();
146 }
147 
148 static void test_self_ipi(void)
149 {
150     int vec = 0xf1;
151 
152     handle_irq(vec, self_ipi_isr);
153     irq_enable();
154     apic_icr_write(APIC_DEST_SELF | APIC_DEST_PHYSICAL | APIC_DM_FIXED | vec,
155                    0);
156     asm volatile ("nop");
157     report("self ipi", ipi_count == 1);
158 }
159 
160 volatile int nmi_counter_private, nmi_counter, nmi_hlt_counter, sti_loop_active;
161 
162 void sti_nop(char *p)
163 {
164     asm volatile (
165 		  ".globl post_sti \n\t"
166 		  "sti \n"
167 		  /*
168 		   * vmx won't exit on external interrupt if blocked-by-sti,
169 		   * so give it a reason to exit by accessing an unmapped page.
170 		   */
171 		  "post_sti: testb $0, %0 \n\t"
172 		  "nop \n\t"
173 		  "cli"
174 		  : : "m"(*p)
175 		  );
176     nmi_counter = nmi_counter_private;
177 }
178 
179 static void sti_loop(void *ignore)
180 {
181     unsigned k = 0;
182 
183     while (sti_loop_active) {
184 	sti_nop((char *)(ulong)((k++ * 4096) % (128 * 1024 * 1024)));
185     }
186 }
187 
188 static void nmi_handler(isr_regs_t *regs)
189 {
190     extern void post_sti(void);
191     ++nmi_counter_private;
192     nmi_hlt_counter += regs->rip == (ulong)post_sti;
193 }
194 
195 static void update_cr3(void *cr3)
196 {
197     write_cr3((ulong)cr3);
198 }
199 
200 static void test_sti_nmi(void)
201 {
202     unsigned old_counter;
203 
204     if (cpu_count() < 2) {
205 	return;
206     }
207 
208     handle_irq(2, nmi_handler);
209     on_cpu(1, update_cr3, (void *)read_cr3());
210 
211     sti_loop_active = 1;
212     on_cpu_async(1, sti_loop, 0);
213     while (nmi_counter < 30000) {
214 	old_counter = nmi_counter;
215 	apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, 1);
216 	while (nmi_counter == old_counter) {
217 	    ;
218 	}
219     }
220     sti_loop_active = 0;
221     report("nmi-after-sti", nmi_hlt_counter == 0);
222 }
223 
224 static volatile bool nmi_done, nmi_flushed;
225 static volatile int nmi_received;
226 static volatile int cpu0_nmi_ctr1, cpu1_nmi_ctr1;
227 static volatile int cpu0_nmi_ctr2, cpu1_nmi_ctr2;
228 
229 static void multiple_nmi_handler(isr_regs_t *regs)
230 {
231     ++nmi_received;
232 }
233 
234 static void kick_me_nmi(void *blah)
235 {
236     while (!nmi_done) {
237 	++cpu1_nmi_ctr1;
238 	while (cpu1_nmi_ctr1 != cpu0_nmi_ctr1 && !nmi_done) {
239 	    pause();
240 	}
241 	if (nmi_done) {
242 	    return;
243 	}
244 	apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, 0);
245 	/* make sure the NMI has arrived by sending an IPI after it */
246 	apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_FIXED | APIC_INT_ASSERT
247 		       | 0x44, 0);
248 	++cpu1_nmi_ctr2;
249 	while (cpu1_nmi_ctr2 != cpu0_nmi_ctr2 && !nmi_done) {
250 	    pause();
251 	}
252     }
253 }
254 
255 static void flush_nmi(isr_regs_t *regs)
256 {
257     nmi_flushed = true;
258     apic_write(APIC_EOI, 0);
259 }
260 
261 static void test_multiple_nmi(void)
262 {
263     int i;
264     bool ok = true;
265 
266     if (cpu_count() < 2) {
267 	return;
268     }
269 
270     sti();
271     handle_irq(2, multiple_nmi_handler);
272     handle_irq(0x44, flush_nmi);
273     on_cpu_async(1, kick_me_nmi, 0);
274     for (i = 0; i < 1000000; ++i) {
275 	nmi_flushed = false;
276 	nmi_received = 0;
277 	++cpu0_nmi_ctr1;
278 	while (cpu1_nmi_ctr1 != cpu0_nmi_ctr1) {
279 	    pause();
280 	}
281 	apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, 0);
282 	while (!nmi_flushed) {
283 	    pause();
284 	}
285 	if (nmi_received != 2) {
286 	    ok = false;
287 	    break;
288 	}
289 	++cpu0_nmi_ctr2;
290 	while (cpu1_nmi_ctr2 != cpu0_nmi_ctr2) {
291 	    pause();
292 	}
293     }
294     nmi_done = true;
295     report("multiple nmi", ok);
296 }
297 
298 int main()
299 {
300     setup_vm();
301     smp_init();
302     setup_idt();
303 
304     test_lapic_existence();
305 
306     mask_pic_interrupts();
307     test_enable_x2apic();
308     test_apicbase();
309 
310     test_self_ipi();
311 
312     test_sti_nmi();
313     test_multiple_nmi();
314 
315     test_tsc_deadline_timer();
316 
317     return report_summary();
318 }
319