From: Jinjie Ruan <[email protected]>

Add MPAMF_ESR error recording with the IHI 0099 Table 7-1
encodings this model can raise, injected on the check-and-reject
write paths: Undefined_RIS_PART_SEL / Undefined_RIS_MON_SEL on
out-of-range selector RIS (the range check also subsumes nonzero
RIS on an MSC without RIS support), intPARTID_Range on INTERNAL=0
or out-of-range intPARTID, Monitor_Range on MON_SEL beyond every
monitor type, and MSMONCFG_ID_RANGE on out-of-range filter PARTID.
All are paths the kernel driver never hits; normal boot raises
nothing (verified).  A recorded error overwrites PARTID_MON/PMG/RIS
and sets OVRWR if a previous error was still latched.

Signed-off-by: Jinjie Ruan <[email protected]>
---
 hw/arm/mpam.c | 124 +++++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 117 insertions(+), 7 deletions(-)

diff --git a/hw/arm/mpam.c b/hw/arm/mpam.c
index 0389dad..6c43e3e 100644
--- a/hw/arm/mpam.c
+++ b/hw/arm/mpam.c
@@ -173,6 +173,14 @@ REG64(MPAMF_ESR, 0x00f8)
     FIELD(MPAMF_ESR, OVRWR, 31, 1)
     FIELD(MPAMF_ESR, RIS, 32, 4)
 
+/* IHI 0099 Table 7-1: MPAMF_ESR.ERR_CODE encodings this model can raise */
+#define MPAM_ERRCODE_PARTID_SEL_RANGE    1
+#define MPAM_ERRCODE_MSMONCFG_ID_RANGE   3
+#define MPAM_ERRCODE_MONITOR_RANGE       5
+#define MPAM_ERRCODE_INTPARTID_RANGE     6
+#define MPAM_ERRCODE_UNDEF_RIS_PART_SEL  8
+#define MPAM_ERRCODE_UNDEF_RIS_MON_SEL  10
+
 REG32(MPAMF_CFG_PART_SEL, 0x0100)
     FIELD(MPAMF_CFG_PART_SEL, PARTID_SEL, 0, 16)
     FIELD(MPAMF_CFG_PART_SEL, INTERNAL, 16, 1)
@@ -448,6 +456,33 @@ static void mpam_msc_update_err_irq(MPAMMSCState *s)
     qemu_set_irq(s->err_irq, int_en && active);
 }
 
