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