Add helpers that separate IRQ_CFG register presence from the runtime
IRQ enable guard. The Non-secure and Secure GERROR_IRQ_CFG register sets
both use SMMU_IDR0.MSI for presence, while writability additionally
requires GERROR_IRQEN to be clear. IRQ_CTRL and IRQ_CTRLACK share one
synchronous backing field in the current model.

Reads return RES0 when the register set is absent. Writes are ignored
when it is absent or GERROR_IRQEN is set. Apply the same checks to all
three GERROR_IRQ_CFG registers and mask reserved bits in CFG0 and CFG2.

Fixes: fae4be38b35d ("hw/arm/smmuv3: Implement MMIO write operations")
Fixes: 10a83cb9887e ("hw/arm/smmuv3: Skeleton")
Signed-off-by: Tao Tang <[email protected]>
Reviewed-by: Pierrick Bouvier <[email protected]>
---
 hw/arm/smmuv3.c | 118 +++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 116 insertions(+), 2 deletions(-)

diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c
index 47f0d575817..628911d3f21 100644
--- a/hw/arm/smmuv3.c
+++ b/hw/arm/smmuv3.c
@@ -1428,6 +1428,62 @@ smmu_cmdq_stage2_supported(SMMUv3State *s, SMMUSecSID 
sec_sid)
     return true;
 }
 
