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 static void cpuid_test(void) 51 { 52 asm volatile ("push %%"R "bx; cpuid; pop %%"R "bx" 53 : : : "eax", "ecx", "edx"); 54 } 55 56 static void vmcall(void) 57 { 58 unsigned long a = 0, b, c, d; 59 60 asm volatile ("vmcall" : "+a"(a), "=b"(b), "=c"(c), "=d"(d)); 61 } 62 63 #define MSR_TSC_ADJUST 0x3b 64 #define MSR_EFER 0xc0000080 65 #define EFER_NX_MASK (1ull << 11) 66 67 #ifdef __x86_64__ 68 static void mov_from_cr8(void) 69 { 70 unsigned long cr8; 71 72 asm volatile ("mov %%cr8, %0" : "=r"(cr8)); 73 } 74 75 static void mov_to_cr8(void) 76 { 77 unsigned long cr8 = 0; 78 79 asm volatile ("mov %0, %%cr8" : : "r"(cr8)); 80 } 81 #endif 82 83 static int is_smp(void) 84 { 85 return cpu_count() > 1; 86 } 87 88 static void nop(void *junk) 89 { 90 } 91 92 static void ipi(void) 93 { 94 on_cpu(1, nop, 0); 95 } 96 97 static void ipi_halt(void) 98 { 99 unsigned long long t; 100 101 on_cpu(1, nop, 0); 102 t = rdtsc() + 2000; 103 while (rdtsc() < t) 104 ; 105 } 106 107 static void inl_pmtimer(void) 108 { 109 inl(0xb008); 110 } 111 112 static void inl_nop_qemu(void) 113 { 114 inl(0x1234); 115 } 116 117 static void inl_nop_kernel(void) 118 { 119 inb(0x4d0); 120 } 121 122 static void outl_elcr_kernel(void) 123 { 124 outb(0x4d0, 0); 125 } 126 127 static void mov_dr(void) 128 { 129 asm volatile("mov %0, %%dr7" : : "r" (0x400L)); 130 } 131 132 static void ple_round_robin(void) 133 { 134 struct counter { 135 volatile int n1; 136 int n2; 137 } __attribute__((aligned(64))); 138 static struct counter counters[64] = { { -1, 0 } }; 139 int me = smp_id(); 140 int you; 141 volatile struct counter *p = &counters[me]; 142 143 while (p->n1 == p->n2) 144 asm volatile ("pause"); 145 146 p->n2 = p->n1; 147 you = me + 1; 148 if (you == nr_cpus) 149 you = 0; 150 ++counters[you].n1; 151 } 152 153 static void rd_tsc_adjust_msr(void) 154 { 155 rdmsr(MSR_TSC_ADJUST); 156 } 157 158 static void wr_tsc_adjust_msr(void) 159 { 160 wrmsr(MSR_TSC_ADJUST, 0x0); 161 } 162 163 struct pci_test_dev_hdr { 164 uint8_t test; 165 uint8_t width; 166 uint8_t pad0[2]; 167 uint32_t offset; 168 uint32_t data; 169 uint32_t count; 170 uint8_t name[]; 171 }; 172 173 static struct pci_test { 174 unsigned iobar; 175 unsigned ioport; 176 volatile void *memaddr; 177 volatile void *mem; 178 int test_idx; 179 uint32_t data; 180 uint32_t offset; 181 } pci_test = { 182 .test_idx = -1 183 }; 184 185 static void pci_mem_testb(void) 186 { 187 *(volatile uint8_t *)pci_test.mem = pci_test.data; 188 } 189 190 static void pci_mem_testw(void) 191 { 192 *(volatile uint16_t *)pci_test.mem = pci_test.data; 193 } 194 195 static void pci_mem_testl(void) 196 { 197 *(volatile uint32_t *)pci_test.mem = pci_test.data; 198 } 199 200 static void pci_io_testb(void) 201 { 202 outb(pci_test.ioport, pci_test.data); 203 } 204 205 static void pci_io_testw(void) 206 { 207 outw(pci_test.ioport, pci_test.data); 208 } 209 210 static void pci_io_testl(void) 211 { 212 outl(pci_test.ioport, pci_test.data); 213 } 214 215 static uint8_t ioreadb(unsigned long addr, bool io) 216 { 217 if (io) { 218 return inb(addr); 219 } else { 220 return *(volatile uint8_t *)addr; 221 } 222 } 223 224 static uint32_t ioreadl(unsigned long addr, bool io) 225 { 226 /* Note: assumes little endian */ 227 if (io) { 228 return inl(addr); 229 } else { 230 return *(volatile uint32_t *)addr; 231 } 232 } 233 234 static void iowriteb(unsigned long addr, uint8_t data, bool io) 235 { 236 if (io) { 237 outb(addr, data); 238 } else { 239 *(volatile uint8_t *)addr = data; 240 } 241 } 242 243 static bool pci_next(struct test *test, unsigned long addr, bool io) 244 { 245 int i; 246 uint8_t width; 247 248 if (!pci_test.memaddr) { 249 test->func = NULL; 250 return true; 251 } 252 pci_test.test_idx++; 253 iowriteb(addr + offsetof(struct pci_test_dev_hdr, test), 254 pci_test.test_idx, io); 255 width = ioreadb(addr + offsetof(struct pci_test_dev_hdr, width), 256 io); 257 switch (width) { 258 case 1: 259 test->func = io ? pci_io_testb : pci_mem_testb; 260 break; 261 case 2: 262 test->func = io ? pci_io_testw : pci_mem_testw; 263 break; 264 case 4: 265 test->func = io ? pci_io_testl : pci_mem_testl; 266 break; 267 default: 268 /* Reset index for purposes of the next test */ 269 pci_test.test_idx = -1; 270 test->func = NULL; 271 return false; 272 } 273 pci_test.data = ioreadl(addr + offsetof(struct pci_test_dev_hdr, data), 274 io); 275 pci_test.offset = ioreadl(addr + offsetof(struct pci_test_dev_hdr, 276 offset), io); 277 for (i = 0; i < pci_test.offset; ++i) { 278 char c = ioreadb(addr + offsetof(struct pci_test_dev_hdr, 279 name) + i, io); 280 if (!c) { 281 break; 282 } 283 printf("%c",c); 284 } 285 printf(":"); 286 return true; 287 } 288 289 static bool pci_mem_next(struct test *test) 290 { 291 bool ret; 292 ret = pci_next(test, ((unsigned long)pci_test.memaddr), false); 293 if (ret) { 294 pci_test.mem = pci_test.memaddr + pci_test.offset; 295 } 296 return ret; 297 } 298 299 static bool pci_io_next(struct test *test) 300 { 301 bool ret; 302 ret = pci_next(test, ((unsigned long)pci_test.iobar), true); 303 if (ret) { 304 pci_test.ioport = pci_test.iobar + pci_test.offset; 305 } 306 return ret; 307 } 308 309 static struct test tests[] = { 310 { cpuid_test, "cpuid", .parallel = 1, }, 311 { vmcall, "vmcall", .parallel = 1, }, 312 #ifdef __x86_64__ 313 { mov_from_cr8, "mov_from_cr8", .parallel = 1, }, 314 { mov_to_cr8, "mov_to_cr8" , .parallel = 1, }, 315 #endif 316 { inl_pmtimer, "inl_from_pmtimer", .parallel = 1, }, 317 { inl_nop_qemu, "inl_from_qemu", .parallel = 1 }, 318 { inl_nop_kernel, "inl_from_kernel", .parallel = 1 }, 319 { outl_elcr_kernel, "outl_to_kernel", .parallel = 1 }, 320 { mov_dr, "mov_dr", .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