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