[PATCH v6 15/15] emul/ns16x50: implement IRQ emulation via vIOAPIC

dmukhin@xen.org posted 15 patches 3 days, 17 hours ago
There is a newer version of this series
[PATCH v6 15/15] emul/ns16x50: implement IRQ emulation via vIOAPIC
Posted by dmukhin@xen.org 3 days, 17 hours ago
From: Denis Mukhin <dmukhin@ford.com> 

PVH domains use vIOAPIC, not vPIC and NS16550 emulates ISA IRQs which cannot
be asserted on vIOAPIC.

{map,unmap}_domain_pirq_emuirq() infrastructure is modified by adding new
type of interrupt resources 'IRQ_EMU' which means 'emulated device IRQ'
(similarly to IRQ_MSI_EMU).

This is necessary to for IOAPIC emulation code to skip IRQ->PIRQ mapping
(vioapic_hwdom_map_gsi()) when guest OS unmasks vIOAPIC pin corresponding to
virtual device's IRQ.

Also, hvm_gsi_eoi() is modified to trigger assertion in hvm_gsi_deassert()
path for ISA IRQs.

Signed-off-by: Denis Mukhin <dmukhin@ford.com>
---
Changes since v5:
- did cosmetic renaming and dropped unneeded changes
- fixed ns16x50_irq_assert() and ns16x50_irq_deassert() as per Jan's
  suggestion in v4 (missed to address in v5)
- fixed __hvm_dpci_eoi()
- Link to v5: https://lore.kernel.org/xen-devel/20250828235409.2835815-16-dmukhin@ford.com/
---
 xen/arch/x86/hvm/vioapic.c        | 10 ++++++++++
 xen/arch/x86/include/asm/irq.h    |  6 ++++++
 xen/common/emul/vuart/ns16x50.c   | 28 ++++++++++++++++++++++++++++
 xen/drivers/passthrough/x86/hvm.c | 11 ++++++++++-
 4 files changed, 54 insertions(+), 1 deletion(-)

diff --git a/xen/arch/x86/hvm/vioapic.c b/xen/arch/x86/hvm/vioapic.c
index 7c725f9e471f..6314874b64f7 100644
--- a/xen/arch/x86/hvm/vioapic.c
+++ b/xen/arch/x86/hvm/vioapic.c
@@ -177,6 +177,16 @@ static int vioapic_hwdom_map_gsi(unsigned int gsi, unsigned int trig,
 
     ASSERT(is_hardware_domain(currd));
 
+    /*
+     * Interrupt is claimed by one of the platform virtual devices (e.g.
+     * NS16550); do nothing.
+     */
+    write_lock(&currd->event_lock);
+    ret = is_domain_emuirq_claimed(currd, gsi);
+    write_unlock(&currd->event_lock);
+    if ( ret )
+        return 0;
+
     /* Interrupt has been unmasked, bind it now. */
     ret = mp_register_gsi(gsi, trig, pol);
     if ( ret == -EEXIST )
diff --git a/xen/arch/x86/include/asm/irq.h b/xen/arch/x86/include/asm/irq.h
index 8bffec3bbfee..bdbe700274e9 100644
--- a/xen/arch/x86/include/asm/irq.h
+++ b/xen/arch/x86/include/asm/irq.h
@@ -168,6 +168,11 @@ void free_domain_pirqs(struct domain *d);
 int map_domain_emuirq_pirq(struct domain *d, int pirq, int emuirq);
 int unmap_domain_pirq_emuirq(struct domain *d, int pirq);
 
+#define domain_emuirq_claim(d, irq)     map_domain_emuirq_pirq(d, irq, IRQ_EMU)
+#define domain_emuirq_unclaim(d, irq)   unmap_domain_pirq_emuirq(d, irq)
+#define is_domain_emuirq_claimed(d, irq) \
+    (domain_pirq_to_emuirq(d, irq) != IRQ_UNBOUND)
+
 /* Evacuate interrupts assigned to CPUs not present in the CPU online map. */
 void fixup_irqs(void);
 void fixup_eoi(void);
@@ -221,6 +226,7 @@ void cleanup_domain_irq_mapping(struct domain *d);
 #define IRQ_UNBOUND (-1)
 #define IRQ_PT      (-2)
 #define IRQ_MSI_EMU (-3)
+#define IRQ_EMU     (-4)
 
 bool cpu_has_pending_apic_eoi(void);
 
diff --git a/xen/common/emul/vuart/ns16x50.c b/xen/common/emul/vuart/ns16x50.c
index bcbd765b815d..723b1b0bb55d 100644
--- a/xen/common/emul/vuart/ns16x50.c
+++ b/xen/common/emul/vuart/ns16x50.c
@@ -292,6 +292,8 @@ static void ns16x50_irq_assert(const struct vuart_ns16x50 *vdev)
 
     if ( has_vpic(d) )
         vector = hvm_isa_irq_assert(d, info->irq, vioapic_get_vector);
+    else if ( has_vioapic(d) )
+        vector = hvm_ioapic_assert(d, info->irq, false);
     else
         ASSERT_UNREACHABLE();
 
@@ -305,6 +307,8 @@ static void ns16x50_irq_deassert(const struct vuart_ns16x50 *vdev)
 
     if ( has_vpic(d) )
         hvm_isa_irq_deassert(d, info->irq);
+    else if ( has_vioapic(d) )
+        hvm_ioapic_deassert(d, info->irq);
     else
         ASSERT_UNREACHABLE();
 
@@ -815,6 +819,17 @@ static int ns16x50_init(void *arg)
         return rc;
     }
 
