xref: /kvm-unit-tests/x86/apic.c (revision 110f0d9389041e34b3597b505ef4774c237b35f7) !
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 int g_fail;
9 static int g_tests;
10 
11 static void report(const char *msg, int pass)
12 {
13     ++g_tests;
14     printf("%s: %s\n", msg, (pass ? "PASS" : "FAIL"));
15     if (!pass)
16         ++g_fail;
17 }
18 
19 static void test_lapic_existence(void)
20 {
21     u32 lvr;
22 
23     lvr = apic_read(APIC_LVR);
24     printf("apic version: %x\n", lvr);
25     report("apic existence", (u16)lvr == 0x14);
26 }
27 
28 #define MSR_APIC_BASE 0x0000001b
29 
30 void test_enable_x2apic(void)
31 {
32     if (enable_x2apic()) {
33         printf("x2apic enabled\n");
34     } else {
35         printf("x2apic not detected\n");
36     }
37 }
38 
39 static void eoi(void)
40 {
41     apic_write(APIC_EOI, 0);
42 }
43 
44 static int ipi_count;
45 
46 static void self_ipi_isr(isr_regs_t *regs)
47 {
48     ++ipi_count;
49     eoi();
50 }
51 
52 static void test_self_ipi(void)
53 {
54     int vec = 0xf1;
55 
56     handle_irq(vec, self_ipi_isr);
57     irq_enable();
58     apic_icr_write(APIC_DEST_SELF | APIC_DEST_PHYSICAL | APIC_DM_FIXED | vec,
59                    0);
60     asm volatile ("nop");
61     report("self ipi", ipi_count == 1);
62 }
63 
64 static void set_ioapic_redir(unsigned line, unsigned vec)
65 {
66     ioapic_redir_entry_t e = {
67         .vector = vec,
68         .delivery_mode = 0,
69         .trig_mode = 0,
70     };
71 
72     ioapic_write_redir(line, e);
73 }
74 
75 static void set_irq_line(unsigned line, int val)
76 {
77     asm volatile("out %0, %1" : : "a"((u8)val), "d"((u16)(0x2000 + line)));
78 }
79 
80 static void toggle_irq_line(unsigned line)
81 {
82     set_irq_line(line, 1);
83     set_irq_line(line, 0);
84 }
85 
86 static int g_isr_77;
87 
88 static void ioapic_isr_77(isr_regs_t *regs)
89 {
90     ++g_isr_77;
91     eoi();
92 }
93 
94 static void test_ioapic_intr(void)
95 {
96     handle_irq(0x77, ioapic_isr_77);
97     set_ioapic_redir(0x10, 0x77);
98     toggle_irq_line(0x10);
99     asm volatile ("nop");
100     report("ioapic interrupt", g_isr_77 == 1);
101 }
102 
103 static int g_78, g_66, g_66_after_78;
104 static ulong g_66_rip, g_78_rip;
105 
106 static void ioapic_isr_78(isr_regs_t *regs)
107 {
108     ++g_78;
109     g_78_rip = regs->rip;
110     eoi();
111 }
112 
113 static void ioapic_isr_66(isr_regs_t *regs)
114 {
115     ++g_66;
116     if (g_78)
117         ++g_66_after_78;
118     g_66_rip = regs->rip;
119     eoi();
120 }
121 
122 static void test_ioapic_simultaneous(void)
123 {
124     handle_irq(0x78, ioapic_isr_78);
125     handle_irq(0x66, ioapic_isr_66);
126     set_ioapic_redir(0x10, 0x78);
127     set_ioapic_redir(0x11, 0x66);
128     irq_disable();
129     toggle_irq_line(0x11);
130     toggle_irq_line(0x10);
131     irq_enable();
132     asm volatile ("nop");
133     report("ioapic simultaneous interrupt",
134            g_66 && g_78 && g_66_after_78 && g_66_rip == g_78_rip);
135 }
136 
137 volatile int nmi_counter_private, nmi_counter, nmi_hlt_counter, sti_loop_active;
138 
139 void sti_nop(char *p)
140 {
141     asm volatile (
142 		  ".globl post_sti \n\t"
143 		  "sti \n"
144 		  /*
145 		   * vmx won't exit on external interrupt if blocked-by-sti,
146 		   * so give it a reason to exit by accessing an unmapped page.
147 		   */
148 		  "post_sti: testb $0, %0 \n\t"
149 		  "nop \n\t"
150 		  "cli"
151 		  : : "m"(*p)
152 		  );
153     nmi_counter = nmi_counter_private;
154 }
155 
156 static void sti_loop(void *ignore)
157 {
158     unsigned k = 0;
159 
160     while (sti_loop_active) {
161 	sti_nop((char *)(ulong)((k++ * 4096) % (128 * 1024 * 1024)));
162     }
163 }
164 
165 static void nmi_handler(isr_regs_t *regs)
166 {
167     extern void post_sti(void);
168     ++nmi_counter_private;
169     nmi_hlt_counter += regs->rip == (ulong)post_sti;
170 }
171 
172 static void update_cr3(void *cr3)
173 {
174     write_cr3((ulong)cr3);
175 }
176 
177 static void test_sti_nmi(void)
178 {
179     unsigned old_counter;
180 
181     if (cpu_count() < 2) {
182 	return;
183     }
184 
185     handle_irq(2, nmi_handler);
186     on_cpu(1, update_cr3, (void *)read_cr3());
187 
188     sti_loop_active = 1;
189     on_cpu_async(1, sti_loop, 0);
190     while (nmi_counter < 30000) {
191 	old_counter = nmi_counter;
192 	apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, 1);
193 	while (nmi_counter == old_counter) {
194 	    ;
195 	}
196     }
197     sti_loop_active = 0;
198     report("nmi-after-sti", nmi_hlt_counter == 0);
199 }
200 
201 int main()
202 {
203     setup_vm();
204     smp_init();
205     setup_idt();
206 
207     test_lapic_existence();
208 
209     mask_pic_interrupts();
210     enable_apic();
211     test_enable_x2apic();
212 
213     test_self_ipi();
214 
215     test_ioapic_intr();
216     test_ioapic_simultaneous();
217     test_sti_nmi();
218 
219     printf("\nsummary: %d tests, %d failures\n", g_tests, g_fail);
220 
221     return g_fail != 0;
222 }
223