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