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