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