[RFC PATCH 15/31] x86/resctrl: Parse ACPI MMRC table

From: Chen Yu

Date: Sun Aug 02 2026 - 12:17:41 EST


The MMRC table is used to provide Memory Bandwidth Monitor Register
information. Its register addresses are mapped, and the MMRC pointer
is saved in the corresponding domain information entry.

Signed-off-by: Chen Yu <yu.c.chen@xxxxxxxxx>
---
arch/x86/kernel/cpu/resctrl/erdt.c | 51 ++++++++++++++++++++++++++
arch/x86/kernel/cpu/resctrl/internal.h | 6 ++-
2 files changed, 56 insertions(+), 1 deletion(-)

diff --git a/arch/x86/kernel/cpu/resctrl/erdt.c b/arch/x86/kernel/cpu/resctrl/erdt.c
index 5f9377c80be6..a14de9eb8b88 100644
--- a/arch/x86/kernel/cpu/resctrl/erdt.c
+++ b/arch/x86/kernel/cpu/resctrl/erdt.c
@@ -24,6 +24,7 @@ static bool erdt_enabled;

#define ERDT_VALID_VERSION 1
#define CMRC_SUPPORTED_INDEX_FN 1
+#define MMRC_SUPPORTED_INDEX_FN 1
#define RMDD_FLAG_CPU_L3_DOMAIN BIT(0)

/* Set in a monitoring counter when it holds no valid data to report. */
@@ -141,6 +142,7 @@ static void cleanup_one_domain(struct erdt_domain_info *d)
{
erdt_iounmap_domain(d);
kfree(d->cmrc);
+ kfree(d->mmrc);
kfree(d);
}

@@ -211,6 +213,49 @@ static __init int cmrc_init(struct acpi_subtbl_hdr_16 *subtbl,
return 0;
}

+static __init int mmrc_init(struct acpi_subtbl_hdr_16 *subtbl,
+ struct erdt_domain_info *domain_info)
+{
+ struct acpi_erdt_mmrc *mmrc = (struct acpi_erdt_mmrc *)subtbl;
+
+ if (subtbl->length < sizeof(*mmrc)) {
+ pr_warn(FW_BUG "Truncated MMRC subtable\n");
+ return -EIO;
+ }
+
+ if (mmrc->index_fn != MMRC_SUPPORTED_INDEX_FN) {
+ pr_info("Unsupported MMRC index function %d\n", mmrc->index_fn);
+ return -EIO;
+ }
+
+ /*
+ * The correction factor list is a trailing flexible array, so it is
+ * not covered by the sizeof(*mmrc) check above. Firmware could claim
+ * more entries than the sub-table actually carries, so only trust the
+ * length the sub-table can back.
+ */
+ if (subtbl->length < struct_size(mmrc, corr_factor_list,
+ mmrc->corr_factor_list_len)) {
+ pr_warn(FW_BUG "MMRC correction factor list of %u entries exceeds sub-table\n",
+ mmrc->corr_factor_list_len);
+ return -EIO;
+ }
+
+ domain_info->base[ERDT_MMIO_MMRC_BASE] =
+ erdt_ioremap(mmrc->reg_base, mmrc->reg_size, "MMRC base");
+ if (!domain_info->base[ERDT_MMIO_MMRC_BASE])
+ return -EIO;
+
+ domain_info->mmrc = kmemdup(mmrc, subtbl->length, GFP_KERNEL);
+ if (!domain_info->mmrc) {
+ iounmap(domain_info->base[ERDT_MMIO_MMRC_BASE]);
+ domain_info->base[ERDT_MMIO_MMRC_BASE] = NULL;
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
static inline struct acpi_subtbl_hdr_16 *rmdd_subtbl(struct acpi_erdt_rmdd *rmdd)
{
return (void *)rmdd + sizeof(*rmdd);
@@ -287,6 +332,12 @@ static __init bool parse_rmdd_table(struct acpi_subtbl_hdr_16 *rmdd_hdr)
!cmrc_init(subtbl, domain_info))
subtbl_mask |= BIT(ACPI_ERDT_TYPE_CMRC);

+ break;
+ case ACPI_ERDT_TYPE_MMRC:
+ if (!(subtbl_mask & BIT(ACPI_ERDT_TYPE_MMRC)) &&
+ !mmrc_init(subtbl, domain_info))
+ subtbl_mask |= BIT(ACPI_ERDT_TYPE_MMRC);
+
break;
default:
break;
diff --git a/arch/x86/kernel/cpu/resctrl/internal.h b/arch/x86/kernel/cpu/resctrl/internal.h
index c175d8884516..4bbe0c80c9e6 100644
--- a/arch/x86/kernel/cpu/resctrl/internal.h
+++ b/arch/x86/kernel/cpu/resctrl/internal.h
@@ -25,11 +25,13 @@
* Index into erdt_domain_info::base[] for each MMIO region.
* @ERDT_MMIO_RMDD_CREG: RMDD control register base address
* @ERDT_MMIO_CMRC_BASE: CMRC monitoring register base address
+ * @ERDT_MMIO_MMRC_BASE: MMRC monitoring register base address
*/
enum erdt_mmio_type {
ERDT_MMIO_RMDD_CREG,
ERDT_MMIO_CMRC_BASE,
- ERDT_MMIO_LAST = ERDT_MMIO_CMRC_BASE
+ ERDT_MMIO_MMRC_BASE,
+ ERDT_MMIO_LAST = ERDT_MMIO_MMRC_BASE
};

#define ERDT_MMIO_NUM_TYPES (ERDT_MMIO_LAST + 1)
@@ -38,6 +40,7 @@ enum erdt_mmio_type {
* struct erdt_domain_info - Per-domain ERDT information
* @base: Array of ioremapped MMIO region base addresses, indexed by ERDT_MMIO_* type
* @cmrc: Copy of the ACPI CMRC sub-table for this domain
+ * @mmrc: Copy of the ACPI MMRC sub-table for this domain
* @cpu_mask: CPUs belonging to this resource management domain
* @max_rmid: Maximum RMID supported by this domain
* @dom_id: L3 cache ID shared by all CPUs in this domain (-1 if unset)
@@ -46,6 +49,7 @@ enum erdt_mmio_type {
struct erdt_domain_info {
void __iomem *base[ERDT_MMIO_NUM_TYPES];
struct acpi_erdt_cmrc *cmrc;
+ struct acpi_erdt_mmrc *mmrc;
struct cpumask cpu_mask;
u32 max_rmid;
int dom_id;
--
2.43.0