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 #include "msr.h" 8 9 static void test_lapic_existence(void) 10 { 11 u32 lvr; 12 13 lvr = apic_read(APIC_LVR); 14 printf("apic version: %x\n", lvr); 15 report("apic existence", (u16)lvr == 0x14); 16 } 17 18 #define TSC_DEADLINE_TIMER_MODE (2 << 17) 19 #define TSC_DEADLINE_TIMER_VECTOR 0xef 20 #define MSR_IA32_TSC 0x00000010 21 #define MSR_IA32_TSCDEADLINE 0x000006e0 22 23 static int tdt_count; 24 25 static void tsc_deadline_timer_isr(isr_regs_t *regs) 26 { 27 ++tdt_count; 28 } 29 30 static void start_tsc_deadline_timer(void) 31 { 32 handle_irq(TSC_DEADLINE_TIMER_VECTOR, tsc_deadline_timer_isr); 33 irq_enable(); 34 35 wrmsr(MSR_IA32_TSCDEADLINE, rdmsr(MSR_IA32_TSC)); 36 asm volatile ("nop"); 37 report("tsc deadline timer", tdt_count == 1); 38 } 39 40 static int enable_tsc_deadline_timer(void) 41 { 42 uint32_t lvtt; 43 44 if (cpuid(1).c & (1 << 24)) { 45 lvtt = TSC_DEADLINE_TIMER_MODE | TSC_DEADLINE_TIMER_VECTOR; 46 apic_write(APIC_LVTT, lvtt); 47 start_tsc_deadline_timer(); 48 return 1; 49 } else { 50 return 0; 51 } 52 } 53 54 static void test_tsc_deadline_timer(void) 55 { 56 if(enable_tsc_deadline_timer()) { 57 printf("tsc deadline timer enabled\n"); 58 } else { 59 printf("tsc deadline timer not detected\n"); 60 } 61 } 62 63 static void do_write_apicbase(void *data) 64 { 65 set_exception_return(&&resume); 66 wrmsr(MSR_IA32_APICBASE, *(u64 *)data); 67 resume: 68 barrier(); 69 } 70 71 void test_enable_x2apic(void) 72 { 73 u64 invalid_state = APIC_DEFAULT_PHYS_BASE | APIC_BSP | APIC_EXTD; 74 u64 apic_enabled = APIC_DEFAULT_PHYS_BASE | APIC_BSP | APIC_EN; 75 u64 x2apic_enabled = 76 APIC_DEFAULT_PHYS_BASE | APIC_BSP | APIC_EN | APIC_EXTD; 77 78 if (enable_x2apic()) { 79 printf("x2apic enabled\n"); 80 81 report("x2apic enabled to invalid state", 82 test_for_exception(GP_VECTOR, do_write_apicbase, 83 &invalid_state)); 84 report("x2apic enabled to apic enabled", 85 test_for_exception(GP_VECTOR, do_write_apicbase, 86 &apic_enabled)); 87 88 wrmsr(MSR_IA32_APICBASE, APIC_DEFAULT_PHYS_BASE | APIC_BSP); 89 report("disabled to invalid state", 90 test_for_exception(GP_VECTOR, do_write_apicbase, 91 &invalid_state)); 92 report("disabled to x2apic enabled", 93 test_for_exception(GP_VECTOR, do_write_apicbase, 94 &x2apic_enabled)); 95 96 wrmsr(MSR_IA32_APICBASE, apic_enabled); 97 report("apic enabled to invalid state", 98 test_for_exception(GP_VECTOR, do_write_apicbase, 99 &invalid_state)); 100 101 wrmsr(MSR_IA32_APICBASE, x2apic_enabled); 102 apic_write(APIC_SPIV, 0x1ff); 103 } else { 104 printf("x2apic not detected\n"); 105 106 report("enable unsupported x2apic", 107 test_for_exception(GP_VECTOR, do_write_apicbase, 108 &x2apic_enabled)); 109 } 110 } 111 112 #define ALTERNATE_APIC_BASE 0x42000000 113 114 static void test_apicbase(void) 115 { 116 u64 orig_apicbase = rdmsr(MSR_IA32_APICBASE); 117 u32 lvr = apic_read(APIC_LVR); 118 u64 value; 119 120 wrmsr(MSR_IA32_APICBASE, orig_apicbase & ~(APIC_EN | APIC_EXTD)); 121 wrmsr(MSR_IA32_APICBASE, ALTERNATE_APIC_BASE | APIC_BSP | APIC_EN); 122 123 report("relocate apic", 124 *(volatile u32 *)(ALTERNATE_APIC_BASE + APIC_LVR) == lvr); 125 126 value = orig_apicbase | (1UL << (cpuid(0x80000008).a & 0xff)); 127 report("apicbase: reserved physaddr bits", 128 test_for_exception(GP_VECTOR, do_write_apicbase, &value)); 129 130 value = orig_apicbase | 1; 131 report("apicbase: reserved low bits", 132 test_for_exception(GP_VECTOR, do_write_apicbase, &value)); 133 134 wrmsr(MSR_IA32_APICBASE, orig_apicbase); 135 apic_write(APIC_SPIV, 0x1ff); 136 } 137 138 static void eoi(void) 139 { 140 apic_write(APIC_EOI, 0); 141 } 142 143 static int ipi_count; 144 145 static void self_ipi_isr(isr_regs_t *regs) 146 { 147 ++ipi_count; 148 eoi(); 149 } 150 151 static void test_self_ipi(void) 152 { 153 int vec = 0xf1; 154 155 handle_irq(vec, self_ipi_isr); 156 irq_enable(); 157 apic_icr_write(APIC_DEST_SELF | APIC_DEST_PHYSICAL | APIC_DM_FIXED | vec, 158 0); 159 asm volatile ("nop"); 160 report("self ipi", ipi_count == 1); 161 } 162 163 static void set_ioapic_redir(unsigned line, unsigned vec) 164 { 165 ioapic_redir_entry_t e = { 166 .vector = vec, 167 .delivery_mode = 0, 168 .trig_mode = 0, 169 }; 170 171 ioapic_write_redir(line, e); 172 } 173 174 static void set_irq_line(unsigned line, int val) 175 { 176 asm volatile("out %0, %1" : : "a"((u8)val), "d"((u16)(0x2000 + line))); 177 } 178 179 static void toggle_irq_line(unsigned line) 180 { 181 set_irq_line(line, 1); 182 set_irq_line(line, 0); 183 } 184 185 static int g_isr_77; 186 187 static void ioapic_isr_77(isr_regs_t *regs) 188 { 189 ++g_isr_77; 190 eoi(); 191 } 192 193 static void test_ioapic_intr(void) 194 { 195 handle_irq(0x77, ioapic_isr_77); 196 set_ioapic_redir(0x0e, 0x77); 197 toggle_irq_line(0x0e); 198 asm volatile ("nop"); 199 report("ioapic interrupt", g_isr_77 == 1); 200 } 201 202 static int g_78, g_66, g_66_after_78; 203 static ulong g_66_rip, g_78_rip; 204 205 static void ioapic_isr_78(isr_regs_t *regs) 206 { 207 ++g_78; 208 g_78_rip = regs->rip; 209 eoi(); 210 } 211 212 static void ioapic_isr_66(isr_regs_t *regs) 213 { 214 ++g_66; 215 if (g_78) 216 ++g_66_after_78; 217 g_66_rip = regs->rip; 218 eoi(); 219 } 220 221 static void test_ioapic_simultaneous(void) 222 { 223 handle_irq(0x78, ioapic_isr_78); 224 handle_irq(0x66, ioapic_isr_66); 225 set_ioapic_redir(0x0e, 0x78); 226 set_ioapic_redir(0x0f, 0x66); 227 irq_disable(); 228 toggle_irq_line(0x0f); 229 toggle_irq_line(0x0e); 230 irq_enable(); 231 asm volatile ("nop"); 232 report("ioapic simultaneous interrupt", 233 g_66 && g_78 && g_66_after_78 && g_66_rip == g_78_rip); 234 } 235 236 volatile int nmi_counter_private, nmi_counter, nmi_hlt_counter, sti_loop_active; 237 238 void sti_nop(char *p) 239 { 240 asm volatile ( 241 ".globl post_sti \n\t" 242 "sti \n" 243 /* 244 * vmx won't exit on external interrupt if blocked-by-sti, 245 * so give it a reason to exit by accessing an unmapped page. 246 */ 247 "post_sti: testb $0, %0 \n\t" 248 "nop \n\t" 249 "cli" 250 : : "m"(*p) 251 ); 252 nmi_counter = nmi_counter_private; 253 } 254 255 static void sti_loop(void *ignore) 256 { 257 unsigned k = 0; 258 259 while (sti_loop_active) { 260 sti_nop((char *)(ulong)((k++ * 4096) % (128 * 1024 * 1024))); 261 } 262 } 263 264 static void nmi_handler(isr_regs_t *regs) 265 { 266 extern void post_sti(void); 267 ++nmi_counter_private; 268 nmi_hlt_counter += regs->rip == (ulong)post_sti; 269 } 270 271 static void update_cr3(void *cr3) 272 { 273 write_cr3((ulong)cr3); 274 } 275 276 static void test_sti_nmi(void) 277 { 278 unsigned old_counter; 279 280 if (cpu_count() < 2) { 281 return; 282 } 283 284 handle_irq(2, nmi_handler); 285 on_cpu(1, update_cr3, (void *)read_cr3()); 286 287 sti_loop_active = 1; 288 on_cpu_async(1, sti_loop, 0); 289 while (nmi_counter < 30000) { 290 old_counter = nmi_counter; 291 apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, 1); 292 while (nmi_counter == old_counter) { 293 ; 294 } 295 } 296 sti_loop_active = 0; 297 report("nmi-after-sti", nmi_hlt_counter == 0); 298 } 299 300 static volatile bool nmi_done, nmi_flushed; 301 static volatile int nmi_received; 302 static volatile int cpu0_nmi_ctr1, cpu1_nmi_ctr1; 303 static volatile int cpu0_nmi_ctr2, cpu1_nmi_ctr2; 304 305 static void multiple_nmi_handler(isr_regs_t *regs) 306 { 307 ++nmi_received; 308 } 309 310 static void kick_me_nmi(void *blah) 311 { 312 while (!nmi_done) { 313 ++cpu1_nmi_ctr1; 314 while (cpu1_nmi_ctr1 != cpu0_nmi_ctr1 && !nmi_done) { 315 pause(); 316 } 317 if (nmi_done) { 318 return; 319 } 320 apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, 0); 321 /* make sure the NMI has arrived by sending an IPI after it */ 322 apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_FIXED | APIC_INT_ASSERT 323 | 0x44, 0); 324 ++cpu1_nmi_ctr2; 325 while (cpu1_nmi_ctr2 != cpu0_nmi_ctr2 && !nmi_done) { 326 pause(); 327 } 328 } 329 } 330 331 static void flush_nmi(isr_regs_t *regs) 332 { 333 nmi_flushed = true; 334 apic_write(APIC_EOI, 0); 335 } 336 337 static void test_multiple_nmi(void) 338 { 339 int i; 340 bool ok = true; 341 342 if (cpu_count() < 2) { 343 return; 344 } 345 346 sti(); 347 handle_irq(2, multiple_nmi_handler); 348 handle_irq(0x44, flush_nmi); 349 on_cpu_async(1, kick_me_nmi, 0); 350 for (i = 0; i < 1000000; ++i) { 351 nmi_flushed = false; 352 nmi_received = 0; 353 ++cpu0_nmi_ctr1; 354 while (cpu1_nmi_ctr1 != cpu0_nmi_ctr1) { 355 pause(); 356 } 357 apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, 0); 358 while (!nmi_flushed) { 359 pause(); 360 } 361 if (nmi_received != 2) { 362 ok = false; 363 break; 364 } 365 ++cpu0_nmi_ctr2; 366 while (cpu1_nmi_ctr2 != cpu0_nmi_ctr2) { 367 pause(); 368 } 369 } 370 nmi_done = true; 371 report("multiple nmi", ok); 372 } 373 374 int main() 375 { 376 setup_vm(); 377 smp_init(); 378 setup_idt(); 379 380 test_lapic_existence(); 381 382 mask_pic_interrupts(); 383 test_enable_x2apic(); 384 test_apicbase(); 385 386 test_self_ipi(); 387 388 test_ioapic_intr(); 389 test_ioapic_simultaneous(); 390 test_sti_nmi(); 391 test_multiple_nmi(); 392 393 test_tsc_deadline_timer(); 394 395 return report_summary(); 396 } 397