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