+/* Check whether the selected IRQ_CFG register set is present. */
+static bool smmu_irq_cfg_present(SMMUv3State *s, SMMUSecSID sec_sid,
+                                 SMMUIrq irq)
+{
+    SMMUv3RegBank *bank = smmuv3_bank(s, SMMU_SEC_SID_NS);
+
+    switch (irq) {
+    case SMMU_IRQ_GERROR:
+        switch (sec_sid) {
+        case SMMU_SEC_SID_NS:
+        case SMMU_SEC_SID_S:
+            return FIELD_EX32(bank->idr[0], IDR0, MSI);
+        case SMMU_SEC_SID_NUM:
+            g_assert_not_reached();
+        }
+        break;
+    case SMMU_IRQ_EVTQ:
+    case SMMU_IRQ_PRIQ:
+    case SMMU_IRQ_CMD_SYNC:
+        g_assert_not_reached();
+    }
+
+    g_assert_not_reached();
+}
+
+/* Check whether the selected IRQ_CFG register set is writable. */
+static bool smmu_irq_cfg_writable(SMMUv3State *s, SMMUSecSID sec_sid,
+                                  SMMUIrq irq)
+{
+    SMMUv3RegBank *bank = smmuv3_bank(s, sec_sid);
+    uint32_t irqen;
+
+    if (!smmu_irq_cfg_present(s, sec_sid, irq)) {
+        return false;
+    }
+
+    switch (irq) {
+    case SMMU_IRQ_GERROR:
+        irqen = FIELD_EX32(bank->irq_ctrl, IRQ_CTRL, GERROR_IRQEN);
+        break;
+    case SMMU_IRQ_EVTQ:
+    case SMMU_IRQ_PRIQ:
+    case SMMU_IRQ_CMD_SYNC:
+        g_assert_not_reached();
+    }
+
+    /* IRQ_CTRL and IRQ_CTRLACK share one synchronous backing field. */
+    return irqen == 0;
+}
+
+static bool
+smmu_gerror_irq_cfg_writable(SMMUv3State *s, SMMUSecSID sec_sid)
+{
+    return smmu_irq_cfg_writable(s, sec_sid, SMMU_IRQ_GERROR);
+}
+
 static int smmuv3_cmdq_consume(SMMUv3State *s, Error **errp, SMMUSecSID 
sec_sid)
 {
     SMMUState *bs = ARM_SMMU(s);
@@ -1735,7 +1791,14 @@ static MemTxResult smmu_writell(SMMUv3State *s, hwaddr 
offset,
 
     switch (offset) {
     case A_GERROR_IRQ_CFG0:
-        bank->gerror_irq_cfg0 = data;
+        if (!smmu_gerror_irq_cfg_writable(s, reg_sec_sid)) {
+            /* SMMU_(*_)_IRQ_CTRL.GERROR_IRQEN == 1: IGNORED this write */
+            qemu_log_mask(LOG_GUEST_ERROR, "GERROR_IRQ_CFG0 write ignored: "
+                         "register is RO when IRQ enabled\n");
+            return MEMTX_OK;
+        }
+
+        bank->gerror_irq_cfg0 = data & SMMU_GERROR_IRQ_CFG0_RESERVED;
         return MEMTX_OK;
     case A_STRTAB_BASE:
         bank->strtab_base = data;
@@ -1803,16 +1866,42 @@ static MemTxResult smmu_writel(SMMUv3State *s, hwaddr 
offset,
         smmuv3_cmdq_consume(s, &local_err, reg_sec_sid);
         break;
     case A_GERROR_IRQ_CFG0: /* 64b */
+        if (!smmu_gerror_irq_cfg_writable(s, reg_sec_sid)) {
+            qemu_log_mask(LOG_GUEST_ERROR, "GERROR_IRQ_CFG0 write ignored: "
+                          "register is RO when IRQ enabled\n");
+            return MEMTX_OK;
+        }
+
+        data &= SMMU_GERROR_IRQ_CFG0_RESERVED;
         bank->gerror_irq_cfg0 = deposit64(bank->gerror_irq_cfg0, 0, 32, data);
         break;
     case A_GERROR_IRQ_CFG0 + 4:
+        if (!smmu_gerror_irq_cfg_writable(s, reg_sec_sid)) {
+            qemu_log_mask(LOG_GUEST_ERROR, "GERROR_IRQ_CFG0 + 4 write ignored: 
"
+                          "register is RO when IRQ enabled\n");
+            return MEMTX_OK;
+        }
+
+        data &= SMMU_GERROR_IRQ_CFG0_RESERVED >> 32;
         bank->gerror_irq_cfg0 = deposit64(bank->gerror_irq_cfg0, 32, 32, data);
         break;
     case A_GERROR_IRQ_CFG1:
+        if (!smmu_gerror_irq_cfg_writable(s, reg_sec_sid)) {
+            qemu_log_mask(LOG_GUEST_ERROR, "GERROR_IRQ_CFG1 write ignored: "
+                          "register is RO when IRQ enabled\n");
+            return MEMTX_OK;
+        }
+
         bank->gerror_irq_cfg1 = data;
         break;
     case A_GERROR_IRQ_CFG2:
-        bank->gerror_irq_cfg2 = data;
+        if (!smmu_gerror_irq_cfg_writable(s, reg_sec_sid)) {
+            qemu_log_mask(LOG_GUEST_ERROR, "GERROR_IRQ_CFG2 write ignored: "
+                          "register is RO when IRQ enabled\n");
+            return MEMTX_OK;
+        }
+
+        bank->gerror_irq_cfg2 = data & SMMU_GERROR_IRQ_CFG2_RESERVED;
         break;
     case A_GBPA:
         /*
@@ -1938,6 +2027,11 @@ static MemTxResult smmu_readll(SMMUv3State *s, hwaddr 
offset,
 
     switch (offset) {
     case A_GERROR_IRQ_CFG0:
+        if (!smmu_irq_cfg_present(s, reg_sec_sid, SMMU_IRQ_GERROR)) {
+            *data = 0; /* RES0 */
+            return MEMTX_OK;
+        }
+
         *data = bank->gerror_irq_cfg0;
         return MEMTX_OK;
     case A_STRTAB_BASE:
@@ -2006,15 +2100,35 @@ static MemTxResult smmu_readl(SMMUv3State *s, hwaddr 
offset,
         *data = bank->gerrorn;
         return MEMTX_OK;
     case A_GERROR_IRQ_CFG0: /* 64b */
+        if (!smmu_irq_cfg_present(s, reg_sec_sid, SMMU_IRQ_GERROR)) {
+            *data = 0; /* RES0 */
+            return MEMTX_OK;
+        }
+
         *data = extract64(bank->gerror_irq_cfg0, 0, 32);
         return MEMTX_OK;
     case A_GERROR_IRQ_CFG0 + 4:
+        if (!smmu_irq_cfg_present(s, reg_sec_sid, SMMU_IRQ_GERROR)) {
+            *data = 0; /* RES0 */
+            return MEMTX_OK;
+        }
+
         *data = extract64(bank->gerror_irq_cfg0, 32, 32);
         return MEMTX_OK;
     case A_GERROR_IRQ_CFG1:
+        if (!smmu_irq_cfg_present(s, reg_sec_sid, SMMU_IRQ_GERROR)) {
+            *data = 0; /* RES0 */
+            return MEMTX_OK;
+        }
+
         *data = bank->gerror_irq_cfg1;
         return MEMTX_OK;
     case A_GERROR_IRQ_CFG2:
+        if (!smmu_irq_cfg_present(s, reg_sec_sid, SMMU_IRQ_GERROR)) {
+            *data = 0; /* RES0 */
+            return MEMTX_OK;
+        }
+
         *data = bank->gerror_irq_cfg2;
         return MEMTX_OK;
     case A_STRTAB_BASE: /* 64b */
-- 
2.34.1


Reply via email to