Skip to content

Commit 4941395

Browse files
mdouchapevik
authored andcommitted
KVM: Add functional test for emulated VMREAD/VMWRITE instructions
Link: https://lore.kernel.org/ltp/[email protected]/ Reviewed-by: Petr Vorel <[email protected]> Signed-off-by: Martin Doucha <[email protected]>
1 parent 3fa3b22 commit 4941395

File tree

1 file changed

+282
-0
lines changed

1 file changed

+282
-0
lines changed

testcases/kernel/kvm/kvm_vmx01.c

Lines changed: 282 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,282 @@
1+
// SPDX-License-Identifier: GPL-2.0-or-later
2+
/*
3+
* Copyright (C) 2024 SUSE LLC <[email protected]>
4+
*/
5+
6+
/*\
7+
* Basic functional test for VMREAD/VMWRITE instructions in KVM environment.
8+
* Verify that VMWRITE instruction changes the contents of current VMCS and
9+
* the values written into shadow VMCS can be read in both parent and nested
10+
* VM.
11+
*/
12+
13+
#include "kvm_test.h"
14+
15+
#ifdef COMPILE_PAYLOAD
16+
#if defined(__i386__) || defined(__x86_64__)
17+
18+
#include "kvm_x86_vmx.h"
19+
20+
#define GUEST_READ_ERROR 1
21+
#define GUEST_WRITE_ERROR 2
22+
#define SHADOW_DATA_LENGTH 37
23+
#define VMCS_FIELD(x) x, #x
24+
25+
struct vmcs_field_table {
26+
unsigned long field_id;
27+
const char *name;
28+
uint64_t value;
29+
};
30+
31+
/* Data written into shadow VMCS by the parent VM and read by the nested VM */
32+
static struct vmcs_field_table host_data[SHADOW_DATA_LENGTH] = {
33+
{VMCS_FIELD(VMX_VMCS_GUEST_ES), 0xe5},
34+
{VMCS_FIELD(VMX_VMCS_GUEST_CS), 0xc5},
35+
{VMCS_FIELD(VMX_VMCS_GUEST_SS), 0x55},
36+
{VMCS_FIELD(VMX_VMCS_GUEST_DS), 0xd5},
37+
{VMCS_FIELD(VMX_VMCS_GUEST_FS), 0xf5},
38+
{VMCS_FIELD(VMX_VMCS_GUEST_GS), 0x65},
39+
{VMCS_FIELD(VMX_VMCS_GUEST_LDTR), 0x1d72},
40+
{VMCS_FIELD(VMX_VMCS_GUEST_TR), 0x72},
41+
{VMCS_FIELD(VMX_VMCS_HOST_ES), 0x5e},
42+
{VMCS_FIELD(VMX_VMCS_HOST_CS), 0x5c},
43+
{VMCS_FIELD(VMX_VMCS_HOST_SS), 0x55},
44+
{VMCS_FIELD(VMX_VMCS_HOST_DS), 0x5d},
45+
{VMCS_FIELD(VMX_VMCS_HOST_FS), 0x5f},
46+
{VMCS_FIELD(VMX_VMCS_HOST_GS), 0x56},
47+
{VMCS_FIELD(VMX_VMCS_HOST_TR), 0x27},
48+
{VMCS_FIELD(VMX_VMCS_GUEST_ES_LIMIT), 0xe51},
49+
{VMCS_FIELD(VMX_VMCS_GUEST_CS_LIMIT), 0xc51},
50+
{VMCS_FIELD(VMX_VMCS_GUEST_SS_LIMIT), 0x551},
51+
{VMCS_FIELD(VMX_VMCS_GUEST_DS_LIMIT), 0xd51},
52+
{VMCS_FIELD(VMX_VMCS_GUEST_FS_LIMIT), 0xf51},
53+
{VMCS_FIELD(VMX_VMCS_GUEST_GS_LIMIT), 0x651},
54+
{VMCS_FIELD(VMX_VMCS_GUEST_LDTR_LIMIT), 0x1d721},
55+
{VMCS_FIELD(VMX_VMCS_GUEST_ES_ACCESS), 0xa0e5},
56+
{VMCS_FIELD(VMX_VMCS_GUEST_CS_ACCESS), 0xa0c5},
57+
{VMCS_FIELD(VMX_VMCS_GUEST_SS_ACCESS), 0xa055},
58+
{VMCS_FIELD(VMX_VMCS_GUEST_DS_ACCESS), 0xa0d5},
59+
{VMCS_FIELD(VMX_VMCS_GUEST_FS_ACCESS), 0xa0f5},
60+
{VMCS_FIELD(VMX_VMCS_GUEST_GS_ACCESS), 0xa065},
61+
{VMCS_FIELD(VMX_VMCS_GUEST_SYSENTER_CS), 0x65c},
62+
{VMCS_FIELD(VMX_VMCS_HOST_SYSENTER_CS), 0x45c},
63+
{VMCS_FIELD(VMX_VMCS_GUEST_ES_BASE), 0xe5b},
64+
{VMCS_FIELD(VMX_VMCS_GUEST_CS_BASE), 0xc5b},
65+
{VMCS_FIELD(VMX_VMCS_GUEST_SS_BASE), 0x55b},
66+
{VMCS_FIELD(VMX_VMCS_GUEST_DS_BASE), 0xd5b},
67+
{VMCS_FIELD(VMX_VMCS_GUEST_FS_BASE), 0xf5b},
68+
{VMCS_FIELD(VMX_VMCS_GUEST_GS_BASE), 0x65b},
69+
{VMCS_FIELD(VMX_VMCS_GUEST_LDTR_BASE), 0x1d72b}
70+
};
71+
72+
/* Data written into shadow VMCS by the nested VM and read by the parent VM */
73+
static struct vmcs_field_table guest_data[SHADOW_DATA_LENGTH] = {
74+
{VMCS_FIELD(VMX_VMCS_GUEST_ES), 0x5e},
75+
{VMCS_FIELD(VMX_VMCS_GUEST_CS), 0x5c},
76+
{VMCS_FIELD(VMX_VMCS_GUEST_SS), 0x55},
77+
{VMCS_FIELD(VMX_VMCS_GUEST_DS), 0x5d},
78+
{VMCS_FIELD(VMX_VMCS_GUEST_FS), 0x5f},
79+
{VMCS_FIELD(VMX_VMCS_GUEST_GS), 0x56},
80+
{VMCS_FIELD(VMX_VMCS_GUEST_LDTR), 0x721d},
81+
{VMCS_FIELD(VMX_VMCS_GUEST_TR), 0x27},
82+
{VMCS_FIELD(VMX_VMCS_HOST_ES), 0xe5},
83+
{VMCS_FIELD(VMX_VMCS_HOST_CS), 0xc5},
84+
{VMCS_FIELD(VMX_VMCS_HOST_SS), 0x55},
85+
{VMCS_FIELD(VMX_VMCS_HOST_DS), 0xd5},
86+
{VMCS_FIELD(VMX_VMCS_HOST_FS), 0xf5},
87+
{VMCS_FIELD(VMX_VMCS_HOST_GS), 0x65},
88+
{VMCS_FIELD(VMX_VMCS_HOST_TR), 0x72},
89+
{VMCS_FIELD(VMX_VMCS_GUEST_ES_LIMIT), 0x1e5},
90+
{VMCS_FIELD(VMX_VMCS_GUEST_CS_LIMIT), 0x1c5},
91+
{VMCS_FIELD(VMX_VMCS_GUEST_SS_LIMIT), 0x155},
92+
{VMCS_FIELD(VMX_VMCS_GUEST_DS_LIMIT), 0x1d5},
93+
{VMCS_FIELD(VMX_VMCS_GUEST_FS_LIMIT), 0x1f5},
94+
{VMCS_FIELD(VMX_VMCS_GUEST_GS_LIMIT), 0x165},
95+
{VMCS_FIELD(VMX_VMCS_GUEST_LDTR_LIMIT), 0x11d72},
96+
{VMCS_FIELD(VMX_VMCS_GUEST_ES_ACCESS), 0xa05e},
97+
{VMCS_FIELD(VMX_VMCS_GUEST_CS_ACCESS), 0xa05c},
98+
{VMCS_FIELD(VMX_VMCS_GUEST_SS_ACCESS), 0xa055},
99+
{VMCS_FIELD(VMX_VMCS_GUEST_DS_ACCESS), 0xa05d},
100+
{VMCS_FIELD(VMX_VMCS_GUEST_FS_ACCESS), 0xa05f},
101+
{VMCS_FIELD(VMX_VMCS_GUEST_GS_ACCESS), 0xa056},
102+
{VMCS_FIELD(VMX_VMCS_GUEST_SYSENTER_CS), 0x5c6},
103+
{VMCS_FIELD(VMX_VMCS_HOST_SYSENTER_CS), 0x5c4},
104+
{VMCS_FIELD(VMX_VMCS_GUEST_ES_BASE), 0xbe5},
105+
{VMCS_FIELD(VMX_VMCS_GUEST_CS_BASE), 0xbc5},
106+
{VMCS_FIELD(VMX_VMCS_GUEST_SS_BASE), 0xb55},
107+
{VMCS_FIELD(VMX_VMCS_GUEST_DS_BASE), 0xbd5},
108+
{VMCS_FIELD(VMX_VMCS_GUEST_FS_BASE), 0xbf5},
109+
{VMCS_FIELD(VMX_VMCS_GUEST_GS_BASE), 0xb65},
110+
{VMCS_FIELD(VMX_VMCS_GUEST_LDTR_BASE), 0xb1d72}
111+
};
112+
113+
static uint64_t vmread_buffer[SHADOW_DATA_LENGTH];
114+
115+
int guest_main(void)
116+
{
117+
int i;
118+
119+
/* kvm_vmx_vmread() calls tst_brk(), don't use it in nested VM */
120+
for (i = 0; i < SHADOW_DATA_LENGTH; i++) {
121+
asm goto(
122+
"vmread %1, (%0)\n"
123+
"jna %l[read_error]\n"
124+
"vmwrite %2, %3\n"
125+
"jna %l[write_error]\n"
126+
:
127+
: "r" (&vmread_buffer[i]), "r" (host_data[i].field_id),
128+
"r" (guest_data[i].value),
129+
"r" (guest_data[i].field_id)
130+
: "cc", "memory"
131+
: read_error, write_error
132+
);
133+
}
134+
135+
return 0;
136+
137+
read_error:
138+
return GUEST_READ_ERROR;
139+
140+
write_error:
141+
return GUEST_WRITE_ERROR;
142+
}
143+
144+
void main(void)
145+
{
146+
struct kvm_vmx_vcpu *vcpu;
147+
struct kvm_vmcs *shadow_vmcs;
148+
char *vmcs_backup;
149+
int i, errors;
150+
uint64_t val;
151+
152+
kvm_set_vmx_state(1);
153+
154+
/* Check secondary VMCS execctl support */
155+
if (kvm_rdmsr(MSR_IA32_VMX_BASIC) & IA32_VMXBASIC_USELESS_CTL_MASKS)
156+
val = kvm_rdmsr(MSR_IA32_VMX_EXECCTL_MASK2);
157+
else
158+
val = kvm_rdmsr(MSR_IA32_VMX_EXECCTL_MASK);
159+
160+
if (!((val >> 32) & VMX_EXECCTL_ENABLE_CTL2))
161+
tst_brk(TCONF, "CPU does not support shadow VMCS");
162+
163+
/* Create and configure guest VMCS */
164+
shadow_vmcs = kvm_alloc_vmcs();
165+
kvm_vmx_vmclear(shadow_vmcs);
166+
shadow_vmcs->version |= VMX_SHADOW_VMCS;
167+
vcpu = kvm_create_vmx_vcpu(guest_main, 1);
168+
kvm_vmx_vmptrld(vcpu->vmcs);
169+
val = kvm_vmx_vmread(VMX_VMCS_VMEXEC_CTL);
170+
val |= VMX_EXECCTL_ENABLE_CTL2;
171+
kvm_vmx_vmwrite(VMX_VMCS_VMEXEC_CTL, val);
172+
val = kvm_rdmsr(MSR_IA32_VMX_EXECCTL2_MASK);
173+
174+
if (!((val >> 32) & VMX_EXECCTL2_SHADOW_VMCS))
175+
tst_brk(TCONF, "CPU does not support shadow VMCS");
176+
177+
val = VMX_EXECCTL2_SHADOW_VMCS | (uint32_t)val;
178+
kvm_vmx_vmwrite(VMX_VMCS_VMEXEC_CTL2, val);
179+
kvm_vmx_vmwrite(VMX_VMCS_LINK_POINTER, (uintptr_t)shadow_vmcs);
180+
181+
/* Configure shadow VMCS */
182+
vmcs_backup = tst_heap_alloc(sizeof(struct kvm_vmcs));
183+
memcpy(vmcs_backup, shadow_vmcs, sizeof(struct kvm_vmcs));
184+
kvm_vmx_vmptrld(shadow_vmcs);
185+
186+
for (i = 0; i < SHADOW_DATA_LENGTH; i++)
187+
kvm_vmx_vmwrite(host_data[i].field_id, host_data[i].value);
188+
189+
/* Flush shadow VMCS just in case */
190+
kvm_vmx_vmptrld(vcpu->vmcs);
191+
192+
if (!memcmp(vmcs_backup, shadow_vmcs, sizeof(struct kvm_vmcs)))
193+
tst_res(TFAIL, "VMWRITE did not modify raw VMCS data");
194+
195+
/* Run nested VM */
196+
memcpy(vmcs_backup, shadow_vmcs, sizeof(struct kvm_vmcs));
197+
kvm_vmx_vmrun(vcpu);
198+
val = kvm_vmx_vmread(VMX_VMCS_EXIT_REASON);
199+
200+
if (val != VMX_EXIT_HLT) {
201+
tst_res(TFAIL, "Unexpected guest exit reason %llx", val);
202+
return;
203+
}
204+
205+
if (vcpu->regs.rax == GUEST_READ_ERROR) {
206+
tst_res(TFAIL, "Guest failed to read shadow VMCS");
207+
return;
208+
}
209+
210+
if (vcpu->regs.rax == GUEST_WRITE_ERROR) {
211+
tst_res(TFAIL, "Guest failed to write shadow VMCS");
212+
return;
213+
}
214+
215+
if (!memcmp(vmcs_backup, shadow_vmcs, sizeof(struct kvm_vmcs)))
216+
tst_res(TFAIL, "Nested VMWRITE did not modify raw VMCS data");
217+
218+
/* Check values read by the nested VM from shadow VMCS */
219+
for (i = 0, errors = 0; i < SHADOW_DATA_LENGTH; i++) {
220+
if (vmread_buffer[i] == host_data[i].value)
221+
continue;
222+
223+
errors++;
224+
tst_res(TFAIL, "Shadow %s guest mismatch: %llx != %llx",
225+
host_data[i].name, vmread_buffer[i],
226+
host_data[i].value);
227+
}
228+
229+
if (!errors)
230+
tst_res(TPASS, "Guest read correct values from shadow VMCS");
231+
232+
/* Check values written by the nested VM to shadow VMCS */
233+
kvm_vmx_vmptrld(shadow_vmcs);
234+
235+
for (i = 0, errors = 0; i < SHADOW_DATA_LENGTH; i++) {
236+
val = kvm_vmx_vmread(guest_data[i].field_id);
237+
238+
if (val == guest_data[i].value)
239+
continue;
240+
241+
errors++;
242+
tst_res(TFAIL, "Shadow %s parent mismatch: %llx != %llx",
243+
guest_data[i].name, val, guest_data[i].value);
244+
}
245+
246+
if (!errors)
247+
tst_res(TPASS, "Parent read correct values from shadow VMCS");
248+
}
249+
250+
#else /* defined(__i386__) || defined(__x86_64__) */
251+
TST_TEST_TCONF("Test supported only on x86");
252+
#endif /* defined(__i386__) || defined(__x86_64__) */
253+
254+
#else /* COMPILE_PAYLOAD */
255+
256+
#include "tst_module.h"
257+
258+
#define NESTED_INTEL_SYSFILE "/sys/module/kvm_intel/parameters/nested"
259+
260+
static void setup(void)
261+
{
262+
if (!tst_read_bool_sys_param(NESTED_INTEL_SYSFILE)) {
263+
tst_module_reload("kvm_intel",
264+
(char *const[]){"nested=1", NULL});
265+
}
266+
267+
tst_kvm_setup();
268+
}
269+
270+
static struct tst_test test = {
271+
.test_all = tst_kvm_run,
272+
.setup = setup,
273+
.cleanup = tst_kvm_cleanup,
274+
.needs_root = 1,
275+
.supported_archs = (const char *const []) {
276+
"x86_64",
277+
"x86",
278+
NULL
279+
},
280+
};
281+
282+
#endif /* COMPILE_PAYLOAD */

0 commit comments

Comments
 (0)