vmexit.c 11 KB

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