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