xref: /kvm-unit-tests/x86/svm.c (revision ad879127eb86fb60ab2da0e529fe4daf7943f4db)
1*ad879127SKrish Sadhukhan /*
2*ad879127SKrish Sadhukhan  * Framework for testing nested virtualization
3*ad879127SKrish Sadhukhan  */
4*ad879127SKrish Sadhukhan 
57d36db35SAvi Kivity #include "svm.h"
67d36db35SAvi Kivity #include "libcflat.h"
77d36db35SAvi Kivity #include "processor.h"
8b46094b4SPaolo Bonzini #include "desc.h"
97d36db35SAvi Kivity #include "msr.h"
107d36db35SAvi Kivity #include "vm.h"
117d36db35SAvi Kivity #include "smp.h"
127d36db35SAvi Kivity #include "types.h"
135aca024eSPaolo Bonzini #include "alloc_page.h"
14306bb7dbSCathy Avery #include "isr.h"
15306bb7dbSCathy Avery #include "apic.h"
167d36db35SAvi Kivity 
171535bf0fSJoerg Roedel /* for the nested page table*/
181535bf0fSJoerg Roedel u64 *pte[2048];
19*ad879127SKrish Sadhukhan u64 *pde[4];
20*ad879127SKrish Sadhukhan u64 *pdpe;
21*ad879127SKrish Sadhukhan u64 *pml4e;
221535bf0fSJoerg Roedel 
23*ad879127SKrish Sadhukhan u64 *npt_get_pte(u64 address)
241535bf0fSJoerg Roedel {
25*ad879127SKrish Sadhukhan 	int i1, i2;
26*ad879127SKrish Sadhukhan 
27*ad879127SKrish Sadhukhan 	address >>= 12;
28*ad879127SKrish Sadhukhan 	i1 = (address >> 9) & 0x7ff;
29*ad879127SKrish Sadhukhan 	i2 = address & 0x1ff;
30*ad879127SKrish Sadhukhan 
31*ad879127SKrish Sadhukhan 	return &pte[i1][i2];
321535bf0fSJoerg Roedel }
331535bf0fSJoerg Roedel 
34*ad879127SKrish Sadhukhan u64 *npt_get_pde(u64 address)
35f6a2ca45SPaolo Bonzini {
36f6a2ca45SPaolo Bonzini 	int i1, i2;
37f6a2ca45SPaolo Bonzini 
38f6a2ca45SPaolo Bonzini 	address >>= 21;
39f6a2ca45SPaolo Bonzini 	i1 = (address >> 9) & 0x3;
40f6a2ca45SPaolo Bonzini 	i2 = address & 0x1ff;
41f6a2ca45SPaolo Bonzini 
42f6a2ca45SPaolo Bonzini 	return &pde[i1][i2];
43f6a2ca45SPaolo Bonzini }
44f6a2ca45SPaolo Bonzini 
45*ad879127SKrish Sadhukhan u64 *npt_get_pdpe(void)
468594b943SJoerg Roedel {
47*ad879127SKrish Sadhukhan 	return pdpe;
48*ad879127SKrish Sadhukhan }
498594b943SJoerg Roedel 
50*ad879127SKrish Sadhukhan bool smp_supported(void)
51*ad879127SKrish Sadhukhan {
52*ad879127SKrish Sadhukhan 	return cpu_count() > 1;
53*ad879127SKrish Sadhukhan }
548594b943SJoerg Roedel 
55*ad879127SKrish Sadhukhan bool default_supported(void)
56*ad879127SKrish Sadhukhan {
57*ad879127SKrish Sadhukhan     return true;
58*ad879127SKrish Sadhukhan }
59*ad879127SKrish Sadhukhan 
60*ad879127SKrish Sadhukhan void default_prepare(struct svm_test *test)
61*ad879127SKrish Sadhukhan {
62*ad879127SKrish Sadhukhan 	vmcb_ident(test->vmcb);
63*ad879127SKrish Sadhukhan }
64*ad879127SKrish Sadhukhan 
65*ad879127SKrish Sadhukhan void default_prepare_gif_clear(struct svm_test *test)
66*ad879127SKrish Sadhukhan {
67*ad879127SKrish Sadhukhan }
68*ad879127SKrish Sadhukhan 
69*ad879127SKrish Sadhukhan bool default_finished(struct svm_test *test)
70*ad879127SKrish Sadhukhan {
71*ad879127SKrish Sadhukhan 	return true; /* one vmexit */
72*ad879127SKrish Sadhukhan }
73*ad879127SKrish Sadhukhan 
74*ad879127SKrish Sadhukhan bool npt_supported(void)
75*ad879127SKrish Sadhukhan {
76*ad879127SKrish Sadhukhan 	return this_cpu_has(X86_FEATURE_NPT);
77*ad879127SKrish Sadhukhan }
78*ad879127SKrish Sadhukhan 
79*ad879127SKrish Sadhukhan int get_test_stage(struct svm_test *test)
80*ad879127SKrish Sadhukhan {
81*ad879127SKrish Sadhukhan 	barrier();
82*ad879127SKrish Sadhukhan 	return test->scratch;
83*ad879127SKrish Sadhukhan }
84*ad879127SKrish Sadhukhan 
85*ad879127SKrish Sadhukhan void set_test_stage(struct svm_test *test, int s)
86*ad879127SKrish Sadhukhan {
87*ad879127SKrish Sadhukhan 	barrier();
88*ad879127SKrish Sadhukhan 	test->scratch = s;
89*ad879127SKrish Sadhukhan 	barrier();
90*ad879127SKrish Sadhukhan }
91*ad879127SKrish Sadhukhan 
92*ad879127SKrish Sadhukhan void inc_test_stage(struct svm_test *test)
93*ad879127SKrish Sadhukhan {
94*ad879127SKrish Sadhukhan 	barrier();
95*ad879127SKrish Sadhukhan 	test->scratch++;
96*ad879127SKrish Sadhukhan 	barrier();
978594b943SJoerg Roedel }
988594b943SJoerg Roedel 
997d36db35SAvi Kivity static void vmcb_set_seg(struct vmcb_seg *seg, u16 selector,
1007d36db35SAvi Kivity                          u64 base, u32 limit, u32 attr)
1017d36db35SAvi Kivity {
1027d36db35SAvi Kivity 	seg->selector = selector;
1037d36db35SAvi Kivity 	seg->attrib = attr;
1047d36db35SAvi Kivity 	seg->limit = limit;
1057d36db35SAvi Kivity 	seg->base = base;
1067d36db35SAvi Kivity }
1077d36db35SAvi Kivity 
108*ad879127SKrish Sadhukhan inline void vmmcall(void)
109*ad879127SKrish Sadhukhan {
110*ad879127SKrish Sadhukhan 	asm volatile ("vmmcall" : : : "memory");
111*ad879127SKrish Sadhukhan }
112*ad879127SKrish Sadhukhan 
113*ad879127SKrish Sadhukhan static void test_thunk(struct svm_test *test)
114*ad879127SKrish Sadhukhan {
115*ad879127SKrish Sadhukhan 	test->guest_func(test);
116*ad879127SKrish Sadhukhan 	vmmcall();
117*ad879127SKrish Sadhukhan }
118*ad879127SKrish Sadhukhan 
119*ad879127SKrish Sadhukhan u8 *io_bitmap;
120*ad879127SKrish Sadhukhan u8 io_bitmap_area[16384];
121*ad879127SKrish Sadhukhan 
122*ad879127SKrish Sadhukhan u8 *msr_bitmap;
123*ad879127SKrish Sadhukhan u8 msr_bitmap_area[MSR_BITMAP_SIZE + PAGE_SIZE];
124*ad879127SKrish Sadhukhan 
125*ad879127SKrish Sadhukhan void vmcb_ident(struct vmcb *vmcb)
1267d36db35SAvi Kivity {
1277d36db35SAvi Kivity 	u64 vmcb_phys = virt_to_phys(vmcb);
1287d36db35SAvi Kivity 	struct vmcb_save_area *save = &vmcb->save;
1297d36db35SAvi Kivity 	struct vmcb_control_area *ctrl = &vmcb->control;
1307d36db35SAvi Kivity 	u32 data_seg_attr = 3 | SVM_SELECTOR_S_MASK | SVM_SELECTOR_P_MASK
1317d36db35SAvi Kivity 	    | SVM_SELECTOR_DB_MASK | SVM_SELECTOR_G_MASK;
1327d36db35SAvi Kivity 	u32 code_seg_attr = 9 | SVM_SELECTOR_S_MASK | SVM_SELECTOR_P_MASK
1337d36db35SAvi Kivity 	    | SVM_SELECTOR_L_MASK | SVM_SELECTOR_G_MASK;
1347d36db35SAvi Kivity 	struct descriptor_table_ptr desc_table_ptr;
1357d36db35SAvi Kivity 
1367d36db35SAvi Kivity 	memset(vmcb, 0, sizeof(*vmcb));
1372c6589bcSPeter Shier 	asm volatile ("vmsave %0" : : "a"(vmcb_phys) : "memory");
1387d36db35SAvi Kivity 	vmcb_set_seg(&save->es, read_es(), 0, -1U, data_seg_attr);
1397d36db35SAvi Kivity 	vmcb_set_seg(&save->cs, read_cs(), 0, -1U, code_seg_attr);
1407d36db35SAvi Kivity 	vmcb_set_seg(&save->ss, read_ss(), 0, -1U, data_seg_attr);
1417d36db35SAvi Kivity 	vmcb_set_seg(&save->ds, read_ds(), 0, -1U, data_seg_attr);
1427d36db35SAvi Kivity 	sgdt(&desc_table_ptr);
1437d36db35SAvi Kivity 	vmcb_set_seg(&save->gdtr, 0, desc_table_ptr.base, desc_table_ptr.limit, 0);
1447d36db35SAvi Kivity 	sidt(&desc_table_ptr);
1457d36db35SAvi Kivity 	vmcb_set_seg(&save->idtr, 0, desc_table_ptr.base, desc_table_ptr.limit, 0);
1467d36db35SAvi Kivity 	ctrl->asid = 1;
1477d36db35SAvi Kivity 	save->cpl = 0;
1487d36db35SAvi Kivity 	save->efer = rdmsr(MSR_EFER);
1497d36db35SAvi Kivity 	save->cr4 = read_cr4();
1507d36db35SAvi Kivity 	save->cr3 = read_cr3();
1517d36db35SAvi Kivity 	save->cr0 = read_cr0();
1527d36db35SAvi Kivity 	save->dr7 = read_dr7();
1537d36db35SAvi Kivity 	save->dr6 = read_dr6();
1547d36db35SAvi Kivity 	save->cr2 = read_cr2();
1557d36db35SAvi Kivity 	save->g_pat = rdmsr(MSR_IA32_CR_PAT);
1567d36db35SAvi Kivity 	save->dbgctl = rdmsr(MSR_IA32_DEBUGCTLMSR);
1577d36db35SAvi Kivity 	ctrl->intercept = (1ULL << INTERCEPT_VMRUN) | (1ULL << INTERCEPT_VMMCALL);
1583d46571bSPaolo Bonzini 	ctrl->iopm_base_pa = virt_to_phys(io_bitmap);
15906a8c023STambe, William 	ctrl->msrpm_base_pa = virt_to_phys(msr_bitmap);
1601535bf0fSJoerg Roedel 
1611535bf0fSJoerg Roedel 	if (npt_supported()) {
1621535bf0fSJoerg Roedel 		ctrl->nested_ctl = 1;
1631535bf0fSJoerg Roedel 		ctrl->nested_cr3 = (u64)pml4e;
1641535bf0fSJoerg Roedel 	}
1657d36db35SAvi Kivity }
1667d36db35SAvi Kivity 
167a43baea0SPaolo Bonzini struct regs regs;
168a43baea0SPaolo Bonzini 
169*ad879127SKrish Sadhukhan struct regs get_regs(void)
170*ad879127SKrish Sadhukhan {
171*ad879127SKrish Sadhukhan 	return regs;
172*ad879127SKrish Sadhukhan }
173*ad879127SKrish Sadhukhan 
174a43baea0SPaolo Bonzini // rax handled specially below
175a43baea0SPaolo Bonzini 
176a43baea0SPaolo Bonzini #define SAVE_GPR_C                              \
177a43baea0SPaolo Bonzini         "xchg %%rbx, regs+0x8\n\t"              \
178a43baea0SPaolo Bonzini         "xchg %%rcx, regs+0x10\n\t"             \
179a43baea0SPaolo Bonzini         "xchg %%rdx, regs+0x18\n\t"             \
180a43baea0SPaolo Bonzini         "xchg %%rbp, regs+0x28\n\t"             \
181a43baea0SPaolo Bonzini         "xchg %%rsi, regs+0x30\n\t"             \
182a43baea0SPaolo Bonzini         "xchg %%rdi, regs+0x38\n\t"             \
183a43baea0SPaolo Bonzini         "xchg %%r8, regs+0x40\n\t"              \
184a43baea0SPaolo Bonzini         "xchg %%r9, regs+0x48\n\t"              \
185a43baea0SPaolo Bonzini         "xchg %%r10, regs+0x50\n\t"             \
186a43baea0SPaolo Bonzini         "xchg %%r11, regs+0x58\n\t"             \
187a43baea0SPaolo Bonzini         "xchg %%r12, regs+0x60\n\t"             \
188a43baea0SPaolo Bonzini         "xchg %%r13, regs+0x68\n\t"             \
189a43baea0SPaolo Bonzini         "xchg %%r14, regs+0x70\n\t"             \
190a43baea0SPaolo Bonzini         "xchg %%r15, regs+0x78\n\t"
191a43baea0SPaolo Bonzini 
192a43baea0SPaolo Bonzini #define LOAD_GPR_C      SAVE_GPR_C
193a43baea0SPaolo Bonzini 
194*ad879127SKrish Sadhukhan static void test_run(struct svm_test *test, struct vmcb *vmcb)
1957d36db35SAvi Kivity {
1967d36db35SAvi Kivity 	u64 vmcb_phys = virt_to_phys(vmcb);
1977d36db35SAvi Kivity 	u64 guest_stack[10000];
1987d36db35SAvi Kivity 
1991500aca4SPaolo Bonzini 	irq_disable();
2007d36db35SAvi Kivity 	test->vmcb = vmcb;
2017d36db35SAvi Kivity 	test->prepare(test);
2027d36db35SAvi Kivity 	vmcb->save.rip = (ulong)test_thunk;
2037d36db35SAvi Kivity 	vmcb->save.rsp = (ulong)(guest_stack + ARRAY_SIZE(guest_stack));
204a43baea0SPaolo Bonzini 	regs.rdi = (ulong)test;
2057d36db35SAvi Kivity 	do {
206*ad879127SKrish Sadhukhan 		struct svm_test *the_test = test;
207e7bce343SPaolo Bonzini 		u64 the_vmcb = vmcb_phys;
2087d36db35SAvi Kivity 		asm volatile (
2092c6589bcSPeter Shier 			"clgi;\n\t" // semi-colon needed for LLVM compatibility
2102e7dd780SCathy Avery 			"sti \n\t"
211e7bce343SPaolo Bonzini 			"call *%c[PREPARE_GIF_CLEAR](%[test]) \n \t"
212e7bce343SPaolo Bonzini 			"mov %[vmcb_phys], %%rax \n\t"
213e7bce343SPaolo Bonzini 			"vmload %%rax\n\t"
214a43baea0SPaolo Bonzini 			"mov regs+0x80, %%r15\n\t"  // rflags
215e7bce343SPaolo Bonzini 			"mov %%r15, 0x170(%%rax)\n\t"
216a43baea0SPaolo Bonzini 			"mov regs, %%r15\n\t"       // rax
217e7bce343SPaolo Bonzini 			"mov %%r15, 0x1f8(%%rax)\n\t"
218a43baea0SPaolo Bonzini 			LOAD_GPR_C
219e7bce343SPaolo Bonzini 			"vmrun %%rax\n\t"
220a43baea0SPaolo Bonzini 			SAVE_GPR_C
221e7bce343SPaolo Bonzini 			"mov 0x170(%%rax), %%r15\n\t"  // rflags
222a43baea0SPaolo Bonzini 			"mov %%r15, regs+0x80\n\t"
223e7bce343SPaolo Bonzini 			"mov 0x1f8(%%rax), %%r15\n\t"  // rax
224a43baea0SPaolo Bonzini 			"mov %%r15, regs\n\t"
225e7bce343SPaolo Bonzini 			"vmsave %%rax\n\t"
2262e7dd780SCathy Avery 			"cli \n\t"
2277d36db35SAvi Kivity 			"stgi"
228e7bce343SPaolo Bonzini 			: // inputs clobbered by the guest:
229e7bce343SPaolo Bonzini 			"+D" (the_test),            // first argument register
230e7bce343SPaolo Bonzini 			"+b" (the_vmcb)             // callee save register!
231e7bce343SPaolo Bonzini 			: [test] "0" (the_test),
232e7bce343SPaolo Bonzini 			[vmcb_phys] "1"(the_vmcb),
233*ad879127SKrish Sadhukhan 			[PREPARE_GIF_CLEAR] "i" (offsetof(struct svm_test, prepare_gif_clear))
234e7bce343SPaolo Bonzini 			: "rax", "rcx", "rdx", "rsi",
2357d36db35SAvi Kivity 			"r8", "r9", "r10", "r11" , "r12", "r13", "r14", "r15",
2367d36db35SAvi Kivity 			"memory");
2377d36db35SAvi Kivity 		++test->exits;
2387d36db35SAvi Kivity 	} while (!test->finished(test));
2391500aca4SPaolo Bonzini 	irq_enable();
2407d36db35SAvi Kivity 
241a299895bSThomas Huth 	report(test->succeeded(test), "%s", test->name);
2427d36db35SAvi Kivity }
2437d36db35SAvi Kivity 
244*ad879127SKrish Sadhukhan static void setup_svm(void)
245095274b4SPrasad Joshi {
246*ad879127SKrish Sadhukhan 	void *hsave = alloc_page();
247*ad879127SKrish Sadhukhan 	u64 *page, address;
248*ad879127SKrish Sadhukhan 	int i,j;
249095274b4SPrasad Joshi 
250*ad879127SKrish Sadhukhan 	wrmsr(MSR_VM_HSAVE_PA, virt_to_phys(hsave));
251*ad879127SKrish Sadhukhan 	wrmsr(MSR_EFER, rdmsr(MSR_EFER) | EFER_SVME);
252*ad879127SKrish Sadhukhan 	wrmsr(MSR_EFER, rdmsr(MSR_EFER) | EFER_NX);
2537d36db35SAvi Kivity 
254*ad879127SKrish Sadhukhan 	io_bitmap = (void *) (((ulong)io_bitmap_area + 4095) & ~4095);
2557d36db35SAvi Kivity 
256*ad879127SKrish Sadhukhan 	msr_bitmap = (void *) ALIGN((ulong)msr_bitmap_area, PAGE_SIZE);
257e7bce343SPaolo Bonzini 
258*ad879127SKrish Sadhukhan 	if (!npt_supported())
259bcd9774aSPaolo Bonzini 		return;
260bcd9774aSPaolo Bonzini 
261*ad879127SKrish Sadhukhan 	printf("NPT detected - running all tests with NPT enabled\n");
2624c8eb156SJoerg Roedel 
2634c8eb156SJoerg Roedel 	/*
264*ad879127SKrish Sadhukhan 	* Nested paging supported - Build a nested page table
265*ad879127SKrish Sadhukhan 	* Build the page-table bottom-up and map everything with 4k
266*ad879127SKrish Sadhukhan 	* pages to get enough granularity for the NPT unit-tests.
2674c8eb156SJoerg Roedel 	*/
268*ad879127SKrish Sadhukhan 
269*ad879127SKrish Sadhukhan 	address = 0;
270*ad879127SKrish Sadhukhan 
271*ad879127SKrish Sadhukhan 	/* PTE level */
272*ad879127SKrish Sadhukhan 	for (i = 0; i < 2048; ++i) {
273*ad879127SKrish Sadhukhan 		page = alloc_page();
274*ad879127SKrish Sadhukhan 
275*ad879127SKrish Sadhukhan 		for (j = 0; j < 512; ++j, address += 4096)
276*ad879127SKrish Sadhukhan 	    		page[j] = address | 0x067ULL;
277*ad879127SKrish Sadhukhan 
278*ad879127SKrish Sadhukhan 		pte[i] = page;
2794c8eb156SJoerg Roedel 	}
2804c8eb156SJoerg Roedel 
281*ad879127SKrish Sadhukhan 	/* PDE level */
282*ad879127SKrish Sadhukhan 	for (i = 0; i < 4; ++i) {
283*ad879127SKrish Sadhukhan 		page = alloc_page();
284*ad879127SKrish Sadhukhan 
285*ad879127SKrish Sadhukhan 	for (j = 0; j < 512; ++j)
286*ad879127SKrish Sadhukhan 	    page[j] = (u64)pte[(i * 512) + j] | 0x027ULL;
287*ad879127SKrish Sadhukhan 
288*ad879127SKrish Sadhukhan 		pde[i] = page;
2894c8eb156SJoerg Roedel 	}
2904c8eb156SJoerg Roedel 
291*ad879127SKrish Sadhukhan 	/* PDPe level */
292*ad879127SKrish Sadhukhan 	pdpe   = alloc_page();
293*ad879127SKrish Sadhukhan 	for (i = 0; i < 4; ++i)
294*ad879127SKrish Sadhukhan 		pdpe[i] = ((u64)(pde[i])) | 0x27;
2958594b943SJoerg Roedel 
296*ad879127SKrish Sadhukhan 	/* PML4e level */
297*ad879127SKrish Sadhukhan 	pml4e    = alloc_page();
298*ad879127SKrish Sadhukhan 	pml4e[0] = ((u64)pdpe) | 0x27;
2998594b943SJoerg Roedel }
3008594b943SJoerg Roedel 
301*ad879127SKrish Sadhukhan extern struct svm_test svm_tests[];
3027d36db35SAvi Kivity 
3037d36db35SAvi Kivity int main(int ac, char **av)
3047d36db35SAvi Kivity {
305*ad879127SKrish Sadhukhan 	int i = 0;
3067d36db35SAvi Kivity 	struct vmcb *vmcb;
3077d36db35SAvi Kivity 
3087d36db35SAvi Kivity 	setup_vm();
3097d36db35SAvi Kivity 	smp_init();
3107d36db35SAvi Kivity 
311badc98caSKrish Sadhukhan 	if (!this_cpu_has(X86_FEATURE_SVM)) {
3127d36db35SAvi Kivity 		printf("SVM not availble\n");
31332b9603cSRadim Krčmář 		return report_summary();
3147d36db35SAvi Kivity 	}
3157d36db35SAvi Kivity 
3167d36db35SAvi Kivity 	setup_svm();
3177d36db35SAvi Kivity 
3187d36db35SAvi Kivity 	vmcb = alloc_page();
3197d36db35SAvi Kivity 
320*ad879127SKrish Sadhukhan 	for (; svm_tests[i].name != NULL; i++) {
321*ad879127SKrish Sadhukhan 		if (!svm_tests[i].supported())
3227d36db35SAvi Kivity 			continue;
323*ad879127SKrish Sadhukhan 		test_run(&svm_tests[i], vmcb);
3247d36db35SAvi Kivity 	}
3257d36db35SAvi Kivity 
326a43ed2acSAndrew Jones 	return report_summary();
3277d36db35SAvi Kivity }
328