lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20251229111708.59402-2-khushit.shah@nutanix.com>
Date: Mon, 29 Dec 2025 11:17:06 +0000
From: Khushit Shah <khushit.shah@...anix.com>
To: seanjc@...gle.com, pbonzini@...hat.com, kai.huang@...el.com,
        dwmw2@...radead.org
Cc: mingo@...hat.com, x86@...nel.org, bp@...en8.de, hpa@...or.com,
        linux-kernel@...r.kernel.org, kvm@...r.kernel.org,
        dave.hansen@...ux.intel.com, tglx@...utronix.de, jon@...anix.com,
        shaju.abraham@...anix.com, Khushit Shah <khushit.shah@...anix.com>
Subject: [PATCH v5 1/3] KVM: x86: Refactor suppress EOI broadcast logic

Extract the suppress EOI broadcast (Directed EOI) logic into helper
functions and move the check from kvm_ioapic_update_eoi_one() to
kvm_ioapic_update_eoi() (required for a later patch). Prepare
kvm_ioapic_send_eoi() to honor Suppress EOI Broadcast in split IRQCHIP
mode.

Introduce two helper functions:
- kvm_lapic_advertise_suppress_eoi_broadcast(): determines whether KVM
  should advertise Suppress EOI Broadcast support to the guest
- kvm_lapic_respect_suppress_eoi_broadcast(): determines whether KVM should
  honor the guest's request to suppress EOI broadcasts

This refactoring prepares for I/O APIC version 0x20 support and userspace
control of suppress EOI broadcast behavior.

Signed-off-by: Khushit Shah <khushit.shah@...anix.com>
---
 arch/x86/kvm/ioapic.c | 12 +++++++---
 arch/x86/kvm/lapic.c  | 53 ++++++++++++++++++++++++++++++++++++-------
 arch/x86/kvm/lapic.h  |  3 +++
 3 files changed, 57 insertions(+), 11 deletions(-)

diff --git a/arch/x86/kvm/ioapic.c b/arch/x86/kvm/ioapic.c
index 2c2783296aed..6bf8d110aece 100644
--- a/arch/x86/kvm/ioapic.c
+++ b/arch/x86/kvm/ioapic.c
@@ -545,7 +545,6 @@ static void kvm_ioapic_update_eoi_one(struct kvm_vcpu *vcpu,
 				      int trigger_mode,
 				      int pin)
 {
-	struct kvm_lapic *apic = vcpu->arch.apic;
 	union kvm_ioapic_redirect_entry *ent = &ioapic->redirtbl[pin];
 
 	/*
@@ -560,8 +559,7 @@ static void kvm_ioapic_update_eoi_one(struct kvm_vcpu *vcpu,
 	kvm_notify_acked_irq(ioapic->kvm, KVM_IRQCHIP_IOAPIC, pin);
 	spin_lock(&ioapic->lock);
 
-	if (trigger_mode != IOAPIC_LEVEL_TRIG ||
-	    kvm_lapic_get_reg(apic, APIC_SPIV) & APIC_SPIV_DIRECTED_EOI)
+	if (trigger_mode != IOAPIC_LEVEL_TRIG)
 		return;
 
 	ASSERT(ent->fields.trig_mode == IOAPIC_LEVEL_TRIG);
@@ -591,10 +589,16 @@ static void kvm_ioapic_update_eoi_one(struct kvm_vcpu *vcpu,
 void kvm_ioapic_update_eoi(struct kvm_vcpu *vcpu, int vector, int trigger_mode)
 {
 	int i;
+	struct kvm_lapic *apic = vcpu->arch.apic;
 	struct kvm_ioapic *ioapic = vcpu->kvm->arch.vioapic;
 
 	spin_lock(&ioapic->lock);
 	rtc_irq_eoi(ioapic, vcpu, vector);
+
+	if ((kvm_lapic_get_reg(apic, APIC_SPIV) & APIC_SPIV_DIRECTED_EOI) &&
+	    kvm_lapic_respect_suppress_eoi_broadcast(ioapic->kvm))
+		goto out;
+
 	for (i = 0; i < IOAPIC_NUM_PINS; i++) {
 		union kvm_ioapic_redirect_entry *ent = &ioapic->redirtbl[i];
 
@@ -602,6 +606,8 @@ void kvm_ioapic_update_eoi(struct kvm_vcpu *vcpu, int vector, int trigger_mode)
 			continue;
 		kvm_ioapic_update_eoi_one(vcpu, ioapic, trigger_mode, i);
 	}
+
+out:
 	spin_unlock(&ioapic->lock);
 }
 
diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c
index 0ae7f913d782..2c24fd8d815f 100644
--- a/arch/x86/kvm/lapic.c
+++ b/arch/x86/kvm/lapic.c
@@ -105,6 +105,39 @@ bool kvm_apic_pending_eoi(struct kvm_vcpu *vcpu, int vector)
 		apic_test_vector(vector, apic->regs + APIC_IRR);
 }
 
+bool kvm_lapic_advertise_suppress_eoi_broadcast(struct kvm *kvm)
+{
+	/*
+	 * The default in-kernel I/O APIC emulates the 82093AA and does not
+	 * implement an EOI register. Some guests (e.g. Windows with the
+	 * Hyper-V role enabled) disable LAPIC EOI broadcast without checking
+	 * the I/O APIC version, which can cause level-triggered interrupts to
+	 * never be EOI'd.
+	 *
+	 * To avoid this, KVM must not advertise Suppress EOI Broadcast support
+	 * when using the default in-kernel I/O APIC.
+	 *
+	 * Historically, in split IRQCHIP mode, KVM always advertised Suppress
+	 * EOI Broadcast support but did not actually suppress EOIs, resulting
+	 * in quirky behavior.
+	 */
+	return !ioapic_in_kernel(kvm);
+}
+
+bool kvm_lapic_respect_suppress_eoi_broadcast(struct kvm *kvm)
+{
+	/*
+	 * Returns true if KVM should honor the guest's request to suppress EOI
+	 * broadcasts, i.e. actually implement Suppress EOI Broadcast.
+	 *
+	 * Historically, in split IRQCHIP mode, KVM ignored the suppress EOI
+	 * broadcast bit set by the guest and broadcasts EOIs to the userspace
+	 * I/O APIC. For In-kernel I/O APIC, the support itself is not
+	 * advertised, but if bit was set by the guest, it was respected.
+	 */
+	return ioapic_in_kernel(kvm);
+}
+
 __read_mostly DEFINE_STATIC_KEY_FALSE(kvm_has_noapic_vcpu);
 EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_has_noapic_vcpu);
 
