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