* [PATCH 00/37] Add Arm MPAM emulation
@ 2026-10-16 9:46 Jinjie Ruan
2026-10-16 10:20 ` Peter Maydell
` (2 more replies)
0 siblings, 3 replies; 14+ messages in thread
From: Jinjie Ruan @ 2026-10-16 9:46 UTC (permalink / raw)
To: anisinha, armbru, eblake, imammedo, michael.roth, mst, pbonzini,
peter.maydell, qemu-devel, qemu-arm
Cc: ruanjinjie
From: Jinjie Ruan <ruanjinjie@huawei.com>
MPAM (Memory System Resource Partitioning and Monitoring) is Arm's
architecture for partitioning and monitoring system memory resources
- Cache portions
- Capacity limits
- Memory bandwidth and priority
And MPAM is the Arm counterpart of x86 RDT, which Linux exposes to user
space via resctrl.
QEMU so far has no MPAM support at all: the CPU-side system registers
are missing (a guest kernel that probes FEAT_MPAM takes an UNDEFINED
exception), and there are no Memory System Components (MSCs), so a virt
guest cannot exercise resctrl on Arm.
This series adds MPAM 1.1 emulation for the virt machine,
and the structure is as below:
* target/arm (patches 1-3): the CPU registers:
- MPAMIDR_EL1 and MPAM0_EL1/MPAM1_EL1
- MPAM2_EL2/MPAMHCR_EL2
- MPAMSM_EL1 for FEAT_MPAM_SMS
* hw/acpi (patch 4): PPTT revision 3 with Cache IDs, which the kernel
needs to resolve the MPAM table's cache references to PPTT cache
nodes.
* hw/arm/mpam device model (patches 5-15): built up layer by layer
- The register map as pure registerfields declarations
- The base MSC device with MMIO access
- The mpam-msc-mem and mpam-msc-cache subtypes
- MPAMCFG_PART_SEL selection
- Per-RIS partition config storage
- The partitioning features one at a time (CPOR cache portions, MBW
bandwidth, PRI priority, CCAP capacity)
- The ECR/ESR register surface
* hw/arm/mpam (patch 16): PARTID narrowing via MPAMCFG_INTPARTID with
the identity-wrap default map
- The linux mainline driver exercises it by default and resctrl
reports num_closids=32.
* hw/arm/virt + hw/acpi (patches 17-23): the board wiring, one concern
per patch
- Machine properties
- The MMIO window reservation
- Device locator/irq properties
- Memory-side MSC creation
- Cache MSC creation derived from the smp cache topology
- The ACPI MPAM table, split into the DEN0065 MSC node builders
and the table assembly
* Monitoring (patches 24-29):
- The MSMON selection/window infrastructure
- The MBWU bandwidth monitor whose counters track the configured
bandwidth control (MBW_MAX or MBW_PBM, whichever the guest drives)
- CMIN/CASSOC storage
- PRI advertised on the memory side (backing the driver's MBPRI
pseudo-resource)
- The CSU occupancy monitor (a snapshot of the capacity scaled by
the CPBM fraction)
- The board sizing of that capacity per cache level
*Error reporting (patches 30-33):
- ECR/ESR as MSC-global registers (IHI 0099 4.3)
- Device reset with power-on state initialisation
- The two level-held interrupt outputs (8.2/8.3)
- Table 7-1 error recording injected on the invalid-configuration
write paths.
* patch 34: the board wiring of the MSC interrupts to free SPIs on the
GIC, with the assigned GSIVs reported in the ACPI MPAM table.
* patch 35: defaulting to a single node when none is specified.
* patches 36-37: the query-mpam-cache QMP introspection, split into
schema+stub and implementation.
With this, a Linux guest booted with -M virt,mpam=on (plus NUMA nodes)
mounts resctrl with the full schemata set (L2/L3 CAT, capacity, MBA,
priority), and MBM bandwidth and L2/L3 occupancy counters track the
configured controls.
Verified with a Linux 7.3-rc6 guest on gic-v3: the arm_mpam driver probes
cleanly, the resctrl regression suite passes and no MPAM error interrupt
fires during normal boot.
Patches 1, 5-8 and 36-37 carry Jonathan Cameron's authorship, being
derived from his RFC series and its QMP face. Compared to that RFC [1],
this series completes most of its TODO list and tracks what the
mainline arm_mpam driver now requires:
- Monitoring
- The MSC interrupts
- Error reporting on invalid parameters
- PARTID narrowing
- The missing CPU registers
- PPTT rev 3 with Cache IDs
- CMIN/CASSOC
- Memory-side
- CSU monitoring
Still open from the RFC's TODO list: ACPI table unit tests. Like the
RFC, this is ACPI-only; the DT binding remains unimplemented.
[1] https://patchew.org/QEMU/20230808115713.2613-1-Jonathan.Cameron@huawei.com/
Jinjie Ruan (30):
target/arm: Add MPAM2_EL2 and MPAMHCR_EL2 registers
target/arm: Add MPAMSM_EL1 register
hw/acpi: Build PPTT rev 3 with cache IDs for MPAM cross-references
hw/arm/mpam: Handle MPAMCFG_PART_SEL selection
hw/arm/mpam: Add per-RIS partition config storage
hw/arm/mpam: Implement CPOR cache portion partitioning
hw/arm/mpam: Implement MBW bandwidth partitioning
hw/arm/mpam: Implement PRI priority partitioning
hw/arm/mpam: Implement CCAP capacity partitioning
hw/arm/mpam: Add ECR/ESR and error MSI register storage
hw/arm/mpam: Implement PARTID narrowing
hw/arm/virt: Add mpam and mpam-min-msc machine properties
hw/arm/virt: Reserve the MPAM MSC MMIO window
hw/arm/mpam: Add node and interrupt GSIV properties
hw/arm/virt: Create memory-side MPAM MSCs
hw/arm/virt: Create L2/L3 cache MPAM MSCs
hw/acpi: Add MPAM table MSC node builders
hw/acpi: Build the MPAM ACPI table
hw/arm/mpam: Add MSMON monitor selection and window configuration
hw/arm/mpam: Emulate MBWU bandwidth monitoring
hw/arm/mpam: Implement CMIN/CASSOC storage
hw/arm/mpam: Advertise PRI on memory-side MSCs
hw/arm/mpam: Emulate CSU cache-usage monitoring
hw/arm/virt: Size the cache MSC occupancy model by level
hw/arm/mpam: Handle ECR/ESR as MSC-global registers
hw/arm/mpam: Add device reset and power-on state initialisation
hw/arm/mpam: Add error and overflow interrupt outputs
hw/arm/mpam: Record errors and reject invalid configuration writes
hw/arm/virt: Wire MSC error and overflow interrupts to the GIC
hw/arm/virt: Default to implicit node 0 for MPAM without -numa
Jonathan Cameron (7):
target/arm: Add support for MPAM CPU registers
hw/arm/mpam: Add MPAM MSC register map
hw/arm/mpam: Add MPAM MSC base device
hw/arm/mpam: Add mpam-msc-mem memory-side MSC subtype
hw/arm/mpam: Add mpam-msc-cache MSC subtype
qapi: Add MPAM cache introspection schema and stub
hw/arm/mpam: Implement query-mpam-cache
hw/acpi/aml-build.c | 31 +-
hw/arm/Kconfig | 4 +
hw/arm/meson.build | 3 +
hw/arm/mpam-qapi-stubs.c | 14 +
hw/arm/mpam-qapi.c | 62 ++
hw/arm/mpam.c | 1620 +++++++++++++++++++++++++++++++++++
hw/arm/virt-acpi-build.c | 231 +++++
hw/arm/virt.c | 239 ++++++
include/hw/acpi/aml-build.h | 8 +
include/hw/arm/mpam.h | 23 +
include/hw/arm/virt.h | 3 +
qapi/meson.build | 1 +
qapi/mpam.json | 120 +++
qapi/qapi-schema.json | 1 +
target/arm/cpu-features.h | 11 +
target/arm/cpu.c | 10 +-
target/arm/cpu.h | 6 +
target/arm/helper.c | 33 +
18 files changed, 2410 insertions(+), 10 deletions(-)
create mode 100644 hw/arm/mpam-qapi-stubs.c
create mode 100644 hw/arm/mpam-qapi.c
create mode 100644 hw/arm/mpam.c
create mode 100644 include/hw/arm/mpam.h
create mode 100644 qapi/mpam.json
--
2.43.0
^ permalink raw reply [flat|nested] 14+ messages in thread* Re: [PATCH 00/37] Add Arm MPAM emulation 2026-10-16 9:46 [PATCH 00/37] Add Arm MPAM emulation Jinjie Ruan @ 2026-10-16 10:20 ` Peter Maydell 2026-10-19 1:57 ` Jinjie Ruan 2026-10-19 9:22 ` Jinjie Ruan 2026-10-20 22:43 ` Drew Fustini 2 siblings, 1 reply; 14+ messages in thread From: Peter Maydell @ 2026-10-16 10:20 UTC (permalink / raw) To: Jinjie Ruan Cc: anisinha, armbru, eblake, imammedo, michael.roth, mst, pbonzini, qemu-devel, qemu-arm, ruanjinjie On Fri, 16 Oct 2026 at 10:47, Jinjie Ruan <2118537295@qq.com> wrote: > > From: Jinjie Ruan <ruanjinjie@huawei.com> > > MPAM (Memory System Resource Partitioning and Monitoring) is Arm's > architecture for partitioning and monitoring system memory resources > - Cache portions > - Capacity limits > - Memory bandwidth and priority > > And MPAM is the Arm counterpart of x86 RDT, which Linux exposes to user > space via resctrl. > > QEMU so far has no MPAM support at all: the CPU-side system registers > are missing (a guest kernel that probes FEAT_MPAM takes an UNDEFINED > exception), and there are no Memory System Components (MSCs), so a virt > guest cannot exercise resctrl on Arm. > > This series adds MPAM 1.1 emulation for the virt machine, > and the structure is as below: > With this, a Linux guest booted with -M virt,mpam=on (plus NUMA nodes) > mounts resctrl with the full schemata set (L2/L3 CAT, capacity, MBA, > priority), and MBM bandwidth and L2/L3 occupancy counters track the > configured controls. I guess my question here is "how much is this actually doing, and how much is it 'we provide enough software-facing interface to keep the guest happy'" ? My (rather shallow) understanding of MPAM is that it's essentially adding tags to memory transactions and then accounting how much bandwidth each tag is using. That seems obviously infeasible for emulation, but on the other hand this looks like it's rather larger than a minimal "we provide the register interface but it does nothing", so what are we doing? thanks -- PMM ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 00/37] Add Arm MPAM emulation 2026-10-16 10:20 ` Peter Maydell @ 2026-10-19 1:57 ` Jinjie Ruan 2026-10-19 9:24 ` Peter Maydell 0 siblings, 1 reply; 14+ messages in thread From: Jinjie Ruan @ 2026-10-19 1:57 UTC (permalink / raw) To: Peter Maydell, Jinjie Ruan Cc: anisinha, armbru, eblake, imammedo, michael.roth, mst, pbonzini, qemu-devel, qemu-arm 在 2026/10/16 18:20, Peter Maydell 写道: > On Fri, 16 Oct 2026 at 10:47, Jinjie Ruan <2118537295@qq.com> wrote: >> >> From: Jinjie Ruan <ruanjinjie@huawei.com> >> >> MPAM (Memory System Resource Partitioning and Monitoring) is Arm's >> architecture for partitioning and monitoring system memory resources >> - Cache portions >> - Capacity limits >> - Memory bandwidth and priority >> >> And MPAM is the Arm counterpart of x86 RDT, which Linux exposes to user >> space via resctrl. >> >> QEMU so far has no MPAM support at all: the CPU-side system registers >> are missing (a guest kernel that probes FEAT_MPAM takes an UNDEFINED >> exception), and there are no Memory System Components (MSCs), so a virt >> guest cannot exercise resctrl on Arm. >> >> This series adds MPAM 1.1 emulation for the virt machine, >> and the structure is as below: > >> With this, a Linux guest booted with -M virt,mpam=on (plus NUMA nodes) >> mounts resctrl with the full schemata set (L2/L3 CAT, capacity, MBA, >> priority), and MBM bandwidth and L2/L3 occupancy counters track the >> configured controls. > > I guess my question here is "how much is this actually doing, > and how much is it 'we provide enough software-facing interface > to keep the guest happy'" ? My (rather shallow) understanding of > MPAM is that it's essentially adding tags to memory transactions > and then accounting how much bandwidth each tag is using. That > seems obviously infeasible for emulation, but on the other hand Hi Peter, Yes, I agree with you. Implementing MPAM bandwidth control and partitioning at the performance-effect level in QEMU is neither feasible nor necessary, and it is not in line with QEMU’s original purpose as a fast functional emulator. We only need to implement the functionality-related parts (i.e., the register interfaces and control flow) to ensure that the software stack can correctly recognize and configure MPAM, without simulating the real performance impact produced by those configurations. > this looks like it's rather larger than a minimal "we provide > the register interface but it does nothing", so what are we doing? By implementing MPAM-related register interfaces and control flow, as well as the MSC hardware topology in QEMU, we can understand the resctrl framework and interfaces in Linux, along with the related hardware topology discovery process. This facilitates kernel debugging and pre-silicon testing, and also allows us to debug and test test cases, avoiding the complexity and slowness of testing on FPGA. Through this series of QEMU implementations, I discovered many kernel issues and test-case issues in advance before FPGA testing, accelerating the development and testing of new kernel features (such as narrow PARTID). Moreover, as far as I understand, many hardware vendors have not yet implemented MPAM. Implementing it in QEMU helps enable MPAM on Linux, and then understand, learn, test, and even develop some MPAM-related features. Link: https://lore.kernel.org/all/20261012082337.822258-1-ruanjinjie@huawei.com/ > > thanks > -- PMM -- Best regards, Jinjie ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 00/37] Add Arm MPAM emulation 2026-10-19 1:57 ` Jinjie Ruan @ 2026-10-19 9:24 ` Peter Maydell 2026-10-19 11:46 ` Jinjie Ruan 0 siblings, 1 reply; 14+ messages in thread From: Peter Maydell @ 2026-10-19 9:24 UTC (permalink / raw) To: Jinjie Ruan Cc: Jinjie Ruan, anisinha, armbru, eblake, imammedo, michael.roth, mst, pbonzini, qemu-devel, qemu-arm On Mon, 19 Oct 2026 at 02:57, Jinjie Ruan <ruanjinjie@huawei.com> wrote: > 在 2026/10/16 18:20, Peter Maydell 写道: > > I guess my question here is "how much is this actually doing, > > and how much is it 'we provide enough software-facing interface > > to keep the guest happy'" ? My (rather shallow) understanding of > > MPAM is that it's essentially adding tags to memory transactions > > and then accounting how much bandwidth each tag is using. That > > seems obviously infeasible for emulation, but on the other hand > > Hi Peter, > > Yes, I agree with you. > > Implementing MPAM bandwidth control and partitioning at the > performance-effect level in QEMU is neither feasible nor necessary, and > it is not in line with QEMU’s original purpose as a fast functional > emulator. > > We only need to implement the functionality-related parts (i.e., the > register interfaces and control flow) to ensure that the software stack > can correctly recognize and configure MPAM, without simulating the real > performance impact produced by those configurations. > > > this looks like it's rather larger than a minimal "we provide > > the register interface but it does nothing", so what are we doing? > > By implementing MPAM-related register interfaces and control flow, as > well as the MSC hardware topology in QEMU, we can understand the resctrl > framework and interfaces in Linux, along with the related hardware > topology discovery process. This facilitates kernel debugging and > pre-silicon testing, and also allows us to debug and test test cases, > avoiding the complexity and slowness of testing on FPGA. Yes, but what is your implementation actually *doing* ? If software reads some MPAM resource monitor, does it read zeroes, or something else, or do we implement no monitors at all? 2400 lines is a lot of code for "nothing actually happens". -- PMM ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 00/37] Add Arm MPAM emulation 2026-10-19 9:24 ` Peter Maydell @ 2026-10-19 11:46 ` Jinjie Ruan 2026-10-19 12:01 ` Peter Maydell 0 siblings, 1 reply; 14+ messages in thread From: Jinjie Ruan @ 2026-10-19 11:46 UTC (permalink / raw) To: Peter Maydell, Jinjie Ruan Cc: anisinha, armbru, eblake, imammedo, michael.roth, mst, pbonzini, qemu-devel, qemu-arm 在 2026/10/19 17:24, Peter Maydell 写道: > On Mon, 19 Oct 2026 at 02:57, Jinjie Ruan <ruanjinjie@huawei.com> wrote: >> 在 2026/10/16 18:20, Peter Maydell 写道: >>> I guess my question here is "how much is this actually doing, >>> and how much is it 'we provide enough software-facing interface >>> to keep the guest happy'" ? My (rather shallow) understanding of >>> MPAM is that it's essentially adding tags to memory transactions >>> and then accounting how much bandwidth each tag is using. That >>> seems obviously infeasible for emulation, but on the other hand >> Hi Peter, >> >> Yes, I agree with you. >> >> Implementing MPAM bandwidth control and partitioning at the >> performance-effect level in QEMU is neither feasible nor necessary, and >> it is not in line with QEMU’s original purpose as a fast functional >> emulator. >> >> We only need to implement the functionality-related parts (i.e., the >> register interfaces and control flow) to ensure that the software stack >> can correctly recognize and configure MPAM, without simulating the real >> performance impact produced by those configurations. >> >>> this looks like it's rather larger than a minimal "we provide >>> the register interface but it does nothing", so what are we doing? >> By implementing MPAM-related register interfaces and control flow, as >> well as the MSC hardware topology in QEMU, we can understand the resctrl >> framework and interfaces in Linux, along with the related hardware >> topology discovery process. This facilitates kernel debugging and >> pre-silicon testing, and also allows us to debug and test test cases, >> avoiding the complexity and slowness of testing on FPGA. > Yes, but what is your implementation actually *doing* ? If > software reads some MPAM resource monitor, does it read zeroes, > or something else, or do we implement no monitors at all? > 2400 lines is a lot of code for "nothing actually happens". Monitors are implemented and do not return inert zeroes — both CSU and MBWU read back values thattrack the partition configuration software has programmed. CSU (MPAMF_MSMON_CSU) — mpam_csu_value(), hw/arm/mpam.c:615, as the "hw/arm/mpam: Emulate CSU cache-usage monitoring" patch done: - Monitor disabled → returns 0. - Monitor enabled with MATCH_PARTID → returns csu_capacity × popcount(CPBM)/32, i.e. the fraction of cache capacity the filtered PARTID is currently allowed to occupy. Granting/revoking cache ways in CPBM changes the readback. static uint32_t mpam_csu_value(MPAMMSCState *s, Mpamf *mf, MpamCsuMon *m) { if (FIELD_EX32(m->ctl, MSMON_CFG_CSU_CTL, EN) && FIELD_EX32(m->ctl, MSMON_CFG_CSU_CTL, MATCH_PARTID)) { uint32_t partid = FIELD_EX32(m->flt, MSMON_CFG_CSU_FLT, PARTID); uint32_t nrw = mpam_nrw_id_for(s, mf, partid); uint32_t cpbm = mf->per_nrw_id[nrw].cfg_cpbm[0]; return muldiv64(s->csu_capacity, ctpop32(cpbm), MPAM_CACHE_PART); } return 0; } The cfg_cpbm[0] was set by software by partid in patch "hw/arm/mpam: Implement CPOR cache portion partitioning" + case A_MPAMF_MPAMCFG_CPBM0... + (A_MPAMF_MPAMCFG_CPBM0 + ((MPAM_CACHE_PART + 31) / 32 - 1) * 4): + { + uint32_t array_offset; ...... + array_offset = (offset - A_MPAMF_MPAMCFG_CPBM0) / sizeof(uint32_t); + mpamf->per_nrw_id[nrw_part_sel].cfg_cpbm[array_offset] = value; MBWU (MSMON_MBWU / MSMON_MBWU_L) — mpam_mbwu_value(), hw/arm/mpam.c:542: - Lazily accumulated: value = base + rate × (now − t0), rate = bw_nominal × MBW_MAX fraction (or MBW_PBM popcount ratio, if set). - Disabled → rate = 0, readback stays at base (0 at reset). - Enabled → counts up over time, wraps at 48 bits, latches OFLOW_STATUS, raises the overflow IRQ. The rate depends on the MBW_MAX/PBM programmed for the filtered PARTID, so raising a PARTID's bandwidth quota is visible in that monitor's readback. if (cfg->cfg_mbw_pbm[0]) { frac_num = __builtin_popcount(cfg->cfg_mbw_pbm[0] & ((1u << MPAM_MBW_PART) - 1)); frac_den = MPAM_MBWetPART; } else { /* MBW_MAX is a 16-bit fraction; 0xffff = full bandwidth. */ frac_num = cfg->cfg_mbw_max & 0xffff; frac_den = 0xffff; } rate_mbs = muldiv64(rate_mbs, frac_num, frac_den); The MBW_MAX was set by software as below in patch "hw/arm/mpam: Implement MBW bandwidth partitioning" case A_MPAMF_MPAMCFG_MBW_MAX: ...... mpamf->per_nrw_id[nrw_part_sel].cfg_mbw_max = value; So the two monitors are closed-loop with the control plane: write CPBM/MBW_MAX → the matching PARTID's CSU/MBWU readback moves. Not no-ops. The 2400 lines break down as: - MPAM2/MPAMHCR/MPAMSM_EL1 + PPTT rev3 cache IDs (guest visibility) - MSC (Memory System Component) register map, mem/cache MSC subtypes, partition control (CPOR/MBW/PRI/CCAP) + PARTID narrowing (*extended feature*), the CSU/MBWU monitors above, error/overflow IRQ + ECR/ESR, reset/power-on state - MSC creation/sizing, MMIO window, IRQ-to-GIC wiring, ACPI MPAM table - qapi/mpam.json + query-mpam-cache: introspection - qemu boot parameters for mpam. The bulk sits in hw/arm/mpam.c (1620 lines, two-thirds of the total), and most of that is IHI 0099 register field definitions plus the MMIO read/write dispatch — fixed overhead from the spec's many registers, not logic bloat. One honest caveat: monitors reflect configuration, not measured traffic — rate is derived from the programmed MBW_MAX/PBM fraction, not from real memory transactions. That is intentional for a functional/configuration model. Thanks, Jinjie > > -- PMM ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 00/37] Add Arm MPAM emulation 2026-10-19 11:46 ` Jinjie Ruan @ 2026-10-19 12:01 ` Peter Maydell 2026-10-19 12:19 ` Jinjie Ruan 0 siblings, 1 reply; 14+ messages in thread From: Peter Maydell @ 2026-10-19 12:01 UTC (permalink / raw) To: Jinjie Ruan Cc: Jinjie Ruan, anisinha, armbru, eblake, imammedo, michael.roth, mst, pbonzini, qemu-devel, qemu-arm On Mon, 19 Oct 2026 at 12:46, Jinjie Ruan <2118537295@qq.com> wrote: > One honest caveat Please don't send LLM output to qemu-devel. thanks -- PMM ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 00/37] Add Arm MPAM emulation 2026-10-19 12:01 ` Peter Maydell @ 2026-10-19 12:19 ` Jinjie Ruan 2026-10-19 12:28 ` Peter Maydell 0 siblings, 1 reply; 14+ messages in thread From: Jinjie Ruan @ 2026-10-19 12:19 UTC (permalink / raw) To: Peter Maydell, Jinjie Ruan Cc: anisinha, armbru, eblake, imammedo, michael.roth, mst, pbonzini, qemu-devel, qemu-arm 在 2026/10/19 20:01, Peter Maydell 写道: > On Mon, 19 Oct 2026 at 12:46, Jinjie Ruan <2118537295@qq.com> wrote: >> One honest caveat > > Please don't send LLM output to qemu-devel. Sorry. Here is my understanding: As mpam_csu_value() and mpam_mbwu_value() below done, the monitor code scales the base value based on the software-configured PBM (Partition-Based Monitoring) or MB MAX values, rather than just returning zeroes. And most of the code are to expose the many MPAM registers, constructing the ACPI tables, and building the hardware topology of the memory controllers and caches to correctly expose the MSC (Memory System Controller) components. static uint32_t mpam_csu_value(MPAMMSCState *s, Mpamf *mf, MpamCsuMon *m) { if (FIELD_EX32(m->ctl, MSMON_CFG_CSU_CTL, EN) && FIELD_EX32(m->ctl, MSMON_CFG_CSU_CTL, MATCH_PARTID)) { uint32_t partid = FIELD_EX32(m->flt, MSMON_CFG_CSU_FLT, PARTID); uint32_t nrw = mpam_nrw_id_for(s, mf, partid); uint32_t cpbm = mf->per_nrw_id[nrw].cfg_cpbm[0]; return muldiv64(s->csu_capacity, ctpop32(cpbm), ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ MPAM_CACHE_PART); ^^^^^^^^^^^^^^^^^^ } return 0; } static uint64_t mpam_mbwu_value(...) { ...... if (cfg->cfg_mbw_pbm[0]) { frac_num = __builtin_popcount(cfg->cfg_mbw_pbm[0] & ((1u << MPAM_MBW_PART) - 1)); frac_den = MPAM_MBW_PART; } else { /* MBW_MAX is a 16-bit fraction; 0xffff = full bandwidth. */ frac_num = cfg->cfg_mbw_max & 0xffff; frac_den = 0xffff; } rate_mbs = muldiv64(rate_mbs, frac_num, frac_den); ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ...... } > > thanks > -- PMM -- Best regards, Jinjie ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 00/37] Add Arm MPAM emulation 2026-10-19 12:19 ` Jinjie Ruan @ 2026-10-19 12:28 ` Peter Maydell 2026-10-20 0:39 ` Jonathan Cameron 0 siblings, 1 reply; 14+ messages in thread From: Peter Maydell @ 2026-10-19 12:28 UTC (permalink / raw) To: Jinjie Ruan Cc: Jinjie Ruan, anisinha, armbru, eblake, imammedo, michael.roth, mst, pbonzini, qemu-devel, qemu-arm On Mon, 19 Oct 2026 at 13:19, Jinjie Ruan <ruanjinjie@huawei.com> wrote: > > > > 在 2026/10/19 20:01, Peter Maydell 写道: > > On Mon, 19 Oct 2026 at 12:46, Jinjie Ruan <2118537295@qq.com> wrote: > >> One honest caveat > > > > Please don't send LLM output to qemu-devel. > > Sorry. Here is my understanding: > > As mpam_csu_value() and mpam_mbwu_value() below done, the monitor code > scales the base value based on the software-configured PBM > (Partition-Based Monitoring) or MB MAX values, rather than just > returning zeroes. Thanks. So we report "you're using the full amount the system is configured to let you use". That seems reasonable. > And most of the code are to expose the many MPAM registers, constructing > the ACPI tables, and building the hardware topology of the memory > controllers and caches to correctly expose the MSC (Memory System > Controller) components. OK, that makes sense too -- I guess there's just a lot of pieces of hardware in there. thanks -- PMM ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 00/37] Add Arm MPAM emulation 2026-10-19 12:28 ` Peter Maydell @ 2026-10-20 0:39 ` Jonathan Cameron 2026-10-27 7:09 ` Jinjie Ruan 0 siblings, 1 reply; 14+ messages in thread From: Jonathan Cameron @ 2026-10-20 0:39 UTC (permalink / raw) To: Peter Maydell Cc: Jinjie Ruan, Jinjie Ruan, anisinha, armbru, eblake, imammedo, michael.roth, mst, pbonzini, qemu-devel, qemu-arm, jonathan.cameron On Mon, 19 Oct 2026 13:28:03 +0100 Peter Maydell <peter.maydell@linaro.org> wrote: > On Mon, 19 Oct 2026 at 13:19, Jinjie Ruan <ruanjinjie@huawei.com> wrote: > > > > > > > > 在 2026/10/19 20:01, Peter Maydell 写道: > > > On Mon, 19 Oct 2026 at 12:46, Jinjie Ruan <2118537295@qq.com> wrote: > > >> One honest caveat > > > > > > Please don't send LLM output to qemu-devel. > > > > Sorry. Here is my understanding: > > > > As mpam_csu_value() and mpam_mbwu_value() below done, the monitor code > > scales the base value based on the software-configured PBM > > (Partition-Based Monitoring) or MB MAX values, rather than just > > returning zeroes. > > Thanks. So we report "you're using the full amount the system > is configured to let you use". That seems reasonable. > > > And most of the code are to expose the many MPAM registers, constructing > > the ACPI tables, and building the hardware topology of the memory > > controllers and caches to correctly expose the MSC (Memory System > > Controller) components. > > OK, that makes sense too -- I guess there's just a lot of pieces > of hardware in there. > > thanks > -- PMM > Hi Peter, ruanjinjie, FWIW the much more minimal emulation that I posted years ago and which I few crumbs of in here came up at plumbers this year with an engineer from yet another company asking if I could look at rebasing and upstreaming because they also found it very useful. I was trying to work out if I could fit that into my todo list or whether to ask a colleague to do it. As such I'm very pleased to see this surface a few days later! Even what I had was very useful for testing so I'm with this much extended feature set I'm not surprised to hear even more issues have shaken out. It might take me a little while to get time to look at this series but I definitely plan on doing so! Jonathan p.s. On off chance that (carefully unnamed) engineer sees this I had no idea ruanjinjie was working on this. Must have been something in the air :) ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 00/37] Add Arm MPAM emulation 2026-10-20 0:39 ` Jonathan Cameron @ 2026-10-27 7:09 ` Jinjie Ruan 2026-11-06 23:14 ` Jonathan Cameron 0 siblings, 1 reply; 14+ messages in thread From: Jinjie Ruan @ 2026-10-27 7:09 UTC (permalink / raw) To: Jonathan Cameron, Peter Maydell Cc: Jinjie Ruan, anisinha, armbru, eblake, imammedo, michael.roth, mst, pbonzini, qemu-devel, qemu-arm, jonathan.cameron 在 2026/10/20 8:39, Jonathan Cameron 写道: > On Mon, 19 Oct 2026 13:28:03 +0100 > Peter Maydell <peter.maydell@linaro.org> wrote: > >> On Mon, 19 Oct 2026 at 13:19, Jinjie Ruan <ruanjinjie@huawei.com> wrote: >>> >>> >>> >>> 在 2026/10/19 20:01, Peter Maydell 写道: >>>> On Mon, 19 Oct 2026 at 12:46, Jinjie Ruan <2118537295@qq.com> wrote: >>>>> One honest caveat >>>> >>>> Please don't send LLM output to qemu-devel. >>> >>> Sorry. Here is my understanding: >>> >>> As mpam_csu_value() and mpam_mbwu_value() below done, the monitor code >>> scales the base value based on the software-configured PBM >>> (Partition-Based Monitoring) or MB MAX values, rather than just >>> returning zeroes. >> >> Thanks. So we report "you're using the full amount the system >> is configured to let you use". That seems reasonable. >> >>> And most of the code are to expose the many MPAM registers, constructing >>> the ACPI tables, and building the hardware topology of the memory >>> controllers and caches to correctly expose the MSC (Memory System >>> Controller) components. >> >> OK, that makes sense too -- I guess there's just a lot of pieces >> of hardware in there. >> >> thanks >> -- PMM >> > Hi Peter, ruanjinjie, Hi Jonathan, > > FWIW the much more minimal emulation that I posted years ago and > which I few crumbs of in here came up at plumbers this year with With your foundational patch, it is very convenient to extend other functionality. > an engineer from yet another company asking if I could look at > rebasing and upstreaming because they also found it very useful. Glad to hear that someone else is also interested in QEMU emulation of MPAM. > I was trying to work out if I could fit that into my todo list > or whether to ask a colleague to do it. > > As such I'm very pleased to see this surface a few days later! > Even what I had was very useful for testing so I'm with this Feel the same way when developing new features or adapting test cases. > much extended feature set I'm not surprised to hear even more > issues have shaken out. > > It might take me a little while to get time to look at this series > but I definitely plan on doing so! Look forward to your review and suggestions. > > Jonathan > > p.s. On off chance that (carefully unnamed) engineer sees this I > had no idea ruanjinjie was working on this. > > Must have been something in the air :) -- Best regards, Jinjie ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 00/37] Add Arm MPAM emulation 2026-10-27 7:09 ` Jinjie Ruan @ 2026-11-06 23:14 ` Jonathan Cameron 2026-11-09 7:42 ` Jinjie Ruan 0 siblings, 1 reply; 14+ messages in thread From: Jonathan Cameron @ 2026-11-06 23:14 UTC (permalink / raw) To: Jinjie Ruan Cc: Jonathan Cameron, Peter Maydell, Jinjie Ruan, anisinha, armbru, eblake, imammedo, michael.roth, mst, pbonzini, qemu-devel, qemu-arm > > Hi Jonathan, > > > > > FWIW the much more minimal emulation that I posted years ago and > > which I few crumbs of in here came up at plumbers this year with > > With your foundational patch, it is very convenient to extend > other functionality. > > > an engineer from yet another company asking if I could look at > > rebasing and upstreaming because they also found it very useful. > > Glad to hear that someone else is also interested in QEMU emulation of MPAM. > > > I was trying to work out if I could fit that into my todo list > > or whether to ask a colleague to do it. > > > > As such I'm very pleased to see this surface a few days later! > > Even what I had was very useful for testing so I'm with this > > Feel the same way when developing new features or adapting test cases. > > > much extended feature set I'm not surprised to hear even more > > issues have shaken out. > > > > It might take me a little while to get time to look at this series > > but I definitely plan on doing so! > > Look forward to your review and suggestions. I perhaps got a little carried away and decided to see how good an LLM was at generating test cases for this... Was a little painful but in the end I think helpful. I also span a bunch of runtime options up to exercise all the optional properties, field widths etc. That adds about 600 lines of code and 25 machine properties (assuming just memory controller, l2 and l3 caches + all homogeneous.) I might clean that up and post at some point but it feels like an optional stage 2 on what you have here! First up, bunch of fixes related to this. The field access width ones are probably most important and then the various width and HAS_ checks. I haven't checked these all closely but some are obviously fine. Because it might be helpful, the test follows. I'm not suggesting this be included for now anyway but might help you with v2! Maybe long term we include a self test, but one that has had more review than I've given this before sharing. Easy to run with: QTEST_QEMU_BINARY=./qemu-system-aarch64 ./tests/qtest/arm-mpam-test --verbose diff --git a/hw/arm/mpam.c b/hw/arm/mpam.c index f4f40cacf0..15280c44a0 100644 --- a/hw/arm/mpam.c +++ b/hw/arm/mpam.c @@ -711,6 +711,37 @@ void mpam_cache_fill_info(Object *obj, MpamCacheInfo *info) } +/* + * IHI0099 9.4: a width-reported fraction field occupies the most + * significant 'wd' bits of its 16-bit field; the remaining low bits are + * RAZ/WI. Returns a mask of the bits software is allowed to set. + */ +static uint32_t mpam_fracwd_mask(uint32_t wd) +{ + if (wd >= 16) { + return 0xffffu; + } + return ~((1u << (16 - wd)) - 1) & 0xffffu; +} + +/* + * IHI0099 9.4 (MPAMCFG_CPBM<n>/MPAMCFG_MBW_PBM<n>): bit x of register n is + * writable iff (n*32)+x <= wd. Returns the writable-bit mask for word + * 'array_offset' of such a bitmap, given the overall bitmap width 'wd'. + */ +static uint32_t mpam_bitmap_mask(uint32_t wd, uint32_t array_offset) +{ + int64_t remaining = (int64_t)wd + 1 - 32 * (int64_t)array_offset; + + if (remaining <= 0) { + return 0; + } + if (remaining >= 32) { + return 0xffffffffu; + } + return (1u << remaining) - 1; +} + static uint64_t mpam_msc_read_reg(void *opaque, hwaddr offset, unsigned size) { @@ -781,14 +812,38 @@ static uint64_t mpam_msc_read_reg(void *opaque, hwaddr offset, case A_MPAMF_MSMON_IDR: return mpamf->msmon_idr; case A_MPAMF_CSUMON_IDR: + if (!FIELD_EX32(mpamf->idr, MPAMF_IDR, HAS_MSMON) || + !FIELD_EX32(mpamf->msmon_idr, MPAMF_MSMON_IDR, MSMON_CSU)) { + qemu_log_mask(LOG_UNIMP, + "MPAM: Unexpected read of CSUMON_IDR with no CSU monitor support\n"); + return 0; + } return mpamf->csumon_idr; case A_MPAMF_MBWUMON_IDR: + if (!FIELD_EX32(mpamf->idr, MPAMF_IDR, HAS_MSMON) || + !FIELD_EX32(mpamf->msmon_idr, MPAMF_MSMON_IDR, MSMON_MBWU)) { + qemu_log_mask(LOG_UNIMP, + "MPAM: Unexpected read of MBWUMON_IDR with no MBWU monitor support\n"); + return 0; + } return mpamf->mbwumon_idr; case A_MSMON_CFG_MON_SEL: return s->mon_sel; case A_MSMON_CFG_CSU_FLT: + if (!FIELD_EX32(mpamf->idr, MPAMF_IDR, HAS_MSMON) || + !FIELD_EX32(mpamf->msmon_idr, MPAMF_MSMON_IDR, MSMON_CSU)) { + qemu_log_mask(LOG_UNIMP, + "MPAM: Unexpected read of CSU_FLT with no CSU monitor support\n"); + return 0; + } return mpam_csu_mon(s, mpam_mon_ris(s))->flt; case A_MSMON_CFG_CSU_CTL: + if (!FIELD_EX32(mpamf->idr, MPAMF_IDR, HAS_MSMON) || + !FIELD_EX32(mpamf->msmon_idr, MPAMF_MSMON_IDR, MSMON_CSU)) { + qemu_log_mask(LOG_UNIMP, + "MPAM: Unexpected read of CSU_CTL with no CSU monitor support\n"); + return 0; + } return mpam_csu_mon(s, mpam_mon_ris(s))->ctl; case A_MSMON_CSU ... (A_MSMON_CSU + 3): case A_MSMON_CSU_CAPTURE ... (A_MSMON_CSU_CAPTURE + 3): @@ -802,12 +857,26 @@ static uint64_t mpam_msc_read_reg(void *opaque, hwaddr offset, return extract32(v, off * 8, 32 - off * 8); } case A_MSMON_CFG_MBWU_FLT: + if (!FIELD_EX32(mpamf->idr, MPAMF_IDR, HAS_MSMON) || + !FIELD_EX32(mpamf->msmon_idr, MPAMF_MSMON_IDR, MSMON_MBWU)) { + qemu_log_mask(LOG_UNIMP, + "MPAM: Unexpected read of MBWU_FLT with no MBWU monitor support\n"); + return 0; + } 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; + MpamMbwuMon *m; + uint32_t ctl; + if (!FIELD_EX32(mpamf->idr, MPAMF_IDR, HAS_MSMON) || + !FIELD_EX32(mpamf->msmon_idr, MPAMF_MSMON_IDR, MSMON_MBWU)) { + qemu_log_mask(LOG_UNIMP, + "MPAM: Unexpected read of MBWU_CTL with no MBWU monitor support\n"); + return 0; + } + m = mpam_mbwu_mon(s, mpam_mon_ris(s)); + ctl = m->ctl; if (m->oflow) { ctl |= R_MSMON_CFG_MBWU_CTL_OFLOW_STATUS_MASK; } @@ -852,11 +921,24 @@ static uint64_t mpam_msc_read_reg(void *opaque, hwaddr offset, case A_MPAMF_ERR_MSI_ATTR: return mpamf->err_msi_attr; case A_MPAMF_ECR: + if (FIELD_EX64(mpamf->idr, MPAMF_IDR, EXT) && + !FIELD_EX64(mpamf->idr, MPAMF_IDR, HAS_ESR)) { + qemu_log_mask(LOG_UNIMP, + "MPAM: Unexpected read of ECR with no ESR support\n"); + return 0; + } return s->ecr; case A_MPAMF_ESR: + if (FIELD_EX64(mpamf->idr, MPAMF_IDR, EXT) && + !FIELD_EX64(mpamf->idr, MPAMF_IDR, HAS_ESR)) { + qemu_log_mask(LOG_UNIMP, + "MPAM: Unexpected read of ESR with no ESR support\n"); + return 0; + } return s->esr; case A_MPAMF_ESR + 0x4: - if (!FIELD_EX32(mpamf->idr, MPAMF_IDR, EXT)) { + if (!FIELD_EX64(mpamf->idr, MPAMF_IDR, EXT) || + !FIELD_EX64(mpamf->idr, MPAMF_IDR, HAS_EXTD_ESR)) { qemu_log_mask(LOG_UNIMP, "MPAM: Unexpected read of extended ESR\n"); return 0; @@ -876,33 +958,59 @@ static uint64_t mpam_msc_read_reg(void *opaque, hwaddr offset, return mpamf->cfg_intpartid[s->part_sel]; } case A_MPAMF_MPAMCFG_CMAX: - if (!FIELD_EX32(mpamf->idr, MPAMF_IDR, HAS_CCAP_PART)) { + if (!FIELD_EX32(mpamf->idr, MPAMF_IDR, HAS_CCAP_PART) || + FIELD_EX32(mpamf->ccap_idr, MPAMF_CCAP_IDR, NO_CMAX)) { qemu_log_mask(LOG_UNIMP, - "MPAM: Unexpected read of CMAX with no CCAP support\n"); + "MPAM: Unexpected read of CMAX with no CMAX support\n"); return 0; } return mpamf->per_nrw_id[nrw_part_sel].cfg_cmax; case A_MPAMF_MPAMCFG_CMIN: - if (!FIELD_EX32(mpamf->idr, MPAMF_IDR, HAS_CCAP_PART)) { + if (!FIELD_EX32(mpamf->idr, MPAMF_IDR, HAS_CCAP_PART) || + !FIELD_EX32(mpamf->ccap_idr, MPAMF_CCAP_IDR, HAS_CMIN)) { qemu_log_mask(LOG_UNIMP, - "MPAM: Unexpected read of CMIN with no CCAP support\n"); + "MPAM: Unexpected read of CMIN with no CMIN support\n"); return 0; } return mpamf->per_nrw_id[nrw_part_sel].cfg_cmin; case A_MPAMF_MPAMCFG_CASSOC: - if (!FIELD_EX32(mpamf->idr, MPAMF_IDR, HAS_CCAP_PART)) { + if (!FIELD_EX32(mpamf->idr, MPAMF_IDR, HAS_CCAP_PART) || + !FIELD_EX32(mpamf->ccap_idr, MPAMF_CCAP_IDR, HAS_CASSOC)) { qemu_log_mask(LOG_UNIMP, - "MPAM: Unexpected read of CASSOC with no CCAP support\n"); + "MPAM: Unexpected read of CASSOC with no CASSOC support\n"); return 0; } return mpamf->per_nrw_id[nrw_part_sel].cfg_cassoc; case A_MPAMF_MPAMCFG_MBW_MIN: + if (!FIELD_EX32(mpamf->idr, MPAMF_IDR, HAS_MBW_PART) || + !FIELD_EX32(mpamf->mbw_idr, MPAMF_MBW_IDR, HAS_MIN)) { + qemu_log_mask(LOG_UNIMP, + "MPAM: Unexpected read of MBW_MIN with no MBW_MIN support\n"); + return 0; + } return mpamf->per_nrw_id[nrw_part_sel].cfg_mbw_min; case A_MPAMF_MPAMCFG_MBW_MAX: + if (!FIELD_EX32(mpamf->idr, MPAMF_IDR, HAS_MBW_PART) || + !FIELD_EX32(mpamf->mbw_idr, MPAMF_MBW_IDR, HAS_MAX)) { + qemu_log_mask(LOG_UNIMP, + "MPAM: Unexpected read of MBW_MAX with no MBW_MAX support\n"); + return 0; + } return mpamf->per_nrw_id[nrw_part_sel].cfg_mbw_max; case A_MPAMF_MPAMCFG_PRI: + if (!FIELD_EX32(mpamf->idr, MPAMF_IDR, HAS_PRI_PART)) { + qemu_log_mask(LOG_UNIMP, + "MPAM: Unexpected read of CFG_PRI with no PRI PART support\n"); + return 0; + } return mpamf->per_nrw_id[nrw_part_sel].cfg_pri; case A_MPAMF_MPAMCFG_MBW_PROP: + if (!FIELD_EX32(mpamf->idr, MPAMF_IDR, HAS_MBW_PART) || + !FIELD_EX32(mpamf->mbw_idr, MPAMF_MBW_IDR, HAS_PROP)) { + qemu_log_mask(LOG_UNIMP, + "MPAM: Unexpected read of MBW_PROP with no MBW_PROP support\n"); + return 0; + } return mpamf->per_nrw_id[nrw_part_sel].cfg_mbw_prop; case A_MPAMF_MPAMCFG_CPBM0... (A_MPAMF_MPAMCFG_CPBM0 + ((MPAM_CACHE_PART + 31) / 32 - 1) * 4): @@ -922,9 +1030,10 @@ static uint64_t mpam_msc_read_reg(void *opaque, hwaddr offset, { uint32_t array_offset; - if (!FIELD_EX32(mpamf->idr, MPAMF_IDR, HAS_MBW_PART)) { + if (!FIELD_EX32(mpamf->idr, MPAMF_IDR, HAS_MBW_PART) || + !FIELD_EX32(mpamf->mbw_idr, MPAMF_MBW_IDR, HAS_PBM)) { qemu_log_mask(LOG_UNIMP, - "MPAM: Unexpected read of MBW_PBM with no MBW_PART support\n"); + "MPAM: Unexpected read of MBW_PBM with no MBW_PBM support\n"); return 0; } array_offset = (offset - A_MPAMF_MPAMCFG_MBW_PBM0) / sizeof(uint32_t); @@ -1020,70 +1129,147 @@ static void mpam_msc_write_reg(void *opaque, hwaddr offset, uint64_t value, FIELD_EX32(value, MPAMF_MPAMCFG_INTPARTID, INTPARTID); return; case A_MPAMF_MPAMCFG_CMAX: + if (!FIELD_EX32(mpamf->idr, MPAMF_IDR, HAS_CCAP_PART) || + FIELD_EX32(mpamf->ccap_idr, MPAMF_CCAP_IDR, NO_CMAX)) { + qemu_log_mask(LOG_UNIMP, + "MPAM: Unexpected write to CMAX when not supported\n"); + return; + } if (value & ~MPAMF_MPAMCFG_CMAX_WRITE_MASK) { qemu_log_mask(LOG_UNIMP, "MPAM: Unexpected write to CMAX Mask=%x Value=%lx\n", MPAMF_MPAMCFG_CMAX_WRITE_MASK, value); } - mpamf->per_nrw_id[nrw_part_sel].cfg_cmax = value; + { + uint32_t wd = FIELD_EX32(mpamf->ccap_idr, MPAMF_CCAP_IDR, + CMAX_WD); + uint32_t valid = mpam_fracwd_mask(wd); + + if (FIELD_EX32(mpamf->ccap_idr, MPAMF_CCAP_IDR, + HAS_CMAX_SOFTLIM)) { + valid |= R_MPAMF_MPAMCFG_CMAX_SOFTLIM_MASK; + } + mpamf->per_nrw_id[nrw_part_sel].cfg_cmax = value & valid; + } return; case A_MPAMF_MPAMCFG_CMIN: + if (!FIELD_EX32(mpamf->idr, MPAMF_IDR, HAS_CCAP_PART) || + !FIELD_EX32(mpamf->ccap_idr, MPAMF_CCAP_IDR, HAS_CMIN)) { + qemu_log_mask(LOG_UNIMP, + "MPAM: Unexpected write to CMIN when not supported\n"); + return; + } if (value & ~MPAMF_MPAMCFG_CMIN_WRITE_MASK) { qemu_log_mask(LOG_UNIMP, "MPAM: Unexpected write to CMIN Mask=%x Value=%lx\n", MPAMF_MPAMCFG_CMIN_WRITE_MASK, value); } - mpamf->per_nrw_id[nrw_part_sel].cfg_cmin = value; + { + uint32_t wd = FIELD_EX32(mpamf->ccap_idr, MPAMF_CCAP_IDR, + CMAX_WD); + + mpamf->per_nrw_id[nrw_part_sel].cfg_cmin = + value & mpam_fracwd_mask(wd); + } return; case A_MPAMF_MPAMCFG_CASSOC: + if (!FIELD_EX32(mpamf->idr, MPAMF_IDR, HAS_CCAP_PART) || + !FIELD_EX32(mpamf->ccap_idr, MPAMF_CCAP_IDR, HAS_CASSOC)) { + qemu_log_mask(LOG_UNIMP, + "MPAM: Unexpected write to CASSOC when not supported\n"); + return; + } if (value & ~MPAMF_MPAMCFG_CASSOC_WRITE_MASK) { qemu_log_mask(LOG_UNIMP, "MPAM: Unexpected write to CASSOC Mask=%x Value=%lx\n", MPAMF_MPAMCFG_CASSOC_WRITE_MASK, value); } - mpamf->per_nrw_id[nrw_part_sel].cfg_cassoc = value; + { + uint32_t wd = FIELD_EX32(mpamf->ccap_idr, MPAMF_CCAP_IDR, + CASSOC_WD); + + mpamf->per_nrw_id[nrw_part_sel].cfg_cassoc = + value & mpam_fracwd_mask(wd); + } return; case A_MPAMF_MPAMCFG_MBW_MIN: + if (!FIELD_EX32(mpamf->idr, MPAMF_IDR, HAS_MBW_PART) || + !FIELD_EX32(mpamf->mbw_idr, MPAMF_MBW_IDR, HAS_MIN)) { + qemu_log_mask(LOG_UNIMP, + "MPAM: Unexpected write to MBW_MIN when not supported\n"); + return; + } if (value & ~MPAMF_MPAMCFG_MBW_MIN_WRITE_MASK) { qemu_log_mask(LOG_UNIMP, "MPAM: Unexpected write to MBW_MIN Mask=%x Value=%lx\n", MPAMF_MPAMCFG_MBW_MIN_WRITE_MASK, value); } - mpamf->per_nrw_id[nrw_part_sel].cfg_mbw_min = value; + { + uint32_t wd = FIELD_EX32(mpamf->mbw_idr, MPAMF_MBW_IDR, BWA_WD); + + mpamf->per_nrw_id[nrw_part_sel].cfg_mbw_min = + value & mpam_fracwd_mask(wd); + } return; case A_MPAMF_MPAMCFG_MBW_MAX: + if (!FIELD_EX32(mpamf->idr, MPAMF_IDR, HAS_MBW_PART) || + !FIELD_EX32(mpamf->mbw_idr, MPAMF_MBW_IDR, HAS_MAX)) { + qemu_log_mask(LOG_UNIMP, + "MPAM: Unexpected write to MBW_MAX when not supported\n"); + return; + } if (value & ~MPAMF_MPAMCFG_MBW_MAX_WRITE_MASK) { qemu_log_mask(LOG_UNIMP, "MPAM: Unexpected write to MBW_MAX Mask=%x Value=%lx\n", MPAMF_MPAMCFG_MBW_MAX_WRITE_MASK, value); } - mpamf->per_nrw_id[nrw_part_sel].cfg_mbw_max = value; + { + uint32_t wd = FIELD_EX32(mpamf->mbw_idr, MPAMF_MBW_IDR, BWA_WD); + uint32_t valid = mpam_fracwd_mask(wd) | + R_MPAMF_MPAMCFG_MBW_MAX_HARDLIM_MASK; + + mpamf->per_nrw_id[nrw_part_sel].cfg_mbw_max = value & valid; + } return; case A_MPAMF_MPAMCFG_PRI: if (!FIELD_EX32(mpamf->idr, MPAMF_IDR, HAS_PRI_PART)) { qemu_log_mask(LOG_UNIMP, "MPAM: Unexpected write to CFG_PRI when !HAS_PRI_PART\n"); - } else { - if (!FIELD_EX32(mpamf->pri_idr, MPAMF_PRI_IDR, HAS_DSPRI) && - FIELD_EX32(value, MPAMF_MPAMCFG_PRI, DSPRI)) { - qemu_log_mask(LOG_UNIMP, - "MPAM: Unexpected write to CFGP_PRI DSPRI when !HAS_DSPRI\n"); - } - if (!FIELD_EX32(mpamf->pri_idr, MPAMF_PRI_IDR, HAS_INTPRI) && - FIELD_EX32(value, MPAMF_MPAMCFG_PRI, INTPRI)) { - qemu_log_mask(LOG_UNIMP, - "MPAM: Unexpected write to CFGP_PRI INTPRI when !HAS_INTPRI\n"); - } + return; + } + { + uint32_t intpri_wd = FIELD_EX32(mpamf->pri_idr, MPAMF_PRI_IDR, + INTPRI_WD); + bool has_dspri = FIELD_EX32(mpamf->pri_idr, MPAMF_PRI_IDR, + HAS_DSPRI); + uint32_t dspri_wd = FIELD_EX32(mpamf->pri_idr, MPAMF_PRI_IDR, + DSPRI_WD); + uint32_t valid = mpam_fracwd_mask(intpri_wd) | + (has_dspri ? + (mpam_fracwd_mask(dspri_wd) << 16) : 0); + + mpamf->per_nrw_id[nrw_part_sel].cfg_pri = value & valid; } - mpamf->per_nrw_id[nrw_part_sel].cfg_pri = value; return; case A_MPAMF_MPAMCFG_MBW_PROP: + if (!FIELD_EX32(mpamf->idr, MPAMF_IDR, HAS_MBW_PART) || + !FIELD_EX32(mpamf->mbw_idr, MPAMF_MBW_IDR, HAS_PROP)) { + qemu_log_mask(LOG_UNIMP, + "MPAM: Unexpected write to MBW_PROP when not supported\n"); + return; + } if (value & ~MPAMF_MPAMCFG_MBW_PROP_WRITE_MASK) { qemu_log_mask(LOG_UNIMP, "MPAM: Unexpected write to MBW_PROP Mask=%x Value=%lx\n", MPAMF_MPAMCFG_MBW_MAX_WRITE_MASK, value); } - mpamf->per_nrw_id[nrw_part_sel].cfg_mbw_prop = value; + { + uint32_t wd = FIELD_EX32(mpamf->mbw_idr, MPAMF_MBW_IDR, BWA_WD); + uint32_t valid = mpam_fracwd_mask(wd) | + R_MPAMF_MPAMCFG_MBW_PROP_EN_MASK; + + mpamf->per_nrw_id[nrw_part_sel].cfg_mbw_prop = value & valid; + } return; case A_MPAMF_MPAMCFG_CPBM0... (A_MPAMF_MPAMCFG_CPBM0 + ((MPAM_CACHE_PART + 31) / 32 - 1) * 4): @@ -1105,15 +1291,21 @@ static void mpam_msc_write_reg(void *opaque, hwaddr offset, uint64_t value, { uint32_t array_offset; - if (!FIELD_EX32(mpamf->idr, MPAMF_IDR, HAS_MBW_PART)) { + if (!FIELD_EX32(mpamf->idr, MPAMF_IDR, HAS_MBW_PART) || + !FIELD_EX32(mpamf->mbw_idr, MPAMF_MBW_IDR, HAS_PBM)) { qemu_log_mask(LOG_UNIMP, - "MPAM: Unexpected write to MBW_PBM when !HAS_MBW_PART\n"); + "MPAM: Unexpected write to MBW_PBM when not supported\n"); return; } - /* TODO: Figure out write mask to check this stays in write bits */ array_offset = (offset - A_MPAMF_MPAMCFG_MBW_PBM0) / sizeof(uint32_t); - mpamf->per_nrw_id[nrw_part_sel].cfg_mbw_pbm[array_offset] = value; + { + uint32_t wd = FIELD_EX32(mpamf->mbw_idr, MPAMF_MBW_IDR, + BWPBM_WD); + + mpamf->per_nrw_id[nrw_part_sel].cfg_mbw_pbm[array_offset] = + value & mpam_bitmap_mask(wd, array_offset); + } return; } case A_MSMON_CFG_MON_SEL: @@ -1151,6 +1343,12 @@ static void mpam_msc_write_reg(void *opaque, hwaddr offset, uint64_t value, { uint32_t partid = FIELD_EX32(value, MSMON_CFG_MBWU_FLT, PARTID); + if (!FIELD_EX32(mpamf->idr, MPAMF_IDR, HAS_MSMON) || + !FIELD_EX32(mpamf->msmon_idr, MPAMF_MSMON_IDR, MSMON_MBWU)) { + qemu_log_mask(LOG_UNIMP, + "MPAM: Unexpected write to MBWU_FLT with no MBWU monitor support\n"); + return; + } if (partid >= s->num_partid) { qemu_log_mask(LOG_GUEST_ERROR, "MPAM: MBWU_FLT PARTID %d out of range\n", partid); @@ -1166,9 +1364,16 @@ static void mpam_msc_write_reg(void *opaque, hwaddr offset, uint64_t value, } case A_MSMON_CFG_MBWU_CTL: { - MpamMbwuMon *m = mpam_mbwu_mon(s, mpam_mon_ris(s)); + MpamMbwuMon *m; - m->ctl = value; + if (!FIELD_EX32(mpamf->idr, MPAMF_IDR, HAS_MSMON) || + !FIELD_EX32(mpamf->msmon_idr, MPAMF_MSMON_IDR, MSMON_MBWU)) { + qemu_log_mask(LOG_UNIMP, + "MPAM: Unexpected write to MBWU_CTL with no MBWU monitor support\n"); + return; + } + m = mpam_mbwu_mon(s, mpam_mon_ris(s)); + m->ctl = value & ~0x00040f00u; if (value & R_MSMON_CFG_MBWU_CTL_OFLOW_STATUS_MASK) { m->oflow = false; /* write-one-to-clear */ } @@ -1190,6 +1395,12 @@ static void mpam_msc_write_reg(void *opaque, hwaddr offset, uint64_t value, { uint32_t partid = FIELD_EX32(value, MSMON_CFG_CSU_FLT, PARTID); + if (!FIELD_EX32(mpamf->idr, MPAMF_IDR, HAS_MSMON) || + !FIELD_EX32(mpamf->msmon_idr, MPAMF_MSMON_IDR, MSMON_CSU)) { + qemu_log_mask(LOG_UNIMP, + "MPAM: Unexpected write to CSU_FLT with no CSU monitor support\n"); + return; + } if (partid >= s->num_partid) { qemu_log_mask(LOG_GUEST_ERROR, "MPAM: CSU_FLT PARTID %d out of range\n", partid); @@ -1204,7 +1415,13 @@ static void mpam_msc_write_reg(void *opaque, hwaddr offset, uint64_t value, return; } case A_MSMON_CFG_CSU_CTL: - mpam_csu_mon(s, mpam_mon_ris(s))->ctl = value; + if (!FIELD_EX32(mpamf->idr, MPAMF_IDR, HAS_MSMON) || + !FIELD_EX32(mpamf->msmon_idr, MPAMF_MSMON_IDR, MSMON_CSU)) { + qemu_log_mask(LOG_UNIMP, + "MPAM: Unexpected write to CSU_CTL with no CSU monitor support\n"); + return; + } + mpam_csu_mon(s, mpam_mon_ris(s))->ctl = value & ~0x7ffc0000u; return; case A_MSMON_CSU: case A_MSMON_CSU_CAPTURE: @@ -1220,6 +1437,12 @@ static void mpam_msc_write_reg(void *opaque, hwaddr offset, uint64_t value, offset); return; case A_MPAMF_ECR: + if (FIELD_EX64(mpamf->idr, MPAMF_IDR, EXT) && + !FIELD_EX64(mpamf->idr, MPAMF_IDR, HAS_ESR)) { + qemu_log_mask(LOG_UNIMP, + "MPAM: Unexpected write to ECR with no ESR support\n"); + return; + } if (value & ~MPAMF_ECR_WRITE_MASK) { qemu_log_mask(LOG_UNIMP, "MPAM: Unexpected write to ECR Mask=%x Value=%lx\n", @@ -1230,16 +1453,28 @@ static void mpam_msc_write_reg(void *opaque, hwaddr offset, uint64_t value, mpam_msc_update_err_irq(s); return; case A_MPAMF_ESR: + if (FIELD_EX64(mpamf->idr, MPAMF_IDR, EXT) && + !FIELD_EX64(mpamf->idr, MPAMF_IDR, HAS_ESR)) { + qemu_log_mask(LOG_UNIMP, + "MPAM: Unexpected write to ESR with no ESR support\n"); + return; + } /* * Software clears an error by writing ERRCODE as zero; writing a * non-zero ERRCODE activates the level error interrupt (8.2). */ - s->esr = deposit64(s->esr, 0, 32, value); + s->esr = deposit64(s->esr, 0, 32, value & ~0x70000000u); mpam_msc_sync_esr_ecr(s); mpam_msc_update_err_irq(s); return; case A_MPAMF_ESR + 0x4: - s->esr = deposit64(s->esr, 32, 32, value); + if (!FIELD_EX64(mpamf->idr, MPAMF_IDR, EXT) || + !FIELD_EX64(mpamf->idr, MPAMF_IDR, HAS_EXTD_ESR)) { + qemu_log_mask(LOG_UNIMP, + "MPAM: Unexpected write to extended ESR\n"); + return; + } + s->esr = deposit64(s->esr, 32, 32, value & ~0xfffffff0u); mpam_msc_sync_esr_ecr(s); mpam_msc_update_err_irq(s); return; -- 2.43.0 And the test --- tests/qtest/arm-mpam-test.c | 766 +++++++++++++++++++++++++++++++ tests/qtest/meson.build | 1 + tests/qtest/run-arm-mpam-test.sh | 33 ++ 3 files changed, 800 insertions(+) diff --git a/tests/qtest/arm-mpam-test.c b/tests/qtest/arm-mpam-test.c new file mode 100644 index 0000000000..667820d427 --- /dev/null +++ b/tests/qtest/arm-mpam-test.c @@ -0,0 +1,766 @@ +/* + * QTest for ARM MPAM MSC register map, checked against IHI0099. + * + * Verifies, independently of hw/arm/mpam.c's own bit-position choices + * (the masks below are hand-derived from the spec, not #included from the + * device model): HAS_*-gated register presence/absence (RES0 when + * absent), reserved-bit zeroing, read-only enforcement on the ID-register + * block, and MPAMF_CFG_PART_SEL.RIS-based Resource Instance selection. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "libqtest-single.h" + +/* + * "%s" is a splice point for a scenario's "x-mpam-no-X=on," fragment(s); + * build the full argument string at runtime with g_strdup_printf() since + * the fragment is itself runtime data, not a string literal. + */ +#define MACHINE_FMT \ + "-machine virt,gic-version=max,mpam=on,mpam-min-msc=on,%s" \ + "smp-cache.0.cache=l1i,smp-cache.0.topology=core," \ + "smp-cache.1.cache=l1d,smp-cache.1.topology=core," \ + "smp-cache.2.cache=l2,smp-cache.2.topology=cluster," \ + "smp-cache.3.cache=l3,smp-cache.3.topology=socket " \ + "-accel tcg -cpu max " \ + "-smp cpus=8,sockets=2,clusters=2,cores=2,threads=1 " \ + "-m 4G " \ + "-object memory-backend-ram,id=mem0,size=2G " \ + "-object memory-backend-ram,id=mem1,size=2G " \ + "-numa node,nodeid=0,cpus=0-3,memdev=mem0 " \ + "-numa node,nodeid=1,cpus=4-7,memdev=mem1 " + +/* Same topology/NUMA layout as MACHINE_FMT, but with mpam-min-msc=off: one + * single-RIS mem MSC per NUMA node with memory, instead of one consolidated + * multi-RIS mem MSC. */ +#define MACHINE_FMT_MIN_MSC_OFF \ + "-machine virt,gic-version=max,mpam=on,mpam-min-msc=off,%s" \ + "smp-cache.0.cache=l1i,smp-cache.0.topology=core," \ + "smp-cache.1.cache=l1d,smp-cache.1.topology=core," \ + "smp-cache.2.cache=l2,smp-cache.2.topology=cluster," \ + "smp-cache.3.cache=l3,smp-cache.3.topology=socket " \ + "-accel tcg -cpu max " \ + "-smp cpus=8,sockets=2,clusters=2,cores=2,threads=1 " \ + "-m 4G " \ + "-object memory-backend-ram,id=mem0,size=2G " \ + "-object memory-backend-ram,id=mem1,size=2G " \ + "-numa node,nodeid=0,cpus=0-3,memdev=mem0 " \ + "-numa node,nodeid=1,cpus=4-7,memdev=mem1 " + +#define MEM_MSC_BASE 0x0b006000 + +/* Each MSC's MMIO window is MPAM_SIZE (16 KiB) - see + * include/hw/arm/mpam.h - independently re-derived here rather than + * #included, so a mismatch in hw/arm/virt.c's base-address arithmetic is a + * real finding. */ +#define MPAM_MSC_STRIDE 0x4000 + +/* 2 L3 (one per socket) + 4 L2 (one per cluster), creation order per + * create_mpam_msc_cache() looping over sockets then clusters. */ +static const uint64_t cache_msc_base[] = { + 0x0b00a000, 0x0b00e000, /* L3: socket 0, socket 1 */ + 0x0b012000, 0x0b016000, /* L2: socket0/cluster{0,1} */ + 0x0b01a000, 0x0b01e000, /* L2: socket1/cluster{0,1} */ +}; + +/* Under mpam-min-msc=off, the two NUMA nodes (each with memory) each get + * their own single-RIS mem MSC, so the cache MSCs' base addresses are + * shifted one extra MPAM_MSC_STRIDE later than under mpam-min-msc=on. */ +static const uint64_t mem_msc_base_min_msc_off[] = { + 0x0b006000, 0x0b00a000, +}; +static const uint64_t cache_msc_base_min_msc_off[] = { + 0x0b00e000, 0x0b012000, /* L3: socket 0, socket 1 */ + 0x0b016000, 0x0b01a000, /* L2: socket0/cluster{0,1} */ + 0x0b01e000, 0x0b022000, /* L2: socket1/cluster{0,1} */ +}; + +/* Register offsets (IHI0099 7/8/9) */ +#define R_MPAMF_IDR_LO 0x000 +#define R_MPAMF_IDR_HI 0x004 +#define R_MPAMF_IIDR 0x018 +#define R_MPAMF_AIDR 0x020 +#define R_MPAMF_IMPL_IDR 0x028 +#define R_MPAMF_CPOR_IDR 0x030 +#define R_MPAMF_CCAP_IDR 0x038 +#define R_MPAMF_MBW_IDR 0x040 +#define R_MPAMF_PRI_IDR 0x048 +#define R_MPAMF_PARTID_NRW_IDR 0x050 +#define R_MPAMF_MSMON_IDR 0x080 +#define R_MPAMF_CSUMON_IDR 0x088 +#define R_MPAMF_MBWUMON_IDR 0x090 +#define R_MPAMF_ERR_MSI_MPAM 0x0dc +#define R_MPAMF_ERR_MSI_ADDR_L 0x0e0 +#define R_MPAMF_ERR_MSI_ADDR_H 0x0e4 +#define R_MPAMF_ERR_MSI_DATA 0x0e8 +#define R_MPAMF_ERR_MSI_ATTR 0x0ec +#define R_MPAMF_ECR 0x0f0 +#define R_MPAMF_ESR_LO 0x0f8 +#define R_MPAMF_ESR_HI 0x0fc +#define R_MPAMF_CFG_PART_SEL 0x100 +#define R_MPAMCFG_CMAX 0x108 +#define R_MPAMCFG_CMIN 0x110 +#define R_MPAMCFG_CASSOC 0x118 +#define R_MPAMCFG_MBW_MIN 0x200 +#define R_MPAMCFG_MBW_MAX 0x208 +#define R_MPAMCFG_WINWD 0x220 +#define R_MPAMCFG_EN 0x300 +#define R_MPAMCFG_DIS 0x310 +#define R_MPAMCFG_EN_FLAGS 0x320 +#define R_MPAMCFG_PRI 0x400 +#define R_MPAMCFG_MBW_PROP 0x500 +#define R_MPAMCFG_INTPARTID 0x600 +#define R_MPAMCFG_CPBM0 0x1000 +#define R_MPAMCFG_MBW_PBM0 0x2000 +#define R_MSMON_CFG_MON_SEL 0x800 +#define R_MSMON_CFG_CSU_FLT 0x810 +#define R_MSMON_CFG_CSU_CTL 0x818 +#define R_MSMON_CFG_MBWU_FLT 0x820 +#define R_MSMON_CFG_MBWU_CTL 0x828 + +/* IDR field positions, independently derived from IHI0099 9.3.1 */ +#define IDR_HAS_CCAP_PART (1u << 24) +#define IDR_HAS_CPOR_PART (1u << 25) +#define IDR_HAS_MBW_PART (1u << 26) +#define IDR_HAS_PRI_PART (1u << 27) +#define IDR_EXT (1u << 28) +#define IDR_HAS_IMPL_IDR (1u << 29) +#define IDR_HAS_MSMON (1u << 30) +#define IDR_HAS_PARTID_NRW (1u << 31) +#define IDR_HI_HAS_RIS (1u << 0) +#define IDR_HI_HAS_EXTD_ESR (1u << 6) +#define IDR_HI_HAS_ESR (1u << 7) +#define IDR_HI_HAS_ERR_MSI (1u << 8) +#define IDR_HI_RIS_MAX_SHIFT 24 +#define IDR_HI_RIS_MAX_MASK 0xfu +#define MSMON_IDR_MSMON_CSU (1u << 16) +#define MSMON_IDR_MSMON_MBWU (1u << 17) +#define MBW_IDR_HAS_PROP (1u << 13) +#define CFG_PART_SEL_RIS_SHIFT 24 + +/* MPAMF_CCAP_IDR/MPAMF_MBW_IDR/MPAMF_CPOR_IDR sub-fields (IHI0099 + * 9.3.5/9.3.9/9.3.4), gating finer-grained MPAMCFG_* presence and + * width than the top-level HAS_CCAP_PART/HAS_MBW_PART bits do. + * Hand-derived from the spec, independent of hw/arm/mpam.c's own + * FIELD() positions. */ +#define CCAP_IDR_CMAX_WD_SHIFT 0 +#define CCAP_IDR_CMAX_WD_MASK 0x3fu +#define CCAP_IDR_CASSOC_WD_SHIFT 8 +#define CCAP_IDR_CASSOC_WD_MASK 0x1fu +#define CCAP_IDR_HAS_CASSOC (1u << 28) +#define CCAP_IDR_HAS_CMIN (1u << 29) +#define CCAP_IDR_NO_CMAX (1u << 30) +#define CCAP_IDR_HAS_CMAX_SOFTLIM (1u << 31) +#define MBW_IDR_BWA_WD_SHIFT 0 +#define MBW_IDR_BWA_WD_MASK 0x3fu +#define MBW_IDR_HAS_MIN (1u << 10) +#define MBW_IDR_HAS_MAX (1u << 11) +#define MBW_IDR_HAS_PBM (1u << 12) +#define MBW_IDR_BWPBM_WD_SHIFT 16 +#define MBW_IDR_BWPBM_WD_MASK 0x1fffu +#define CPOR_IDR_CPBM_WD_SHIFT 0 +#define CPOR_IDR_CPBM_WD_MASK 0xffffu +#define PRI_IDR_INTPRI_WD_SHIFT 4 +#define PRI_IDR_INTPRI_WD_MASK 0x3fu +#define PRI_IDR_HAS_DSPRI (1u << 16) +#define PRI_IDR_DSPRI_WD_SHIFT 20 +#define PRI_IDR_DSPRI_WD_MASK 0x3fu + +typedef struct { + bool has_ccap_part, has_cpor_part, has_mbw_part, has_pri_part; + bool has_partid_nrw, has_msmon, has_impl_idr, ext; + bool has_msmon_csu, has_msmon_mbwu; + bool has_cmax, has_cmin, has_cassoc, has_cmax_softlim; + bool has_mbw_min, has_mbw_max, has_mbw_pbm, has_mbw_prop; + bool has_esr, has_extd_esr, has_err_msi; + bool has_dspri; + uint32_t cmax_wd, cassoc_wd, bwa_wd, cpbm_wd, bwpbm_wd; + uint32_t intpri_wd, dspri_wd; +} MpamGates; + +typedef enum { + GATE_ALWAYS, + GATE_EXT, + GATE_HAS_IMPL_IDR, + GATE_HAS_CPOR_PART, + GATE_HAS_CCAP_PART, + GATE_HAS_MBW_PART, + GATE_HAS_PRI_PART, + GATE_HAS_PARTID_NRW, + GATE_HAS_MSMON_CSU, + GATE_HAS_MSMON_MBWU, + GATE_HAS_CMAX, + GATE_HAS_CMIN, + GATE_HAS_CASSOC, + GATE_HAS_MBW_MIN, + GATE_HAS_MBW_MAX, + GATE_HAS_MBW_PBM, + GATE_HAS_MBW_PROP, + GATE_HAS_ESR, + GATE_HAS_EXTD_ESR, + GATE_HAS_ERR_MSI, +} MpamGate; + +/* Several MPAMCFG_* registers' valid-bit width is itself variable, governed + * by a _WD capability field (IHI0099 9.4). WK_NONE means "use the static + * reserved_mask below, unconditionally" as for every other register. */ +typedef enum { + WK_NONE, + WK_CMAX, + WK_CMIN, + WK_CASSOC, + WK_MBW_MIN, + WK_MBW_MAX, + WK_MBW_PROP, + WK_PRI, +} WidthKind; + +typedef struct { + const char *name; + uint32_t offset; + uint32_t reserved_mask; + MpamGate gate; + bool ro; + WidthKind width_kind; +} RegDesc; + +/* + * Reserved-bit masks are hand-derived from IHI0099's per-register bit + * tables (deliberately not reusing hw/arm/mpam.c's FIELD() positions), + * so a mismatch here is a real finding, not a tautology. + */ +static const RegDesc regs[] = { + { "MPAMF_IDR_LO", R_MPAMF_IDR_LO, 0x00000000, GATE_ALWAYS, true, WK_NONE }, + { "MPAMF_IDR_HI", R_MPAMF_IDR_HI, 0xf0fff00e, GATE_EXT, true, WK_NONE }, + { "MPAMF_IIDR", R_MPAMF_IIDR, 0x00000000, GATE_ALWAYS, true, WK_NONE }, + { "MPAMF_AIDR", R_MPAMF_AIDR, 0xffffff00, GATE_ALWAYS, true, WK_NONE }, + { "MPAMF_IMPL_IDR", R_MPAMF_IMPL_IDR, 0x00000000, GATE_HAS_IMPL_IDR, true, WK_NONE }, + { "MPAMF_CPOR_IDR", R_MPAMF_CPOR_IDR, 0xffff0000, GATE_HAS_CPOR_PART, true, WK_NONE }, + { "MPAMF_CCAP_IDR", R_MPAMF_CCAP_IDR, 0x0fffe0c0, GATE_HAS_CCAP_PART, true, WK_NONE }, + { "MPAMF_MBW_IDR", R_MPAMF_MBW_IDR, 0xe00083c0, GATE_HAS_MBW_PART, true, WK_NONE }, + { "MPAMF_PRI_IDR", R_MPAMF_PRI_IDR, 0xfc0cfc0c, GATE_HAS_PRI_PART, true, WK_NONE }, + { "MPAMF_PARTID_NRW_IDR", R_MPAMF_PARTID_NRW_IDR, 0xffff0000, GATE_HAS_PARTID_NRW, true, WK_NONE }, + { "MPAMF_MSMON_IDR", R_MPAMF_MSMON_IDR, 0x0ffcffff, GATE_ALWAYS, true, WK_NONE }, + { "MPAMF_CSUMON_IDR", R_MPAMF_CSUMON_IDR, 0x10ff0000, GATE_HAS_MSMON_CSU, true, WK_NONE }, + { "MPAMF_MBWUMON_IDR", R_MPAMF_MBWUMON_IDR, 0x00e00000, GATE_HAS_MSMON_MBWU, true, WK_NONE }, + + { "MPAMF_CFG_PART_SEL", R_MPAMF_CFG_PART_SEL, 0xf0fe0000, GATE_ALWAYS, false, WK_NONE }, + + /* reserved_mask for these 7 is dynamic - see compute_reserved_mask() - + * since the valid bit width depends on a _WD capability field, and/or + * presence depends on a finer sub-bit than HAS_CCAP_PART/HAS_MBW_PART + * alone (IHI0099 9.4). The 0 below is intentionally unused. */ + { "MPAMCFG_CMAX", R_MPAMCFG_CMAX, 0x00000000, GATE_HAS_CMAX, false, WK_CMAX }, + { "MPAMCFG_CMIN", R_MPAMCFG_CMIN, 0x00000000, GATE_HAS_CMIN, false, WK_CMIN }, + { "MPAMCFG_CASSOC", R_MPAMCFG_CASSOC, 0x00000000, GATE_HAS_CASSOC, false, WK_CASSOC }, + { "MPAMCFG_MBW_MIN", R_MPAMCFG_MBW_MIN, 0x00000000, GATE_HAS_MBW_MIN, false, WK_MBW_MIN }, + { "MPAMCFG_MBW_MAX", R_MPAMCFG_MBW_MAX, 0x00000000, GATE_HAS_MBW_MAX, false, WK_MBW_MAX }, + + /* No case in mpam_msc_{read,write}_reg(): spec-defined offsets that + * are entirely unimplemented today, so they read 0 / ignore writes + * regardless of gate - recorded as a finding in the comment, not a + * hard failure, since the generic check happens to pass today. */ + { "MPAMCFG_WINWD (unimplemented)", R_MPAMCFG_WINWD, 0x00000000, GATE_ALWAYS, true, WK_NONE }, + { "MPAMCFG_EN (unimplemented)", R_MPAMCFG_EN, 0x00000000, GATE_ALWAYS, true, WK_NONE }, + { "MPAMCFG_DIS (unimplemented)", R_MPAMCFG_DIS, 0x00000000, GATE_ALWAYS, true, WK_NONE }, + { "MPAMCFG_EN_FLAGS (unimplemented)", R_MPAMCFG_EN_FLAGS, 0x00000000, GATE_ALWAYS, true, WK_NONE }, + + { "MPAMCFG_PRI", R_MPAMCFG_PRI, 0x00000000, GATE_HAS_PRI_PART, false, WK_PRI }, + /* Known gap: mpam_msc_read_reg()/write_reg() gate this on HAS_MBW_PART + * only (hw/arm/mpam.c), not on the finer HAS_PROP sub-bit - expect a + * reported failure once HAS_PROP differs from HAS_MBW_PART, i.e. under + * x-mpam-no-mbw-prop. */ + { "MPAMCFG_MBW_PROP", R_MPAMCFG_MBW_PROP, 0x00000000, GATE_HAS_MBW_PROP, false, WK_MBW_PROP }, + { "MPAMCFG_INTPARTID", R_MPAMCFG_INTPARTID, 0xfffe0000, GATE_HAS_PARTID_NRW, false, WK_NONE }, + /* MPAMCFG_CPBM0/MPAMCFG_MBW_PBM0 are checked separately by + * check_bitmap_reg(), which walks every implemented word (plural, + * since CPBM_WD/BWPBM_WD can now exceed 31), not just word 0. */ + + /* IHI0099 9.3.1: implemented iff MPAMF_IDR_HI.HAS_ERR_MSI == 1 (itself + * only meaningful, and required 0, unless EXT==1). */ + { "MPAMF_ERR_MSI_MPAM", R_MPAMF_ERR_MSI_MPAM, 0x00000000, GATE_HAS_ERR_MSI, false, WK_NONE }, + { "MPAMF_ERR_MSI_ADDR_L", R_MPAMF_ERR_MSI_ADDR_L, 0x00000000, GATE_HAS_ERR_MSI, false, WK_NONE }, + { "MPAMF_ERR_MSI_ADDR_H", R_MPAMF_ERR_MSI_ADDR_H, 0x00000000, GATE_HAS_ERR_MSI, false, WK_NONE }, + { "MPAMF_ERR_MSI_DATA", R_MPAMF_ERR_MSI_DATA, 0x00000000, GATE_HAS_ERR_MSI, false, WK_NONE }, + { "MPAMF_ERR_MSI_ATTR", R_MPAMF_ERR_MSI_ATTR, 0x00000000, GATE_HAS_ERR_MSI, false, WK_NONE }, + + /* MPAMF_ECR/MPAMF_ESR are mandatory when !EXT (base MPAM); under EXT + * they're implemented iff HAS_ESR == 1 - see GATE_HAS_ESR derivation. */ + { "MPAMF_ECR", R_MPAMF_ECR, 0xfffffffe, GATE_HAS_ESR, false, WK_NONE }, + { "MPAMF_ESR_LO", R_MPAMF_ESR_LO, 0x70000000, GATE_HAS_ESR, false, WK_NONE }, + /* ESR widens to 64 bits (this high half exists) iff HAS_EXTD_ESR == 1. */ + { "MPAMF_ESR_HI", R_MPAMF_ESR_HI, 0xfffffff0, GATE_HAS_EXTD_ESR, false, WK_NONE }, + + /* Known gap: not gated by MSMON_IDR.MSMON_CSU/MBWU in the write path - + * expect a reported failure on the mem MSC (MSMON_CSU=0). */ + { "MSMON_CFG_CSU_FLT", R_MSMON_CFG_CSU_FLT, 0x7f000000, GATE_HAS_MSMON_CSU, false, WK_NONE }, + { "MSMON_CFG_CSU_CTL", R_MSMON_CFG_CSU_CTL, 0x7ffc0000, GATE_HAS_MSMON_CSU, false, WK_NONE }, + { "MSMON_CFG_MBWU_FLT", R_MSMON_CFG_MBWU_FLT, 0x3f000000, GATE_HAS_MSMON_MBWU, false, WK_NONE }, + { "MSMON_CFG_MBWU_CTL", R_MSMON_CFG_MBWU_CTL, 0x00040f00, GATE_HAS_MSMON_MBWU, false, WK_NONE }, +}; + +static int g_failures; + +static void expect(bool cond, const char *fmt, ...) G_GNUC_PRINTF(2, 3); + +static void expect(bool cond, const char *fmt, ...) +{ + if (!cond) { + va_list ap; + char *msg; + + va_start(ap, fmt); + msg = g_strdup_vprintf(fmt, ap); + va_end(ap); + + g_test_message("FAIL: %s", msg); + g_test_fail(); + g_failures++; + g_free(msg); + } +} + +static bool gate_present(const MpamGates *g, MpamGate gate) +{ + switch (gate) { + case GATE_ALWAYS: + return true; + case GATE_EXT: + return g->ext; + case GATE_HAS_IMPL_IDR: + return g->has_impl_idr; + case GATE_HAS_CPOR_PART: + return g->has_cpor_part; + case GATE_HAS_CCAP_PART: + return g->has_ccap_part; + case GATE_HAS_MBW_PART: + return g->has_mbw_part; + case GATE_HAS_PRI_PART: + return g->has_pri_part; + case GATE_HAS_PARTID_NRW: + return g->has_partid_nrw; + case GATE_HAS_MSMON_CSU: + return g->has_msmon_csu; + case GATE_HAS_MSMON_MBWU: + return g->has_msmon_mbwu; + case GATE_HAS_CMAX: + return g->has_cmax; + case GATE_HAS_CMIN: + return g->has_cmin; + case GATE_HAS_CASSOC: + return g->has_cassoc; + case GATE_HAS_MBW_MIN: + return g->has_mbw_min; + case GATE_HAS_MBW_MAX: + return g->has_mbw_max; + case GATE_HAS_MBW_PBM: + return g->has_mbw_pbm; + case GATE_HAS_MBW_PROP: + return g->has_mbw_prop; + case GATE_HAS_ESR: + return g->has_esr; + case GATE_HAS_EXTD_ESR: + return g->has_extd_esr; + case GATE_HAS_ERR_MSI: + return g->has_err_msi; + } + g_assert_not_reached(); +} + +/* + * MSB-aligned fraction-field reserved mask: bits [15:16-wd] are real, + * bits [16-wd-1:0] are RAZ/WI (IHI0099 9.4, "implemented bits ... are + * always the most significant bits of the field"). wd==0 reserves the + * whole low 16 bits; wd>=16 reserves none of them. + */ +static uint32_t frac16_reserved(uint32_t wd) +{ + if (wd >= 16) { + return 0; + } + return (1u << (16 - wd)) - 1; +} + +/* + * LSB/position-indexed bitmap reserved mask for register word n: per the + * spec's literal RES0 condition "(n*32)+x > UInt(WD)" (IHI0099, the + * MPAMCFG_CPBM<n>/MPAMCFG_MBW_PBM<n> register descriptions), bit x of word n + * is reserved iff (n*32)+x > wd. A word entirely beyond the implemented + * range (remaining <= 0) is fully reserved; a word entirely within it + * (remaining >= 32) has nothing reserved. + */ +static uint32_t bitmapN_reserved(uint32_t wd, uint32_t n) +{ + int64_t remaining = (int64_t)wd + 1 - 32 * (int64_t)n; + + if (remaining <= 0) { + return 0xffffffffu; + } + if (remaining >= 32) { + return 0; + } + return ~((1u << remaining) - 1); +} + +static uint32_t compute_reserved_mask(const RegDesc *d, const MpamGates *g) +{ + switch (d->width_kind) { + case WK_NONE: + return d->reserved_mask; + case WK_CMAX: + /* bits[15:0] width-dependent; bit31 SOFTLIM gated on + * HAS_CMAX_SOFTLIM; bits[30:16] always reserved. */ + return frac16_reserved(g->cmax_wd) | 0x7fff0000u | + (g->has_cmax_softlim ? 0 : 0x80000000u); + case WK_CMIN: + return frac16_reserved(g->cmax_wd) | 0xffff0000u; + case WK_CASSOC: + return frac16_reserved(g->cassoc_wd) | 0xffff0000u; + case WK_MBW_MIN: + return frac16_reserved(g->bwa_wd) | 0xffff0000u; + case WK_MBW_MAX: + /* bit31 HARDLIM unconditionally valid; bits[30:16] reserved. */ + return frac16_reserved(g->bwa_wd) | 0x7fff0000u; + case WK_MBW_PROP: + /* bit31 EN unconditionally valid; bits[30:16] reserved. */ + return frac16_reserved(g->bwa_wd) | 0x7fff0000u; + case WK_PRI: + /* INTPRI in bits[15:0], DSPRI in bits[31:16] (only present/valid + * when HAS_DSPRI); both are MSB-aligned fraction fields. */ + return frac16_reserved(g->intpri_wd) | + (g->has_dspri ? (frac16_reserved(g->dspri_wd) << 16) : + 0xffff0000u); + } + g_assert_not_reached(); +} + +/* Select RIS 'ris' both for the main PARTID-config path (CFG_PART_SEL) + * and for the monitor-config path (MSMON_CFG_MON_SEL), which has an + * independent RIS selector (IHI0099 7.2.9). */ +static void select_ris(uint64_t base, uint32_t ris) +{ + writel(base + R_MPAMF_CFG_PART_SEL, ris << CFG_PART_SEL_RIS_SHIFT); + writel(base + R_MSMON_CFG_MON_SEL, ris << CFG_PART_SEL_RIS_SHIFT); +} + +static MpamGates read_gates(uint64_t base) +{ + MpamGates g; + uint32_t idr_lo = readl(base + R_MPAMF_IDR_LO); + uint32_t msmon_idr, ccap_idr, mbw_idr, cpor_idr, idr_hi; + + memset(&g, 0, sizeof(g)); + + g.has_ccap_part = idr_lo & IDR_HAS_CCAP_PART; + g.has_cpor_part = idr_lo & IDR_HAS_CPOR_PART; + g.has_mbw_part = idr_lo & IDR_HAS_MBW_PART; + g.has_pri_part = idr_lo & IDR_HAS_PRI_PART; + g.ext = idr_lo & IDR_EXT; + g.has_impl_idr = idr_lo & IDR_HAS_IMPL_IDR; + g.has_msmon = idr_lo & IDR_HAS_MSMON; + g.has_partid_nrw = idr_lo & IDR_HAS_PARTID_NRW; + + msmon_idr = g.has_msmon ? readl(base + R_MPAMF_MSMON_IDR) : 0; + g.has_msmon_csu = msmon_idr & MSMON_IDR_MSMON_CSU; + g.has_msmon_mbwu = msmon_idr & MSMON_IDR_MSMON_MBWU; + + ccap_idr = g.has_ccap_part ? readl(base + R_MPAMF_CCAP_IDR) : 0; + g.has_cmax = g.has_ccap_part && !(ccap_idr & CCAP_IDR_NO_CMAX); + g.has_cmin = g.has_ccap_part && (ccap_idr & CCAP_IDR_HAS_CMIN); + g.has_cassoc = g.has_ccap_part && (ccap_idr & CCAP_IDR_HAS_CASSOC); + g.has_cmax_softlim = g.has_ccap_part && + (ccap_idr & CCAP_IDR_HAS_CMAX_SOFTLIM); + g.cmax_wd = (ccap_idr >> CCAP_IDR_CMAX_WD_SHIFT) & CCAP_IDR_CMAX_WD_MASK; + g.cassoc_wd = (ccap_idr >> CCAP_IDR_CASSOC_WD_SHIFT) & + CCAP_IDR_CASSOC_WD_MASK; + + mbw_idr = g.has_mbw_part ? readl(base + R_MPAMF_MBW_IDR) : 0; + g.has_mbw_min = g.has_mbw_part && (mbw_idr & MBW_IDR_HAS_MIN); + g.has_mbw_max = g.has_mbw_part && (mbw_idr & MBW_IDR_HAS_MAX); + g.has_mbw_pbm = g.has_mbw_part && (mbw_idr & MBW_IDR_HAS_PBM); + g.has_mbw_prop = g.has_mbw_part && (mbw_idr & MBW_IDR_HAS_PROP); + g.bwa_wd = (mbw_idr >> MBW_IDR_BWA_WD_SHIFT) & MBW_IDR_BWA_WD_MASK; + g.bwpbm_wd = (mbw_idr >> MBW_IDR_BWPBM_WD_SHIFT) & MBW_IDR_BWPBM_WD_MASK; + + cpor_idr = g.has_cpor_part ? readl(base + R_MPAMF_CPOR_IDR) : 0; + g.cpbm_wd = (cpor_idr >> CPOR_IDR_CPBM_WD_SHIFT) & CPOR_IDR_CPBM_WD_MASK; + + { + uint32_t pri_idr = g.has_pri_part ? readl(base + R_MPAMF_PRI_IDR) : 0; + + g.intpri_wd = (pri_idr >> PRI_IDR_INTPRI_WD_SHIFT) & + PRI_IDR_INTPRI_WD_MASK; + g.has_dspri = g.has_pri_part && (pri_idr & PRI_IDR_HAS_DSPRI); + g.dspri_wd = (pri_idr >> PRI_IDR_DSPRI_WD_SHIFT) & + PRI_IDR_DSPRI_WD_MASK; + } + + /* + * MPAMF_ESR/MPAMF_ECR are mandatory in base (non-extended) MPAM, so + * HAS_ESR only narrows their presence when EXT==1 (IHI0099 9.3.1, + * "Discovery of MPAMF_ESR and MPAMF_ECR"); HAS_EXTD_ESR/HAS_ERR_MSI + * are themselves only meaningful (and non-RES0) when EXT==1. + */ + idr_hi = g.ext ? readl(base + R_MPAMF_IDR_HI) : 0; + g.has_esr = !g.ext || (idr_hi & IDR_HI_HAS_ESR); + g.has_extd_esr = g.ext && (idr_hi & IDR_HI_HAS_EXTD_ESR); + g.has_err_msi = g.ext && (idr_hi & IDR_HI_HAS_ERR_MSI); + + return g; +} + +static void check_presence_and_reserved(uint64_t base, const RegDesc *d, + const MpamGates *gates, + const char *ctx) +{ + uint32_t value = readl(base + d->offset); + uint32_t reserved_mask = compute_reserved_mask(d, gates); + + if (!gate_present(gates, d->gate)) { + expect(value == 0, + "%s: %s reads %#x while gated absent, expected RES0", + ctx, d->name, value); + } else { + expect((value & reserved_mask) == 0, + "%s: %s reserved bits set, value=%#x reserved_mask=%#x", + ctx, d->name, value, reserved_mask); + } +} + +static void check_write_behaviour(uint64_t base, const RegDesc *d, + const MpamGates *gates, const char *ctx) +{ + uint32_t orig = readl(base + d->offset); + uint32_t reserved_mask = compute_reserved_mask(d, gates); + uint32_t after; + + writel(base + d->offset, 0xffffffffu); + after = readl(base + d->offset); + + if (d->ro) { + expect(after == orig, + "%s: %s is read-only but changed %#x -> %#x on write", + ctx, d->name, orig, after); + } else if (!gate_present(gates, d->gate)) { + expect(after == 0, + "%s: %s write took effect (%#x) while gated absent", + ctx, d->name, after); + } else { + expect((after & reserved_mask) == 0, + "%s: %s reserved bits became settable, value=%#x reserved_mask=%#x", + ctx, d->name, after, reserved_mask); + } + + /* Restore so later registers/RIS in this MSC aren't perturbed. */ + writel(base + d->offset, orig); +} + +/* + * MPAMCFG_CPBM<n>/MPAMCFG_MBW_PBM<n> are a multi-word bitmap, unlike every + * other register in regs[] - walk every word up to one past the + * implemented range (wd/32 + 1 words), rather than just word 0, to confirm + * the first fully-unimplemented word also reads back as entirely reserved. + */ +static void check_bitmap_reg(uint64_t base, uint32_t offset0, uint32_t wd, + bool present, const char *name, + const char *ctx) +{ + uint32_t words_impl = wd / 32 + 1; + uint32_t words_to_check = words_impl + 1; + uint32_t n; + + for (n = 0; n < words_to_check; n++) { + uint32_t offset = offset0 + n * 4; + uint32_t reserved_mask = present ? bitmapN_reserved(wd, n) : + 0xffffffffu; + uint32_t orig = readl(base + offset); + uint32_t after; + char *rctx = g_strdup_printf("%s word %u", name, n); + + if (!present) { + expect(orig == 0, + "%s: %s reads %#x while gated absent, expected RES0", + ctx, rctx, orig); + } else { + expect((orig & reserved_mask) == 0, + "%s: %s reserved bits set, value=%#x reserved_mask=%#x", + ctx, rctx, orig, reserved_mask); + } + + writel(base + offset, 0xffffffffu); + after = readl(base + offset); + if (!present) { + expect(after == 0, + "%s: %s write took effect (%#x) while gated absent", + ctx, rctx, after); + } else { + expect((after & reserved_mask) == 0, + "%s: %s reserved bits became settable, value=%#x reserved_mask=%#x", + ctx, rctx, after, reserved_mask); + } + writel(base + offset, orig); + + g_free(rctx); + } +} + +static void check_ris_selection(uint64_t base, uint32_t num_ris, + const char *ctx) +{ + uint32_t before, after, ris_mask = 0xfu << CFG_PART_SEL_RIS_SHIFT; + uint32_t ris; + + before = readl(base + R_MPAMF_CFG_PART_SEL); + writel(base + R_MPAMF_CFG_PART_SEL, num_ris << CFG_PART_SEL_RIS_SHIFT); + after = readl(base + R_MPAMF_CFG_PART_SEL); + expect((after & ris_mask) == (before & ris_mask), + "%s: selecting out-of-range RIS %u (num_ris=%u) was not rejected" + " (CFG_PART_SEL %#x -> %#x)", + ctx, num_ris, num_ris, before, after); + + for (ris = 0; ris < num_ris; ris++) { + writel(base + R_MPAMF_CFG_PART_SEL, ris << CFG_PART_SEL_RIS_SHIFT); + after = readl(base + R_MPAMF_CFG_PART_SEL); + expect(((after >> CFG_PART_SEL_RIS_SHIFT) & 0xf) == ris, + "%s: selecting valid RIS %u did not round-trip (read back %#x)", + ctx, ris, after); + } + + writel(base + R_MPAMF_CFG_PART_SEL, before); +} + +static void check_msc(uint64_t base, const char *ctx) +{ + uint32_t idr_hi, num_ris; + size_t i; + + qtest_system_reset(global_qtest); + + select_ris(base, 0); + idr_hi = readl(base + R_MPAMF_IDR_LO) & IDR_EXT ? + readl(base + R_MPAMF_IDR_HI) : 0; + num_ris = (idr_hi & IDR_HI_HAS_RIS) ? + ((idr_hi >> IDR_HI_RIS_MAX_SHIFT) & IDR_HI_RIS_MAX_MASK) + 1 : 1; + + g_test_message("%s: num_ris=%u", ctx, num_ris); + + if (num_ris > 1) { + check_ris_selection(base, num_ris, ctx); + } + + for (uint32_t ris = 0; ris < num_ris; ris++) { + MpamGates gates; + char *ris_ctx = g_strdup_printf("%s RIS %u", ctx, ris); + + select_ris(base, ris); + gates = read_gates(base); + + for (i = 0; i < G_N_ELEMENTS(regs); i++) { + check_presence_and_reserved(base, ®s[i], &gates, ris_ctx); + check_write_behaviour(base, ®s[i], &gates, ris_ctx); + } + + check_bitmap_reg(base, R_MPAMCFG_CPBM0, gates.cpbm_wd, + gates.has_cpor_part, "MPAMCFG_CPBM0", ris_ctx); + check_bitmap_reg(base, R_MPAMCFG_MBW_PBM0, gates.bwpbm_wd, + gates.has_mbw_pbm, "MPAMCFG_MBW_PBM0", ris_ctx); + + g_free(ris_ctx); + } +} + +static void test_mem_msc(void) +{ + check_msc(MEM_MSC_BASE, "mem MSC"); +} + +static void test_cache_msc_0(void) { check_msc(cache_msc_base[0], "cache MSC 0 (L3 socket0)"); } +static void test_cache_msc_1(void) { check_msc(cache_msc_base[1], "cache MSC 1 (L3 socket1)"); } +static void test_cache_msc_2(void) { check_msc(cache_msc_base[2], "cache MSC 2 (L2 socket0/cluster0)"); } +static void test_cache_msc_3(void) { check_msc(cache_msc_base[3], "cache MSC 3 (L2 socket0/cluster1)"); } +static void test_cache_msc_4(void) { check_msc(cache_msc_base[4], "cache MSC 4 (L2 socket1/cluster0)"); } +static void test_cache_msc_5(void) { check_msc(cache_msc_base[5], "cache MSC 5 (L2 socket1/cluster1)"); } + +/* + * mpam-min-msc=off gives each NUMA node with memory its own single-RIS mem + * MSC instead of one MSC with multiple RIS - reboot and re-run the same + * check_msc() walk against both mem MSCs and all 6 (now differently based) + * cache MSCs, to confirm create_mpam_msc()'s base-address arithmetic for + * this path. + */ +static void test_mpam_min_msc_off(void) +{ + char *machine_args = g_strdup_printf(MACHINE_FMT_MIN_MSC_OFF, ""); + size_t i; + + qtest_end(); + qtest_start(machine_args); + g_free(machine_args); + + for (i = 0; i < G_N_ELEMENTS(mem_msc_base_min_msc_off); i++) { + char *ctx = g_strdup_printf("mem MSC %zu (mpam-min-msc=off)", i); + check_msc(mem_msc_base_min_msc_off[i], ctx); + g_free(ctx); + } + for (i = 0; i < G_N_ELEMENTS(cache_msc_base_min_msc_off); i++) { + char *ctx = g_strdup_printf("cache MSC %zu (mpam-min-msc=off)", i); + check_msc(cache_msc_base_min_msc_off[i], ctx); + g_free(ctx); + } +} + +int main(int argc, char **argv) +{ + int ret; + const char *arch = qtest_get_arch(); + GPtrArray *full_argv = g_ptr_array_new(); + int full_argc; + + /* + * Failures here are reported via g_test_fail()/g_test_message() so the + * walk can continue past the first mismatch; that only works if the + * overall suite doesn't bail out after the first failed test, which is + * GLib's default without "-k"/"--keep-going". Inject it unconditionally + * so this holds regardless of how the binary is invoked (meson test, + * standalone, ...). + */ + g_ptr_array_add(full_argv, argv[0]); + g_ptr_array_add(full_argv, (char *)"-k"); + for (int i = 1; i < argc; i++) { + g_ptr_array_add(full_argv, argv[i]); + } + full_argc = full_argv->len; + g_ptr_array_add(full_argv, NULL); + argv = (char **)full_argv->pdata; + argc = full_argc; + + g_test_init(&argc, &argv, NULL); + + if (strcmp(arch, "aarch64") || !qtest_has_machine("virt")) { + g_test_skip("'aarch64/virt' machine is not available"); + return 0; + } + + qtest_add_func("/arm-mpam/mem-msc", test_mem_msc); + qtest_add_func("/arm-mpam/cache-msc-0", test_cache_msc_0); + qtest_add_func("/arm-mpam/cache-msc-1", test_cache_msc_1); + qtest_add_func("/arm-mpam/cache-msc-2", test_cache_msc_2); + qtest_add_func("/arm-mpam/cache-msc-3", test_cache_msc_3); + qtest_add_func("/arm-mpam/cache-msc-4", test_cache_msc_4); + qtest_add_func("/arm-mpam/cache-msc-5", test_cache_msc_5); + qtest_add_func("/arm-mpam/mpam-min-msc-off", test_mpam_min_msc_off); + + { + char *machine_args = g_strdup_printf(MACHINE_FMT, ""); + qtest_start(machine_args); + g_free(machine_args); + } + ret = g_test_run(); + qtest_end(); + + g_test_message("total failures recorded: %d", g_failures); + + return ret; +} diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index ff4bf9528a..ea2ecb7eea 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -296,6 +296,7 @@ qtests_aarch64 = \ config_all_devices.has_key('CONFIG_ARM_VIRT') ? ['iommu-smmuv3-test'] : []) + \ (config_all_devices.has_key('CONFIG_WDT_SBSA') ? ['sbsa-gwdt-test'] : []) + \ + (config_all_devices.has_key('CONFIG_MPAM') ? ['arm-mpam-test'] : []) + \ qtests_cxl + \ ['arm-cpu-features', 'numa-test', diff --git a/tests/qtest/run-arm-mpam-test.sh b/tests/qtest/run-arm-mpam-test.sh new file mode 100755 index 0000000000..03323afee8 --- /dev/null +++ b/tests/qtest/run-arm-mpam-test.sh @@ -0,0 +1,33 @@ +#!/bin/sh +# Build and run tests/qtest/arm-mpam-test.c against a given build directory. +# +# Usage: tests/qtest/run-arm-mpam-test.sh [build-dir] [-- extra test args] +# +# build-dir defaults to "build" relative to the repo root, and must already +# be configured with --target-list=aarch64-softmmu (or including it). + +set -e + +SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd) +REPO_ROOT=$(cd "$SCRIPT_DIR/../.." && pwd) +BUILD_DIR="${1:-$REPO_ROOT/build}" + +if [ -n "$1" ] && [ "$1" != "--" ]; then + shift +fi +if [ "$1" = "--" ]; then + shift +fi + +if [ ! -f "$BUILD_DIR/build.ninja" ]; then + echo "error: '$BUILD_DIR' is not a configured build directory" >&2 + echo "configure one first, e.g.:" >&2 + echo " $REPO_ROOT/configure --target-list=aarch64-softmmu" >&2 + exit 1 +fi + +cd "$BUILD_DIR" +ninja tests/qtest/arm-mpam-test + +QTEST_QEMU_BINARY=./qemu-system-aarch64 \ + exec ./tests/qtest/arm-mpam-test --verbose "$@" -- 2.43.0 > > > > > Jonathan > > > > p.s. On off chance that (carefully unnamed) engineer sees this I > > had no idea ruanjinjie was working on this. > > > > Must have been something in the air :) > ^ permalink raw reply related [flat|nested] 14+ messages in thread
* Re: [PATCH 00/37] Add Arm MPAM emulation 2026-11-06 23:14 ` Jonathan Cameron @ 2026-11-09 7:42 ` Jinjie Ruan 0 siblings, 0 replies; 14+ messages in thread From: Jinjie Ruan @ 2026-11-09 7:42 UTC (permalink / raw) To: Jonathan Cameron, Jinjie Ruan Cc: Jonathan Cameron, Peter Maydell, anisinha, armbru, eblake, imammedo, michael.roth, mst, pbonzini, qemu-devel, qemu-arm 在 2026/11/7 07:14, Jonathan Cameron 写道: > >> >> Hi Jonathan, >> >>> >>> FWIW the much more minimal emulation that I posted years ago and >>> which I few crumbs of in here came up at plumbers this year with >> >> With your foundational patch, it is very convenient to extend >> other functionality. >> >>> an engineer from yet another company asking if I could look at >>> rebasing and upstreaming because they also found it very useful. >> >> Glad to hear that someone else is also interested in QEMU emulation of MPAM. >> >>> I was trying to work out if I could fit that into my todo list >>> or whether to ask a colleague to do it. >>> >>> As such I'm very pleased to see this surface a few days later! >>> Even what I had was very useful for testing so I'm with this >> >> Feel the same way when developing new features or adapting test cases. >> >>> much extended feature set I'm not surprised to hear even more >>> issues have shaken out. >>> >>> It might take me a little while to get time to look at this series >>> but I definitely plan on doing so! >> >> Look forward to your review and suggestions. > > I perhaps got a little carried away and decided to see how good an LLM > was at generating test cases for this... Was a little painful but > in the end I think helpful. > > I also span a bunch of runtime options up to exercise all the optional > properties, field widths etc. That adds about 600 lines of code and > 25 machine properties (assuming just memory controller, l2 and l3 caches > + all homogeneous.) I might clean that up and post at some point but > it feels like an optional stage 2 on what you have here! Hi Jonathan, Thanks for doing this. > > First up, bunch of fixes related to this. The field access width ones > are probably most important and then the various width and HAS_ checks. > I haven't checked these all closely but some are obviously fine. Will add these fix in v2, thank you! > > Because it might be helpful, the test follows. I'm not suggesting this be > included for now anyway but might help you with v2! > > Maybe long term we include a self test, but one that has had more review > than I've given this before sharing. Thank you! have a selftest is good to find these problems. Best regards, Jinjie > > Easy to run with: > > QTEST_QEMU_BINARY=./qemu-system-aarch64 ./tests/qtest/arm-mpam-test --verbose > > diff --git a/hw/arm/mpam.c b/hw/arm/mpam.c > index f4f40cacf0..15280c44a0 100644 > --- a/hw/arm/mpam.c > +++ b/hw/arm/mpam.c > @@ -711,6 +711,37 @@ void mpam_cache_fill_info(Object *obj, MpamCacheInfo *info) > > } > > +/* > + * IHI0099 9.4: a width-reported fraction field occupies the most > + * significant 'wd' bits of its 16-bit field; the remaining low bits are > + * RAZ/WI. Returns a mask of the bits software is allowed to set. > + */ > +static uint32_t mpam_fracwd_mask(uint32_t wd) > +{ > + if (wd >= 16) { > + return 0xffffu; > + } > + return ~((1u << (16 - wd)) - 1) & 0xffffu; > +} > + > +/* > + * IHI0099 9.4 (MPAMCFG_CPBM<n>/MPAMCFG_MBW_PBM<n>): bit x of register n is > + * writable iff (n*32)+x <= wd. Returns the writable-bit mask for word > + * 'array_offset' of such a bitmap, given the overall bitmap width 'wd'. > + */ > +static uint32_t mpam_bitmap_mask(uint32_t wd, uint32_t array_offset) > +{ > + int64_t remaining = (int64_t)wd + 1 - 32 * (int64_t)array_offset; > + > + if (remaining <= 0) { > + return 0; > + } > + if (remaining >= 32) { > + return 0xffffffffu; > + } > + return (1u << remaining) - 1; > +} > + > static uint64_t mpam_msc_read_reg(void *opaque, hwaddr offset, > unsigned size) > { > @@ -781,14 +812,38 @@ static uint64_t mpam_msc_read_reg(void *opaque, hwaddr offset, > case A_MPAMF_MSMON_IDR: > return mpamf->msmon_idr; > case A_MPAMF_CSUMON_IDR: > + if (!FIELD_EX32(mpamf->idr, MPAMF_IDR, HAS_MSMON) || > + !FIELD_EX32(mpamf->msmon_idr, MPAMF_MSMON_IDR, MSMON_CSU)) { > + qemu_log_mask(LOG_UNIMP, > + "MPAM: Unexpected read of CSUMON_IDR with no CSU monitor support\n"); > + return 0; > + } > return mpamf->csumon_idr; > case A_MPAMF_MBWUMON_IDR: > + if (!FIELD_EX32(mpamf->idr, MPAMF_IDR, HAS_MSMON) || > + !FIELD_EX32(mpamf->msmon_idr, MPAMF_MSMON_IDR, MSMON_MBWU)) { > + qemu_log_mask(LOG_UNIMP, > + "MPAM: Unexpected read of MBWUMON_IDR with no MBWU monitor support\n"); > + return 0; > + } > return mpamf->mbwumon_idr; > case A_MSMON_CFG_MON_SEL: > return s->mon_sel; > case A_MSMON_CFG_CSU_FLT: > + if (!FIELD_EX32(mpamf->idr, MPAMF_IDR, HAS_MSMON) || > + !FIELD_EX32(mpamf->msmon_idr, MPAMF_MSMON_IDR, MSMON_CSU)) { > + qemu_log_mask(LOG_UNIMP, > + "MPAM: Unexpected read of CSU_FLT with no CSU monitor support\n"); > + return 0; > + } > return mpam_csu_mon(s, mpam_mon_ris(s))->flt; > case A_MSMON_CFG_CSU_CTL: > + if (!FIELD_EX32(mpamf->idr, MPAMF_IDR, HAS_MSMON) || > + !FIELD_EX32(mpamf->msmon_idr, MPAMF_MSMON_IDR, MSMON_CSU)) { > + qemu_log_mask(LOG_UNIMP, > + "MPAM: Unexpected read of CSU_CTL with no CSU monitor support\n"); > + return 0; > + } > return mpam_csu_mon(s, mpam_mon_ris(s))->ctl; > case A_MSMON_CSU ... (A_MSMON_CSU + 3): > case A_MSMON_CSU_CAPTURE ... (A_MSMON_CSU_CAPTURE + 3): > @@ -802,12 +857,26 @@ static uint64_t mpam_msc_read_reg(void *opaque, hwaddr offset, > return extract32(v, off * 8, 32 - off * 8); > } > case A_MSMON_CFG_MBWU_FLT: > + if (!FIELD_EX32(mpamf->idr, MPAMF_IDR, HAS_MSMON) || > + !FIELD_EX32(mpamf->msmon_idr, MPAMF_MSMON_IDR, MSMON_MBWU)) { > + qemu_log_mask(LOG_UNIMP, > + "MPAM: Unexpected read of MBWU_FLT with no MBWU monitor support\n"); > + return 0; > + } > 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; > + MpamMbwuMon *m; > + uint32_t ctl; > > + if (!FIELD_EX32(mpamf->idr, MPAMF_IDR, HAS_MSMON) || > + !FIELD_EX32(mpamf->msmon_idr, MPAMF_MSMON_IDR, MSMON_MBWU)) { > + qemu_log_mask(LOG_UNIMP, > + "MPAM: Unexpected read of MBWU_CTL with no MBWU monitor support\n"); > + return 0; > + } > + m = mpam_mbwu_mon(s, mpam_mon_ris(s)); > + ctl = m->ctl; > if (m->oflow) { > ctl |= R_MSMON_CFG_MBWU_CTL_OFLOW_STATUS_MASK; > } > @@ -852,11 +921,24 @@ static uint64_t mpam_msc_read_reg(void *opaque, hwaddr offset, > case A_MPAMF_ERR_MSI_ATTR: > return mpamf->err_msi_attr; > case A_MPAMF_ECR: > + if (FIELD_EX64(mpamf->idr, MPAMF_IDR, EXT) && > + !FIELD_EX64(mpamf->idr, MPAMF_IDR, HAS_ESR)) { > + qemu_log_mask(LOG_UNIMP, > + "MPAM: Unexpected read of ECR with no ESR support\n"); > + return 0; > + } > return s->ecr; > case A_MPAMF_ESR: > + if (FIELD_EX64(mpamf->idr, MPAMF_IDR, EXT) && > + !FIELD_EX64(mpamf->idr, MPAMF_IDR, HAS_ESR)) { > + qemu_log_mask(LOG_UNIMP, > + "MPAM: Unexpected read of ESR with no ESR support\n"); > + return 0; > + } > return s->esr; > case A_MPAMF_ESR + 0x4: > - if (!FIELD_EX32(mpamf->idr, MPAMF_IDR, EXT)) { > + if (!FIELD_EX64(mpamf->idr, MPAMF_IDR, EXT) || > + !FIELD_EX64(mpamf->idr, MPAMF_IDR, HAS_EXTD_ESR)) { > qemu_log_mask(LOG_UNIMP, > "MPAM: Unexpected read of extended ESR\n"); > return 0; > @@ -876,33 +958,59 @@ static uint64_t mpam_msc_read_reg(void *opaque, hwaddr offset, > return mpamf->cfg_intpartid[s->part_sel]; > } > case A_MPAMF_MPAMCFG_CMAX: > - if (!FIELD_EX32(mpamf->idr, MPAMF_IDR, HAS_CCAP_PART)) { > + if (!FIELD_EX32(mpamf->idr, MPAMF_IDR, HAS_CCAP_PART) || > + FIELD_EX32(mpamf->ccap_idr, MPAMF_CCAP_IDR, NO_CMAX)) { > qemu_log_mask(LOG_UNIMP, > - "MPAM: Unexpected read of CMAX with no CCAP support\n"); > + "MPAM: Unexpected read of CMAX with no CMAX support\n"); > return 0; > } > return mpamf->per_nrw_id[nrw_part_sel].cfg_cmax; > case A_MPAMF_MPAMCFG_CMIN: > - if (!FIELD_EX32(mpamf->idr, MPAMF_IDR, HAS_CCAP_PART)) { > + if (!FIELD_EX32(mpamf->idr, MPAMF_IDR, HAS_CCAP_PART) || > + !FIELD_EX32(mpamf->ccap_idr, MPAMF_CCAP_IDR, HAS_CMIN)) { > qemu_log_mask(LOG_UNIMP, > - "MPAM: Unexpected read of CMIN with no CCAP support\n"); > + "MPAM: Unexpected read of CMIN with no CMIN support\n"); > return 0; > } > return mpamf->per_nrw_id[nrw_part_sel].cfg_cmin; > case A_MPAMF_MPAMCFG_CASSOC: > - if (!FIELD_EX32(mpamf->idr, MPAMF_IDR, HAS_CCAP_PART)) { > + if (!FIELD_EX32(mpamf->idr, MPAMF_IDR, HAS_CCAP_PART) || > + !FIELD_EX32(mpamf->ccap_idr, MPAMF_CCAP_IDR, HAS_CASSOC)) { > qemu_log_mask(LOG_UNIMP, > - "MPAM: Unexpected read of CASSOC with no CCAP support\n"); > + "MPAM: Unexpected read of CASSOC with no CASSOC support\n"); > return 0; > } > return mpamf->per_nrw_id[nrw_part_sel].cfg_cassoc; > case A_MPAMF_MPAMCFG_MBW_MIN: > + if (!FIELD_EX32(mpamf->idr, MPAMF_IDR, HAS_MBW_PART) || > + !FIELD_EX32(mpamf->mbw_idr, MPAMF_MBW_IDR, HAS_MIN)) { > + qemu_log_mask(LOG_UNIMP, > + "MPAM: Unexpected read of MBW_MIN with no MBW_MIN support\n"); > + return 0; > + } > return mpamf->per_nrw_id[nrw_part_sel].cfg_mbw_min; > case A_MPAMF_MPAMCFG_MBW_MAX: > + if (!FIELD_EX32(mpamf->idr, MPAMF_IDR, HAS_MBW_PART) || > + !FIELD_EX32(mpamf->mbw_idr, MPAMF_MBW_IDR, HAS_MAX)) { > + qemu_log_mask(LOG_UNIMP, > + "MPAM: Unexpected read of MBW_MAX with no MBW_MAX support\n"); > + return 0; > + } > return mpamf->per_nrw_id[nrw_part_sel].cfg_mbw_max; > case A_MPAMF_MPAMCFG_PRI: > + if (!FIELD_EX32(mpamf->idr, MPAMF_IDR, HAS_PRI_PART)) { > + qemu_log_mask(LOG_UNIMP, > + "MPAM: Unexpected read of CFG_PRI with no PRI PART support\n"); > + return 0; > + } > return mpamf->per_nrw_id[nrw_part_sel].cfg_pri; > case A_MPAMF_MPAMCFG_MBW_PROP: > + if (!FIELD_EX32(mpamf->idr, MPAMF_IDR, HAS_MBW_PART) || > + !FIELD_EX32(mpamf->mbw_idr, MPAMF_MBW_IDR, HAS_PROP)) { > + qemu_log_mask(LOG_UNIMP, > + "MPAM: Unexpected read of MBW_PROP with no MBW_PROP support\n"); > + return 0; > + } > return mpamf->per_nrw_id[nrw_part_sel].cfg_mbw_prop; > case A_MPAMF_MPAMCFG_CPBM0... > (A_MPAMF_MPAMCFG_CPBM0 + ((MPAM_CACHE_PART + 31) / 32 - 1) * 4): > @@ -922,9 +1030,10 @@ static uint64_t mpam_msc_read_reg(void *opaque, hwaddr offset, > { > uint32_t array_offset; > > - if (!FIELD_EX32(mpamf->idr, MPAMF_IDR, HAS_MBW_PART)) { > + if (!FIELD_EX32(mpamf->idr, MPAMF_IDR, HAS_MBW_PART) || > + !FIELD_EX32(mpamf->mbw_idr, MPAMF_MBW_IDR, HAS_PBM)) { > qemu_log_mask(LOG_UNIMP, > - "MPAM: Unexpected read of MBW_PBM with no MBW_PART support\n"); > + "MPAM: Unexpected read of MBW_PBM with no MBW_PBM support\n"); > return 0; > } > array_offset = (offset - A_MPAMF_MPAMCFG_MBW_PBM0) / sizeof(uint32_t); > @@ -1020,70 +1129,147 @@ static void mpam_msc_write_reg(void *opaque, hwaddr offset, uint64_t value, > FIELD_EX32(value, MPAMF_MPAMCFG_INTPARTID, INTPARTID); > return; > case A_MPAMF_MPAMCFG_CMAX: > + if (!FIELD_EX32(mpamf->idr, MPAMF_IDR, HAS_CCAP_PART) || > + FIELD_EX32(mpamf->ccap_idr, MPAMF_CCAP_IDR, NO_CMAX)) { > + qemu_log_mask(LOG_UNIMP, > + "MPAM: Unexpected write to CMAX when not supported\n"); > + return; > + } > if (value & ~MPAMF_MPAMCFG_CMAX_WRITE_MASK) { > qemu_log_mask(LOG_UNIMP, > "MPAM: Unexpected write to CMAX Mask=%x Value=%lx\n", > MPAMF_MPAMCFG_CMAX_WRITE_MASK, value); > } > - mpamf->per_nrw_id[nrw_part_sel].cfg_cmax = value; > + { > + uint32_t wd = FIELD_EX32(mpamf->ccap_idr, MPAMF_CCAP_IDR, > + CMAX_WD); > + uint32_t valid = mpam_fracwd_mask(wd); > + > + if (FIELD_EX32(mpamf->ccap_idr, MPAMF_CCAP_IDR, > + HAS_CMAX_SOFTLIM)) { > + valid |= R_MPAMF_MPAMCFG_CMAX_SOFTLIM_MASK; > + } > + mpamf->per_nrw_id[nrw_part_sel].cfg_cmax = value & valid; > + } > return; > case A_MPAMF_MPAMCFG_CMIN: > + if (!FIELD_EX32(mpamf->idr, MPAMF_IDR, HAS_CCAP_PART) || > + !FIELD_EX32(mpamf->ccap_idr, MPAMF_CCAP_IDR, HAS_CMIN)) { > + qemu_log_mask(LOG_UNIMP, > + "MPAM: Unexpected write to CMIN when not supported\n"); > + return; > + } > if (value & ~MPAMF_MPAMCFG_CMIN_WRITE_MASK) { > qemu_log_mask(LOG_UNIMP, > "MPAM: Unexpected write to CMIN Mask=%x Value=%lx\n", > MPAMF_MPAMCFG_CMIN_WRITE_MASK, value); > } > - mpamf->per_nrw_id[nrw_part_sel].cfg_cmin = value; > + { > + uint32_t wd = FIELD_EX32(mpamf->ccap_idr, MPAMF_CCAP_IDR, > + CMAX_WD); > + > + mpamf->per_nrw_id[nrw_part_sel].cfg_cmin = > + value & mpam_fracwd_mask(wd); > + } > return; > case A_MPAMF_MPAMCFG_CASSOC: > + if (!FIELD_EX32(mpamf->idr, MPAMF_IDR, HAS_CCAP_PART) || > + !FIELD_EX32(mpamf->ccap_idr, MPAMF_CCAP_IDR, HAS_CASSOC)) { > + qemu_log_mask(LOG_UNIMP, > + "MPAM: Unexpected write to CASSOC when not supported\n"); > + return; > + } > if (value & ~MPAMF_MPAMCFG_CASSOC_WRITE_MASK) { > qemu_log_mask(LOG_UNIMP, > "MPAM: Unexpected write to CASSOC Mask=%x Value=%lx\n", > MPAMF_MPAMCFG_CASSOC_WRITE_MASK, value); > } > - mpamf->per_nrw_id[nrw_part_sel].cfg_cassoc = value; > + { > + uint32_t wd = FIELD_EX32(mpamf->ccap_idr, MPAMF_CCAP_IDR, > + CASSOC_WD); > + > + mpamf->per_nrw_id[nrw_part_sel].cfg_cassoc = > + value & mpam_fracwd_mask(wd); > + } > return; > case A_MPAMF_MPAMCFG_MBW_MIN: > + if (!FIELD_EX32(mpamf->idr, MPAMF_IDR, HAS_MBW_PART) || > + !FIELD_EX32(mpamf->mbw_idr, MPAMF_MBW_IDR, HAS_MIN)) { > + qemu_log_mask(LOG_UNIMP, > + "MPAM: Unexpected write to MBW_MIN when not supported\n"); > + return; > + } > if (value & ~MPAMF_MPAMCFG_MBW_MIN_WRITE_MASK) { > qemu_log_mask(LOG_UNIMP, > "MPAM: Unexpected write to MBW_MIN Mask=%x Value=%lx\n", > MPAMF_MPAMCFG_MBW_MIN_WRITE_MASK, value); > } > - mpamf->per_nrw_id[nrw_part_sel].cfg_mbw_min = value; > + { > + uint32_t wd = FIELD_EX32(mpamf->mbw_idr, MPAMF_MBW_IDR, BWA_WD); > + > + mpamf->per_nrw_id[nrw_part_sel].cfg_mbw_min = > + value & mpam_fracwd_mask(wd); > + } > return; > case A_MPAMF_MPAMCFG_MBW_MAX: > + if (!FIELD_EX32(mpamf->idr, MPAMF_IDR, HAS_MBW_PART) || > + !FIELD_EX32(mpamf->mbw_idr, MPAMF_MBW_IDR, HAS_MAX)) { > + qemu_log_mask(LOG_UNIMP, > + "MPAM: Unexpected write to MBW_MAX when not supported\n"); > + return; > + } > if (value & ~MPAMF_MPAMCFG_MBW_MAX_WRITE_MASK) { > qemu_log_mask(LOG_UNIMP, > "MPAM: Unexpected write to MBW_MAX Mask=%x Value=%lx\n", > MPAMF_MPAMCFG_MBW_MAX_WRITE_MASK, value); > } > - mpamf->per_nrw_id[nrw_part_sel].cfg_mbw_max = value; > + { > + uint32_t wd = FIELD_EX32(mpamf->mbw_idr, MPAMF_MBW_IDR, BWA_WD); > + uint32_t valid = mpam_fracwd_mask(wd) | > + R_MPAMF_MPAMCFG_MBW_MAX_HARDLIM_MASK; > + > + mpamf->per_nrw_id[nrw_part_sel].cfg_mbw_max = value & valid; > + } > return; > case A_MPAMF_MPAMCFG_PRI: > if (!FIELD_EX32(mpamf->idr, MPAMF_IDR, HAS_PRI_PART)) { > qemu_log_mask(LOG_UNIMP, > "MPAM: Unexpected write to CFG_PRI when !HAS_PRI_PART\n"); > - } else { > - if (!FIELD_EX32(mpamf->pri_idr, MPAMF_PRI_IDR, HAS_DSPRI) && > - FIELD_EX32(value, MPAMF_MPAMCFG_PRI, DSPRI)) { > - qemu_log_mask(LOG_UNIMP, > - "MPAM: Unexpected write to CFGP_PRI DSPRI when !HAS_DSPRI\n"); > - } > - if (!FIELD_EX32(mpamf->pri_idr, MPAMF_PRI_IDR, HAS_INTPRI) && > - FIELD_EX32(value, MPAMF_MPAMCFG_PRI, INTPRI)) { > - qemu_log_mask(LOG_UNIMP, > - "MPAM: Unexpected write to CFGP_PRI INTPRI when !HAS_INTPRI\n"); > - } > + return; > + } > + { > + uint32_t intpri_wd = FIELD_EX32(mpamf->pri_idr, MPAMF_PRI_IDR, > + INTPRI_WD); > + bool has_dspri = FIELD_EX32(mpamf->pri_idr, MPAMF_PRI_IDR, > + HAS_DSPRI); > + uint32_t dspri_wd = FIELD_EX32(mpamf->pri_idr, MPAMF_PRI_IDR, > + DSPRI_WD); > + uint32_t valid = mpam_fracwd_mask(intpri_wd) | > + (has_dspri ? > + (mpam_fracwd_mask(dspri_wd) << 16) : 0); > + > + mpamf->per_nrw_id[nrw_part_sel].cfg_pri = value & valid; > } > - mpamf->per_nrw_id[nrw_part_sel].cfg_pri = value; > return; > case A_MPAMF_MPAMCFG_MBW_PROP: > + if (!FIELD_EX32(mpamf->idr, MPAMF_IDR, HAS_MBW_PART) || > + !FIELD_EX32(mpamf->mbw_idr, MPAMF_MBW_IDR, HAS_PROP)) { > + qemu_log_mask(LOG_UNIMP, > + "MPAM: Unexpected write to MBW_PROP when not supported\n"); > + return; > + } > if (value & ~MPAMF_MPAMCFG_MBW_PROP_WRITE_MASK) { > qemu_log_mask(LOG_UNIMP, > "MPAM: Unexpected write to MBW_PROP Mask=%x Value=%lx\n", > MPAMF_MPAMCFG_MBW_MAX_WRITE_MASK, value); > } > - mpamf->per_nrw_id[nrw_part_sel].cfg_mbw_prop = value; > + { > + uint32_t wd = FIELD_EX32(mpamf->mbw_idr, MPAMF_MBW_IDR, BWA_WD); > + uint32_t valid = mpam_fracwd_mask(wd) | > + R_MPAMF_MPAMCFG_MBW_PROP_EN_MASK; > + > + mpamf->per_nrw_id[nrw_part_sel].cfg_mbw_prop = value & valid; > + } > return; > case A_MPAMF_MPAMCFG_CPBM0... > (A_MPAMF_MPAMCFG_CPBM0 + ((MPAM_CACHE_PART + 31) / 32 - 1) * 4): > @@ -1105,15 +1291,21 @@ static void mpam_msc_write_reg(void *opaque, hwaddr offset, uint64_t value, > { > uint32_t array_offset; > > - if (!FIELD_EX32(mpamf->idr, MPAMF_IDR, HAS_MBW_PART)) { > + if (!FIELD_EX32(mpamf->idr, MPAMF_IDR, HAS_MBW_PART) || > + !FIELD_EX32(mpamf->mbw_idr, MPAMF_MBW_IDR, HAS_PBM)) { > qemu_log_mask(LOG_UNIMP, > - "MPAM: Unexpected write to MBW_PBM when !HAS_MBW_PART\n"); > + "MPAM: Unexpected write to MBW_PBM when not supported\n"); > return; > } > - /* TODO: Figure out write mask to check this stays in write bits */ > array_offset = (offset - A_MPAMF_MPAMCFG_MBW_PBM0) / sizeof(uint32_t); > > - mpamf->per_nrw_id[nrw_part_sel].cfg_mbw_pbm[array_offset] = value; > + { > + uint32_t wd = FIELD_EX32(mpamf->mbw_idr, MPAMF_MBW_IDR, > + BWPBM_WD); > + > + mpamf->per_nrw_id[nrw_part_sel].cfg_mbw_pbm[array_offset] = > + value & mpam_bitmap_mask(wd, array_offset); > + } > return; > } > case A_MSMON_CFG_MON_SEL: > @@ -1151,6 +1343,12 @@ static void mpam_msc_write_reg(void *opaque, hwaddr offset, uint64_t value, > { > uint32_t partid = FIELD_EX32(value, MSMON_CFG_MBWU_FLT, PARTID); > > + if (!FIELD_EX32(mpamf->idr, MPAMF_IDR, HAS_MSMON) || > + !FIELD_EX32(mpamf->msmon_idr, MPAMF_MSMON_IDR, MSMON_MBWU)) { > + qemu_log_mask(LOG_UNIMP, > + "MPAM: Unexpected write to MBWU_FLT with no MBWU monitor support\n"); > + return; > + } > if (partid >= s->num_partid) { > qemu_log_mask(LOG_GUEST_ERROR, > "MPAM: MBWU_FLT PARTID %d out of range\n", partid); > @@ -1166,9 +1364,16 @@ static void mpam_msc_write_reg(void *opaque, hwaddr offset, uint64_t value, > } > case A_MSMON_CFG_MBWU_CTL: > { > - MpamMbwuMon *m = mpam_mbwu_mon(s, mpam_mon_ris(s)); > + MpamMbwuMon *m; > > - m->ctl = value; > + if (!FIELD_EX32(mpamf->idr, MPAMF_IDR, HAS_MSMON) || > + !FIELD_EX32(mpamf->msmon_idr, MPAMF_MSMON_IDR, MSMON_MBWU)) { > + qemu_log_mask(LOG_UNIMP, > + "MPAM: Unexpected write to MBWU_CTL with no MBWU monitor support\n"); > + return; > + } > + m = mpam_mbwu_mon(s, mpam_mon_ris(s)); > + m->ctl = value & ~0x00040f00u; > if (value & R_MSMON_CFG_MBWU_CTL_OFLOW_STATUS_MASK) { > m->oflow = false; /* write-one-to-clear */ > } > @@ -1190,6 +1395,12 @@ static void mpam_msc_write_reg(void *opaque, hwaddr offset, uint64_t value, > { > uint32_t partid = FIELD_EX32(value, MSMON_CFG_CSU_FLT, PARTID); > > + if (!FIELD_EX32(mpamf->idr, MPAMF_IDR, HAS_MSMON) || > + !FIELD_EX32(mpamf->msmon_idr, MPAMF_MSMON_IDR, MSMON_CSU)) { > + qemu_log_mask(LOG_UNIMP, > + "MPAM: Unexpected write to CSU_FLT with no CSU monitor support\n"); > + return; > + } > if (partid >= s->num_partid) { > qemu_log_mask(LOG_GUEST_ERROR, > "MPAM: CSU_FLT PARTID %d out of range\n", partid); > @@ -1204,7 +1415,13 @@ static void mpam_msc_write_reg(void *opaque, hwaddr offset, uint64_t value, > return; > } > case A_MSMON_CFG_CSU_CTL: > - mpam_csu_mon(s, mpam_mon_ris(s))->ctl = value; > + if (!FIELD_EX32(mpamf->idr, MPAMF_IDR, HAS_MSMON) || > + !FIELD_EX32(mpamf->msmon_idr, MPAMF_MSMON_IDR, MSMON_CSU)) { > + qemu_log_mask(LOG_UNIMP, > + "MPAM: Unexpected write to CSU_CTL with no CSU monitor support\n"); > + return; > + } > + mpam_csu_mon(s, mpam_mon_ris(s))->ctl = value & ~0x7ffc0000u; > return; > case A_MSMON_CSU: > case A_MSMON_CSU_CAPTURE: > @@ -1220,6 +1437,12 @@ static void mpam_msc_write_reg(void *opaque, hwaddr offset, uint64_t value, > offset); > return; > case A_MPAMF_ECR: > + if (FIELD_EX64(mpamf->idr, MPAMF_IDR, EXT) && > + !FIELD_EX64(mpamf->idr, MPAMF_IDR, HAS_ESR)) { > + qemu_log_mask(LOG_UNIMP, > + "MPAM: Unexpected write to ECR with no ESR support\n"); > + return; > + } > if (value & ~MPAMF_ECR_WRITE_MASK) { > qemu_log_mask(LOG_UNIMP, > "MPAM: Unexpected write to ECR Mask=%x Value=%lx\n", > @@ -1230,16 +1453,28 @@ static void mpam_msc_write_reg(void *opaque, hwaddr offset, uint64_t value, > mpam_msc_update_err_irq(s); > return; > case A_MPAMF_ESR: > + if (FIELD_EX64(mpamf->idr, MPAMF_IDR, EXT) && > + !FIELD_EX64(mpamf->idr, MPAMF_IDR, HAS_ESR)) { > + qemu_log_mask(LOG_UNIMP, > + "MPAM: Unexpected write to ESR with no ESR support\n"); > + return; > + } > /* > * Software clears an error by writing ERRCODE as zero; writing a > * non-zero ERRCODE activates the level error interrupt (8.2). > */ > - s->esr = deposit64(s->esr, 0, 32, value); > + s->esr = deposit64(s->esr, 0, 32, value & ~0x70000000u); > mpam_msc_sync_esr_ecr(s); > mpam_msc_update_err_irq(s); > return; > case A_MPAMF_ESR + 0x4: > - s->esr = deposit64(s->esr, 32, 32, value); > + if (!FIELD_EX64(mpamf->idr, MPAMF_IDR, EXT) || > + !FIELD_EX64(mpamf->idr, MPAMF_IDR, HAS_EXTD_ESR)) { > + qemu_log_mask(LOG_UNIMP, > + "MPAM: Unexpected write to extended ESR\n"); > + return; > + } > + s->esr = deposit64(s->esr, 32, 32, value & ~0xfffffff0u); > mpam_msc_sync_esr_ecr(s); > mpam_msc_update_err_irq(s); > return; ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 00/37] Add Arm MPAM emulation 2026-10-16 9:46 [PATCH 00/37] Add Arm MPAM emulation Jinjie Ruan 2026-10-16 10:20 ` Peter Maydell @ 2026-10-19 9:22 ` Jinjie Ruan 2026-10-20 22:43 ` Drew Fustini 2 siblings, 0 replies; 14+ messages in thread From: Jinjie Ruan @ 2026-10-19 9:22 UTC (permalink / raw) To: Jinjie Ruan, anisinha, armbru, eblake, imammedo, michael.roth, mst, pbonzini, peter.maydell, qemu-devel, qemu-arm Hi all, Sorry, due to SMTP rate limiting, this series has been split into multiple threads. The complete 37 patches are as follows: - patch 1 ~ 16: https://lore.kernel.org/all/tencent_0D8D61C20611EC56598A6F86E7B869052E05@qq.com/ - patch 17 ~ 37: https://lore.kernel.org/all/tencent_88B063FC15FDB4BD89164484DB67A9B61907@qq.com/#t 在 2026/10/16 17:46, Jinjie Ruan 写道: > From: Jinjie Ruan <ruanjinjie@huawei.com> > > MPAM (Memory System Resource Partitioning and Monitoring) is Arm's > architecture for partitioning and monitoring system memory resources > - Cache portions > - Capacity limits > - Memory bandwidth and priority > > And MPAM is the Arm counterpart of x86 RDT, which Linux exposes to user > space via resctrl. > > QEMU so far has no MPAM support at all: the CPU-side system registers > are missing (a guest kernel that probes FEAT_MPAM takes an UNDEFINED > exception), and there are no Memory System Components (MSCs), so a virt > guest cannot exercise resctrl on Arm. > > This series adds MPAM 1.1 emulation for the virt machine, > and the structure is as below: > > * target/arm (patches 1-3): the CPU registers: > - MPAMIDR_EL1 and MPAM0_EL1/MPAM1_EL1 > - MPAM2_EL2/MPAMHCR_EL2 > - MPAMSM_EL1 for FEAT_MPAM_SMS > > * hw/acpi (patch 4): PPTT revision 3 with Cache IDs, which the kernel > needs to resolve the MPAM table's cache references to PPTT cache > nodes. > > * hw/arm/mpam device model (patches 5-15): built up layer by layer > - The register map as pure registerfields declarations > - The base MSC device with MMIO access > - The mpam-msc-mem and mpam-msc-cache subtypes > - MPAMCFG_PART_SEL selection > - Per-RIS partition config storage > - The partitioning features one at a time (CPOR cache portions, MBW > bandwidth, PRI priority, CCAP capacity) > - The ECR/ESR register surface > > * hw/arm/mpam (patch 16): PARTID narrowing via MPAMCFG_INTPARTID with > the identity-wrap default map > - The linux mainline driver exercises it by default and resctrl > reports num_closids=32. > > * hw/arm/virt + hw/acpi (patches 17-23): the board wiring, one concern > per patch > - Machine properties > - The MMIO window reservation > - Device locator/irq properties > - Memory-side MSC creation > - Cache MSC creation derived from the smp cache topology > - The ACPI MPAM table, split into the DEN0065 MSC node builders > and the table assembly > > * Monitoring (patches 24-29): > - The MSMON selection/window infrastructure > - The MBWU bandwidth monitor whose counters track the configured > bandwidth control (MBW_MAX or MBW_PBM, whichever the guest drives) > - CMIN/CASSOC storage > - PRI advertised on the memory side (backing the driver's MBPRI > pseudo-resource) > - The CSU occupancy monitor (a snapshot of the capacity scaled by > the CPBM fraction) > - The board sizing of that capacity per cache level > > *Error reporting (patches 30-33): > - ECR/ESR as MSC-global registers (IHI 0099 4.3) > - Device reset with power-on state initialisation > - The two level-held interrupt outputs (8.2/8.3) > - Table 7-1 error recording injected on the invalid-configuration > write paths. > > * patch 34: the board wiring of the MSC interrupts to free SPIs on the > GIC, with the assigned GSIVs reported in the ACPI MPAM table. > > * patch 35: defaulting to a single node when none is specified. > > * patches 36-37: the query-mpam-cache QMP introspection, split into > schema+stub and implementation. > > With this, a Linux guest booted with -M virt,mpam=on (plus NUMA nodes) > mounts resctrl with the full schemata set (L2/L3 CAT, capacity, MBA, > priority), and MBM bandwidth and L2/L3 occupancy counters track the > configured controls. > > Verified with a Linux 7.3-rc6 guest on gic-v3: the arm_mpam driver probes > cleanly, the resctrl regression suite passes and no MPAM error interrupt > fires during normal boot. > > Patches 1, 5-8 and 36-37 carry Jonathan Cameron's authorship, being > derived from his RFC series and its QMP face. Compared to that RFC [1], > this series completes most of its TODO list and tracks what the > mainline arm_mpam driver now requires: > - Monitoring > - The MSC interrupts > - Error reporting on invalid parameters > - PARTID narrowing > - The missing CPU registers > - PPTT rev 3 with Cache IDs > - CMIN/CASSOC > - Memory-side > - CSU monitoring > > Still open from the RFC's TODO list: ACPI table unit tests. Like the > RFC, this is ACPI-only; the DT binding remains unimplemented. > > [1] https://patchew.org/QEMU/20230808115713.2613-1-Jonathan.Cameron@huawei.com/ > > Jinjie Ruan (30): > target/arm: Add MPAM2_EL2 and MPAMHCR_EL2 registers > target/arm: Add MPAMSM_EL1 register > hw/acpi: Build PPTT rev 3 with cache IDs for MPAM cross-references > hw/arm/mpam: Handle MPAMCFG_PART_SEL selection > hw/arm/mpam: Add per-RIS partition config storage > hw/arm/mpam: Implement CPOR cache portion partitioning > hw/arm/mpam: Implement MBW bandwidth partitioning > hw/arm/mpam: Implement PRI priority partitioning > hw/arm/mpam: Implement CCAP capacity partitioning > hw/arm/mpam: Add ECR/ESR and error MSI register storage > hw/arm/mpam: Implement PARTID narrowing > hw/arm/virt: Add mpam and mpam-min-msc machine properties > hw/arm/virt: Reserve the MPAM MSC MMIO window > hw/arm/mpam: Add node and interrupt GSIV properties > hw/arm/virt: Create memory-side MPAM MSCs > hw/arm/virt: Create L2/L3 cache MPAM MSCs > hw/acpi: Add MPAM table MSC node builders > hw/acpi: Build the MPAM ACPI table > hw/arm/mpam: Add MSMON monitor selection and window configuration > hw/arm/mpam: Emulate MBWU bandwidth monitoring > hw/arm/mpam: Implement CMIN/CASSOC storage > hw/arm/mpam: Advertise PRI on memory-side MSCs > hw/arm/mpam: Emulate CSU cache-usage monitoring > hw/arm/virt: Size the cache MSC occupancy model by level > hw/arm/mpam: Handle ECR/ESR as MSC-global registers > hw/arm/mpam: Add device reset and power-on state initialisation > hw/arm/mpam: Add error and overflow interrupt outputs > hw/arm/mpam: Record errors and reject invalid configuration writes > hw/arm/virt: Wire MSC error and overflow interrupts to the GIC > hw/arm/virt: Default to implicit node 0 for MPAM without -numa > > Jonathan Cameron (7): > target/arm: Add support for MPAM CPU registers > hw/arm/mpam: Add MPAM MSC register map > hw/arm/mpam: Add MPAM MSC base device > hw/arm/mpam: Add mpam-msc-mem memory-side MSC subtype > hw/arm/mpam: Add mpam-msc-cache MSC subtype > qapi: Add MPAM cache introspection schema and stub > hw/arm/mpam: Implement query-mpam-cache > > hw/acpi/aml-build.c | 31 +- > hw/arm/Kconfig | 4 + > hw/arm/meson.build | 3 + > hw/arm/mpam-qapi-stubs.c | 14 + > hw/arm/mpam-qapi.c | 62 ++ > hw/arm/mpam.c | 1620 +++++++++++++++++++++++++++++++++++ > hw/arm/virt-acpi-build.c | 231 +++++ > hw/arm/virt.c | 239 ++++++ > include/hw/acpi/aml-build.h | 8 + > include/hw/arm/mpam.h | 23 + > include/hw/arm/virt.h | 3 + > qapi/meson.build | 1 + > qapi/mpam.json | 120 +++ > qapi/qapi-schema.json | 1 + > target/arm/cpu-features.h | 11 + > target/arm/cpu.c | 10 +- > target/arm/cpu.h | 6 + > target/arm/helper.c | 33 + > 18 files changed, 2410 insertions(+), 10 deletions(-) > create mode 100644 hw/arm/mpam-qapi-stubs.c > create mode 100644 hw/arm/mpam-qapi.c > create mode 100644 hw/arm/mpam.c > create mode 100644 include/hw/arm/mpam.h > create mode 100644 qapi/mpam.json > -- Best regards, Jinjie ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 00/37] Add Arm MPAM emulation 2026-10-16 9:46 [PATCH 00/37] Add Arm MPAM emulation Jinjie Ruan 2026-10-16 10:20 ` Peter Maydell 2026-10-19 9:22 ` Jinjie Ruan @ 2026-10-20 22:43 ` Drew Fustini 2 siblings, 0 replies; 14+ messages in thread From: Drew Fustini @ 2026-10-20 22:43 UTC (permalink / raw) To: Jinjie Ruan Cc: anisinha, armbru, eblake, imammedo, michael.roth, mst, pbonzini, peter.maydell, qemu-devel, qemu-arm, ruanjinjie, daniel.barboza, alistair.francis, anup.patel, sunilvl On Fri, Oct 16, 2026 at 05:46:54PM +0800, Jinjie Ruan wrote: > From: Jinjie Ruan <ruanjinjie@huawei.com> > > MPAM (Memory System Resource Partitioning and Monitoring) is Arm's > architecture for partitioning and monitoring system memory resources > - Cache portions > - Capacity limits > - Memory bandwidth and priority > > And MPAM is the Arm counterpart of x86 RDT, which Linux exposes to user > space via resctrl. > > QEMU so far has no MPAM support at all: the CPU-side system registers > are missing (a guest kernel that probes FEAT_MPAM takes an UNDEFINED > exception), and there are no Memory System Components (MSCs), so a virt > guest cannot exercise resctrl on Arm. > > This series adds MPAM 1.1 emulation for the virt machine, > and the structure is as below: > > * target/arm (patches 1-3): the CPU registers: > - MPAMIDR_EL1 and MPAM0_EL1/MPAM1_EL1 > - MPAM2_EL2/MPAMHCR_EL2 > - MPAMSM_EL1 for FEAT_MPAM_SMS > > * hw/acpi (patch 4): PPTT revision 3 with Cache IDs, which the kernel > needs to resolve the MPAM table's cache references to PPTT cache > node. On Fri, Oct 16, 2026 at 05:46:54PM +0800, Jinjie Ruan wrote: > From: Jinjie Ruan <ruanjinjie@huawei.com> > > MPAM (Memory System Resource Partitioning and Monitoring) is Arm's > architecture for partitioning and monitoring system memory resources > - Cache portions > - Capacity limits > - Memory bandwidth and priority > > And MPAM is the Arm counterpart of x86 RDT, which Linux exposes to user > space via resctrl. > > QEMU so far has no MPAM support at all: the CPU-side system registers > are missing (a guest kernel that probes FEAT_MPAM takes an UNDEFINED > exception), and there are no Memory System Components (MSCs), so a virt > guest cannot exercise resctrl on Arm. > > This series adds MPAM 1.1 emulation for the virt machine, > and the structure is as below: > > * target/arm (patches 1-3): the CPU registers: > - MPAMIDR_EL1 and MPAM0_EL1/MPAM1_EL1 > - MPAM2_EL2/MPAMHCR_EL2 > - MPAMSM_EL1 for FEAT_MPAM_SMS > > * hw/acpi (patch 4): PPTT revision 3 with Cache IDs, which the kernel > needs to resolve the MPAM table's cache references to PPTT cache > node. Thanks for posting as this patch would also be useful for RISC-V. The QoS equivalent to MPAM is CBQRI [1], and I've been working on the Qemu implementation and Linux resctrl integration. The ACPI RQSC table [2] needs to reference PPTT cache IDs, too. Sunil recently posted a PPTT series [3] but this patch 04/37 would also be needed. Thanks, Drew [1] https://lore.kernel.org/qemu-devel/20260201-riscv-ssqosid-cbqri-v5-0-273ea4a21703@kernel.org/ [2] https://lore.kernel.org/qemu-devel/20260202-riscv-rqsc-v1-0-dcf448a3ed73@kernel.org/ [3] https://lore.kernel.org/qemu-devel/20261015064957.4076464-1-sunilvl@oss.qualcomm.com/ ^ permalink raw reply [flat|nested] 14+ messages in thread
end of thread, other threads:[~2026-11-09 7:43 UTC | newest] Thread overview: 14+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-10-16 9:46 [PATCH 00/37] Add Arm MPAM emulation Jinjie Ruan 2026-10-16 10:20 ` Peter Maydell 2026-10-19 1:57 ` Jinjie Ruan 2026-10-19 9:24 ` Peter Maydell 2026-10-19 11:46 ` Jinjie Ruan 2026-10-19 12:01 ` Peter Maydell 2026-10-19 12:19 ` Jinjie Ruan 2026-10-19 12:28 ` Peter Maydell 2026-10-20 0:39 ` Jonathan Cameron 2026-10-27 7:09 ` Jinjie Ruan 2026-11-06 23:14 ` Jonathan Cameron 2026-11-09 7:42 ` Jinjie Ruan 2026-10-19 9:22 ` Jinjie Ruan 2026-10-20 22:43 ` Drew Fustini
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.