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