All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 18/37] hw/arm/virt: Reserve the MPAM MSC MMIO window
       [not found] <20261016101058.1900808-1-2118537295@qq.com>
@ 2026-10-16 10:10 ` Jinjie Ruan
  2026-10-16 10:10 ` [PATCH 17/37] hw/arm/virt: Add mpam and mpam-min-msc machine properties Jinjie Ruan
                   ` (20 subsequent siblings)
  21 siblings, 0 replies; 24+ messages in thread
From: Jinjie Ruan @ 2026-10-16 10:10 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>

Reserve a 1 MiB MMIO window at 0x0b006000 for the MPAM MSCs
(MPAM_SIZE * 256 slots, right above the virtio MMIO transports)
and add the VIRT_MPAM_MSC memmap entry the creation code uses.

Signed-off-by: Jinjie Ruan <ruanjinjie@huawei.com>
---
 hw/arm/virt.c         | 3 +++
 include/hw/arm/virt.h | 1 +
 2 files changed, 4 insertions(+)

diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index aaadff6..3ad7de8 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -90,6 +90,7 @@
 #include "hw/virtio/virtio-md-pci.h"
 #include "hw/virtio/virtio-iommu.h"
 #include "hw/char/pl011.h"
+#include "hw/arm/mpam.h"
 #include "hw/core/cpu.h"
 #include "hw/cxl/cxl.h"
 #include "hw/cxl/cxl_host.h"
@@ -211,6 +212,8 @@ static const MemMapEntry base_memmap[] = {
     [VIRT_SECURE_GPIO] =        { 0x090b0000, 0x00001000 },
     [VIRT_ACPI_PCIHP] =         { 0x090c0000, ACPI_PCIHP_SIZE },
     [VIRT_MMIO] =               { 0x0a000000, 0x00000200 },
+    /* MPAM MSC MMIO windows: MPAM_SIZE (16 KiB) each, 256 slots (1 MiB). */
+    [VIRT_MPAM_MSC] =           { 0x0b006000, MPAM_SIZE * 256 },
     /* ...repeating for a total of NUM_VIRTIO_TRANSPORTS, each of that size */
     [VIRT_PLATFORM_BUS] =       { 0x0c000000, 0x02000000 },
     [VIRT_SECURE_MEM] =         { 0x0e000000, 0x01000000 },
diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h
index c847136..56bee15 100644
--- a/include/hw/arm/virt.h
+++ b/include/hw/arm/virt.h
@@ -81,6 +81,7 @@ enum {
     VIRT_SMMU,
     VIRT_UART0,
     VIRT_MMIO,
+    VIRT_MPAM_MSC,
     VIRT_RTC,
     VIRT_FW_CFG,
     VIRT_PCIE,
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH 17/37] hw/arm/virt: Add mpam and mpam-min-msc machine properties
       [not found] <20261016101058.1900808-1-2118537295@qq.com>
  2026-10-16 10:10 ` [PATCH 18/37] hw/arm/virt: Reserve the MPAM MSC MMIO window Jinjie Ruan
@ 2026-10-16 10:10 ` Jinjie Ruan
  2026-10-16 10:10 ` [PATCH 18/37] hw/arm/virt: Reserve the MPAM MSC MMIO window Jinjie Ruan
                   ` (19 subsequent siblings)
  21 siblings, 0 replies; 24+ messages in thread
From: Jinjie Ruan @ 2026-10-16 10:10 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>

Add the two MPAM machine properties: mpam=on enables MSC emulation
for the virt board, and mpam-min-msc=on collapses the per-node
memory MSCs into a single MSC with one RIS per node.  The state
fields and the property accessors only; MSC creation arrives with
the later patches.

Signed-off-by: Jinjie Ruan <ruanjinjie@huawei.com>
---
 hw/arm/virt.c         | 47 +++++++++++++++++++++++++++++++++++++++++++
 include/hw/arm/virt.h |  2 ++
 2 files changed, 49 insertions(+)

diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index 9d19251..aaadff6 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -2133,6 +2133,14 @@ static void create_virtio_iommu_dt_bindings(VirtMachineState *vms)
     }
 }
 
+/*
+ * Partitioning geometry for board-created MSCs: 256 request PARTIDs
+ * narrowed to 32 internal PARTIDs (enables HAS_PARTID_NRW, so resctrl
+ * reports num_closids=32).
+ */
+#define MPAM_VIRT_NUM_PARTID     256
+#define MPAM_VIRT_NUM_INTPARTID  32
+
 static void create_pcie(VirtMachineState *vms)
 {
     hwaddr base_mmio = vms->memmap[VIRT_PCIE_MMIO].base;
@@ -3498,6 +3506,34 @@ static void virt_set_virtio_transports(Object *obj, Visitor *v,
     vms->virtio_transports = transports;
 }
 
+static bool virt_get_mpam(Object *obj, Error **errp)
+{
+    VirtMachineState *vms = VIRT_MACHINE(obj);
+
+    return vms->mpam;
+}
+
+static void virt_set_mpam(Object *obj, bool value, Error **errp)
+{
+    VirtMachineState *vms = VIRT_MACHINE(obj);
+
+    vms->mpam = value;
+}
+
+static bool virt_get_mpam_min_msc(Object *obj, Error **errp)
+{
+    VirtMachineState *vms = VIRT_MACHINE(obj);
+
+    return vms->mpam_min_msc;
+}
+
+static void virt_set_mpam_min_msc(Object *obj, bool value, Error **errp)
+{
+    VirtMachineState *vms = VIRT_MACHINE(obj);
+
+    vms->mpam_min_msc = value;
+}
+
 static bool virt_get_dtb_randomness(Object *obj, Error **errp)
 {
     VirtMachineState *vms = VIRT_MACHINE(obj);
@@ -4303,6 +4339,17 @@ static void virt_machine_class_init(ObjectClass *oc, const void *data)
                                           "guest CPU which implements the ARM "
                                           "Memory Tagging Extension");
 
+    object_class_property_add_bool(oc, "mpam", virt_get_mpam, virt_set_mpam);
+    object_class_property_set_description(oc, "mpam",
+                                          "Set on/off to enable/disable "
+                                          "emulation of MPAM MSCs");
+
+    object_class_property_add_bool(oc, "mpam-min-msc", virt_get_mpam_min_msc,
+                                   virt_set_mpam_min_msc);
+    object_class_property_set_description(oc, "mpam-min-msc",
+                                          "Use RIS to reduce the number of "
+                                          "MSCs exposed");
+
     object_class_property_add_bool(oc, "its", virt_get_its,
                                    virt_set_its);
     object_class_property_set_description(oc, "its",
diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h
index 22e66d1..c847136 100644
--- a/include/hw/arm/virt.h
+++ b/include/hw/arm/virt.h
@@ -177,6 +177,8 @@ struct VirtMachineState {
     bool ras;
     bool mte;
     bool dtb_randomness;
+    bool mpam;
+    bool mpam_min_msc;
     bool second_ns_uart_present;
     OnOffAuto acpi;
     VirtGICType gic_version;
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH 18/37] hw/arm/virt: Reserve the MPAM MSC MMIO window
       [not found] <20261016101058.1900808-1-2118537295@qq.com>
  2026-10-16 10:10 ` [PATCH 18/37] hw/arm/virt: Reserve the MPAM MSC MMIO window Jinjie Ruan
  2026-10-16 10:10 ` [PATCH 17/37] hw/arm/virt: Add mpam and mpam-min-msc machine properties Jinjie Ruan
@ 2026-10-16 10:10 ` Jinjie Ruan
  2026-10-16 10:10 ` [PATCH 19/37] hw/arm/mpam: Add node and interrupt GSIV properties Jinjie Ruan
                   ` (18 subsequent siblings)
  21 siblings, 0 replies; 24+ messages in thread
From: Jinjie Ruan @ 2026-10-16 10:10 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>

Reserve a 1 MiB MMIO window at 0x0b006000 for the MPAM MSCs
(MPAM_SIZE * 256 slots, right above the virtio MMIO transports)
and add the VIRT_MPAM_MSC memmap entry the creation code uses.

Signed-off-by: Jinjie Ruan <ruanjinjie@huawei.com>
---
 hw/arm/virt.c         | 3 +++
 include/hw/arm/virt.h | 1 +
 2 files changed, 4 insertions(+)

diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index aaadff6..3ad7de8 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -90,6 +90,7 @@
 #include "hw/virtio/virtio-md-pci.h"
 #include "hw/virtio/virtio-iommu.h"
 #include "hw/char/pl011.h"
+#include "hw/arm/mpam.h"
 #include "hw/core/cpu.h"
 #include "hw/cxl/cxl.h"
 #include "hw/cxl/cxl_host.h"
