1 // SPDX-License-Identifier: GPL-2.0-only
5 * Copyright (C) 2018, Red Hat, Inc.
7 * Tests for vCPU state save/restore, including nested guest state.
9 #define _GNU_SOURCE /* for program_invocation_short_name */
14 #include <sys/ioctl.h>
16 #include "test_util.h"
19 #include "processor.h"
23 #define L2_GUEST_STACK_SIZE 256
25 void svm_l2_guest_code(void)
31 /* Done, exit to L1 and never come back. */
35 static void svm_l1_guest_code(struct svm_test_data
*svm
)
37 unsigned long l2_guest_stack
[L2_GUEST_STACK_SIZE
];
38 struct vmcb
*vmcb
= svm
->vmcb
;
40 GUEST_ASSERT(svm
->vmcb_gpa
);
41 /* Prepare for L2 execution. */
42 generic_svm_setup(svm
, svm_l2_guest_code
,
43 &l2_guest_stack
[L2_GUEST_STACK_SIZE
]);
46 run_guest(vmcb
, svm
->vmcb_gpa
);
47 GUEST_ASSERT(vmcb
->control
.exit_code
== SVM_EXIT_VMMCALL
);
50 run_guest(vmcb
, svm
->vmcb_gpa
);
51 GUEST_ASSERT(vmcb
->control
.exit_code
== SVM_EXIT_VMMCALL
);
55 void vmx_l2_guest_code(void)
62 /* L1 has now set up a shadow VMCS for us. */
63 GUEST_ASSERT(vmreadz(GUEST_RIP
) == 0xc0ffee);
65 GUEST_ASSERT(vmreadz(GUEST_RIP
) == 0xc0ffee);
66 GUEST_ASSERT(!vmwrite(GUEST_RIP
, 0xc0fffee));
68 GUEST_ASSERT(vmreadz(GUEST_RIP
) == 0xc0fffee);
69 GUEST_ASSERT(!vmwrite(GUEST_RIP
, 0xc0ffffee));
72 /* Done, exit to L1 and never come back. */
76 static void vmx_l1_guest_code(struct vmx_pages
*vmx_pages
)
78 unsigned long l2_guest_stack
[L2_GUEST_STACK_SIZE
];
80 GUEST_ASSERT(vmx_pages
->vmcs_gpa
);
81 GUEST_ASSERT(prepare_for_vmx_operation(vmx_pages
));
83 GUEST_ASSERT(load_vmcs(vmx_pages
));
84 GUEST_ASSERT(vmptrstz() == vmx_pages
->vmcs_gpa
);
87 GUEST_ASSERT(vmptrstz() == vmx_pages
->vmcs_gpa
);
89 prepare_vmcs(vmx_pages
, vmx_l2_guest_code
,
90 &l2_guest_stack
[L2_GUEST_STACK_SIZE
]);
93 GUEST_ASSERT(vmptrstz() == vmx_pages
->vmcs_gpa
);
94 GUEST_ASSERT(!vmlaunch());
95 GUEST_ASSERT(vmptrstz() == vmx_pages
->vmcs_gpa
);
96 GUEST_ASSERT(vmreadz(VM_EXIT_REASON
) == EXIT_REASON_VMCALL
);
98 /* Check that the launched state is preserved. */
99 GUEST_ASSERT(vmlaunch());
101 GUEST_ASSERT(!vmresume());
102 GUEST_ASSERT(vmreadz(VM_EXIT_REASON
) == EXIT_REASON_VMCALL
);
105 GUEST_ASSERT(vmreadz(VM_EXIT_REASON
) == EXIT_REASON_VMCALL
);
107 GUEST_ASSERT(!vmresume());
108 GUEST_ASSERT(vmreadz(VM_EXIT_REASON
) == EXIT_REASON_VMCALL
);
110 vmwrite(GUEST_RIP
, vmreadz(GUEST_RIP
) + 3);
112 vmwrite(SECONDARY_VM_EXEC_CONTROL
, SECONDARY_EXEC_SHADOW_VMCS
);
113 vmwrite(VMCS_LINK_POINTER
, vmx_pages
->shadow_vmcs_gpa
);
115 GUEST_ASSERT(!vmptrld(vmx_pages
->shadow_vmcs_gpa
));
116 GUEST_ASSERT(vmlaunch());
118 GUEST_ASSERT(vmlaunch());
119 GUEST_ASSERT(vmresume());
121 vmwrite(GUEST_RIP
, 0xc0ffee);
123 GUEST_ASSERT(vmreadz(GUEST_RIP
) == 0xc0ffee);
125 GUEST_ASSERT(!vmptrld(vmx_pages
->vmcs_gpa
));
126 GUEST_ASSERT(!vmresume());
127 GUEST_ASSERT(vmreadz(VM_EXIT_REASON
) == EXIT_REASON_VMCALL
);
129 GUEST_ASSERT(!vmptrld(vmx_pages
->shadow_vmcs_gpa
));
130 GUEST_ASSERT(vmreadz(GUEST_RIP
) == 0xc0ffffee);
131 GUEST_ASSERT(vmlaunch());
132 GUEST_ASSERT(vmresume());
134 GUEST_ASSERT(vmreadz(GUEST_RIP
) == 0xc0ffffee);
135 GUEST_ASSERT(vmlaunch());
136 GUEST_ASSERT(vmresume());
139 static void __attribute__((__flatten__
)) guest_code(void *arg
)
145 if (this_cpu_has(X86_FEATURE_SVM
))
146 svm_l1_guest_code(arg
);
148 vmx_l1_guest_code(arg
);
154 int main(int argc
, char *argv
[])
156 vm_vaddr_t nested_gva
= 0;
158 struct kvm_regs regs1
, regs2
;
159 struct kvm_vcpu
*vcpu
;
161 struct kvm_x86_state
*state
;
166 vm
= vm_create_with_one_vcpu(&vcpu
, guest_code
);
168 vcpu_regs_get(vcpu
, ®s1
);
170 if (kvm_has_cap(KVM_CAP_NESTED_STATE
)) {
171 if (kvm_cpu_has(X86_FEATURE_SVM
))
172 vcpu_alloc_svm(vm
, &nested_gva
);
173 else if (kvm_cpu_has(X86_FEATURE_VMX
))
174 vcpu_alloc_vmx(vm
, &nested_gva
);
178 pr_info("will skip nested state checks\n");
180 vcpu_args_set(vcpu
, 1, nested_gva
);
182 for (stage
= 1;; stage
++) {
184 TEST_ASSERT_KVM_EXIT_REASON(vcpu
, KVM_EXIT_IO
);
186 switch (get_ucall(vcpu
, &uc
)) {
188 REPORT_GUEST_ASSERT(uc
);
195 TEST_FAIL("Unknown ucall %lu", uc
.cmd
);
198 /* UCALL_SYNC is handled here. */
199 TEST_ASSERT(!strcmp((const char *)uc
.args
[0], "hello") &&
200 uc
.args
[1] == stage
, "Stage %d: Unexpected register values vmexit, got %lx",
201 stage
, (ulong
)uc
.args
[1]);
203 state
= vcpu_save_state(vcpu
);
204 memset(®s1
, 0, sizeof(regs1
));
205 vcpu_regs_get(vcpu
, ®s1
);
209 /* Restore state in a new VM. */
210 vcpu
= vm_recreate_with_one_vcpu(vm
);
211 vcpu_load_state(vcpu
, state
);
212 kvm_x86_state_cleanup(state
);
214 memset(®s2
, 0, sizeof(regs2
));
215 vcpu_regs_get(vcpu
, ®s2
);
216 TEST_ASSERT(!memcmp(®s1
, ®s2
, sizeof(regs2
)),
217 "Unexpected register values after vcpu_load_state; rdi: %lx rsi: %lx",
218 (ulong
) regs2
.rdi
, (ulong
) regs2
.rsi
);