[RFC PATCH v3 09/27] KVM: SVM: Disable IRQ bypass for Secure AVIC

From: Naveen N Rao (AMD)

Date: Wed Jul 08 2026 - 02:42:42 EST


Secure AVIC does not support accelerating device interrupts, so have
kvm_arch_has_irq_bypass() return false for a Secure AVIC enabled SEV-SNP
guest. Since we now have to consult kvm_arch, move the function to x86.c
and export it for use by KVM submodules.

Due to this, avic_pi_update_irte() should never be called for a Secure
AVIC guest. Add a WARN_ON() so that it is clear something is broken.

Signed-off-by: Naveen N Rao (AMD) <naveen@xxxxxxxxxx>
---
arch/x86/include/asm/kvm_host.h | 8 ++------
arch/x86/kvm/svm/avic.c | 7 +++++++
arch/x86/kvm/svm/sev.c | 3 +++
arch/x86/kvm/x86.c | 7 +++++++
4 files changed, 19 insertions(+), 6 deletions(-)

diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index d81179b43199..cabb368b7908 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -1380,6 +1380,8 @@ struct kvm_arch {
bool apic_access_memslot_enabled;
bool apic_access_memslot_inhibited;

+ bool apicv_has_irq_bypass;
+
/*
* Force apicv_update_lock and apicv_nr_irq_window_req to reside in a
* dedicated cacheline. They are write-mostly, whereas most everything
@@ -2063,10 +2065,4 @@ static inline void kvm_arch_vcpu_unblocking(struct kvm_vcpu *vcpu)
{
kvm_x86_call(vcpu_unblocking)(vcpu);
}
-
-static inline bool kvm_arch_has_irq_bypass(struct kvm *kvm)
-{
- return enable_device_posted_irqs;
-}
-
#endif /* _ASM_X86_KVM_HOST_H */
diff --git a/arch/x86/kvm/svm/avic.c b/arch/x86/kvm/svm/avic.c
index 7340c9e251e0..9b4d3a10198b 100644
--- a/arch/x86/kvm/svm/avic.c
+++ b/arch/x86/kvm/svm/avic.c
@@ -919,6 +919,13 @@ int avic_pi_update_irte(struct kvm_kernel_irqfd *irqfd, struct kvm *kvm,
unsigned int host_irq, uint32_t guest_irq,
struct kvm_vcpu *vcpu, u32 vector)
{
+ /*
+ * Complain loudly as we should never reach here for Secure AVIC.
+ * See kvm_arch_has_irq_bypass()
+ */
+ if (WARN_ON_ONCE(snp_is_secure_avic_enabled(kvm)))
+ return -EINVAL;
+
/*
* If the IRQ was affined to a different vCPU, remove the IRTE metadata
* from the *previous* vCPU's list.
diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c
index 686227a15328..a47bb417bd98 100644
--- a/arch/x86/kvm/svm/sev.c
+++ b/arch/x86/kvm/svm/sev.c
@@ -566,6 +566,9 @@ static int __sev_guest_init(struct kvm *kvm, struct kvm_sev_cmd *argp,
INIT_LIST_HEAD(&sev->mirror_vms);
sev->need_init = false;

+ if (snp_is_secure_avic_enabled(kvm))
+ kvm->arch.apicv_has_irq_bypass = false;
+
kvm_set_apicv_inhibit(kvm, APICV_INHIBIT_REASON_SEV);

return 0;
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 8dbc0fa302a8..ed94aa8ebc0d 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -7233,6 +7233,7 @@ static void kvm_apicv_init(struct kvm *kvm)
APICV_INHIBIT_REASON_DISABLED;

set_or_clear_apicv_inhibit(&kvm->arch.apicv_inhibit_reasons, reason, true);
+ kvm->arch.apicv_has_irq_bypass = true;

init_rwsem(&kvm->arch.apicv_update_lock);
}
@@ -10572,6 +10573,12 @@ bool kvm_arch_no_poll(struct kvm_vcpu *vcpu)
return (vcpu->arch.msr_kvm_poll_control & 1) == 0;
}

+bool kvm_arch_has_irq_bypass(struct kvm *kvm)
+{
+ return enable_device_posted_irqs && kvm->arch.apicv_has_irq_bypass;
+}
+EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_arch_has_irq_bypass);
+
#ifdef CONFIG_KVM_GUEST_MEMFD
/*
* KVM doesn't yet support initializing guest_memfd memory as shared for VMs
--
2.54.0