On Thu, 24 Sep 2026 14:52:00 +0100
Suzuki K Poulose [off-list ref] wrote:
From: Steven Price <steven.price@arm.com>
The RMM maintains the state of all the granules in the system to make
sure that the host is abiding by the rules. This state can be maintained
at different granularity, per page (TRACKING_FINE) or per region
(TRACKING_COARSE or TRACKING_INTERMEDIATE). The region size depends on the
underlying "RMI_GRANULE_SIZE". For a "coarse"/"intermediate" region,
all pages in the region must be of the same state, this implies we need to
have "fine" tracking for DRAM, so that we can delegate individual pages.
For now we only support a statically carved out memory for tracking
granules for the "fine" regions. This can be extended in the future to
allow modifying the tracking granularity and remove the need for a
static allocation by the firmware.
Similarly, the firmware may create L0 GPT entries describing the total
address space. But if we change the "PAS" (Physical Address Space) of a
granule, then the firmware may need to create L1 tables to track the PAS
at a finer granularity. Linux therefore checks if the platform firmware
manages the PAR region. i.e., the firmware is in charge of managing the
L1 GPTs (creation and the required memory for the GPT tables - via static
carveouts) without host intervention. Support for dynamic GPT creation by
the host will be added later.
If the firmware requires us to manage the tracking or GPT memory,
deactivate the RMM and reclaim any memory donated at RMM activation.
Apply the same checks when hotplugged memory is brought online.
Signed-off-by: Steven Price <steven.price@arm.com>
[ Switch to RMI_GPT_L1_INFO for checking GPTs and deactivate RMM ]
Co-developed-by: Suzuki K Poulose <suzuki.poulose@arm.com>
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
A few trivial comments. Either way on those
Reviewed-by: Jonathan Cameron <redacted>
quoted hunk ↗ jump to hunk
---
drivers/firmware/arm_rmm/rmi.c | 218 ++++++++++++++++++++++++++++++++-
include/linux/arm-rmi-cmds.h | 2 +
2 files changed, 219 insertions(+), 1 deletion(-)
diff --git a/drivers/firmware/arm_rmm/rmi.c b/drivers/firmware/arm_rmm/rmi.c
index 0859f256e192b..1a8f3debd844a 100644
--- a/drivers/firmware/arm_rmm/rmi.c
+++ b/drivers/firmware/arm_rmm/rmi.c
quoted hunk ↗ jump to hunk
+
+/*
+ * rmi_gpt_info - Query the GPT info for the given PAR.
+ * @start: Base of the physical address region
+ * @end: Top of the physical address region
+ * @out_top: Top of the physical address region for which
+ * the GPT @out_gpt_par_state is valid
wrap to 80.
quoted hunk ↗ jump to hunk
+ * @out_gpt_par_state: State of the GPT covered by [start, out_top)
+ */
+static long rmi_gpt_info(unsigned long start, unsigned long end,
+ unsigned long *out_top,
+ unsigned long *out_gpt_par_state)
+{
+ struct arm_smccc_1_2_regs regs = {
+ SMC_RMI_GPT_INFO, start, end,
+ };
+
+ rmi_smccc_invoke(®s);
+ if (regs.a0 != RMI_SUCCESS)
+ return regs.a0;
+
+ if (out_top)
+ *out_top = regs.a1;
+ if (out_gpt_par_state)
+ *out_gpt_par_state = regs.a2;
+
+ return RMI_SUCCESS;
+}
+
+/*
+ * We do not support creating L1 GPTs yet. So, make sure that
+ * all the regions are managed by the firmware.
Wrap to 80 chars.
Not relevant to this patch, but when are you thinking we will support that?
quoted hunk ↗ jump to hunk
+ */
+static int rmi_verify_gpt_firmware_managed(phys_addr_t start, phys_addr_t end)
+{
+ unsigned long l0gpt_sz;
+ unsigned long next, par_state;
+
+ l0gpt_sz = 1UL << (30 + FIELD_GET(RMI_FEATURE_REGISTER_1_L0GPTSZ,
+ rmi_feat_reg(1)));
+ start = ALIGN_DOWN(start, l0gpt_sz);
+ end = ALIGN(end, l0gpt_sz);
+
+ while (start < end) {
+ long ret = rmi_gpt_info(start, end, &next, &par_state);
+
+ if (ret != RMI_SUCCESS)
+ return -ENOMEM;
+
+ if (WARN_ON(next <= start))
+ return -ENXIO;
+
+ if (par_state != RMI_GPT_PAR_PLAT) {
+ pr_err("GPT for the region is not managed by firmware %llx-%lx\n",
+ start, next);
+ return -ENOMEM;
+ }
+ start = next;
+ }
+
+ return 0;
+}
quoted hunk ↗ jump to hunk
+
+static int rmi_init_metadata(void)
+{
+ phys_addr_t start, end;
+ struct memblock_region *r;
+
+ for_each_mem_region(r) {
+ int ret;
+
+ /* Firmware-reserved NOMAP regions are not usable system RAM */
+ if (memblock_is_nomap(r))
+ continue;
+
+ start = PAGE_ALIGN(r->base);
+ end = PAGE_ALIGN_DOWN(r->base + r->size);
Add a comment on why rounding down. Doe we expect that to ever be relevant?
quoted hunk ↗ jump to hunk
+ /* Too small ? */
+ if (start >= end)
+ continue;
+
+ ret = rmi_prepare_memory(start, end);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
quoted hunk ↗ jump to hunk
+
static int __init arm64_init_rmi(void)
{
int ret;@@ -843,9 +1049,19 @@ static int __init arm64_init_rmi(void)
if (ret) {
pr_err("RMM activate failed (%d)\n", ret);
ret = ret < 0 ? ret : -ENXIO;
+ return ret;
}
- return ret;
+ ret = rmi_init_memory();
+ if (ret) {
+ /* Deactivate the RMM */
+ WARN_ON(rmi_sro_memxfer_cmd(sro, GFP_KERNEL, SMC_RMI_RMM_DEACTIVATE));
+ return ret;
+ }
+
+ arm64_rmi_is_available = true;
+ pr_info("RMI configured\n");
Blank line nice for the eye to spot the return.
+ return 0;
}