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