[PATCH 2/3] KVM: x86: forward LAPIC ICR writes to userspace

From: muliang.shou

Date: Fri Jun 05 2026 - 05:46:39 EST


Intercept LAPIC ICR/ICR2 MMIO writes, record the forwarding payload, and
return to userspace with KVM_EXIT_DSM_SEND_IRQ when the pending request is
processed.

Signed-off-by: muliang.shou <muliang.shou@xxxxxxxxxxx>
---
arch/x86/kvm/lapic.c | 30 ++++++++++++++++++++++++++++++
arch/x86/kvm/x86.c | 14 ++++++++++++++
2 files changed, 44 insertions(+)

diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c
index 4078e624ca667..2485d50b45647 100644
--- a/arch/x86/kvm/lapic.c
+++ b/arch/x86/kvm/lapic.c
@@ -2561,6 +2561,10 @@ static int apic_mmio_write(struct kvm_vcpu *vcpu, struct kvm_io_device *this,
struct kvm_lapic *apic = to_lapic(this);
unsigned int offset = address - apic->base_address;
u32 val;
+#ifdef CONFIG_KVM_DSM_IRQ_FORWARD
+ u32 reg;
+ u32 dest_id = 0;
+#endif

if (!apic_mmio_in_range(apic, address))
return -EOPNOTSUPP;
@@ -2583,6 +2587,32 @@ static int apic_mmio_write(struct kvm_vcpu *vcpu, struct kvm_io_device *this,

val = *(u32*)data;

+#ifdef CONFIG_KVM_DSM_IRQ_FORWARD
+ reg = offset & 0xff0;
+ if (reg == APIC_ICR || reg == APIC_ICR2) {
+ kvm_lapic_reg_write(apic, reg, val);
+ if (reg == APIC_ICR) {
+ if (apic_x2apic_mode(apic)) {
+ dest_id = kvm_lapic_get_reg(apic, APIC_ICR2);
+ } else {
+ dest_id = kvm_lapic_get_reg(apic, APIC_ICR2);
+ dest_id = GET_XAPIC_DEST_FIELD(dest_id);
+ }
+ } else {
+ dest_id = GET_XAPIC_DEST_FIELD(val);
+ }
+
+ vcpu->arch.dsm_irq_forward_pending = true;
+ vcpu->arch.dsm_irq_forward_reg = reg;
+ vcpu->arch.dsm_irq_forward_val = val;
+ vcpu->arch.dsm_irq_forward_dest_id = dest_id;
+
+ kvm_make_request(KVM_REQ_DSM_IRQ_FORWARD, vcpu);
+
+ return 0;
+ }
+#endif
+
kvm_lapic_reg_write(apic, offset & 0xff0, val);

return 0;
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 0550359ed798f..42d6f8c977985 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -11338,6 +11338,20 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu)
goto out;
}
}
+#ifdef CONFIG_KVM_DSM_IRQ_FORWARD
+ if (kvm_check_request(KVM_REQ_DSM_IRQ_FORWARD, vcpu)) {
+ vcpu->run->exit_reason = KVM_EXIT_DSM_SEND_IRQ;
+ vcpu->run->lapic_irq.id = vcpu->vcpu_id;
+ vcpu->run->lapic_irq.val = vcpu->arch.dsm_irq_forward_reg;
+ vcpu->run->lapic_irq.val2 = vcpu->arch.dsm_irq_forward_val;
+ vcpu->run->lapic_irq.dest_id = vcpu->arch.dsm_irq_forward_dest_id;
+
+ vcpu->arch.dsm_irq_forward_pending = false;
+
+ r = 0;
+ goto out;
+ }
+#endif
}

if (kvm_check_request(KVM_REQ_EVENT, vcpu) || req_int_win ||
--
2.43.0