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