On Fri, 16 Oct 2026 18:10:44 +0800 Jinjie Ruan <[email protected]> wrote:
> From: Jinjie Ruan <[email protected]> > > Add the monitor-side plumbing shared by the monitor types: the > MSMON register definitions (MON_SEL, MBWU_FLT/CTL), the per-RIS > monitor instance array with the num-mbwu-mon and bw-nominal-bps > properties, and the read/write handling of the selection and > window registers (monitor/RIS selection with range clamping, > filter and control storage, write-one-to-clear overflow status). > > The monitor selection resolves a (RIS, monitor) pair through > mpam_mon_ris()/mpam_mbwu_mon(), and mpam_nrw_id_for() maps a > filter PARTID onto the internal PARTID whose configuration > governs it. No monitor type is advertised yet; the MBWU counter > model arrives with the next patch. > > Signed-off-by: Jinjie Ruan <[email protected]> > --- > hw/arm/mpam.c | 134 ++++++++++++++++++++++++++++++++++++++++++++++++++ > 1 file changed, 134 insertions(+) > > diff --git a/hw/arm/mpam.c b/hw/arm/mpam.c > index b484509..77e0277 100644 > --- a/hw/arm/mpam.c > +++ b/hw/arm/mpam.c > @@ -109,6 +109,36 @@ REG32(MPAMF_MBWUMON_IDR, 0x0090) > FIELD(MPAMF_MBWUMON_IDR, HAS_LONG, 30, 1) > FIELD(MPAMF_MBWUMON_IDR, HAS_CAPTURE, 31, 1) > > +REG32(MSMON_CFG_MON_SEL, 0x0800) > + FIELD(MSMON_CFG_MON_SEL, MON_SEL, 0, 8) This look wrong (LLM found it) seems to be 16 bits. Now narrower than that is fine, but we haven't been modifying these masks for other field defines to suit what is implemented so should look to be consistent. > + FIELD(MSMON_CFG_MON_SEL, RIS, 24, 4) > + > +REG32(MSMON_CFG_MBWU_FLT, 0x0820) > + FIELD(MSMON_CFG_MBWU_FLT, PARTID, 0, 16) > + FIELD(MSMON_CFG_MBWU_FLT, PMG, 16, 8) > + FIELD(MSMON_CFG_MBWU_FLT, RWBW, 30, 2) > + > +REG32(MSMON_CFG_MBWU_CTL, 0x0828) > + FIELD(MSMON_CFG_MBWU_CTL, TYPE, 0, 8) > + FIELD(MSMON_CFG_MBWU_CTL, MATCH_PARTID, 16, 1) > + FIELD(MSMON_CFG_MBWU_CTL, MATCH_PMG, 17, 1) > + FIELD(MSMON_CFG_MBWU_CTL, SCLEN, 19, 1) > + FIELD(MSMON_CFG_MBWU_CTL, SUBTYPE, 20, 4) 3 bits. There is another field in the top one. (LLM spotted this was different from Linux) > + FIELD(MSMON_CFG_MBWU_CTL, OFLOW_FRZ, 24, 1) > + FIELD(MSMON_CFG_MBWU_CTL, OFLOW_INTR, 25, 1) > + FIELD(MSMON_CFG_MBWU_CTL, OFLOW_STATUS, 26, 1) > + FIELD(MSMON_CFG_MBWU_CTL, CAPT_RESET, 27, 1) > + FIELD(MSMON_CFG_MBWU_CTL, CAPT_EVNT, 28, 3) > + FIELD(MSMON_CFG_MBWU_CTL, EN, 31, 1) > + > +REG32(MSMON_MBWU, 0x0860) /* 31-bit counter, bit31 NRDY */ > +REG32(MSMON_MBWU_L, 0x0880) /* 64-bit counter */ > +REG32(MSMON_MBWU_CAPTURE, 0x0868) > +REG32(MSMON_MBWU_CAPTURE_L, 0x0890) > +REG32(MSMON_CAPT_EVNT, 0x0808) > +REG32(MSMON_CFG_CSU_FLT, 0x0810) > +REG32(MSMON_CFG_CSU_CTL, 0x0818) > + > REG32(MPAMF_ERR_MSI_MPAM, 0x00dc) > REG32(MPAMF_ERR_MSI_ADDR_L, 0x00e0) > REG32(MPAMF_ERR_MSI_ADDR_H, 0x00e4) > @@ -232,6 +262,21 @@ typedef struct MpamfPerNrwId { > uint32_t cfg_mbw_prop; > } MpamfPerNrwId; > > +/* > + * One MBWU monitor instance. The counter is lazily advanced on > + * access: value = base + rate x (now - t0), where rate is the > + * nominal bandwidth scaled by the MBW_MAX fraction of the filtered > + * PARTID. Monitoring therefore reads back values that track the > + * configured bandwidth control. > + */ > +typedef struct MpamMbwuMon { > + uint32_t flt; > + uint32_t ctl; > + uint64_t base; > + int64_t t0; > + bool oflow; > +} MpamMbwuMon; > + > typedef struct Mpamf { > uint64_t idr; > uint32_t iidr; > @@ -257,6 +302,8 @@ typedef struct Mpamf { > > MpamfPerNrwId *per_nrw_id; > > + MpamMbwuMon *mbwu_mons; > + No blank line here. Like the one above should have been in this patch, this one should be in whatever comes after the white space. > } Mpamf; > > typedef struct MPAMMSCMemState { > MPAMMSCState parent; > } MPAMMSCMemState; > @@ -405,6 +497,20 @@ static uint64_t mpam_msc_read_reg(void *opaque, hwaddr > offset, > return mpamf->csumon_idr; > case A_MPAMF_MBWUMON_IDR: > return mpamf->mbwumon_idr; > + case A_MSMON_CFG_MON_SEL: > + return s->mon_sel; > + case A_MSMON_CFG_MBWU_FLT: > + return mpam_mbwu_mon(s, mpam_mon_ris(s))->flt; > + case A_MSMON_CFG_MBWU_CTL: > + { > + MpamMbwuMon *m = mpam_mbwu_mon(s, mpam_mon_ris(s)); > + uint32_t ctl = m->ctl; > + > + if (m->oflow) { > + ctl |= R_MSMON_CFG_MBWU_CTL_OFLOW_STATUS_MASK; See below. Either the write needs to actually clear the bit or this side needs to mask it from m->ctl > + } > + return ctl; > + } > case A_MPAMF_ERR_MSI_MPAM: > return mpamf->err_msi_mpam; > case A_MPAMF_ERR_MSI_ADDR_L: > @@ -623,6 +729,30 @@ static void mpam_msc_write_reg(void *opaque, hwaddr > offset, uint64_t value, > mpamf->per_nrw_id[nrw_part_sel].cfg_mbw_pbm[array_offset] = value; > return; > } > + case A_MSMON_CFG_MON_SEL: > + s->mon_sel = value & (R_MSMON_CFG_MON_SEL_MON_SEL_MASK | > + R_MSMON_CFG_MON_SEL_RIS_MASK); Mask on whether HAS_RIS? > + return; > + case A_MSMON_CFG_MBWU_FLT: > + mpam_mbwu_mon(s, mpam_mon_ris(s))->flt = value; I think this wants a write mask and bits of it are conditional on HAS_RWBW as well. > + return; > + case A_MSMON_CFG_MBWU_CTL: > + { > + MpamMbwuMon *m = mpam_mbwu_mon(s, mpam_mon_ris(s)); > + > + m->ctl = value; Probably wants a write mask as some of this register is read only. Given this doesn't mask out the oflow (to match a write one to clear) and the read path isn't masking that bit out, how does it ever appear to clear? > + if (value & R_MSMON_CFG_MBWU_CTL_OFLOW_STATUS_MASK) { > + m->oflow = false; /* write-one-to-clear */ > + } > + return; > + } > + case A_MSMON_CAPT_EVNT: > + case A_MSMON_CFG_CSU_FLT: > + case A_MSMON_CFG_CSU_CTL: > + qemu_log_mask(LOG_UNIMP, > + "MPAM: cache-usage monitoring not implemented (write > %lx)\n", > + offset); > + return;
