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