kvm: testsuite: test simultaneous irq injection
[qemu-kvm/fedora.git] / kvm / user / test / x86 / apic.c
blob9c6205b4d56e114cc10e4cffa6095672dea1562f
1 #include "libcflat.h"
2 #include "apic.h"
3 #include "vm.h"
5 static void *g_apic;
6 static void *g_ioapic;
8 typedef unsigned char u8;
9 typedef unsigned short u16;
10 typedef unsigned u32;
11 typedef unsigned long ulong;
13 typedef struct {
14 unsigned short offset0;
15 unsigned short selector;
16 unsigned short ist : 3;
17 unsigned short : 5;
18 unsigned short type : 4;
19 unsigned short : 1;
20 unsigned short dpl : 2;
21 unsigned short p : 1;
22 unsigned short offset1;
23 #ifdef __x86_64__
24 unsigned offset2;
25 unsigned reserved;
26 #endif
27 } idt_entry_t;
29 typedef struct {
30 ulong rflags;
31 ulong cs;
32 ulong rip;
33 ulong func;
34 ulong regs[sizeof(ulong)*2];
35 } isr_regs_t;
37 #ifdef __x86_64__
38 # define R "r"
39 #else
40 # define R "e"
41 #endif
43 extern char isr_entry_point[];
45 asm (
46 "isr_entry_point: \n"
47 #ifdef __x86_64__
48 "push %r15 \n\t"
49 "push %r14 \n\t"
50 "push %r13 \n\t"
51 "push %r12 \n\t"
52 "push %r11 \n\t"
53 "push %r10 \n\t"
54 "push %r9 \n\t"
55 "push %r8 \n\t"
56 #endif
57 "push %"R"di \n\t"
58 "push %"R"si \n\t"
59 "push %"R"bp \n\t"
60 "push %"R"sp \n\t"
61 "push %"R"bx \n\t"
62 "push %"R"dx \n\t"
63 "push %"R"cx \n\t"
64 "push %"R"ax \n\t"
65 #ifdef __x86_64__
66 "mov %rsp, %rdi \n\t"
67 "callq *8*16(%rsp) \n\t"
68 #else
69 "push %esp \n\t"
70 "calll *4+4*8(%esp) \n\t"
71 "add $4, %esp \n\t"
72 #endif
73 "pop %"R"ax \n\t"
74 "pop %"R"cx \n\t"
75 "pop %"R"dx \n\t"
76 "pop %"R"bx \n\t"
77 "pop %"R"bp \n\t"
78 "pop %"R"bp \n\t"
79 "pop %"R"si \n\t"
80 "pop %"R"di \n\t"
81 #ifdef __x86_64__
82 "pop %r8 \n\t"
83 "pop %r9 \n\t"
84 "pop %r10 \n\t"
85 "pop %r11 \n\t"
86 "pop %r12 \n\t"
87 "pop %r13 \n\t"
88 "pop %r14 \n\t"
89 "pop %r15 \n\t"
90 #endif
91 #ifdef __x86_64__
92 "add $8, %rsp \n\t"
93 "iretq \n\t"
94 #else
95 "add $4, %esp \n\t"
96 "iretl \n\t"
97 #endif
100 static idt_entry_t idt[256];
102 static int g_fail;
103 static int g_tests;
105 static void report(const char *msg, int pass)
107 ++g_tests;
108 printf("%s: %s\n", msg, (pass ? "PASS" : "FAIL"));
109 if (!pass)
110 ++g_fail;
113 static u32 apic_read(unsigned reg)
115 return *(volatile u32 *)(g_apic + reg);
118 static void apic_write(unsigned reg, u32 val)
120 *(volatile u32 *)(g_apic + reg) = val;
123 static void test_lapic_existence(void)
125 u32 lvr;
127 lvr = apic_read(APIC_LVR);
128 printf("apic version: %x\n", lvr);
129 report("apic existence", (u16)lvr == 0x14);
132 static u16 read_cs(void)
134 u16 v;
136 asm("mov %%cs, %0" : "=rm"(v));
137 return v;
140 static void init_idt(void)
142 struct {
143 u16 limit;
144 ulong idt;
145 } __attribute__((packed)) idt_ptr = {
146 sizeof(idt_entry_t) * 256 - 1,
147 (ulong)&idt,
150 asm volatile("lidt %0" : : "m"(idt_ptr));
153 static void set_idt_entry(unsigned vec, void (*func)(isr_regs_t *regs))
155 u8 *thunk = vmalloc(50);
156 ulong ptr = (ulong)thunk;
157 idt_entry_t ent = {
158 .offset0 = ptr,
159 .selector = read_cs(),
160 .ist = 0,
161 .type = 14,
162 .dpl = 0,
163 .p = 1,
164 .offset1 = ptr >> 16,
165 #ifdef __x86_64__
166 .offset2 = ptr >> 32,
167 #endif
169 #ifdef __x86_64__
170 /* sub $8, %rsp */
171 *thunk++ = 0x48; *thunk++ = 0x83; *thunk++ = 0xec; *thunk++ = 0x08;
172 /* mov $func_low, %(rsp) */
173 *thunk++ = 0xc7; *thunk++ = 0x04; *thunk++ = 0x24;
174 *(u32 *)thunk = (ulong)func; thunk += 4;
175 /* mov $func_high, %(rsp+4) */
176 *thunk++ = 0xc7; *thunk++ = 0x44; *thunk++ = 0x24; *thunk++ = 0x04;
177 *(u32 *)thunk = (ulong)func >> 32; thunk += 4;
178 /* jmp isr_entry_point */
179 *thunk ++ = 0xe9;
180 *(u32 *)thunk = (ulong)isr_entry_point - (ulong)(thunk + 4);
181 #else
182 /* push $func */
183 *thunk++ = 0x68;
184 *(u32 *)thunk = (ulong)func;
185 /* jmp isr_entry_point */
186 *thunk ++ = 0xe9;
187 *(u32 *)thunk = (ulong)isr_entry_point - (ulong)(thunk + 4);
188 #endif
189 idt[vec] = ent;
192 static void irq_disable(void)
194 asm volatile("cli");
197 static void irq_enable(void)
199 asm volatile("sti");
202 static void eoi(void)
204 apic_write(APIC_EOI, 0);
207 static int ipi_count;
209 static void self_ipi_isr(isr_regs_t *regs)
211 ++ipi_count;
212 eoi();
215 static void test_self_ipi(void)
217 int vec = 0xf1;
219 set_idt_entry(vec, self_ipi_isr);
220 irq_enable();
221 apic_write(APIC_ICR,
222 APIC_DEST_SELF | APIC_DEST_PHYSICAL | APIC_DM_FIXED | vec);
223 asm volatile ("nop");
224 report("self ipi", ipi_count == 1);
227 static void ioapic_write_reg(unsigned reg, u32 value)
229 *(volatile u32 *)g_ioapic = reg;
230 *(volatile u32 *)(g_ioapic + 0x10) = value;
233 typedef struct {
234 u8 vector;
235 u8 delivery_mode:3;
236 u8 dest_mode:1;
237 u8 delivery_status:1;
238 u8 polarity:1;
239 u8 remote_irr:1;
240 u8 trig_mode:1;
241 u8 mask:1;
242 u8 reserve:7;
243 u8 reserved[4];
244 u8 dest_id;
245 } ioapic_redir_entry_t;
247 static void ioapic_write_redir(unsigned line, ioapic_redir_entry_t e)
249 ioapic_write_reg(0x10 + line * 2 + 0, ((u32 *)&e)[0]);
250 ioapic_write_reg(0x10 + line * 2 + 1, ((u32 *)&e)[1]);
253 static void set_ioapic_redir(unsigned line, unsigned vec)
255 ioapic_redir_entry_t e = {
256 .vector = vec,
257 .delivery_mode = 0,
258 .trig_mode = 0,
261 ioapic_write_redir(line, e);
264 static void set_irq_line(unsigned line, int val)
266 asm volatile("out %0, %1" : : "a"((u8)val), "d"((u16)(0x2000 + line)));
269 static void toggle_irq_line(unsigned line)
271 set_irq_line(line, 1);
272 set_irq_line(line, 0);
275 static int g_isr_77;
277 static void ioapic_isr_77(isr_regs_t *regs)
279 ++g_isr_77;
280 eoi();
283 static void test_ioapic_intr(void)
285 set_idt_entry(0x77, ioapic_isr_77);
286 set_ioapic_redir(0x10, 0x77);
287 toggle_irq_line(0x10);
288 asm volatile ("nop");
289 report("ioapic interrupt", g_isr_77 == 1);
292 static int g_78, g_66, g_66_after_78;
293 static ulong g_66_rip, g_78_rip;
295 static void ioapic_isr_78(isr_regs_t *regs)
297 ++g_78;
298 g_78_rip = regs->rip;
299 eoi();
302 static void ioapic_isr_66(isr_regs_t *regs)
304 ++g_66;
305 if (g_78)
306 ++g_66_after_78;
307 g_66_rip = regs->rip;
308 eoi();
311 static void test_ioapic_simultaneous(void)
313 set_idt_entry(0x78, ioapic_isr_78);
314 set_idt_entry(0x66, ioapic_isr_66);
315 set_ioapic_redir(0x10, 0x78);
316 set_ioapic_redir(0x11, 0x66);
317 irq_disable();
318 toggle_irq_line(0x11);
319 toggle_irq_line(0x10);
320 irq_enable();
321 asm volatile ("nop");
322 report("ioapic simultaneous interrupt",
323 g_66 && g_78 && g_66_after_78 && g_66_rip == g_78_rip);
326 static void enable_apic(void)
328 apic_write(0xf0, 0x1ff); /* spurious vector register */
331 int main()
333 setup_vm();
335 g_apic = vmap(0xfee00000, 0x1000);
336 g_ioapic = vmap(0xfec00000, 0x1000);
338 test_lapic_existence();
340 enable_apic();
341 init_idt();
343 test_self_ipi();
345 test_ioapic_intr();
346 test_ioapic_simultaneous();
348 printf("\nsummary: %d tests, %d failures\n", g_tests, g_fail);
350 return g_fail != 0;