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 mov_dr(void) 134 { 135 asm volatile("mov %0, %%dr7" : : "r" (0x400L)); 136 } 137 138 static void ple_round_robin(void) 139 { 140 struct counter { 141 volatile int n1; 142 int n2; 143 } __attribute__((aligned(64))); 144 static struct counter counters[64] = { { -1, 0 } }; 145 int me = smp_id(); 146 int you; 147 volatile struct counter *p = &counters[me]; 148 149 while (p->n1 == p->n2) 150 asm volatile ("pause"); 151 152 p->n2 = p->n1; 153 you = me + 1; 154 if (you == nr_cpus) 155 you = 0; 156 ++counters[you].n1; 157 } 158 159 static void rd_tsc_adjust_msr(void) 160 { 161 rdmsr(MSR_TSC_ADJUST); 162 } 163 164 static void wr_tsc_adjust_msr(void) 165 { 166 wrmsr(MSR_TSC_ADJUST, 0x0); 167 } 168 169 struct pci_test_dev_hdr { 170 uint8_t test; 171 uint8_t width; 172 uint8_t pad0[2]; 173 uint32_t offset; 174 uint32_t data; 175 uint32_t count; 176 uint8_t name[]; 177 }; 178 179 static struct pci_test { 180 unsigned iobar; 181 unsigned ioport; 182 volatile void *memaddr; 183 volatile void *mem; 184 int test_idx; 185 uint32_t data; 186 uint32_t offset; 187 } pci_test = { 188 .test_idx = -1 189 }; 190 191 static void pci_mem_testb(void) 192 { 193 *(volatile uint8_t *)pci_test.mem = pci_test.data; 194 } 195 196 static void pci_mem_testw(void) 197 { 198 *(volatile uint16_t *)pci_test.mem = pci_test.data; 199 } 200 201 static void pci_mem_testl(void) 202 { 203 *(volatile uint32_t *)pci_test.mem = pci_test.data; 204 } 205 206 static void pci_io_testb(void) 207 { 208 outb(pci_test.ioport, pci_test.data); 209 } 210 211 static void pci_io_testw(void) 212 { 213 outw(pci_test.ioport, pci_test.data); 214 } 215 216 static void pci_io_testl(void) 217 { 218 outl(pci_test.ioport, pci_test.data); 219 } 220 221 static uint8_t ioreadb(unsigned long addr, bool io) 222 { 223 if (io) { 224 return inb(addr); 225 } else { 226 return *(volatile uint8_t *)addr; 227 } 228 } 229 230 static uint32_t ioreadl(unsigned long addr, bool io) 231 { 232 /* Note: assumes little endian */ 233 if (io) { 234 return inl(addr); 235 } else { 236 return *(volatile uint32_t *)addr; 237 } 238 } 239 240 static void iowriteb(unsigned long addr, uint8_t data, bool io) 241 { 242 if (io) { 243 outb(addr, data); 244 } else { 245 *(volatile uint8_t *)addr = data; 246 } 247 } 248 249 static bool pci_next(struct test *test, unsigned long addr, bool io) 250 { 251 int i; 252 uint8_t width; 253 254 if (!pci_test.memaddr) { 255 test->func = NULL; 256 return true; 257 } 258 pci_test.test_idx++; 259 iowriteb(addr + offsetof(struct pci_test_dev_hdr, test), 260 pci_test.test_idx, io); 261 width = ioreadb(addr + offsetof(struct pci_test_dev_hdr, width), 262 io); 263 switch (width) { 264 case 1: 265 test->func = io ? pci_io_testb : pci_mem_testb; 266 break; 267 case 2: 268 test->func = io ? pci_io_testw : pci_mem_testw; 269 break; 270 case 4: 271 test->func = io ? pci_io_testl : pci_mem_testl; 272 break; 273 default: 274 /* Reset index for purposes of the next test */ 275 pci_test.test_idx = -1; 276 test->func = NULL; 277 return false; 278 } 279 pci_test.data = ioreadl(addr + offsetof(struct pci_test_dev_hdr, data), 280 io); 281 pci_test.offset = ioreadl(addr + offsetof(struct pci_test_dev_hdr, 282 offset), io); 283 for (i = 0; i < pci_test.offset; ++i) { 284 char c = ioreadb(addr + offsetof(struct pci_test_dev_hdr, 285 name) + i, io); 286 if (!c) { 287 break; 288 } 289 printf("%c",c); 290 } 291 printf(":"); 292 return true; 293 } 294 295 static bool pci_mem_next(struct test *test) 296 { 297 bool ret; 298 ret = pci_next(test, ((unsigned long)pci_test.memaddr), false); 299 if (ret) { 300 pci_test.mem = pci_test.memaddr + pci_test.offset; 301 } 302 return ret; 303 } 304 305 static bool pci_io_next(struct test *test) 306 { 307 bool ret; 308 ret = pci_next(test, ((unsigned long)pci_test.iobar), true); 309 if (ret) { 310 pci_test.ioport = pci_test.iobar + pci_test.offset; 311 } 312 return ret; 313 } 314 315 static struct test tests[] = { 316 { cpuid_test, "cpuid", .parallel = 1, }, 317 { vmcall, "vmcall", .parallel = 1, }, 318 #ifdef __x86_64__ 319 { mov_from_cr8, "mov_from_cr8", .parallel = 1, }, 320 { mov_to_cr8, "mov_to_cr8" , .parallel = 1, }, 321 #endif 322 { inl_pmtimer, "inl_from_pmtimer", .parallel = 1, }, 323 { inl_nop_qemu, "inl_from_qemu", .parallel = 1 }, 324 { inl_nop_kernel, "inl_from_kernel", .parallel = 1 }, 325 { outl_elcr_kernel, "outl_to_kernel", .parallel = 1 }, 326 { mov_dr, "mov_dr", .parallel = 1 }, 327 { ipi, "ipi", is_smp, .parallel = 0, }, 328 { ipi_halt, "ipi+halt", is_smp, .parallel = 0, }, 329 { ple_round_robin, "ple-round-robin", .parallel = 1 }, 330 { wr_tsc_adjust_msr, "wr_tsc_adjust_msr", .parallel = 1 }, 331 { rd_tsc_adjust_msr, "rd_tsc_adjust_msr", .parallel = 1 }, 332 { NULL, "pci-mem", .parallel = 0, .next = pci_mem_next }, 333 { NULL, "pci-io", .parallel = 0, .next = pci_io_next }, 334 }; 335 336 unsigned iterations; 337 static atomic_t nr_cpus_done; 338 339 static void run_test(void *_func) 340 { 341 int i; 342 void (*func)(void) = _func; 343 344 for (i = 0; i < iterations; ++i) 345 func(); 346 347 atomic_inc(&nr_cpus_done); 348 } 349 350 static bool do_test(struct test *test) 351 { 352 int i; 353 unsigned long long t1, t2; 354 void (*func)(void); 355 356 iterations = 32; 357 358 if (test->valid && !test->valid()) { 359 printf("%s (skipped)\n", test->name); 360 return false; 361 } 362 363 if (test->next && !test->next(test)) { 364 return false; 365 } 366 367 func = test->func; 368 if (!func) { 369 printf("%s (skipped)\n", test->name); 370 return false; 371 } 372 373 do { 374 iterations *= 2; 375 t1 = rdtsc(); 376 377 if (!test->parallel) { 378 for (i = 0; i < iterations; ++i) 379 func(); 380 } else { 381 atomic_set(&nr_cpus_done, 0); 382 for (i = cpu_count(); i > 0; i--) 383 on_cpu_async(i-1, run_test, func); 384 while (atomic_read(&nr_cpus_done) < cpu_count()) 385 ; 386 } 387 t2 = rdtsc(); 388 } while ((t2 - t1) < GOAL); 389 printf("%s %d\n", test->name, (int)((t2 - t1) / iterations)); 390 return test->next; 391 } 392 393 static void enable_nx(void *junk) 394 { 395 if (cpuid(0x80000001).d & (1 << 20)) 396 wrmsr(MSR_EFER, rdmsr(MSR_EFER) | EFER_NX_MASK); 397 } 398 399 bool test_wanted(struct test *test, char *wanted[], int nwanted) 400 { 401 int i; 402 403 if (!nwanted) 404 return true; 405 406 for (i = 0; i < nwanted; ++i) 407 if (strcmp(wanted[i], test->name) == 0) 408 return true; 409 410 return false; 411 } 412 413 int main(int ac, char **av) 414 { 415 int i; 416 unsigned long membar = 0, base, offset; 417 void *m; 418 pcidevaddr_t pcidev; 419 420 smp_init(); 421 setup_vm(); 422 nr_cpus = cpu_count(); 423 424 for (i = cpu_count(); i > 0; i--) 425 on_cpu(i-1, enable_nx, 0); 426 427 pcidev = pci_find_dev(0x1b36, 0x0005); 428 if (pcidev) { 429 for (i = 0; i < 2; i++) { 430 if (!pci_bar_is_valid(pcidev, i)) { 431 continue; 432 } 433 if (pci_bar_is_memory(pcidev, i)) { 434 membar = pci_bar_addr(pcidev, i); 435 base = membar & ~4095; 436 offset = membar - base; 437 m = alloc_vpages(1); 438 439 install_page((void *)read_cr3(), base, m); 440 pci_test.memaddr = m + offset; 441 } else { 442 pci_test.iobar = pci_bar_addr(pcidev, i); 443 } 444 } 445 printf("pci-testdev at 0x%x membar %lx iobar %x\n", 446 pcidev, membar, pci_test.iobar); 447 } 448 449 for (i = 0; i < ARRAY_SIZE(tests); ++i) 450 if (test_wanted(&tests[i], av + 1, ac - 1)) 451 while (do_test(&tests[i])) {} 452 453 return 0; 454 } 455