xref: /kvm-unit-tests/x86/ioapic.c (revision 22207960faac8b16674d56bd3710fbe6ac38ecf9)
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 
8 static void toggle_irq_line(unsigned line)
9 {
10 	set_irq_line(line, 1);
11 	set_irq_line(line, 0);
12 }
13 
14 static void ioapic_reg_version(void)
15 {
16 	u8 version_offset;
17 	uint32_t data_read, data_write;
18 
19 	version_offset = 0x01;
20 	data_read = ioapic_read_reg(version_offset);
21 	data_write = data_read ^ 0xffffffff;
22 
23 	ioapic_write_reg(version_offset, data_write);
24 	report("version register read only test",
25 	       data_read == ioapic_read_reg(version_offset));
26 }
27 
28 static void ioapic_reg_id(void)
29 {
30 	u8 id_offset;
31 	uint32_t data_read, data_write, diff;
32 
33 	id_offset = 0x0;
34 	data_read = ioapic_read_reg(id_offset);
35 	data_write = data_read ^ 0xffffffff;
36 
37 	ioapic_write_reg(id_offset, data_write);
38 
39 	diff = data_read ^ ioapic_read_reg(id_offset);
40 	report("id register only bits [24:27] writable",
41 	       diff == 0x0f000000);
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("arbitration register set by id",
55 	       ioapic_read_reg(arb_offset) == write);
56 
57 	ioapic_write_reg(arb_offset, 0x0);
58 	report("arbtration register read only",
59                ioapic_read_reg(arb_offset) == write);
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("edge triggered intr", g_isr_76 == 1);
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("level triggered intr", g_isr_77 == 1);
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("ioapic simultaneous edge interrupts",
128 	       g_66 && g_78 && g_66_after_78 && g_66_rip == g_78_rip);
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 for ioapic edge interrupts (expected %s)",
150 	       tmr_before == expected_tmr_before && !g_tmr_79,
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 for ioapic level interrupts (expected %s)",
164 	       tmr_before == expected_tmr_before && g_tmr_79,
165 	       expected_tmr_before ? "true" : "false");
166 }
167 
168 #define IPI_DELAY 1000000
169 
170 static void delay(int count)
171 {
172 	while(count--) asm("");
173 }
174 
175 static void toggle_irq_line_0x0e(void *data)
176 {
177 	irq_disable();
178 	delay(IPI_DELAY);
179 	toggle_irq_line(0x0e);
180 	irq_enable();
181 }
182 
183 static void test_ioapic_edge_tmr_smp(bool expected_tmr_before)
184 {
185 	int tmr_before;
186 	int i;
187 
188 	g_tmr_79 = -1;
189 	handle_irq(0x79, ioapic_isr_79);
190 	ioapic_set_redir(0x0e, 0x79, TRIGGER_EDGE);
191 	tmr_before = apic_read_bit(APIC_TMR, 0x79);
192 	on_cpu_async(1, toggle_irq_line_0x0e, 0);
193 	i = 0;
194 	while(g_tmr_79 == -1) i++;
195 	printf("%d iterations before interrupt received\n", i);
196 	report("TMR for ioapic edge interrupts (expected %s)",
197 	       tmr_before == expected_tmr_before && !g_tmr_79,
198 	       expected_tmr_before ? "true" : "false");
199 }
200 
201 static void set_irq_line_0x0e(void *data)
202 {
203 	irq_disable();
204 	delay(IPI_DELAY);
205 	set_irq_line(0x0e, 1);
206 	irq_enable();
207 }
208 
209 static void test_ioapic_level_tmr_smp(bool expected_tmr_before)
210 {
211 	int i, tmr_before;
212 
213 	g_tmr_79 = -1;
214 	handle_irq(0x79, ioapic_isr_79);
215 	ioapic_set_redir(0x0e, 0x79, TRIGGER_LEVEL);
216 	tmr_before = apic_read_bit(APIC_TMR, 0x79);
217 	on_cpu_async(1, set_irq_line_0x0e, 0);
218 	i = 0;
219 	while(g_tmr_79 == -1) i++;
220 	printf("%d iterations before interrupt received\n", i);
221 	report("TMR for ioapic level interrupts (expected %s)",
222 	       tmr_before == expected_tmr_before && g_tmr_79,
223 	       expected_tmr_before ? "true" : "false");
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("coalesce simultaneous level interrupts", g_isr_98 == 1);
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("sequential level interrupts", g_isr_99 == 2);
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("retriggered level interrupts without masking", g_isr_9a == 2);
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("masked level interrupt", g_isr_81 == 0);
319 
320 	set_mask(0x0e, false);
321 	set_irq_line(0x0e, 1);
322 
323 	asm volatile ("nop");
324 	report("unmasked level interrupt", g_isr_81 == 1);
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("masked level interrupt", g_isr_82 == 0);
346 
347 	set_mask(0x0e, false);
348 
349 	asm volatile ("nop");
350 	report("unmasked level interrupt", g_isr_82 == 1);
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("retriggered level interrupts with mask", g_isr_83 == 2);
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("Reconfigure self", g_isr_84 == 1 && e.remote_irr == 0);
412 }
413 
414 
415 int main(void)
416 {
417 	setup_vm();
418 	smp_init();
419 
420 	mask_pic_interrupts();
421 
422 	if (enable_x2apic())
423 		printf("x2apic enabled\n");
424 	else
425 		printf("x2apic not detected\n");
426 
427 	irq_enable();
428 
429 	ioapic_reg_version();
430 	ioapic_reg_id();
431 	ioapic_arbitration_id();
432 
433 	test_ioapic_edge_intr();
434 	test_ioapic_level_intr();
435 	test_ioapic_simultaneous();
436 
437 	test_ioapic_level_coalesce();
438 	test_ioapic_level_sequential();
439 	test_ioapic_level_retrigger();
440 
441 	test_ioapic_edge_mask();
442 	test_ioapic_level_mask();
443 	test_ioapic_level_retrigger_mask();
444 
445 	test_ioapic_edge_tmr(false);
446 	test_ioapic_level_tmr(false);
447 	test_ioapic_level_tmr(true);
448 	test_ioapic_edge_tmr(true);
449 
450 	if (cpu_count() > 1) {
451 		test_ioapic_edge_tmr_smp(false);
452 		test_ioapic_level_tmr_smp(false);
453 		test_ioapic_level_tmr_smp(true);
454 		test_ioapic_edge_tmr_smp(true);
455 
456 		test_ioapic_self_reconfigure();
457 	}
458 
459 	return report_summary();
460 }
461