xref: /kvm-unit-tests/x86/ioapic.c (revision b4571f8b3824b8e6f6ba6ae5f31d689ffff185f0)
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 "delay.h"
8 
9 static void poll_remote_irr(unsigned line)
10 {
11 	while (ioapic_read_redir(line).remote_irr)
12 		cpu_relax();
13 }
14 
15 static void toggle_irq_line(unsigned line)
16 {
17 	set_irq_line(line, 1);
18 	set_irq_line(line, 0);
19 }
20 
21 static void ioapic_reg_version(void)
22 {
23 	u8 version_offset;
24 	uint32_t data_read, data_write;
25 
26 	version_offset = 0x01;
27 	data_read = ioapic_read_reg(version_offset);
28 	data_write = data_read ^ 0xffffffff;
29 
30 	ioapic_write_reg(version_offset, data_write);
31 	report(data_read == ioapic_read_reg(version_offset),
32 	       "version register read only test");
33 }
34 
35 static void ioapic_reg_id(void)
36 {
37 	u8 id_offset;
38 	uint32_t data_read, data_write, diff;
39 
40 	id_offset = 0x0;
41 	data_read = ioapic_read_reg(id_offset);
42 	data_write = data_read ^ 0xffffffff;
43 
44 	ioapic_write_reg(id_offset, data_write);
45 
46 	diff = data_read ^ ioapic_read_reg(id_offset);
47 	report(diff == 0x0f000000, "id register only bits [24:27] writable");
48 }
49 
50 static void ioapic_arbitration_id(void)
51 {
52 	u8 id_offset, arb_offset;
53 	uint32_t write;
54 
55 	id_offset = 0x0;
56 	arb_offset = 0x2;
57 	write = 0x0f000000;
58 
59 	ioapic_write_reg(id_offset, write);
60 	report(ioapic_read_reg(arb_offset) == write,
61 	       "arbitration register set by id");
62 
63 	ioapic_write_reg(arb_offset, 0x0);
64 	report(ioapic_read_reg(arb_offset) == write,
65 	       "arbtration register read only");
66 }
67 
68 static volatile int g_isr_76;
69 
70 static void ioapic_isr_76(isr_regs_t *regs)
71 {
72 	++g_isr_76;
73 	eoi();
74 }
75 
76 static void test_ioapic_edge_intr(void)
77 {
78 	handle_irq(0x76, ioapic_isr_76);
79 	ioapic_set_redir(0x0e, 0x76, TRIGGER_EDGE);
80 	toggle_irq_line(0x0e);
81 	asm volatile ("nop");
82 	report(g_isr_76 == 1, "edge triggered intr");
83 }
84 
85 static volatile int g_isr_77;
86 
87 static void ioapic_isr_77(isr_regs_t *regs)
88 {
89 	++g_isr_77;
90 	set_irq_line(0x0e, 0);
91 	eoi();
92 }
93 
94 static void test_ioapic_level_intr(void)
95 {
96 	handle_irq(0x77, ioapic_isr_77);
97 	ioapic_set_redir(0x0e, 0x77, TRIGGER_LEVEL);
98 	set_irq_line(0x0e, 1);
99 	asm volatile ("nop");
100 	report(g_isr_77 == 1, "level triggered intr");
101 }
102 
103 static int g_78, g_66, g_66_after_78;
104 static ulong g_66_rip, g_78_rip;
105 
106 static void ioapic_isr_78(isr_regs_t *regs)
107 {
108 	++g_78;
109 	g_78_rip = regs->rip;
110 	eoi();
111 }
112 
113 static void ioapic_isr_66(isr_regs_t *regs)
114 {
115 	++g_66;
116 	if (g_78)
117 		++g_66_after_78;
118 	g_66_rip = regs->rip;
119 	eoi();
120 }
121 
122 static void test_ioapic_simultaneous(void)
123 {
124 	handle_irq(0x78, ioapic_isr_78);
125 	handle_irq(0x66, ioapic_isr_66);
126 	ioapic_set_redir(0x0e, 0x78, TRIGGER_EDGE);
127 	ioapic_set_redir(0x0f, 0x66, TRIGGER_EDGE);
128 	irq_disable();
129 	toggle_irq_line(0x0f);
130 	toggle_irq_line(0x0e);
131 	irq_enable();
132 	asm volatile ("nop");
133 	report(g_66 && g_78 && g_66_after_78 && g_66_rip == g_78_rip,
134 	       "ioapic simultaneous edge interrupts");
135 }
136 
137 static volatile int g_tmr_79 = -1;
138 
139 static void ioapic_isr_79(isr_regs_t *regs)
140 {
141 	g_tmr_79 = apic_read_bit(APIC_TMR, 0x79);
142 	set_irq_line(0x0e, 0);
143 	eoi();
144 }
145 
146 static void test_ioapic_edge_tmr(bool expected_tmr_before)
147 {
148 	int tmr_before;
149 
150 	handle_irq(0x79, ioapic_isr_79);
151 	ioapic_set_redir(0x0e, 0x79, TRIGGER_EDGE);
152 	tmr_before = apic_read_bit(APIC_TMR, 0x79);
153 	toggle_irq_line(0x0e);
154 	asm volatile ("nop");
155 	report(tmr_before == expected_tmr_before && !g_tmr_79,
156 	       "TMR for ioapic edge interrupts (expected %s)",
157 	       expected_tmr_before ? "true" : "false");
158 }
159 
160 static void test_ioapic_level_tmr(bool expected_tmr_before)
161 {
162 	int tmr_before;
163 
164 	handle_irq(0x79, ioapic_isr_79);
165 	ioapic_set_redir(0x0e, 0x79, TRIGGER_LEVEL);
166 	tmr_before = apic_read_bit(APIC_TMR, 0x79);
167 	set_irq_line(0x0e, 1);
168 	asm volatile ("nop");
169 	report(tmr_before == expected_tmr_before && g_tmr_79,
170 	       "TMR for ioapic level interrupts (expected %s)",
171 	       expected_tmr_before ? "true" : "false");
172 }
173 
174 static void toggle_irq_line_0x0e(void *data)
175 {
176 	irq_disable();
177 	delay(IPI_DELAY);
178 	toggle_irq_line(0x0e);
179 	irq_enable();
180 }
181 
182 static void test_ioapic_edge_tmr_smp(bool expected_tmr_before)
183 {
184 	int tmr_before;
185 	int i;
186 
187 	g_tmr_79 = -1;
188 	handle_irq(0x79, ioapic_isr_79);
189 	ioapic_set_redir(0x0e, 0x79, TRIGGER_EDGE);
190 	tmr_before = apic_read_bit(APIC_TMR, 0x79);
191 	on_cpu_async(1, toggle_irq_line_0x0e, 0);
192 	i = 0;
193 	while(g_tmr_79 == -1) i++;
194 	printf("%d iterations before interrupt received\n", i);
195 	report(tmr_before == expected_tmr_before && !g_tmr_79,
196 	       "TMR for ioapic edge interrupts (expected %s)",
197 	       expected_tmr_before ? "true" : "false");
198 }
199 
200 static void set_irq_line_0x0e(void *data)
201 {
202 	irq_disable();
203 	delay(IPI_DELAY);
204 	set_irq_line(0x0e, 1);
205 	irq_enable();
206 }
207 
208 static void test_ioapic_level_tmr_smp(bool expected_tmr_before)
209 {
210 	int i, tmr_before;
211 
212 	g_tmr_79 = -1;
213 	handle_irq(0x79, ioapic_isr_79);
214 	ioapic_set_redir(0x0e, 0x79, TRIGGER_LEVEL);
215 	tmr_before = apic_read_bit(APIC_TMR, 0x79);
216 	on_cpu_async(1, set_irq_line_0x0e, 0);
217 	i = 0;
218 	while(g_tmr_79 == -1) i++;
219 	printf("%d iterations before interrupt received\n", i);
220 	report(tmr_before == expected_tmr_before && g_tmr_79,
221 	       "TMR for ioapic level interrupts (expected %s)",
222 	       expected_tmr_before ? "true" : "false");
223 	poll_remote_irr(0xe);
224 }
225 
226 static int g_isr_98;
227 
228 static void ioapic_isr_98(isr_regs_t *regs)
229 {
230 	++g_isr_98;
231 	if (g_isr_98 == 1) {
232 		set_irq_line(0x0e, 0);
233 		set_irq_line(0x0e, 1);
234 	}
235 	set_irq_line(0x0e, 0);
236 	eoi();
237 }
238 
239 static void test_ioapic_level_coalesce(void)
240 {
241 	handle_irq(0x98, ioapic_isr_98);
242 	ioapic_set_redir(0x0e, 0x98, TRIGGER_LEVEL);
243 	set_irq_line(0x0e, 1);
244 	asm volatile ("nop");
245 	report(g_isr_98 == 1, "coalesce simultaneous level interrupts");
246 }
247 
248 static int g_isr_99;
249 
250 static void ioapic_isr_99(isr_regs_t *regs)
251 {
252 	++g_isr_99;
253 	set_irq_line(0x0e, 0);
254 	eoi();
255 }
256 
257 static void test_ioapic_level_sequential(void)
258 {
259 	handle_irq(0x99, ioapic_isr_99);
260 	ioapic_set_redir(0x0e, 0x99, TRIGGER_LEVEL);
261 	set_irq_line(0x0e, 1);
262 	set_irq_line(0x0e, 1);
263 	asm volatile ("nop");
264 	report(g_isr_99 == 2, "sequential level interrupts");
265 }
266 
267 static volatile int g_isr_9a;
268 
269 static void ioapic_isr_9a(isr_regs_t *regs)
270 {
271 	++g_isr_9a;
272 	if (g_isr_9a == 2)
273 		set_irq_line(0x0e, 0);
274 	eoi();
275 }
276 
277 static void test_ioapic_level_retrigger(void)
278 {
279 	int i;
280 
281 	handle_irq(0x9a, ioapic_isr_9a);
282 	ioapic_set_redir(0x0e, 0x9a, TRIGGER_LEVEL);
283 
284 	asm volatile ("cli");
285 	set_irq_line(0x0e, 1);
286 
287 	for (i = 0; i < 10; i++) {
288 		if (g_isr_9a == 2)
289 			break;
290 
291 		asm volatile ("sti; hlt; cli");
292 	}
293 
294 	asm volatile ("sti");
295 
296 	report(g_isr_9a == 2, "retriggered level interrupts without masking");
297 }
298 
299 static volatile int g_isr_81;
300 
301 static void ioapic_isr_81(isr_regs_t *regs)
302 {
303 	++g_isr_81;
304 	set_irq_line(0x0e, 0);
305 	eoi();
306 }
307 
308 static void test_ioapic_edge_mask(void)
309 {
310 	handle_irq(0x81, ioapic_isr_81);
311 	ioapic_set_redir(0x0e, 0x81, TRIGGER_EDGE);
312 
313 	set_mask(0x0e, true);
314 	set_irq_line(0x0e, 1);
315 	set_irq_line(0x0e, 0);
316 
317 	asm volatile ("nop");
318 	report(g_isr_81 == 0, "masked level interrupt");
319 
320 	set_mask(0x0e, false);
321 	set_irq_line(0x0e, 1);
322 
323 	asm volatile ("nop");
324 	report(g_isr_81 == 1, "unmasked level interrupt");
325 }
326 
327 static volatile int g_isr_82;
328 
329 static void ioapic_isr_82(isr_regs_t *regs)
330 {
331 	++g_isr_82;
332 	set_irq_line(0x0e, 0);
333 	eoi();
334 }
335 
336 static void test_ioapic_level_mask(void)
337 {
338 	handle_irq(0x82, ioapic_isr_82);
339 	ioapic_set_redir(0x0e, 0x82, TRIGGER_LEVEL);
340 
341 	set_mask(0x0e, true);
342 	set_irq_line(0x0e, 1);
343 
344 	asm volatile ("nop");
345 	report(g_isr_82 == 0, "masked level interrupt");
346 
347 	set_mask(0x0e, false);
348 
349 	asm volatile ("nop");
350 	report(g_isr_82 == 1, "unmasked level interrupt");
351 }
352 
353 static volatile int g_isr_83;
354 
355 static void ioapic_isr_83(isr_regs_t *regs)
356 {
357 	++g_isr_83;
358 	set_mask(0x0e, true);
359 	eoi();
360 }
361 
362 static void test_ioapic_level_retrigger_mask(void)
363 {
364 	handle_irq(0x83, ioapic_isr_83);
365 	ioapic_set_redir(0x0e, 0x83, TRIGGER_LEVEL);
366 
367 	set_irq_line(0x0e, 1);
368 	asm volatile ("nop");
369 	set_mask(0x0e, false);
370 	asm volatile ("nop");
371 	report(g_isr_83 == 2, "retriggered level interrupts with mask");
372 
373 	set_irq_line(0x0e, 0);
374 	set_mask(0x0e, false);
375 }
376 
377 static volatile int g_isr_84;
378 
379 static void ioapic_isr_84(isr_regs_t *regs)
380 {
381 	int line = 0xe;
382 	ioapic_redir_entry_t e;
383 
384 	++g_isr_84;
385 	set_irq_line(line, 0);
386 
387 	e = ioapic_read_redir(line);
388 	e.dest_id = 1;
389 
390 	// Update only upper part of the register because we only change the
391 	// destination, which resides in the upper part
392 	ioapic_write_reg(0x10 + line * 2 + 1, ((u32 *)&e)[1]);
393 
394 	eoi();
395 }
396 
397 static void test_ioapic_self_reconfigure(void)
398 {
399 	ioapic_redir_entry_t e = {
400 		.vector = 0x84,
401 		.delivery_mode = 0,
402 		.dest_mode = 0,
403 		.dest_id = 0,
404 		.trig_mode = TRIGGER_LEVEL,
405 	};
406 
407 	handle_irq(0x84, ioapic_isr_84);
408 	ioapic_write_redir(0xe, e);
409 	set_irq_line(0x0e, 1);
410 	e = ioapic_read_redir(0xe);
411 	report(g_isr_84 == 1 && e.remote_irr == 0, "Reconfigure self");
412 	poll_remote_irr(0xe);
413 }
414 
415 static volatile int g_isr_85;
416 
417 static void ioapic_isr_85(isr_regs_t *regs)
418 {
419 	++g_isr_85;
420 	set_irq_line(0x0e, 0);
421 	eoi();
422 }
423 
424 static void test_ioapic_physical_destination_mode(void)
425 {
426 	ioapic_redir_entry_t e = {
427 		.vector = 0x85,
428 		.delivery_mode = 0,
429 		.dest_mode = 0,
430 		.dest_id = 0x1,
431 		.trig_mode = TRIGGER_LEVEL,
432 	};
433 	handle_irq(0x85, ioapic_isr_85);
434 	ioapic_write_redir(0xe, e);
435 	set_irq_line(0x0e, 1);
436 	do {
437 		pause();
438 	} while(g_isr_85 != 1);
439 	report(g_isr_85 == 1, "ioapic physical destination mode");
440 	poll_remote_irr(0xe);
441 }
442 
443 static volatile int g_isr_86;
444 struct spinlock ioapic_lock;
445 
446 static void ioapic_isr_86(isr_regs_t *regs)
447 {
448 	spin_lock(&ioapic_lock);
449 	++g_isr_86;
450 	spin_unlock(&ioapic_lock);
451 	set_irq_line(0x0e, 0);
452 	eoi();
453 }
454 
455 static void test_ioapic_logical_destination_mode(void)
456 {
457 	/* Number of vcpus which are configured/set in dest_id */
458 	int nr_vcpus = 3;
459 	ioapic_redir_entry_t e = {
460 		.vector = 0x86,
461 		.delivery_mode = 0,
462 		.dest_mode = 1,
463 		.dest_id = 0xd,
464 		.trig_mode = TRIGGER_LEVEL,
465 	};
466 	handle_irq(0x86, ioapic_isr_86);
467 	ioapic_write_redir(0xe, e);
468 	set_irq_line(0x0e, 1);
469 	do {
470 		pause();
471 	} while(g_isr_86 < nr_vcpus);
472 	report(g_isr_86 == nr_vcpus, "ioapic logical destination mode");
473 	poll_remote_irr(0xe);
474 }
475 
476 int main(void)
477 {
478 	setup_vm();
479 
480 	on_cpus(update_cr3, (void *)read_cr3());
481 	mask_pic_interrupts();
482 
483 	if (enable_x2apic())
484 		printf("x2apic enabled\n");
485 	else
486 		printf("x2apic not detected\n");
487 
488 	irq_enable();
489 
490 	ioapic_reg_version();
491 	ioapic_reg_id();
492 	ioapic_arbitration_id();
493 
494 	test_ioapic_edge_intr();
495 	test_ioapic_level_intr();
496 	test_ioapic_simultaneous();
497 
498 	test_ioapic_level_coalesce();
499 	test_ioapic_level_sequential();
500 	test_ioapic_level_retrigger();
501 
502 	test_ioapic_edge_mask();
503 	test_ioapic_level_mask();
504 	test_ioapic_level_retrigger_mask();
505 
506 	test_ioapic_edge_tmr(false);
507 	test_ioapic_level_tmr(false);
508 	test_ioapic_level_tmr(true);
509 	test_ioapic_edge_tmr(true);
510 
511 	if (cpu_count() > 1)
512 		test_ioapic_physical_destination_mode();
513 	if (cpu_count() > 3)
514 		test_ioapic_logical_destination_mode();
515 
516 	if (cpu_count() > 1) {
517 		test_ioapic_edge_tmr_smp(false);
518 		test_ioapic_level_tmr_smp(false);
519 		test_ioapic_level_tmr_smp(true);
520 		test_ioapic_edge_tmr_smp(true);
521 
522 		test_ioapic_self_reconfigure();
523 	}
524 
525 	return report_summary();
526 }
527