+/*
+ * Record an error in MPAMF_ESR and raise the error interrupt if enabled.
+ * PARTID_MON also carries a selector value (e.g. MON_SEL) for the errors
+ * where Table 7-1 lists "MON_SEL and RIS" as the fields in effect.
+ */
+static void mpam_msc_inject_error(MPAMMSCState *s, uint32_t errcode,
+                                  uint16_t partid_mon, uint8_t pmg,
+                                  uint8_t ris)
+{
+    uint64_t esr = 0;
+
+    if (FIELD_EX64(s->esr, MPAMF_ESR, ERR_CODE)) {
+        esr = FIELD_DP64(esr, MPAMF_ESR, OVRWR, 1);
+    }
+    esr = FIELD_DP64(esr, MPAMF_ESR, PARID_MON, partid_mon);
+    esr = FIELD_DP64(esr, MPAMF_ESR, PMG, pmg);
+    esr = FIELD_DP64(esr, MPAMF_ESR, ERR_CODE, errcode);
+    esr = FIELD_DP64(esr, MPAMF_ESR, RIS, ris);
+    s->esr = esr;
+    mpam_msc_sync_esr_ecr(s);
+
+    qemu_log_mask(LOG_GUEST_ERROR,
+                  "MPAM: error 0x%x partid/mon %u pmg %u ris %u\n",
+                  errcode, partid_mon, pmg, ris);
+    mpam_msc_update_err_irq(s);
+}
+
 /*
  * IHI 0099 8.3: the overflow interrupt is signalled while any monitor has
  * latched OFLOW_STATUS with OFLOW_INTR set; clearing OFLOW_STATUS of all
@@ -852,18 +887,21 @@ static void mpam_msc_write_reg(void *opaque, hwaddr 
offset, uint64_t value,
                           "MPAM: Unexpected write to CFG_PART_SEL Mask=%x 
Value=%lx\n",
                           MPAMF_CFG_PART_SEL_WRITE_MASK, value);
         }
-        /* Field matches for all RIS */
-        if (!FIELD_EX64(mpamf->idr, MPAMF_IDR, HAS_RIS) &&
-            FIELD_EX32(value, MPAMF_CFG_PART_SEL, RIS) != 0) {
-            qemu_log_mask(LOG_UNIMP,
-                          "MPAM: Unexpected write of non 0 RIS on MSC with 
!HAS_RIS\n");
-            return;
-        }
+        /*
+         * Field matches for all RIS. The range check subsumes the !HAS_RIS
+         * case: such an MSC implements only RIS 0, so any nonzero RIS is
+         * undefined (IHI 0099 Table 7-1: Undefined_RIS_PART_SEL).
+         */
         if (FIELD_EX32(value, MPAMF_CFG_PART_SEL, RIS) >= s->num_ris) {
             qemu_log_mask(LOG_GUEST_ERROR,
                           "MPAM: RIS %d out of range (num-ris %d)\n",
                           FIELD_EX32(value, MPAMF_CFG_PART_SEL, RIS),
                           s->num_ris);
+            mpam_msc_inject_error(s, MPAM_ERRCODE_UNDEF_RIS_PART_SEL,
+                                  FIELD_EX32(value, MPAMF_CFG_PART_SEL,
+                                             PARTID_SEL),
+                                  0,
+                                  FIELD_EX32(value, MPAMF_CFG_PART_SEL, RIS));
             return;
         }
         {
@@ -894,6 +932,23 @@ static void mpam_msc_write_reg(void *opaque, hwaddr 
offset, uint64_t value,
                           "MPAM: Unexpected write to INTPARTID when 
!HAS_PARTID_NRW\n");
             return;
         }
+        /*
+         * Table 7-1: writing INTERNAL as zero, or an out-of-range
+         * intPARTID, raises intPARTID_Range and is not written.
+         */
+        if (!FIELD_EX32(value, MPAMF_MPAMCFG_INTPARTID, INTERNAL) ||
+            FIELD_EX32(value, MPAMF_MPAMCFG_INTPARTID, INTPARTID) >=
+                s->num_int_partid) {
+            qemu_log_mask(LOG_GUEST_ERROR,
+                          "MPAM: INTPARTID write invalid (value %x, max %d)\n",
+                          FIELD_EX32(value, MPAMF_MPAMCFG_INTPARTID, 
INTPARTID),
+                          s->num_int_partid - 1);
+            mpam_msc_inject_error(s, MPAM_ERRCODE_INTPARTID_RANGE,
+                                  FIELD_EX32(value, MPAMF_MPAMCFG_INTPARTID,
+                                             INTPARTID),
+                                  0, s->ris);
+            return;
+        }
 
         mpamf->cfg_intpartid[s->part_sel] =
             FIELD_EX32(value, MPAMF_MPAMCFG_INTPARTID, INTPARTID);
@@ -996,12 +1051,53 @@ static void mpam_msc_write_reg(void *opaque, hwaddr 
offset, uint64_t value,
         return;
     }
     case A_MSMON_CFG_MON_SEL:
+    {
+        uint32_t mon = FIELD_EX32(value, MSMON_CFG_MON_SEL, MON_SEL);
+        uint32_t ris = FIELD_EX32(value, MSMON_CFG_MON_SEL, RIS);
+        uint32_t num_mon = MAX(s->num_csu_mon, s->num_mbwu_mon);
+
+        /*
+         * Check-and-reject (IHI 0099 7.2.9): RIS selects a nonexistent
+         * resource instance or MON_SEL exceeds every monitor type this
+         * MSC implements.
+         */
+        if (ris >= s->num_ris) {
+            qemu_log_mask(LOG_GUEST_ERROR,
+                          "MPAM: MON_SEL RIS %d out of range (num-ris %d)\n",
+                          ris, s->num_ris);
+            mpam_msc_inject_error(s, MPAM_ERRCODE_UNDEF_RIS_MON_SEL,
+                                  mon, 0, ris);
+            return;
+        }
+        if (mon >= num_mon) {
+            qemu_log_mask(LOG_GUEST_ERROR,
+                          "MPAM: MON_SEL %d out of range (max %d)\n",
+                          mon, num_mon - 1);
+            mpam_msc_inject_error(s, MPAM_ERRCODE_MONITOR_RANGE,
+                                  mon, 0, ris);
+            return;
+        }
         s->mon_sel = value & (R_MSMON_CFG_MON_SEL_MON_SEL_MASK |
                               R_MSMON_CFG_MON_SEL_RIS_MASK);
         return;
+    }
     case A_MSMON_CFG_MBWU_FLT:
+    {
+        uint32_t partid = FIELD_EX32(value, MSMON_CFG_MBWU_FLT, PARTID);
+
+        if (partid >= s->num_partid) {
+            qemu_log_mask(LOG_GUEST_ERROR,
+                          "MPAM: MBWU_FLT PARTID %d out of range\n", partid);
+            mpam_msc_inject_error(s, MPAM_ERRCODE_MSMONCFG_ID_RANGE,
+                                  partid,
+                                  FIELD_EX32(value, MSMON_CFG_MBWU_FLT, PMG),
+                                  FIELD_EX32(s->mon_sel, MSMON_CFG_MON_SEL,
+                                             RIS));
+            return;
+        }
         mpam_mbwu_mon(s, mpam_mon_ris(s))->flt = value;
         return;
+    }
     case A_MSMON_CFG_MBWU_CTL:
     {
         MpamMbwuMon *m = mpam_mbwu_mon(s, mpam_mon_ris(s));
@@ -1025,8 +1121,22 @@ static void mpam_msc_write_reg(void *opaque, hwaddr 
offset, uint64_t value,
         return;
     }
     case A_MSMON_CFG_CSU_FLT:
+    {
+        uint32_t partid = FIELD_EX32(value, MSMON_CFG_CSU_FLT, PARTID);
+
+        if (partid >= s->num_partid) {
+            qemu_log_mask(LOG_GUEST_ERROR,
+                          "MPAM: CSU_FLT PARTID %d out of range\n", partid);
+            mpam_msc_inject_error(s, MPAM_ERRCODE_MSMONCFG_ID_RANGE,
+                                  partid,
+                                  FIELD_EX32(value, MSMON_CFG_CSU_FLT, PMG),
+                                  FIELD_EX32(s->mon_sel, MSMON_CFG_MON_SEL,
+                                             RIS));
+            return;
+        }
         mpam_csu_mon(s, mpam_mon_ris(s))->flt = value;
         return;
+    }
     case A_MSMON_CFG_CSU_CTL:
         mpam_csu_mon(s, mpam_mon_ris(s))->ctl = value;
         return;
-- 
2.43.0


Reply via email to