1 #include "libcflat.h" 2 #include "smp.h" 3 #include "processor.h" 4 #include "atomic.h" 5 #include "pci.h" 6 #include "x86/vm.h" 7 #include "x86/desc.h" 8 #include "x86/acpi.h" 9 #include "x86/apic.h" 10 #include "x86/isr.h" 11 12 #define IPI_TEST_VECTOR 0xb0 13 14 struct test { 15 void (*func)(void); 16 const char *name; 17 int (*valid)(void); 18 int parallel; 19 bool (*next)(struct test *); 20 }; 21 22 #define GOAL (1ull << 30) 23 24 static int nr_cpus; 25 26 static void cpuid_test(void) 27 { 28 asm volatile ("push %%"R "bx; cpuid; pop %%"R "bx" 29 : : : "eax", "ecx", "edx"); 30 } 31 32 static void vmcall(void) 33 { 34 unsigned long a = 0, b, c, d; 35 36 asm volatile ("vmcall" : "+a"(a), "=b"(b), "=c"(c), "=d"(d)); 37 } 38 39 #define MSR_TSC_ADJUST 0x3b 40 #define MSR_EFER 0xc0000080 41 #define EFER_NX_MASK (1ull << 11) 42 43 #ifdef __x86_64__ 44 static void mov_from_cr8(void) 45 { 46 unsigned long cr8; 47 48 asm volatile ("mov %%cr8, %0" : "=r"(cr8)); 49 } 50 51 static void mov_to_cr8(void) 52 { 53 unsigned long cr8 = 0; 54 55 asm volatile ("mov %0, %%cr8" : : "r"(cr8)); 56 } 57 #endif 58 59 static int is_smp(void) 60 { 61 return cpu_count() > 1; 62 } 63 64 static void nop(void *junk) 65 { 66 } 67 68 volatile int x = 0; 69 70 static void self_ipi_isr(isr_regs_t *regs) 71 { 72 x++; 73 eoi(); 74 } 75 76 static void x2apic_self_ipi(int vec) 77 { 78 wrmsr(0x83f, vec); 79 } 80 81 static void apic_self_ipi(int vec) 82 { 83 apic_icr_write(APIC_INT_ASSERT | APIC_DEST_SELF | APIC_DEST_PHYSICAL | 84 APIC_DM_FIXED | IPI_TEST_VECTOR, vec); 85 } 86 87 static void self_ipi_sti_nop(void) 88 { 89 x = 0; 90 irq_disable(); 91 apic_self_ipi(IPI_TEST_VECTOR); 92 asm volatile("sti; nop"); 93 if (x != 1) printf("%d", x); 94 } 95 96 static void self_ipi_sti_hlt(void) 97 { 98 x = 0; 99 irq_disable(); 100 apic_self_ipi(IPI_TEST_VECTOR); 101 asm volatile("sti; hlt"); 102 if (x != 1) printf("%d", x); 103 } 104 105 static void self_ipi_tpr(void) 106 { 107 x = 0; 108 apic_set_tpr(0x0f); 109 apic_self_ipi(IPI_TEST_VECTOR); 110 apic_set_tpr(0x00); 111 asm volatile("nop"); 112 if (x != 1) printf("%d", x); 113 } 114 115 static void self_ipi_tpr_sti_nop(void) 116 { 117 x = 0; 118 irq_disable(); 119 apic_set_tpr(0x0f); 120 apic_self_ipi(IPI_TEST_VECTOR); 121 apic_set_tpr(0x00); 122 asm volatile("sti; nop"); 123 if (x != 1) printf("%d", x); 124 } 125 126 static void self_ipi_tpr_sti_hlt(void) 127 { 128 x = 0; 129 irq_disable(); 130 apic_set_tpr(0x0f); 131 apic_self_ipi(IPI_TEST_VECTOR); 132 apic_set_tpr(0x00); 133 asm volatile("sti; hlt"); 134 if (x != 1) printf("%d", x); 135 } 136 137 static int is_x2apic(void) 138 { 139 return rdmsr(MSR_IA32_APICBASE) & APIC_EXTD; 140 } 141 142 static void x2apic_self_ipi_sti_nop(void) 143 { 144 irq_disable(); 145 x2apic_self_ipi(IPI_TEST_VECTOR); 146 asm volatile("sti; nop"); 147 } 148 149 static void x2apic_self_ipi_sti_hlt(void) 150 { 151 irq_disable(); 152 x2apic_self_ipi(IPI_TEST_VECTOR); 153 asm volatile("sti; hlt"); 154 } 155 156 static void x2apic_self_ipi_tpr(void) 157 { 158 apic_set_tpr(0x0f); 159 x2apic_self_ipi(IPI_TEST_VECTOR); 160 apic_set_tpr(0x00); 161 asm volatile("nop"); 162 } 163 164 static void x2apic_self_ipi_tpr_sti_nop(void) 165 { 166 irq_disable(); 167 apic_set_tpr(0x0f); 168 x2apic_self_ipi(IPI_TEST_VECTOR); 169 apic_set_tpr(0x00); 170 asm volatile("sti; nop"); 171 } 172 173 static void x2apic_self_ipi_tpr_sti_hlt(void) 174 { 175 irq_disable(); 176 apic_set_tpr(0x0f); 177 x2apic_self_ipi(IPI_TEST_VECTOR); 178 apic_set_tpr(0x00); 179 asm volatile("sti; hlt"); 180 } 181 182 static void ipi(void) 183 { 184 on_cpu(1, nop, 0); 185 } 186 187 static void ipi_halt(void) 188 { 189 unsigned long long t; 190 191 on_cpu(1, nop, 0); 192 t = rdtsc() + 2000; 193 while (rdtsc() < t) 194 ; 195 } 196 197 int pm_tmr_blk; 198 static void inl_pmtimer(void) 199 { 200 inl(pm_tmr_blk); 201 } 202 203 static void inl_nop_qemu(void) 204 { 205 inl(0x1234); 206 } 207 208 static void inl_nop_kernel(void) 209 { 210 inb(0x4d0); 211 } 212 213 static void outl_elcr_kernel(void) 214 { 215 outb(0, 0x4d0); 216 } 217 218 static void mov_dr(void) 219 { 220 asm volatile("mov %0, %%dr7" : : "r" (0x400L)); 221 } 222 223 static void ple_round_robin(void) 224 { 225 struct counter { 226 volatile int n1; 227 int n2; 228 } __attribute__((aligned(64))); 229 static struct counter counters[64] = { { -1, 0 } }; 230 int me = smp_id(); 231 int you; 232 volatile struct counter *p = &counters[me]; 233 234 while (p->n1 == p->n2) 235 asm volatile ("pause"); 236 237 p->n2 = p->n1; 238 you = me + 1; 239 if (you == nr_cpus) 240 you = 0; 241 ++counters[you].n1; 242 } 243 244 static void rd_tsc_adjust_msr(void) 245 { 246 rdmsr(MSR_TSC_ADJUST); 247 } 248 249 static void wr_tsc_adjust_msr(void) 250 { 251 wrmsr(MSR_TSC_ADJUST, 0x0); 252 } 253 254 static struct pci_test { 255 unsigned iobar; 256 unsigned ioport; 257 volatile void *memaddr; 258 volatile void *mem; 259 int test_idx; 260 uint32_t data; 261 uint32_t offset; 262 } pci_test = { 263 .test_idx = -1 264 }; 265 266 static void pci_mem_testb(void) 267 { 268 *(volatile uint8_t *)pci_test.mem = pci_test.data; 269 } 270 271 static void pci_mem_testw(void) 272 { 273 *(volatile uint16_t *)pci_test.mem = pci_test.data; 274 } 275 276 static void pci_mem_testl(void) 277 { 278 *(volatile uint32_t *)pci_test.mem = pci_test.data; 279 } 280 281 static void pci_io_testb(void) 282 { 283 outb(pci_test.data, pci_test.ioport); 284 } 285 286 static void pci_io_testw(void) 287 { 288 outw(pci_test.data, pci_test.ioport); 289 } 290 291 static void pci_io_testl(void) 292 { 293 outl(pci_test.data, pci_test.ioport); 294 } 295 296 static uint8_t ioreadb(unsigned long addr, bool io) 297 { 298 if (io) { 299 return inb(addr); 300 } else { 301 return *(volatile uint8_t *)addr; 302 } 303 } 304 305 static uint32_t ioreadl(unsigned long addr, bool io) 306 { 307 /* Note: assumes little endian */ 308 if (io) { 309 return inl(addr); 310 } else { 311 return *(volatile uint32_t *)addr; 312 } 313 } 314 315 static void iowriteb(unsigned long addr, uint8_t data, bool io) 316 { 317 if (io) { 318 outb(data, addr); 319 } else { 320 *(volatile uint8_t *)addr = data; 321 } 322 } 323 324 static bool pci_next(struct test *test, unsigned long addr, bool io) 325 { 326 int i; 327 uint8_t width; 328 329 if (!pci_test.memaddr) { 330 test->func = NULL; 331 return true; 332 } 333 pci_test.test_idx++; 334 iowriteb(addr + offsetof(struct pci_test_dev_hdr, test), 335 pci_test.test_idx, io); 336 width = ioreadb(addr + offsetof(struct pci_test_dev_hdr, width), 337 io); 338 switch (width) { 339 case 1: 340 test->func = io ? pci_io_testb : pci_mem_testb; 341 break; 342 case 2: 343 test->func = io ? pci_io_testw : pci_mem_testw; 344 break; 345 case 4: 346 test->func = io ? pci_io_testl : pci_mem_testl; 347 break; 348 default: 349 /* Reset index for purposes of the next test */ 350 pci_test.test_idx = -1; 351 test->func = NULL; 352 return false; 353 } 354 pci_test.data = ioreadl(addr + offsetof(struct pci_test_dev_hdr, data), 355 io); 356 pci_test.offset = ioreadl(addr + offsetof(struct pci_test_dev_hdr, 357 offset), io); 358 for (i = 0; i < pci_test.offset; ++i) { 359 char c = ioreadb(addr + offsetof(struct pci_test_dev_hdr, 360 name) + i, io); 361 if (!c) { 362 break; 363 } 364 printf("%c",c); 365 } 366 printf(":"); 367 return true; 368 } 369 370 static bool pci_mem_next(struct test *test) 371 { 372 bool ret; 373 ret = pci_next(test, ((unsigned long)pci_test.memaddr), false); 374 if (ret) { 375 pci_test.mem = pci_test.memaddr + pci_test.offset; 376 } 377 return ret; 378 } 379 380 static bool pci_io_next(struct test *test) 381 { 382 bool ret; 383 ret = pci_next(test, ((unsigned long)pci_test.iobar), true); 384 if (ret) { 385 pci_test.ioport = pci_test.iobar + pci_test.offset; 386 } 387 return ret; 388 } 389 390 static struct test tests[] = { 391 { cpuid_test, "cpuid", .parallel = 1, }, 392 { vmcall, "vmcall", .parallel = 1, }, 393 #ifdef __x86_64__ 394 { mov_from_cr8, "mov_from_cr8", .parallel = 1, }, 395 { mov_to_cr8, "mov_to_cr8" , .parallel = 1, }, 396 #endif 397 { inl_pmtimer, "inl_from_pmtimer", .parallel = 1, }, 398 { inl_nop_qemu, "inl_from_qemu", .parallel = 1 }, 399 { inl_nop_kernel, "inl_from_kernel", .parallel = 1 }, 400 { outl_elcr_kernel, "outl_to_kernel", .parallel = 1 }, 401 { mov_dr, "mov_dr", .parallel = 1 }, 402 { self_ipi_sti_nop, "self_ipi_sti_nop", .parallel = 0, }, 403 { self_ipi_sti_hlt, "self_ipi_sti_hlt", .parallel = 0, }, 404 { self_ipi_tpr, "self_ipi_tpr", .parallel = 0, }, 405 { self_ipi_tpr_sti_nop, "self_ipi_tpr_sti_nop", .parallel = 0, }, 406 { self_ipi_tpr_sti_hlt, "self_ipi_tpr_sti_hlt", .parallel = 0, }, 407 { x2apic_self_ipi_sti_nop, "x2apic_self_ipi_sti_nop", is_x2apic, .parallel = 0, }, 408 { x2apic_self_ipi_sti_hlt, "x2apic_self_ipi_sti_hlt", is_x2apic, .parallel = 0, }, 409 { x2apic_self_ipi_tpr, "x2apic_self_ipi_tpr", is_x2apic, .parallel = 0, }, 410 { x2apic_self_ipi_tpr_sti_nop, "x2apic_self_ipi_tpr_sti_nop", is_x2apic, .parallel = 0, }, 411 { x2apic_self_ipi_tpr_sti_hlt, "x2apic_self_ipi_tpr_sti_hlt", is_x2apic, .parallel = 0, }, 412 { ipi, "ipi", is_smp, .parallel = 0, }, 413 { ipi_halt, "ipi+halt", is_smp, .parallel = 0, }, 414 { ple_round_robin, "ple-round-robin", .parallel = 1 }, 415 { wr_tsc_adjust_msr, "wr_tsc_adjust_msr", .parallel = 1 }, 416 { rd_tsc_adjust_msr, "rd_tsc_adjust_msr", .parallel = 1 }, 417 { NULL, "pci-mem", .parallel = 0, .next = pci_mem_next }, 418 { NULL, "pci-io", .parallel = 0, .next = pci_io_next }, 419 }; 420 421 unsigned iterations; 422 static atomic_t nr_cpus_done; 423 424 static void run_test(void *_func) 425 { 426 int i; 427 void (*func)(void) = _func; 428 429 for (i = 0; i < iterations; ++i) 430 func(); 431 432 atomic_inc(&nr_cpus_done); 433 } 434 435 static bool do_test(struct test *test) 436 { 437 int i; 438 unsigned long long t1, t2; 439 void (*func)(void); 440 441 iterations = 32; 442 443 if (test->valid && !test->valid()) { 444 printf("%s (skipped)\n", test->name); 445 return false; 446 } 447 448 if (test->next && !test->next(test)) { 449 return false; 450 } 451 452 func = test->func; 453 if (!func) { 454 printf("%s (skipped)\n", test->name); 455 return false; 456 } 457 458 do { 459 iterations *= 2; 460 t1 = rdtsc(); 461 462 if (!test->parallel) { 463 for (i = 0; i < iterations; ++i) 464 func(); 465 } else { 466 atomic_set(&nr_cpus_done, 0); 467 for (i = cpu_count(); i > 0; i--) 468 on_cpu_async(i-1, run_test, func); 469 while (atomic_read(&nr_cpus_done) < cpu_count()) 470 ; 471 } 472 t2 = rdtsc(); 473 } while ((t2 - t1) < GOAL); 474 printf("%s %d\n", test->name, (int)((t2 - t1) / iterations)); 475 return test->next; 476 } 477 478 static void enable_nx(void *junk) 479 { 480 if (cpuid(0x80000001).d & (1 << 20)) 481 wrmsr(MSR_EFER, rdmsr(MSR_EFER) | EFER_NX_MASK); 482 } 483 484 bool test_wanted(struct test *test, char *wanted[], int nwanted) 485 { 486 int i; 487 488 if (!nwanted) 489 return true; 490 491 for (i = 0; i < nwanted; ++i) 492 if (strcmp(wanted[i], test->name) == 0) 493 return true; 494 495 return false; 496 } 497 498 int main(int ac, char **av) 499 { 500 struct fadt_descriptor_rev1 *fadt; 501 int i; 502 unsigned long membar = 0; 503 struct pci_dev pcidev; 504 int ret; 505 506 smp_init(); 507 setup_vm(); 508 handle_irq(IPI_TEST_VECTOR, self_ipi_isr); 509 nr_cpus = cpu_count(); 510 511 irq_enable(); 512 for (i = cpu_count(); i > 0; i--) 513 on_cpu(i-1, enable_nx, 0); 514 515 fadt = find_acpi_table_addr(FACP_SIGNATURE); 516 pm_tmr_blk = fadt->pm_tmr_blk; 517 printf("PM timer port is %x\n", pm_tmr_blk); 518 519 ret = pci_find_dev(PCI_VENDOR_ID_REDHAT, PCI_DEVICE_ID_REDHAT_TEST); 520 if (ret != PCIDEVADDR_INVALID) { 521 pci_dev_init(&pcidev, ret); 522 assert(pci_bar_is_memory(&pcidev, PCI_TESTDEV_BAR_MEM)); 523 assert(!pci_bar_is_memory(&pcidev, PCI_TESTDEV_BAR_IO)); 524 membar = pcidev.resource[PCI_TESTDEV_BAR_MEM]; 525 pci_test.memaddr = ioremap(membar, PAGE_SIZE); 526 pci_test.iobar = pcidev.resource[PCI_TESTDEV_BAR_IO]; 527 printf("pci-testdev at %#x membar %lx iobar %x\n", 528 pcidev.bdf, membar, pci_test.iobar); 529 } 530 531 for (i = 0; i < ARRAY_SIZE(tests); ++i) 532 if (test_wanted(&tests[i], av + 1, ac - 1)) 533 while (do_test(&tests[i])) {} 534 535 return 0; 536 } 537