@@ -211,6 +212,8 @@ static const MemMapEntry base_memmap[] = {
     [VIRT_SECURE_GPIO] =        { 0x090b0000, 0x00001000 },
     [VIRT_ACPI_PCIHP] =         { 0x090c0000, ACPI_PCIHP_SIZE },
     [VIRT_MMIO] =               { 0x0a000000, 0x00000200 },
+    /* MPAM MSC MMIO windows: MPAM_SIZE (16 KiB) each, 256 slots (1 MiB). */
+    [VIRT_MPAM_MSC] =           { 0x0b006000, MPAM_SIZE * 256 },
     /* ...repeating for a total of NUM_VIRTIO_TRANSPORTS, each of that size */
     [VIRT_PLATFORM_BUS] =       { 0x0c000000, 0x02000000 },
     [VIRT_SECURE_MEM] =         { 0x0e000000, 0x01000000 },
diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h
index c847136..56bee15 100644
--- a/include/hw/arm/virt.h
+++ b/include/hw/arm/virt.h
@@ -81,6 +81,7 @@ enum {
     VIRT_SMMU,
     VIRT_UART0,
     VIRT_MMIO,
+    VIRT_MPAM_MSC,
     VIRT_RTC,
     VIRT_FW_CFG,
     VIRT_PCIE,
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH 19/37] hw/arm/mpam: Add node and interrupt GSIV properties
       [not found] <20261016101058.1900808-1-2118537295@qq.com>
                   ` (2 preceding siblings ...)
  2026-10-16 10:10 ` [PATCH 18/37] hw/arm/virt: Reserve the MPAM MSC MMIO window Jinjie Ruan
@ 2026-10-16 10:10 ` Jinjie Ruan
  2026-10-16 10:10 ` [PATCH 20/37] hw/arm/virt: Create memory-side MPAM MSCs Jinjie Ruan
                   ` (17 subsequent siblings)
  21 siblings, 0 replies; 24+ messages in thread
From: Jinjie Ruan @ 2026-10-16 10:10 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>

Add the qdev properties the board and the ACPI table need: node
(the NUMA node a memory-side MSC serves, UINT16_MAX = unset) and
err-gsiv/oflow-gsiv (the wired interrupt GSIVs the board assigned;
zero means no wired interrupt and the ACPI MPAM table reports it
as not present).

Signed-off-by: Jinjie Ruan <ruanjinjie@huawei.com>
---
 hw/arm/mpam.c | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/hw/arm/mpam.c b/hw/arm/mpam.c
index 91d1c31..b484509 100644
--- a/hw/arm/mpam.c
+++ b/hw/arm/mpam.c
@@ -272,6 +272,19 @@ typedef struct MPAMMSCState {
     uint32_t num_int_partid;
     uint8_t num_ris;
 
+    /*
+     * Wired-interrupt GSIVs the board assigned (INTID = SPI + 32).  Zero
+     * means "no wired interrupt"; the ACPI MPAM table reports them as-is.
+     */
+    uint32_t err_gsiv;
+    uint32_t oflow_gsiv;
+
+    /*
+     * Memory-MSC locator: the NUMA node this instance serves.  UINT16_MAX
+     * means unset (single-MSC mode covers every memory node instead).
+     */
+    uint16_t node;
+
 } MPAMMSCState;
 
 /*
@@ -832,6 +845,9 @@ static const Property mpam_msc_props[] = {
     DEFINE_PROP_UINT8("num-ris", MPAMMSCState, num_ris, 1),
     DEFINE_PROP_UINT32("num-partid", MPAMMSCState, num_partid, 1),
     DEFINE_PROP_UINT32("num-int-partid", MPAMMSCState, num_int_partid, 0),
+    DEFINE_PROP_UINT32("err-gsiv", MPAMMSCState, err_gsiv, 0),
+    DEFINE_PROP_UINT32("oflow-gsiv", MPAMMSCState, oflow_gsiv, 0),
+    DEFINE_PROP_UINT16("node", MPAMMSCState, node, UINT16_MAX),
 };
 
 static const Property mpam_msc_cache_props[] = {
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH 20/37] hw/arm/virt: Create memory-side MPAM MSCs
       [not found] <20261016101058.1900808-1-2118537295@qq.com>
                   ` (3 preceding siblings ...)
  2026-10-16 10:10 ` [PATCH 19/37] hw/arm/mpam: Add node and interrupt GSIV properties Jinjie Ruan
@ 2026-10-16 10:10 ` Jinjie Ruan
  2026-10-16 10:10 ` [PATCH 21/37] hw/arm/virt: Create L2/L3 cache " Jinjie Ruan
                   ` (16 subsequent siblings)
  21 siblings, 0 replies; 24+ messages in thread
From: Jinjie Ruan @ 2026-10-16 10:10 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>

With mpam=on, create one mpam-msc-mem per NUMA node with memory
(256 request PARTIDs narrowed to 32 internal PARTIDs), or with
mpam-min-msc=on a single shared MSC with one RIS per memory node
and extreme narrowing (2 internal PARTIDs).  MSC creation requires
NUMA nodes to be specified.  Cache MSC creation arrives with the
next patch.

Based on an RFC series by Jonathan Cameron, adapted to the current
cache topology API (machine_get_cache_topo_level).

Signed-off-by: Jinjie Ruan <ruanjinjie@huawei.com>
---
 hw/arm/virt.c | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 61 insertions(+)

diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index 3ad7de8..77942e7 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -2144,6 +2144,63 @@ static void create_virtio_iommu_dt_bindings(VirtMachineState *vms)
 #define MPAM_VIRT_NUM_PARTID     256
 #define MPAM_VIRT_NUM_INTPARTID  32
 
+static void create_mpam_msc(VirtMachineState *vms, Error **errp)
+{
+    MachineState *ms = MACHINE(vms);
+    DeviceState *dev;
+    int i, count = 0;
+    hwaddr base = vms->memmap[VIRT_MPAM_MSC].base;
+
+    if (ms->numa_state->num_nodes == 0) {
+        error_setg(errp,
+                   "MPAM support requires NUMA nodes to be specified");
+        return;
+    }
+    if (!vms->mpam_min_msc) {
+        for (i = 0; i < ms->numa_state->num_nodes; i++) {
+            /* at most 16 mem MSCs (4-bit selector space, SPI budget) */
+            if (ms->numa_state->nodes[i].node_mem > 0 && count < 16) {
+                dev = qdev_new(TYPE_MPAM_MSC_MEM);
+
+                object_property_set_uint(OBJECT(dev), "node", i,
+                                         &error_fatal);
+                object_property_set_uint(OBJECT(dev), "num-ris", 1,
+                                         &error_fatal);
+                object_property_set_uint(OBJECT(dev), "num-partid",
+                                         MPAM_VIRT_NUM_PARTID,
+                                         &error_fatal);
+                object_property_set_uint(OBJECT(dev), "num-int-partid",
+                                         MPAM_VIRT_NUM_INTPARTID,
+                                         &error_fatal);
+                sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
+                sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base);
+                base += MPAM_SIZE;
+                count++;
+            }
+        }
+    } else {
+        int count_with_mem = 0;
+
+        for (i = 0; i < ms->numa_state->num_nodes; i++) {
+            if (ms->numa_state->nodes[i].node_mem) {
+                count_with_mem++;
+            }
+        }
+        dev = qdev_new(TYPE_MPAM_MSC_MEM);
+        object_property_set_uint(OBJECT(dev), "num-ris", count_with_mem,
+                                 &error_fatal);
+        object_property_set_uint(OBJECT(dev), "num-partid",
+                                 MPAM_VIRT_NUM_PARTID, &error_fatal);
+        object_property_set_uint(OBJECT(dev), "num-int-partid",
+                                 2, /* extreme narrowing */
+                                 &error_fatal);
+
+        sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
+        sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base);
+        base += MPAM_SIZE;
+    }
+}
+
 static void create_pcie(VirtMachineState *vms)
 {
     hwaddr base_mmio = vms->memmap[VIRT_PCIE_MMIO].base;
@@ -3216,6 +3273,10 @@ static void machvirt_init(MachineState *machine)
     create_pcie(vms);
     create_cxl_host_reg_region(vms);
 
+    if (vms->mpam) {
+        create_mpam_msc(vms, &error_fatal);
+    }
+
     if (aarch64 && firmware_loaded && virt_is_acpi_enabled(vms)) {
         vms->acpi_dev = create_acpi_ged(vms);
         vms->generic_error_notifier.notify = virt_generic_error_req;
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH 21/37] hw/arm/virt: Create L2/L3 cache MPAM MSCs
       [not found] <20261016101058.1900808-1-2118537295@qq.com>
                   ` (4 preceding siblings ...)
  2026-10-16 10:10 ` [PATCH 20/37] hw/arm/virt: Create memory-side MPAM MSCs Jinjie Ruan
@ 2026-10-16 10:10 ` Jinjie Ruan
  2026-10-16 10:10 ` [PATCH 22/37] hw/acpi: Add MPAM table MSC node builders Jinjie Ruan
                   ` (15 subsequent siblings)
  21 siblings, 0 replies; 24+ messages in thread
From: Jinjie Ruan @ 2026-10-16 10:10 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>

Derive the L2/L3 cache MSCs from the smp cache topology: the
sharing step comes from machine_get_cache_topo_level() so L3 MSCs
group per socket and L2 MSCs per cluster, and each MSC's cache id
(cpu | level << 16 | type << 24) matches the PPTT cache node the
MPAM table will point at.

Based on an RFC series by Jonathan Cameron, adapted to the current
cache topology API.

Signed-off-by: Jinjie Ruan <ruanjinjie@huawei.com>
---
 hw/arm/virt.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 57 insertions(+)

diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index 77942e7..a57ba56 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -2144,6 +2144,60 @@ static void create_virtio_iommu_dt_bindings(VirtMachineState *vms)
 #define MPAM_VIRT_NUM_PARTID     256
 #define MPAM_VIRT_NUM_INTPARTID  32
 
+static void create_mpam_msc_cache(VirtMachineState *vms, int level,
+                                  hwaddr *base)
+{
+    MachineState *ms = MACHINE(vms);
+    const CPUArchIdList *cpu_list = ms->possible_cpus;
+    DeviceState *dev;
+    int step, i;
+    CpuTopologyLevel topo;
+
+    /*
+     * Work out how many CPUs share a cache at this level, so each MSC
+     * covers exactly one cache, and the cache_id (derived from the
+     * first CPU of the group) matches the PPTT cache node the MPAM
+     * table points at.
+     */
+    topo = (level == 3) ?
+        machine_get_cache_topo_level(ms, CACHE_LEVEL_AND_TYPE_L3) :
+        machine_get_cache_topo_level(ms, CACHE_LEVEL_AND_TYPE_L2);
+
+    if (topo == CPU_TOPOLOGY_LEVEL_SOCKET && ms->smp.sockets > 0) {
+        step = cpu_list->len / ms->smp.sockets;
+    } else if (topo == CPU_TOPOLOGY_LEVEL_CLUSTER && ms->smp.clusters > 0) {
+        /*
+         * ms->smp.clusters is per socket; possible_cpus is ordered
+         * socket-major, so dividing by the total cluster count keeps
+         * each group inside one cluster.
+         */
+        step = cpu_list->len / (ms->smp.sockets * ms->smp.clusters);
+    } else {
+        step = ms->smp.threads;
+    }
+    if (step < 1) {
+        step = 1;
+    }
+
+    for (i = 0; i < cpu_list->len; i += step) {
+        dev = qdev_new(TYPE_MPAM_MSC_CACHE);
+        object_property_set_uint(OBJECT(dev), "num-ris", 1, &error_fatal);
+        object_property_set_uint(OBJECT(dev), "num-partid",
+                                 MPAM_VIRT_NUM_PARTID, &error_fatal);
+        object_property_set_uint(OBJECT(dev), "num-int-partid",
+                                 MPAM_VIRT_NUM_INTPARTID,
+                                 &error_fatal);
+        object_property_set_uint(OBJECT(dev), "cache-level", level,
+                                 &error_fatal);
+        object_property_set_uint(OBJECT(dev), "cache-type", UNIFIED_CACHE,
+                                 &error_fatal);
+        object_property_set_uint(OBJECT(dev), "cpu", i, &error_fatal);
+        sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
+        sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, *base);
+        *base += MPAM_SIZE;
+    }
+}
+
 static void create_mpam_msc(VirtMachineState *vms, Error **errp)
 {
     MachineState *ms = MACHINE(vms);
@@ -2199,6 +2253,9 @@ static void create_mpam_msc(VirtMachineState *vms, Error **errp)
         sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base);
         base += MPAM_SIZE;
     }
+
+    create_mpam_msc_cache(vms, 3, &base);
+    create_mpam_msc_cache(vms, 2, &base);
 }
 
 static void create_pcie(VirtMachineState *vms)
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH 22/37] hw/acpi: Add MPAM table MSC node builders
       [not found] <20261016101058.1900808-1-2118537295@qq.com>
                   ` (5 preceding siblings ...)
  2026-10-16 10:10 ` [PATCH 21/37] hw/arm/virt: Create L2/L3 cache " Jinjie Ruan
@ 2026-10-16 10:10 ` Jinjie Ruan
  2026-10-16 10:10 ` [PATCH 23/37] hw/acpi: Build the MPAM ACPI table Jinjie Ruan
                   ` (14 subsequent siblings)
  21 siblings, 0 replies; 24+ messages in thread
From: Jinjie Ruan @ 2026-10-16 10:10 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>

Add the DEN0065 MSC node builders for the MPAM table: the
interrupt entry (flags 0 = wired, level per Table 5; GSIV 0 means
not present) and the memory-controller and cache-controller node
layouts (fixed 72-byte header plus one 24-byte resource node per
RIS, MAX_NRDY_USEC 100) with the matching locators (proximity
domain for memory, PPTT cache id for caches).

Static helpers; wired into the generated table by the next patch.

Signed-off-by: Jinjie Ruan <ruanjinjie@huawei.com>
---
 hw/arm/virt-acpi-build.c | 130 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 130 insertions(+)

diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c
index d368913..871ee45 100644
--- a/hw/arm/virt-acpi-build.c
+++ b/hw/arm/virt-acpi-build.c
@@ -52,6 +52,7 @@
 #include "hw/pci/pci_bus.h"
 #include "hw/pci-host/gpex.h"
 #include "hw/arm/virt.h"
+#include "hw/arm/mpam.h"
 #include "hw/intc/arm_gicv3_its_common.h"
 #include "hw/mem/nvdimm.h"
 #include "hw/core/platform-bus.h"
@@ -855,6 +856,135 @@ build_srat(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
     acpi_table_end(linker, &table);
 }
 
+/*
+ * DEN0065 3.0 Table 5: interrupt flags bit0 = mode (0 level, 1 edge),
+ * bits[2:1] = type (00b wired), bit4 = affinity valid (must be 0 for an
+ * SPI).  The MSC model signals both interrupts level, so flags are 0.
+ * GSIV 0 means the interrupt is not present.
+ */
+/*
+ * DEN0065 4.0: an MSC node is a 72-byte fixed header plus one 24-byte
+ * resource node per RIS.  MAX_NRDY_USEC 100 is the default ready-time.
+ */
+#define MPAM_MSC_NODE_HDR_LEN   72
+#define MPAM_MSC_RIS_ENTRY_LEN   24
+#define MPAM_MSC_MAX_NRDY_USEC  100
+
+static void build_msc_interrupt(GArray *table_data, uint32_t gsiv)
+{
+    build_append_int_noprefix(table_data, gsiv, 4);
+    build_append_int_noprefix(table_data, 0 /* wired, level */, 4);
+}
+
+static void build_msc_memory_controller(GArray *table_data, int identifier,
+                                        hwaddr base_addr, uint32_t mpam_id,
+                                        int num_ris, uint64_t *nodes,
+                                        uint32_t oflow_gsiv,
+                                        uint32_t err_gsiv)
+{
+    int length = MPAM_MSC_NODE_HDR_LEN + MPAM_MSC_RIS_ENTRY_LEN * num_ris;
+    int i;
+
+    build_append_int_noprefix(table_data, length, 2);
+    /* Interface Type */
+    build_append_int_noprefix(table_data, 0 /* MMIO */, 1);
+    /* Reserved */
+    build_append_int_noprefix(table_data, 0, 1);
+    build_append_int_noprefix(table_data, identifier, 4);
+    build_append_int_noprefix(table_data, base_addr, 8);
+    build_append_int_noprefix(table_data, MPAM_SIZE, 4);
+    /* Overflow interrupt */
+    build_msc_interrupt(table_data, oflow_gsiv);
+    /* Reserved */
+    build_append_int_noprefix(table_data, 0, 4);
+    /* Overflow Int Affinity */
+    build_append_int_noprefix(table_data, 0, 4);
+    /* Error interrupt */
+    build_msc_interrupt(table_data, err_gsiv);
+    /* More reserved */
+    build_append_int_noprefix(table_data, 0, 4);
+    /* Error Int Affinity */
+    build_append_int_noprefix(table_data, 0, 4);
+    /* MAX_NRDY_USEC */
+    build_append_int_noprefix(table_data, MPAM_MSC_MAX_NRDY_USEC, 4);
+    /* Linked Device - none for now */
+    build_append_int_noprefix(table_data, 0, 8);
+    /* _UID of linked device */
+    build_append_int_noprefix(table_data, 0, 4);
+    build_append_int_noprefix(table_data, num_ris, 4);
+    /* Build a memory controller resource */
+    for (i = 0; i < num_ris; i++) {
+        build_append_int_noprefix(table_data, mpam_id, 4);
+        build_append_int_noprefix(table_data, i, 1);
+        /* Reserved1 */
+        build_append_int_noprefix(table_data, 0, 2);
+        /* Locator type - 1 = Memory */
+        build_append_int_noprefix(table_data, 1, 1);
+        /* Locator part 1 Node */
+        build_append_int_noprefix(table_data, nodes[i], 8);
+        /* Locator part 2 reserved */
+        build_append_int_noprefix(table_data, 0, 4);
+        /* Num functional dependencies */
+        build_append_int_noprefix(table_data, 0, 4);
+    }
+}
+
+static void build_msc_cache_controller(GArray *table_data, int identifier,
+                                       hwaddr base_addr, uint32_t mpam_id,
+                                       int num_ris, uint64_t *cache_id,
+                                       uint32_t oflow_gsiv,
+                                       uint32_t err_gsiv)
+{
+    int length = MPAM_MSC_NODE_HDR_LEN + MPAM_MSC_RIS_ENTRY_LEN * num_ris;
+    int i;
+
+    build_append_int_noprefix(table_data, length, 2);
+    /* Interface Type */
+    build_append_int_noprefix(table_data, 0 /* MMIO */, 1);
+    /* Reserved */
+    build_append_int_noprefix(table_data, 0, 1);
+    build_append_int_noprefix(table_data, identifier, 4);
+    build_append_int_noprefix(table_data, base_addr, 8);
+    build_append_int_noprefix(table_data, MPAM_SIZE, 4);
+    /* Overflow interrupt */
+    build_msc_interrupt(table_data, oflow_gsiv);
+    /* Reserved */
+    build_append_int_noprefix(table_data, 0, 4);
+    /* Overflow Int Affinity */
+    build_append_int_noprefix(table_data, 0, 4);
+    /* Error interrupt */
+    build_msc_interrupt(table_data, err_gsiv);
+    /* More reserved */
+    build_append_int_noprefix(table_data, 0, 4);
+    /* Error Int Affinity */
+    build_append_int_noprefix(table_data, 0, 4);
+    /* MAX_NRDY_USEC */
+    build_append_int_noprefix(table_data, MPAM_MSC_MAX_NRDY_USEC, 4);
+    /* Linked Device - none for now */
+    build_append_int_noprefix(table_data, 0, 8);
+    /* _UID of linked device */
+    build_append_int_noprefix(table_data, 0, 4);
+    /* Num resource nodes */
+    build_append_int_noprefix(table_data, num_ris, 4);
+    /* Build a cache resource */
+    for (i = 0; i < num_ris; i++) {
+        /* Identifier */
+        build_append_int_noprefix(table_data, mpam_id, 4);
+        /* RIS index */
+        build_append_int_noprefix(table_data, i, 1);
+        /* Reserved1 */
+        build_append_int_noprefix(table_data, 0, 2);
+        /* Locator type - 0 = Cache */
+        build_append_int_noprefix(table_data, 0, 1);
+        /* Locator part 1 PPTT ID */
+        build_append_int_noprefix(table_data, cache_id[i], 8);
+        /* Locator part 2 reserved */
+        build_append_int_noprefix(table_data, 0, 4);
+        /* Num functional dependencies */
+        build_append_int_noprefix(table_data, 0, 4);
+    }
+}
+
 /*
  * ACPI spec, Revision 6.5
  * 5.2.25 Generic Timer Description Table (GTDT)
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH 23/37] hw/acpi: Build the MPAM ACPI table
       [not found] <20261016101058.1900808-1-2118537295@qq.com>
                   ` (6 preceding siblings ...)
  2026-10-16 10:10 ` [PATCH 22/37] hw/acpi: Add MPAM table MSC node builders Jinjie Ruan
@ 2026-10-16 10:10 ` Jinjie Ruan
  2026-10-16 10:10 ` [PATCH 24/37] hw/arm/mpam: Add MSMON monitor selection and window configuration Jinjie Ruan
                   ` (13 subsequent siblings)
  21 siblings, 0 replies; 24+ messages in thread
From: Jinjie Ruan @ 2026-10-16 10:10 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>

Assemble the MPAM table when vms->mpam is set: walk the QOM tree
for the board-created MSCs and emit each as a memory-controller or
cache-controller node with its MMIO base, wired interrupt GSIVs
and per-RIS locators -- per-node instances scope their locator to
their own NUMA node, single-MSC instances span every memory node,
and cache nodes carry the PPTT-referencing cache id.

Signed-off-by: Jinjie Ruan <ruanjinjie@huawei.com>
---
 hw/arm/virt-acpi-build.c | 101 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 101 insertions(+)

diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c
index 871ee45..3b71af9 100644
--- a/hw/arm/virt-acpi-build.c
+++ b/hw/arm/virt-acpi-build.c
@@ -985,6 +985,102 @@ static void build_msc_cache_controller(GArray *table_data, int identifier,
     }
 }
 
+struct mpam_stat {
+    MachineState *ms;
+    GArray *table_data;
+    hwaddr base_addr;
+    int count;
+    int cpu_count;
+    uint32_t mpam_id; /* Just needs to be unique */
+};
+
+static int mpam_add_msc(Object *obj, void *opaque)
+{
+    if (object_dynamic_cast(obj, TYPE_MPAM_MSC_MEM)) {
+        struct mpam_stat *mpam_s = opaque;
+        SysBusDevice *s = SYS_BUS_DEVICE(obj);
+        int num_ris = object_property_get_uint(obj, "num-ris", &error_fatal);
+        uint32_t oflow_gsiv = object_property_get_uint(obj, "oflow-gsiv",
+                                                       &error_fatal);
+        uint32_t err_gsiv = object_property_get_uint(obj, "err-gsiv",
+                                                     &error_fatal);
+        uint64_t *ids = g_new0(uint64_t, num_ris);
+        int j = 0;
+        int i;
+
+        /*
+         * Locator for each RIS.  A per-node instance (node property set)
+         * covers exactly its own NUMA node; a single-MSC instance spans
+         * every memory-bearing node, one RIS each.
+         */
+        if (object_property_get_uint(obj, "node", &error_fatal) != UINT16_MAX) {
+            ids[j++] = object_property_get_uint(obj, "node", &error_fatal);
+        }
+        for (i = 0; i < mpam_s->ms->numa_state->num_nodes; i++) {
+            if (j >= num_ris) {
+                break;
+            }
+            if (mpam_s->ms->numa_state->nodes[i].node_mem) {
+                ids[j++] = i;
+            }
+        }
+
+        build_msc_memory_controller(mpam_s->table_data, mpam_s->count,
+                                    s->mmio[0].addr, mpam_s->mpam_id++,
+                                    num_ris, ids, oflow_gsiv, err_gsiv);
+        mpam_s->count++;
+        g_free(ids);
+    }
+
+    if (object_dynamic_cast(obj, TYPE_MPAM_MSC_CACHE)) {
+        struct mpam_stat *mpam_s = opaque;
+        SysBusDevice *s = SYS_BUS_DEVICE(obj);
+        int num_ris = 1;
+        uint64_t *ids = g_new0(uint64_t, num_ris);
+        uint8_t cache_level = object_property_get_uint(obj, "cache-level",
+                                                       &error_fatal);
+        uint8_t cache_type = object_property_get_uint(obj, "cache-type",
+                                                      &error_fatal);
+        uint16_t cpu = object_property_get_uint(obj, "cpu", &error_fatal);
+        uint32_t oflow_gsiv = object_property_get_uint(obj, "oflow-gsiv",
+                                                       &error_fatal);
+        uint32_t err_gsiv = object_property_get_uint(obj, "err-gsiv",
+                                                     &error_fatal);
+
+        ids[0] = PPTT_CACHE_ID(cache_type, cache_level, cpu);
+        build_msc_cache_controller(mpam_s->table_data, mpam_s->count,
+                                   s->mmio[0].addr, mpam_s->mpam_id++,
+                                   num_ris, ids, oflow_gsiv, err_gsiv);
+        mpam_s->count++;
+        g_free(ids);
+    }
+
+    return 0;
+}
+
+static void
+build_mpam(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
+{
+    AcpiTable table = {
+        .sig = "MPAM",
+        .rev = 1,
+        .oem_id = vms->oem_id,
+        .oem_table_id = vms->oem_table_id,
+    };
+    struct mpam_stat mpam_s = {
+        .ms = MACHINE(vms),
+        .count = 0,
+        .base_addr = vms->memmap[VIRT_MPAM_MSC].base,
+        .table_data = table_data,
+    };
+
+    acpi_table_begin(&table, table_data);
+
+    object_child_foreach_recursive(object_get_root(), mpam_add_msc, &mpam_s);
+
+    acpi_table_end(linker, &table);
+}
+
 /*
  * ACPI spec, Revision 6.5
  * 5.2.25 Generic Timer Description Table (GTDT)
@@ -1517,6 +1613,11 @@ void virt_acpi_build(VirtMachineState *vms, AcpiBuildTables *tables)
     acpi_add_table(table_offsets, tables_blob);
     build_gtdt(tables_blob, tables->linker, vms, wdt && !has_wdat);
 
+    if (vms->mpam) {
+        acpi_add_table(table_offsets, tables_blob);
+        build_mpam(tables_blob, tables->linker, vms);
+    }
+
     acpi_add_table(table_offsets, tables_blob);
     {
         AcpiMcfgInfo mcfg = {
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH 24/37] hw/arm/mpam: Add MSMON monitor selection and window configuration
       [not found] <20261016101058.1900808-1-2118537295@qq.com>
                   ` (7 preceding siblings ...)
  2026-10-16 10:10 ` [PATCH 23/37] hw/acpi: Build the MPAM ACPI table Jinjie Ruan
@ 2026-10-16 10:10 ` Jinjie Ruan
  2026-10-16 10:10 ` [PATCH 25/37] hw/arm/mpam: Emulate MBWU bandwidth monitoring Jinjie Ruan
                   ` (12 subsequent siblings)
  21 siblings, 0 replies; 24+ messages in thread
From: Jinjie Ruan @ 2026-10-16 10:10 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>

Add the monitor-side plumbing shared by the monitor types: the
MSMON register definitions (MON_SEL, MBWU_FLT/CTL), the per-RIS
monitor instance array with the num-mbwu-mon and bw-nominal-bps
properties, and the read/write handling of the selection and
window registers (monitor/RIS selection with range clamping,
filter and control storage, write-one-to-clear overflow status).

The monitor selection resolves a (RIS, monitor) pair through
mpam_mon_ris()/mpam_mbwu_mon(), and mpam_nrw_id_for() maps a
filter PARTID onto the internal PARTID whose configuration
governs it.  No monitor type is advertised yet; the MBWU counter
model arrives with the next patch.

Signed-off-by: Jinjie Ruan <ruanjinjie@huawei.com>
---
 hw/arm/mpam.c | 134 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 134 insertions(+)

diff --git a/hw/arm/mpam.c b/hw/arm/mpam.c
index b484509..77e0277 100644
--- a/hw/arm/mpam.c
+++ b/hw/arm/mpam.c
@@ -109,6 +109,36 @@ REG32(MPAMF_MBWUMON_IDR, 0x0090)
     FIELD(MPAMF_MBWUMON_IDR, HAS_LONG, 30, 1)
     FIELD(MPAMF_MBWUMON_IDR, HAS_CAPTURE, 31, 1)
 
+REG32(MSMON_CFG_MON_SEL, 0x0800)
+    FIELD(MSMON_CFG_MON_SEL, MON_SEL, 0, 8)
+    FIELD(MSMON_CFG_MON_SEL, RIS, 24, 4)
+
+REG32(MSMON_CFG_MBWU_FLT, 0x0820)
+    FIELD(MSMON_CFG_MBWU_FLT, PARTID, 0, 16)
+    FIELD(MSMON_CFG_MBWU_FLT, PMG, 16, 8)
+    FIELD(MSMON_CFG_MBWU_FLT, RWBW, 30, 2)
+
+REG32(MSMON_CFG_MBWU_CTL, 0x0828)
+    FIELD(MSMON_CFG_MBWU_CTL, TYPE, 0, 8)
+    FIELD(MSMON_CFG_MBWU_CTL, MATCH_PARTID, 16, 1)
+    FIELD(MSMON_CFG_MBWU_CTL, MATCH_PMG, 17, 1)
+    FIELD(MSMON_CFG_MBWU_CTL, SCLEN, 19, 1)
+    FIELD(MSMON_CFG_MBWU_CTL, SUBTYPE, 20, 4)
+    FIELD(MSMON_CFG_MBWU_CTL, OFLOW_FRZ, 24, 1)
+    FIELD(MSMON_CFG_MBWU_CTL, OFLOW_INTR, 25, 1)
+    FIELD(MSMON_CFG_MBWU_CTL, OFLOW_STATUS, 26, 1)
+    FIELD(MSMON_CFG_MBWU_CTL, CAPT_RESET, 27, 1)
+    FIELD(MSMON_CFG_MBWU_CTL, CAPT_EVNT, 28, 3)
+    FIELD(MSMON_CFG_MBWU_CTL, EN, 31, 1)
+
+REG32(MSMON_MBWU, 0x0860)           /* 31-bit counter, bit31 NRDY */
+REG32(MSMON_MBWU_L, 0x0880)         /* 64-bit counter */
+REG32(MSMON_MBWU_CAPTURE, 0x0868)
+REG32(MSMON_MBWU_CAPTURE_L, 0x0890)
+REG32(MSMON_CAPT_EVNT, 0x0808)
+REG32(MSMON_CFG_CSU_FLT, 0x0810)
+REG32(MSMON_CFG_CSU_CTL, 0x0818)
+
 REG32(MPAMF_ERR_MSI_MPAM, 0x00dc)
 REG32(MPAMF_ERR_MSI_ADDR_L, 0x00e0)
 REG32(MPAMF_ERR_MSI_ADDR_H, 0x00e4)
@@ -232,6 +262,21 @@ typedef struct MpamfPerNrwId {
         uint32_t cfg_mbw_prop;
 } MpamfPerNrwId;
 
+/*
+ * One MBWU monitor instance.  The counter is lazily advanced on
+ * access: value = base + rate x (now - t0), where rate is the
+ * nominal bandwidth scaled by the MBW_MAX fraction of the filtered
+ * PARTID.  Monitoring therefore reads back values that track the
+ * configured bandwidth control.
+ */
+typedef struct MpamMbwuMon {
+    uint32_t flt;
+    uint32_t ctl;
+    uint64_t base;
+    int64_t t0;
+    bool oflow;
+} MpamMbwuMon;
+
 typedef struct Mpamf {
     uint64_t idr;
     uint32_t iidr;
@@ -257,6 +302,8 @@ typedef struct Mpamf {
 
     MpamfPerNrwId *per_nrw_id;
 
+    MpamMbwuMon *mbwu_mons;
+
 } Mpamf;
 
 typedef struct MPAMMSCState {
@@ -267,10 +314,13 @@ typedef struct MPAMMSCState {
     uint8_t ris;
     uint16_t part_sel; /* Technically per ris, but in same reg */
     bool internal_part_sel;
+    uint32_t mon_sel;
     struct MemoryRegion mr;
     uint32_t num_partid;
     uint32_t num_int_partid;
     uint8_t num_ris;
+    uint32_t num_mbwu_mon;
+    uint64_t bw_nominal_bps;
 
     /*
      * Wired-interrupt GSIVs the board assigned (INTID = SPI + 32).  Zero
@@ -312,6 +362,48 @@ static uint32_t mpam_get_nrw_id(MPAMMSCState *s)
     return intpartid % s->num_int_partid;
 }
 
+/*
+ * Resolve a request PARTID from a monitor filter onto the internal
+ * PARTID whose configuration governs it.
+ */
+static uint32_t mpam_nrw_id_for(MPAMMSCState *s, Mpamf *mf, uint32_t partid)
+{
+    uint32_t intpartid;
+
+    if (!FIELD_EX32(mf->idr, MPAMF_IDR, HAS_PARTID_NRW)) {
+        return partid;
+    }
+
+    intpartid = mf->cfg_intpartid[partid % s->num_partid];
+    return intpartid % s->num_int_partid;
+}
+
+static Mpamf *mpam_mon_ris(MPAMMSCState *s)
+{
+    unsigned ris = FIELD_EX32(s->mon_sel, MSMON_CFG_MON_SEL, RIS);
+
+    if (ris >= s->num_ris) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "MPAM: MON_SEL RIS %d out of range (num-ris %d)\n",
+                      ris, s->num_ris);
+        ris = 0;
+    }
+    return &s->mpamf[ris];
+}
+
+static MpamMbwuMon *mpam_mbwu_mon(MPAMMSCState *s, Mpamf *mf)
+{
+    unsigned mon = FIELD_EX32(s->mon_sel, MSMON_CFG_MON_SEL, MON_SEL);
+
+    if (mon >= s->num_mbwu_mon) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "MPAM: MON_SEL %d out of range (num-mbwu-mon %d)\n",
+                      mon, s->num_mbwu_mon);
+        mon = 0;
+    }
+    return &mf->mbwu_mons[mon];
+}
+
 typedef struct MPAMMSCMemState {
     MPAMMSCState parent;
 } MPAMMSCMemState;
