Secure AVIC relies on NMI virtualization for injecting NMIs into the guest. Similar to V_GIF, set V_NMI_ENABLE_MASK in the BSP's VMSA as this is consulted by hardware instead of the corresponding VMCB field. Since EVENTINJ is not supported for Secure AVIC enabled guests, KVM can accept/pend at most 1 NMI at any point. Use this as the limit in process_nmi(). Add a WARN_ON() in svm_inject_nmi() since we should never hit this path. Always return 1 for nmi_allowed() since KVM can pend an NMI at any point and does not have visibility into the guest NMI blocking status. Signed-off-by: Naveen N Rao (AMD) --- arch/x86/kvm/svm/sev.c | 2 +- arch/x86/kvm/svm/svm.c | 8 ++++++++ arch/x86/kvm/x86.c | 8 +++++++- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c index 06555dbec45b..707537ad7271 100644 --- a/arch/x86/kvm/svm/sev.c +++ b/arch/x86/kvm/svm/sev.c @@ -1034,7 +1034,7 @@ static int sev_es_sync_vmsa(struct vcpu_svm *svm) /* Secure AVIC loads the below from the VMSA, rather than the VMCB */ if (snp_is_secure_avic_enabled(vcpu->kvm)) - save->vintr_ctrl |= V_GIF_MASK; + save->vintr_ctrl |= V_GIF_MASK | V_NMI_ENABLE_MASK; /* * Skip FPU and AVX setup with KVM_SEV_ES_INIT to avoid diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c index 63ee36501383..2cb38953c0cf 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -3768,6 +3768,10 @@ static void svm_inject_nmi(struct kvm_vcpu *vcpu) { struct vcpu_svm *svm = to_svm(vcpu); + /* We should never reach here for Secure AVIC - see svm_set_vnmi_pending() */ + if (WARN_ON_ONCE(snp_is_secure_avic_enabled(vcpu->kvm))) + return; + svm->vmcb->control.event_inj = SVM_EVTINJ_VALID | SVM_EVTINJ_TYPE_NMI; if (svm->nmi_l1_to_l2) @@ -4018,6 +4022,10 @@ static int svm_nmi_allowed(struct kvm_vcpu *vcpu, bool for_injection) if (vcpu->arch.nested_run_pending) return -EBUSY; + /* Assume it is always allowed for Secure AVIC (hardware gates delivery) */ + if (snp_is_secure_avic_enabled(vcpu->kvm)) + return 1; + if (svm_nmi_blocked(vcpu)) return 0; diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 2609a2972526..7f5978cac5f5 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -7742,8 +7742,14 @@ static void process_nmi(struct kvm_vcpu *vcpu) * waiting for a previous NMI injection to complete (which effectively * blocks NMIs). KVM will immediately inject one of the two NMIs, and * will request an NMI window to handle the second NMI. + * + * For protected APIC guests, NMI window isn't visible to KVM, so the + * only thing KVM can do is to collapse all pending NMIs and inject a + * single NMI. The guest is expected to scan all NMI sources as part + * of handling the NMI. */ - if (kvm_x86_call(get_nmi_mask)(vcpu) || vcpu->arch.nmi_injected) + if (kvm_x86_call(get_nmi_mask)(vcpu) || vcpu->arch.nmi_injected || + (lapic_in_kernel(vcpu) && vcpu->arch.apic->guest_apic_protected)) limit = 1; else limit = 2; -- 2.54.0