On Tue, Dec 30, 2025 at 01:13:40PM -0800, Sean Christopherson wrote: > Add a helper to detect VMRUN failures so that KVM can guard against its > own long-standing bug, where KVM neglects to set exitcode[63:32] when > synthesizing a nested VMFAIL_INVALID VM-Exit. This will allow fixing > KVM's mess of treating exitcode as two separate 32-bit values without > breaking KVM-on-KVM when running on an older, unfixed KVM. > > Cc: Jim Mattson <[email protected]> > Cc: Yosry Ahmed <[email protected]> > Signed-off-by: Sean Christopherson <[email protected]>
Reviewed-by: Yosry Ahmed <[email protected]> > --- > arch/x86/kvm/svm/nested.c | 16 +++++++--------- > arch/x86/kvm/svm/svm.c | 4 ++-- > arch/x86/kvm/svm/svm.h | 5 +++++ > 3 files changed, 14 insertions(+), 11 deletions(-) > > diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c > index ba0f11c68372..f5bde972a2b1 100644 > --- a/arch/x86/kvm/svm/nested.c > +++ b/arch/x86/kvm/svm/nested.c > @@ -1134,7 +1134,7 @@ int nested_svm_vmexit(struct vcpu_svm *svm) > vmcb12->control.exit_info_1 = vmcb02->control.exit_info_1; > vmcb12->control.exit_info_2 = vmcb02->control.exit_info_2; > > - if (vmcb12->control.exit_code != SVM_EXIT_ERR) > + if (!svm_is_vmrun_failure(vmcb12->control.exit_code)) > nested_save_pending_event_to_vmcb12(svm, vmcb12); > > if (guest_cpu_cap_has(vcpu, X86_FEATURE_NRIPS)) > @@ -1425,6 +1425,9 @@ static int nested_svm_intercept(struct vcpu_svm *svm) > u32 exit_code = svm->vmcb->control.exit_code; > int vmexit = NESTED_EXIT_HOST; > > + if (svm_is_vmrun_failure(exit_code)) > + return NESTED_EXIT_DONE; > + > switch (exit_code) { > case SVM_EXIT_MSR: > vmexit = nested_svm_exit_handled_msr(svm); > @@ -1432,7 +1435,7 @@ static int nested_svm_intercept(struct vcpu_svm *svm) > case SVM_EXIT_IOIO: > vmexit = nested_svm_intercept_ioio(svm); > break; > - case SVM_EXIT_EXCP_BASE ... SVM_EXIT_EXCP_BASE + 0x1f: { > + case SVM_EXIT_EXCP_BASE ... SVM_EXIT_EXCP_BASE + 0x1f: > /* > * Host-intercepted exceptions have been checked already in > * nested_svm_exit_special. There is nothing to do here, > @@ -1440,15 +1443,10 @@ static int nested_svm_intercept(struct vcpu_svm *svm) > */ > vmexit = NESTED_EXIT_DONE; > break; > - } > - case SVM_EXIT_ERR: { > - vmexit = NESTED_EXIT_DONE; > - break; > - } > - default: { > + default: > if (vmcb12_is_intercept(&svm->nested.ctl, exit_code)) > vmexit = NESTED_EXIT_DONE; > - } > + break; > } > > return vmexit; > diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c > index 24d59ccfa40d..c2ddf2e0aa1a 100644 > --- a/arch/x86/kvm/svm/svm.c > +++ b/arch/x86/kvm/svm/svm.c > @@ -3540,7 +3540,7 @@ static int svm_handle_exit(struct kvm_vcpu *vcpu, > fastpath_t exit_fastpath) > return 1; > } > > - if (svm->vmcb->control.exit_code == SVM_EXIT_ERR) { > + if (svm_is_vmrun_failure(svm->vmcb->control.exit_code)) { > kvm_run->exit_reason = KVM_EXIT_FAIL_ENTRY; > kvm_run->fail_entry.hardware_entry_failure_reason > = svm->vmcb->control.exit_code; > @@ -4311,7 +4311,7 @@ static __no_kcsan fastpath_t svm_vcpu_run(struct > kvm_vcpu *vcpu, u64 run_flags) > > /* Track VMRUNs that have made past consistency checking */ > if (svm->nested.nested_run_pending && > - svm->vmcb->control.exit_code != SVM_EXIT_ERR) > + !svm_is_vmrun_failure(svm->vmcb->control.exit_code)) > ++vcpu->stat.nested_run; > > svm->nested.nested_run_pending = 0; > diff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h > index 01be93a53d07..0f006793f973 100644 > --- a/arch/x86/kvm/svm/svm.h > +++ b/arch/x86/kvm/svm/svm.h > @@ -424,6 +424,11 @@ static __always_inline struct vcpu_svm *to_svm(struct > kvm_vcpu *vcpu) > return container_of(vcpu, struct vcpu_svm, vcpu); > } > > +static inline bool svm_is_vmrun_failure(u64 exit_code) > +{ > + return (u32)exit_code == (u32)SVM_EXIT_ERR; > +} > + > /* > * Only the PDPTRs are loaded on demand into the shadow MMU. All other > * fields are synchronized on VM-Exit, because accessing the VMCB is cheap. > -- > 2.52.0.351.gbe84eed79e-goog >
