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 eoi(void) 138 { 139 apic_write(APIC_EOI, 0); 140 } 141 142 static int ipi_count; 143 144 static void self_ipi_isr(isr_regs_t *regs) 145 { 146 ++ipi_count; 147 eoi(); 148 } 149 150 static void test_self_ipi(void) 151 { 152 int vec = 0xf1; 153 154 handle_irq(vec, self_ipi_isr); 155 irq_enable(); 156 apic_icr_write(APIC_DEST_SELF | APIC_DEST_PHYSICAL | APIC_DM_FIXED | vec, 157 0); 158 asm volatile ("nop"); 159 report("self ipi", ipi_count == 1); 160 } 161 162 static void set_ioapic_redir(unsigned line, unsigned vec) 163 { 164 ioapic_redir_entry_t e = { 165 .vector = vec, 166 .delivery_mode = 0, 167 .trig_mode = 0, 168 }; 169 170 ioapic_write_redir(line, e); 171 } 172 173 static void set_irq_line(unsigned line, int val) 174 { 175 asm volatile("out %0, %1" : : "a"((u8)val), "d"((u16)(0x2000 + line))); 176 } 177 178 static void toggle_irq_line(unsigned line) 179 { 180 set_irq_line(line, 1); 181 set_irq_line(line, 0); 182 } 183 184 static int g_isr_77; 185 186 static void ioapic_isr_77(isr_regs_t *regs) 187 { 188 ++g_isr_77; 189 eoi(); 190 } 191 192 static void test_ioapic_intr(void) 193 { 194 handle_irq(0x77, ioapic_isr_77); 195 set_ioapic_redir(0x10, 0x77); 196 toggle_irq_line(0x10); 197 asm volatile ("nop"); 198 report("ioapic interrupt", g_isr_77 == 1); 199 } 200 201 static int g_78, g_66, g_66_after_78; 202 static ulong g_66_rip, g_78_rip; 203 204 static void ioapic_isr_78(isr_regs_t *regs) 205 { 206 ++g_78; 207 g_78_rip = regs->rip; 208 eoi(); 209 } 210 211 static void ioapic_isr_66(isr_regs_t *regs) 212 { 213 ++g_66; 214 if (g_78) 215 ++g_66_after_78; 216 g_66_rip = regs->rip; 217 eoi(); 218 } 219 220 static void test_ioapic_simultaneous(void) 221 { 222 handle_irq(0x78, ioapic_isr_78); 223 handle_irq(0x66, ioapic_isr_66); 224 set_ioapic_redir(0x10, 0x78); 225 set_ioapic_redir(0x11, 0x66); 226 irq_disable(); 227 toggle_irq_line(0x11); 228 toggle_irq_line(0x10); 229 irq_enable(); 230 asm volatile ("nop"); 231 report("ioapic simultaneous interrupt", 232 g_66 && g_78 && g_66_after_78 && g_66_rip == g_78_rip); 233 } 234 235 volatile int nmi_counter_private, nmi_counter, nmi_hlt_counter, sti_loop_active; 236 237 void sti_nop(char *p) 238 { 239 asm volatile ( 240 ".globl post_sti \n\t" 241 "sti \n" 242 /* 243 * vmx won't exit on external interrupt if blocked-by-sti, 244 * so give it a reason to exit by accessing an unmapped page. 245 */ 246 "post_sti: testb $0, %0 \n\t" 247 "nop \n\t" 248 "cli" 249 : : "m"(*p) 250 ); 251 nmi_counter = nmi_counter_private; 252 } 253 254 static void sti_loop(void *ignore) 255 { 256 unsigned k = 0; 257 258 while (sti_loop_active) { 259 sti_nop((char *)(ulong)((k++ * 4096) % (128 * 1024 * 1024))); 260 } 261 } 262 263 static void nmi_handler(isr_regs_t *regs) 264 { 265 extern void post_sti(void); 266 ++nmi_counter_private; 267 nmi_hlt_counter += regs->rip == (ulong)post_sti; 268 } 269 270 static void update_cr3(void *cr3) 271 { 272 write_cr3((ulong)cr3); 273 } 274 275 static void test_sti_nmi(void) 276 { 277 unsigned old_counter; 278 279 if (cpu_count() < 2) { 280 return; 281 } 282 283 handle_irq(2, nmi_handler); 284 on_cpu(1, update_cr3, (void *)read_cr3()); 285 286 sti_loop_active = 1; 287 on_cpu_async(1, sti_loop, 0); 288 while (nmi_counter < 30000) { 289 old_counter = nmi_counter; 290 apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, 1); 291 while (nmi_counter == old_counter) { 292 ; 293 } 294 } 295 sti_loop_active = 0; 296 report("nmi-after-sti", nmi_hlt_counter == 0); 297 } 298 299 int main() 300 { 301 setup_vm(); 302 smp_init(); 303 setup_idt(); 304 305 test_lapic_existence(); 306 307 mask_pic_interrupts(); 308 enable_apic(); 309 test_enable_x2apic(); 310 311 test_self_ipi(); 312 313 test_ioapic_intr(); 314 test_ioapic_simultaneous(); 315 test_sti_nmi(); 316 317 printf("\nsummary: %d tests, %d failures\n", g_tests, g_fail); 318 319 return g_fail != 0; 320 } 321