xref: /kvm-unit-tests/x86/vmx_tests.c (revision 403e25190fd95b7e9c90877835ec044437bf49d4)
1 #include "vmx.h"
2 #include "msr.h"
3 #include "processor.h"
4 #include "vm.h"
5 
6 u64 ia32_pat;
7 u64 ia32_efer;
8 
9 static inline void vmcall()
10 {
11 	asm volatile("vmcall");
12 }
13 
14 void basic_init()
15 {
16 }
17 
18 void basic_guest_main()
19 {
20 	/* Here is a basic guest_main, print Hello World */
21 	printf("\tHello World, this is null_guest_main!\n");
22 }
23 
24 int basic_exit_handler()
25 {
26 	u64 guest_rip;
27 	ulong reason;
28 
29 	guest_rip = vmcs_read(GUEST_RIP);
30 	reason = vmcs_read(EXI_REASON) & 0xff;
31 
32 	switch (reason) {
33 	case VMX_VMCALL:
34 		print_vmexit_info();
35 		vmcs_write(GUEST_RIP, guest_rip + 3);
36 		return VMX_TEST_RESUME;
37 	default:
38 		break;
39 	}
40 	printf("ERROR : Unhandled vmx exit.\n");
41 	print_vmexit_info();
42 	return VMX_TEST_EXIT;
43 }
44 
45 void basic_syscall_handler(u64 syscall_no)
46 {
47 }
48 
49 void vmenter_main()
50 {
51 	u64 rax;
52 	u64 rsp, resume_rsp;
53 
54 	report("test vmlaunch", 1);
55 
56 	asm volatile(
57 		"mov %%rsp, %0\n\t"
58 		"mov %3, %%rax\n\t"
59 		"vmcall\n\t"
60 		"mov %%rax, %1\n\t"
61 		"mov %%rsp, %2\n\t"
62 		: "=r"(rsp), "=r"(rax), "=r"(resume_rsp)
63 		: "g"(0xABCD));
64 	report("test vmresume", (rax == 0xFFFF) && (rsp == resume_rsp));
65 }
66 
67 int vmenter_exit_handler()
68 {
69 	u64 guest_rip;
70 	ulong reason;
71 
72 	guest_rip = vmcs_read(GUEST_RIP);
73 	reason = vmcs_read(EXI_REASON) & 0xff;
74 	switch (reason) {
75 	case VMX_VMCALL:
76 		if (regs.rax != 0xABCD) {
77 			report("test vmresume", 0);
78 			return VMX_TEST_VMEXIT;
79 		}
80 		regs.rax = 0xFFFF;
81 		vmcs_write(GUEST_RIP, guest_rip + 3);
82 		return VMX_TEST_RESUME;
83 	default:
84 		report("test vmresume", 0);
85 		print_vmexit_info();
86 	}
87 	return VMX_TEST_VMEXIT;
88 }
89 
90 void msr_bmp_init()
91 {
92 	void *msr_bitmap;
93 	u32 ctrl_cpu0;
94 
95 	msr_bitmap = alloc_page();
96 	memset(msr_bitmap, 0x0, PAGE_SIZE);
97 	ctrl_cpu0 = vmcs_read(CPU_EXEC_CTRL0);
98 	ctrl_cpu0 |= CPU_MSR_BITMAP;
99 	vmcs_write(CPU_EXEC_CTRL0, ctrl_cpu0);
100 	vmcs_write(MSR_BITMAP, (u64)msr_bitmap);
101 }
102 
103 static void test_ctrl_pat_init()
104 {
105 	u64 ctrl_ent;
106 	u64 ctrl_exi;
107 
108 	msr_bmp_init();
109 	ctrl_ent = vmcs_read(ENT_CONTROLS);
110 	ctrl_exi = vmcs_read(EXI_CONTROLS);
111 	vmcs_write(ENT_CONTROLS, ctrl_ent | ENT_LOAD_PAT);
112 	vmcs_write(EXI_CONTROLS, ctrl_exi | (EXI_SAVE_PAT | EXI_LOAD_PAT));
113 	ia32_pat = rdmsr(MSR_IA32_CR_PAT);
114 	vmcs_write(GUEST_PAT, 0x0);
115 	vmcs_write(HOST_PAT, ia32_pat);
116 }
117 
118 static void test_ctrl_pat_main()
119 {
120 	u64 guest_ia32_pat;
121 
122 	guest_ia32_pat = rdmsr(MSR_IA32_CR_PAT);
123 	if (!(ctrl_enter_rev.clr & ENT_LOAD_PAT))
124 		printf("\tENT_LOAD_PAT is not supported.\n");
125 	else {
126 		if (guest_ia32_pat != 0) {
127 			report("Entry load PAT", 0);
128 			return;
129 		}
130 	}
131 	wrmsr(MSR_IA32_CR_PAT, 0x6);
132 	vmcall();
133 	guest_ia32_pat = rdmsr(MSR_IA32_CR_PAT);
134 	if (ctrl_enter_rev.clr & ENT_LOAD_PAT) {
135 		if (guest_ia32_pat != ia32_pat) {
136 			report("Entry load PAT", 0);
137 			return;
138 		}
139 		report("Entry load PAT", 1);
140 	}
141 }
142 
143 static int test_ctrl_pat_exit_handler()
144 {
145 	u64 guest_rip;
146 	ulong reason;
147 	u64 guest_pat;
148 
149 	guest_rip = vmcs_read(GUEST_RIP);
150 	reason = vmcs_read(EXI_REASON) & 0xff;
151 	switch (reason) {
152 	case VMX_VMCALL:
153 		guest_pat = vmcs_read(GUEST_PAT);
154 		if (!(ctrl_exit_rev.clr & EXI_SAVE_PAT)) {
155 			printf("\tEXI_SAVE_PAT is not supported\n");
156 			vmcs_write(GUEST_PAT, 0x6);
157 		} else {
158 			if (guest_pat == 0x6)
159 				report("Exit save PAT", 1);
160 			else
161 				report("Exit save PAT", 0);
162 		}
163 		if (!(ctrl_exit_rev.clr & EXI_LOAD_PAT))
164 			printf("\tEXI_LOAD_PAT is not supported\n");
165 		else {
166 			if (rdmsr(MSR_IA32_CR_PAT) == ia32_pat)
167 				report("Exit load PAT", 1);
168 			else
169 				report("Exit load PAT", 0);
170 		}
171 		vmcs_write(GUEST_PAT, ia32_pat);
172 		vmcs_write(GUEST_RIP, guest_rip + 3);
173 		return VMX_TEST_RESUME;
174 	default:
175 		printf("ERROR : Undefined exit reason, reason = %d.\n", reason);
176 		break;
177 	}
178 	return VMX_TEST_VMEXIT;
179 }
180 
181 static void test_ctrl_efer_init()
182 {
183 	u64 ctrl_ent;
184 	u64 ctrl_exi;
185 
186 	msr_bmp_init();
187 	ctrl_ent = vmcs_read(ENT_CONTROLS) | ENT_LOAD_EFER;
188 	ctrl_exi = vmcs_read(EXI_CONTROLS) | EXI_SAVE_EFER | EXI_LOAD_EFER;
189 	vmcs_write(ENT_CONTROLS, ctrl_ent & ctrl_enter_rev.clr);
190 	vmcs_write(EXI_CONTROLS, ctrl_exi & ctrl_exit_rev.clr);
191 	ia32_efer = rdmsr(MSR_EFER);
192 	vmcs_write(GUEST_EFER, ia32_efer ^ EFER_NX);
193 	vmcs_write(HOST_EFER, ia32_efer ^ EFER_NX);
194 }
195 
196 static void test_ctrl_efer_main()
197 {
198 	u64 guest_ia32_efer;
199 
200 	guest_ia32_efer = rdmsr(MSR_EFER);
201 	if (!(ctrl_enter_rev.clr & ENT_LOAD_EFER))
202 		printf("\tENT_LOAD_EFER is not supported.\n");
203 	else {
204 		if (guest_ia32_efer != (ia32_efer ^ EFER_NX)) {
205 			report("Entry load EFER", 0);
206 			return;
207 		}
208 	}
209 	wrmsr(MSR_EFER, ia32_efer);
210 	vmcall();
211 	guest_ia32_efer = rdmsr(MSR_EFER);
212 	if (ctrl_enter_rev.clr & ENT_LOAD_EFER) {
213 		if (guest_ia32_efer != ia32_efer) {
214 			report("Entry load EFER", 0);
215 			return;
216 		}
217 		report("Entry load EFER", 1);
218 	}
219 }
220 
221 static int test_ctrl_efer_exit_handler()
222 {
223 	u64 guest_rip;
224 	ulong reason;
225 	u64 guest_efer;
226 
227 	guest_rip = vmcs_read(GUEST_RIP);
228 	reason = vmcs_read(EXI_REASON) & 0xff;
229 	switch (reason) {
230 	case VMX_VMCALL:
231 		guest_efer = vmcs_read(GUEST_EFER);
232 		if (!(ctrl_exit_rev.clr & EXI_SAVE_EFER)) {
233 			printf("\tEXI_SAVE_EFER is not supported\n");
234 			vmcs_write(GUEST_EFER, ia32_efer);
235 		} else {
236 			if (guest_efer == ia32_efer)
237 				report("Exit save EFER", 1);
238 			else
239 				report("Exit save EFER", 0);
240 		}
241 		if (!(ctrl_exit_rev.clr & EXI_LOAD_EFER)) {
242 			printf("\tEXI_LOAD_EFER is not supported\n");
243 			wrmsr(MSR_EFER, ia32_efer ^ EFER_NX);
244 		} else {
245 			if (rdmsr(MSR_EFER) == (ia32_efer ^ EFER_NX))
246 				report("Exit load EFER", 1);
247 			else
248 				report("Exit load EFER", 0);
249 		}
250 		vmcs_write(GUEST_PAT, ia32_efer);
251 		vmcs_write(GUEST_RIP, guest_rip + 3);
252 		return VMX_TEST_RESUME;
253 	default:
254 		printf("ERROR : Undefined exit reason, reason = %d.\n", reason);
255 		break;
256 	}
257 	return VMX_TEST_VMEXIT;
258 }
259 
260 /* name/init/guest_main/exit_handler/syscall_handler/guest_regs
261    basic_* just implement some basic functions */
262 struct vmx_test vmx_tests[] = {
263 	{ "null", basic_init, basic_guest_main, basic_exit_handler,
264 		basic_syscall_handler, {0} },
265 	{ "vmenter", basic_init, vmenter_main, vmenter_exit_handler,
266 		basic_syscall_handler, {0} },
267 	{ "control field PAT", test_ctrl_pat_init, test_ctrl_pat_main,
268 		test_ctrl_pat_exit_handler, basic_syscall_handler, {0} },
269 	{ "control field EFER", test_ctrl_efer_init, test_ctrl_efer_main,
270 		test_ctrl_efer_exit_handler, basic_syscall_handler, {0} },
271 	{ NULL, NULL, NULL, NULL, NULL, {0} },
272 };
273