xref: /kvm-unit-tests/x86/vmexit.c (revision 850479e34fc448ede1909b24a28c58bd68b565c2)
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 static void mov_from_cr8(void)
53 {
54 	unsigned long cr8;
55 
56 	asm volatile ("mov %%cr8, %0" : "=r"(cr8));
57 }
58 
59 static void mov_to_cr8(void)
60 {
61 	unsigned long cr8 = 0;
62 
63 	asm volatile ("mov %0, %%cr8" : : "r"(cr8));
64 }
65 
66 static int is_smp(void)
67 {
68 	return cpu_count() > 1;
69 }
70 
71 static void nop(void *junk)
72 {
73 }
74 
75 static void ipi(void)
76 {
77 	on_cpu(1, nop, 0);
78 }
79 
80 static void ipi_halt(void)
81 {
82 	unsigned long long t;
83 
84 	on_cpu(1, nop, 0);
85 	t = rdtsc() + 2000;
86 	while (rdtsc() < t)
87 		;
88 }
89 
90 static void inl_pmtimer(void)
91 {
92     inl(0xb008);
93 }
94 
95 static struct test {
96 	void (*func)(void);
97 	const char *name;
98 	int (*valid)(void);
99 	int parallel;
100 } tests[] = {
101 	{ cpuid_test, "cpuid", .parallel = 1,  },
102 	{ vmcall, "vmcall", .parallel = 1, },
103 	{ mov_from_cr8, "mov_from_cr8", .parallel = 1, },
104 	{ mov_to_cr8, "mov_to_cr8" , .parallel = 1, },
105 	{ inl_pmtimer, "inl_from_pmtimer", .parallel = 1, },
106 	{ ipi, "ipi", is_smp, .parallel = 0, },
107 	{ ipi_halt, "ipi+halt", is_smp, .parallel = 0, },
108 };
109 
110 unsigned iterations;
111 volatile int nr_cpus_done;
112 
113 static void run_test(void *_func)
114 {
115     int i;
116     void (*func)(void) = _func;
117 
118     for (i = 0; i < iterations; ++i)
119         func();
120 
121     nr_cpus_done++;
122 }
123 
124 static void do_test(struct test *test)
125 {
126 	int i;
127 	unsigned long long t1, t2;
128         void (*func)(void) = test->func;
129 
130         iterations = 32;
131 
132         if (test->valid && !test->valid()) {
133 		printf("%s (skipped)\n", test->name);
134 		return;
135 	}
136 
137 	do {
138 		iterations *= 2;
139 		t1 = rdtsc();
140 
141 		if (!test->parallel) {
142 			for (i = 0; i < iterations; ++i)
143 				func();
144 		} else {
145 			nr_cpus_done = 0;
146 			for (i = cpu_count(); i > 0; i--)
147 				on_cpu_async(i-1, run_test, func);
148 			while (nr_cpus_done < cpu_count())
149 				;
150 		}
151 		t2 = rdtsc();
152 	} while ((t2 - t1) < GOAL);
153 	printf("%s %d\n", test->name, (int)((t2 - t1) / iterations));
154 }
155 
156 static void enable_nx(void *junk)
157 {
158 	wrmsr(MSR_EFER, rdmsr(MSR_EFER) | EFER_NX_MASK);
159 }
160 
161 int main(void)
162 {
163 	int i;
164 
165 	smp_init();
166 
167 	for (i = cpu_count(); i > 0; i--)
168 		on_cpu(i-1, enable_nx, 0);
169 
170 	for (i = 0; i < ARRAY_SIZE(tests); ++i)
171 		do_test(&tests[i]);
172 
173 	return 0;
174 }
175