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