xref: /kvm-unit-tests/x86/vmexit.c (revision eda71b28fa122203e316483b35f37aaacd42f545)
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 static int nr_cpus;
16 
17 #ifdef __x86_64__
18 #  define R "r"
19 #else
20 #  define R "e"
21 #endif
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_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 static void inl_pmtimer(void)
80 {
81     inl(0xb008);
82 }
83 
84 static void ple_round_robin(void)
85 {
86 	struct counter {
87 		volatile int n1;
88 		int n2;
89 	} __attribute__((aligned(64)));
90 	static struct counter counters[64] = { { -1, 0 } };
91 	int me = smp_id();
92 	int you;
93 	volatile struct counter *p = &counters[me];
94 
95 	while (p->n1 == p->n2)
96 		asm volatile ("pause");
97 
98 	p->n2 = p->n1;
99 	you = me + 1;
100 	if (you == nr_cpus)
101 		you = 0;
102 	++counters[you].n1;
103 }
104 
105 static struct test {
106 	void (*func)(void);
107 	const char *name;
108 	int (*valid)(void);
109 	int parallel;
110 } tests[] = {
111 	{ cpuid_test, "cpuid", .parallel = 1,  },
112 	{ vmcall, "vmcall", .parallel = 1, },
113 #ifdef __x86_64__
114 	{ mov_from_cr8, "mov_from_cr8", .parallel = 1, },
115 	{ mov_to_cr8, "mov_to_cr8" , .parallel = 1, },
116 #endif
117 	{ inl_pmtimer, "inl_from_pmtimer", .parallel = 1, },
118 	{ ipi, "ipi", is_smp, .parallel = 0, },
119 	{ ipi_halt, "ipi+halt", is_smp, .parallel = 0, },
120 	{ ple_round_robin, "ple-round-robin", .parallel = 1 },
121 };
122 
123 unsigned iterations;
124 volatile int nr_cpus_done;
125 
126 static void run_test(void *_func)
127 {
128     int i;
129     void (*func)(void) = _func;
130 
131     for (i = 0; i < iterations; ++i)
132         func();
133 
134     nr_cpus_done++;
135 }
136 
137 static void do_test(struct test *test)
138 {
139 	int i;
140 	unsigned long long t1, t2;
141         void (*func)(void) = test->func;
142 
143         iterations = 32;
144 
145         if (test->valid && !test->valid()) {
146 		printf("%s (skipped)\n", test->name);
147 		return;
148 	}
149 
150 	do {
151 		iterations *= 2;
152 		t1 = rdtsc();
153 
154 		if (!test->parallel) {
155 			for (i = 0; i < iterations; ++i)
156 				func();
157 		} else {
158 			nr_cpus_done = 0;
159 			for (i = cpu_count(); i > 0; i--)
160 				on_cpu_async(i-1, run_test, func);
161 			while (nr_cpus_done < cpu_count())
162 				;
163 		}
164 		t2 = rdtsc();
165 	} while ((t2 - t1) < GOAL);
166 	printf("%s %d\n", test->name, (int)((t2 - t1) / iterations));
167 }
168 
169 static void enable_nx(void *junk)
170 {
171 	if (cpuid(0x80000001).d & (1 << 20))
172 		wrmsr(MSR_EFER, rdmsr(MSR_EFER) | EFER_NX_MASK);
173 }
174 
175 bool test_wanted(struct test *test, char *wanted[], int nwanted)
176 {
177 	int i;
178 
179 	if (!nwanted)
180 		return true;
181 
182 	for (i = 0; i < nwanted; ++i)
183 		if (strcmp(wanted[i], test->name) == 0)
184 			return true;
185 
186 	return false;
187 }
188 
189 int main(int ac, char **av)
190 {
191 	int i;
192 
193 	smp_init();
194 	nr_cpus = cpu_count();
195 
196 	for (i = cpu_count(); i > 0; i--)
197 		on_cpu(i-1, enable_nx, 0);
198 
199 	for (i = 0; i < ARRAY_SIZE(tests); ++i)
200 		if (test_wanted(&tests[i], av + 1, ac - 1))
201 			do_test(&tests[i]);
202 
203 	return 0;
204 }
205