@@ -405,6 +497,20 @@ static uint64_t mpam_msc_read_reg(void *opaque, hwaddr offset,
         return mpamf->csumon_idr;
     case A_MPAMF_MBWUMON_IDR:
         return mpamf->mbwumon_idr;
+    case A_MSMON_CFG_MON_SEL:
+        return s->mon_sel;
+    case A_MSMON_CFG_MBWU_FLT:
+        return mpam_mbwu_mon(s, mpam_mon_ris(s))->flt;
+    case A_MSMON_CFG_MBWU_CTL:
+    {
+        MpamMbwuMon *m = mpam_mbwu_mon(s, mpam_mon_ris(s));
+        uint32_t ctl = m->ctl;
+
+        if (m->oflow) {
+            ctl |= R_MSMON_CFG_MBWU_CTL_OFLOW_STATUS_MASK;
+        }
+        return ctl;
+    }
     case A_MPAMF_ERR_MSI_MPAM:
         return mpamf->err_msi_mpam;
     case A_MPAMF_ERR_MSI_ADDR_L:
@@ -623,6 +729,30 @@ static void mpam_msc_write_reg(void *opaque, hwaddr offset, uint64_t value,
         mpamf->per_nrw_id[nrw_part_sel].cfg_mbw_pbm[array_offset] = value;
         return;
     }
+    case A_MSMON_CFG_MON_SEL:
+        s->mon_sel = value & (R_MSMON_CFG_MON_SEL_MON_SEL_MASK |
+                              R_MSMON_CFG_MON_SEL_RIS_MASK);
+        return;
+    case A_MSMON_CFG_MBWU_FLT:
+        mpam_mbwu_mon(s, mpam_mon_ris(s))->flt = value;
+        return;
+    case A_MSMON_CFG_MBWU_CTL:
+    {
+        MpamMbwuMon *m = mpam_mbwu_mon(s, mpam_mon_ris(s));
+
+        m->ctl = value;
+        if (value & R_MSMON_CFG_MBWU_CTL_OFLOW_STATUS_MASK) {
+            m->oflow = false;               /* write-one-to-clear */
+        }
+        return;
+    }
+    case A_MSMON_CAPT_EVNT:
+    case A_MSMON_CFG_CSU_FLT:
+    case A_MSMON_CFG_CSU_CTL:
+        qemu_log_mask(LOG_UNIMP,
+                      "MPAM: cache-usage monitoring not implemented (write %lx)\n",
+                      offset);
+        return;
     case A_MPAMF_ECR:
         if (value & ~MPAMF_ECR_WRITE_MASK) {
             qemu_log_mask(LOG_UNIMP,
@@ -677,6 +807,7 @@ static void mpam_msc_realize(DeviceState *dev, Error **errp)
 
         s->mpamf[i].per_nrw_id = g_new0(MpamfPerNrwId, s->num_int_partid);
         s->mpamf[i].cfg_intpartid = g_new0(uint32_t, s->num_partid);
+        s->mpamf[i].mbwu_mons = g_new0(MpamMbwuMon, s->num_mbwu_mon);
         /*
          * Until resctrl programs the map, wrap each request PARTID onto an
          * internal PARTID so the narrowing indirection is always in range.
@@ -845,6 +976,9 @@ static const Property mpam_msc_props[] = {
     DEFINE_PROP_UINT8("num-ris", MPAMMSCState, num_ris, 1),
     DEFINE_PROP_UINT32("num-partid", MPAMMSCState, num_partid, 1),
     DEFINE_PROP_UINT32("num-int-partid", MPAMMSCState, num_int_partid, 0),
+    DEFINE_PROP_UINT32("num-mbwu-mon", MPAMMSCState, num_mbwu_mon, 8),
+    DEFINE_PROP_UINT64("bw-nominal-bps", MPAMMSCState, bw_nominal_bps,
+                       10000000000ULL),  /* 10 GB/s default bandwidth */
     DEFINE_PROP_UINT32("err-gsiv", MPAMMSCState, err_gsiv, 0),
     DEFINE_PROP_UINT32("oflow-gsiv", MPAMMSCState, oflow_gsiv, 0),
     DEFINE_PROP_UINT16("node", MPAMMSCState, node, UINT16_MAX),
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH 25/37] hw/arm/mpam: Emulate MBWU bandwidth monitoring
       [not found] <20261016101058.1900808-1-2118537295@qq.com>
                   ` (8 preceding siblings ...)
  2026-10-16 10:10 ` [PATCH 24/37] hw/arm/mpam: Add MSMON monitor selection and window configuration Jinjie Ruan
@ 2026-10-16 10:10 ` Jinjie Ruan
  2026-10-16 10:10 ` [PATCH 26/37] hw/arm/mpam: Implement CMIN/CASSOC storage Jinjie Ruan
                   ` (11 subsequent siblings)
  21 siblings, 0 replies; 24+ messages in thread
From: Jinjie Ruan @ 2026-10-16 10:10 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>

Add the MBWU monitor: per monitor a lazily accumulated 64-bit
counter (value = base + rate x elapsed), where the rate is the
nominal bandwidth scaled by the bandwidth fraction of the filtered
PARTID.  The fraction follows whichever control the guest drives:
MBW_MAX, or the MBW_PBM bitmap (popcount over its width) that the
openEuler MBA resource programs instead, so monitor readings track
the configured bandwidth control.  The counter wraps at 48 bits,
latching the overflow status, and writing any counter register
resets it.

MSMON_IDR/MBWUMON_IDR now advertise the feature (num-mbwu-mon
monitors, 64-bit counters with read/write bandwidth) and
MPAMF_IDR reports HAS_MSMON on both subtypes, which lets resctrl
mount the MBM counters.

Signed-off-by: Jinjie Ruan <ruanjinjie@huawei.com>
---
 hw/arm/mpam.c | 129 +++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 127 insertions(+), 2 deletions(-)

diff --git a/hw/arm/mpam.c b/hw/arm/mpam.c
index 77e0277..610ae88 100644
--- a/hw/arm/mpam.c
+++ b/hw/arm/mpam.c
@@ -8,6 +8,9 @@
 #include "qemu/osdep.h"
 #include "qapi/error.h"
 #include "qemu/log.h"
+#include "qemu/timer.h"
+#include "qemu/host-utils.h"
+#include "qemu/bitops.h"
 #include "hw/core/irq.h"
 #include "hw/core/qdev-properties.h"
 #include "hw/core/registerfields.h"
@@ -404,6 +407,65 @@ static MpamMbwuMon *mpam_mbwu_mon(MPAMMSCState *s, Mpamf *mf)
     return &mf->mbwu_mons[mon];
 }
 
+/*
+ * Lazily advance the monitor's counter.  While enabled it accumulates
+ * the nominal bandwidth scaled by the MBW_MAX fraction of the PARTID
+ * the filter selects, so monitor readings track the configured
+ * bandwidth control.
+ */
+static uint64_t mpam_mbwu_value(MPAMMSCState *s, Mpamf *mf, MpamMbwuMon *m)
+{
+    uint64_t v = m->base;
+    int64_t now = qemu_clock_get_ns(QEMU_CLOCK_HOST);
+    int64_t delta;
+
+    if (m->t0 == 0) {
+        m->t0 = now;
+    }
+    delta = now - m->t0;
+
+    if (FIELD_EX32(m->ctl, MSMON_CFG_MBWU_CTL, EN) && delta > 0) {
+        /* bw_nominal_bps is bytes/s; MBWU is modelled in bytes. */
+        uint64_t rate_mbs = s->bw_nominal_bps / 1000000;
+
+        if (FIELD_EX32(m->ctl, MSMON_CFG_MBWU_CTL, MATCH_PARTID)) {
+            uint32_t partid = FIELD_EX32(m->flt, MSMON_CFG_MBWU_FLT,
+                                         PARTID);
+            uint32_t nrw = mpam_nrw_id_for(s, mf, partid);
+            MpamfPerNrwId *cfg = &mf->per_nrw_id[nrw];
+            uint64_t frac_num, frac_den;
+
+            /*
+             * The kernel drives bandwidth through either MBW_MAX or,
+             * on classes that advertise MBW_PBM, the PBM bitmap
+             * (popcount of the programmed bits over its width).
+             */
+            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);
+        }
+
+        /* rate_mbs is MB/s; delta is ns, so /1000 yields bytes. */
+        v += muldiv64(delta, rate_mbs, 1000);
+        /* 48-bit counter wraps (MSMON_MBWU_L is 48 bits of value). */
+        if (v > (1ULL << 48)) {
+            m->oflow = true;
+            v &= (1ULL << 48) - 1;
+        }
+    }
+    m->base = v;
+    m->t0 = now;
+
+    return v;
+}
+
 typedef struct MPAMMSCMemState {
     MPAMMSCState parent;
 } MPAMMSCMemState;
@@ -511,6 +573,34 @@ static uint64_t mpam_msc_read_reg(void *opaque, hwaddr offset,
         }
         return ctl;
     }
+    case A_MSMON_MBWU ... (A_MSMON_MBWU + 3):
+    {
+        uint64_t v = mpam_mbwu_value(s, mpam_mon_ris(s),
+                                     mpam_mbwu_mon(s, mpam_mon_ris(s)));
+
+        return extract64(v, (offset - A_MSMON_MBWU) * 8, 32);
+    }
+    case A_MSMON_MBWU_L ... (A_MSMON_MBWU_L + 7):
+    {
+        uint64_t v = mpam_mbwu_value(s, mpam_mon_ris(s),
+                                     mpam_mbwu_mon(s, mpam_mon_ris(s)));
+
+        return extract64(v, (offset - A_MSMON_MBWU_L) * 8, 32);
+    }
+    case A_MSMON_MBWU_CAPTURE ... (A_MSMON_MBWU_CAPTURE + 3):
+    {
+        uint64_t v = mpam_mbwu_value(s, mpam_mon_ris(s),
+                                     mpam_mbwu_mon(s, mpam_mon_ris(s)));
+
+        return (uint32_t)v;
+    }
+    case A_MSMON_MBWU_CAPTURE_L ... (A_MSMON_MBWU_CAPTURE_L + 7):
+    {
+        uint64_t v = mpam_mbwu_value(s, mpam_mon_ris(s),
+                                     mpam_mbwu_mon(s, mpam_mon_ris(s)));
+
+        return extract64(v, (offset - A_MSMON_MBWU_CAPTURE_L) * 8, 32);
+    }
     case A_MPAMF_ERR_MSI_MPAM:
         return mpamf->err_msi_mpam;
     case A_MPAMF_ERR_MSI_ADDR_L:
@@ -746,6 +836,17 @@ static void mpam_msc_write_reg(void *opaque, hwaddr offset, uint64_t value,
         }
         return;
     }
+    case A_MSMON_MBWU:
+    case A_MSMON_MBWU_L:
+    case A_MSMON_MBWU_CAPTURE:
+    case A_MSMON_MBWU_CAPTURE_L:
+    {
+        MpamMbwuMon *m = mpam_mbwu_mon(s, mpam_mon_ris(s));
+
+        m->base = 0;
+        m->t0 = qemu_clock_get_ns(QEMU_CLOCK_HOST);
+        return;
+    }
     case A_MSMON_CAPT_EVNT:
     case A_MSMON_CFG_CSU_FLT:
     case A_MSMON_CFG_CSU_CTL:
@@ -855,10 +956,22 @@ static void mpam_msc_mem_realize(DeviceState *dev, Error **errp)
 
         /* We won't implement any implementation specific stuff */
         mpamf->idr = FIELD_DP64(mpamf->idr, MPAMF_IDR, NO_IMPL_PART, 1);
-        mpamf->idr = FIELD_DP64(mpamf->idr, MPAMF_IDR, NO_IMPL_MSMON, 1);
+        mpamf->idr = FIELD_DP64(mpamf->idr, MPAMF_IDR, HAS_MSMON, 1);
         /* Memory specific bit */
         mpamf->idr = FIELD_DP64(mpamf->idr, MPAMF_IDR, HAS_MBW_PART, 1);
 
+        mpamf->msmon_idr = FIELD_DP32(mpamf->msmon_idr,
+                                      MPAMF_MSMON_IDR, MSMON_MBWU, 1);
+        mpamf->msmon_idr = FIELD_DP32(mpamf->msmon_idr,
+                                      MPAMF_MSMON_IDR, HAS_OFLOW_SR, 1);
+        mpamf->mbwumon_idr = FIELD_DP32(mpamf->mbwumon_idr,
+                                        MPAMF_MBWUMON_IDR, NUM_MON,
+                                        s->num_mbwu_mon);
+        mpamf->mbwumon_idr = FIELD_DP32(mpamf->mbwumon_idr,
+                                        MPAMF_MBWUMON_IDR, HAS_LONG, 1);
+        mpamf->mbwumon_idr = FIELD_DP32(mpamf->mbwumon_idr,
+                                        MPAMF_MBWUMON_IDR, HAS_RWBW, 1);
+
         mpamf->iidr = FIELD_DP64(mpamf->iidr, MPAMF_IIDR, IMPLEMENTER,
                                  MPAM_IIDR_IMPLEMENTER);
         mpamf->iidr = FIELD_DP64(mpamf->iidr, MPAMF_IIDR, REVISION, 0);
@@ -914,11 +1027,23 @@ static void mpam_msc_cache_realize(DeviceState *dev, Error **errp)
 
         /* We won't implement any implementation specific stuff */
         mpamf->idr = FIELD_DP64(mpamf->idr, MPAMF_IDR, NO_IMPL_PART, 1);
-        mpamf->idr = FIELD_DP64(mpamf->idr, MPAMF_IDR, NO_IMPL_MSMON, 1);
+        mpamf->idr = FIELD_DP64(mpamf->idr, MPAMF_IDR, HAS_MSMON, 1);
 
         /* Need to implement for RME */
         mpamf->idr = FIELD_DP64(mpamf->idr, MPAMF_IDR, SP4, 0);
 
+        mpamf->msmon_idr = FIELD_DP32(mpamf->msmon_idr,
+                                      MPAMF_MSMON_IDR, MSMON_MBWU, 1);
+        mpamf->msmon_idr = FIELD_DP32(mpamf->msmon_idr,
+                                      MPAMF_MSMON_IDR, HAS_OFLOW_SR, 1);
+        mpamf->mbwumon_idr = FIELD_DP32(mpamf->mbwumon_idr,
+                                        MPAMF_MBWUMON_IDR, NUM_MON,
+                                        s->num_mbwu_mon);
+        mpamf->mbwumon_idr = FIELD_DP32(mpamf->mbwumon_idr,
+                                        MPAMF_MBWUMON_IDR, HAS_LONG, 1);
+        mpamf->mbwumon_idr = FIELD_DP32(mpamf->mbwumon_idr,
+                                        MPAMF_MBWUMON_IDR, HAS_RWBW, 1);
+
         /* Cache specific bit */
         mpamf->idr = FIELD_DP64(mpamf->idr, MPAMF_IDR, HAS_CPOR_PART, 1);
         mpamf->idr = FIELD_DP64(mpamf->idr, MPAMF_IDR, HAS_CCAP_PART, 1);
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH 26/37] hw/arm/mpam: Implement CMIN/CASSOC storage
       [not found] <20261016101058.1900808-1-2118537295@qq.com>
                   ` (9 preceding siblings ...)
  2026-10-16 10:10 ` [PATCH 25/37] hw/arm/mpam: Emulate MBWU bandwidth monitoring Jinjie Ruan
@ 2026-10-16 10:10 ` Jinjie Ruan
  2026-10-16 10:10 ` [PATCH 27/37] hw/arm/mpam: Advertise PRI on memory-side MSCs Jinjie Ruan
                   ` (10 subsequent siblings)
  21 siblings, 0 replies; 24+ messages in thread
From: Jinjie Ruan @ 2026-10-16 10:10 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>

MPAMF_CCAP_IDR advertised HAS_CMIN/HAS_CASSOC (since the CCAP
patch) but the MPAMCFG_CMIN and MPAMCFG_CASSOC registers had no
storage or read/write cases, so L2MIN/L3MIN schemata values were
dropped and read back as 0.  Add the per-internal-PARTID storage
and read/write handling, both guarded on CCAP support.

Also fix the MBW_MIN write mask check, which referenced the CMIN
mask (and logged it as "CMAX") since the device model was
introduced.

Signed-off-by: Jinjie Ruan <ruanjinjie@huawei.com>
---
 hw/arm/mpam.c | 36 ++++++++++++++++++++++++++++++++++--
 1 file changed, 34 insertions(+), 2 deletions(-)

diff --git a/hw/arm/mpam.c b/hw/arm/mpam.c
index 610ae88..1bd76ba 100644
--- a/hw/arm/mpam.c
+++ b/hw/arm/mpam.c
@@ -260,6 +260,8 @@ typedef struct MpamfPerNrwId {
         uint32_t cfg_mbw_pbm[(MPAM_MBW_PART + 31) / 32];
         uint32_t cfg_pri;
         uint32_t cfg_cmax;
+        uint32_t cfg_cmin;
+        uint32_t cfg_cassoc;
         uint32_t cfg_mbw_min;
         uint32_t cfg_mbw_max;
         uint32_t cfg_mbw_prop;
@@ -635,6 +637,20 @@ static uint64_t mpam_msc_read_reg(void *opaque, hwaddr offset,
             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)) {
+            qemu_log_mask(LOG_UNIMP,
+                "MPAM: Unexpected read of CMIN with no CCAP 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)) {
+            qemu_log_mask(LOG_UNIMP,
+                "MPAM: Unexpected read of CASSOC with no CCAP support\n");
+            return 0;
+        }
+        return mpamf->per_nrw_id[nrw_part_sel].cfg_cassoc;
     case A_MPAMF_MPAMCFG_MBW_MIN:
         return mpamf->per_nrw_id[nrw_part_sel].cfg_mbw_min;
     case A_MPAMF_MPAMCFG_MBW_MAX:
@@ -746,12 +762,28 @@ static void mpam_msc_write_reg(void *opaque, hwaddr offset, uint64_t value,
         }
         mpamf->per_nrw_id[nrw_part_sel].cfg_cmax = value;
         return;
-    case A_MPAMF_MPAMCFG_MBW_MIN:
+    case A_MPAMF_MPAMCFG_CMIN:
         if (value & ~MPAMF_MPAMCFG_CMIN_WRITE_MASK) {
             qemu_log_mask(LOG_UNIMP,
-                          "MPAM: Unexpected write to CMAX Mask=%x Value=%lx\n",
+                          "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;
+        return;
+    case A_MPAMF_MPAMCFG_CASSOC:
+        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;
+        return;
+    case A_MPAMF_MPAMCFG_MBW_MIN:
+        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;
         return;
     case A_MPAMF_MPAMCFG_MBW_MAX:
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH 27/37] hw/arm/mpam: Advertise PRI on memory-side MSCs
       [not found] <20261016101058.1900808-1-2118537295@qq.com>
                   ` (10 preceding siblings ...)
  2026-10-16 10:10 ` [PATCH 26/37] hw/arm/mpam: Implement CMIN/CASSOC storage Jinjie Ruan
@ 2026-10-16 10:10 ` Jinjie Ruan
  2026-10-16 10:10 ` [PATCH 28/37] hw/arm/mpam: Emulate CSU cache-usage monitoring Jinjie Ruan
                   ` (9 subsequent siblings)
  21 siblings, 0 replies; 24+ messages in thread
From: Jinjie Ruan @ 2026-10-16 10:10 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>

The arm_mpam driver picks the MBA-backing class for the MBPRI
pseudo-resource, which requires the memory-side MSC to advertise
HAS_PRI_PART -- without it the driver never exposes MBPRI even
though the register is handled.  Assemble the same PRI_IDR view
(INTPRI, 2 bits, 0 = low) as the cache subtype.

Signed-off-by: Jinjie Ruan <ruanjinjie@huawei.com>
---
 hw/arm/mpam.c | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/hw/arm/mpam.c b/hw/arm/mpam.c
index 1bd76ba..50c8a8e 100644
--- a/hw/arm/mpam.c
+++ b/hw/arm/mpam.c
@@ -991,6 +991,18 @@ static void mpam_msc_mem_realize(DeviceState *dev, Error **errp)
         mpamf->idr = FIELD_DP64(mpamf->idr, MPAMF_IDR, HAS_MSMON, 1);
         /* Memory specific bit */
         mpamf->idr = FIELD_DP64(mpamf->idr, MPAMF_IDR, HAS_MBW_PART, 1);
+        /*
+         * Priority partitioning on the memory side too: the driver exposes
+         * the MBPRI pseudo-resource from the class backing MBA, which
+         * requires the memory-side MSC to advertise HAS_PRI_PART.
+         */
+        mpamf->idr = FIELD_DP64(mpamf->idr, MPAMF_IDR, HAS_PRI_PART, 1);
+        mpamf->pri_idr = FIELD_DP32(mpamf->pri_idr, MPAMF_PRI_IDR,
+                                    HAS_INTPRI, 1);
+        mpamf->pri_idr = FIELD_DP32(mpamf->pri_idr, MPAMF_PRI_IDR,
+                                    INTPRI_0_IS_LOW, 1);
+        mpamf->pri_idr = FIELD_DP32(mpamf->pri_idr, MPAMF_PRI_IDR,
+                                    INTPRI_WD, 2);
 
         mpamf->msmon_idr = FIELD_DP32(mpamf->msmon_idr,
                                       MPAMF_MSMON_IDR, MSMON_MBWU, 1);
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH 28/37] hw/arm/mpam: Emulate CSU cache-usage monitoring
       [not found] <20261016101058.1900808-1-2118537295@qq.com>
                   ` (11 preceding siblings ...)
  2026-10-16 10:10 ` [PATCH 27/37] hw/arm/mpam: Advertise PRI on memory-side MSCs Jinjie Ruan
@ 2026-10-16 10:10 ` Jinjie Ruan
  2026-10-16 10:10 ` [PATCH 29/37] hw/arm/virt: Size the cache MSC occupancy model by level Jinjie Ruan
                   ` (8 subsequent siblings)
  21 siblings, 0 replies; 24+ messages in thread
From: Jinjie Ruan @ 2026-10-16 10:10 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>

Add the CSU monitor: MSMON_CFG_CSU_FLT/CTL window configuration
(replacing the unimplemented-monitor placeholder), the MSMON_CSU
and MSMON_CSU_CAPTURE reads, and the per-RIS monitor array with
the num-csu-mon and csu-capacity-bytes properties.  Occupancy is
modelled as a snapshot: csu-capacity-bytes scaled by the CPBM
portion fraction of the filtered PARTID, so cache-usage readings
track the configured CBM.  Writes to the CSU counters are ignored
(they probe NRDY; the snapshot is always ready) and CSU
monitoring is advertised in MSMON_IDR/CSUMON_IDR on the cache
subtype, which lets the driver pick QOS_L2/L3_OCCUP
(llc_occupancy).

Signed-off-by: Jinjie Ruan <ruanjinjie@huawei.com>
---
 hw/arm/mpam.c | 99 +++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 97 insertions(+), 2 deletions(-)

diff --git a/hw/arm/mpam.c b/hw/arm/mpam.c
index 50c8a8e..6c93ead 100644
--- a/hw/arm/mpam.c
+++ b/hw/arm/mpam.c
@@ -11,6 +11,7 @@
 #include "qemu/timer.h"
 #include "qemu/host-utils.h"
 #include "qemu/bitops.h"
+#include "qemu/units.h"
 #include "hw/core/irq.h"
 #include "hw/core/qdev-properties.h"
 #include "hw/core/registerfields.h"
@@ -139,8 +140,20 @@ REG32(MSMON_MBWU_L, 0x0880)         /* 64-bit counter */
 REG32(MSMON_MBWU_CAPTURE, 0x0868)
 REG32(MSMON_MBWU_CAPTURE_L, 0x0890)
 REG32(MSMON_CAPT_EVNT, 0x0808)
+
 REG32(MSMON_CFG_CSU_FLT, 0x0810)
+    FIELD(MSMON_CFG_CSU_FLT, PARTID, 0, 16)
+    FIELD(MSMON_CFG_CSU_FLT, PMG, 16, 8)
+    FIELD(MSMON_CFG_CSU_FLT, XCL, 31, 1)
+
 REG32(MSMON_CFG_CSU_CTL, 0x0818)
+    FIELD(MSMON_CFG_CSU_CTL, TYPE, 0, 8)
+    FIELD(MSMON_CFG_CSU_CTL, MATCH_PARTID, 16, 1)
+    FIELD(MSMON_CFG_CSU_CTL, MATCH_PMG, 17, 1)
+    FIELD(MSMON_CFG_CSU_CTL, EN, 31, 1)
+
+REG32(MSMON_CSU, 0x0840)             /* 31-bit value, bit31 NRDY */
+REG32(MSMON_CSU_CAPTURE, 0x0848)
 
 REG32(MPAMF_ERR_MSI_MPAM, 0x00dc)
 REG32(MPAMF_ERR_MSI_ADDR_L, 0x00e0)
@@ -282,6 +295,16 @@ typedef struct MpamMbwuMon {
     bool oflow;
 } MpamMbwuMon;
 
+/*
+ * One CSU monitor instance.  Occupancy is modelled as a snapshot: the
+ * cache capacity scaled by the CPBM portion fraction of the filtered
+ * PARTID, so cache-usage readings track the configured CBM.
+ */
+typedef struct MpamCsuMon {
+    uint32_t flt;
+    uint32_t ctl;
+} MpamCsuMon;
+
 typedef struct Mpamf {
     uint64_t idr;
     uint32_t iidr;
@@ -308,6 +331,7 @@ typedef struct Mpamf {
     MpamfPerNrwId *per_nrw_id;
 
     MpamMbwuMon *mbwu_mons;
+    MpamCsuMon *csu_mons;
 
 } Mpamf;
 
@@ -326,6 +350,8 @@ typedef struct MPAMMSCState {
     uint8_t num_ris;
     uint32_t num_mbwu_mon;
     uint64_t bw_nominal_bps;
+    uint32_t num_csu_mon;
+    uint64_t csu_capacity;
 
     /*
      * Wired-interrupt GSIVs the board assigned (INTID = SPI + 32).  Zero
@@ -468,6 +494,36 @@ static uint64_t mpam_mbwu_value(MPAMMSCState *s, Mpamf *mf, MpamMbwuMon *m)
     return v;
 }
 
+static MpamCsuMon *mpam_csu_mon(MPAMMSCState *s, Mpamf *mf)
+{
+    unsigned mon = FIELD_EX32(s->mon_sel, MSMON_CFG_MON_SEL, MON_SEL);
+
+    if (mon >= s->num_csu_mon) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "MPAM: MON_SEL %d out of range (num-csu-mon %d)\n",
+                      mon, s->num_csu_mon);
+        mon = 0;
+    }
+    return &mf->csu_mons[mon];
+}
+
+/*
+ * Snapshot occupancy: the cache capacity scaled by the fraction of CPBM
+ * portions the filtered PARTID is allowed to occupy.
+ */
+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;
+}
+
 typedef struct MPAMMSCMemState {
     MPAMMSCState parent;
 } MPAMMSCMemState;
@@ -563,6 +619,21 @@ static uint64_t mpam_msc_read_reg(void *opaque, hwaddr offset,
         return mpamf->mbwumon_idr;
     case A_MSMON_CFG_MON_SEL:
         return s->mon_sel;
+    case A_MSMON_CFG_CSU_FLT:
+        return mpam_csu_mon(s, mpam_mon_ris(s))->flt;
+    case A_MSMON_CFG_CSU_CTL:
+        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):
+    {
+        uint32_t v = mpam_csu_value(s, mpam_mon_ris(s),
+                                    mpam_csu_mon(s, mpam_mon_ris(s)));
+        hwaddr off = (offset >= A_MSMON_CSU_CAPTURE) ?
+                     offset - A_MSMON_CSU_CAPTURE : offset - A_MSMON_CSU;
+
+        /* NRDY (bit 31) is never set: the snapshot is always ready */
+        return extract32(v, off * 8, 32 - off * 8);
+    }
     case A_MSMON_CFG_MBWU_FLT:
         return mpam_mbwu_mon(s, mpam_mon_ris(s))->flt;
     case A_MSMON_CFG_MBWU_CTL:
@@ -879,11 +950,23 @@ static void mpam_msc_write_reg(void *opaque, hwaddr offset, uint64_t value,
         m->t0 = qemu_clock_get_ns(QEMU_CLOCK_HOST);
         return;
     }
-    case A_MSMON_CAPT_EVNT:
     case A_MSMON_CFG_CSU_FLT:
+        mpam_csu_mon(s, mpam_mon_ris(s))->flt = value;
+        return;
     case A_MSMON_CFG_CSU_CTL:
+        mpam_csu_mon(s, mpam_mon_ris(s))->ctl = value;
+        return;
+    case A_MSMON_CSU:
+    case A_MSMON_CSU_CAPTURE:
+        /*
+         * The driver pokes these with MSMON___NRDY to probe whether the
+         * counter needs polling.  Ignoring the write leaves the snapshot
+         * permanently ready, which is what the emulation models.
+         */
+        return;
+    case A_MSMON_CAPT_EVNT:
         qemu_log_mask(LOG_UNIMP,
-                      "MPAM: cache-usage monitoring not implemented (write %lx)\n",
+                      "MPAM: capture events not implemented (write %lx)\n",
                       offset);
         return;
     case A_MPAMF_ECR:
@@ -941,6 +1024,7 @@ static void mpam_msc_realize(DeviceState *dev, Error **errp)
         s->mpamf[i].per_nrw_id = g_new0(MpamfPerNrwId, s->num_int_partid);
         s->mpamf[i].cfg_intpartid = g_new0(uint32_t, s->num_partid);
         s->mpamf[i].mbwu_mons = g_new0(MpamMbwuMon, s->num_mbwu_mon);
+        s->mpamf[i].csu_mons = g_new0(MpamCsuMon, s->num_csu_mon);
         /*
          * Until resctrl programs the map, wrap each request PARTID onto an
          * internal PARTID so the narrowing indirection is always in range.
@@ -1088,6 +1172,14 @@ static void mpam_msc_cache_realize(DeviceState *dev, Error **errp)
         mpamf->mbwumon_idr = FIELD_DP32(mpamf->mbwumon_idr,
                                         MPAMF_MBWUMON_IDR, HAS_RWBW, 1);
 
+        if (s->num_csu_mon) {
+            mpamf->msmon_idr = FIELD_DP32(mpamf->msmon_idr,
+                                          MPAMF_MSMON_IDR, MSMON_CSU, 1);
+            mpamf->csumon_idr = FIELD_DP32(mpamf->csumon_idr,
+                                           MPAMF_CSUMON_IDR, NUM_MON,
+                                           s->num_csu_mon);
+        }
+
         /* Cache specific bit */
         mpamf->idr = FIELD_DP64(mpamf->idr, MPAMF_IDR, HAS_CPOR_PART, 1);
         mpamf->idr = FIELD_DP64(mpamf->idr, MPAMF_IDR, HAS_CCAP_PART, 1);
@@ -1148,6 +1240,9 @@ static const Property mpam_msc_props[] = {
     DEFINE_PROP_UINT32("num-mbwu-mon", MPAMMSCState, num_mbwu_mon, 8),
     DEFINE_PROP_UINT64("bw-nominal-bps", MPAMMSCState, bw_nominal_bps,
                        10000000000ULL),  /* 10 GB/s default bandwidth */
+    DEFINE_PROP_UINT32("num-csu-mon", MPAMMSCState, num_csu_mon, 4),
+    DEFINE_PROP_UINT64("csu-capacity-bytes", MPAMMSCState, csu_capacity,
+                       32 * MiB),
     DEFINE_PROP_UINT32("err-gsiv", MPAMMSCState, err_gsiv, 0),
     DEFINE_PROP_UINT32("oflow-gsiv", MPAMMSCState, oflow_gsiv, 0),
     DEFINE_PROP_UINT16("node", MPAMMSCState, node, UINT16_MAX),
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH 29/37] hw/arm/virt: Size the cache MSC occupancy model by level
       [not found] <20261016101058.1900808-1-2118537295@qq.com>
                   ` (12 preceding siblings ...)
  2026-10-16 10:10 ` [PATCH 28/37] hw/arm/mpam: Emulate CSU cache-usage monitoring Jinjie Ruan
@ 2026-10-16 10:10 ` Jinjie Ruan
  2026-10-16 10:10 ` [PATCH 30/37] hw/arm/mpam: Handle ECR/ESR as MSC-global registers Jinjie Ruan
                   ` (7 subsequent siblings)
  21 siblings, 0 replies; 24+ messages in thread
From: Jinjie Ruan @ 2026-10-16 10:10 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>

Give the cache MSCs their CSU model capacity: 512 KiB for L2 and
32 MiB for L3, matching the cache sizes guests infer from the
topology, so the occupancy snapshot reads back realistic values.

Signed-off-by: Jinjie Ruan <ruanjinjie@huawei.com>
---
 hw/arm/virt.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index a57ba56..1bcdd20 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -2192,6 +2192,10 @@ static void create_mpam_msc_cache(VirtMachineState *vms, int level,
         object_property_set_uint(OBJECT(dev), "cache-type", UNIFIED_CACHE,
                                  &error_fatal);
         object_property_set_uint(OBJECT(dev), "cpu", i, &error_fatal);
+        /* Occupancy model: usage = capacity scaled by the CPBM fraction */
+        object_property_set_uint(OBJECT(dev), "csu-capacity-bytes",
+                                 level == 2 ? 512 * KiB : 32 * MiB,
+                                 &error_fatal);
         sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
         sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, *base);
         *base += MPAM_SIZE;
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH 30/37] hw/arm/mpam: Handle ECR/ESR as MSC-global registers
       [not found] <20261016101058.1900808-1-2118537295@qq.com>
                   ` (13 preceding siblings ...)
  2026-10-16 10:10 ` [PATCH 29/37] hw/arm/virt: Size the cache MSC occupancy model by level Jinjie Ruan
@ 2026-10-16 10:10 ` Jinjie Ruan
  2026-10-16 10:10 ` [PATCH 31/37] hw/arm/mpam: Add device reset and power-on state initialisation Jinjie Ruan
                   ` (6 subsequent siblings)
  21 siblings, 0 replies; 24+ messages in thread
From: Jinjie Ruan @ 2026-10-16 10:10 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>

MPAMF_ECR and MPAMF_ESR are single per-MSC registers (IHI 0099
4.3), independent of the RIS selection, but the model stored them
per RIS.  Move the live copy to MSC level: ECR/ESR reads and the
extended ESR upper half (guarded on MPAMF_IDR.EXT) come from the
MSC-global view, ECR writes store masked and ESR writes deposit
into either half, and mpam_msc_sync_esr_ecr() keeps the per-RIS
export copies (used by the QMP introspection) in sync.

Signed-off-by: Jinjie Ruan <ruanjinjie@huawei.com>
---
 hw/arm/mpam.c | 48 +++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 45 insertions(+), 3 deletions(-)

diff --git a/hw/arm/mpam.c b/hw/arm/mpam.c
index 6c93ead..fe78adb 100644
--- a/hw/arm/mpam.c
+++ b/hw/arm/mpam.c
@@ -353,6 +353,14 @@ typedef struct MPAMMSCState {
     uint32_t num_csu_mon;
     uint64_t csu_capacity;
 
+    /*
+     * MPAMF_ECR / MPAMF_ESR are single per-MSC registers (IHI 0099 4.3),
+     * independent of the RIS selection.  The 64-bit ESR records the RIS
+     * the error was detected against in its top half.
+     */
+    uint32_t ecr;
+    uint64_t esr;
+
     /*
      * Wired-interrupt GSIVs the board assigned (INTID = SPI + 32).  Zero
      * means "no wired interrupt"; the ACPI MPAM table reports them as-is.
@@ -409,6 +417,20 @@ static uint32_t mpam_nrw_id_for(MPAMMSCState *s, Mpamf *mf, uint32_t partid)
     return intpartid % s->num_int_partid;
 }
 
+/*
+ * Keep the per-RIS copies in Mpamf (exported through the QMP/PPTT path)
+ * in sync with the MSC-level ECR/ESR view the MMIO handlers use.
+ */
+static void mpam_msc_sync_esr_ecr(MPAMMSCState *s)
+{
+    int i;
+
+    for (i = 0; i < s->num_ris; i++) {
+        s->mpamf[i].ecr = s->ecr;
+        s->mpamf[i].esr = (uint32_t)s->esr;
+    }
+}
+
 static Mpamf *mpam_mon_ris(MPAMMSCState *s)
 {
     unsigned ris = FIELD_EX32(s->mon_sel, MSMON_CFG_MON_SEL, RIS);
@@ -685,9 +707,16 @@ 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:
-        return mpamf->ecr;
+        return s->ecr;
     case A_MPAMF_ESR:
-        return mpamf->esr;
+        return s->esr;
+    case A_MPAMF_ESR + 0x4:
+        if (!FIELD_EX32(mpamf->idr, MPAMF_IDR, EXT)) {
+            qemu_log_mask(LOG_UNIMP,
+                          "MPAM: Unexpected read of extended ESR\n");
+            return 0;
+        }
+        return s->esr >> 32;
     case A_MPAMF_CFG_PART_SEL:
         return mpamf->cfg_part_sel;
     case A_MPAMF_MPAMCFG_INTPARTID: {
@@ -975,7 +1004,20 @@ static void mpam_msc_write_reg(void *opaque, hwaddr offset, uint64_t value,
                           "MPAM: Unexpected write to ECR Mask=%x Value=%lx\n",
                           MPAMF_ECR_WRITE_MASK, value);
         }
-        mpamf->ecr = value;
+        s->ecr = value & MPAMF_ECR_WRITE_MASK;
+        mpam_msc_sync_esr_ecr(s);
+        return;
+    case A_MPAMF_ESR:
+        /*
+         * 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);
+        mpam_msc_sync_esr_ecr(s);
+        return;
+    case A_MPAMF_ESR + 0x4:
+        s->esr = deposit64(s->esr, 32, 32, value);
+        mpam_msc_sync_esr_ecr(s);
         return;
     default:
         qemu_log_mask(LOG_UNIMP,
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH 31/37] hw/arm/mpam: Add device reset and power-on state initialisation
       [not found] <20261016101058.1900808-1-2118537295@qq.com>
                   ` (14 preceding siblings ...)
  2026-10-16 10:10 ` [PATCH 30/37] hw/arm/mpam: Handle ECR/ESR as MSC-global registers Jinjie Ruan
@ 2026-10-16 10:10 ` Jinjie Ruan
  2026-10-16 10:10 ` [PATCH 32/37] hw/arm/mpam: Add error and overflow interrupt outputs Jinjie Ruan
                   ` (5 subsequent siblings)
  21 siblings, 0 replies; 24+ messages in thread
From: Jinjie Ruan @ 2026-10-16 10:10 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>

Move the power-on initialisation out of realize into
mpam_msc_state_init() (shared with the new legacy reset callback):
selection state and ECR/ESR cleared, per-RIS partition and monitor
storage zeroed, the PARTID narrowing map identity-wrapped so the
indirection is always in range before the driver programs it, and
MBW_MAX reset to 0xffff (full bandwidth) -- an unprogrammed PARTID
must not read as zero bandwidth.

Also fix the realize error message for num-partid == 0, which
repeated the num-ris text.

Signed-off-by: Jinjie Ruan <ruanjinjie@huawei.com>
---
 hw/arm/mpam.c | 78 +++++++++++++++++++++++++++++++++++++++------------
 1 file changed, 60 insertions(+), 18 deletions(-)

diff --git a/hw/arm/mpam.c b/hw/arm/mpam.c
index fe78adb..c4a7567 100644
--- a/hw/arm/mpam.c
+++ b/hw/arm/mpam.c
@@ -1041,6 +1041,63 @@ static const MemoryRegionOps mpam_msc_ops = {
     },
 };
 
+
+/*
+ * Power-on state of the MSC, shared between realize and reset: ID narrowing
+ * map identity-wrapped, partition config at reset values, monitors off.
+ */
+static void mpam_msc_state_init(MPAMMSCState *s)
+{
+    int i;
+
+    s->ris = 0;
+    s->part_sel = 0;
+    s->internal_part_sel = false;
+    s->mon_sel = 0;
+    s->ecr = 0;
+    s->esr = 0;
+
+    for (i = 0; i < s->num_ris; i++) {
+        Mpamf *mpamf = &s->mpamf[i];
+        int p;
+
+        mpamf->ecr = 0;
+        mpamf->esr = 0;
+        memset(mpamf->per_nrw_id, 0,
+               sizeof(MpamfPerNrwId) * s->num_int_partid);
+        memset(mpamf->cfg_intpartid, 0,
+               sizeof(uint32_t) * s->num_partid);
+        memset(mpamf->mbwu_mons, 0,
+               sizeof(MpamMbwuMon) * s->num_mbwu_mon);
+        memset(mpamf->csu_mons, 0, sizeof(MpamCsuMon) * s->num_csu_mon);
+
+        /*
+         * Until resctrl programs the map, wrap each request PARTID onto an
+         * internal PARTID so the narrowing indirection is always in range.
+         */
+        for (p = 0; p < s->num_partid; p++) {
+            mpamf->cfg_intpartid[p] = p % s->num_int_partid;
+        }
+        /*
+         * per_nrw_id is indexed by INTERNAL PARTID: it has
+         * num_int_partid entries, not num_partid.  Writing it with the
+         * request-PARTID count overflowed the allocation by 224 entries
+         * (malloc(): corrupted top size once the struct grew).
+         */
+        for (p = 0; p < s->num_int_partid; p++) {
+            /* MBW_MAX resets to full bandwidth on real hardware */
+            mpamf->per_nrw_id[p].cfg_mbw_max = 0xffff;
+        }
+    }
+}
+
+static void mpam_msc_reset(DeviceState *dev)
+{
+    MPAMMSCState *s = MPAM_MSC_DEVICE(dev);
+
+    mpam_msc_state_init(s);
+}
+
 static void mpam_msc_realize(DeviceState *dev, Error **errp)
 {
     MPAMMSCState *s = MPAM_MSC_DEVICE(dev);
@@ -1052,7 +1109,7 @@ static void mpam_msc_realize(DeviceState *dev, Error **errp)
         return;
     }
     if (s->num_partid == 0) {
-        error_setg(errp, "num-ris must be <= 16");
+        error_setg(errp, "num-partid must be nonzero");
         return;
     }
     if (s->num_int_partid == 0) {
@@ -1061,28 +1118,12 @@ static void mpam_msc_realize(DeviceState *dev, Error **errp)
 
     s->mpamf = g_new0(Mpamf, s->num_ris);
     for (i = 0; i < s->num_ris; i++) {
-        int p;
-
         s->mpamf[i].per_nrw_id = g_new0(MpamfPerNrwId, s->num_int_partid);
         s->mpamf[i].cfg_intpartid = g_new0(uint32_t, s->num_partid);
         s->mpamf[i].mbwu_mons = g_new0(MpamMbwuMon, s->num_mbwu_mon);
         s->mpamf[i].csu_mons = g_new0(MpamCsuMon, s->num_csu_mon);
-        /*
-         * Until resctrl programs the map, wrap each request PARTID onto an
-         * internal PARTID so the narrowing indirection is always in range.
-         */
-        for (p = 0; p < s->num_partid; p++) {
-            s->mpamf[i].cfg_intpartid[p] = p % s->num_int_partid;
-        }
-        /*
-         * per_nrw_id is indexed by INTERNAL PARTID: it has num_int_partid
-         * entries, not num_partid.
-         */
-        for (p = 0; p < s->num_int_partid; p++) {
-            /* MBW_MAX resets to full bandwidth on real hardware */
-            s->mpamf[i].per_nrw_id[p].cfg_mbw_max = 0xffff;
-        }
     }
+    mpam_msc_state_init(s);
 
     memory_region_init_io(&s->mr, OBJECT(s), &mpam_msc_ops, s, "mpam_msc",
                           MPAM_SIZE);
@@ -1301,6 +1342,7 @@ static void mpam_msc_init(ObjectClass *klass, const void *data)
     DeviceClass *dc = DEVICE_CLASS(klass);
 
     device_class_set_props(dc, mpam_msc_props);
+    device_class_set_legacy_reset(dc, mpam_msc_reset);
 }
 
 static void mpam_msc_mem_init(ObjectClass *klass, const void *data)
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH 32/37] hw/arm/mpam: Add error and overflow interrupt outputs
       [not found] <20261016101058.1900808-1-2118537295@qq.com>
                   ` (15 preceding siblings ...)
  2026-10-16 10:10 ` [PATCH 31/37] hw/arm/mpam: Add device reset and power-on state initialisation Jinjie Ruan
@ 2026-10-16 10:10 ` Jinjie Ruan
  2026-10-16 10:10 ` [PATCH 33/37] hw/arm/mpam: Record errors and reject invalid configuration writes Jinjie Ruan
                   ` (4 subsequent siblings)
  21 siblings, 0 replies; 24+ messages in thread
From: Jinjie Ruan @ 2026-10-16 10:10 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>

Add the two sysbus irq outputs ([0] error, [1] overflow) with the
IHI 0099 level semantics: the error line is active while
MPAMF_ESR.ERR_CODE is non-zero and MPAMF_ECR.INTEN is set (8.2),
deasserted by writing ERRCODE zero; the overflow line is signalled
while any MBWU monitor has latched OFLOW_STATUS with OFLOW_INTR
set (8.3), deasserted by the write-one-to-clear of every overflowed
monitor.  Counter overflow latches the status exactly once, and
device reset deasserts both lines.

Signed-off-by: Jinjie Ruan <ruanjinjie@huawei.com>
---
 hw/arm/mpam.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 55 insertions(+), 1 deletion(-)

diff --git a/hw/arm/mpam.c b/hw/arm/mpam.c
index c4a7567..0389dad 100644
--- a/hw/arm/mpam.c
+++ b/hw/arm/mpam.c
@@ -361,6 +361,10 @@ typedef struct MPAMMSCState {
     uint32_t ecr;
     uint64_t esr;
 
+    /* Wired interrupt outputs: [0] error, [1] overflow (level) */
+    qemu_irq err_irq;
+    qemu_irq oflow_irq;
+
     /*
      * Wired-interrupt GSIVs the board assigned (INTID = SPI + 32).  Zero
      * means "no wired interrupt"; the ACPI MPAM table reports them as-is.
@@ -431,6 +435,43 @@ static void mpam_msc_sync_esr_ecr(MPAMMSCState *s)
     }
 }
 
+/*
+ * IHI 0099 8.2: the MPAM error interrupt is level-sensitive; it is active
+ * while MPAMF_ESR.ERR_CODE is non-zero and MPAMF_ECR.INTEN is set, and is
+ * deactivated by software writing ERRCODE to zero.
+ */
+static void mpam_msc_update_err_irq(MPAMMSCState *s)
+{
+    bool int_en = FIELD_EX32(s->ecr, MPAMF_ECR, INTEN);
+    bool active = FIELD_EX64(s->esr, MPAMF_ESR, ERR_CODE) != 0;
+
+    qemu_set_irq(s->err_irq, int_en && active);
+}
+
+/*
+ * IHI 0099 8.3: the overflow interrupt is signalled while any monitor has
+ * latched OFLOW_STATUS with OFLOW_INTR set; clearing OFLOW_STATUS of all
+ * overflowed monitors (W1C) deasserts it.
+ */
+static void mpam_msc_update_oflow_irq(MPAMMSCState *s)
+{
+    bool active = false;
+    int i, m;
+
+    for (i = 0; i < s->num_ris && !active; i++) {
+        for (m = 0; m < s->num_mbwu_mon; m++) {
+            MpamMbwuMon *mon = &s->mpamf[i].mbwu_mons[m];
+
+            if (mon->oflow &&
+                FIELD_EX32(mon->ctl, MSMON_CFG_MBWU_CTL, OFLOW_INTR)) {
+                active = true;
+                break;
+            }
+        }
+    }
+    qemu_set_irq(s->oflow_irq, active);
+}
+
 static Mpamf *mpam_mon_ris(MPAMMSCState *s)
 {
     unsigned ris = FIELD_EX32(s->mon_sel, MSMON_CFG_MON_SEL, RIS);
@@ -506,7 +547,10 @@ static uint64_t mpam_mbwu_value(MPAMMSCState *s, Mpamf *mf, MpamMbwuMon *m)
         v += muldiv64(delta, rate_mbs, 1000);
         /* 48-bit counter wraps (MSMON_MBWU_L is 48 bits of value). */
         if (v > (1ULL << 48)) {
-            m->oflow = true;
+            if (!m->oflow) {
+                m->oflow = true;
+                mpam_msc_update_oflow_irq(s);
+            }
             v &= (1ULL << 48) - 1;
         }
     }
@@ -966,6 +1010,7 @@ static void mpam_msc_write_reg(void *opaque, hwaddr offset, uint64_t value,
         if (value & R_MSMON_CFG_MBWU_CTL_OFLOW_STATUS_MASK) {
             m->oflow = false;               /* write-one-to-clear */
         }
+        mpam_msc_update_oflow_irq(s);
         return;
     }
     case A_MSMON_MBWU:
@@ -1006,6 +1051,7 @@ static void mpam_msc_write_reg(void *opaque, hwaddr offset, uint64_t value,
         }
         s->ecr = value & MPAMF_ECR_WRITE_MASK;
         mpam_msc_sync_esr_ecr(s);
+        mpam_msc_update_err_irq(s);
         return;
     case A_MPAMF_ESR:
         /*
@@ -1014,10 +1060,12 @@ static void mpam_msc_write_reg(void *opaque, hwaddr offset, uint64_t value,
          */
         s->esr = deposit64(s->esr, 0, 32, value);
         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);
         mpam_msc_sync_esr_ecr(s);
+        mpam_msc_update_err_irq(s);
         return;
     default:
         qemu_log_mask(LOG_UNIMP,
@@ -1096,6 +1144,9 @@ static void mpam_msc_reset(DeviceState *dev)
     MPAMMSCState *s = MPAM_MSC_DEVICE(dev);
 
     mpam_msc_state_init(s);
+    /* Deassert the wired interrupt lines */
+    mpam_msc_update_err_irq(s);
+    mpam_msc_update_oflow_irq(s);
 }
 
 static void mpam_msc_realize(DeviceState *dev, Error **errp)
@@ -1128,6 +1179,9 @@ static void mpam_msc_realize(DeviceState *dev, Error **errp)
     memory_region_init_io(&s->mr, OBJECT(s), &mpam_msc_ops, s, "mpam_msc",
                           MPAM_SIZE);
     sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mr);
+    /* Wired interrupt outputs: index 0 = error, index 1 = overflow */
+    sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->err_irq);
+    sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->oflow_irq);
 }
 
 static void mpam_msc_mem_realize(DeviceState *dev, Error **errp)
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH 33/37] hw/arm/mpam: Record errors and reject invalid configuration writes
       [not found] <20261016101058.1900808-1-2118537295@qq.com>
                   ` (16 preceding siblings ...)
  2026-10-16 10:10 ` [PATCH 32/37] hw/arm/mpam: Add error and overflow interrupt outputs Jinjie Ruan
@ 2026-10-16 10:10 ` Jinjie Ruan
  2026-10-16 10:10 ` [PATCH 34/37] hw/arm/virt: Wire MSC error and overflow interrupts to the GIC Jinjie Ruan
                   ` (3 subsequent siblings)
  21 siblings, 0 replies; 24+ messages in thread
From: Jinjie Ruan @ 2026-10-16 10:10 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>

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

Signed-off-by: Jinjie Ruan <ruanjinjie@huawei.com>
---
 hw/arm/mpam.c | 124 +++++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 117 insertions(+), 7 deletions(-)

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



^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH 34/37] hw/arm/virt: Wire MSC error and overflow interrupts to the GIC
       [not found] <20261016101058.1900808-1-2118537295@qq.com>
                   ` (17 preceding siblings ...)
  2026-10-16 10:10 ` [PATCH 33/37] hw/arm/mpam: Record errors and reject invalid configuration writes Jinjie Ruan
@ 2026-10-16 10:10 ` Jinjie Ruan
  2026-10-16 10:10 ` [PATCH 35/37] hw/arm/virt: Default to implicit node 0 for MPAM without -numa Jinjie Ruan
                   ` (2 subsequent siblings)
  21 siblings, 0 replies; 24+ messages in thread
From: Jinjie Ruan @ 2026-10-16 10:10 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>

Each board-created MSC gets two free SPIs above the platform bus range
(MPAM_SPI_BASE = 112 + PLATFORM_BUS_NUM_IRQS), connected to the GIC
(sysbus irq outputs [0] error, [1] overflow, both level).  The
assigned GSIVs (SPI + 32) are recorded as err-gsiv/oflow-gsiv qdev
properties, which the ACPI MPAM table already reports.

Guest-verified on virt,mpam=on gic-v3 with linux 7.3.0-rc6:
/proc/interrupts lists mpam:msc:error on hwirq 208/210/212 Level, no
error storms, full resctrl regression suite passes.

Signed-off-by: Jinjie Ruan <ruanjinjie@huawei.com>
---
 hw/arm/virt.c | 59 +++++++++++++++++++++++++++++++++++++++++++++------
 1 file changed, 53 insertions(+), 6 deletions(-)

diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index 1bcdd20..4ef7aed 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -2144,8 +2144,35 @@ static void create_virtio_iommu_dt_bindings(VirtMachineState *vms)
 #define MPAM_VIRT_NUM_PARTID     256
 #define MPAM_VIRT_NUM_INTPARTID  32
 
+/*
+ * MPAM MSC wired interrupts start above the platform bus SPI range
+ * (112 .. 112 + PLATFORM_BUS_NUM_IRQS - 1).  Two lines per MSC.
+ */
+#define MPAM_SPI_BASE   (112 + PLATFORM_BUS_NUM_IRQS)
+
+/*
+ * Hand a not-yet-realized MSC its two wired interrupt SPIs (error first,
+ * overflow second — the sysbus_init_irq() order in hw/arm/mpam.c), and
+ * record the GSIVs (INTID = SPI + 32) the ACPI MPAM table must report.
+ */
+static void mpam_msc_assign_irqs(DeviceState *dev,
+                                 int *err_spi, int *oflow_spi, int *next_spi)
+{
+    if (*next_spi + 1 >= NUM_IRQS) {
+        error_report("virt: out of SPIs for MPAM MSC interrupts");
+        exit(1);
+    }
+    *err_spi = (*next_spi)++;
+    *oflow_spi = (*next_spi)++;
+
+    object_property_set_int(OBJECT(dev), "err-gsiv", *err_spi + 32,
+                            &error_fatal);
+    object_property_set_int(OBJECT(dev), "oflow-gsiv", *oflow_spi + 32,
+                            &error_fatal);
+}
+
 static void create_mpam_msc_cache(VirtMachineState *vms, int level,
-                                  hwaddr *base)
+                                  hwaddr *base, int *next_spi)
 {
     MachineState *ms = MACHINE(vms);
     const CPUArchIdList *cpu_list = ms->possible_cpus;
@@ -2180,13 +2207,14 @@ static void create_mpam_msc_cache(VirtMachineState *vms, int level,
     }
 
     for (i = 0; i < cpu_list->len; i += step) {
+        int err_spi, oflow_spi;
+
         dev = qdev_new(TYPE_MPAM_MSC_CACHE);
         object_property_set_uint(OBJECT(dev), "num-ris", 1, &error_fatal);
         object_property_set_uint(OBJECT(dev), "num-partid",
                                  MPAM_VIRT_NUM_PARTID, &error_fatal);
         object_property_set_uint(OBJECT(dev), "num-int-partid",
-                                 MPAM_VIRT_NUM_INTPARTID,
-                                 &error_fatal);
+                                 MPAM_VIRT_NUM_INTPARTID, &error_fatal);
         object_property_set_uint(OBJECT(dev), "cache-level", level,
                                  &error_fatal);
         object_property_set_uint(OBJECT(dev), "cache-type", UNIFIED_CACHE,
@@ -2196,8 +2224,13 @@ static void create_mpam_msc_cache(VirtMachineState *vms, int level,
         object_property_set_uint(OBJECT(dev), "csu-capacity-bytes",
                                  level == 2 ? 512 * KiB : 32 * MiB,
                                  &error_fatal);
+        mpam_msc_assign_irqs(dev, &err_spi, &oflow_spi, next_spi);
         sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
         sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, *base);
+        sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0,
+                           qdev_get_gpio_in(vms->gic, err_spi));
+        sysbus_connect_irq(SYS_BUS_DEVICE(dev), 1,
+                           qdev_get_gpio_in(vms->gic, oflow_spi));
         *base += MPAM_SIZE;
     }
 }
@@ -2207,6 +2240,7 @@ static void create_mpam_msc(VirtMachineState *vms, Error **errp)
     MachineState *ms = MACHINE(vms);
     DeviceState *dev;
     int i, count = 0;
+    int next_spi = MPAM_SPI_BASE;
     hwaddr base = vms->memmap[VIRT_MPAM_MSC].base;
 
     if (ms->numa_state->num_nodes == 0) {
@@ -2218,6 +2252,8 @@ static void create_mpam_msc(VirtMachineState *vms, Error **errp)
         for (i = 0; i < ms->numa_state->num_nodes; i++) {
             /* at most 16 mem MSCs (4-bit selector space, SPI budget) */
             if (ms->numa_state->nodes[i].node_mem > 0 && count < 16) {
+                int err_spi, oflow_spi;
+
                 dev = qdev_new(TYPE_MPAM_MSC_MEM);
 
                 object_property_set_uint(OBJECT(dev), "node", i,
@@ -2230,14 +2266,21 @@ static void create_mpam_msc(VirtMachineState *vms, Error **errp)
                 object_property_set_uint(OBJECT(dev), "num-int-partid",
                                          MPAM_VIRT_NUM_INTPARTID,
                                          &error_fatal);
+                mpam_msc_assign_irqs(dev, &err_spi, &oflow_spi,
+                                     &next_spi);
                 sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
                 sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base);
+                sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0,
+                                   qdev_get_gpio_in(vms->gic, err_spi));
+                sysbus_connect_irq(SYS_BUS_DEVICE(dev), 1,
+                                   qdev_get_gpio_in(vms->gic, oflow_spi));
                 base += MPAM_SIZE;
                 count++;
             }
         }
     } else {
         int count_with_mem = 0;
+        int err_spi, oflow_spi;
 
         for (i = 0; i < ms->numa_state->num_nodes; i++) {
             if (ms->numa_state->nodes[i].node_mem) {
@@ -2252,14 +2295,18 @@ static void create_mpam_msc(VirtMachineState *vms, Error **errp)
         object_property_set_uint(OBJECT(dev), "num-int-partid",
                                  2, /* extreme narrowing */
                                  &error_fatal);
-
+        mpam_msc_assign_irqs(dev, &err_spi, &oflow_spi, &next_spi);
         sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
         sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base);
+        sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0,
+                           qdev_get_gpio_in(vms->gic, err_spi));
+        sysbus_connect_irq(SYS_BUS_DEVICE(dev), 1,
+                           qdev_get_gpio_in(vms->gic, oflow_spi));
         base += MPAM_SIZE;
     }
 
-    create_mpam_msc_cache(vms, 3, &base);
-    create_mpam_msc_cache(vms, 2, &base);
+    create_mpam_msc_cache(vms, 3, &base, &next_spi);
+    create_mpam_msc_cache(vms, 2, &base, &next_spi);
 }
 
 static void create_pcie(VirtMachineState *vms)
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH 35/37] hw/arm/virt: Default to implicit node 0 for MPAM without -numa
       [not found] <20261016101058.1900808-1-2118537295@qq.com>
                   ` (18 preceding siblings ...)
  2026-10-16 10:10 ` [PATCH 34/37] hw/arm/virt: Wire MSC error and overflow interrupts to the GIC Jinjie Ruan
@ 2026-10-16 10:10 ` Jinjie Ruan
  2026-10-16 10:10 ` [PATCH 36/37] qapi: Add MPAM cache introspection schema and stub Jinjie Ruan
  2026-10-16 10:10 ` [PATCH 37/37] hw/arm/mpam: Implement query-mpam-cache Jinjie Ruan
  21 siblings, 0 replies; 24+ messages in thread
From: Jinjie Ruan @ 2026-10-16 10:10 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>

Instead of failing with 'MPAM support requires NUMA nodes to be
specified', -M virt,mpam=on with no -numa now treats all RAM as one
implicit node 0 and creates a single memory MSC for it (node 0, one
RIS, same partitioning configuration as a per-node instance).  The
ACPI MPAM locator keeps proximity domain 0, which the mainline
arm_mpam driver resolves to node 0 via its pxm_to_node()
NUMA_NO_NODE fallback; no SRAT is fabricated and the guest stays
non-NUMA-aware.  Cache MSC creation is unaffected, and explicit
-numa / mpam-min-msc paths are unchanged.

Signed-off-by: Jinjie Ruan <ruanjinjie@huawei.com>
---
 hw/arm/virt.c | 30 +++++++++++++++++++++++++-----
 1 file changed, 25 insertions(+), 5 deletions(-)

diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index 4ef7aed..ee4308e 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -2244,11 +2244,31 @@ static void create_mpam_msc(VirtMachineState *vms, Error **errp)
     hwaddr base = vms->memmap[VIRT_MPAM_MSC].base;
 
     if (ms->numa_state->num_nodes == 0) {
-        error_setg(errp,
-                   "MPAM support requires NUMA nodes to be specified");
-        return;
-    }
-    if (!vms->mpam_min_msc) {
+        /*
+         * No -numa: treat all RAM as one implicit node 0 -- a single
+         * memory MSC with one RIS.  The ACPI locator keeps proximity
+         * domain 0, which the arm_mpam driver resolves to node 0 via its
+         * pxm_to_node() NUMA_NO_NODE fallback; no SRAT is fabricated and
+         * the guest stays non-NUMA-aware.
+         */
+        int err_spi, oflow_spi;
+
+        dev = qdev_new(TYPE_MPAM_MSC_MEM);
+        object_property_set_uint(OBJECT(dev), "node", 0, &error_fatal);
+        object_property_set_uint(OBJECT(dev), "num-ris", 1, &error_fatal);
+        object_property_set_uint(OBJECT(dev), "num-partid",
+                                 MPAM_VIRT_NUM_PARTID, &error_fatal);
+        object_property_set_uint(OBJECT(dev), "num-int-partid",
+                                 MPAM_VIRT_NUM_INTPARTID, &error_fatal);
+        mpam_msc_assign_irqs(dev, &err_spi, &oflow_spi, &next_spi);
+        sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
+        sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base);
+        sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0,
+                           qdev_get_gpio_in(vms->gic, err_spi));
+        sysbus_connect_irq(SYS_BUS_DEVICE(dev), 1,
+                           qdev_get_gpio_in(vms->gic, oflow_spi));
+        base += MPAM_SIZE;
+    } else if (!vms->mpam_min_msc) {
         for (i = 0; i < ms->numa_state->num_nodes; i++) {
             /* at most 16 mem MSCs (4-bit selector space, SPI budget) */
             if (ms->numa_state->nodes[i].node_mem > 0 && count < 16) {
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH 36/37] qapi: Add MPAM cache introspection schema and stub
       [not found] <20261016101058.1900808-1-2118537295@qq.com>
                   ` (19 preceding siblings ...)
  2026-10-16 10:10 ` [PATCH 35/37] hw/arm/virt: Default to implicit node 0 for MPAM without -numa Jinjie Ruan
@ 2026-10-16 10:10 ` Jinjie Ruan
  2026-10-26 13:05   ` Markus Armbruster
  2026-10-16 10:10 ` [PATCH 37/37] hw/arm/mpam: Implement query-mpam-cache Jinjie Ruan
  21 siblings, 1 reply; 24+ messages in thread
From: Jinjie Ruan @ 2026-10-16 10:10 UTC (permalink / raw)
  To: anisinha, armbru, eblake, imammedo, michael.roth, mst, pbonzini,
	peter.maydell, qemu-devel, qemu-arm
  Cc: ruanjinjie

From: Jonathan Cameron <Jonathan.Cameron@huawei.com>

Add the query-mpam-cache QMP command schema: the MpamBm bitmap
type, the per-RIS MpamRegs state and the MpamCacheInfo summary of
a cache MSC, plus an optional cache level filter.  The command is
stubbed to return NULL so the generated marshal links; the real
implementation walks the device tree in the next patch.

Based on an RFC series by Jonathan Cameron, adapted to the current
qapi documentation rules.

Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Signed-off-by: Jinjie Ruan <ruanjinjie@huawei.com>
---
 hw/arm/meson.build       |   1 +
 hw/arm/mpam-qapi-stubs.c |  14 +++++
 qapi/meson.build         |   1 +
 qapi/mpam.json           | 120 +++++++++++++++++++++++++++++++++++++++
 qapi/qapi-schema.json    |   1 +
 5 files changed, 137 insertions(+)
 create mode 100644 hw/arm/mpam-qapi-stubs.c
 create mode 100644 qapi/mpam.json

diff --git a/hw/arm/meson.build b/hw/arm/meson.build
index 42ca75b..adf9146 100644
--- a/hw/arm/meson.build
+++ b/hw/arm/meson.build
@@ -107,6 +107,7 @@ arm_common_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_peripherals.c'))
 arm_common_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2838_peripherals.c'))
 arm_common_ss.add(when: 'CONFIG_STRONGARM', if_true: files('strongarm.c'))
 arm_common_ss.add(when: 'CONFIG_MPAM', if_true: files('mpam.c'))
+arm_common_ss.add(files('mpam-qapi-stubs.c'))
 arm_common_ss.add(when: 'CONFIG_SX1', if_true: files('omap_sx1.c'))
 arm_common_ss.add(when: 'CONFIG_VERSATILE', if_true: files('versatilepb.c'))
 arm_common_ss.add(when: 'CONFIG_VEXPRESS', if_true: files('vexpress.c'))
diff --git a/hw/arm/mpam-qapi-stubs.c b/hw/arm/mpam-qapi-stubs.c
new file mode 100644
index 0000000..77735ed
--- /dev/null
+++ b/hw/arm/mpam-qapi-stubs.c
@@ -0,0 +1,14 @@
+/*
+ * QEMU MPAM QMP command stubs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qapi/qapi-commands-mpam.h"
+
+MpamCacheInfoList *qmp_query_mpam_cache(bool has_level, int64_t level,
+                                        Error **errp)
+{
+    return NULL;
+}
diff --git a/qapi/meson.build b/qapi/meson.build
index a46269b..c6663e5 100644
--- a/qapi/meson.build
+++ b/qapi/meson.build
@@ -42,6 +42,7 @@ qapi_all_modules = [
   'machine-s390x',
   'migration',
   'misc',
+  'mpam',
   'net',
   'pragma',
   'qom',
diff --git a/qapi/mpam.json b/qapi/mpam.json
new file mode 100644
index 0000000..ff476a1
--- /dev/null
+++ b/qapi/mpam.json
@@ -0,0 +1,120 @@
+# -*- Mode: Python -*-
+# vim: filetype=python
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+##
+# = ARM MPAM State Introspection
+##
+
+##
+# @MpamBm:
+#
+# A bitmap control value, as used by MPAM cache-portion (CPBM) and
+# memory-bandwidth-portion (MBW_PBM) controls.  The value is
+# carried as an array of 32-bit words, least significant first.
+#
+# @words: The bitmap, one element per 32-bits of the control
+#
+# Since: 10.1
+##
+{ 'struct': 'MpamBm',
+  'data' : { 'words' : [ 'int' ]
+           }
+}
+
+##
+# @MpamRegs:
+#
+# Per RIS register state of an emulated MPAM Memory System Component.
+#
+# @idr: MPAMF_IDR, the MPAM features identification register
+#
+# @iidr: MPAMF_IIDR, the MPAM implementer identification register
+#
+# @aidr: MPAMF_AIDR, the MPAM architecture identification register
+#
+# @cpor-idr: MPAMF_CPOR_IDR, cache-portion feature register
+#
+# @ccap-idr: MPAMF_CCAP_IDR, cache-capacity feature register
+#
+# @mbw-idr: MPAMF_MBW_IDR, memory-bandwidth feature register
+#
+# @pri-idr: MPAMF_PRI_IDR, priority feature register
+#
+# @partid-nrw-idr: MPAMF_PARTID_NRW_IDR, PARTID narrowing register
+#
+# @msmon-idr: MPAMF_MSMON_IDR, monitor feature register
+#
+# @csumon-idr: MPAMF_CSUMON_IDR, cache-usage monitor register
+#
+# @mbwumon-idr: MPAMF_MBWUMON_IDR, memory-bandwidth monitor register
+#
+# @ecr: MPAMF_ECR, the error control register
+#
+# @esr: MPAMF_ESR, the error status register
+#
+# @cfg-part-sel: MPAMCFG_PART_SEL, the partition-selection register
+#
+# @cfg-cpbm: The configured cache-portion bitmaps, one per
+#     internal PARTID
+#
+# Since: 10.1
+##
+{ 'struct' : 'MpamRegs',
+  'data' : { 'idr' : 'int',
+             'iidr' : 'int',
+             'aidr' : 'int',
+             'cpor-idr': 'int',
+             'ccap-idr': 'int',
+             'mbw-idr': 'int',
+             'pri-idr': 'int',
+             'partid-nrw-idr': 'int',
+             'msmon-idr': 'int',
+             'csumon-idr': 'int',
+             'mbwumon-idr': 'int',
+             'ecr': 'int',
+             'esr': 'int',
+             'cfg-part-sel': 'int',
+             'cfg-cpbm': ['MpamBm']#lazy
+          }
+}
+
+##
+# @MpamCacheInfo:
+#
+# Information about an MPAM cache Memory System Component.
+#
+# @cpu: First CPU of the set associated with the cache
+#
+# @level: Level of cache
+#
+# @type: Type of cache
+#
+# @regs: The per RIS register state
+#
+# Since: 10.1
+##
+{ 'struct': 'MpamCacheInfo',
+  'data' : { 'cpu' : 'int',
+             'level' : 'int',
+             'type' : 'int',
+             'regs' : ['MpamRegs']
+           }
+}
+
+##
+# @query-mpam-cache:
+#
+# Get a list of MpamCacheInfo for all Cache related MSC
+#
+# @level: Provide a cache level to filter against
+#
+# Returns: a list of @MpamCacheInfo describing all
+#   the MPAM cache MSC instances
+#
+# Since: 10.1
+##
+{ 'command': 'query-mpam-cache',
+  'data': { '*level': 'int' },
+  'returns' : ['MpamCacheInfo'],
+  'allow-preconfig': true }
diff --git a/qapi/qapi-schema.json b/qapi/qapi-schema.json
index b93dd68..cef175d 100644
--- a/qapi/qapi-schema.json
+++ b/qapi/qapi-schema.json
@@ -64,6 +64,7 @@
 { 'include': 'replay.json' }
 { 'include': 'yank.json' }
 { 'include': 'misc.json' }
+{ 'include': 'mpam.json' }
 { 'include': 'misc-arm.json' }
 { 'include': 'misc-i386.json' }
 { 'include': 'audio.json' }
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH 37/37] hw/arm/mpam: Implement query-mpam-cache
       [not found] <20261016101058.1900808-1-2118537295@qq.com>
                   ` (20 preceding siblings ...)
  2026-10-16 10:10 ` [PATCH 36/37] qapi: Add MPAM cache introspection schema and stub Jinjie Ruan
@ 2026-10-16 10:10 ` Jinjie Ruan
  21 siblings, 0 replies; 24+ messages in thread
From: Jinjie Ruan @ 2026-10-16 10:10 UTC (permalink / raw)
  To: anisinha, armbru, eblake, imammedo, michael.roth, mst, pbonzini,
	peter.maydell, qemu-devel, qemu-arm
  Cc: ruanjinjie

From: Jonathan Cameron <Jonathan.Cameron@huawei.com>

Implement the query-mpam-cache QMP command: walk the QOM tree for
mpam-msc-cache devices and report each via mpam_cache_fill_info()
(with the per-RIS register state and the configured CPBM bitmaps),
building on the generated header now included from the device
contract header.

Based on an RFC series by Jonathan Cameron, adapted to the current
qapi documentation rules.

Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Signed-off-by: Jinjie Ruan <ruanjinjie@huawei.com>
---
 hw/arm/meson.build    |  5 ++--
 hw/arm/mpam-qapi.c    | 62 ++++++++++++++++++++++++++++++++++++++++
 hw/arm/mpam.c         | 66 +++++++++++++++++++++++++++++++++++++++++++
 include/hw/arm/mpam.h |  3 ++
 4 files changed, 134 insertions(+), 2 deletions(-)
 create mode 100644 hw/arm/mpam-qapi.c

diff --git a/hw/arm/meson.build b/hw/arm/meson.build
index adf9146..da73546 100644
--- a/hw/arm/meson.build
+++ b/hw/arm/meson.build
@@ -106,8 +106,9 @@ arm_common_ss.add(when: 'CONFIG_NETDUINO2', if_true: files('netduino2.c'))
 arm_common_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_peripherals.c'))
 arm_common_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2838_peripherals.c'))
 arm_common_ss.add(when: 'CONFIG_STRONGARM', if_true: files('strongarm.c'))
-arm_common_ss.add(when: 'CONFIG_MPAM', if_true: files('mpam.c'))
-arm_common_ss.add(files('mpam-qapi-stubs.c'))
+arm_common_ss.add(when: 'CONFIG_MPAM', if_true: files('mpam.c', 'mpam-qapi.c'))
+arm_common_ss.add(when: 'CONFIG_MPAM', if_false: files('mpam-qapi-stubs.c'))
+arm_common_ss.add(when: 'CONFIG_ALL', if_true: files('mpam-qapi-stubs.c'))
 arm_common_ss.add(when: 'CONFIG_SX1', if_true: files('omap_sx1.c'))
 arm_common_ss.add(when: 'CONFIG_VERSATILE', if_true: files('versatilepb.c'))
 arm_common_ss.add(when: 'CONFIG_VEXPRESS', if_true: files('vexpress.c'))
diff --git a/hw/arm/mpam-qapi.c b/hw/arm/mpam-qapi.c
new file mode 100644
index 0000000..ceed734
--- /dev/null
+++ b/hw/arm/mpam-qapi.c
@@ -0,0 +1,62 @@
+/*
+ * QEMU MPAM QMP commands
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "hw/arm/mpam.h"
+#include "qom/object.h"
+#include "qapi/qapi-commands-mpam.h"
+
+typedef struct MPAMQueryState {
+    Error **errp;
+    MpamCacheInfoList **head;
+    bool level_filter_on;
+    int level;
+} MPAMQueryState;
+
+static int mpam_query_cache(Object *obj, void *opaque)
+{
+    MPAMQueryState *state = opaque;
+    MpamCacheInfoList *infolist;
+    MpamCacheInfo *info;
+
+    if (!object_dynamic_cast(obj, TYPE_MPAM_MSC_CACHE)) {
+        return 0;
+    }
+    if (state->level_filter_on &&
+        object_property_get_uint(obj, "cache-level", state->errp) !=
+        state->level) {
+        return 0;
+    }
+
+    infolist = g_malloc0(sizeof(*infolist));
+    info = g_malloc0(sizeof(*info));
+
+    mpam_cache_fill_info(obj, info);
+
+    infolist->value = info;
+
+    *state->head = infolist;
+    state->head = &infolist->next;
+
+    return 0;
+}
+
+MpamCacheInfoList *qmp_query_mpam_cache(bool has_level, int64_t level,
+                                        Error **errp)
+{
+
+    MpamCacheInfoList *head = NULL;
+    MPAMQueryState state = {
+        .errp = errp,
+        .head = &head,
+        .level_filter_on = has_level,
+        .level = level,
+    };
+
+    object_child_foreach_recursive(object_get_root(), mpam_query_cache, &state);
+
+    return head;
+}
diff --git a/hw/arm/mpam.c b/hw/arm/mpam.c
index 6c43e3e..f4f40ca 100644
--- a/hw/arm/mpam.c
+++ b/hw/arm/mpam.c
@@ -645,6 +645,72 @@ DECLARE_INSTANCE_CHECKER(MPAMMSCMemState, MPAM_MSC_MEM_DEVICE,
 DECLARE_INSTANCE_CHECKER(MPAMMSCCacheState, MPAM_MSC_CACHE_DEVICE,
                          TYPE_MPAM_MSC_CACHE);
 
+void mpam_cache_fill_info(Object *obj, MpamCacheInfo *info)
+{
+    MPAMMSCCacheState *cs = MPAM_MSC_CACHE_DEVICE(obj);
+    MPAMMSCState *s = MPAM_MSC_DEVICE(obj);
+    MpamRegsList *reg_list = NULL, **r_next = &reg_list;
+    int i, p, b;
+
+    info->cpu = cs->cpu;
+    info->level = cs->cache_level;
+    info->type = cs->cache_level;
+    for (i = 0; i < s->num_ris; i++) {
+        MpamRegsList *regs;
+        MpamRegs *r;
+        MpamBmList *bm_list = NULL, **bm_next = &bm_list;
+
+        Mpamf *mpamf = &s->mpamf[i];
+
+        r = g_malloc0(sizeof(*r));
+        regs = g_malloc0(sizeof(*regs));
+        regs->value = r;
+
+        *r = (MpamRegs) {
+            .idr = mpamf->idr,
+            .iidr = mpamf->iidr,
+            .aidr = mpamf->aidr,
+            .cpor_idr = mpamf->cpor_idr,
+            .ccap_idr = mpamf->ccap_idr,
+            .mbw_idr = mpamf->mbw_idr,
+            .pri_idr = mpamf->pri_idr,
+            .partid_nrw_idr = mpamf->partid_nrw_idr,
+            .msmon_idr = mpamf->msmon_idr,
+            .csumon_idr = mpamf->csumon_idr,
+            .mbwumon_idr = mpamf->mbwumon_idr,
+            .ecr = mpamf->ecr,
+            .esr = mpamf->esr,
+            .cfg_part_sel = mpamf->cfg_part_sel, /* Garbage */
+        };
+
+        /* This is annoyingly complex */
+        for (p = 0; p < s->num_int_partid; p++) {
+            intList *w_list = NULL, **w_next = &w_list;
+            MpamBm *bm = g_malloc0(sizeof(*bm));
+            MpamBmList *bml = g_malloc0(sizeof(*bml));
+
+            bml->value = bm;
+
+            for (b = 0; b < (MPAM_CACHE_PART + 31) / 32; b++) {
+                intList *il = g_malloc0(sizeof(*il));
+
+                il->value = mpamf->per_nrw_id[p].cfg_cpbm[b];
+                *w_next = il;
+                w_next = &il->next;
+
+            }
+            *bm_next = bml;
+            bm_next = &bml->next;
+            bm->words = w_list;
+        }
+        r->cfg_cpbm = bm_list;
+        *r_next = regs;
+        r_next = &regs->next;
+    }
+    info->regs = reg_list;
+
+}
+
 static uint64_t mpam_msc_read_reg(void *opaque, hwaddr offset,
                                   unsigned size)
 {
diff --git a/include/hw/arm/mpam.h b/include/hw/arm/mpam.h
index e7557a9..cb1c106 100644
--- a/include/hw/arm/mpam.h
+++ b/include/hw/arm/mpam.h
@@ -7,6 +7,7 @@
 #define _MPAM_H_
 
 #include "qom/object.h"
+#include "qapi/qapi-commands-mpam.h"
 
 #define TYPE_MPAM_MSC_MEM "mpam-msc-mem"
 #define TYPE_MPAM_MSC_CACHE "mpam-msc-cache"
@@ -17,4 +18,6 @@
  */
 #define MPAM_SIZE 0x4000
 
+void mpam_cache_fill_info(Object *obj, MpamCacheInfo *info);
+
 #endif /* _MPAM_H_ */
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 24+ messages in thread

* Re: [PATCH 36/37] qapi: Add MPAM cache introspection schema and stub
  2026-10-16 10:10 ` [PATCH 36/37] qapi: Add MPAM cache introspection schema and stub Jinjie Ruan
@ 2026-10-26 13:05   ` Markus Armbruster
  2026-10-27  7:20     ` Jinjie Ruan
  0 siblings, 1 reply; 24+ messages in thread
From: Markus Armbruster @ 2026-10-26 13:05 UTC (permalink / raw)
  To: Jinjie Ruan
  Cc: anisinha, eblake, imammedo, michael.roth, mst, pbonzini,
	peter.maydell, qemu-devel, qemu-arm, ruanjinjie

Jinjie Ruan <2118537295@qq.com> writes:

> From: Jonathan Cameron <Jonathan.Cameron@huawei.com>
>
> Add the query-mpam-cache QMP command schema: the MpamBm bitmap
> type, the per-RIS MpamRegs state and the MpamCacheInfo summary of
> a cache MSC, plus an optional cache level filter.  The command is
> stubbed to return NULL so the generated marshal links; the real
> implementation walks the device tree in the next patch.
>
> Based on an RFC series by Jonathan Cameron, adapted to the current
> qapi documentation rules.
>
> Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
> Signed-off-by: Jinjie Ruan <ruanjinjie@huawei.com>

[...]

> diff --git a/qapi/meson.build b/qapi/meson.build
> index a46269b..c6663e5 100644
> --- a/qapi/meson.build
> +++ b/qapi/meson.build
> @@ -42,6 +42,7 @@ qapi_all_modules = [
>    'machine-s390x',
>    'migration',
>    'misc',
> +  'mpam',
>    'net',
>    'pragma',
>    'qom',
> diff --git a/qapi/mpam.json b/qapi/mpam.json
> new file mode 100644
> index 0000000..ff476a1
> --- /dev/null
> +++ b/qapi/mpam.json
> @@ -0,0 +1,120 @@
> +# -*- Mode: Python -*-
> +# vim: filetype=python
> +# SPDX-License-Identifier: GPL-2.0-or-later
> +
> +##
> +# = ARM MPAM State Introspection
> +##

Special QAPI doc markup that hasn't been recognized in more than a year.
Please build with --enable-docs, and examine
docs/manual/interop/qemu-qmp-ref.html to guard against such mishaps.

Use standard reST markup instead:

   ##
   # ****************************
   # ARM MPAM State Introspection
   # ****************************
   ##

Are you *sure* this ought to be between "Miscellanea" and "Audio" and at
the same heading level?  Or should it be deeper and/or elsewhere?

> +
> +##
> +# @MpamBm:
> +#
> +# A bitmap control value, as used by MPAM cache-portion (CPBM) and
> +# memory-bandwidth-portion (MBW_PBM) controls.  The value is
> +# carried as an array of 32-bit words, least significant first.
> +#

Anti-pattern: the introduction (above) discusses the value of a member
(below).

> +# @words: The bitmap, one element per 32-bits of the control
> +#
> +# Since: 10.1

11.2 now.  Same elsewhere, not noting it again.

> +##
> +{ 'struct': 'MpamBm',
> +  'data' : { 'words' : [ 'int' ]

The description says it's 32 bit words.  Shouldn't you use 'uint32'
then?

> +           }
> +}
> +
> +##
> +# @MpamRegs:
> +#
> +# Per RIS register state of an emulated MPAM Memory System Component.

Please use the current syntax for introductions:

   ##
   # @name: introduction, best to keep it brief, but you may continue it
   #     like this if necessary.  If your text need to refer to
   #     arguments, it's almost certainly not brief introduction
   #     anymore.

Same elsewhere, not noting it again.

> +#
> +# @idr: MPAMF_IDR, the MPAM features identification register
> +#
> +# @iidr: MPAMF_IIDR, the MPAM implementer identification register
> +#
> +# @aidr: MPAMF_AIDR, the MPAM architecture identification register
> +#
> +# @cpor-idr: MPAMF_CPOR_IDR, cache-portion feature register
> +#
> +# @ccap-idr: MPAMF_CCAP_IDR, cache-capacity feature register
> +#
> +# @mbw-idr: MPAMF_MBW_IDR, memory-bandwidth feature register
> +#
> +# @pri-idr: MPAMF_PRI_IDR, priority feature register
> +#
> +# @partid-nrw-idr: MPAMF_PARTID_NRW_IDR, PARTID narrowing register
> +#
> +# @msmon-idr: MPAMF_MSMON_IDR, monitor feature register
> +#
> +# @csumon-idr: MPAMF_CSUMON_IDR, cache-usage monitor register
> +#
> +# @mbwumon-idr: MPAMF_MBWUMON_IDR, memory-bandwidth monitor register
> +#
> +# @ecr: MPAMF_ECR, the error control register
> +#
> +# @esr: MPAMF_ESR, the error status register
> +#
> +# @cfg-part-sel: MPAMCFG_PART_SEL, the partition-selection register
> +#
> +# @cfg-cpbm: The configured cache-portion bitmaps, one per
> +#     internal PARTID
> +#
> +# Since: 10.1
> +##
> +{ 'struct' : 'MpamRegs',
> +  'data' : { 'idr' : 'int',
> +             'iidr' : 'int',
> +             'aidr' : 'int',
> +             'cpor-idr': 'int',
> +             'ccap-idr': 'int',
> +             'mbw-idr': 'int',
> +             'pri-idr': 'int',
> +             'partid-nrw-idr': 'int',
> +             'msmon-idr': 'int',
> +             'csumon-idr': 'int',
> +             'mbwumon-idr': 'int',
> +             'ecr': 'int',
> +             'esr': 'int',
> +             'cfg-part-sel': 'int',
> +             'cfg-cpbm': ['MpamBm']#lazy

Is the comment an accident?

How wide are these registers?  Is 'int' the appropriate type?

> +          }
> +}
> +
> +##
> +# @MpamCacheInfo:
> +#
> +# Information about an MPAM cache Memory System Component.
> +#
> +# @cpu: First CPU of the set associated with the cache
> +#
> +# @level: Level of cache
> +#
> +# @type: Type of cache
> +#
> +# @regs: The per RIS register state
> +#
> +# Since: 10.1
> +##
> +{ 'struct': 'MpamCacheInfo',
> +  'data' : { 'cpu' : 'int',
> +             'level' : 'int',
> +             'type' : 'int',
> +             'regs' : ['MpamRegs']
> +           }
> +}
> +
> +##
> +# @query-mpam-cache:
> +#
> +# Get a list of MpamCacheInfo for all Cache related MSC
> +#
> +# @level: Provide a cache level to filter against

What exactly does this argument filter out?

Is it really needed?  How large can unfiltered output become?

> +#
> +# Returns: a list of @MpamCacheInfo describing all
> +#   the MPAM cache MSC instances

This gets rendered like

    Return: [MpamCacheInfo] – a list of MpamCacheInfo describing all the
            MPAM cache MSC instances

As is, the description is mostly redundant.  Do you need a description?
Try building without it, and see how it comes out.

> +#
> +# Since: 10.1
> +##
> +{ 'command': 'query-mpam-cache',
> +  'data': { '*level': 'int' },
> +  'returns' : ['MpamCacheInfo'],
> +  'allow-preconfig': true }
> diff --git a/qapi/qapi-schema.json b/qapi/qapi-schema.json
> index b93dd68..cef175d 100644
> --- a/qapi/qapi-schema.json
> +++ b/qapi/qapi-schema.json
> @@ -64,6 +64,7 @@
>  { 'include': 'replay.json' }
>  { 'include': 'yank.json' }
>  { 'include': 'misc.json' }
> +{ 'include': 'mpam.json' }
>  { 'include': 'misc-arm.json' }
>  { 'include': 'misc-i386.json' }
>  { 'include': 'audio.json' }



^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH 36/37] qapi: Add MPAM cache introspection schema and stub
  2026-10-26 13:05   ` Markus Armbruster
@ 2026-10-27  7:20     ` Jinjie Ruan
  0 siblings, 0 replies; 24+ messages in thread
From: Jinjie Ruan @ 2026-10-27  7:20 UTC (permalink / raw)
  To: Markus Armbruster, Jinjie Ruan
  Cc: anisinha, eblake, imammedo, michael.roth, mst, pbonzini,
	peter.maydell, qemu-devel, qemu-arm



在 2026/10/26 21:05, Markus Armbruster 写道:
> Jinjie Ruan <2118537295@qq.com> writes:
> 
>> From: Jonathan Cameron <Jonathan.Cameron@huawei.com>
>>
>> Add the query-mpam-cache QMP command schema: the MpamBm bitmap
>> type, the per-RIS MpamRegs state and the MpamCacheInfo summary of
>> a cache MSC, plus an optional cache level filter.  The command is
>> stubbed to return NULL so the generated marshal links; the real
>> implementation walks the device tree in the next patch.
>>
>> Based on an RFC series by Jonathan Cameron, adapted to the current
>> qapi documentation rules.
>>
>> Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
>> Signed-off-by: Jinjie Ruan <ruanjinjie@huawei.com>
> 
> [...]
> 
>> diff --git a/qapi/meson.build b/qapi/meson.build
>> index a46269b..c6663e5 100644
>> --- a/qapi/meson.build
>> +++ b/qapi/meson.build
>> @@ -42,6 +42,7 @@ qapi_all_modules = [
>>    'machine-s390x',
>>    'migration',
>>    'misc',
>> +  'mpam',
>>    'net',
>>    'pragma',
>>    'qom',
>> diff --git a/qapi/mpam.json b/qapi/mpam.json
>> new file mode 100644
>> index 0000000..ff476a1
>> --- /dev/null
>> +++ b/qapi/mpam.json
>> @@ -0,0 +1,120 @@
>> +# -*- Mode: Python -*-
>> +# vim: filetype=python
>> +# SPDX-License-Identifier: GPL-2.0-or-later
>> +
>> +##
>> +# = ARM MPAM State Introspection
>> +##
> 
> Special QAPI doc markup that hasn't been recognized in more than a year.
> Please build with --enable-docs, and examine
> docs/manual/interop/qemu-qmp-ref.html to guard against such mishaps.
> 
> Use standard reST markup instead:

Hi Markus,

Will fix it in v2 and build with --enable-docs.

> 
>    ##
>    # ****************************
>    # ARM MPAM State Introspection
>    # ****************************
>    ##
> 
> Are you *sure* this ought to be between "Miscellanea" and "Audio" and at
> the same heading level?  Or should it be deeper and/or elsewhere?

Yes, it should be placed in arm64 related chapter.

> 
>> +
>> +##
>> +# @MpamBm:
>> +#
>> +# A bitmap control value, as used by MPAM cache-portion (CPBM) and
>> +# memory-bandwidth-portion (MBW_PBM) controls.  The value is
>> +# carried as an array of 32-bit words, least significant first.
>> +#
> 
> Anti-pattern: the introduction (above) discusses the value of a member
> (below).
> 
>> +# @words: The bitmap, one element per 32-bits of the control
>> +#
>> +# Since: 10.1
> 
> 11.2 now.  Same elsewhere, not noting it again.

Will update it.

> 
>> +##
>> +{ 'struct': 'MpamBm',
>> +  'data' : { 'words' : [ 'int' ]
> 
> The description says it's 32 bit words.  Shouldn't you use 'uint32'
> then?
> 
>> +           }
>> +}
>> +
>> +##
>> +# @MpamRegs:
>> +#
>> +# Per RIS register state of an emulated MPAM Memory System Component.
> 
> Please use the current syntax for introductions:

Ok, will update it.

> 
>    ##
>    # @name: introduction, best to keep it brief, but you may continue it
>    #     like this if necessary.  If your text need to refer to
>    #     arguments, it's almost certainly not brief introduction
>    #     anymore.
> 
> Same elsewhere, not noting it again.

Ok, will update all the same comment.

> 
>> +#
>> +# @idr: MPAMF_IDR, the MPAM features identification register
>> +#
>> +# @iidr: MPAMF_IIDR, the MPAM implementer identification register
>> +#
>> +# @aidr: MPAMF_AIDR, the MPAM architecture identification register
>> +#
>> +# @cpor-idr: MPAMF_CPOR_IDR, cache-portion feature register
>> +#
>> +# @ccap-idr: MPAMF_CCAP_IDR, cache-capacity feature register
>> +#
>> +# @mbw-idr: MPAMF_MBW_IDR, memory-bandwidth feature register
>> +#
>> +# @pri-idr: MPAMF_PRI_IDR, priority feature register
>> +#
>> +# @partid-nrw-idr: MPAMF_PARTID_NRW_IDR, PARTID narrowing register
>> +#
>> +# @msmon-idr: MPAMF_MSMON_IDR, monitor feature register
>> +#
>> +# @csumon-idr: MPAMF_CSUMON_IDR, cache-usage monitor register
>> +#
>> +# @mbwumon-idr: MPAMF_MBWUMON_IDR, memory-bandwidth monitor register
>> +#
>> +# @ecr: MPAMF_ECR, the error control register
>> +#
>> +# @esr: MPAMF_ESR, the error status register
>> +#
>> +# @cfg-part-sel: MPAMCFG_PART_SEL, the partition-selection register
>> +#
>> +# @cfg-cpbm: The configured cache-portion bitmaps, one per
>> +#     internal PARTID
>> +#
>> +# Since: 10.1
>> +##
>> +{ 'struct' : 'MpamRegs',
>> +  'data' : { 'idr' : 'int',
>> +             'iidr' : 'int',
>> +             'aidr' : 'int',
>> +             'cpor-idr': 'int',
>> +             'ccap-idr': 'int',
>> +             'mbw-idr': 'int',
>> +             'pri-idr': 'int',
>> +             'partid-nrw-idr': 'int',
>> +             'msmon-idr': 'int',
>> +             'csumon-idr': 'int',
>> +             'mbwumon-idr': 'int',
>> +             'ecr': 'int',
>> +             'esr': 'int',
>> +             'cfg-part-sel': 'int',
>> +             'cfg-cpbm': ['MpamBm']#lazy
> 
> Is the comment an accident?
> 
> How wide are these registers?  Is 'int' the appropriate type?
> 
>> +          }
>> +}
>> +
>> +##
>> +# @MpamCacheInfo:
>> +#
>> +# Information about an MPAM cache Memory System Component.
>> +#
>> +# @cpu: First CPU of the set associated with the cache
>> +#
>> +# @level: Level of cache
>> +#
>> +# @type: Type of cache
>> +#
>> +# @regs: The per RIS register state
>> +#
>> +# Since: 10.1
>> +##
>> +{ 'struct': 'MpamCacheInfo',
>> +  'data' : { 'cpu' : 'int',
>> +             'level' : 'int',
>> +             'type' : 'int',
>> +             'regs' : ['MpamRegs']
>> +           }
>> +}
>> +
>> +##
>> +# @query-mpam-cache:
>> +#
>> +# Get a list of MpamCacheInfo for all Cache related MSC
>> +#
>> +# @level: Provide a cache level to filter against
> 
> What exactly does this argument filter out?
> 
> Is it really needed?  How large can unfiltered output become?
> 
>> +#
>> +# Returns: a list of @MpamCacheInfo describing all
>> +#   the MPAM cache MSC instances
> 
> This gets rendered like
> 
>     Return: [MpamCacheInfo] – a list of MpamCacheInfo describing all the
>             MPAM cache MSC instances
> 
> As is, the description is mostly redundant.  Do you need a description?
> Try building without it, and see how it comes out.
> 
>> +#
>> +# Since: 10.1
>> +##
>> +{ 'command': 'query-mpam-cache',
>> +  'data': { '*level': 'int' },
>> +  'returns' : ['MpamCacheInfo'],
>> +  'allow-preconfig': true }
>> diff --git a/qapi/qapi-schema.json b/qapi/qapi-schema.json
>> index b93dd68..cef175d 100644
>> --- a/qapi/qapi-schema.json
>> +++ b/qapi/qapi-schema.json
>> @@ -64,6 +64,7 @@
>>  { 'include': 'replay.json' }
>>  { 'include': 'yank.json' }
>>  { 'include': 'misc.json' }
>> +{ 'include': 'mpam.json' }
>>  { 'include': 'misc-arm.json' }
>>  { 'include': 'misc-i386.json' }
>>  { 'include': 'audio.json' }
> 

-- 
Best regards,
Jinjie



^ permalink raw reply	[flat|nested] 24+ messages in thread

end of thread, other threads:[~2026-10-27  7:21 UTC | newest]

Thread overview: 24+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
     [not found] <20261016101058.1900808-1-2118537295@qq.com>
2026-10-16 10:10 ` [PATCH 18/37] hw/arm/virt: Reserve the MPAM MSC MMIO window Jinjie Ruan
2026-10-16 10:10 ` [PATCH 17/37] hw/arm/virt: Add mpam and mpam-min-msc machine properties Jinjie Ruan
2026-10-16 10:10 ` [PATCH 18/37] hw/arm/virt: Reserve the MPAM MSC MMIO window Jinjie Ruan
2026-10-16 10:10 ` [PATCH 19/37] hw/arm/mpam: Add node and interrupt GSIV properties Jinjie Ruan
2026-10-16 10:10 ` [PATCH 20/37] hw/arm/virt: Create memory-side MPAM MSCs Jinjie Ruan
2026-10-16 10:10 ` [PATCH 21/37] hw/arm/virt: Create L2/L3 cache " Jinjie Ruan
2026-10-16 10:10 ` [PATCH 22/37] hw/acpi: Add MPAM table MSC node builders Jinjie Ruan
2026-10-16 10:10 ` [PATCH 23/37] hw/acpi: Build the MPAM ACPI table Jinjie Ruan
2026-10-16 10:10 ` [PATCH 24/37] hw/arm/mpam: Add MSMON monitor selection and window configuration Jinjie Ruan
2026-10-16 10:10 ` [PATCH 25/37] hw/arm/mpam: Emulate MBWU bandwidth monitoring Jinjie Ruan
2026-10-16 10:10 ` [PATCH 26/37] hw/arm/mpam: Implement CMIN/CASSOC storage Jinjie Ruan
2026-10-16 10:10 ` [PATCH 27/37] hw/arm/mpam: Advertise PRI on memory-side MSCs Jinjie Ruan
2026-10-16 10:10 ` [PATCH 28/37] hw/arm/mpam: Emulate CSU cache-usage monitoring Jinjie Ruan
2026-10-16 10:10 ` [PATCH 29/37] hw/arm/virt: Size the cache MSC occupancy model by level Jinjie Ruan
2026-10-16 10:10 ` [PATCH 30/37] hw/arm/mpam: Handle ECR/ESR as MSC-global registers Jinjie Ruan
2026-10-16 10:10 ` [PATCH 31/37] hw/arm/mpam: Add device reset and power-on state initialisation Jinjie Ruan
2026-10-16 10:10 ` [PATCH 32/37] hw/arm/mpam: Add error and overflow interrupt outputs Jinjie Ruan
2026-10-16 10:10 ` [PATCH 33/37] hw/arm/mpam: Record errors and reject invalid configuration writes Jinjie Ruan
2026-10-16 10:10 ` [PATCH 34/37] hw/arm/virt: Wire MSC error and overflow interrupts to the GIC Jinjie Ruan
2026-10-16 10:10 ` [PATCH 35/37] hw/arm/virt: Default to implicit node 0 for MPAM without -numa Jinjie Ruan
2026-10-16 10:10 ` [PATCH 36/37] qapi: Add MPAM cache introspection schema and stub Jinjie Ruan
2026-10-26 13:05   ` Markus Armbruster
2026-10-27  7:20     ` Jinjie Ruan
2026-10-16 10:10 ` [PATCH 37/37] hw/arm/mpam: Implement query-mpam-cache Jinjie Ruan

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.