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