+    /* Claim virtual IRQ */
+    write_lock(&d->event_lock);
+    rc = domain_emuirq_claim(d, info->irq);
+    write_unlock(&d->event_lock);
+    if ( rc )
+    {
+        ns16x50_err(info, "virtual IRQ#%d: cannot claim: %d\n",
+                    info->irq, rc);
+        return rc;
+    }
+
     /* NB: report 115200 baud rate. */
     vdev->regs[NS16X50_REGS_NUM + UART_DLL] = divisor & 0xff;
     vdev->regs[NS16X50_REGS_NUM + UART_DLM] = (divisor >> 8) & 0xff;
@@ -834,9 +849,22 @@ static int ns16x50_init(void *arg)
 static void cf_check ns16x50_deinit(void *arg)
 {
     struct vuart_ns16x50 *vdev = arg;
+    const struct vuart_info *info;
+    struct domain *d;
+    int rc;
 
     ASSERT(vdev);
 
+    d = vdev->owner;
+    info = vdev->info;
+
+    write_lock(&d->event_lock);
+    rc = domain_emuirq_unclaim(d, info->irq);
+    write_unlock(&d->event_lock);
+    if ( rc )
+        ns16x50_err(vdev, "virtual IRQ#%d: cannot unclaim: %d\n",
+                    info->irq, rc);
+
     spin_lock(&vdev->lock);
     ns16x50_fifo_tx_flush(vdev);
     spin_unlock(&vdev->lock);
diff --git a/xen/drivers/passthrough/x86/hvm.c b/xen/drivers/passthrough/x86/hvm.c
index a2ca7e0e570c..20641194561f 100644
--- a/xen/drivers/passthrough/x86/hvm.c
+++ b/xen/drivers/passthrough/x86/hvm.c
@@ -922,7 +922,16 @@ static void __hvm_dpci_eoi(struct domain *d,
 
 static void hvm_gsi_eoi(struct domain *d, unsigned int gsi)
 {
-    struct pirq *pirq = pirq_info(d, gsi);
+    struct pirq *pirq;
+
+    /* Check if GSI is claimed by one of the virtual devices. */
+    if ( is_domain_emuirq_claimed(d, gsi) )
+    {
+        hvm_gsi_deassert(d, gsi);
+        return;
+    }
+
+    pirq = pirq_info(d, gsi);
 
     /* Check if GSI is actually mapped. */
     if ( !pirq_dpci(pirq) )
-- 
2.51.0