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