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