1 #include "libcflat.h" 2 #include "apic.h" 3 #include "vm.h" 4 #include "smp.h" 5 #include "desc.h" 6 7 typedef struct { 8 ulong regs[sizeof(ulong)*2]; 9 ulong func; 10 ulong rip; 11 ulong cs; 12 ulong rflags; 13 } isr_regs_t; 14 15 #ifdef __x86_64__ 16 # define R "r" 17 #else 18 # define R "e" 19 #endif 20 21 extern char isr_entry_point[]; 22 23 asm ( 24 "isr_entry_point: \n" 25 #ifdef __x86_64__ 26 "push %r15 \n\t" 27 "push %r14 \n\t" 28 "push %r13 \n\t" 29 "push %r12 \n\t" 30 "push %r11 \n\t" 31 "push %r10 \n\t" 32 "push %r9 \n\t" 33 "push %r8 \n\t" 34 #endif 35 "push %"R "di \n\t" 36 "push %"R "si \n\t" 37 "push %"R "bp \n\t" 38 "push %"R "sp \n\t" 39 "push %"R "bx \n\t" 40 "push %"R "dx \n\t" 41 "push %"R "cx \n\t" 42 "push %"R "ax \n\t" 43 #ifdef __x86_64__ 44 "mov %rsp, %rdi \n\t" 45 "callq *8*16(%rsp) \n\t" 46 #else 47 "push %esp \n\t" 48 "calll *4+4*8(%esp) \n\t" 49 "add $4, %esp \n\t" 50 #endif 51 "pop %"R "ax \n\t" 52 "pop %"R "cx \n\t" 53 "pop %"R "dx \n\t" 54 "pop %"R "bx \n\t" 55 "pop %"R "bp \n\t" 56 "pop %"R "bp \n\t" 57 "pop %"R "si \n\t" 58 "pop %"R "di \n\t" 59 #ifdef __x86_64__ 60 "pop %r8 \n\t" 61 "pop %r9 \n\t" 62 "pop %r10 \n\t" 63 "pop %r11 \n\t" 64 "pop %r12 \n\t" 65 "pop %r13 \n\t" 66 "pop %r14 \n\t" 67 "pop %r15 \n\t" 68 #endif 69 #ifdef __x86_64__ 70 "add $8, %rsp \n\t" 71 "iretq \n\t" 72 #else 73 "add $4, %esp \n\t" 74 "iretl \n\t" 75 #endif 76 ); 77 78 static int g_fail; 79 static int g_tests; 80 81 static void report(const char *msg, int pass) 82 { 83 ++g_tests; 84 printf("%s: %s\n", msg, (pass ? "PASS" : "FAIL")); 85 if (!pass) 86 ++g_fail; 87 } 88 89 static void test_lapic_existence(void) 90 { 91 u32 lvr; 92 93 lvr = apic_read(APIC_LVR); 94 printf("apic version: %x\n", lvr); 95 report("apic existence", (u16)lvr == 0x14); 96 } 97 98 #define MSR_APIC_BASE 0x0000001b 99 100 void test_enable_x2apic(void) 101 { 102 if (enable_x2apic()) { 103 printf("x2apic enabled\n"); 104 } else { 105 printf("x2apic not detected\n"); 106 } 107 } 108 109 static void handle_irq(unsigned vec, void (*func)(isr_regs_t *regs)) 110 { 111 u8 *thunk = vmalloc(50); 112 113 set_idt_entry(vec, thunk, 0); 114 115 #ifdef __x86_64__ 116 /* sub $8, %rsp */ 117 *thunk++ = 0x48; *thunk++ = 0x83; *thunk++ = 0xec; *thunk++ = 0x08; 118 /* mov $func_low, %(rsp) */ 119 *thunk++ = 0xc7; *thunk++ = 0x04; *thunk++ = 0x24; 120 *(u32 *)thunk = (ulong)func; thunk += 4; 121 /* mov $func_high, %(rsp+4) */ 122 *thunk++ = 0xc7; *thunk++ = 0x44; *thunk++ = 0x24; *thunk++ = 0x04; 123 *(u32 *)thunk = (ulong)func >> 32; thunk += 4; 124 /* jmp isr_entry_point */ 125 *thunk ++ = 0xe9; 126 *(u32 *)thunk = (ulong)isr_entry_point - (ulong)(thunk + 4); 127 #else 128 /* push $func */ 129 *thunk++ = 0x68; 130 *(u32 *)thunk = (ulong)func; 131 /* jmp isr_entry_point */ 132 *thunk ++ = 0xe9; 133 *(u32 *)thunk = (ulong)isr_entry_point - (ulong)(thunk + 4); 134 #endif 135 } 136 137 static void irq_disable(void) 138 { 139 asm volatile("cli"); 140 } 141 142 static void irq_enable(void) 143 { 144 asm volatile("sti"); 145 } 146 147 static void eoi(void) 148 { 149 apic_write(APIC_EOI, 0); 150 } 151 152 static int ipi_count; 153 154 static void self_ipi_isr(isr_regs_t *regs) 155 { 156 ++ipi_count; 157 eoi(); 158 } 159 160 static void test_self_ipi(void) 161 { 162 int vec = 0xf1; 163 164 handle_irq(vec, self_ipi_isr); 165 irq_enable(); 166 apic_icr_write(APIC_DEST_SELF | APIC_DEST_PHYSICAL | APIC_DM_FIXED | vec, 167 0); 168 asm volatile ("nop"); 169 report("self ipi", ipi_count == 1); 170 } 171 172 static void set_ioapic_redir(unsigned line, unsigned vec) 173 { 174 ioapic_redir_entry_t e = { 175 .vector = vec, 176 .delivery_mode = 0, 177 .trig_mode = 0, 178 }; 179 180 ioapic_write_redir(line, e); 181 } 182 183 static void set_irq_line(unsigned line, int val) 184 { 185 asm volatile("out %0, %1" : : "a"((u8)val), "d"((u16)(0x2000 + line))); 186 } 187 188 static void toggle_irq_line(unsigned line) 189 { 190 set_irq_line(line, 1); 191 set_irq_line(line, 0); 192 } 193 194 static int g_isr_77; 195 196 static void ioapic_isr_77(isr_regs_t *regs) 197 { 198 ++g_isr_77; 199 eoi(); 200 } 201 202 static void test_ioapic_intr(void) 203 { 204 handle_irq(0x77, ioapic_isr_77); 205 set_ioapic_redir(0x10, 0x77); 206 toggle_irq_line(0x10); 207 asm volatile ("nop"); 208 report("ioapic interrupt", g_isr_77 == 1); 209 } 210 211 static int g_78, g_66, g_66_after_78; 212 static ulong g_66_rip, g_78_rip; 213 214 static void ioapic_isr_78(isr_regs_t *regs) 215 { 216 ++g_78; 217 g_78_rip = regs->rip; 218 eoi(); 219 } 220 221 static void ioapic_isr_66(isr_regs_t *regs) 222 { 223 ++g_66; 224 if (g_78) 225 ++g_66_after_78; 226 g_66_rip = regs->rip; 227 eoi(); 228 } 229 230 static void test_ioapic_simultaneous(void) 231 { 232 handle_irq(0x78, ioapic_isr_78); 233 handle_irq(0x66, ioapic_isr_66); 234 set_ioapic_redir(0x10, 0x78); 235 set_ioapic_redir(0x11, 0x66); 236 irq_disable(); 237 toggle_irq_line(0x11); 238 toggle_irq_line(0x10); 239 irq_enable(); 240 asm volatile ("nop"); 241 report("ioapic simultaneous interrupt", 242 g_66 && g_78 && g_66_after_78 && g_66_rip == g_78_rip); 243 } 244 245 volatile int nmi_counter_private, nmi_counter, nmi_hlt_counter, sti_loop_active; 246 247 void sti_nop(char *p) 248 { 249 asm volatile ( 250 ".globl post_sti \n\t" 251 "sti \n" 252 /* 253 * vmx won't exit on external interrupt if blocked-by-sti, 254 * so give it a reason to exit by accessing an unmapped page. 255 */ 256 "post_sti: testb $0, %0 \n\t" 257 "nop \n\t" 258 "cli" 259 : : "m"(*p) 260 ); 261 nmi_counter = nmi_counter_private; 262 } 263 264 static void sti_loop(void *ignore) 265 { 266 unsigned k = 0; 267 268 while (sti_loop_active) { 269 sti_nop((char *)(ulong)((k++ * 4096) % (128 * 1024 * 1024))); 270 } 271 } 272 273 static void nmi_handler(isr_regs_t *regs) 274 { 275 extern void post_sti(void); 276 ++nmi_counter_private; 277 nmi_hlt_counter += regs->rip == (ulong)post_sti; 278 } 279 280 static void update_cr3(void *cr3) 281 { 282 write_cr3((ulong)cr3); 283 } 284 285 static void test_sti_nmi(void) 286 { 287 unsigned old_counter; 288 289 if (cpu_count() < 2) { 290 return; 291 } 292 293 handle_irq(2, nmi_handler); 294 on_cpu(1, update_cr3, (void *)read_cr3()); 295 296 sti_loop_active = 1; 297 on_cpu_async(1, sti_loop, 0); 298 while (nmi_counter < 30000) { 299 old_counter = nmi_counter; 300 apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, 1); 301 while (nmi_counter == old_counter) { 302 ; 303 } 304 } 305 sti_loop_active = 0; 306 report("nmi-after-sti", nmi_hlt_counter == 0); 307 } 308 309 int main() 310 { 311 setup_vm(); 312 smp_init(); 313 setup_idt(); 314 315 test_lapic_existence(); 316 317 mask_pic_interrupts(); 318 enable_apic(); 319 test_enable_x2apic(); 320 321 test_self_ipi(); 322 323 test_ioapic_intr(); 324 test_ioapic_simultaneous(); 325 test_sti_nmi(); 326 327 printf("\nsummary: %d tests, %d failures\n", g_tests, g_fail); 328 329 return g_fail != 0; 330 } 331