xref: /kvm-unit-tests/x86/vmexit.c (revision 456c55bc9e0ae44906b4eb55da41cbbea0638984)
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 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