diff mbox series

hw/pci-host/pnv_phb4: Fix LSI irq source calculation overrun

Message ID 20241203060715.1149886-1-npiggin@gmail.com (mailing list archive)
State New
Headers show
Series hw/pci-host/pnv_phb4: Fix LSI irq source calculation overrun | expand

Commit Message

Nicholas Piggin Dec. 3, 2024, 6:07 a.m. UTC
PHB5 has big and small PHBs, that have 4k and 2k interrupt sources
respective. The LSI interrupt sources are set with a 9-bit register
field, that selects the aligned block of 8 irqs, giving a 4k range.
This is initialized by hardware to 0x1ff in order to select the
last 8 irq sources.

The small PHB is meant to ignore the MSB of the source ID, so the
LSI default would select the last 8 of 2k irqs. The PHB model does
not mask this bit consistently, which can cause the irq to be
miscalculated and the source irq array to be overrun.

Move the lsi_base calculation into a helper function that does the
masking and some extra checking.

Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
 hw/pci-host/pnv_phb4.c | 28 +++++++++++++++++++++++-----
 1 file changed, 23 insertions(+), 5 deletions(-)
diff mbox series

Patch

diff --git a/hw/pci-host/pnv_phb4.c b/hw/pci-host/pnv_phb4.c
index fb129f3266..5486ef44ea 100644
--- a/hw/pci-host/pnv_phb4.c
+++ b/hw/pci-host/pnv_phb4.c
@@ -458,6 +458,27 @@  static void pnv_phb4_update_all_msi_regions(PnvPHB4 *phb)
     }
 }
 
+static int pnv_phb4_get_lsi_base(PnvPHB4 *phb)
+{
+    int lsi_base, nr_irqs;
+
+    lsi_base = GETFIELD(PHB_LSI_SRC_ID, phb->regs[PHB_LSI_SOURCE_ID >> 3]);
+    if (!phb->big_phb) {
+        /* Small PHBs ignore the top bit of the source ID */
+        lsi_base &= ~0x100;
+    }
+    lsi_base <<= 3;
+
+    if (phb->big_phb) {
+        nr_irqs = PNV_PHB4_MAX_INTs;
+    } else {
+        nr_irqs = PNV_PHB4_MAX_INTs >> 1;
+    }
+    g_assert(lsi_base + 8 <= nr_irqs);
+
+    return lsi_base;
+}
+
 static void pnv_phb4_update_xsrc(PnvPHB4 *phb)
 {
     int shift, flags, i, lsi_base;
@@ -487,9 +508,7 @@  static void pnv_phb4_update_xsrc(PnvPHB4 *phb)
     phb->xsrc.esb_shift = shift;
     phb->xsrc.esb_flags = flags;
 
-    lsi_base = GETFIELD(PHB_LSI_SRC_ID, phb->regs[PHB_LSI_SOURCE_ID >> 3]);
-    lsi_base <<= 3;
-    lsi_base &= (xsrc->nr_irqs - 1);
+    lsi_base = pnv_phb4_get_lsi_base(phb);
 
     /* TODO: need a xive_source_irq_reset_lsi() */
     bitmap_zero(xsrc->lsi_map, xsrc->nr_irqs);
@@ -1597,8 +1616,7 @@  static void pnv_phb4_set_irq(void *opaque, int irq_num, int level)
     if (irq_num > 3) {
         phb_error(phb, "IRQ %x is not an LSI", irq_num);
     }
-    lsi_base = GETFIELD(PHB_LSI_SRC_ID, phb->regs[PHB_LSI_SOURCE_ID >> 3]);
-    lsi_base <<= 3;
+    lsi_base = pnv_phb4_get_lsi_base(phb);
     qemu_set_irq(phb->qirqs[lsi_base + irq_num], level);
 }