[RFC PATCH v3 23/27] KVM: SVM: Add support for injecting NMIs for Secure AVIC guests
From: Naveen N Rao (AMD)
Date: Wed Jul 08 2026 - 02:51:53 EST
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) <naveen@xxxxxxxxxx>
---
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