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