mirror of
https://github.com/torvalds/linux.git
synced 2026-09-25 17:42:03 +02:00
KVM: selftests: Trigger L2->L1 exits stress save+restore and #PF test
Extend the testing coverage in L2 by forcing a nested VM-Exit from L2 to
L1 right after restore on every other iteration. Forcing a nested
VM-Exit while L0 has control (e.g. without explicitly running L2 and
making a hypercall) is valuable, as it often happens during live
migration (e.g. L1 timer interrupt fires by the time the VM lands on the
destination).
To force the nested VM-Exit inject a #UD in to the saved vCPU state, and
intercept #UD from L1.
With this change, the test reliably reproduces the CR2 bug fixed by
commit 5c247d08bc ("KVM: nSVM: Use vcpu->arch.cr2 when updating vmcb12
on nested #VMEXIT") -- at least on Milan, Genoa, and Turin CPUs.
Assisted-by: Gemini:gemini-3.1-pro
Signed-off-by: Yosry Ahmed <yosry@kernel.org>
Link: https://patch.msgid.link/20260728174232.2423257-14-yosry@kernel.org
Signed-off-by: Sean Christopherson <seanjc@google.com>
This commit is contained in:
parent
1494b3d17c
commit
583ad2052d
|
|
@ -956,6 +956,11 @@ struct kvm_x86_state *vcpu_save_state(struct kvm_vcpu *vcpu);
|
|||
void vcpu_load_state(struct kvm_vcpu *vcpu, struct kvm_x86_state *state);
|
||||
void kvm_x86_state_cleanup(struct kvm_x86_state *state);
|
||||
|
||||
static inline bool kvm_x86_state_is_guest_mode(struct kvm_x86_state *state)
|
||||
{
|
||||
return state->nested.size && (state->nested.flags & KVM_STATE_NESTED_GUEST_MODE);
|
||||
}
|
||||
|
||||
const struct kvm_msr_list *kvm_get_msr_index_list(void);
|
||||
const struct kvm_msr_list *kvm_get_feature_msr_index_list(void);
|
||||
bool kvm_msr_is_in_save_restore_list(u32 msr_index);
|
||||
|
|
|
|||
|
|
@ -87,8 +87,13 @@ static void guest_access_memory(void *arg)
|
|||
static void l1_svm_code(struct svm_test_data *svm)
|
||||
{
|
||||
generic_svm_setup(svm, guest_access_memory);
|
||||
run_guest(svm->vmcb, svm->vmcb_gpa);
|
||||
GUEST_ASSERT(false);
|
||||
svm->vmcb->control.intercept_exceptions |= BIT(UD_VECTOR);
|
||||
|
||||
while (1) {
|
||||
run_guest(svm->vmcb, svm->vmcb_gpa);
|
||||
GUEST_ASSERT_EQ(svm->vmcb->control.exit_code,
|
||||
(SVM_EXIT_EXCP_BASE + UD_VECTOR));
|
||||
}
|
||||
}
|
||||
|
||||
static void l1_vmx_code(struct vmx_pages *vmx)
|
||||
|
|
@ -97,8 +102,14 @@ static void l1_vmx_code(struct vmx_pages *vmx)
|
|||
GUEST_ASSERT(load_vmcs(vmx));
|
||||
prepare_vmcs(vmx, guest_access_memory);
|
||||
|
||||
GUEST_ASSERT(!vmwrite(EXCEPTION_BITMAP, BIT(UD_VECTOR)));
|
||||
|
||||
GUEST_ASSERT(!vmlaunch());
|
||||
GUEST_ASSERT(false);
|
||||
while (1) {
|
||||
GUEST_ASSERT_EQ(vmreadz(VM_EXIT_REASON), EXIT_REASON_EXCEPTION_NMI);
|
||||
GUEST_ASSERT_EQ(vmreadz(VM_EXIT_INTR_INFO) & 0xff, UD_VECTOR);
|
||||
GUEST_ASSERT(!vmresume());
|
||||
}
|
||||
}
|
||||
|
||||
static void l1_guest_code(void *test_data)
|
||||
|
|
@ -136,6 +147,19 @@ static void vcpu_sigusr_ignore(void)
|
|||
sigaction(SIGUSR1, &sa, NULL);
|
||||
}
|
||||
|
||||
static void kvm_x86_state_queue_ud(struct kvm_x86_state *state)
|
||||
{
|
||||
if (state->events.exception.pending || state->events.exception.injected)
|
||||
return;
|
||||
|
||||
state->events.flags |= KVM_VCPUEVENT_VALID_PAYLOAD;
|
||||
state->events.exception.pending = true;
|
||||
state->events.exception.injected = false;
|
||||
state->events.exception.nr = UD_VECTOR;
|
||||
state->events.exception.has_error_code = false;
|
||||
state->events.exception_has_payload = false;
|
||||
}
|
||||
|
||||
static void run_test(bool nested)
|
||||
{
|
||||
struct kvm_x86_state *state;
|
||||
|
|
@ -153,6 +177,7 @@ static void run_test(bool nested)
|
|||
vm_install_exception_handler(vm, PF_VECTOR, guest_pf_handler);
|
||||
|
||||
if (nested) {
|
||||
vm_enable_cap(vm, KVM_CAP_EXCEPTION_PAYLOAD, -2ul);
|
||||
if (kvm_cpu_has(X86_FEATURE_SVM))
|
||||
vcpu_alloc_svm(vm, &gva);
|
||||
else
|
||||
|
|
@ -218,8 +243,17 @@ static void run_test(bool nested)
|
|||
|
||||
state = vcpu_save_state(vcpu);
|
||||
|
||||
/*
|
||||
* If the vCPU is in guest mode, inject a #UD to trigger an
|
||||
* L2->L1 VM-Exit every other iteration.
|
||||
*/
|
||||
if (kvm_x86_state_is_guest_mode(state) && i % 2 == 0)
|
||||
kvm_x86_state_queue_ud(state);
|
||||
|
||||
kvm_vm_release(vm);
|
||||
vcpu = vm_recreate_with_one_vcpu(vm);
|
||||
if (nested)
|
||||
vm_enable_cap(vm, KVM_CAP_EXCEPTION_PAYLOAD, -2ul);
|
||||
vcpu_load_state(vcpu, state);
|
||||
kvm_x86_state_cleanup(state);
|
||||
|
||||
|
|
@ -241,7 +275,9 @@ int main(int argc, char *argv[])
|
|||
pr_info("Running save+restore stress test...\n");
|
||||
run_test(/*nested=*/false);
|
||||
|
||||
if (!kvm_cpu_has(X86_FEATURE_SVM) && !kvm_cpu_has(X86_FEATURE_VMX)) {
|
||||
if (!kvm_has_cap(KVM_CAP_EXCEPTION_PAYLOAD) ||
|
||||
!kvm_has_cap(KVM_CAP_NESTED_STATE) ||
|
||||
(!kvm_cpu_has(X86_FEATURE_SVM) && !kvm_cpu_has(X86_FEATURE_VMX))) {
|
||||
pr_info("Nested virtualization not supported, skipping nested test\n");
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user