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