xref: /kvm-unit-tests/x86/svm.c (revision 5b70cbdb7bc2ea65096b51565c75815cc95945b8)
1 /*
2  * Framework for testing nested virtualization
3  */
4 
5 #include "svm.h"
6 #include "libcflat.h"
7 #include "processor.h"
8 #include "desc.h"
9 #include "msr.h"
10 #include "vm.h"
11 #include "smp.h"
12 #include "types.h"
13 #include "alloc_page.h"
14 #include "isr.h"
15 #include "apic.h"
16 
17 /* for the nested page table*/
18 u64 *pte[2048];
19 u64 *pde[4];
20 u64 *pdpe;
21 u64 *pml4e;
22 
23 struct vmcb *vmcb;
24 
25 u64 *npt_get_pte(u64 address)
26 {
27 	int i1, i2;
28 
29 	address >>= 12;
30 	i1 = (address >> 9) & 0x7ff;
31 	i2 = address & 0x1ff;
32 
33 	return &pte[i1][i2];
34 }
35 
36 u64 *npt_get_pde(u64 address)
37 {
38 	int i1, i2;
39 
40 	address >>= 21;
41 	i1 = (address >> 9) & 0x3;
42 	i2 = address & 0x1ff;
43 
44 	return &pde[i1][i2];
45 }
46 
47 u64 *npt_get_pdpe(void)
48 {
49 	return pdpe;
50 }
51 
52 bool smp_supported(void)
53 {
54 	return cpu_count() > 1;
55 }
56 
57 bool default_supported(void)
58 {
59     return true;
60 }
61 
62 void default_prepare(struct svm_test *test)
63 {
64 	vmcb_ident(vmcb);
65 }
66 
67 void default_prepare_gif_clear(struct svm_test *test)
68 {
69 }
70 
71 bool default_finished(struct svm_test *test)
72 {
73 	return true; /* one vmexit */
74 }
75 
76 bool npt_supported(void)
77 {
78 	return this_cpu_has(X86_FEATURE_NPT);
79 }
80 
81 int get_test_stage(struct svm_test *test)
82 {
83 	barrier();
84 	return test->scratch;
85 }
86 
87 void set_test_stage(struct svm_test *test, int s)
88 {
89 	barrier();
90 	test->scratch = s;
91 	barrier();
92 }
93 
94 void inc_test_stage(struct svm_test *test)
95 {
96 	barrier();
97 	test->scratch++;
98 	barrier();
99 }
100 
101 static void vmcb_set_seg(struct vmcb_seg *seg, u16 selector,
102                          u64 base, u32 limit, u32 attr)
103 {
104 	seg->selector = selector;
105 	seg->attrib = attr;
106 	seg->limit = limit;
107 	seg->base = base;
108 }
109 
110 inline void vmmcall(void)
111 {
112 	asm volatile ("vmmcall" : : : "memory");
113 }
114 
115 static test_guest_func guest_main;
116 
117 void test_set_guest(test_guest_func func)
118 {
119 	guest_main = func;
120 }
121 
122 static void test_thunk(struct svm_test *test)
123 {
124 	guest_main(test);
125 	vmmcall();
126 }
127 
128 u8 *io_bitmap;
129 u8 io_bitmap_area[16384];
130 
131 u8 *msr_bitmap;
132 u8 msr_bitmap_area[MSR_BITMAP_SIZE + PAGE_SIZE];
133 
134 void vmcb_ident(struct vmcb *vmcb)
135 {
136 	u64 vmcb_phys = virt_to_phys(vmcb);
137 	struct vmcb_save_area *save = &vmcb->save;
138 	struct vmcb_control_area *ctrl = &vmcb->control;
139 	u32 data_seg_attr = 3 | SVM_SELECTOR_S_MASK | SVM_SELECTOR_P_MASK
140 	    | SVM_SELECTOR_DB_MASK | SVM_SELECTOR_G_MASK;
141 	u32 code_seg_attr = 9 | SVM_SELECTOR_S_MASK | SVM_SELECTOR_P_MASK
142 	    | SVM_SELECTOR_L_MASK | SVM_SELECTOR_G_MASK;
143 	struct descriptor_table_ptr desc_table_ptr;
144 
145 	memset(vmcb, 0, sizeof(*vmcb));
146 	asm volatile ("vmsave %0" : : "a"(vmcb_phys) : "memory");
147 	vmcb_set_seg(&save->es, read_es(), 0, -1U, data_seg_attr);
148 	vmcb_set_seg(&save->cs, read_cs(), 0, -1U, code_seg_attr);
149 	vmcb_set_seg(&save->ss, read_ss(), 0, -1U, data_seg_attr);
150 	vmcb_set_seg(&save->ds, read_ds(), 0, -1U, data_seg_attr);
151 	sgdt(&desc_table_ptr);
152 	vmcb_set_seg(&save->gdtr, 0, desc_table_ptr.base, desc_table_ptr.limit, 0);
153 	sidt(&desc_table_ptr);
154 	vmcb_set_seg(&save->idtr, 0, desc_table_ptr.base, desc_table_ptr.limit, 0);
155 	ctrl->asid = 1;
156 	save->cpl = 0;
157 	save->efer = rdmsr(MSR_EFER);
158 	save->cr4 = read_cr4();
159 	save->cr3 = read_cr3();
160 	save->cr0 = read_cr0();
161 	save->dr7 = read_dr7();
162 	save->dr6 = read_dr6();
163 	save->cr2 = read_cr2();
164 	save->g_pat = rdmsr(MSR_IA32_CR_PAT);
165 	save->dbgctl = rdmsr(MSR_IA32_DEBUGCTLMSR);
166 	ctrl->intercept = (1ULL << INTERCEPT_VMRUN) | (1ULL << INTERCEPT_VMMCALL);
167 	ctrl->iopm_base_pa = virt_to_phys(io_bitmap);
168 	ctrl->msrpm_base_pa = virt_to_phys(msr_bitmap);
169 
170 	if (npt_supported()) {
171 		ctrl->nested_ctl = 1;
172 		ctrl->nested_cr3 = (u64)pml4e;
173 	}
174 }
175 
176 struct regs regs;
177 
178 struct regs get_regs(void)
179 {
180 	return regs;
181 }
182 
183 // rax handled specially below
184 
185 #define SAVE_GPR_C                              \
186         "xchg %%rbx, regs+0x8\n\t"              \
187         "xchg %%rcx, regs+0x10\n\t"             \
188         "xchg %%rdx, regs+0x18\n\t"             \
189         "xchg %%rbp, regs+0x28\n\t"             \
190         "xchg %%rsi, regs+0x30\n\t"             \
191         "xchg %%rdi, regs+0x38\n\t"             \
192         "xchg %%r8, regs+0x40\n\t"              \
193         "xchg %%r9, regs+0x48\n\t"              \
194         "xchg %%r10, regs+0x50\n\t"             \
195         "xchg %%r11, regs+0x58\n\t"             \
196         "xchg %%r12, regs+0x60\n\t"             \
197         "xchg %%r13, regs+0x68\n\t"             \
198         "xchg %%r14, regs+0x70\n\t"             \
199         "xchg %%r15, regs+0x78\n\t"
200 
201 #define LOAD_GPR_C      SAVE_GPR_C
202 
203 struct svm_test *v2_test;
204 struct vmcb *vmcb;
205 
206 #define ASM_VMRUN_CMD                           \
207                 "vmload %%rax\n\t"              \
208                 "mov regs+0x80, %%r15\n\t"      \
209                 "mov %%r15, 0x170(%%rax)\n\t"   \
210                 "mov regs, %%r15\n\t"           \
211                 "mov %%r15, 0x1f8(%%rax)\n\t"   \
212                 LOAD_GPR_C                      \
213                 "vmrun %%rax\n\t"               \
214                 SAVE_GPR_C                      \
215                 "mov 0x170(%%rax), %%r15\n\t"   \
216                 "mov %%r15, regs+0x80\n\t"      \
217                 "mov 0x1f8(%%rax), %%r15\n\t"   \
218                 "mov %%r15, regs\n\t"           \
219                 "vmsave %%rax\n\t"              \
220 
221 u64 guest_stack[10000];
222 
223 int svm_vmrun(void)
224 {
225 	vmcb->save.rip = (ulong)test_thunk;
226 	vmcb->save.rsp = (ulong)(guest_stack + ARRAY_SIZE(guest_stack));
227 	regs.rdi = (ulong)v2_test;
228 
229 	asm volatile (
230 		ASM_VMRUN_CMD
231 		:
232 		: "a" (virt_to_phys(vmcb))
233 		: "memory");
234 
235 	return (vmcb->control.exit_code);
236 }
237 
238 static void test_run(struct svm_test *test)
239 {
240 	u64 vmcb_phys = virt_to_phys(vmcb);
241 
242 	irq_disable();
243 	test->prepare(test);
244 	guest_main = test->guest_func;
245 	vmcb->save.rip = (ulong)test_thunk;
246 	vmcb->save.rsp = (ulong)(guest_stack + ARRAY_SIZE(guest_stack));
247 	regs.rdi = (ulong)test;
248 	do {
249 		struct svm_test *the_test = test;
250 		u64 the_vmcb = vmcb_phys;
251 		asm volatile (
252 			"clgi;\n\t" // semi-colon needed for LLVM compatibility
253 			"sti \n\t"
254 			"call *%c[PREPARE_GIF_CLEAR](%[test]) \n \t"
255 			"mov %[vmcb_phys], %%rax \n\t"
256 			ASM_VMRUN_CMD
257 			"cli \n\t"
258 			"stgi"
259 			: // inputs clobbered by the guest:
260 			"=D" (the_test),            // first argument register
261 			"=b" (the_vmcb)             // callee save register!
262 			: [test] "0" (the_test),
263 			[vmcb_phys] "1"(the_vmcb),
264 			[PREPARE_GIF_CLEAR] "i" (offsetof(struct svm_test, prepare_gif_clear))
265 			: "rax", "rcx", "rdx", "rsi",
266 			"r8", "r9", "r10", "r11" , "r12", "r13", "r14", "r15",
267 			"memory");
268 		++test->exits;
269 	} while (!test->finished(test));
270 	irq_enable();
271 
272 	report(test->succeeded(test), "%s", test->name);
273 }
274 
275 static void setup_svm(void)
276 {
277 	void *hsave = alloc_page();
278 	u64 *page, address;
279 	int i,j;
280 
281 	wrmsr(MSR_VM_HSAVE_PA, virt_to_phys(hsave));
282 	wrmsr(MSR_EFER, rdmsr(MSR_EFER) | EFER_SVME);
283 	wrmsr(MSR_EFER, rdmsr(MSR_EFER) | EFER_NX);
284 
285 	io_bitmap = (void *) (((ulong)io_bitmap_area + 4095) & ~4095);
286 
287 	msr_bitmap = (void *) ALIGN((ulong)msr_bitmap_area, PAGE_SIZE);
288 
289 	if (!npt_supported())
290 		return;
291 
292 	printf("NPT detected - running all tests with NPT enabled\n");
293 
294 	/*
295 	* Nested paging supported - Build a nested page table
296 	* Build the page-table bottom-up and map everything with 4k
297 	* pages to get enough granularity for the NPT unit-tests.
298 	*/
299 
300 	address = 0;
301 
302 	/* PTE level */
303 	for (i = 0; i < 2048; ++i) {
304 		page = alloc_page();
305 
306 		for (j = 0; j < 512; ++j, address += 4096)
307 	    		page[j] = address | 0x067ULL;
308 
309 		pte[i] = page;
310 	}
311 
312 	/* PDE level */
313 	for (i = 0; i < 4; ++i) {
314 		page = alloc_page();
315 
316 	for (j = 0; j < 512; ++j)
317 	    page[j] = (u64)pte[(i * 512) + j] | 0x027ULL;
318 
319 		pde[i] = page;
320 	}
321 
322 	/* PDPe level */
323 	pdpe   = alloc_page();
324 	for (i = 0; i < 4; ++i)
325 		pdpe[i] = ((u64)(pde[i])) | 0x27;
326 
327 	/* PML4e level */
328 	pml4e    = alloc_page();
329 	pml4e[0] = ((u64)pdpe) | 0x27;
330 }
331 
332 int matched;
333 
334 static bool
335 test_wanted(const char *name, char *filters[], int filter_count)
336 {
337         int i;
338         bool positive = false;
339         bool match = false;
340         char clean_name[strlen(name) + 1];
341         char *c;
342         const char *n;
343 
344         /* Replace spaces with underscores. */
345         n = name;
346         c = &clean_name[0];
347         do *c++ = (*n == ' ') ? '_' : *n;
348         while (*n++);
349 
350         for (i = 0; i < filter_count; i++) {
351                 const char *filter = filters[i];
352 
353                 if (filter[0] == '-') {
354                         if (simple_glob(clean_name, filter + 1))
355                                 return false;
356                 } else {
357                         positive = true;
358                         match |= simple_glob(clean_name, filter);
359                 }
360         }
361 
362         if (!positive || match) {
363                 matched++;
364                 return true;
365         } else {
366                 return false;
367         }
368 }
369 
370 int main(int ac, char **av)
371 {
372 	int i = 0;
373 
374 	ac--;
375 	av++;
376 
377 	setup_vm();
378 
379 	if (!this_cpu_has(X86_FEATURE_SVM)) {
380 		printf("SVM not availble\n");
381 		return report_summary();
382 	}
383 
384 	setup_svm();
385 
386 	vmcb = alloc_page();
387 
388 	for (; svm_tests[i].name != NULL; i++) {
389 		if (!test_wanted(svm_tests[i].name, av, ac))
390 			continue;
391 		if (svm_tests[i].supported && !svm_tests[i].supported())
392 			continue;
393 		if (svm_tests[i].v2 == NULL) {
394 			test_run(&svm_tests[i]);
395 		} else {
396 			vmcb_ident(vmcb);
397 			v2_test = &(svm_tests[i]);
398 			svm_tests[i].v2();
399 		}
400 	}
401 
402 	if (!matched)
403 		report(matched, "command line didn't match any tests!");
404 
405 	return report_summary();
406 }
407