xref: /kvm-unit-tests/x86/vmexit.c (revision 0b2671832293a102bdeac7f7de7afdef7c78ddec)
1 
2 #include "libcflat.h"
3 #include "smp.h"
4 #include "processor.h"
5 
6 static unsigned int inl(unsigned short port)
7 {
8     unsigned int val;
9     asm volatile("inl %w1, %0" : "=a"(val) : "Nd"(port));
10     return val;
11 }
12 
13 #define GOAL (1ull << 30)
14 
15 #ifdef __x86_64__
16 #  define R "r"
17 #else
18 #  define R "e"
19 #endif
20 
21 static void cpuid_test(void)
22 {
23 	asm volatile ("push %%"R "bx; cpuid; pop %%"R "bx"
24 		      : : : "eax", "ecx", "edx");
25 }
26 
27 static void vmcall(void)
28 {
29 	unsigned long a = 0, b, c, d;
30 
31 	asm volatile ("vmcall" : "+a"(a), "=b"(b), "=c"(c), "=d"(d));
32 }
33 
34 #define MSR_EFER 0xc0000080
35 #define EFER_NX_MASK            (1ull << 11)
36 
37 #ifdef __x86_64__
38 static void mov_from_cr8(void)
39 {
40 	unsigned long cr8;
41 
42 	asm volatile ("mov %%cr8, %0" : "=r"(cr8));
43 }
44 
45 static void mov_to_cr8(void)
46 {
47 	unsigned long cr8 = 0;
48 
49 	asm volatile ("mov %0, %%cr8" : : "r"(cr8));
50 }
51 #endif
52 
53 static int is_smp(void)
54 {
55 	return cpu_count() > 1;
56 }
57 
58 static void nop(void *junk)
59 {
60 }
61 
62 static void ipi(void)
63 {
64 	on_cpu(1, nop, 0);
65 }
66 
67 static void ipi_halt(void)
68 {
69 	unsigned long long t;
70 
71 	on_cpu(1, nop, 0);
72 	t = rdtsc() + 2000;
73 	while (rdtsc() < t)
74 		;
75 }
76 
77 static void inl_pmtimer(void)
78 {
79     inl(0xb008);
80 }
81 
82 static struct test {
83 	void (*func)(void);
84 	const char *name;
85 	int (*valid)(void);
86 	int parallel;
87 } tests[] = {
88 	{ cpuid_test, "cpuid", .parallel = 1,  },
89 	{ vmcall, "vmcall", .parallel = 1, },
90 #ifdef __x86_64__
91 	{ mov_from_cr8, "mov_from_cr8", .parallel = 1, },
92 	{ mov_to_cr8, "mov_to_cr8" , .parallel = 1, },
93 #endif
94 	{ inl_pmtimer, "inl_from_pmtimer", .parallel = 1, },
95 	{ ipi, "ipi", is_smp, .parallel = 0, },
96 	{ ipi_halt, "ipi+halt", is_smp, .parallel = 0, },
97 };
98 
99 unsigned iterations;
100 volatile int nr_cpus_done;
101 
102 static void run_test(void *_func)
103 {
104     int i;
105     void (*func)(void) = _func;
106 
107     for (i = 0; i < iterations; ++i)
108         func();
109 
110     nr_cpus_done++;
111 }
112 
113 static void do_test(struct test *test)
114 {
115 	int i;
116 	unsigned long long t1, t2;
117         void (*func)(void) = test->func;
118 
119         iterations = 32;
120 
121         if (test->valid && !test->valid()) {
122 		printf("%s (skipped)\n", test->name);
123 		return;
124 	}
125 
126 	do {
127 		iterations *= 2;
128 		t1 = rdtsc();
129 
130 		if (!test->parallel) {
131 			for (i = 0; i < iterations; ++i)
132 				func();
133 		} else {
134 			nr_cpus_done = 0;
135 			for (i = cpu_count(); i > 0; i--)
136 				on_cpu_async(i-1, run_test, func);
137 			while (nr_cpus_done < cpu_count())
138 				;
139 		}
140 		t2 = rdtsc();
141 	} while ((t2 - t1) < GOAL);
142 	printf("%s %d\n", test->name, (int)((t2 - t1) / iterations));
143 }
144 
145 static void enable_nx(void *junk)
146 {
147 	if (cpuid(0x80000001).d & (1 << 20))
148 		wrmsr(MSR_EFER, rdmsr(MSR_EFER) | EFER_NX_MASK);
149 }
150 
151 bool test_wanted(struct test *test, char *wanted[], int nwanted)
152 {
153 	int i;
154 
155 	if (!nwanted)
156 		return true;
157 
158 	for (i = 0; i < nwanted; ++i)
159 		if (strcmp(wanted[i], test->name) == 0)
160 			return true;
161 
162 	return false;
163 }
164 
165 int main(int ac, char **av)
166 {
167 	int i;
168 
169 	smp_init();
170 
171 	for (i = cpu_count(); i > 0; i--)
172 		on_cpu(i-1, enable_nx, 0);
173 
174 	for (i = 0; i < ARRAY_SIZE(tests); ++i)
175 		if (test_wanted(&tests[i], av + 1, ac - 1))
176 			do_test(&tests[i]);
177 
178 	return 0;
179 }
180