@@ -554,15 +587,9 @@ void kvm_apic_set_version(struct kvm_vcpu *vcpu)
 
 	v = APIC_VERSION | ((apic->nr_lvt_entries - 1) << 16);
 
-	/*
-	 * KVM emulates 82093AA datasheet (with in-kernel IOAPIC implementation)
-	 * which doesn't have EOI register; Some buggy OSes (e.g. Windows with
-	 * Hyper-V role) disable EOI broadcast in lapic not checking for IOAPIC
-	 * version first and level-triggered interrupts never get EOIed in
-	 * IOAPIC.
-	 */
+
 	if (guest_cpu_cap_has(vcpu, X86_FEATURE_X2APIC) &&
-	    !ioapic_in_kernel(vcpu->kvm))
+	    kvm_lapic_advertise_suppress_eoi_broadcast(vcpu->kvm))
 		v |= APIC_LVR_DIRECTED_EOI;
 	kvm_lapic_set_reg(apic, APIC_LVR, v);
 }
@@ -1517,6 +1544,16 @@ static void kvm_ioapic_send_eoi(struct kvm_lapic *apic, int vector)
 
 	/* Request a KVM exit to inform the userspace IOAPIC. */
 	if (irqchip_split(apic->vcpu->kvm)) {
+		/*
+		 * Don't exit to userspace if the guest has enabled Directed
+		 * EOI, a.k.a. Suppress EOI Broadcasts, in which case the local
+		 * APIC doesn't broadcast EOIs (the guest must EOI the target
+		 * I/O APIC(s) directly).
+		 */
+		if ((kvm_lapic_get_reg(apic, APIC_SPIV) & APIC_SPIV_DIRECTED_EOI) &&
+		     kvm_lapic_respect_suppress_eoi_broadcast(apic->vcpu->kvm))
+			return;
+
 		apic->vcpu->arch.pending_ioapic_eoi = vector;
 		kvm_make_request(KVM_REQ_IOAPIC_EOI_EXIT, apic->vcpu);
 		return;
diff --git a/arch/x86/kvm/lapic.h b/arch/x86/kvm/lapic.h
index 282b9b7da98c..fe2db0f1d190 100644
--- a/arch/x86/kvm/lapic.h
+++ b/arch/x86/kvm/lapic.h
@@ -231,6 +231,9 @@ static inline int kvm_lapic_latched_init(struct kvm_vcpu *vcpu)
 
 bool kvm_apic_pending_eoi(struct kvm_vcpu *vcpu, int vector);
 
+bool kvm_lapic_advertise_suppress_eoi_broadcast(struct kvm *kvm);
+bool kvm_lapic_respect_suppress_eoi_broadcast(struct kvm *kvm);
+
 void kvm_wait_lapic_expire(struct kvm_vcpu *vcpu);
 
 void kvm_bitmap_or_dest_vcpus(struct kvm *kvm, struct kvm_lapic_irq *irq,
-- 
2.39.3


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