xref: /kvm-unit-tests/x86/vmexit.c (revision 289ebf8fd796220358499d3f0505bc4c8c288b6e)
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 static struct pci_test {
138 	unsigned iobar;
139 	unsigned ioport;
140 	volatile void *memaddr;
141 	volatile void *mem;
142 	int test_idx;
143 	uint32_t data;
144 	uint32_t offset;
145 } pci_test = {
146 	.test_idx = -1
147 };
148 
149 static void pci_mem_testb(void)
150 {
151 	*(volatile uint8_t *)pci_test.mem = pci_test.data;
152 }
153 
154 static void pci_mem_testw(void)
155 {
156 	*(volatile uint16_t *)pci_test.mem = pci_test.data;
157 }
158 
159 static void pci_mem_testl(void)
160 {
161 	*(volatile uint32_t *)pci_test.mem = pci_test.data;
162 }
163 
164 static void pci_io_testb(void)
165 {
166 	outb(pci_test.data, pci_test.ioport);
167 }
168 
169 static void pci_io_testw(void)
170 {
171 	outw(pci_test.data, pci_test.ioport);
172 }
173 
174 static void pci_io_testl(void)
175 {
176 	outl(pci_test.data, pci_test.ioport);
177 }
178 
179 static uint8_t ioreadb(unsigned long addr, bool io)
180 {
181 	if (io) {
182 		return inb(addr);
183 	} else {
184 		return *(volatile uint8_t *)addr;
185 	}
186 }
187 
188 static uint32_t ioreadl(unsigned long addr, bool io)
189 {
190 	/* Note: assumes little endian */
191 	if (io) {
192 		return inl(addr);
193 	} else {
194 		return *(volatile uint32_t *)addr;
195 	}
196 }
197 
198 static void iowriteb(unsigned long addr, uint8_t data, bool io)
199 {
200 	if (io) {
201 		outb(data, addr);
202 	} else {
203 		*(volatile uint8_t *)addr = data;
204 	}
205 }
206 
207 static bool pci_next(struct test *test, unsigned long addr, bool io)
208 {
209 	int i;
210 	uint8_t width;
211 
212 	if (!pci_test.memaddr) {
213 		test->func = NULL;
214 		return true;
215 	}
216 	pci_test.test_idx++;
217 	iowriteb(addr + offsetof(struct pci_test_dev_hdr, test),
218 		 pci_test.test_idx, io);
219 	width = ioreadb(addr + offsetof(struct pci_test_dev_hdr, width),
220 			io);
221 	switch (width) {
222 		case 1:
223 			test->func = io ? pci_io_testb : pci_mem_testb;
224 			break;
225 		case 2:
226 			test->func = io ? pci_io_testw : pci_mem_testw;
227 			break;
228 		case 4:
229 			test->func = io ? pci_io_testl : pci_mem_testl;
230 			break;
231 		default:
232 			/* Reset index for purposes of the next test */
233 			pci_test.test_idx = -1;
234 			test->func = NULL;
235 			return false;
236 	}
237 	pci_test.data = ioreadl(addr + offsetof(struct pci_test_dev_hdr, data),
238 				io);
239 	pci_test.offset = ioreadl(addr + offsetof(struct pci_test_dev_hdr,
240 						  offset), io);
241 	for (i = 0; i < pci_test.offset; ++i) {
242 		char c = ioreadb(addr + offsetof(struct pci_test_dev_hdr,
243 						 name) + i, io);
244 		if (!c) {
245 			break;
246 		}
247 		printf("%c",c);
248 	}
249 	printf(":");
250 	return true;
251 }
252 
253 static bool pci_mem_next(struct test *test)
254 {
255 	bool ret;
256 	ret = pci_next(test, ((unsigned long)pci_test.memaddr), false);
257 	if (ret) {
258 		pci_test.mem = pci_test.memaddr + pci_test.offset;
259 	}
260 	return ret;
261 }
262 
263 static bool pci_io_next(struct test *test)
264 {
265 	bool ret;
266 	ret = pci_next(test, ((unsigned long)pci_test.iobar), true);
267 	if (ret) {
268 		pci_test.ioport = pci_test.iobar + pci_test.offset;
269 	}
270 	return ret;
271 }
272 
273 static struct test tests[] = {
274 	{ cpuid_test, "cpuid", .parallel = 1,  },
275 	{ vmcall, "vmcall", .parallel = 1, },
276 #ifdef __x86_64__
277 	{ mov_from_cr8, "mov_from_cr8", .parallel = 1, },
278 	{ mov_to_cr8, "mov_to_cr8" , .parallel = 1, },
279 #endif
280 	{ inl_pmtimer, "inl_from_pmtimer", .parallel = 1, },
281 	{ inl_nop_qemu, "inl_from_qemu", .parallel = 1 },
282 	{ inl_nop_kernel, "inl_from_kernel", .parallel = 1 },
283 	{ outl_elcr_kernel, "outl_to_kernel", .parallel = 1 },
284 	{ mov_dr, "mov_dr", .parallel = 1 },
285 	{ ipi, "ipi", is_smp, .parallel = 0, },
286 	{ ipi_halt, "ipi+halt", is_smp, .parallel = 0, },
287 	{ ple_round_robin, "ple-round-robin", .parallel = 1 },
288 	{ wr_tsc_adjust_msr, "wr_tsc_adjust_msr", .parallel = 1 },
289 	{ rd_tsc_adjust_msr, "rd_tsc_adjust_msr", .parallel = 1 },
290 	{ NULL, "pci-mem", .parallel = 0, .next = pci_mem_next },
291 	{ NULL, "pci-io", .parallel = 0, .next = pci_io_next },
292 };
293 
294 unsigned iterations;
295 static atomic_t nr_cpus_done;
296 
297 static void run_test(void *_func)
298 {
299     int i;
300     void (*func)(void) = _func;
301 
302     for (i = 0; i < iterations; ++i)
303         func();
304 
305     atomic_inc(&nr_cpus_done);
306 }
307 
308 static bool do_test(struct test *test)
309 {
310 	int i;
311 	unsigned long long t1, t2;
312         void (*func)(void);
313 
314         iterations = 32;
315 
316         if (test->valid && !test->valid()) {
317 		printf("%s (skipped)\n", test->name);
318 		return false;
319 	}
320 
321 	if (test->next && !test->next(test)) {
322 		return false;
323 	}
324 
325 	func = test->func;
326         if (!func) {
327 		printf("%s (skipped)\n", test->name);
328 		return false;
329 	}
330 
331 	do {
332 		iterations *= 2;
333 		t1 = rdtsc();
334 
335 		if (!test->parallel) {
336 			for (i = 0; i < iterations; ++i)
337 				func();
338 		} else {
339 			atomic_set(&nr_cpus_done, 0);
340 			for (i = cpu_count(); i > 0; i--)
341 				on_cpu_async(i-1, run_test, func);
342 			while (atomic_read(&nr_cpus_done) < cpu_count())
343 				;
344 		}
345 		t2 = rdtsc();
346 	} while ((t2 - t1) < GOAL);
347 	printf("%s %d\n", test->name, (int)((t2 - t1) / iterations));
348 	return test->next;
349 }
350 
351 static void enable_nx(void *junk)
352 {
353 	if (cpuid(0x80000001).d & (1 << 20))
354 		wrmsr(MSR_EFER, rdmsr(MSR_EFER) | EFER_NX_MASK);
355 }
356 
357 bool test_wanted(struct test *test, char *wanted[], int nwanted)
358 {
359 	int i;
360 
361 	if (!nwanted)
362 		return true;
363 
364 	for (i = 0; i < nwanted; ++i)
365 		if (strcmp(wanted[i], test->name) == 0)
366 			return true;
367 
368 	return false;
369 }
370 
371 int main(int ac, char **av)
372 {
373 	struct fadt_descriptor_rev1 *fadt;
374 	int i;
375 	unsigned long membar = 0, base, offset;
376 	void *m;
377 	pcidevaddr_t pcidev;
378 
379 	smp_init();
380 	setup_vm();
381 	nr_cpus = cpu_count();
382 
383 	for (i = cpu_count(); i > 0; i--)
384 		on_cpu(i-1, enable_nx, 0);
385 
386 	fadt = find_acpi_table_addr(FACP_SIGNATURE);
387 	pm_tmr_blk = fadt->pm_tmr_blk;
388 	printf("PM timer port is %x\n", pm_tmr_blk);
389 
390 	pcidev = pci_find_dev(PCI_VENDOR_ID_REDHAT, PCI_DEVICE_ID_REDHAT_TEST);
391 	if (pcidev) {
392 		for (i = 0; i < PCI_TESTDEV_NUM_BARS; i++) {
393 			if (!pci_bar_is_valid(pcidev, i)) {
394 				continue;
395 			}
396 			if (pci_bar_is_memory(pcidev, i)) {
397 				membar = pci_bar_addr(pcidev, i);
398 				base = membar & ~4095;
399 				offset = membar - base;
400 				m = alloc_vpages(1);
401 
402 				install_page((void *)read_cr3(), base, m);
403 				pci_test.memaddr = m + offset;
404 			} else {
405 				pci_test.iobar = pci_bar_addr(pcidev, i);
406 			}
407 		}
408 		printf("pci-testdev at 0x%x membar %lx iobar %x\n",
409 		       pcidev, membar, pci_test.iobar);
410 	}
411 
412 	for (i = 0; i < ARRAY_SIZE(tests); ++i)
413 		if (test_wanted(&tests[i], av + 1, ac - 1))
414 			while (do_test(&tests[i])) {}
415 
416 	return 0;
417 }
418