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