AI Trajectory


Trajectory: (Expand/Collapse All)
Seq Timestamp Type Name Duration
0/0 2026/09/26 19:11 flow patch-triage
0m Args:
null

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "KMSANReasoning": "",
  "NeedsKMSAN": false,
  "Reasoning": "The patch series introduces support for the Arm Realm Management Interface (RMI) and cpufeature handling for Arm Confidential Compute Architecture (CCA). The new driver and changes depend on ARCH_SUPPORTS_RMM, which is only selected by ARM64. Consequently, all modified and added code paths are exclusive to the arm64 architecture and are not compiled or executable on the target architecture (amd64).",
  "WorthFuzzing": false
}

1/1 2026/09/26 19:11 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit c71bfd6872edba9866a77241829919b5da5bdedf\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Sat Sep 26 19:11:20 2026 +0000\n\n    syz-cluster: applied patch under review\n\ndiff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig\nindex b5a51b0ef9440..ff9565d3ffa59 100644\n--- a/arch/arm64/Kconfig\n+++ b/arch/arm64/Kconfig\n@@ -38,6 +38,7 @@ config ARM64\n \tselect ARCH_HAS_MEMBARRIER_SYNC_CORE\n \tselect ARCH_HAS_MEM_ENCRYPT\n \tselect ARCH_SUPPORTS_MSEAL_SYSTEM_MAPPINGS\n+\tselect ARCH_SUPPORTS_RMM\n \tselect ARCH_HAS_NMI_SAFE_THIS_CPU_OPS\n \tselect ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE\n \tselect ARCH_HAS_NONLEAF_PMD_YOUNG if ARM64_HAFT\ndiff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c\nindex 32102c3912fa7..e8b29983b0021 100644\n--- a/arch/arm64/kernel/cpufeature.c\n+++ b/arch/arm64/kernel/cpufeature.c\n@@ -293,6 +293,7 @@ static const struct arm64_ftr_bits ftr_id_aa64isar3[] = {\n static const struct arm64_ftr_bits ftr_id_aa64pfr0[] = {\n \tARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64PFR0_EL1_CSV3_SHIFT, 4, 0),\n \tARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64PFR0_EL1_CSV2_SHIFT, 4, 0),\n+\tARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64PFR0_EL1_RME_SHIFT, 4, 0),\n \tARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64PFR0_EL1_DIT_SHIFT, 4, 0),\n \tARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64PFR0_EL1_AMU_SHIFT, 4, 0),\n \tARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64PFR0_EL1_MPAM_SHIFT, 4, 0),\ndiff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig\nindex b7cc11e4fbfa6..62660bf520a8d 100644\n--- a/drivers/firmware/Kconfig\n+++ b/drivers/firmware/Kconfig\n@@ -310,5 +310,6 @@ source \"drivers/firmware/samsung/Kconfig\"\n source \"drivers/firmware/smccc/Kconfig\"\n source \"drivers/firmware/tegra/Kconfig\"\n source \"drivers/firmware/xilinx/Kconfig\"\n+source \"drivers/firmware/arm_rmm/Kconfig\"\n \n endmenu\ndiff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile\nindex be46f1e1dc77f..196a650ccf025 100644\n--- a/drivers/firmware/Makefile\n+++ b/drivers/firmware/Makefile\n@@ -39,3 +39,4 @@ obj-y\t\t\t\t+= samsung/\n obj-y\t\t\t\t+= smccc/\n obj-y\t\t\t\t+= tegra/\n obj-y\t\t\t\t+= xilinx/\n+obj-y\t\t\t\t+= arm_rmm/\ndiff --git a/drivers/firmware/arm_rmm/Kconfig b/drivers/firmware/arm_rmm/Kconfig\nnew file mode 100644\nindex 0000000000000..a37cba6647360\n--- /dev/null\n+++ b/drivers/firmware/arm_rmm/Kconfig\n@@ -0,0 +1,25 @@\n+\n+config ARCH_SUPPORTS_RMM\n+\tbool\n+\n+config ARM_RMM_RMI\n+\tbool \"Realm Management Interface (RMI) Support\"\n+\tdepends on ARCH_SUPPORTS_RMM\n+\thelp\n+\t  Support the Realm Management Monitor (RMM) on Arm systems that\n+\t  implement the Realm Management Extension (RME), as defined by the\n+\t  Arm Confidential Compute Architecture.\n+\n+\t  The RMM runs at EL2 in the Realm world and provides the Realm\n+\t  Management Interface (RMI) used by a Normal World host to create,\n+\t  manage and run protected virtual machines called Realms. The RMM can\n+\t  also act as a TSM, as defined by the PCIe TDISP and can manage the\n+\t  PCI IDE setup for securing the PCIe links.\n+\n+\t  This option builds the host-side RMI support used by KVM to detect a\n+\t  compatible RMM, configure it, manage delegated memory and enable\n+\t  Realm guests.\n+\n+\t  Selecting this option does not by itself make Realm guests available:\n+\t  the system must also provide RME-capable hardware and firmware with a\n+\t  compatible RMM implementation.\ndiff --git a/drivers/firmware/arm_rmm/Makefile b/drivers/firmware/arm_rmm/Makefile\nnew file mode 100644\nindex 0000000000000..65171988fdcae\n--- /dev/null\n+++ b/drivers/firmware/arm_rmm/Makefile\n@@ -0,0 +1,2 @@\n+\n+obj-$(CONFIG_ARM_RMM_RMI)\t= rmi.o\ndiff --git a/drivers/firmware/arm_rmm/rmi.c b/drivers/firmware/arm_rmm/rmi.c\nnew file mode 100644\nindex 0000000000000..1a8f3debd844a\n--- /dev/null\n+++ b/drivers/firmware/arm_rmm/rmi.c\n@@ -0,0 +1,1072 @@\n+// SPDX-License-Identifier: GPL-2.0\n+/*\n+ * Copyright (C) 2023-2026 ARM Ltd.\n+ */\n+\n+#include \u003clinux/cpufeature.h\u003e\n+#include \u003clinux/memblock.h\u003e\n+#include \u003clinux/memory.h\u003e\n+#include \u003clinux/arm-rmi-cmds.h\u003e\n+#include \u003clinux/slab.h\u003e\n+\n+#include \u003casm/memory.h\u003e\n+#include \u003casm/pgtable-hwdef.h\u003e\n+\n+/* RMM defines RmiFeatureRegister0 to RmiFeatureRegister5. */\n+static unsigned long rmi_feat_reg_cache[5] __ro_after_init;\n+static bool arm64_rmi_is_available;\n+\n+/**\n+ * rmi_granule_range_undelegate() - Undelegate a range of granules\n+ * @base: Base PA of the target range\n+ * @top: Top PA of the target range\n+ * @out_top: Returns the top PA of range whose state is undelegated\n+ *\n+ * Undelegate a range of granules to allow use by the normal world. Will fail\n+ * if the granules are in use by RMM. RMM can ignore granules that are already\n+ * undelegated and thus is safe to be called on a range with a mix of delegated\n+ * and undelegated granules.\n+ *\n+ * Return: 0 on success, positive RMI result code or negative Linux error code\n+ */\n+static inline long rmi_granule_range_undelegate(unsigned long base,\n+\t\t\t\t\t\tunsigned long top,\n+\t\t\t\t\t\tunsigned long *out_top)\n+{\n+\tstruct arm_smccc_1_2_regs regs = {\n+\t\tSMC_RMI_GRANULE_RANGE_UNDELEGATE, base, top\n+\t};\n+\tlong ret = rmi_sro_execute(\u0026regs);\n+\n+\tif (ret == RMI_SUCCESS \u0026\u0026 out_top)\n+\t\t*out_top = regs.a1;\n+\n+\treturn ret;\n+}\n+\n+int rmi_undelegate_range(phys_addr_t phys,\n+\t\t\t unsigned long size)\n+{\n+\tlong ret = 0;\n+\tunsigned long top = phys + size;\n+\tunsigned long out_top;\n+\n+\twhile (phys \u003c top) {\n+\t\tret = rmi_granule_range_undelegate(phys, top, \u0026out_top);\n+\n+\t\tif (ret == RMI_SUCCESS) {\n+\t\t\t/* Buggy RMM ? Let the caller leak the pages */\n+\t\t\tif (WARN_ON(out_top \u003c= phys))\n+\t\t\t\treturn -ENXIO;\n+\t\t\tphys = out_top;\n+\t\t} else {\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\n+\treturn ret;\n+}\n+EXPORT_SYMBOL_GPL(rmi_undelegate_range);\n+\n+/**\n+ * rmi_granule_range_delegate() - Delegate granules\n+ * @base: PA of the first granule of the range\n+ * @top: PA of the first granule after the range\n+ * @out_top: PA of the first granule not delegated\n+ *\n+ * Delegate a range of granule for use by the realm world. If the entire range\n+ * was delegated then @out_top == @top, otherwise the function should be called\n+ * again with @base == @out_top.\n+ *\n+ * Return: 0 on success, positive RMI result code or negative Linux error code\n+ */\n+static long rmi_granule_range_delegate(unsigned long base,\n+\t\t\t\t       unsigned long top,\n+\t\t\t\t       unsigned long *out_top)\n+{\n+\tstruct arm_smccc_1_2_regs regs = {\n+\t\tSMC_RMI_GRANULE_RANGE_DELEGATE, base, top\n+\t};\n+\tlong ret = rmi_sro_execute(\u0026regs);\n+\n+\tif (ret == RMI_SUCCESS \u0026\u0026 out_top)\n+\t\t*out_top = regs.a1;\n+\n+\treturn ret;\n+}\n+\n+/*\n+ * rmi_delegate_range: Delegate a physically contiguous range.\n+ * We iterate over the range until we hit an error. So we may\n+ * return an error, but with a partially delegated range. The\n+ * caller must always look at the @out_phys to figure out, how\n+ * much progress was made.\n+ *\n+ * @phys:\tBase of the physical address range\n+ * @size:\tSize of the physical address range\n+ * @out_phys:\tTop of the range that was completed. This is always\n+ *\t\tvalid, irrespective of the result.\n+ *\n+ * Returns RMI_SUCCESS on successful completion. Otherwise, returns\n+ * the Linux error number or the RMI status code as described\n+ * by the RMM spec for RMI_GRANULE_DELEGATE_RANGE or RMI_BLOCKED.\n+ */\n+int rmi_delegate_range(phys_addr_t phys,\n+\t\t       unsigned long size,\n+\t\t       phys_addr_t *out_phys)\n+{\n+\tlong ret = 0;\n+\tunsigned long top = phys + size;\n+\tunsigned long out_top;\n+\n+\twhile (phys \u003c top) {\n+\t\tret = rmi_granule_range_delegate(phys, top, \u0026out_top);\n+\n+\t\tif (ret == RMI_SUCCESS) {\n+\t\t\t/*\n+\t\t\t * Buggy RMM ? Let the caller handle the failure.\n+\t\t\t * We can't know how far the RMM delegated in this\n+\t\t\t * iteration, so we return the best known good limit.\n+\t\t\t * RMM can deal with granules already in \"undelegated\"\n+\t\t\t * in a given range. So, it is fine for the caller to\n+\t\t\t * try the range we return.\n+\t\t\t */\n+\t\t\tif (WARN_ON(out_top \u003c= phys)) {\n+\t\t\t\tret = -ENXIO;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t\tphys = out_top;\n+\t\t} else {\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\n+\tif (out_phys)\n+\t\t*out_phys = phys;\n+\n+\treturn ret;\n+}\n+EXPORT_SYMBOL_GPL(rmi_delegate_range);\n+\n+/*\n+ * Convert the RmiAddrBlockSize to actual size. This is used in RmiDonateReq\n+ * and RmiAddrRangeDesc*.\n+ */\n+static unsigned long rmi_addr_block_size_to_bytes(unsigned long block_size_fld)\n+{\n+\treturn BIT(ARM64_HW_PGTABLE_LEVEL_SHIFT(3 - block_size_fld));\n+}\n+\n+/*\n+ * free_addr_range: Free memory described by the address range entry, which may\n+ *\t\t    be partially consumed by RMM.\n+ *\n+ * @entry: RMI_ADDR_RANGE descriptor\n+ * @consumed_size: Page aligned size consumed by the RMM from the address range.\n+ *\n+ * If the state of the address is DELEGATED, undelegate it back, before freeing.\n+ * Leaks the memory if we cannot undelegate the range.\n+ */\n+static void free_addr_range(unsigned long entry, unsigned long consumed_size)\n+{\n+\tunsigned long phys = RMI_ADDR_RANGE_ADDR(entry);\n+\tunsigned long block_size_fld = RMI_ADDR_RANGE_BLOCK_SIZE(entry);\n+\tunsigned long count = RMI_ADDR_RANGE_COUNT(entry);\n+\tunsigned long state = RMI_ADDR_RANGE_STATE(entry);\n+\tunsigned long size = rmi_addr_block_size_to_bytes(block_size_fld) * count;\n+\n+\tWARN_ON(!PAGE_ALIGNED(phys) || !PAGE_ALIGNED(consumed_size));\n+\n+\t/* We shouldn't see this in reclaim path, leak it for now  */\n+\tif (WARN_ON(state == RMI_OP_MEM_CONDITIONAL))\n+\t\treturn;\n+\n+\t/* Adjust the address and size for partially consumed entry */\n+\tphys += consumed_size;\n+\tsize -= consumed_size;\n+\t/*\n+\t * Undelegate the pages back if required. If we can't\n+\t * change them back, leak the pages.\n+\t */\n+\tif (state == RMI_OP_MEM_DELEGATED \u0026\u0026\n+\t    WARN_ON(rmi_undelegate_range(phys, size)))\n+\t\treturn;\n+\tfree_pages_exact(phys_to_virt(phys), size);\n+}\n+\n+static void rmi_op_continue(unsigned long sro_handle, unsigned long flags,\n+\t\t\t    struct arm_smccc_1_2_regs *out_regs)\n+{\n+\t*out_regs = (struct arm_smccc_1_2_regs) {\n+\t\tSMC_RMI_OP_CONTINUE, sro_handle, flags\n+\t};\n+\n+\trmi_smccc_invoke(out_regs);\n+}\n+\n+static void rmi_op_cancel(unsigned long sro_handle,\n+\t\t\t  struct arm_smccc_1_2_regs *out_regs)\n+{\n+\t*out_regs = (struct arm_smccc_1_2_regs) {\n+\t\tSMC_RMI_OP_CANCEL, sro_handle\n+\t};\n+\n+\trmi_smccc_invoke(out_regs);\n+}\n+\n+static void rmi_op_mem_donate(unsigned long sro_handle, unsigned long list_addr,\n+\t\t\t      unsigned long list_count, unsigned long flags,\n+\t\t\t      struct arm_smccc_1_2_regs *out_regs)\n+{\n+\t*out_regs = (struct arm_smccc_1_2_regs) {\n+\t\tSMC_RMI_OP_MEM_DONATE, sro_handle, list_addr, list_count, flags\n+\t};\n+\n+\t/*\n+\t * The output donated count (a1) is always valid, irrespective\n+\t * of the return result. i.e., 0 if there was an error\n+\t */\n+\trmi_smccc_invoke(out_regs);\n+}\n+\n+static void rmi_op_mem_reclaim(unsigned long sro_handle,\n+\t\t\t       unsigned long list_addr,\n+\t\t\t       unsigned long list_count,\n+\t\t\t       struct arm_smccc_1_2_regs *out_regs)\n+{\n+\t*out_regs = (struct arm_smccc_1_2_regs) {\n+\t\tSMC_RMI_OP_MEM_RECLAIM, sro_handle, list_addr, list_count\n+\t};\n+\n+\trmi_smccc_invoke(out_regs);\n+}\n+\n+/*\n+ * rmi_free_delegated_page: Undelegate and free a page that has been previously\n+ * delegated to the Realm world. If we are unable to undelegate it, the page is\n+ * leaked.\n+ * NOTE: Do not use this helper if the page could be concurrently operated by\n+ * another thread, as it may get leaked if the undelegation fails due to RMI_BLOCKED\n+ */\n+int rmi_free_delegated_page(phys_addr_t phys)\n+{\n+\tif (WARN_ON_ONCE(rmi_undelegate_page(phys))) {\n+\t\t/* Undelegate failed: leak the page */\n+\t\treturn -EBUSY;\n+\t}\n+\n+\tfree_page((unsigned long)phys_to_virt(phys));\n+\n+\treturn 0;\n+}\n+EXPORT_SYMBOL_GPL(rmi_free_delegated_page);\n+\n+static int rmi_sro_ensure_capacity(struct rmi_sro_state *sro,\n+\t\t\t\t   unsigned long count)\n+{\n+\tif (WARN_ON_ONCE(sro-\u003eaddr_count \u003e RMI_MAX_ADDR_LIST))\n+\t\treturn -EOVERFLOW;\n+\n+\tif (count \u003e RMI_MAX_ADDR_LIST - sro-\u003eaddr_count)\n+\t\treturn -ENOSPC;\n+\n+\treturn 0;\n+}\n+\n+static int rmi_sro_donate_contig(struct rmi_sro_state *sro,\n+\t\t\t\t unsigned long sro_handle,\n+\t\t\t\t unsigned long donatereq,\n+\t\t\t\t struct arm_smccc_1_2_regs *out_regs,\n+\t\t\t\t gfp_t gfp)\n+{\n+\tunsigned long block_size_fld = RMI_DONATE_BLOCK_SIZE(donatereq);\n+\tunsigned long block_size = rmi_addr_block_size_to_bytes(block_size_fld);\n+\tunsigned long count = RMI_DONATE_COUNT(donatereq);\n+\tunsigned long state = RMI_DONATE_STATE(donatereq);\n+\tunsigned long size = block_size * count;\n+\tunsigned long addr_range;\n+\tunsigned long donated_granules;\n+\tunsigned long donated_size;\n+\tint ret;\n+\tvoid *virt;\n+\tphys_addr_t phys;\n+\n+\t/*\n+\t * The RMM specification requires contiguous allocations are always a\n+\t * power of 2\n+\t */\n+\tif (WARN_ON_ONCE(!is_power_of_2(size)))\n+\t\treturn -EINVAL;\n+\n+\t/* Reuse the cached address range if we have one */\n+\tfor (int i = 0; i \u003c sro-\u003eaddr_count; i++) {\n+\t\tunsigned long entry = sro-\u003eaddr_list[i];\n+\n+\t\tif (RMI_ADDR_RANGE_BLOCK_SIZE(entry) == block_size_fld \u0026\u0026\n+\t\t    RMI_ADDR_RANGE_COUNT(entry) == count \u0026\u0026\n+\t\t    RMI_ADDR_RANGE_STATE(entry) == state \u0026\u0026\n+\t\t    IS_ALIGNED(RMI_ADDR_RANGE_ADDR(entry), size)) {\n+\t\t\tsro-\u003eaddr_count--;\n+\t\t\tswap(sro-\u003eaddr_list[sro-\u003eaddr_count],\n+\t\t\t     sro-\u003eaddr_list[i]);\n+\n+\t\t\tgoto mem_donate;\n+\t\t}\n+\t}\n+\n+\tret = rmi_sro_ensure_capacity(sro, 1);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tvirt = alloc_pages_exact(size, gfp);\n+\tif (!virt)\n+\t\treturn -ENOMEM;\n+\tphys = virt_to_phys(virt);\n+\n+\tif (state == RMI_OP_MEM_DELEGATED) {\n+\t\tphys_addr_t delegated_phys;\n+\n+\t\tif (rmi_delegate_range(phys, size, \u0026delegated_phys)) {\n+\t\t\tif (!rmi_undelegate_range(phys, delegated_phys - phys))\n+\t\t\t\tfree_pages_exact(virt, size);\n+\t\t\treturn -ENXIO;\n+\t\t}\n+\t}\n+\n+\taddr_range = phys \u0026 RMI_ADDR_RANGE_ADDR_MASK;\n+\tFIELD_MODIFY(RMI_ADDR_RANGE_BLOCK_SIZE_MASK, \u0026addr_range, block_size_fld);\n+\tFIELD_MODIFY(RMI_ADDR_RANGE_COUNT_MASK, \u0026addr_range, count);\n+\tFIELD_MODIFY(RMI_ADDR_RANGE_STATE_MASK, \u0026addr_range, state);\n+\n+\tsro-\u003eaddr_list[sro-\u003eaddr_count] = addr_range;\n+\n+mem_donate:\n+\trmi_op_mem_donate(sro_handle,\n+\t\t\t  virt_to_phys(\u0026sro-\u003eaddr_list[sro-\u003eaddr_count]), 1,\n+\t\t\t  0, out_regs);\n+\tdonated_granules = out_regs-\u003ea1;\n+\n+\tif (WARN_ON(donated_granules \u003e (size \u003e\u003e PAGE_SHIFT)))\n+\t\tdonated_granules = (size \u003e\u003e PAGE_SHIFT);\n+\n+\tdonated_size = donated_granules \u003c\u003c PAGE_SHIFT;\n+\n+\t/* All granules consumed by the RMM */\n+\tif (donated_size == size)\n+\t\treturn 0;\n+\t/* No granules were consumed by the RMM, cache them */\n+\tif (donated_granules == 0) {\n+\t\tsro-\u003eaddr_count++;\n+\t\treturn 0;\n+\t}\n+\n+\t/* The granules were partially consumed, reclaim the unused ones. */\n+\tfree_addr_range(sro-\u003eaddr_list[sro-\u003eaddr_count], donated_size);\n+\n+\treturn 0;\n+}\n+\n+static int rmi_sro_donate_noncontig(struct rmi_sro_state *sro,\n+\t\t\t\t    unsigned long sro_handle,\n+\t\t\t\t    unsigned long donatereq,\n+\t\t\t\t    struct arm_smccc_1_2_regs *out_regs,\n+\t\t\t\t    gfp_t gfp)\n+{\n+\tunsigned long block_size_fld = RMI_DONATE_BLOCK_SIZE(donatereq);\n+\tunsigned long block_size = rmi_addr_block_size_to_bytes(block_size_fld);\n+\tunsigned long count = RMI_DONATE_COUNT(donatereq);\n+\tunsigned long state = RMI_DONATE_STATE(donatereq);\n+\tunsigned long found = 0;\n+\tunsigned long donated_granules;\n+\tunsigned long granules_per_block = block_size \u003e\u003e PAGE_SHIFT;\n+\tunsigned long consumed_blocks;\n+\tint addr_list_start = sro-\u003eaddr_count;\n+\tint ret, i;\n+\n+\t/*\n+\t * Clamp the number of entries to the maximum we can do in one go.\n+\t * The RMM can request  the remaining in the next iteration.\n+\t */\n+\tif (count \u003e RMI_MAX_ADDR_LIST)\n+\t\tcount = RMI_MAX_ADDR_LIST;\n+\n+\t/* Gather the suitable entries to the end of the list */\n+\ti = 0;\n+\twhile (i \u003c  addr_list_start \u0026\u0026 found \u003c count) {\n+\t\tunsigned long entry = sro-\u003eaddr_list[i];\n+\n+\t\tif (RMI_ADDR_RANGE_BLOCK_SIZE(entry) == block_size_fld \u0026\u0026\n+\t\t    RMI_ADDR_RANGE_COUNT(entry) == 1 \u0026\u0026\n+\t\t    RMI_ADDR_RANGE_STATE(entry) == state) {\n+\t\t\taddr_list_start--;\n+\t\t\tswap(sro-\u003eaddr_list[addr_list_start],\n+\t\t\t     sro-\u003eaddr_list[i]);\n+\t\t\tfound++;\n+\t\t\t/* Continue from the swapped in entry */\n+\t\t\tcontinue;\n+\t\t}\n+\t\t/* skip past the entry */\n+\t\ti++;\n+\t}\n+\n+\tret = rmi_sro_ensure_capacity(sro, count - found);\n+\tif (ret) {\n+\t\t/* If we have found some entries, donate them and try again */\n+\t\tif (found)\n+\t\t\tgoto mem_donate;\n+\t\t/* Otherwise free up the list and start again */\n+\t\trmi_sro_free(sro);\n+\t\t/* Reset the addr_list_start to match sro-\u003eaddr_count */\n+\t\taddr_list_start = 0;\n+\t}\n+\n+\tfor (; found \u003c count; found++) {\n+\t\tunsigned long addr_range;\n+\t\tvoid *virt = alloc_pages_exact(block_size, gfp);\n+\t\tphys_addr_t phys;\n+\n+\t\tif (!virt)\n+\t\t\treturn -ENOMEM;\n+\n+\t\tphys = virt_to_phys(virt);\n+\n+\t\tif (state == RMI_OP_MEM_DELEGATED) {\n+\t\t\tphys_addr_t delegated_phys;\n+\n+\t\t\tif (rmi_delegate_range(phys, block_size, \u0026delegated_phys)) {\n+\t\t\t\tif (!rmi_undelegate_range(phys, delegated_phys - phys))\n+\t\t\t\t\tfree_pages_exact(virt, block_size);\n+\t\t\t\treturn -ENXIO;\n+\t\t\t}\n+\t\t}\n+\n+\t\taddr_range = phys \u0026 RMI_ADDR_RANGE_ADDR_MASK;\n+\t\tFIELD_MODIFY(RMI_ADDR_RANGE_BLOCK_SIZE_MASK, \u0026addr_range, block_size_fld);\n+\t\tFIELD_MODIFY(RMI_ADDR_RANGE_COUNT_MASK, \u0026addr_range, 1);\n+\t\tFIELD_MODIFY(RMI_ADDR_RANGE_STATE_MASK, \u0026addr_range, state);\n+\n+\t\tsro-\u003eaddr_list[sro-\u003eaddr_count++] = addr_range;\n+\t}\n+\n+mem_donate:\n+\trmi_op_mem_donate(sro_handle,\n+\t\t\t  virt_to_phys(\u0026sro-\u003eaddr_list[addr_list_start]),\n+\t\t\t  found, 0, out_regs);\n+\n+\tdonated_granules = out_regs-\u003ea1;\n+\t/*\n+\t * The RMM shouldn't report more granules than we provided, but clamp\n+\t * just in case.\n+\t */\n+\tif (WARN_ON_ONCE(donated_granules \u003e found * granules_per_block))\n+\t\tdonated_granules = found * granules_per_block;\n+\n+\t/*\n+\t * The RMM reports the consumed memory in terms of granules, but we\n+\t * track in the address lists in block-sized ranges. So divide to get\n+\t * the number of (complete) consumed blocks.\n+\t */\n+\tconsumed_blocks = donated_granules / granules_per_block;\n+\tif (donated_granules % granules_per_block) {\n+\t\t/*\n+\t\t * A block has been partially consumed, the start is owned by\n+\t\t * the RMM, the tail is owned by the host\n+\t\t */\n+\t\tunsigned long entry =\n+\t\t\tsro-\u003eaddr_list[addr_list_start + consumed_blocks];\n+\t\tunsigned long donated_size =\n+\t\t\t(donated_granules % granules_per_block) \u003c\u003c PAGE_SHIFT;\n+\n+\t\tfree_addr_range(entry, donated_size);\n+\t\t/*\n+\t\t * This block is now fully 'consumed' (either held by the RMM or\n+\t\t * freed)\n+\t\t */\n+\t\tconsumed_blocks++;\n+\t}\n+\n+\t/*\n+\t * Keep just the blocks the RMM didn't use in addr_list\n+\t * RMM claimed consumed_blocks entries from addr_list_start.\n+\t * Move the entries left out at the end i.e.,\n+\t * [ addr_list_start + consumed_blocks, addr_list_start + found)\n+\t * to the rest of the valid entries and adjust the addr_count to\n+\t * reflect the available entries.\n+\t */\n+\tfor (int i = 0, src = addr_list_start + consumed_blocks;\n+\t\ti \u003c found - consumed_blocks; i++)\n+\t\tsro-\u003eaddr_list[addr_list_start + i] = sro-\u003eaddr_list[src + i];\n+\n+\tsro-\u003eaddr_count -= consumed_blocks;\n+\n+\treturn 0;\n+}\n+\n+static int rmi_sro_donate(struct rmi_sro_state *sro,\n+\t\t\t  unsigned long sro_handle,\n+\t\t\t  unsigned long donatereq,\n+\t\t\t  struct arm_smccc_1_2_regs *regs,\n+\t\t\t  gfp_t gfp)\n+{\n+\tif (WARN_ON_ONCE(!RMI_DONATE_COUNT(donatereq)))\n+\t\treturn -EINVAL;\n+\n+\t/*\n+\t * We do not support RMI_OP_MEM_CONDITIONAL yet. This is only required\n+\t * for use in RMI_GRANULE_TRACKING_SET, which we don't support yet.\n+\t */\n+\tif (WARN_ON_ONCE(RMI_DONATE_STATE(donatereq) == RMI_OP_MEM_CONDITIONAL))\n+\t\treturn -EINVAL;\n+\n+\tif (RMI_DONATE_CONTIG(donatereq) == RMI_OP_MEM_CONTIG)\n+\t\treturn rmi_sro_donate_contig(sro, sro_handle, donatereq, regs, gfp);\n+\telse\n+\t\treturn rmi_sro_donate_noncontig(sro, sro_handle, donatereq, regs, gfp);\n+}\n+\n+static int rmi_sro_reclaim(struct rmi_sro_state *sro,\n+\t\t\t   unsigned long sro_handle,\n+\t\t\t   struct arm_smccc_1_2_regs *out_regs)\n+{\n+\tunsigned long capacity;\n+\n+\t/*\n+\t * We don't do a partial free of the entries. So for\n+\t * now free the entire address list as we prepare\n+\t * to reclaim more from the RMM.\n+\t */\n+\tif (rmi_sro_ensure_capacity(sro, 1))\n+\t\trmi_sro_free(sro);\n+\n+\tcapacity = RMI_MAX_ADDR_LIST - sro-\u003eaddr_count;\n+\n+\trmi_op_mem_reclaim(sro_handle,\n+\t\t\t   virt_to_phys(\u0026sro-\u003eaddr_list[sro-\u003eaddr_count]),\n+\t\t\t   capacity, out_regs);\n+\n+\t/*\n+\t * RMI_OP_MEM_RECLAIM always return RMI_INCOMPLETE, except when the\n+\t * input parameters were invalid.\n+\t */\n+\tif (WARN_ON_ONCE(RMI_RESULT_STATUS(out_regs-\u003ea0) != RMI_INCOMPLETE))\n+\t\treturn -EINVAL;\n+\tif (WARN_ON_ONCE(out_regs-\u003ea1 \u003e capacity))\n+\t\tout_regs-\u003ea1 = capacity;\n+\n+\tsro-\u003eaddr_count += out_regs-\u003ea1;\n+\n+\treturn 0;\n+}\n+\n+void rmi_sro_free(struct rmi_sro_state *sro)\n+{\n+\t/* Handle the worse */\n+\tif (WARN_ON(sro-\u003eaddr_count \u003c 0))\n+\t\treturn;\n+\n+\tif (WARN_ON(sro-\u003eaddr_count \u003e RMI_MAX_ADDR_LIST))\n+\t\tsro-\u003eaddr_count = RMI_MAX_ADDR_LIST;\n+\n+\tfor (int i = 0; i \u003c sro-\u003eaddr_count; i++)\n+\t\tfree_addr_range(sro-\u003eaddr_list[i], 0);\n+\n+\tsro-\u003eaddr_count = 0;\n+}\n+EXPORT_SYMBOL_GPL(rmi_sro_free);\n+\n+long rmi_sro_memxfer_execute(struct rmi_sro_state *sro, gfp_t gfp)\n+{\n+\tstruct arm_smccc_1_2_regs *regs = \u0026sro-\u003eregs;\n+\tbool cancelled = false;\n+\tunsigned long sro_handle;\n+\n+\trmi_smccc_invoke(regs);\n+\n+\tsro_handle = regs-\u003ea1;\n+\twhile (RMI_RESULT_STATUS(regs-\u003ea0) == RMI_INCOMPLETE) {\n+\t\tbool can_cancel = RMI_RESULT_CAN_CANCEL(regs-\u003ea0) == RMI_OP_CAN_CANCEL;\n+\t\tint ret = 0;\n+\n+\t\tswitch (RMI_RESULT_MEMREQ(regs-\u003ea0)) {\n+\t\tcase RMI_OP_MEM_REQ_NONE:\n+\t\t\trmi_op_continue(sro_handle, RMI_CONTINUE_KEEP_GOING,\n+\t\t\t\t\tregs);\n+\t\t\tbreak;\n+\t\tcase RMI_OP_MEM_REQ_DONATE:\n+\t\t\tret = rmi_sro_donate(sro, sro_handle, regs-\u003ea2, regs,\n+\t\t\t\t\t     gfp);\n+\t\t\tbreak;\n+\t\tcase RMI_OP_MEM_REQ_RECLAIM:\n+\t\t\tret = rmi_sro_reclaim(sro, sro_handle, regs);\n+\t\t\tbreak;\n+\t\tdefault:\n+\t\t\tWARN_ON_ONCE(1);\n+\t\t\tret = -ENXIO;\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\tif (ret) {\n+\t\t\t/*\n+\t\t\t * All memory donating SROs must be cancellable. So a\n+\t\t\t * failure in memory allocation shouldn't be an issue.\n+\t\t\t * However, if we encounter a random failure (e.g.,\n+\t\t\t * buggy RMM), don't loop forever, just give up.\n+\t\t\t */\n+\t\t\tif (WARN_ON_ONCE(!can_cancel))\n+\t\t\t\treturn ret;\n+\t\t\t/*\n+\t\t\t * If we have already cancelled, and came back here due\n+\t\t\t * to an error in MEMREQ, then there is no point\n+\t\t\t * in going in loops.\n+\t\t\t */\n+\t\t\tif (WARN_ON_ONCE(cancelled))\n+\t\t\t\tbreak;\n+\t\t\trmi_op_cancel(sro_handle, regs);\n+\t\t\tcancelled = true;\n+\n+\t\t\tif (WARN_ON_ONCE(RMI_RESULT_STATUS(regs-\u003ea0) != RMI_INCOMPLETE))\n+\t\t\t\treturn ret;\n+\t\t}\n+\t}\n+\n+\tif (cancelled)\n+\t\treturn -ECANCELED;\n+\n+\treturn regs-\u003ea0;\n+}\n+EXPORT_SYMBOL_GPL(rmi_sro_memxfer_execute);\n+\n+/*\n+ * rmi_sro_execute: Execute an RMI command that is Stateful but not memory\n+ * tranfserring. Takes regs, filled with the FIDs and the arguments in place.\n+ *\n+ * Returns :\n+ *  -ECANCELLED - If the operation had to be aborted and SRO was cancellable.\n+ *  Otherwise, returns the result of the RMI command.\n+ */\n+long rmi_sro_execute(struct arm_smccc_1_2_regs *regs)\n+{\n+\tbool cancelled = false;\n+\tunsigned long sro_handle = regs-\u003ea1;\n+\n+\trmi_smccc_invoke(regs);\n+\n+\tsro_handle = regs-\u003ea1;\n+\twhile (RMI_RESULT_STATUS(regs-\u003ea0) == RMI_INCOMPLETE) {\n+\t\tbool can_cancel = RMI_RESULT_CAN_CANCEL(regs-\u003ea0) == RMI_OP_CAN_CANCEL;\n+\n+\t\tswitch (RMI_RESULT_MEMREQ(regs-\u003ea0)) {\n+\t\tcase RMI_OP_MEM_REQ_NONE:\n+\t\t\trmi_op_continue(sro_handle, RMI_CONTINUE_KEEP_GOING,\n+\t\t\t\t\tregs);\n+\t\t\tbreak;\n+\t\tdefault:\n+\t\t\tWARN_ON_ONCE(1);\n+\t\t\tif (!can_cancel)\n+\t\t\t\treturn regs-\u003ea0;\n+\t\t\t/*\n+\t\t\t * We can't get here normally, but handle this anyway\n+\t\t\t * for a buggy RMM implementation.\n+\t\t\t */\n+\t\t\tif (cancelled)\n+\t\t\t\treturn -ECANCELED;\n+\t\t\trmi_op_cancel(sro_handle, regs);\n+\t\t\tcancelled = true;\n+\t\t}\n+\t}\n+\n+\tif (cancelled)\n+\t\treturn -ECANCELED;\n+\n+\treturn regs-\u003ea0;\n+}\n+EXPORT_SYMBOL_GPL(rmi_sro_execute);\n+\n+static int rmi_check_version(void)\n+{\n+\tunsigned short version_major, version_minor;\n+\tunsigned long host_version = RMI_ABI_VERSION(RMI_ABI_MAJOR_VERSION,\n+\t\t\t\t\t\t     RMI_ABI_MINOR_VERSION);\n+\tunsigned long aa64pfr0 = read_sanitised_ftr_reg(SYS_ID_AA64PFR0_EL1);\n+\tstruct arm_smccc_1_2_regs res = {\n+\t\tSMC_RMI_VERSION, host_version,\n+\t};\n+\n+\t/* If RME isn't supported, then RMI can't be */\n+\tif (cpuid_feature_extract_unsigned_field(aa64pfr0, ID_AA64PFR0_EL1_RME_SHIFT) == 0)\n+\t\treturn -ENXIO;\n+\n+\trmi_smccc_invoke(\u0026res);\n+\tif (res.a0 == SMCCC_RET_NOT_SUPPORTED)\n+\t\treturn -ENXIO;\n+\n+\tversion_major = RMI_ABI_VERSION_GET_MAJOR(res.a1);\n+\tversion_minor = RMI_ABI_VERSION_GET_MINOR(res.a1);\n+\n+\tif (res.a0 != RMI_SUCCESS) {\n+\t\tunsigned short high_version_major, high_version_minor;\n+\n+\t\thigh_version_major = RMI_ABI_VERSION_GET_MAJOR(res.a2);\n+\t\thigh_version_minor = RMI_ABI_VERSION_GET_MINOR(res.a2);\n+\n+\t\tpr_err(\"Unsupported RMI ABI (v%d.%d - v%d.%d) we want v%d.%d\\n\",\n+\t\t       version_major, version_minor,\n+\t\t       high_version_major, high_version_minor,\n+\t\t       RMI_ABI_MAJOR_VERSION,\n+\t\t       RMI_ABI_MINOR_VERSION);\n+\t\treturn -ENXIO;\n+\t}\n+\n+\tpr_info(\"RMI ABI version %d.%d\\n\", version_major, version_minor);\n+\n+\treturn 0;\n+}\n+\n+static int rmi_read_features(void)\n+{\n+\t/*\n+\t * Since we've negotiated a compatible version these feature registers\n+\t * should always be available\n+\t */\n+\tfor (int i = 0; i \u003c ARRAY_SIZE(rmi_feat_reg_cache); i++) {\n+\t\tstruct arm_smccc_1_2_regs args = {\n+\t\t\tSMC_RMI_FEATURES, i,\n+\t\t};\n+\n+\t\trmi_smccc_invoke(\u0026args);\n+\t\tif (WARN_ON(args.a0 != RMI_SUCCESS))\n+\t\t\treturn -EINVAL;\n+\n+\t\trmi_feat_reg_cache[i] = args.a1;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+unsigned long rmi_feat_reg(unsigned long index)\n+{\n+\tif (WARN_ON(index \u003e= ARRAY_SIZE(rmi_feat_reg_cache)))\n+\t\treturn 0;\n+\n+\treturn rmi_feat_reg_cache[index];\n+}\n+EXPORT_SYMBOL_GPL(rmi_feat_reg);\n+\n+static int rmi_rmm_config_set(unsigned long cfg_ptr)\n+{\n+\tstruct arm_smccc_1_2_regs regs = {\n+\t\tSMC_RMI_RMM_CONFIG_SET, cfg_ptr,\n+\t};\n+\n+\trmi_smccc_invoke(\u0026regs);\n+\n+\treturn regs.a0;\n+}\n+\n+static int rmi_configure(void)\n+{\n+\tunsigned long granule_feature;\n+\tunsigned long granule_size;\n+\tint ret = 0;\n+\n+\tswitch (PAGE_SIZE) {\n+\tcase SZ_4K:\n+\t\tgranule_size = RMI_GRANULE_SIZE_4KB;\n+\t\tgranule_feature = RMI_FEATURE_REGISTER_1_RMI_GRAN_SZ_4KB;\n+\t\tbreak;\n+\tcase SZ_16K:\n+\t\tgranule_size = RMI_GRANULE_SIZE_16KB;\n+\t\tgranule_feature = RMI_FEATURE_REGISTER_1_RMI_GRAN_SZ_16KB;\n+\t\tbreak;\n+\tcase SZ_64K:\n+\t\tgranule_size = RMI_GRANULE_SIZE_64KB;\n+\t\tgranule_feature = RMI_FEATURE_REGISTER_1_RMI_GRAN_SZ_64KB;\n+\t\tbreak;\n+\tdefault:\n+\t\tBUILD_BUG();\n+\t}\n+\n+\tif (!(rmi_feat_reg(1) \u0026 granule_feature)) {\n+\t\tpr_err(\"RMM does not support %luKB granules\\n\",\n+\t\t       PAGE_SIZE \u003e\u003e 10);\n+\t\treturn -ENXIO;\n+\t}\n+\n+\tstruct rmm_config *config __free(free_page) =\n+\t\t(struct rmm_config *)get_zeroed_page(GFP_KERNEL);\n+\n+\tif (!config) {\n+\t\tpr_err(\"Unable to allocate memory for RMM config\\n\");\n+\t\treturn -ENOMEM;\n+\t}\n+\n+\tconfig-\u003ermi_granule_size = granule_size;\n+\n+\t/*\n+\t * For now we set the tracking_region_size to 0 which is the only option\n+\t * for 4KB PAGE_SIZE (1GB for 4KB PAGE_SIZE, 32MB/512MB for 16KB/64KB).\n+\t * TODO: Support other tracking sizes via Kconfig option for other\n+\t * PAGE_SIZES\n+\t */\n+\tconfig-\u003etracking_region_size = 0;\n+\n+\tret = rmi_rmm_config_set(virt_to_phys(config));\n+\tif (ret) {\n+\t\tpr_err(\"RMM config set failed (%d)\\n\", ret);\n+\t\tret = -EINVAL;\n+\t}\n+\n+\treturn ret;\n+}\n+\n+/**\n+ * rmi_granule_tracking_get() - Get configuration of a Granule tracking region\n+ * @start: Base PA of the tracking region\n+ * @end: End of the PA region\n+ * @out_category: Memory category\n+ * @out_state: Tracking region state\n+ * @out_top: Top of the memory region\n+ *\n+ * Return: RMI return code\n+ */\n+static int rmi_granule_tracking_get(unsigned long start,\n+\t\t\t\t    unsigned long end,\n+\t\t\t\t    unsigned long *out_category,\n+\t\t\t\t    unsigned long *out_state,\n+\t\t\t\t    unsigned long *out_top)\n+{\n+\tstruct arm_smccc_1_2_regs regs = {\n+\t\tSMC_RMI_GRANULE_TRACKING_GET, start, end,\n+\t};\n+\n+\trmi_smccc_invoke(\u0026regs);\n+\n+\tif (regs.a0 != RMI_SUCCESS)\n+\t\treturn regs.a0;\n+\n+\tif (out_category)\n+\t\t*out_category = regs.a1;\n+\tif (out_state)\n+\t\t*out_state = regs.a2;\n+\tif (out_top)\n+\t\t*out_top = regs.a3;\n+\n+\treturn RMI_SUCCESS;\n+}\n+\n+/*\n+ * Make sure the area is tracked by RMM at FINE granularity.\n+ * We do not support changing the tracking yet.\n+ */\n+static int rmi_verify_memory_tracking(phys_addr_t start, phys_addr_t end)\n+{\n+\twhile (start \u003c end) {\n+\t\tunsigned long ret, category, state, next;\n+\n+\t\tret = rmi_granule_tracking_get(start, end, \u0026category, \u0026state, \u0026next);\n+\t\tif (ret != RMI_SUCCESS)\n+\t\t\treturn -ENOMEM;\n+\n+\t\tif (WARN_ON(next \u003c= start))\n+\t\t\treturn -ENXIO;\n+\n+\t\tif (state != RMI_TRACKING_FINE ||\n+\t\t    category != RMI_MEM_CATEGORY_CONVENTIONAL) {\n+\t\t\t/* TODO: Set granule tracking in this case */\n+\t\t\tpr_err(\"Granule tracking for region isn't fine/conventional: %llx-%lx\\n\",\n+\t\t\t       start, next);\n+\t\t\treturn -ENODEV;\n+\t\t}\n+\t\tstart = next;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+/*\n+ * rmi_gpt_info - Query the GPT info for the given PAR.\n+ * @start: Base of the physical address region\n+ * @end: Top of the physical address region\n+ * @out_top: Top of the physical address region for which\n+ *\t\tthe GPT @out_gpt_par_state is valid\n+ * @out_gpt_par_state: State of the GPT covered by [start, out_top)\n+ */\n+static long rmi_gpt_info(unsigned long start, unsigned long end,\n+\t\t\t unsigned long *out_top,\n+\t\t\t unsigned long *out_gpt_par_state)\n+{\n+\tstruct arm_smccc_1_2_regs regs = {\n+\t\tSMC_RMI_GPT_INFO, start, end,\n+\t};\n+\n+\trmi_smccc_invoke(\u0026regs);\n+\tif (regs.a0 != RMI_SUCCESS)\n+\t\treturn regs.a0;\n+\n+\tif (out_top)\n+\t\t*out_top = regs.a1;\n+\tif (out_gpt_par_state)\n+\t\t*out_gpt_par_state = regs.a2;\n+\n+\treturn RMI_SUCCESS;\n+}\n+\n+/*\n+ * We do not support creating L1 GPTs yet. So, make sure that\n+ * all the regions are managed by the firmware.\n+ */\n+static int rmi_verify_gpt_firmware_managed(phys_addr_t start, phys_addr_t end)\n+{\n+\tunsigned long l0gpt_sz;\n+\tunsigned long next, par_state;\n+\n+\tl0gpt_sz = 1UL \u003c\u003c (30 + FIELD_GET(RMI_FEATURE_REGISTER_1_L0GPTSZ,\n+\t\t\t\t\t  rmi_feat_reg(1)));\n+\tstart = ALIGN_DOWN(start, l0gpt_sz);\n+\tend = ALIGN(end, l0gpt_sz);\n+\n+\twhile (start \u003c end) {\n+\t\tlong ret = rmi_gpt_info(start, end, \u0026next, \u0026par_state);\n+\n+\t\tif (ret != RMI_SUCCESS)\n+\t\t\treturn -ENOMEM;\n+\n+\t\tif (WARN_ON(next \u003c= start))\n+\t\t\treturn -ENXIO;\n+\n+\t\tif (par_state != RMI_GPT_PAR_PLAT) {\n+\t\t\tpr_err(\"GPT for the region is not managed by firmware %llx-%lx\\n\",\n+\t\t\t\tstart, next);\n+\t\t\treturn -ENOMEM;\n+\t\t}\n+\t\tstart = next;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int rmi_prepare_memory(phys_addr_t start, phys_addr_t end)\n+{\n+\tint ret;\n+\n+\tif (start \u003e= end)\n+\t\treturn -EINVAL;\n+\n+\tret = rmi_verify_memory_tracking(start, end);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\treturn rmi_verify_gpt_firmware_managed(start, end);\n+}\n+\n+static int rmi_init_metadata(void)\n+{\n+\tphys_addr_t start, end;\n+\tstruct memblock_region *r;\n+\n+\tfor_each_mem_region(r) {\n+\t\tint ret;\n+\n+\t\t/* Firmware-reserved NOMAP regions are not usable system RAM */\n+\t\tif (memblock_is_nomap(r))\n+\t\t\tcontinue;\n+\n+\t\tstart = PAGE_ALIGN(r-\u003ebase);\n+\t\tend = PAGE_ALIGN_DOWN(r-\u003ebase + r-\u003esize);\n+\t\t/* Too small ? */\n+\t\tif (start \u003e= end)\n+\t\t\tcontinue;\n+\n+\t\tret = rmi_prepare_memory(start, end);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int rmi_memory_notifier(struct notifier_block *nb,\n+\t\t\t       unsigned long action, void *data)\n+{\n+\tstruct memory_notify *arg = data;\n+\tphys_addr_t start, end;\n+\tint ret;\n+\n+\tif (action != MEM_GOING_ONLINE)\n+\t\treturn NOTIFY_DONE;\n+\n+\tstart = PFN_PHYS(arg-\u003estart_pfn);\n+\tend = PFN_PHYS(arg-\u003estart_pfn + arg-\u003enr_pages);\n+\tret = rmi_prepare_memory(start, end);\n+\n+\treturn notifier_from_errno(ret);\n+}\n+\n+static struct notifier_block rmi_memory_nb = {\n+\t.notifier_call = rmi_memory_notifier,\n+};\n+\n+bool is_rmi_available(void)\n+{\n+\treturn arm64_rmi_is_available;\n+}\n+EXPORT_SYMBOL_GPL(is_rmi_available);\n+\n+static int rmi_init_memory(void)\n+{\n+\tint ret;\n+\n+\tret = rmi_init_metadata();\n+\tif (ret)\n+\t\treturn ret;\n+\n+\treturn register_memory_notifier(\u0026rmi_memory_nb);\n+}\n+\n+static int __init arm64_init_rmi(void)\n+{\n+\tint ret;\n+\n+\t/* If we can't agree on the RMI ABI version, don't proceed further */\n+\tret = rmi_check_version();\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tret = rmi_read_features();\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tret = rmi_configure();\n+\tif (ret)\n+\t\treturn ret;\n+\n+\t/* Activate the RMM */\n+\tstruct rmi_sro_state *sro __free(kfree) = kmalloc_obj(*sro);\n+\tif (!sro)\n+\t\treturn -ENOMEM;\n+\n+\tret = rmi_sro_memxfer_cmd(sro, GFP_KERNEL, SMC_RMI_RMM_ACTIVATE);\n+\tif (ret) {\n+\t\tpr_err(\"RMM activate failed (%d)\\n\", ret);\n+\t\tret = ret \u003c 0 ? ret : -ENXIO;\n+\t\treturn ret;\n+\t}\n+\n+\tret = rmi_init_memory();\n+\tif (ret) {\n+\t\t/* Deactivate the RMM */\n+\t\tWARN_ON(rmi_sro_memxfer_cmd(sro, GFP_KERNEL, SMC_RMI_RMM_DEACTIVATE));\n+\t\treturn ret;\n+\t}\n+\n+\tarm64_rmi_is_available = true;\n+\tpr_info(\"RMI configured\\n\");\n+\treturn 0;\n+}\n+\n+/*\n+ * Note arm64_init_rmi() must be called before kvm_init_rmi() otherwise KVM\n+ * will not support realm guests. subsys_initcall() is called before\n+ * module_init() (used for KVM) so this is OK.\n+ */\n+subsys_initcall(arm64_init_rmi);\ndiff --git a/include/linux/arm-rmi-cmds.h b/include/linux/arm-rmi-cmds.h\nnew file mode 100644\nindex 0000000000000..e03dd8316695d\n--- /dev/null\n+++ b/include/linux/arm-rmi-cmds.h\n@@ -0,0 +1,558 @@\n+/* SPDX-License-Identifier: GPL-2.0 */\n+/*\n+ * Copyright (C) 2026 ARM Ltd.\n+ */\n+\n+#ifndef __LINUX_ARM_RMI_CMDS_H_\n+#define __LINUX_ARM_RMI_CMDS_H_\n+\n+#include \u003clinux/arm-smccc-rmi.h\u003e\n+#include \u003clinux/bug.h\u003e\n+#include \u003clinux/gfp.h\u003e\n+#include \u003clinux/processor.h\u003e\n+#include \u003clinux/string.h\u003e\n+#include \u003clinux/types.h\u003e\n+\n+#define RMI_MAX_ADDR_LIST\t256\n+\n+struct rmi_sro_state {\n+\tstruct arm_smccc_1_2_regs regs;\n+\tint addr_count;\n+\tunsigned long addr_list[RMI_MAX_ADDR_LIST];\n+};\n+\n+#define RMM_BLOCKED_RETRY_COUNT\t\t2\n+/*\n+ * rmi_smccc_invoke: Invoke the RMI call and return the results, retrying the\n+ * command when status is RMI_BUSY. If we encounter RMI_BLOCKED, we retry\n+ * it one more time before we give up. The caller is supposed to handle the\n+ * result and reissue if required.\n+ *\n+ * We don't expect to see RMI_BLOCKED on a practical system, except when\n+ * there are parallel requests that results in long standing operation,\n+ * with one blocking the other.\n+ *\n+ * @regs: Input parameters filled in. Updated with the ouptput results\n+ * after the call.\n+ */\n+static inline void rmi_smccc_invoke(struct arm_smccc_1_2_regs *regs)\n+{\n+\tstruct arm_smccc_1_2_regs args = *regs;\n+\tlong status;\n+\tint i = 0;\n+\n+\twhile (i \u003c RMM_BLOCKED_RETRY_COUNT) {\n+\t\tarm_smccc_1_2_invoke(\u0026args, regs);\n+\n+\t\tstatus = RMI_RESULT_STATUS(regs-\u003ea0);\n+\t\tif (status != RMI_BUSY \u0026\u0026 status != RMI_BLOCKED)\n+\t\t\tbreak;\n+\t\tif (status == RMI_BLOCKED)\n+\t\t\ti++;\n+\t\tcpu_relax();\n+\t}\n+}\n+\n+unsigned long rmi_feat_reg(unsigned long index);\n+\n+int rmi_delegate_range(phys_addr_t phys, unsigned long size,\n+\t\t       phys_addr_t *out_phys);\n+int rmi_undelegate_range(phys_addr_t phys, unsigned long size);\n+int rmi_free_delegated_page(phys_addr_t phys);\n+\n+static inline int rmi_delegate_page(phys_addr_t phys)\n+{\n+\treturn rmi_delegate_range(phys, PAGE_SIZE, NULL);\n+}\n+\n+static inline int rmi_undelegate_page(phys_addr_t phys)\n+{\n+\treturn rmi_undelegate_range(phys, PAGE_SIZE);\n+}\n+\n+bool is_rmi_available(void);\n+\n+long rmi_sro_memxfer_execute(struct rmi_sro_state *sro, gfp_t gfp);\n+void rmi_sro_free(struct rmi_sro_state *sro);\n+long rmi_sro_execute(struct arm_smccc_1_2_regs *regs);\n+\n+/*\n+ * Resetting the addr_count is sufficient to ignore the addr_list contents.\n+ */\n+#define rmi_sro_memxfer_cmd(sro, gfp, ...) ({\t\t\t\t\\\n+\tstruct rmi_sro_state *__sro = (sro);\t\t\t\t\\\n+\t__sro-\u003eaddr_count = 0;\t\t\t\t\t\t\\\n+\t__sro-\u003eregs = (struct arm_smccc_1_2_regs){ __VA_ARGS__ };\t\\\n+\tlong __ret = rmi_sro_memxfer_execute(__sro, gfp);\t\t\\\n+\trmi_sro_free(__sro);\t\t\t\t\t\t\\\n+\t__ret;\t\t\t\t\t\t\t\t\\\n+})\n+\n+/**\n+ * rmi_rtt_data_map_init() - Create a mapping at protected IPA, copying contents\n+ *\t\t\t     from a given non-secure source granule.\n+ * @rd: PA of the RD\n+ * @data: PA of the target granule mapped in the guest\n+ * @ipa: IPA at which the granule @data will be mapped in the guest\n+ * @src: PA of the source granule with contents\n+ * @flags: RMI_MEASURE_CONTENT if the contents should be measured\n+ *\n+ * Create a mapping from Protected IPA space to conventional memory, copying\n+ * contents from a Non-secure Granule provided by the caller.\n+ *\n+ * Return: 0 on success, positive RMI result code or negative Linux error code\n+ */\n+static inline long rmi_rtt_data_map_init(unsigned long rd, unsigned long data,\n+\t\t\t\t\t unsigned long ipa, unsigned long src,\n+\t\t\t\t\t unsigned long flags)\n+{\n+\tstruct arm_smccc_1_2_regs regs = {\n+\t\tSMC_RMI_RTT_DATA_MAP_INIT, rd, data, ipa, src, flags\n+\t};\n+\n+\treturn rmi_sro_execute(\u0026regs);\n+}\n+\n+/**\n+ * rmi_rtt_data_map() - Create mappings in protected IPA range with unknown contents\n+ * @rd: PA of the RD\n+ * @base: Base of the target IPA range\n+ * @top: Top of the target IPA range\n+ * @flags: Flags\n+ * @oaddr: Output address set descriptor\n+ * @out_top: Top address of range which was processed.\n+ *\n+ * Return: 0 on success, positive RMI result code or negative Linux error code\n+ */\n+static inline long rmi_rtt_data_map(unsigned long rd,\n+\t\t\t\t    unsigned long base,\n+\t\t\t\t    unsigned long top,\n+\t\t\t\t    unsigned long flags,\n+\t\t\t\t    unsigned long oaddr,\n+\t\t\t\t    unsigned long *out_top)\n+{\n+\tstruct arm_smccc_1_2_regs regs = {\n+\t\tSMC_RMI_RTT_DATA_MAP, rd, base, top, flags, oaddr\n+\t};\n+\tlong ret;\n+\n+\tret = rmi_sro_execute(\u0026regs);\n+\n+\tif (ret == RMI_SUCCESS \u0026\u0026 out_top)\n+\t\t*out_top = regs.a1;\n+\n+\treturn ret;\n+}\n+\n+/**\n+ * rmi_rtt_data_unmap() - Remove mappings to conventional memory at a protected\n+ *\t\t\t  IPA range\n+ * @rd: PA of the RD\n+ * @base: Base of the target IPA range\n+ * @top: Top of the target IPA range\n+ * @flags: Flags\n+ * @oaddr: Output address set descriptor\n+ * @out_top: Returns top IPA of range which has been unmapped\n+ * @out_range: Output address range\n+ * @out_count: Number of entries in output address list\n+ *\n+ * Removes mappings to convention memory with a target Protected IPA range.\n+ *\n+ * Return: 0 on success, positive RMI result code or negative Linux error code\n+ */\n+static inline long rmi_rtt_data_unmap(unsigned long rd,\n+\t\t\t\t      unsigned long base,\n+\t\t\t\t      unsigned long top,\n+\t\t\t\t      unsigned long flags,\n+\t\t\t\t      unsigned long oaddr,\n+\t\t\t\t      unsigned long *out_top,\n+\t\t\t\t      unsigned long *out_range,\n+\t\t\t\t      unsigned long *out_count)\n+{\n+\tstruct arm_smccc_1_2_regs regs = {\n+\t\tSMC_RMI_RTT_DATA_UNMAP, rd, base, top, flags, oaddr\n+\t};\n+\tlong ret;\n+\n+\tret = rmi_sro_execute(\u0026regs);\n+\n+\tif (ret != RMI_SUCCESS)\n+\t\treturn ret;\n+\n+\tif (out_top)\n+\t\t*out_top = regs.a1;\n+\tif (out_range)\n+\t\t*out_range = regs.a2;\n+\tif (out_count)\n+\t\t*out_count = regs.a3;\n+\n+\treturn RMI_SUCCESS;\n+}\n+\n+/**\n+ * rmi_psci_complete() - Complete pending PSCI command\n+ * @calling_rec: PA of the calling REC\n+ * @status: Status of the PSCI request\n+ *\n+ * Completes a pending PSCI command.\n+ *\n+ * Return: RMI return code\n+ */\n+static inline long rmi_psci_complete(unsigned long calling_rec,\n+\t\t\t\t     unsigned long status)\n+{\n+\tstruct arm_smccc_1_2_regs regs = {\n+\t\tSMC_RMI_PSCI_COMPLETE, calling_rec, status,\n+\t};\n+\n+\trmi_smccc_invoke(\u0026regs);\n+\treturn regs.a0;\n+}\n+\n+/**\n+ * rmi_realm_activate() - Activate a realm\n+ * @rd: PA of the RD\n+ *\n+ * Mark a realm as Active, signalling that creation is completed, allowing\n+ * execution of the realm.\n+ *\n+ * Return: RMI return code\n+ */\n+static inline long rmi_realm_activate(unsigned long rd)\n+{\n+\tstruct arm_smccc_1_2_regs regs = {\n+\t\tSMC_RMI_REALM_ACTIVATE, rd,\n+\t};\n+\n+\trmi_smccc_invoke(\u0026regs);\n+\treturn regs.a0;\n+}\n+\n+/**\n+ * rmi_realm_create() - Create a realm\n+ * @rd: PA of the RD\n+ * @params: PA of realm parameters\n+ * @sro: Preallocated SRO context\n+ *\n+ * Create a new realm using the given parameters.\n+ *\n+ * Return: 0 on success, positive RMI result code or negative Linux error code\n+ */\n+static inline long rmi_realm_create(unsigned long rd, unsigned long params,\n+\t\t\t\t    struct rmi_sro_state *sro)\n+{\n+\treturn rmi_sro_memxfer_cmd(sro, GFP_KERNEL,\n+\t\t\t\t   SMC_RMI_REALM_CREATE, rd, params);\n+}\n+\n+/**\n+ * rmi_realm_terminate() - Terminate a realm\n+ * @rd: PA of the RD\n+ * @sro: Preallocated SRO context\n+ *\n+ * Terminates a realm, moving it into a ZOMBIE state\n+ *\n+ * Return: 0 on success, positive RMI result code or negative Linux error code\n+ */\n+static inline long rmi_realm_terminate(unsigned long rd,\n+\t\t\t\t       struct rmi_sro_state *sro)\n+{\n+\treturn rmi_sro_memxfer_cmd(sro, GFP_KERNEL,\n+\t\t\t\t   SMC_RMI_REALM_TERMINATE, rd);\n+}\n+\n+/**\n+ * rmi_realm_destroy() - Destroy a realm\n+ * @rd: PA of the RD\n+ * @sro: Preallocated SRO context\n+ *\n+ * Destroys a realm, all objects belonging to the realm must be destroyed first.\n+ *\n+ * Return: 0 on success, positive RMI result code or negative Linux error code\n+ */\n+static inline long rmi_realm_destroy(unsigned long rd,\n+\t\t\t\t     struct rmi_sro_state *sro)\n+{\n+\treturn rmi_sro_memxfer_cmd(sro, GFP_KERNEL,\n+\t\t\t\t   SMC_RMI_REALM_DESTROY, rd);\n+}\n+\n+/**\n+ * rmi_rec_create() - Create a REC\n+ * @rd: PA of the RD\n+ * @rec: PA of the target REC\n+ * @params: PA of REC parameters\n+ * @sro: Allocated SRO context to be used\n+ *\n+ * Create a REC using the parameters specified in the struct rec_params pointed\n+ * to by @params.\n+ *\n+ * Return: 0 on success, positive RMI result code or negative Linux error code\n+ */\n+static inline long rmi_rec_create(unsigned long rd,\n+\t\t\t\t  unsigned long rec,\n+\t\t\t\t  unsigned long params,\n+\t\t\t\t  struct rmi_sro_state *sro)\n+{\n+\tlong ret;\n+\n+\tsro-\u003eaddr_count = 0;\n+\tsro-\u003eregs = (struct arm_smccc_1_2_regs) {\n+\t\tSMC_RMI_REC_CREATE, rd, rec, params\n+\t};\n+\tret = rmi_sro_memxfer_execute(sro, GFP_KERNEL);\n+\trmi_sro_free(sro);\n+\n+\treturn ret;\n+}\n+\n+/**\n+ * rmi_rec_destroy() - Destroy a REC\n+ * @rec: PA of the target REC\n+ * @sro: Allocated SRO context to be used\n+ *\n+ * Destroys a REC. The REC must not be running.\n+ *\n+ * Return: 0 on success, positive RMI result code or negative Linux error code\n+ */\n+static inline long rmi_rec_destroy(unsigned long rec,\n+\t\t\t\t   struct rmi_sro_state *sro)\n+{\n+\treturn rmi_sro_memxfer_cmd(sro, GFP_KERNEL, SMC_RMI_REC_DESTROY, rec);\n+}\n+\n+/**\n+ * rmi_rec_enter() - Enter a REC\n+ * @rec: PA of the target REC\n+ * @run_ptr: PA of RecRun structure\n+ *\n+ * Starts (or continues) execution within a REC.\n+ *\n+ * Return: RMI return code\n+ */\n+static inline long rmi_rec_enter(unsigned long rec, unsigned long run_ptr)\n+{\n+\tstruct arm_smccc_1_2_regs regs = {\n+\t\tSMC_RMI_REC_ENTER, rec, run_ptr,\n+\t};\n+\n+\trmi_smccc_invoke(\u0026regs);\n+\treturn regs.a0;\n+}\n+\n+/**\n+ * rmi_rtt_create() - Creates an RTT\n+ * @rd: PA of the RD\n+ * @rtt: PA of the target RTT\n+ * @ipa: Base of the IPA range described by the RTT\n+ * @level: Depth of the RTT within the tree\n+ *\n+ * Creates an RTT (Realm Translation Table) at the specified level for the\n+ * translation of the specified address within the realm.\n+ *\n+ * Return: 0 on success, positive RMI result code or negative Linux error code\n+ */\n+static inline long rmi_rtt_create(unsigned long rd, unsigned long rtt,\n+\t\t\t\t  unsigned long ipa, long level)\n+{\n+\tstruct arm_smccc_1_2_regs regs = {\n+\t\tSMC_RMI_RTT_CREATE, rd, rtt, ipa, level\n+\t};\n+\n+\treturn rmi_sro_execute(\u0026regs);\n+}\n+\n+/**\n+ * rmi_rtt_destroy() - Destroy an RTT\n+ * @rd: PA of the RD\n+ * @ipa: Base of the IPA range described by the RTT\n+ * @level: RTT level\n+ * @out_rtt: Pointer to write the PA of the RTT which was destroyed\n+ * @out_top: Pointer to write the top IPA of non-live RTT entries, from entry\n+ * at which the RTT walk terminated.\n+ *\n+ * Destroys an RTT. The RTT must be non-live, i.e. none of the entries in the\n+ * table are in ASSIGNED or TABLE state.\n+ *\n+ * Return: 0 on success, positive RMI result code or negative Linux error code.\n+ */\n+static inline long rmi_rtt_destroy(unsigned long rd,\n+\t\t\t\t   unsigned long ipa,\n+\t\t\t\t   long level,\n+\t\t\t\t   unsigned long *out_rtt,\n+\t\t\t\t   unsigned long *out_top)\n+{\n+\tstruct arm_smccc_1_2_regs regs = {\n+\t\tSMC_RMI_RTT_DESTROY, rd, ipa, level\n+\t};\n+\tlong ret = rmi_sro_execute(\u0026regs);\n+\n+\tswitch (RMI_RESULT_STATUS(ret)) {\n+\tcase RMI_SUCCESS:\n+\t\tif (out_rtt)\n+\t\t\t*out_rtt = regs.a1;\n+\t\tfallthrough;\n+\tcase RMI_ERROR_RTT:\n+\t\tif (out_top)\n+\t\t\t*out_top = regs.a2;\n+\t\tbreak;\n+\tdefault:\n+\t\tbreak;\n+\t}\n+\n+\treturn ret;\n+}\n+\n+/**\n+ * rmi_rtt_fold() - Fold an RTT\n+ * @rd: PA of the RD\n+ * @ipa: Base of the IPA range described by the RTT\n+ * @level: Depth of the RTT within the tree\n+ * @out_rtt: Pointer to write the PA of the RTT which was destroyed\n+ *\n+ * Folds an RTT. If all entries with the RTT are 'homogeneous' the RTT can be\n+ * folded into the parent and the RTT destroyed.\n+ *\n+ * Return: 0 on success, positive RMI result code or negative Linux error code\n+ */\n+static inline long rmi_rtt_fold(unsigned long rd, unsigned long ipa,\n+\t\t\t\tlong level, unsigned long *out_rtt)\n+{\n+\tstruct arm_smccc_1_2_regs regs = {\n+\t\tSMC_RMI_RTT_FOLD, rd, ipa, level\n+\t};\n+\tlong ret = rmi_sro_execute(\u0026regs);\n+\n+\tif (ret == RMI_SUCCESS \u0026\u0026 out_rtt)\n+\t\t*out_rtt = regs.a1;\n+\n+\treturn ret;\n+}\n+\n+/**\n+ * rmi_rtt_init_ripas() - Set RIPAS for new realm\n+ * @rd: PA of the RD\n+ * @base: Base of target IPA region\n+ * @top: Top of target IPA region\n+ * @out_top: Top IPA of range whose RIPAS was modified\n+ *\n+ * Sets the RIPAS of a target IPA range to RAM, for a realm in the NEW state.\n+ *\n+ * Return: 0 on success, positive RMI result code or negative Linux error code\n+ */\n+static inline long rmi_rtt_init_ripas(unsigned long rd, unsigned long base,\n+\t\t\t\t      unsigned long top, unsigned long *out_top)\n+{\n+\tstruct arm_smccc_1_2_regs regs = {\n+\t\tSMC_RMI_RTT_INIT_RIPAS, rd, base, top\n+\t};\n+\tlong ret = rmi_sro_execute(\u0026regs);\n+\n+\tif (ret == RMI_SUCCESS \u0026\u0026 out_top)\n+\t\t*out_top = regs.a1;\n+\n+\treturn ret;\n+}\n+\n+/**\n+ * rmi_rtt_unprot_map() - Map unprotected granules into a realm\n+ * @rd: PA of the RD\n+ * @base: Base IPA of the mapping\n+ * @top: Top of the target IPA range\n+ * @flags: Flags\n+ * @oaddr: Output address set descriptor\n+ * @out_top: Top IPA of range which has been mapped\n+ *\n+ * Create mappings to memory within a target unprotected IPA range.\n+ *\n+ * Return: 0 on success, positive RMI result code or negative Linux error code\n+ */\n+static inline long rmi_rtt_unprot_map(unsigned long rd,\n+\t\t\t\t      unsigned long base,\n+\t\t\t\t      unsigned long top,\n+\t\t\t\t      unsigned long flags,\n+\t\t\t\t      unsigned long oaddr,\n+\t\t\t\t      unsigned long *out_top)\n+{\n+\tstruct arm_smccc_1_2_regs regs = {\n+\t\tSMC_RMI_RTT_UNPROT_MAP, rd, base, top, flags, oaddr\n+\t};\n+\tlong ret = rmi_sro_execute(\u0026regs);\n+\n+\tif (ret == RMI_SUCCESS \u0026\u0026 out_top)\n+\t\t*out_top = regs.a1;\n+\n+\treturn ret;\n+}\n+\n+/**\n+ * rmi_rtt_set_ripas() - Set RIPAS for an running realm\n+ * @rd: PA of the RD\n+ * @rec: PA of the REC making the request\n+ * @base: Base of target IPA region\n+ * @top: Top of target IPA region\n+ * @out_top: Pointer to write top IPA of range whose RIPAS was modified\n+ *\n+ * Completes a request made by the realm to change the RIPAS of a target IPA\n+ * range.\n+ *\n+ * Return: 0 on success, positive RMI result code or negative Linux error code\n+ */\n+static inline long rmi_rtt_set_ripas(unsigned long rd, unsigned long rec,\n+\t\t\t\t     unsigned long base, unsigned long top,\n+\t\t\t\t     unsigned long *out_top)\n+{\n+\tstruct arm_smccc_1_2_regs regs = {\n+\t\tSMC_RMI_RTT_SET_RIPAS, rd, rec, base, top\n+\t};\n+\tlong ret = rmi_sro_execute(\u0026regs);\n+\n+\tif (ret == RMI_SUCCESS \u0026\u0026 out_top)\n+\t\t*out_top = regs.a1;\n+\n+\treturn ret;\n+}\n+\n+/**\n+ * rmi_rtt_unprot_unmap() - Remove mappings within an unprotected IPA range\n+ * @rd: PA of the RD\n+ * @base: Base IPA of the mapping\n+ * @top: Top of the target IPA range\n+ * @flags: Flags\n+ * @oaddr: Output address set descriptor\n+ * @out_top: Top IPA which has been unmapped\n+ * @out_range: Output address range\n+ * @out_count: Number of entries in output address list\n+ *\n+ * Removes mappings to memory within a target unprotected IPA range.\n+ *\n+ * Return: 0 on success, positive RMI result code or negative Linux error code\n+ */\n+static inline long rmi_rtt_unprot_unmap(unsigned long rd,\n+\t\t\t\t\tunsigned long base,\n+\t\t\t\t\tunsigned long top,\n+\t\t\t\t\tunsigned long flags,\n+\t\t\t\t\tunsigned long oaddr,\n+\t\t\t\t\tunsigned long *out_top,\n+\t\t\t\t\tunsigned long *out_range,\n+\t\t\t\t\tunsigned long *out_count)\n+{\n+\tstruct arm_smccc_1_2_regs regs = {\n+\t\tSMC_RMI_RTT_UNPROT_UNMAP, rd, base, top, flags, oaddr\n+\t};\n+\tlong ret = rmi_sro_execute(\u0026regs);\n+\n+\tif (ret != RMI_SUCCESS)\n+\t\treturn ret;\n+\n+\tif (out_top)\n+\t\t*out_top = regs.a1;\n+\tif (out_range)\n+\t\t*out_range = regs.a2;\n+\tif (out_count)\n+\t\t*out_count = regs.a3;\n+\n+\treturn RMI_SUCCESS;\n+}\n+\n+#endif\ndiff --git a/include/linux/arm-smccc-rmi.h b/include/linux/arm-smccc-rmi.h\nnew file mode 100644\nindex 0000000000000..3b2a3ae6d81cd\n--- /dev/null\n+++ b/include/linux/arm-smccc-rmi.h\n@@ -0,0 +1,505 @@\n+/* SPDX-License-Identifier: GPL-2.0 */\n+/*\n+ * Copyright (C) 2023-2026 ARM Ltd.\n+ *\n+ * The values and structures in this file are from the Realm Management Monitor\n+ * specification (DEN0137) version 2.0-bet3:\n+ * https://developer.arm.com/documentation/den0137/2-0bet3/\n+ */\n+\n+#ifndef __LINUX_ARM_SMCCC_RMI_H_\n+#define __LINUX_ARM_SMCCC_RMI_H_\n+\n+#include \u003clinux/arm-smccc.h\u003e\n+#include \u003clinux/bitfield.h\u003e\n+#include \u003clinux/bits.h\u003e\n+#include \u003clinux/build_bug.h\u003e\n+#include \u003clinux/sizes.h\u003e\n+\n+#include \u003casm/page.h\u003e\n+\n+#define SMC_RMI_CALL(func)\t\t\t\t\\\n+\tARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL,\t\t\\\n+\t\t\t   ARM_SMCCC_SMC_64,\t\t\\\n+\t\t\t   ARM_SMCCC_OWNER_STANDARD,\t\\\n+\t\t\t   (func))\n+\n+#define SMC_RMI_VERSION\t\t\t\tSMC_RMI_CALL(0x0150)\n+\n+#define SMC_RMI_RTT_DATA_MAP_INIT\t\tSMC_RMI_CALL(0x0153)\n+\n+#define SMC_RMI_REALM_ACTIVATE\t\t\tSMC_RMI_CALL(0x0157)\n+#define SMC_RMI_REALM_CREATE\t\t\tSMC_RMI_CALL(0x0158)\n+#define SMC_RMI_REALM_DESTROY\t\t\tSMC_RMI_CALL(0x0159)\n+#define SMC_RMI_REC_CREATE\t\t\tSMC_RMI_CALL(0x015a)\n+#define SMC_RMI_REC_DESTROY\t\t\tSMC_RMI_CALL(0x015b)\n+#define SMC_RMI_REC_ENTER\t\t\tSMC_RMI_CALL(0x015c)\n+#define SMC_RMI_RTT_CREATE\t\t\tSMC_RMI_CALL(0x015d)\n+#define SMC_RMI_RTT_DESTROY\t\t\tSMC_RMI_CALL(0x015e)\n+\n+#define SMC_RMI_RTT_READ_ENTRY\t\t\tSMC_RMI_CALL(0x0161)\n+\n+#define SMC_RMI_RTT_DEV_VALIDATE\t\tSMC_RMI_CALL(0x0163)\n+#define SMC_RMI_PSCI_COMPLETE\t\t\tSMC_RMI_CALL(0x0164)\n+#define SMC_RMI_FEATURES\t\t\tSMC_RMI_CALL(0x0165)\n+#define SMC_RMI_RTT_FOLD\t\t\tSMC_RMI_CALL(0x0166)\n+\n+#define SMC_RMI_RTT_INIT_RIPAS\t\t\tSMC_RMI_CALL(0x0168)\n+#define SMC_RMI_RTT_SET_RIPAS\t\t\tSMC_RMI_CALL(0x0169)\n+#define SMC_RMI_VSMMU_CREATE\t\t\tSMC_RMI_CALL(0x016a)\n+#define SMC_RMI_VSMMU_DESTROY\t\t\tSMC_RMI_CALL(0x016b)\n+\n+#define SMC_RMI_RMM_CONFIG_SET\t\t\tSMC_RMI_CALL(0x016e)\n+#define SMC_RMI_PSMMU_IRQ_NOTIFY\t\tSMC_RMI_CALL(0x016f)\n+#define SMC_RMI_ATTEST_PLAT_TOKEN_REFRESH\tSMC_RMI_CALL(0x0170)\n+\n+#define SMC_RMI_PDEV_ABORT\t\t\tSMC_RMI_CALL(0x0174)\n+#define SMC_RMI_PDEV_COMMUNICATE\t\tSMC_RMI_CALL(0x0175)\n+#define SMC_RMI_PDEV_CREATE\t\t\tSMC_RMI_CALL(0x0176)\n+#define SMC_RMI_PDEV_DESTROY\t\t\tSMC_RMI_CALL(0x0177)\n+#define SMC_RMI_PDEV_GET_STATE\t\t\tSMC_RMI_CALL(0x0178)\n+\n+#define SMC_RMI_PDEV_STREAM_KEY_REFRESH\t\tSMC_RMI_CALL(0x017a)\n+#define SMC_RMI_PDEV_SET_PUBKEY\t\t\tSMC_RMI_CALL(0x017b)\n+#define SMC_RMI_PDEV_STOP\t\t\tSMC_RMI_CALL(0x017c)\n+#define SMC_RMI_RTT_AUX_CREATE\t\t\tSMC_RMI_CALL(0x017d)\n+#define SMC_RMI_RTT_AUX_DESTROY\t\t\tSMC_RMI_CALL(0x017e)\n+#define SMC_RMI_RTT_AUX_FOLD\t\t\tSMC_RMI_CALL(0x017f)\n+\n+#define SMC_RMI_VDEV_ABORT\t\t\tSMC_RMI_CALL(0x0185)\n+#define SMC_RMI_VDEV_COMMUNICATE\t\tSMC_RMI_CALL(0x0186)\n+#define SMC_RMI_VDEV_CREATE\t\t\tSMC_RMI_CALL(0x0187)\n+#define SMC_RMI_VDEV_DESTROY\t\t\tSMC_RMI_CALL(0x0188)\n+#define SMC_RMI_VDEV_GET_STATE\t\t\tSMC_RMI_CALL(0x0189)\n+#define SMC_RMI_VDEV_UNLOCK\t\t\tSMC_RMI_CALL(0x018a)\n+#define SMC_RMI_RTT_SET_S2AP\t\t\tSMC_RMI_CALL(0x018b)\n+\n+#define SMC_RMI_VDEV_GET_INTERFACE_REPORT\tSMC_RMI_CALL(0x01d0)\n+#define SMC_RMI_VDEV_GET_MEASUREMENTS\t\tSMC_RMI_CALL(0x01d1)\n+#define SMC_RMI_VDEV_LOCK\t\t\tSMC_RMI_CALL(0x01d2)\n+#define SMC_RMI_VDEV_START\t\t\tSMC_RMI_CALL(0x01d3)\n+\n+#define SMC_RMI_VSMMU_EVENT_HANDLE\t\tSMC_RMI_CALL(0x01d6)\n+#define SMC_RMI_PSMMU_ACTIVATE\t\t\tSMC_RMI_CALL(0x01d7)\n+#define SMC_RMI_PSMMU_DEACTIVATE\t\tSMC_RMI_CALL(0x01d8)\n+\n+#define SMC_RMI_PSMMU_ST_L2_CREATE\t\tSMC_RMI_CALL(0x01db)\n+#define SMC_RMI_PSMMU_ST_L2_DESTROY\t\tSMC_RMI_CALL(0x01dc)\n+#define SMC_RMI_DPT_L0_CREATE\t\t\tSMC_RMI_CALL(0x01dd)\n+#define SMC_RMI_DPT_L0_DESTROY\t\t\tSMC_RMI_CALL(0x01de)\n+#define SMC_RMI_DPT_L1_CREATE\t\t\tSMC_RMI_CALL(0x01df)\n+#define SMC_RMI_DPT_L1_DESTROY\t\t\tSMC_RMI_CALL(0x01e0)\n+#define SMC_RMI_GRANULE_TRACKING_GET\t\tSMC_RMI_CALL(0x01e1)\n+\n+#define SMC_RMI_GRANULE_TRACKING_SET\t\tSMC_RMI_CALL(0x01e3)\n+\n+#define SMC_RMI_RMM_CONFIG_GET\t\t\tSMC_RMI_CALL(0x01ec)\n+\n+#define SMC_RMI_RMM_STATE_GET\t\t\tSMC_RMI_CALL(0x01ee)\n+\n+#define SMC_RMI_PSMMU_EVENT_CONSUME\t\tSMC_RMI_CALL(0x01f0)\n+#define SMC_RMI_GRANULE_RANGE_DELEGATE\t\tSMC_RMI_CALL(0x01f1)\n+#define SMC_RMI_GRANULE_RANGE_UNDELEGATE\tSMC_RMI_CALL(0x01f2)\n+#define SMC_RMI_GPT_L1_CREATE\t\t\tSMC_RMI_CALL(0x01f3)\n+#define SMC_RMI_GPT_L1_DESTROY\t\t\tSMC_RMI_CALL(0x01f4)\n+#define SMC_RMI_RTT_DATA_MAP\t\t\tSMC_RMI_CALL(0x01f5)\n+#define SMC_RMI_RTT_DATA_UNMAP\t\t\tSMC_RMI_CALL(0x01f6)\n+#define SMC_RMI_RTT_DEV_MAP\t\t\tSMC_RMI_CALL(0x01f7)\n+#define SMC_RMI_RTT_DEV_UNMAP\t\t\tSMC_RMI_CALL(0x01f8)\n+#define SMC_RMI_RTT_ARCH_DEV_MAP\t\tSMC_RMI_CALL(0x01f9)\n+#define SMC_RMI_RTT_ARCH_DEV_UNMAP\t\tSMC_RMI_CALL(0x01fa)\n+#define SMC_RMI_RTT_UNPROT_MAP\t\t\tSMC_RMI_CALL(0x01fb)\n+#define SMC_RMI_RTT_UNPROT_UNMAP\t\tSMC_RMI_CALL(0x01fc)\n+#define SMC_RMI_RTT_AUX_PROT_MAP\t\tSMC_RMI_CALL(0x01fd)\n+#define SMC_RMI_RTT_AUX_PROT_UNMAP\t\tSMC_RMI_CALL(0x01fe)\n+#define SMC_RMI_RTT_AUX_UNPROT_MAP\t\tSMC_RMI_CALL(0x01ff)\n+#define SMC_RMI_RTT_AUX_UNPROT_UNMAP\t\tSMC_RMI_CALL(0x0200)\n+#define SMC_RMI_REALM_TERMINATE\t\t\tSMC_RMI_CALL(0x0201)\n+#define SMC_RMI_RMM_ACTIVATE\t\t\tSMC_RMI_CALL(0x0202)\n+#define SMC_RMI_OP_CONTINUE\t\t\tSMC_RMI_CALL(0x0203)\n+#define SMC_RMI_PDEV_STREAM_CONNECT\t\tSMC_RMI_CALL(0x0204)\n+#define SMC_RMI_PDEV_STREAM_DISCONNECT\t\tSMC_RMI_CALL(0x0205)\n+#define SMC_RMI_PDEV_STREAM_COMPLETE\t\tSMC_RMI_CALL(0x0206)\n+#define SMC_RMI_PDEV_STREAM_KEY_PURGE\t\tSMC_RMI_CALL(0x0207)\n+#define SMC_RMI_OP_MEM_DONATE\t\t\tSMC_RMI_CALL(0x0208)\n+#define SMC_RMI_OP_MEM_RECLAIM\t\t\tSMC_RMI_CALL(0x0209)\n+#define SMC_RMI_OP_CANCEL\t\t\tSMC_RMI_CALL(0x020a)\n+#define SMC_RMI_VSMMU_FEATURES\t\t\tSMC_RMI_CALL(0x020b)\n+#define SMC_RMI_VSMMU_CMD_GET\t\t\tSMC_RMI_CALL(0x020c)\n+#define SMC_RMI_VSMMU_CMD_COMPLETE\t\tSMC_RMI_CALL(0x020d)\n+#define SMC_RMI_PSMMU_INFO\t\t\tSMC_RMI_CALL(0x020e)\n+#define SMC_RMI_RMM_DEACTIVATE\t\t\tSMC_RMI_CALL(0x020f)\n+#define SMC_RMI_PDEV_STREAM_INFO\t\tSMC_RMI_CALL(0x0210)\n+#define SMC_RMI_GPT_INFO\t\t\tSMC_RMI_CALL(0x0211)\n+\n+#define RMI_ABI_MAJOR_VERSION\t2\n+#define RMI_ABI_MINOR_VERSION\t0\n+\n+#define RMI_ABI_VERSION_MAJOR_MASK\t\tGENMASK(30, 16)\n+#define RMI_ABI_VERSION_MINOR_MASK\t\tGENMASK(15, 0)\n+\n+#define RMI_ABI_VERSION_GET_MAJOR(v)\tFIELD_GET(RMI_ABI_VERSION_MAJOR_MASK, (v))\n+#define RMI_ABI_VERSION_GET_MINOR(v)\tFIELD_GET(RMI_ABI_VERSION_MINOR_MASK, (v))\n+#define RMI_ABI_VERSION(major, minor)\t\t\t\t\\\n+\t(FIELD_PREP(RMI_ABI_VERSION_MAJOR_MASK, (major)) |\t\\\n+\t FIELD_PREP(RMI_ABI_VERSION_MINOR_MASK, (minor)))\n+\n+/* RmiResult Type definitions */\n+#define RMI_RESULT_STATUS_MASK\t\tGENMASK(7, 0)\n+/* RmiResultDataLevel field */\n+#define RMI_RESULT_DATA_LEVEL_MASK\tGENMASK(15, 8)\n+/* RmiResultDataIncomplete fields */\n+#define RMI_RESULT_MEMREQ_MASK\t\tGENMASK(9, 8)\n+#define RMI_RESULT_CAN_CANCEL_MASK\tBIT(10)\n+\n+#define RMI_RESULT_STATUS(ret)\t\tFIELD_GET(RMI_RESULT_STATUS_MASK, ret)\n+#define RMI_RESULT_DATA_LEVEL(ret)\tFIELD_GET(RMI_RESULT_DATA_LEVEL_MASK, ret)\n+#define RMI_RESULT_MEMREQ(ret)\t\tFIELD_GET(RMI_RESULT_MEMREQ_MASK, ret)\n+#define RMI_RESULT_CAN_CANCEL(ret)\tFIELD_GET(RMI_RESULT_CAN_CANCEL_MASK, ret)\n+\n+#define RMI_SUCCESS\t\t\t0\n+#define RMI_ERROR_INPUT\t\t\t1\n+#define RMI_ERROR_REALM\t\t\t2\n+#define RMI_ERROR_REC\t\t\t3\n+#define RMI_ERROR_RTT\t\t\t4\n+#define RMI_ERROR_NOT_SUPPORTED\t\t5\n+#define RMI_ERROR_DEVICE\t\t6\n+#define RMI_ERROR_RTT_AUX\t\t7\n+#define RMI_ERROR_PSMMU_ST\t\t8\n+#define RMI_ERROR_DPT\t\t\t9\n+#define RMI_BUSY\t\t\t10\n+#define RMI_ERROR_GLOBAL\t\t11\n+#define RMI_ERROR_TRACKING\t\t12\n+#define RMI_INCOMPLETE\t\t\t13\n+#define RMI_BLOCKED\t\t\t14\n+#define RMI_ERROR_GPT\t\t\t15\n+#define RMI_ERROR_GRANULE\t\t16\n+\n+#define RMI_CONTINUE_KEEP_GOING\t\t0\n+#define RMI_CONTINUE_STOP\t\t1\n+\n+#define RMI_OP_MEM_REQ_NONE\t\t0\n+#define RMI_OP_MEM_REQ_DONATE\t\t1\n+#define RMI_OP_MEM_REQ_RECLAIM\t\t2\n+\n+#define RMI_OP_CANNOT_CANCEL\t\t0\n+#define RMI_OP_CAN_CANCEL\t\t1\n+\n+#define RMI_DONATE_STATE_MASK\t\tGENMASK(18, 17)\n+#define RMI_DONATE_CONTIG_MASK\t\tBIT(16)\n+#define RMI_DONATE_COUNT_MASK\t\tGENMASK(15, 2)\n+#define RMI_DONATE_BLOCK_SIZE_MASK\tGENMASK(1, 0)\n+\n+#define RMI_DONATE_STATE(req)\t\tFIELD_GET(RMI_DONATE_STATE_MASK, req)\n+#define RMI_DONATE_CONTIG(req)\t\tFIELD_GET(RMI_DONATE_CONTIG_MASK, req)\n+#define RMI_DONATE_COUNT(req)\t\tFIELD_GET(RMI_DONATE_COUNT_MASK, req)\n+#define RMI_DONATE_BLOCK_SIZE(req)\tFIELD_GET(RMI_DONATE_BLOCK_SIZE_MASK, req)\n+\n+#define RMI_OP_MEM_DELEGATED\t\t0\n+#define RMI_OP_MEM_UNDELEGATED\t\t1\n+#define RMI_OP_MEM_CONDITIONAL\t\t2\n+\n+#define RMI_OP_MEM_NON_CONTIG\t\t0\n+#define RMI_OP_MEM_CONTIG\t\t1\n+\n+#define RMI_ADDR_TYPE_NONE\t\t0\n+#define RMI_ADDR_TYPE_SINGLE\t\t1\n+#define RMI_ADDR_TYPE_LIST\t\t2\n+\n+#define RMI_ADDR_RANGE_STATE_MASK\tGENMASK_ULL(63, 62)\n+#define RMI_ADDR_RANGE_ADDR_MASK\tGENMASK_ULL(51, PAGE_SHIFT)\n+#define RMI_ADDR_RANGE_COUNT_MASK\tGENMASK(PAGE_SHIFT - 1, 2)\n+#define RMI_ADDR_RANGE_BLOCK_SIZE_MASK\tGENMASK(1, 0)\n+\n+#define RMI_ADDR_RANGE_STATE(r)\t\tFIELD_GET(RMI_ADDR_RANGE_STATE_MASK, (r))\n+#define RMI_ADDR_RANGE_ADDR(r)\t\t((r) \u0026 RMI_ADDR_RANGE_ADDR_MASK)\n+#define RMI_ADDR_RANGE_COUNT(r)\t\tFIELD_GET(RMI_ADDR_RANGE_COUNT_MASK, (r))\n+#define RMI_ADDR_RANGE_BLOCK_SIZE(r)\tFIELD_GET(RMI_ADDR_RANGE_BLOCK_SIZE_MASK, (r))\n+\n+enum rmi_ripas {\n+\tRMI_EMPTY = 0,\n+\tRMI_RAM = 1,\n+\tRMI_DESTROYED = 2,\n+\tRMI_DEV = 3,\n+};\n+\n+#define RMI_NO_MEASURE_CONTENT\t0\n+#define RMI_MEASURE_CONTENT\t1\n+\n+#define RMI_FEATURE_REGISTER_0_S2OASZ\t\t\tGENMASK_ULL(40, 33)\n+#define RMI_FEATURE_REGISTER_0_L0GPT_BLOCK_DELEGATE\tBIT_ULL(32)\n+#define RMI_FEATURE_REGISTER_0_PMU_NUM_CTRS\t\tGENMASK(31, 27)\n+#define RMI_FEATURE_REGISTER_0_PMU\t\t\tBIT(26)\n+#define RMI_FEATURE_REGISTER_0_NUM_WPS\t\t\tGENMASK(25, 20)\n+#define RMI_FEATURE_REGISTER_0_NUM_BPS\t\t\tGENMASK(19, 14)\n+#define RMI_FEATURE_REGISTER_0_SVE_VL\t\t\tGENMASK(13, 10)\n+#define RMI_FEATURE_REGISTER_0_SVE\t\t\tBIT(9)\n+#define RMI_FEATURE_REGISTER_0_LPA2\t\t\tBIT(8)\n+#define RMI_FEATURE_REGISTER_0_S2SZ\t\t\tGENMASK(7, 0)\n+\n+#define RMI_FEATURE_REGISTER_1_PPS\t\t\tGENMASK(16, 14)\n+#define RMI_FEATURE_REGISTER_1_L0GPTSZ\t\t\tGENMASK(13, 10)\n+#define RMI_FEATURE_REGISTER_1_MAX_RECS_ORDER\t\tGENMASK(9, 6)\n+#define RMI_FEATURE_REGISTER_1_HASH_SHA_512\t\tBIT(5)\n+#define RMI_FEATURE_REGISTER_1_HASH_SHA_384\t\tBIT(4)\n+#define RMI_FEATURE_REGISTER_1_HASH_SHA_256\t\tBIT(3)\n+#define RMI_FEATURE_REGISTER_1_RMI_GRAN_SZ_64KB\t\tBIT(2)\n+#define RMI_FEATURE_REGISTER_1_RMI_GRAN_SZ_16KB\t\tBIT(1)\n+#define RMI_FEATURE_REGISTER_1_RMI_GRAN_SZ_4KB\t\tBIT(0)\n+\n+#define RMI_FEATURE_REGISTER_2_REALM_MAX_VDEVS_ORDER\tGENMASK(14, 10)\n+#define RMI_FEATURE_REGISTER_2_NON_TEE_STREAM\t\tBIT(9)\n+#define RMI_FEATURE_REGISTER_2_VDEV_KROU\t\tBIT(8)\n+#define RMI_FEATURE_REGISTER_2_PDEV_MAX_VDEVS_ORDER\tGENMASK(7, 4)\n+#define RMI_FEATURE_REGISTER_2_ATS\t\t\tBIT(3)\n+#define RMI_FEATURE_REGISTER_2_VSMMU\t\t\tBIT(2)\n+#define RMI_FEATURE_REGISTER_2_DA_COH\t\t\tBIT(1)\n+#define RMI_FEATURE_REGISTER_2_DA\t\t\tBIT(0)\n+\n+#define RMI_FEATURE_REGISTER_3_RTT_S2AP_INDIRECT\tBIT(6)\n+#define RMI_FEATURE_REGISTER_3_RTT_PLANE\t\tGENMASK(5, 4)\n+#define RMI_FEATURE_REGISTER_3_MAX_NUM_AUX_PLANES\tGENMASK(3, 0)\n+\n+#define RMI_FEATURE_REGISTER_4_MEC_COUNT\t\tGENMASK_ULL(63, 0)\n+\n+#define RMI_MEM_CATEGORY_CONVENTIONAL\t\t0\n+#define RMI_MEM_CATEGORY_DEV_NCOH\t\t1\n+#define RMI_MEM_CATEGORY_DEV_COH\t\t2\n+#define RMI_MEM_CATEGORY_NONE\t\t\t3\n+\n+#define RMI_TRACKING_RESERVED\t\t\t0\n+#define RMI_TRACKING_NONE\t\t\t1\n+#define RMI_TRACKING_FINE\t\t\t2\n+#define RMI_TRACKING_COARSE\t\t\t3\n+#define RMI_TRACKING_INTERMEDIATE\t\t4\n+\n+#define RMI_GRANULE_SIZE_4KB\t0\n+#define RMI_GRANULE_SIZE_16KB\t1\n+#define RMI_GRANULE_SIZE_64KB\t2\n+\n+#define RMI_GPT_PAR_RESERVED           0U\n+#define RMI_GPT_PAR_PLAT               1U\n+#define RMI_GPT_PAR_HOST_NOT_CREATED   2U\n+#define RMI_GPT_PAR_HOST_CREATED       3U\n+\n+/*\n+ * Note many of these fields are smaller than u64 but all fields have u64\n+ * alignment, so use u64 to ensure correct alignment.\n+ */\n+struct rmm_config {\n+\tunion { /* 0x0 */\n+\t\tstruct {\n+\t\t\tu64 tracking_region_size;\n+\t\t\tu64 rmi_granule_size;\n+\t\t};\n+\t\tu8 sizer[SZ_4K];\n+\t};\n+};\n+\n+static_assert(sizeof(struct rmm_config) == SZ_4K);\n+\n+#define RMI_REALM_PARAM_FLAG_MEC_POLICY_MASK\tGENMASK(8, 7)\n+#define RMI_REALM_PARAM_FLAG_LFA_POLICY_MASK\tGENMASK(6, 5)\n+#define RMI_REALM_PARAM_FLAG_DA\t\t\tBIT(3)\n+#define RMI_REALM_PARAM_FLAG_PMU\t\tBIT(2)\n+#define RMI_REALM_PARAM_FLAG_SVE\t\tBIT(1)\n+\n+#define RMI_HASH_SHA_256\t\t\t0\n+#define RMI_HASH_SHA_512\t\t\t1\n+#define RMI_HASH_SHA_384\t\t\t2\n+\n+struct realm_params {\n+\tunion { /* 0x0 */\n+\t\tstruct {\n+\t\t\tu64 flags0;\n+\t\t\tu64 s2sz;\n+\t\t\tu64 sve_vl;\n+\t\t\tu64 num_bps;\n+\t\t\tu64 num_wps;\n+\t\t\tu64 pmu_num_ctrs;\n+\t\t\tu64 hash_algo;\n+\t\t\tu64 num_aux_planes;\n+\t\t};\n+\t\tu8 padding0[0x400];\n+\t};\n+\tunion { /* 0x400 */\n+\t\tstruct {\n+\t\t\tu8 rpv[64];\n+\t\t\tu64 ats_plane;\n+\t\t};\n+\t\tu8 padding1[0x400];\n+\t};\n+\tunion { /* 0x800 */\n+\t\tstruct {\n+\t\t\tu64 padding2;\n+\t\t\tu64 rtt_base;\n+\t\t\ts64 rtt_level_start;\n+\t\t\tu64 rtt_num_start;\n+\t\t\tu64 flags1;\n+\t\t\tu64 max_num_vdevs;\n+\t\t};\n+\t\tu8 padding3[0x700];\n+\t};\n+\tunion { /* 0xf00 */\n+\t\tstruct {\n+\t\t\tu8 padding4[0x80];\n+\t\t\tu64 aux_rtt_base[3];\n+\t\t};\n+\t\tu8 padding5[0x100];\n+\t};\n+};\n+\n+static_assert(sizeof(struct realm_params) == SZ_4K);\n+\n+/*\n+ * The number of GPRs (starting from X0) that are configured by the host when\n+ * a REC is created.\n+ */\n+#define REC_CREATE_NR_GPRS\t\t8\n+\n+#define REC_PARAMS_FLAG_RUNNABLE\tBIT(0)\n+\n+struct rec_params {\n+\tunion { /* 0x0 */\n+\t\tu64 flags;\n+\t\tu8 padding0[0x100];\n+\t};\n+\tunion { /* 0x100 */\n+\t\tu64 mpidr;\n+\t\tu8 padding1[0x100];\n+\t};\n+\tunion { /* 0x200 */\n+\t\tu64 pc;\n+\t\tu8 padding2[0x100];\n+\t};\n+\tunion { /* 0x300 */\n+\t\tu64 gprs[REC_CREATE_NR_GPRS];\n+\t\tu8 padding3[0xd00];\n+\t};\n+};\n+\n+static_assert(sizeof(struct rec_params) == SZ_4K);\n+\n+#define REC_ENTER_FLAG_FORCE_P0\t\tBIT(7)\n+#define REC_ENTER_FLAG_DEV_MEM_RESPONSE\tBIT(6)\n+#define REC_ENTER_FLAG_S2AP_RESPONSE\tBIT(5)\n+#define REC_ENTER_FLAG_RIPAS_RESPONSE\tBIT(4)\n+#define REC_ENTER_FLAG_TRAP_WFE\t\tBIT(3)\n+#define REC_ENTER_FLAG_TRAP_WFI\t\tBIT(2)\n+#define REC_ENTER_FLAG_INJECT_SEA\tBIT(1)\n+#define REC_ENTER_FLAG_EMULATED_MMIO\tBIT(0)\n+\n+#define REC_RUN_GPRS\t\t\t31\n+\n+struct rec_enter {\n+\tunion { /* 0x000 */\n+\t\tu64 flags;\n+\t\tu8 padding0[0x200];\n+\t};\n+\tunion { /* 0x200 */\n+\t\tu64 gprs[REC_RUN_GPRS];\n+\t\tu8 padding1[0x600];\n+\t};\n+};\n+\n+static_assert(sizeof(struct rec_enter) == SZ_2K);\n+\n+#define RMI_EXIT_SYNC\t\t\t0x00\n+#define RMI_EXIT_IRQ\t\t\t0x01\n+#define RMI_EXIT_FIQ\t\t\t0x02\n+#define RMI_EXIT_PSCI\t\t\t0x03\n+#define RMI_EXIT_RIPAS_CHANGE\t\t0x04\n+#define RMI_EXIT_HOST_CALL\t\t0x05\n+#define RMI_EXIT_SERROR\t\t\t0x06\n+#define RMI_EXIT_S2AP_CHANGE\t\t0x07\n+#define RMI_EXIT_VDEV_VALIDATE_MAPPING\t0x08\n+#define RMI_EXIT_VSMMU_COMMAND\t\t0x0a\n+\n+struct rec_exit {\n+\tunion { /* 0x000 */\n+\t\tu8 exit_reason;\n+\t\tu8 padding0[0x100];\n+\t};\n+\tunion { /* 0x100 */\n+\t\tstruct {\n+\t\t\tu64 esr;\n+\t\t\tu64 far;\n+\t\t\tu64 hpfar;\n+\t\t\tu64 rtt_tree;\n+\t\t};\n+\t\tu8 padding1[0x100];\n+\t};\n+\tunion { /* 0x200 */\n+\t\tu64 gprs[REC_RUN_GPRS];\n+\t\tu8 padding2[0x100];\n+\t};\n+\tunion { /* 0x300 */\n+\t\tu8 padding3[0x100];\n+\t};\n+\tunion { /* 0x400 */\n+\t\tstruct {\n+\t\t\tu64 cntp_ctl;\n+\t\t\tu64 cntp_cval;\n+\t\t\tu64 cntv_ctl;\n+\t\t\tu64 cntv_cval;\n+\t\t};\n+\t\tu8 padding4[0x100];\n+\t};\n+\tunion { /* 0x500 */\n+\t\tstruct {\n+\t\t\tu64 ripas_base;\n+\t\t\tu64 ripas_top;\n+\t\t\tu8 ripas_value;\n+\t\t\tu8 padding5[0xf];\n+\t\t\tu64 s2ap_base;\n+\t\t\tu64 s2ap_top;\n+\t\t\tu64 vdev_id_1;\n+\t\t\tu64 vdev_id_2;\n+\t\t\tu64 dev_mem_base;\n+\t\t\tu64 dev_mem_top;\n+\t\t\tu64 dev_mem_pa;\n+\t\t};\n+\t\tu8 padding6[0x100];\n+\t};\n+\tunion { /* 0x600 */\n+\t\tstruct {\n+\t\t\tu16 imm;\n+\t\t\tu8 padding7[0x6];\n+\t\t\tu64 plane;\n+\t\t};\n+\t\tu8 padding8[0x100];\n+\t};\n+\tunion { /* 0x700 */\n+\t\tstruct {\n+\t\t\tu8 pmu_ovf_status;\n+\t\t\tu8 padding9[0xf];\n+\t\t\tu64 vsmmu;\n+\t\t};\n+\t\tu8 padding10[0x100];\n+\t};\n+};\n+\n+static_assert(sizeof(struct rec_exit) == SZ_2K);\n+\n+struct rec_run {\n+\tstruct rec_enter enter;\n+\tstruct rec_exit exit;\n+};\n+\n+static_assert(sizeof(struct rec_run) == SZ_4K);\n+\n+/* RMI_RTT_UNPROT_MAP_FLAGS definitions */\n+#define RMI_RTT_UNPROT_MAP_FLAGS_S2AP_MASK\t\tGENMASK(22, 19)\n+#define RMI_RTT_UNPROT_MAP_FLAGS_MEMATTR_MASK\t\tGENMASK(18, 16)\n+#define RMI_RTT_UNPROT_MAP_FLAGS_LIST_COUNT_MASK\tGENMASK(15, 2)\n+#define RMI_RTT_UNPROT_MAP_FLAGS_OADDR_TYPE_MASK\tGENMASK(1, 0)\n+\n+/* RMI_RTT_PROT_MAP_FLAGS definitions */\n+#define RMI_RTT_PROT_MAP_FLAGS_LIST_COUNT_MASK\t\tGENMASK(15, 2)\n+#define RMI_RTT_PROT_MAP_FLAGS_OADDR_TYPE_MASK\t\tGENMASK(1, 0)\n+\n+/* S2AP Direct Encodings, used in RMI_RTT_UNPROT_MAP_FLAGS_S2AP */\n+#define RMI_S2AP_DIRECT_READ\t\t\tBIT(1)\n+#define RMI_S2AP_DIRECT_WRITE\t\t\tBIT(0)\n+\n+#endif /* __LINUX_ARM_SMCCC_RMI_H_ */\n"
}

2/1 2026/09/26 19:11 agent patch-evaluator
0m Model:
core

Args:
null

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "Reasoning": "The patch series introduces support for the Arm Realm Management Interface (RMI) and cpufeature handling for Arm Confidential Compute Architecture (CCA). The new driver and changes depend on ARCH_SUPPORTS_RMM, which is only selected by ARM64. Consequently, all modified and added code paths are exclusive to the arm64 architecture and are not compiled or executable on the target architecture (amd64).",
  "WorthFuzzing": false
}

Instruction:
You are an expert Linux kernel maintainer and security engineer.
Your job is to review a provided patch series and evaluate whether it warrants fuzzing with syzkaller.

IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in
your workspace. Do NOT rely on internal assumptions. You must actively use your code access
tools to inspect the actual source code, callers, and surrounding context.

================================================================================
1. CORE TRIAGE PHILOSOPHY
================================================================================
The goal of patch fuzzing is to discover crashes, regressions, exposed latent bugs,
and newly triggered assertions introduced by the patch series.

- REACHABILITY IS THE PRIMARY GATE:
  Fuzzing can only discover bugs in code that can actually execute in standard virtualized
  environments (GCE or QEMU, utilizing software-emulated devices like USB gadgets, netdev, tun/tap).
  If the modified code is structurally unreachable (see Section 2), it MUST NOT be fuzzed,
  regardless of whether it adds assertions or complex logic.

- DO NOT BLINDLY TRUST "NO FUNCTIONAL CHANGE" (NFCI) OR "REFACTORING" CLAIMS:
  Patch authors routinely label changes as "cleanups", "refactorings", or state
  "No functional change intended". Do NOT take these claims at face value.
  Code refactorings that rearrange logic, introduce helper functions, or alter state management
  in core subsystems frequently introduce subtle semantic shifts or uncover latent kernel bugs.
  If reachable executable code is modified or refactored, it MUST be fuzzed.

- NEW OR MODIFIED ASSERTIONS IN REACHABLE CODE MUST BE FUZZED:
  When a patch introduces or modifies runtime checks or assertions (e.g., WARN_ON*, VM_WARN_ON*,
  BUG_ON*, lockdep_assert*) in reachable code paths, it enforces new or stricter invariants.
  Even if the author believes the invariant always holds, fuzzing is essential to verify whether
  an unusual sequence of operations can violate it.

================================================================================
2. WHEN TO RETURN WorthFuzzing=false (NEGATIVE CRITERIA)
================================================================================
Return WorthFuzzing=false ONLY IF all modified code falls strictly into one or more of these categories:

- Non-kernel and non-executable changes:
  * Modifications to Documentation/, comments, or spelling fixes.
  * User-space directories, self-tests, samples, or scripts (e.g., tools/, samples/, scripts/, usr/)
    that do not affect the compiled kernel image (vmlinux) or kernel modules.
  * Purely decorative logging (e.g., message strings in pr_err, printk, dev_info) or tracepoints
    that do not alter control flow or data structures.
  * Build system or Kconfig changes that do not alter compiled C logic.
- Structurally unreachable hardware:
  * Vendor-specific PCIe switches, SmartNICs, or GPU drivers (e.g., mlxsw, pds_core, qed,
    ionic, amdgpu) requiring physical ASIC/PCIe cards not emulated in standard QEMU.
- Unreachable execution paths:
  * Driver teardown callbacks (.remove, .shutdown, pci_unregister_driver) executed only during
    physical PCI hot-unplug or manual sysfs driver unbinding.
  * Code paths exclusive to architectures other than the target architecture.

================================================================================
3. WHEN TO RETURN WorthFuzzing=true (POSITIVE CRITERIA)
================================================================================
Return WorthFuzzing=true whenever the patch touches reachable executable code, including:
- Core Subsystems:
  * Any logic modifications in memory management (mm/), synchronization/locking (kernel/locking/),
    BPF, scheduler, core networking, VFS, or syscall handling.
- Refactorings and Code Cleanups:
  * Any restructuring of reachable data structures, helper abstractions, or algorithm flows.
- Runtime Assertions and Defensive Checks:
  * Any introduction or alteration of assertions (WARN_ON*, VM_WARN_ON*, BUG_ON*, etc.) in reachable paths.
- Reachable Drivers and Protocols:
  * Drivers accessible via virtual buses (virtio, USB gadget, loopback, netlink, binder, sockets, etc.).

================================================================================
4. EXTRACTING FocusSymbols (PREVENTING DILUTION)
================================================================================
When WorthFuzzing=true, you must extract specific kernel functions into FocusSymbols to guide the fuzzer:

- AVOID UBIQUITOUS LIFECYCLE HOT-PATHS:
  Do NOT list generic, ubiquitous functions called by almost every program in the corpus
  (including, but not limited to: general memory allocators and deallocators, page fault
  and trap handlers, or core synchronization primitives; this is not an exhaustive list).
  Listing ubiquitous functions causes the fuzzer to classify thousands of unrelated tests as "focused",
  which severely dilutes fuzzing effort away from the actual changes.

- TARGET SPECIFIC FEATURE LOGIC AND ENTRYPOINTS:
  List functions that specifically implement the logic being added or altered, or direct API entrypoints
  for the subsystem feature under review.

- HANDLING STATIC INLINE FUNCTIONS IN HEADERS (.h):
  Compiler-inlined static functions (such as static inlines in mm/*.h or include/linux/*.h) lack
  distinct symbol addresses in vmlinux and cannot be targeted directly by symbol coverage filters.
  If the changes are primarily in static inline helpers, identify non-static, feature-specific caller
  functions in .c files that exercise them (avoiding ubiquitous lifecycle wrappers).

================================================================================
5. IDENTIFYING EnableConfigs
================================================================================
Identify any specific CONFIG_ options required to properly compile and reach the modified code:
- Inspect Kconfig files and #ifdef guards; do not make assumptions.
- Check "depends on" lines in Kconfig to include any non-standard parent subsystem configs needed.
- Strip any 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4").
Prefer calling several tools at the same time to save round-trips.


Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.

Prompt:
Target architecture: amd64

For your convenience, here is the diff of the changes:
commit c71bfd6872edba9866a77241829919b5da5bdedf
Author: syz-cluster <triage@syzkaller.com>
Date:   Sat Sep 26 19:11:20 2026 +0000

    syz-cluster: applied patch under review

diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index b5a51b0ef9440..ff9565d3ffa59 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -38,6 +38,7 @@ config ARM64
 	select ARCH_HAS_MEMBARRIER_SYNC_CORE
 	select ARCH_HAS_MEM_ENCRYPT
 	select ARCH_SUPPORTS_MSEAL_SYSTEM_MAPPINGS
+	select ARCH_SUPPORTS_RMM
 	select ARCH_HAS_NMI_SAFE_THIS_CPU_OPS
 	select ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE
 	select ARCH_HAS_NONLEAF_PMD_YOUNG if ARM64_HAFT
diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
index 32102c3912fa7..e8b29983b0021 100644
--- a/arch/arm64/kernel/cpufeature.c
+++ b/arch/arm64/kernel/cpufeature.c
@@ -293,6 +293,7 @@ static const struct arm64_ftr_bits ftr_id_aa64isar3[] = {
 static const struct arm64_ftr_bits ftr_id_aa64pfr0[] = {
 	ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64PFR0_EL1_CSV3_SHIFT, 4, 0),
 	ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64PFR0_EL1_CSV2_SHIFT, 4, 0),
+	ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64PFR0_EL1_RME_SHIFT, 4, 0),
 	ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64PFR0_EL1_DIT_SHIFT, 4, 0),
 	ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64PFR0_EL1_AMU_SHIFT, 4, 0),
 	ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64PFR0_EL1_MPAM_SHIFT, 4, 0),
diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig
index b7cc11e4fbfa6..62660bf520a8d 100644
--- a/drivers/firmware/Kconfig
+++ b/drivers/firmware/Kconfig
@@ -310,5 +310,6 @@ source "drivers/firmware/samsung/Kconfig"
 source "drivers/firmware/smccc/Kconfig"
 source "drivers/firmware/tegra/Kconfig"
 source "drivers/firmware/xilinx/Kconfig"
+source "drivers/firmware/arm_rmm/Kconfig"
 
 endmenu
diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile
index be46f1e1dc77f..196a650ccf025 100644
--- a/drivers/firmware/Makefile
+++ b/drivers/firmware/Makefile
@@ -39,3 +39,4 @@ obj-y				+= samsung/
 obj-y				+= smccc/
 obj-y				+= tegra/
 obj-y				+= xilinx/
+obj-y				+= arm_rmm/
diff --git a/drivers/firmware/arm_rmm/Kconfig b/drivers/firmware/arm_rmm/Kconfig
new file mode 100644
index 0000000000000..a37cba6647360
--- /dev/null
+++ b/drivers/firmware/arm_rmm/Kconfig
@@ -0,0 +1,25 @@
+
+config ARCH_SUPPORTS_RMM
+	bool
+
+config ARM_RMM_RMI
+	bool "Realm Management Interface (RMI) Support"
+	depends on ARCH_SUPPORTS_RMM
+	help
+	  Support the Realm Management Monitor (RMM) on Arm systems that
+	  implement the Realm Management Extension (RME), as defined by the
+	  Arm Confidential Compute Architecture.
+
+	  The RMM runs at EL2 in the Realm world and provides the Realm
+	  Management Interface (RMI) used by a Normal World host to create,
+	  manage and run protected virtual machines called Realms. The RMM can
+	  also act as a TSM, as defined by the PCIe TDISP and can manage the
+	  PCI IDE setup for securing the PCIe links.
+
+	  This option builds the host-side RMI support used by KVM to detect a
+	  compatible RMM, configure it, manage delegated memory and enable
+	  Realm guests.
+
+	  Selecting this option does not by itself make Realm guests available:
+	  the system must also provide RME-capable hardware and firmware with a
+	  compatible RMM implementation.
diff --git a/drivers/firmware/arm_rmm/Makefile b/drivers/firmware/arm_rmm/Makefile
new file mode 100644
index 0000000000000..65171988fdcae
--- /dev/null
+++ b/drivers/firmware/arm_rmm/Makefile
@@ -0,0 +1,2 @@
+
+obj-$(CONFIG_ARM_RMM_RMI)	= rmi.o
diff --git a/drivers/firmware/arm_rmm/rmi.c b/drivers/firmware/arm_rmm/rmi.c
new file mode 100644
index 0000000000000..1a8f3debd844a
--- /dev/null
+++ b/drivers/firmware/arm_rmm/rmi.c
@@ -0,0 +1,1072 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023-2026 ARM Ltd.
+ */
+
+#include <linux/cpufeature.h>
+#include <linux/memblock.h>
+#include <linux/memory.h>
+#include <linux/arm-rmi-cmds.h>
+#include <linux/slab.h>
+
+#include <asm/memory.h>
+#include <asm/pgtable-hwdef.h>
+
+/* RMM defines RmiFeatureRegister0 to RmiFeatureRegister5. */
+static unsigned long rmi_feat_reg_cache[5] __ro_after_init;
+static bool arm64_rmi_is_available;
+
+/**
+ * rmi_granule_range_undelegate() - Undelegate a range of granules
+ * @base: Base PA of the target range
+ * @top: Top PA of the target range
+ * @out_top: Returns the top PA of range whose state is undelegated
+ *
+ * Undelegate a range of granules to allow use by the normal world. Will fail
+ * if the granules are in use by RMM. RMM can ignore granules that are already
+ * undelegated and thus is safe to be called on a range with a mix of delegated
+ * and undelegated granules.
+ *
+ * Return: 0 on success, positive RMI result code or negative Linux error code
+ */
+static inline long rmi_granule_range_undelegate(unsigned long base,
+						unsigned long top,
+						unsigned long *out_top)
+{
+	struct arm_smccc_1_2_regs regs = {
+		SMC_RMI_GRANULE_RANGE_UNDELEGATE, base, top
+	};
+	long ret = rmi_sro_execute(&regs);
+
+	if (ret == RMI_SUCCESS && out_top)
+		*out_top = regs.a1;
+
+	return ret;
+}
+
+int rmi_undelegate_range(phys_addr_t phys,
+			 unsigned long size)
+{
+	long ret = 0;
+	unsigned long top = phys + size;
+	unsigned long out_top;
+
+	while (phys < top) {
+		ret = rmi_granule_range_undelegate(phys, top, &out_top);
+
+		if (ret == RMI_SUCCESS) {
+			/* Buggy RMM ? Let the caller leak the pages */
+			if (WARN_ON(out_top <= phys))
+				return -ENXIO;
+			phys = out_top;
+		} else {
+			break;
+		}
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(rmi_undelegate_range);
+
+/**
+ * rmi_granule_range_delegate() - Delegate granules
+ * @base: PA of the first granule of the range
+ * @top: PA of the first granule after the range
+ * @out_top: PA of the first granule not delegated
+ *
+ * Delegate a range of granule for use by the realm world. If the entire range
+ * was delegated then @out_top == @top, otherwise the function should be called
+ * again with @base == @out_top.
+ *
+ * Return: 0 on success, positive RMI result code or negative Linux error code
+ */
+static long rmi_granule_range_delegate(unsigned long base,
+				       unsigned long top,
+				       unsigned long *out_top)
+{
+	struct arm_smccc_1_2_regs regs = {
+		SMC_RMI_GRANULE_RANGE_DELEGATE, base, top
+	};
+	long ret = rmi_sro_execute(&regs);
+
+	if (ret == RMI_SUCCESS && out_top)
+		*out_top = regs.a1;
+
+	return ret;
+}
+
+/*
+ * rmi_delegate_range: Delegate a physically contiguous range.
+ * We iterate over the range until we hit an error. So we may
+ * return an error, but with a partially delegated range. The
+ * caller must always look at the @out_phys to figure out, how
+ * much progress was made.
+ *
+ * @phys:	Base of the physical address range
+ * @size:	Size of the physical address range
+ * @out_phys:	Top of the range that was completed. This is always
+ *		valid, irrespective of the result.
+ *
+ * Returns RMI_SUCCESS on successful completion. Otherwise, returns
+ * the Linux error number or the RMI status code as described
+ * by the RMM spec for RMI_GRANULE_DELEGATE_RANGE or RMI_BLOCKED.
+ */
+int rmi_delegate_range(phys_addr_t phys,
+		       unsigned long size,
+		       phys_addr_t *out_phys)
+{
+	long ret = 0;
+	unsigned long top = phys + size;
+	unsigned long out_top;
+
+	while (phys < top) {
+		ret = rmi_granule_range_delegate(phys, top, &out_top);
+
+		if (ret == RMI_SUCCESS) {
+			/*
+			 * Buggy RMM ? Let the caller handle the failure.
+			 * We can't know how far the RMM delegated in this
+			 * iteration, so we return the best known good limit.
+			 * RMM can deal with granules already in "undelegated"
+			 * in a given range. So, it is fine for the caller to
+			 * try the range we return.
+			 */
+			if (WARN_ON(out_top <= phys)) {
+				ret = -ENXIO;
+				break;
+			}
+			phys = out_top;
+		} else {
+			break;
+		}
+	}
+
+	if (out_phys)
+		*out_phys = phys;
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(rmi_delegate_range);
+
+/*
+ * Convert the RmiAddrBlockSize to actual size. This is used in RmiDonateReq
+ * and RmiAddrRangeDesc*.
+ */
+static unsigned long rmi_addr_block_size_to_bytes(unsigned long block_size_fld)
+{
+	return BIT(ARM64_HW_PGTABLE_LEVEL_SHIFT(3 - block_size_fld));
+}
+
+/*
+ * free_addr_range: Free memory described by the address range entry, which may
+ *		    be partially consumed by RMM.
+ *
+ * @entry: RMI_ADDR_RANGE descriptor
+ * @consumed_size: Page aligned size consumed by the RMM from the address range.
+ *
+ * If the state of the address is DELEGATED, undelegate it back, before freeing.
+ * Leaks the memory if we cannot undelegate the range.
+ */
+static void free_addr_range(unsigned long entry, unsigned long consumed_size)
+{
+	unsigned long phys = RMI_ADDR_RANGE_ADDR(entry);
+	unsigned long block_size_fld = RMI_ADDR_RANGE_BLOCK_SIZE(entry);
+	unsigned long count = RMI_ADDR_RANGE_COUNT(entry);
+	unsigned long state = RMI_ADDR_RANGE_STATE(entry);
+	unsigned long size = rmi_addr_block_size_to_bytes(block_size_fld) * count;
+
+	WARN_ON(!PAGE_ALIGNED(phys) || !PAGE_ALIGNED(consumed_size));
+
+	/* We shouldn't see this in reclaim path, leak it for now  */
+	if (WARN_ON(state == RMI_OP_MEM_CONDITIONAL))
+		return;
+
+	/* Adjust the address and size for partially consumed entry */
+	phys += consumed_size;
+	size -= consumed_size;
+	/*
+	 * Undelegate the pages back if required. If we can't
+	 * change them back, leak the pages.
+	 */
+	if (state == RMI_OP_MEM_DELEGATED &&
+	    WARN_ON(rmi_undelegate_range(phys, size)))
+		return;
+	free_pages_exact(phys_to_virt(phys), size);
+}
+
+static void rmi_op_continue(unsigned long sro_handle, unsigned long flags,
+			    struct arm_smccc_1_2_regs *out_regs)
+{
+	*out_regs = (struct arm_smccc_1_2_regs) {
+		SMC_RMI_OP_CONTINUE, sro_handle, flags
+	};
+
+	rmi_smccc_invoke(out_regs);
+}
+
+static void rmi_op_cancel(unsigned long sro_handle,
+			  struct arm_smccc_1_2_regs *out_regs)
+{
+	*out_regs = (struct arm_smccc_1_2_regs) {
+		SMC_RMI_OP_CANCEL, sro_handle
+	};
+
+	rmi_smccc_invoke(out_regs);
+}
+
+static void rmi_op_mem_donate(unsigned long sro_handle, unsigned long list_addr,
+			      unsigned long list_count, unsigned long flags,
+			      struct arm_smccc_1_2_regs *out_regs)
+{
+	*out_regs = (struct arm_smccc_1_2_regs) {
+		SMC_RMI_OP_MEM_DONATE, sro_handle, list_addr, list_count, flags
+	};
+
+	/*
+	 * The output donated count (a1) is always valid, irrespective
+	 * of the return result. i.e., 0 if there was an error
+	 */
+	rmi_smccc_invoke(out_regs);
+}
+
+static void rmi_op_mem_reclaim(unsigned long sro_handle,
+			       unsigned long list_addr,
+			       unsigned long list_count,
+			       struct arm_smccc_1_2_regs *out_regs)
+{
+	*out_regs = (struct arm_smccc_1_2_regs) {
+		SMC_RMI_OP_MEM_RECLAIM, sro_handle, list_addr, list_count
+	};
+
+	rmi_smccc_invoke(out_regs);
+}
+
+/*
+ * rmi_free_delegated_page: Undelegate and free a page that has been previously
+ * delegated to the Realm world. If we are unable to undelegate it, the page is
+ * leaked.
+ * NOTE: Do not use this helper if the page could be concurrently operated by
+ * another thread, as it may get leaked if the undelegation fails due to RMI_BLOCKED
+ */
+int rmi_free_delegated_page(phys_addr_t phys)
+{
+	if (WARN_ON_ONCE(rmi_undelegate_page(phys))) {
+		/* Undelegate failed: leak the page */
+		return -EBUSY;
+	}
+
+	free_page((unsigned long)phys_to_virt(phys));
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(rmi_free_delegated_page);
+
+static int rmi_sro_ensure_capacity(struct rmi_sro_state *sro,
+				   unsigned long count)
+{
+	if (WARN_ON_ONCE(sro->addr_count > RMI_MAX_ADDR_LIST))
+		return -EOVERFLOW;
+
+	if (count > RMI_MAX_ADDR_LIST - sro->addr_count)
+		return -ENOSPC;
+
+	return 0;
+}
+
+static int rmi_sro_donate_contig(struct rmi_sro_state *sro,
+				 unsigned long sro_handle,
+				 unsigned long donatereq,
+				 struct arm_smccc_1_2_regs *out_regs,
+				 gfp_t gfp)
+{
+	unsigned long block_size_fld = RMI_DONATE_BLOCK_SIZE(donatereq);
+	unsigned long block_size = rmi_addr_block_size_to_bytes(block_size_fld);
+	unsigned long count = RMI_DONATE_COUNT(donatereq);
+	unsigned long state = RMI_DONATE_STATE(donatereq);
+	unsigned long size = block_size * count;
+	unsigned long addr_range;
+	unsigned long donated_granules;
+	unsigned long donated_size;
+	int ret;
+	void *virt;
+	phys_addr_t phys;
+
+	/*
+	 * The RMM specification requires contiguous allocations are always a
+	 * power of 2
+	 */
+	if (WARN_ON_ONCE(!is_power_of_2(size)))
+		return -EINVAL;
+
+	/* Reuse the cached address range if we have one */
+	for (int i = 0; i < sro->addr_count; i++) {
+		unsigned long entry = sro->addr_list[i];
+
+		if (RMI_ADDR_RANGE_BLOCK_SIZE(entry) == block_size_fld &&
+		    RMI_ADDR_RANGE_COUNT(entry) == count &&
+		    RMI_ADDR_RANGE_STATE(entry) == state &&
+		    IS_ALIGNED(RMI_ADDR_RANGE_ADDR(entry), size)) {
+			sro->addr_count--;
+			swap(sro->addr_list[sro->addr_count],
+			     sro->addr_list[i]);
+
+			goto mem_donate;
+		}
+	}
+
+	ret = rmi_sro_ensure_capacity(sro, 1);
+	if (ret)
+		return ret;
+
+	virt = alloc_pages_exact(size, gfp);
+	if (!virt)
+		return -ENOMEM;
+	phys = virt_to_phys(virt);
+
+	if (state == RMI_OP_MEM_DELEGATED) {
+		phys_addr_t delegated_phys;
+
+		if (rmi_delegate_range(phys, size, &delegated_phys)) {
+			if (!rmi_undelegate_range(phys, delegated_phys - phys))
+				free_pages_exact(virt, size);
+			return -ENXIO;
+		}
+	}
+
+	addr_range = phys & RMI_ADDR_RANGE_ADDR_MASK;
+	FIELD_MODIFY(RMI_ADDR_RANGE_BLOCK_SIZE_MASK, &addr_range, block_size_fld);
+	FIELD_MODIFY(RMI_ADDR_RANGE_COUNT_MASK, &addr_range, count);
+	FIELD_MODIFY(RMI_ADDR_RANGE_STATE_MASK, &addr_range, state);
+
+	sro->addr_list[sro->addr_count] = addr_range;
+
+mem_donate:
+	rmi_op_mem_donate(sro_handle,
+			  virt_to_phys(&sro->addr_list[sro->addr_count]), 1,
+			  0, out_regs);
+	donated_granules = out_regs->a1;
+
+	if (WARN_ON(donated_granules > (size >> PAGE_SHIFT)))
+		donated_granules = (size >> PAGE_SHIFT);
+
+	donated_size = donated_granules << PAGE_SHIFT;
+
+	/* All granules consumed by the RMM */
+	if (donated_size == size)
+		return 0;
+	/* No granules were consumed by the RMM, cache them */
+	if (donated_granules == 0) {
+		sro->addr_count++;
+		return 0;
+	}
+
+	/* The granules were partially consumed, reclaim the unused ones. */
+	free_addr_range(sro->addr_list[sro->addr_count], donated_size);
+
+	return 0;
+}
+
+static int rmi_sro_donate_noncontig(struct rmi_sro_state *sro,
+				    unsigned long sro_handle,
+				    unsigned long donatereq,
+				    struct arm_smccc_1_2_regs *out_regs,
+				    gfp_t gfp)
+{
+	unsigned long block_size_fld = RMI_DONATE_BLOCK_SIZE(donatereq);
+	unsigned long block_size = rmi_addr_block_size_to_bytes(block_size_fld);
+	unsigned long count = RMI_DONATE_COUNT(donatereq);
+	unsigned long state = RMI_DONATE_STATE(donatereq);
+	unsigned long found = 0;
+	unsigned long donated_granules;
+	unsigned long granules_per_block = block_size >> PAGE_SHIFT;
+	unsigned long consumed_blocks;
+	int addr_list_start = sro->addr_count;
+	int ret, i;
+
+	/*
+	 * Clamp the number of entries to the maximum we can do in one go.
+	 * The RMM can request  the remaining in the next iteration.
+	 */
+	if (count > RMI_MAX_ADDR_LIST)
+		count = RMI_MAX_ADDR_LIST;
+
+	/* Gather the suitable entries to the end of the list */
+	i = 0;
+	while (i <  addr_list_start && found < count) {
+		unsigned long entry = sro->addr_list[i];
+
+		if (RMI_ADDR_RANGE_BLOCK_SIZE(entry) == block_size_fld &&
+		    RMI_ADDR_RANGE_COUNT(entry) == 1 &&
+		    RMI_ADDR_RANGE_STATE(entry) == state) {
+			addr_list_start--;
+			swap(sro->addr_list[addr_list_start],
+			     sro->addr_list[i]);
+			found++;
+			/* Continue from the swapped in entry */
+			continue;
+		}
+		/* skip past the entry */
+		i++;
+	}
+
+	ret = rmi_sro_ensure_capacity(sro, count - found);
+	if (ret) {
+		/* If we have found some entries, donate them and try again */
+		if (found)
+			goto mem_donate;
+		/* Otherwise free up the list and start again */
+		rmi_sro_free(sro);
+		/* Reset the addr_list_start to match sro->addr_count */
+		addr_list_start = 0;
+	}
+
+	for (; found < count; found++) {
+		unsigned long addr_range;
+		void *virt = alloc_pages_exact(block_size, gfp);
+		phys_addr_t phys;
+
+		if (!virt)
+			return -ENOMEM;
+
+		phys = virt_to_phys(virt);
+
+		if (state == RMI_OP_MEM_DELEGATED) {
+			phys_addr_t delegated_phys;
+
+			if (rmi_delegate_range(phys, block_size, &delegated_phys)) {
+				if (!rmi_undelegate_range(phys, delegated_phys - phys))
+					free_pages_exact(virt, block_size);
+				return -ENXIO;
+			}
+		}
+
+		addr_range = phys & RMI_ADDR_RANGE_ADDR_MASK;
+		FIELD_MODIFY(RMI_ADDR_RANGE_BLOCK_SIZE_MASK, &addr_range, block_size_fld);
+		FIELD_MODIFY(RMI_ADDR_RANGE_COUNT_MASK, &addr_range, 1);
+		FIELD_MODIFY(RMI_ADDR_RANGE_STATE_MASK, &addr_range, state);
+
+		sro->addr_list[sro->addr_count++] = addr_range;
+	}
+
+mem_donate:
+	rmi_op_mem_donate(sro_handle,
+			  virt_to_phys(&sro->addr_list[addr_list_start]),
+			  found, 0, out_regs);
+
+	donated_granules = out_regs->a1;
+	/*
+	 * The RMM shouldn't report more granules than we provided, but clamp
+	 * just in case.
+	 */
+	if (WARN_ON_ONCE(donated_granules > found * granules_per_block))
+		donated_granules = found * granules_per_block;
+
+	/*
+	 * The RMM reports the consumed memory in terms of granules, but we
+	 * track in the address lists in block-sized ranges. So divide to get
+	 * the number of (complete) consumed blocks.
+	 */
+	consumed_blocks = donated_granules / granules_per_block;
+	if (donated_granules % granules_per_block) {
+		/*
+		 * A block has been partially consumed, the start is owned by
+		 * the RMM, the tail is owned by the host
+		 */
+		unsigned long entry =
+			sro->addr_list[addr_list_start + consumed_blocks];
+		unsigned long donated_size =
+			(donated_granules % granules_per_block) << PAGE_SHIFT;
+
+		free_addr_range(entry, donated_size);
+		/*
+		 * This block is now fully 'consumed' (either held by the RMM or
+		 * freed)
+		 */
+		consumed_blocks++;
+	}
+
+	/*
+	 * Keep just the blocks the RMM didn't use in addr_list
+	 * RMM claimed consumed_blocks entries from addr_list_start.
+	 * Move the entries left out at the end i.e.,
+	 * [ addr_list_start + consumed_blocks, addr_list_start + found)
+	 * to the rest of the valid entries and adjust the addr_count to
+	 * reflect the available entries.
+	 */
+	for (int i = 0, src = addr_list_start + consumed_blocks;
+		i < found - consumed_blocks; i++)
+		sro->addr_list[addr_list_start + i] = sro->addr_list[src + i];
+
+	sro->addr_count -= consumed_blocks;
+
+	return 0;
+}
+
+static int rmi_sro_donate(struct rmi_sro_state *sro,
+			  unsigned long sro_handle,
+			  unsigned long donatereq,
+			  struct arm_smccc_1_2_regs *regs,
+			  gfp_t gfp)
+{
+	if (WARN_ON_ONCE(!RMI_DONATE_COUNT(donatereq)))
+		return -EINVAL;
+
+	/*
+	 * We do not support RMI_OP_MEM_CONDITIONAL yet. This is only required
+	 * for use in RMI_GRANULE_TRACKING_SET, which we don't support yet.
+	 */
+	if (WARN_ON_ONCE(RMI_DONATE_STATE(donatereq) == RMI_OP_MEM_CONDITIONAL))
+		return -EINVAL;
+
+	if (RMI_DONATE_CONTIG(donatereq) == RMI_OP_MEM_CONTIG)
+		return rmi_sro_donate_contig(sro, sro_handle, donatereq, regs, gfp);
+	else
+		return rmi_sro_donate_noncontig(sro, sro_handle, donatereq, regs, gfp);
+}
+
+static int rmi_sro_reclaim(struct rmi_sro_state *sro,
+			   unsigned long sro_handle,
+			   struct arm_smccc_1_2_regs *out_regs)
+{
+	unsigned long capacity;
+
+	/*
+	 * We don't do a partial free of the entries. So for
+	 * now free the entire address list as we prepare
+	 * to reclaim more from the RMM.
+	 */
+	if (rmi_sro_ensure_capacity(sro, 1))
+		rmi_sro_free(sro);
+
+	capacity = RMI_MAX_ADDR_LIST - sro->addr_count;
+
+	rmi_op_mem_reclaim(sro_handle,
+			   virt_to_phys(&sro->addr_list[sro->addr_count]),
+			   capacity, out_regs);
+
+	/*
+	 * RMI_OP_MEM_RECLAIM always return RMI_INCOMPLETE, except when the
+	 * input parameters were invalid.
+	 */
+	if (WARN_ON_ONCE(RMI_RESULT_STATUS(out_regs->a0) != RMI_INCOMPLETE))
+		return -EINVAL;
+	if (WARN_ON_ONCE(out_regs->a1 > capacity))
+		out_regs->a1 = capacity;
+
+	sro->addr_count += out_regs->a1;
+
+	return 0;
+}
+
+void rmi_sro_free(struct rmi_sro_state *sro)
+{
+	/* Handle the worse */
+	if (WARN_ON(sro->addr_count < 0))
+		return;
+
+	if (WARN_ON(sro->addr_count > RMI_MAX_ADDR_LIST))
+		sro->addr_count = RMI_MAX_ADDR_LIST;
+
+	for (int i = 0; i < sro->addr_count; i++)
+		free_addr_range(sro->addr_list[i], 0);
+
+	sro->addr_count = 0;
+}
+EXPORT_SYMBOL_GPL(rmi_sro_free);
+
+long rmi_sro_memxfer_execute(struct rmi_sro_state *sro, gfp_t gfp)
+{
+	struct arm_smccc_1_2_regs *regs = &sro->regs;
+	bool cancelled = false;
+	unsigned long sro_handle;
+
+	rmi_smccc_invoke(regs);
+
+	sro_handle = regs->a1;
+	while (RMI_RESULT_STATUS(regs->a0) == RMI_INCOMPLETE) {
+		bool can_cancel = RMI_RESULT_CAN_CANCEL(regs->a0) == RMI_OP_CAN_CANCEL;
+		int ret = 0;
+
+		switch (RMI_RESULT_MEMREQ(regs->a0)) {
+		case RMI_OP_MEM_REQ_NONE:
+			rmi_op_continue(sro_handle, RMI_CONTINUE_KEEP_GOING,
+					regs);
+			break;
+		case RMI_OP_MEM_REQ_DONATE:
+			ret = rmi_sro_donate(sro, sro_handle, regs->a2, regs,
+					     gfp);
+			break;
+		case RMI_OP_MEM_REQ_RECLAIM:
+			ret = rmi_sro_reclaim(sro, sro_handle, regs);
+			break;
+		default:
+			WARN_ON_ONCE(1);
+			ret = -ENXIO;
+			break;
+		}
+
+		if (ret) {
+			/*
+			 * All memory donating SROs must be cancellable. So a
+			 * failure in memory allocation shouldn't be an issue.
+			 * However, if we encounter a random failure (e.g.,
+			 * buggy RMM), don't loop forever, just give up.
+			 */
+			if (WARN_ON_ONCE(!can_cancel))
+				return ret;
+			/*
+			 * If we have already cancelled, and came back here due
+			 * to an error in MEMREQ, then there is no point
+			 * in going in loops.
+			 */
+			if (WARN_ON_ONCE(cancelled))
+				break;
+			rmi_op_cancel(sro_handle, regs);
+			cancelled = true;
+
+			if (WARN_ON_ONCE(RMI_RESULT_STATUS(regs->a0) != RMI_INCOMPLETE))
+				return ret;
+		}
+	}
+
+	if (cancelled)
+		return -ECANCELED;
+
+	return regs->a0;
+}
+EXPORT_SYMBOL_GPL(rmi_sro_memxfer_execute);
+
+/*
+ * rmi_sro_execute: Execute an RMI command that is Stateful but not memory
+ * tranfserring. Takes regs, filled with the FIDs and the arguments in place.
+ *
+ * Returns :
+ *  -ECANCELLED - If the operation had to be aborted and SRO was cancellable.
+ *  Otherwise, returns the result of the RMI command.
+ */
+long rmi_sro_execute(struct arm_smccc_1_2_regs *regs)
+{
+	bool cancelled = false;
+	unsigned long sro_handle = regs->a1;
+
+	rmi_smccc_invoke(regs);
+
+	sro_handle = regs->a1;
+	while (RMI_RESULT_STATUS(regs->a0) == RMI_INCOMPLETE) {
+		bool can_cancel = RMI_RESULT_CAN_CANCEL(regs->a0) == RMI_OP_CAN_CANCEL;
+
+		switch (RMI_RESULT_MEMREQ(regs->a0)) {
+		case RMI_OP_MEM_REQ_NONE:
+			rmi_op_continue(sro_handle, RMI_CONTINUE_KEEP_GOING,
+					regs);
+			break;
+		default:
+			WARN_ON_ONCE(1);
+			if (!can_cancel)
+				return regs->a0;
+			/*
+			 * We can't get here normally, but handle this anyway
+			 * for a buggy RMM implementation.
+			 */
+			if (cancelled)
+				return -ECANCELED;
+			rmi_op_cancel(sro_handle, regs);
+			cancelled = true;
+		}
+	}
+
+	if (cancelled)
+		return -ECANCELED;
+
+	return regs->a0;
+}
+EXPORT_SYMBOL_GPL(rmi_sro_execute);
+
+static int rmi_check_version(void)
+{
+	unsigned short version_major, version_minor;
+	unsigned long host_version = RMI_ABI_VERSION(RMI_ABI_MAJOR_VERSION,
+						     RMI_ABI_MINOR_VERSION);
+	unsigned long aa64pfr0 = read_sanitised_ftr_reg(SYS_ID_AA64PFR0_EL1);
+	struct arm_smccc_1_2_regs res = {
+		SMC_RMI_VERSION, host_version,
+	};
+
+	/* If RME isn't supported, then RMI can't be */
+	if (cpuid_feature_extract_unsigned_field(aa64pfr0, ID_AA64PFR0_EL1_RME_SHIFT) == 0)
+		return -ENXIO;
+
+	rmi_smccc_invoke(&res);
+	if (res.a0 == SMCCC_RET_NOT_SUPPORTED)
+		return -ENXIO;
+
+	version_major = RMI_ABI_VERSION_GET_MAJOR(res.a1);
+	version_minor = RMI_ABI_VERSION_GET_MINOR(res.a1);
+
+	if (res.a0 != RMI_SUCCESS) {
+		unsigned short high_version_major, high_version_minor;
+
+		high_version_major = RMI_ABI_VERSION_GET_MAJOR(res.a2);
+		high_version_minor = RMI_ABI_VERSION_GET_MINOR(res.a2);
+
+		pr_err("Unsupported RMI ABI (v%d.%d - v%d.%d) we want v%d.%d\n",
+		       version_major, version_minor,
+		       high_version_major, high_version_minor,
+		       RMI_ABI_MAJOR_VERSION,
+		       RMI_ABI_MINOR_VERSION);
+		return -ENXIO;
+	}
+
+	pr_info("RMI ABI version %d.%d\n", version_major, version_minor);
+
+	return 0;
+}
+
+static int rmi_read_features(void)
+{
+	/*
+	 * Since we've negotiated a compatible version these feature registers
+	 * should always be available
+	 */
+	for (int i = 0; i < ARRAY_SIZE(rmi_feat_reg_cache); i++) {
+		struct arm_smccc_1_2_regs args = {
+			SMC_RMI_FEATURES, i,
+		};
+
+		rmi_smccc_invoke(&args);
+		if (WARN_ON(args.a0 != RMI_SUCCESS))
+			return -EINVAL;
+
+		rmi_feat_reg_cache[i] = args.a1;
+	}
+
+	return 0;
+}
+
+unsigned long rmi_feat_reg(unsigned long index)
+{
+	if (WARN_ON(index >= ARRAY_SIZE(rmi_feat_reg_cache)))
+		return 0;
+
+	return rmi_feat_reg_cache[index];
+}
+EXPORT_SYMBOL_GPL(rmi_feat_reg);
+
+static int rmi_rmm_config_set(unsigned long cfg_ptr)
+{
+	struct arm_smccc_1_2_regs regs = {
+		SMC_RMI_RMM_CONFIG_SET, cfg_ptr,
+	};
+
+	rmi_smccc_invoke(&regs);
+
+	return regs.a0;
+}
+
+static int rmi_configure(void)
+{
+	unsigned long granule_feature;
+	unsigned long granule_size;
+	int ret = 0;
+
+	switch (PAGE_SIZE) {
+	case SZ_4K:
+		granule_size = RMI_GRANULE_SIZE_4KB;
+		granule_feature = RMI_FEATURE_REGISTER_1_RMI_GRAN_SZ_4KB;
+		break;
+	case SZ_16K:
+		granule_size = RMI_GRANULE_SIZE_16KB;
+		granule_feature = RMI_FEATURE_REGISTER_1_RMI_GRAN_SZ_16KB;
+		break;
+	case SZ_64K:
+		granule_size = RMI_GRANULE_SIZE_64KB;
+		granule_feature = RMI_FEATURE_REGISTER_1_RMI_GRAN_SZ_64KB;
+		break;
+	default:
+		BUILD_BUG();
+	}
+
+	if (!(rmi_feat_reg(1) & granule_feature)) {
+		pr_err("RMM does not support %luKB granules\n",
+		       PAGE_SIZE >> 10);
+		return -ENXIO;
+	}
+
+	struct rmm_config *config __free(free_page) =
+		(struct rmm_config *)get_zeroed_page(GFP_KERNEL);
+
+	if (!config) {
+		pr_err("Unable to allocate memory for RMM config\n");
+		return -ENOMEM;
+	}
+
+	config->rmi_granule_size = granule_size;
+
+	/*
+	 * For now we set the tracking_region_size to 0 which is the only option
+	 * for 4KB PAGE_SIZE (1GB for 4KB PAGE_SIZE, 32MB/512MB for 16KB/64KB).
+	 * TODO: Support other tracking sizes via Kconfig option for other
+	 * PAGE_SIZES
+	 */
+	config->tracking_region_size = 0;
+
+	ret = rmi_rmm_config_set(virt_to_phys(config));
+	if (ret) {
+		pr_err("RMM config set failed (%d)\n", ret);
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+/**
+ * rmi_granule_tracking_get() - Get configuration of a Granule tracking region
+ * @start: Base PA of the tracking region
+ * @end: End of the PA region
+ * @out_category: Memory category
+ * @out_state: Tracking region state
+ * @out_top: Top of the memory region
+ *
+ * Return: RMI return code
+ */
+static int rmi_granule_tracking_get(unsigned long start,
+				    unsigned long end,
+				    unsigned long *out_category,
+				    unsigned long *out_state,
+				    unsigned long *out_top)
+{
+	struct arm_smccc_1_2_regs regs = {
+		SMC_RMI_GRANULE_TRACKING_GET, start, end,
+	};
+
+	rmi_smccc_invoke(&regs);
+
+	if (regs.a0 != RMI_SUCCESS)
+		return regs.a0;
+
+	if (out_category)
+		*out_category = regs.a1;
+	if (out_state)
+		*out_state = regs.a2;
+	if (out_top)
+		*out_top = regs.a3;
+
+	return RMI_SUCCESS;
+}
+
+/*
+ * Make sure the area is tracked by RMM at FINE granularity.
+ * We do not support changing the tracking yet.
+ */
+static int rmi_verify_memory_tracking(phys_addr_t start, phys_addr_t end)
+{
+	while (start < end) {
+		unsigned long ret, category, state, next;
+
+		ret = rmi_granule_tracking_get(start, end, &category, &state, &next);
+		if (ret != RMI_SUCCESS)
+			return -ENOMEM;
+
+		if (WARN_ON(next <= start))
+			return -ENXIO;
+
+		if (state != RMI_TRACKING_FINE ||
+		    category != RMI_MEM_CATEGORY_CONVENTIONAL) {
+			/* TODO: Set granule tracking in this case */
+			pr_err("Granule tracking for region isn't fine/conventional: %llx-%lx\n",
+			       start, next);
+			return -ENODEV;
+		}
+		start = next;
+	}
+
+	return 0;
+}
+
+/*
+ * 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
+ * @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(&regs);
+	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.
+ */
+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;
+}
+
+static int rmi_prepare_memory(phys_addr_t start, phys_addr_t end)
+{
+	int ret;
+
+	if (start >= end)
+		return -EINVAL;
+
+	ret = rmi_verify_memory_tracking(start, end);
+	if (ret)
+		return ret;
+
+	return rmi_verify_gpt_firmware_managed(start, end);
+}
+
+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);
+		/* Too small ? */
+		if (start >= end)
+			continue;
+
+		ret = rmi_prepare_memory(start, end);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int rmi_memory_notifier(struct notifier_block *nb,
+			       unsigned long action, void *data)
+{
+	struct memory_notify *arg = data;
+	phys_addr_t start, end;
+	int ret;
+
+	if (action != MEM_GOING_ONLINE)
+		return NOTIFY_DONE;
+
+	start = PFN_PHYS(arg->start_pfn);
+	end = PFN_PHYS(arg->start_pfn + arg->nr_pages);
+	ret = rmi_prepare_memory(start, end);
+
+	return notifier_from_errno(ret);
+}
+
+static struct notifier_block rmi_memory_nb = {
+	.notifier_call = rmi_memory_notifier,
+};
+
+bool is_rmi_available(void)
+{
+	return arm64_rmi_is_available;
+}
+EXPORT_SYMBOL_GPL(is_rmi_available);
+
+static int rmi_init_memory(void)
+{
+	int ret;
+
+	ret = rmi_init_metadata();
+	if (ret)
+		return ret;
+
+	return register_memory_notifier(&rmi_memory_nb);
+}
+
+static int __init arm64_init_rmi(void)
+{
+	int ret;
+
+	/* If we can't agree on the RMI ABI version, don't proceed further */
+	ret = rmi_check_version();
+	if (ret)
+		return ret;
+
+	ret = rmi_read_features();
+	if (ret)
+		return ret;
+
+	ret = rmi_configure();
+	if (ret)
+		return ret;
+
+	/* Activate the RMM */
+	struct rmi_sro_state *sro __free(kfree) = kmalloc_obj(*sro);
+	if (!sro)
+		return -ENOMEM;
+
+	ret = rmi_sro_memxfer_cmd(sro, GFP_KERNEL, SMC_RMI_RMM_ACTIVATE);
+	if (ret) {
+		pr_err("RMM activate failed (%d)\n", ret);
+		ret = ret < 0 ? ret : -ENXIO;
+		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");
+	return 0;
+}
+
+/*
+ * Note arm64_init_rmi() must be called before kvm_init_rmi() otherwise KVM
+ * will not support realm guests. subsys_initcall() is called before
+ * module_init() (used for KVM) so this is OK.
+ */
+subsys_initcall(arm64_init_rmi);
diff --git a/include/linux/arm-rmi-cmds.h b/include/linux/arm-rmi-cmds.h
new file mode 100644
index 0000000000000..e03dd8316695d
--- /dev/null
+++ b/include/linux/arm-rmi-cmds.h
@@ -0,0 +1,558 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2026 ARM Ltd.
+ */
+
+#ifndef __LINUX_ARM_RMI_CMDS_H_
+#define __LINUX_ARM_RMI_CMDS_H_
+
+#include <linux/arm-smccc-rmi.h>
+#include <linux/bug.h>
+#include <linux/gfp.h>
+#include <linux/processor.h>
+#include <linux/string.h>
+#include <linux/types.h>
+
+#define RMI_MAX_ADDR_LIST	256
+
+struct rmi_sro_state {
+	struct arm_smccc_1_2_regs regs;
+	int addr_count;
+	unsigned long addr_list[RMI_MAX_ADDR_LIST];
+};
+
+#define RMM_BLOCKED_RETRY_COUNT		2
+/*
+ * rmi_smccc_invoke: Invoke the RMI call and return the results, retrying the
+ * command when status is RMI_BUSY. If we encounter RMI_BLOCKED, we retry
+ * it one more time before we give up. The caller is supposed to handle the
+ * result and reissue if required.
+ *
+ * We don't expect to see RMI_BLOCKED on a practical system, except when
+ * there are parallel requests that results in long standing operation,
+ * with one blocking the other.
+ *
+ * @regs: Input parameters filled in. Updated with the ouptput results
+ * after the call.
+ */
+static inline void rmi_smccc_invoke(struct arm_smccc_1_2_regs *regs)
+{
+	struct arm_smccc_1_2_regs args = *regs;
+	long status;
+	int i = 0;
+
+	while (i < RMM_BLOCKED_RETRY_COUNT) {
+		arm_smccc_1_2_invoke(&args, regs);
+
+		status = RMI_RESULT_STATUS(regs->a0);
+		if (status != RMI_BUSY && status != RMI_BLOCKED)
+			break;
+		if (status == RMI_BLOCKED)
+			i++;
+		cpu_relax();
+	}
+}
+
+unsigned long rmi_feat_reg(unsigned long index);
+
+int rmi_delegate_range(phys_addr_t phys, unsigned long size,
+		       phys_addr_t *out_phys);
+int rmi_undelegate_range(phys_addr_t phys, unsigned long size);
+int rmi_free_delegated_page(phys_addr_t phys);
+
+static inline int rmi_delegate_page(phys_addr_t phys)
+{
+	return rmi_delegate_range(phys, PAGE_SIZE, NULL);
+}
+
+static inline int rmi_undelegate_page(phys_addr_t phys)
+{
+	return rmi_undelegate_range(phys, PAGE_SIZE);
+}
+
+bool is_rmi_available(void);
+
+long rmi_sro_memxfer_execute(struct rmi_sro_state *sro, gfp_t gfp);
+void rmi_sro_free(struct rmi_sro_state *sro);
+long rmi_sro_execute(struct arm_smccc_1_2_regs *regs);
+
+/*
+ * Resetting the addr_count is sufficient to ignore the addr_list contents.
+ */
+#define rmi_sro_memxfer_cmd(sro, gfp, ...) ({				\
+	struct rmi_sro_state *__sro = (sro);				\
+	__sro->addr_count = 0;						\
+	__sro->regs = (struct arm_smccc_1_2_regs){ __VA_ARGS__ };	\
+	long __ret = rmi_sro_memxfer_execute(__sro, gfp);		\
+	rmi_sro_free(__sro);						\
+	__ret;								\
+})
+
+/**
+ * rmi_rtt_data_map_init() - Create a mapping at protected IPA, copying contents
+ *			     from a given non-secure source granule.
+ * @rd: PA of the RD
+ * @data: PA of the target granule mapped in the guest
+ * @ipa: IPA at which the granule @data will be mapped in the guest
+ * @src: PA of the source granule with contents
+ * @flags: RMI_MEASURE_CONTENT if the contents should be measured
+ *
+ * Create a mapping from Protected IPA space to conventional memory, copying
+ * contents from a Non-secure Granule provided by the caller.
+ *
+ * Return: 0 on success, positive RMI result code or negative Linux error code
+ */
+static inline long rmi_rtt_data_map_init(unsigned long rd, unsigned long data,
+					 unsigned long ipa, unsigned long src,
+					 unsigned long flags)
+{
+	struct arm_smccc_1_2_regs regs = {
+		SMC_RMI_RTT_DATA_MAP_INIT, rd, data, ipa, src, flags
+	};
+
+	return rmi_sro_execute(&regs);
+}
+
+/**
+ * rmi_rtt_data_map() - Create mappings in protected IPA range with unknown contents
+ * @rd: PA of the RD
+ * @base: Base of the target IPA range
+ * @top: Top of the target IPA range
+ * @flags: Flags
+ * @oaddr: Output address set descriptor
+ * @out_top: Top address of range which was processed.
+ *
+ * Return: 0 on success, positive RMI result code or negative Linux error code
+ */
+static inline long rmi_rtt_data_map(unsigned long rd,
+				    unsigned long base,
+				    unsigned long top,
+				    unsigned long flags,
+				    unsigned long oaddr,
+				    unsigned long *out_top)
+{
+	struct arm_smccc_1_2_regs regs = {
+		SMC_RMI_RTT_DATA_MAP, rd, base, top, flags, oaddr
+	};
+	long ret;
+
+	ret = rmi_sro_execute(&regs);
+
+	if (ret == RMI_SUCCESS && out_top)
+		*out_top = regs.a1;
+
+	return ret;
+}
+
+/**
+ * rmi_rtt_data_unmap() - Remove mappings to conventional memory at a protected
+ *			  IPA range
+ * @rd: PA of the RD
+ * @base: Base of the target IPA range
+ * @top: Top of the target IPA range
+ * @flags: Flags
+ * @oaddr: Output address set descriptor
+ * @out_top: Returns top IPA of range which has been unmapped
+ * @out_range: Output address range
+ * @out_count: Number of entries in output address list
+ *
+ * Removes mappings to convention memory with a target Protected IPA range.
+ *
+ * Return: 0 on success, positive RMI result code or negative Linux error code
+ */
+static inline long rmi_rtt_data_unmap(unsigned long rd,
+				      unsigned long base,
+				      unsigned long top,
+				      unsigned long flags,
+				      unsigned long oaddr,
+				      unsigned long *out_top,
+				      unsigned long *out_range,
+				      unsigned long *out_count)
+{
+	struct arm_smccc_1_2_regs regs = {
+		SMC_RMI_RTT_DATA_UNMAP, rd, base, top, flags, oaddr
+	};
+	long ret;
+
+	ret = rmi_sro_execute(&regs);
+
+	if (ret != RMI_SUCCESS)
+		return ret;
+
+	if (out_top)
+		*out_top = regs.a1;
+	if (out_range)
+		*out_range = regs.a2;
+	if (out_count)
+		*out_count = regs.a3;
+
+	return RMI_SUCCESS;
+}
+
+/**
+ * rmi_psci_complete() - Complete pending PSCI command
+ * @calling_rec: PA of the calling REC
+ * @status: Status of the PSCI request
+ *
+ * Completes a pending PSCI command.
+ *
+ * Return: RMI return code
+ */
+static inline long rmi_psci_complete(unsigned long calling_rec,
+				     unsigned long status)
+{
+	struct arm_smccc_1_2_regs regs = {
+		SMC_RMI_PSCI_COMPLETE, calling_rec, status,
+	};
+
+	rmi_smccc_invoke(&regs);
+	return regs.a0;
+}
+
+/**
+ * rmi_realm_activate() - Activate a realm
+ * @rd: PA of the RD
+ *
+ * Mark a realm as Active, signalling that creation is completed, allowing
+ * execution of the realm.
+ *
+ * Return: RMI return code
+ */
+static inline long rmi_realm_activate(unsigned long rd)
+{
+	struct arm_smccc_1_2_regs regs = {
+		SMC_RMI_REALM_ACTIVATE, rd,
+	};
+
+	rmi_smccc_invoke(&regs);
+	return regs.a0;
+}
+
+/**
+ * rmi_realm_create() - Create a realm
+ * @rd: PA of the RD
+ * @params: PA of realm parameters
+ * @sro: Preallocated SRO context
+ *
+ * Create a new realm using the given parameters.
+ *
+ * Return: 0 on success, positive RMI result code or negative Linux error code
+ */
+static inline long rmi_realm_create(unsigned long rd, unsigned long params,
+				    struct rmi_sro_state *sro)
+{
+	return rmi_sro_memxfer_cmd(sro, GFP_KERNEL,
+				   SMC_RMI_REALM_CREATE, rd, params);
+}
+
+/**
+ * rmi_realm_terminate() - Terminate a realm
+ * @rd: PA of the RD
+ * @sro: Preallocated SRO context
+ *
+ * Terminates a realm, moving it into a ZOMBIE state
+ *
+ * Return: 0 on success, positive RMI result code or negative Linux error code
+ */
+static inline long rmi_realm_terminate(unsigned long rd,
+				       struct rmi_sro_state *sro)
+{
+	return rmi_sro_memxfer_cmd(sro, GFP_KERNEL,
+				   SMC_RMI_REALM_TERMINATE, rd);
+}
+
+/**
+ * rmi_realm_destroy() - Destroy a realm
+ * @rd: PA of the RD
+ * @sro: Preallocated SRO context
+ *
+ * Destroys a realm, all objects belonging to the realm must be destroyed first.
+ *
+ * Return: 0 on success, positive RMI result code or negative Linux error code
+ */
+static inline long rmi_realm_destroy(unsigned long rd,
+				     struct rmi_sro_state *sro)
+{
+	return rmi_sro_memxfer_cmd(sro, GFP_KERNEL,
+				   SMC_RMI_REALM_DESTROY, rd);
+}
+
+/**
+ * rmi_rec_create() - Create a REC
+ * @rd: PA of the RD
+ * @rec: PA of the target REC
+ * @params: PA of REC parameters
+ * @sro: Allocated SRO context to be used
+ *
+ * Create a REC using the parameters specified in the struct rec_params pointed
+ * to by @params.
+ *
+ * Return: 0 on success, positive RMI result code or negative Linux error code
+ */
+static inline long rmi_rec_create(unsigned long rd,
+				  unsigned long rec,
+				  unsigned long params,
+				  struct rmi_sro_state *sro)
+{
+	long ret;
+
+	sro->addr_count = 0;
+	sro->regs = (struct arm_smccc_1_2_regs) {
+		SMC_RMI_REC_CREATE, rd, rec, params
+	};
+	ret = rmi_sro_memxfer_execute(sro, GFP_KERNEL);
+	rmi_sro_free(sro);
+
+	return ret;
+}
+
+/**
+ * rmi_rec_destroy() - Destroy a REC
+ * @rec: PA of the target REC
+ * @sro: Allocated SRO context to be used
+ *
+ * Destroys a REC. The REC must not be running.
+ *
+ * Return: 0 on success, positive RMI result code or negative Linux error code
+ */
+static inline long rmi_rec_destroy(unsigned long rec,
+				   struct rmi_sro_state *sro)
+{
+	return rmi_sro_memxfer_cmd(sro, GFP_KERNEL, SMC_RMI_REC_DESTROY, rec);
+}
+
+/**
+ * rmi_rec_enter() - Enter a REC
+ * @rec: PA of the target REC
+ * @run_ptr: PA of RecRun structure
+ *
+ * Starts (or continues) execution within a REC.
+ *
+ * Return: RMI return code
+ */
+static inline long rmi_rec_enter(unsigned long rec, unsigned long run_ptr)
+{
+	struct arm_smccc_1_2_regs regs = {
+		SMC_RMI_REC_ENTER, rec, run_ptr,
+	};
+
+	rmi_smccc_invoke(&regs);
+	return regs.a0;
+}
+
+/**
+ * rmi_rtt_create() - Creates an RTT
+ * @rd: PA of the RD
+ * @rtt: PA of the target RTT
+ * @ipa: Base of the IPA range described by the RTT
+ * @level: Depth of the RTT within the tree
+ *
+ * Creates an RTT (Realm Translation Table) at the specified level for the
+ * translation of the specified address within the realm.
+ *
+ * Return: 0 on success, positive RMI result code or negative Linux error code
+ */
+static inline long rmi_rtt_create(unsigned long rd, unsigned long rtt,
+				  unsigned long ipa, long level)
+{
+	struct arm_smccc_1_2_regs regs = {
+		SMC_RMI_RTT_CREATE, rd, rtt, ipa, level
+	};
+
+	return rmi_sro_execute(&regs);
+}
+
+/**
+ * rmi_rtt_destroy() - Destroy an RTT
+ * @rd: PA of the RD
+ * @ipa: Base of the IPA range described by the RTT
+ * @level: RTT level
+ * @out_rtt: Pointer to write the PA of the RTT which was destroyed
+ * @out_top: Pointer to write the top IPA of non-live RTT entries, from entry
+ * at which the RTT walk terminated.
+ *
+ * Destroys an RTT. The RTT must be non-live, i.e. none of the entries in the
+ * table are in ASSIGNED or TABLE state.
+ *
+ * Return: 0 on success, positive RMI result code or negative Linux error code.
+ */
+static inline long rmi_rtt_destroy(unsigned long rd,
+				   unsigned long ipa,
+				   long level,
+				   unsigned long *out_rtt,
+				   unsigned long *out_top)
+{
+	struct arm_smccc_1_2_regs regs = {
+		SMC_RMI_RTT_DESTROY, rd, ipa, level
+	};
+	long ret = rmi_sro_execute(&regs);
+
+	switch (RMI_RESULT_STATUS(ret)) {
+	case RMI_SUCCESS:
+		if (out_rtt)
+			*out_rtt = regs.a1;
+		fallthrough;
+	case RMI_ERROR_RTT:
+		if (out_top)
+			*out_top = regs.a2;
+		break;
+	default:
+		break;
+	}
+
+	return ret;
+}
+
+/**
+ * rmi_rtt_fold() - Fold an RTT
+ * @rd: PA of the RD
+ * @ipa: Base of the IPA range described by the RTT
+ * @level: Depth of the RTT within the tree
+ * @out_rtt: Pointer to write the PA of the RTT which was destroyed
+ *
+ * Folds an RTT. If all entries with the RTT are 'homogeneous' the RTT can be
+ * folded into the parent and the RTT destroyed.
+ *
+ * Return: 0 on success, positive RMI result code or negative Linux error code
+ */
+static inline long rmi_rtt_fold(unsigned long rd, unsigned long ipa,
+				long level, unsigned long *out_rtt)
+{
+	struct arm_smccc_1_2_regs regs = {
+		SMC_RMI_RTT_FOLD, rd, ipa, level
+	};
+	long ret = rmi_sro_execute(&regs);
+
+	if (ret == RMI_SUCCESS && out_rtt)
+		*out_rtt = regs.a1;
+
+	return ret;
+}
+
+/**
+ * rmi_rtt_init_ripas() - Set RIPAS for new realm
+ * @rd: PA of the RD
+ * @base: Base of target IPA region
+ * @top: Top of target IPA region
+ * @out_top: Top IPA of range whose RIPAS was modified
+ *
+ * Sets the RIPAS of a target IPA range to RAM, for a realm in the NEW state.
+ *
+ * Return: 0 on success, positive RMI result code or negative Linux error code
+ */
+static inline long rmi_rtt_init_ripas(unsigned long rd, unsigned long base,
+				      unsigned long top, unsigned long *out_top)
+{
+	struct arm_smccc_1_2_regs regs = {
+		SMC_RMI_RTT_INIT_RIPAS, rd, base, top
+	};
+	long ret = rmi_sro_execute(&regs);
+
+	if (ret == RMI_SUCCESS && out_top)
+		*out_top = regs.a1;
+
+	return ret;
+}
+
+/**
+ * rmi_rtt_unprot_map() - Map unprotected granules into a realm
+ * @rd: PA of the RD
+ * @base: Base IPA of the mapping
+ * @top: Top of the target IPA range
+ * @flags: Flags
+ * @oaddr: Output address set descriptor
+ * @out_top: Top IPA of range which has been mapped
+ *
+ * Create mappings to memory within a target unprotected IPA range.
+ *
+ * Return: 0 on success, positive RMI result code or negative Linux error code
+ */
+static inline long rmi_rtt_unprot_map(unsigned long rd,
+				      unsigned long base,
+				      unsigned long top,
+				      unsigned long flags,
+				      unsigned long oaddr,
+				      unsigned long *out_top)
+{
+	struct arm_smccc_1_2_regs regs = {
+		SMC_RMI_RTT_UNPROT_MAP, rd, base, top, flags, oaddr
+	};
+	long ret = rmi_sro_execute(&regs);
+
+	if (ret == RMI_SUCCESS && out_top)
+		*out_top = regs.a1;
+
+	return ret;
+}
+
+/**
+ * rmi_rtt_set_ripas() - Set RIPAS for an running realm
+ * @rd: PA of the RD
+ * @rec: PA of the REC making the request
+ * @base: Base of target IPA region
+ * @top: Top of target IPA region
+ * @out_top: Pointer to write top IPA of range whose RIPAS was modified
+ *
+ * Completes a request made by the realm to change the RIPAS of a target IPA
+ * range.
+ *
+ * Return: 0 on success, positive RMI result code or negative Linux error code
+ */
+static inline long rmi_rtt_set_ripas(unsigned long rd, unsigned long rec,
+				     unsigned long base, unsigned long top,
+				     unsigned long *out_top)
+{
+	struct arm_smccc_1_2_regs regs = {
+		SMC_RMI_RTT_SET_RIPAS, rd, rec, base, top
+	};
+	long ret = rmi_sro_execute(&regs);
+
+	if (ret == RMI_SUCCESS && out_top)
+		*out_top = regs.a1;
+
+	return ret;
+}
+
+/**
+ * rmi_rtt_unprot_unmap() - Remove mappings within an unprotected IPA range
+ * @rd: PA of the RD
+ * @base: Base IPA of the mapping
+ * @top: Top of the target IPA range
+ * @flags: Flags
+ * @oaddr: Output address set descriptor
+ * @out_top: Top IPA which has been unmapped
+ * @out_range: Output address range
+ * @out_count: Number of entries in output address list
+ *
+ * Removes mappings to memory within a target unprotected IPA range.
+ *
+ * Return: 0 on success, positive RMI result code or negative Linux error code
+ */
+static inline long rmi_rtt_unprot_unmap(unsigned long rd,
+					unsigned long base,
+					unsigned long top,
+					unsigned long flags,
+					unsigned long oaddr,
+					unsigned long *out_top,
+					unsigned long *out_range,
+					unsigned long *out_count)
+{
+	struct arm_smccc_1_2_regs regs = {
+		SMC_RMI_RTT_UNPROT_UNMAP, rd, base, top, flags, oaddr
+	};
+	long ret = rmi_sro_execute(&regs);
+
+	if (ret != RMI_SUCCESS)
+		return ret;
+
+	if (out_top)
+		*out_top = regs.a1;
+	if (out_range)
+		*out_range = regs.a2;
+	if (out_count)
+		*out_count = regs.a3;
+
+	return RMI_SUCCESS;
+}
+
+#endif
diff --git a/include/linux/arm-smccc-rmi.h b/include/linux/arm-smccc-rmi.h
new file mode 100644
index 0000000000000..3b2a3ae6d81cd
--- /dev/null
+++ b/include/linux/arm-smccc-rmi.h
@@ -0,0 +1,505 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2023-2026 ARM Ltd.
+ *
+ * The values and structures in this file are from the Realm Management Monitor
+ * specification (DEN0137) version 2.0-bet3:
+ * https://developer.arm.com/documentation/den0137/2-0bet3/
+ */
+
+#ifndef __LINUX_ARM_SMCCC_RMI_H_
+#define __LINUX_ARM_SMCCC_RMI_H_
+
+#include <linux/arm-smccc.h>
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/build_bug.h>
+#include <linux/sizes.h>
+
+#include <asm/page.h>
+
+#define SMC_RMI_CALL(func)				\
+	ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL,		\
+			   ARM_SMCCC_SMC_64,		\
+			   ARM_SMCCC_OWNER_STANDARD,	\
+			   (func))
+
+#define SMC_RMI_VERSION				SMC_RMI_CALL(0x0150)
+
+#define SMC_RMI_RTT_DATA_MAP_INIT		SMC_RMI_CALL(0x0153)
+
+#define SMC_RMI_REALM_ACTIVATE			SMC_RMI_CALL(0x0157)
+#define SMC_RMI_REALM_CREATE			SMC_RMI_CALL(0x0158)
+#define SMC_RMI_REALM_DESTROY			SMC_RMI_CALL(0x0159)
+#define SMC_RMI_REC_CREATE			SMC_RMI_CALL(0x015a)
+#define SMC_RMI_REC_DESTROY			SMC_RMI_CALL(0x015b)
+#define SMC_RMI_REC_ENTER			SMC_RMI_CALL(0x015c)
+#define SMC_RMI_RTT_CREATE			SMC_RMI_CALL(0x015d)
+#define SMC_RMI_RTT_DESTROY			SMC_RMI_CALL(0x015e)
+
+#define SMC_RMI_RTT_READ_ENTRY			SMC_RMI_CALL(0x0161)
+
+#define SMC_RMI_RTT_DEV_VALIDATE		SMC_RMI_CALL(0x0163)
+#define SMC_RMI_PSCI_COMPLETE			SMC_RMI_CALL(0x0164)
+#define SMC_RMI_FEATURES			SMC_RMI_CALL(0x0165)
+#define SMC_RMI_RTT_FOLD			SMC_RMI_CALL(0x0166)
+
+#define SMC_RMI_RTT_INIT_RIPAS			SMC_RMI_CALL(0x0168)
+#define SMC_RMI_RTT_SET_RIPAS			SMC_RMI_CALL(0x0169)
+#define SMC_RMI_VSMMU_CREATE			SMC_RMI_CALL(0x016a)
+#define SMC_RMI_VSMMU_DESTROY			SMC_RMI_CALL(0x016b)
+
+#define SMC_RMI_RMM_CONFIG_SET			SMC_RMI_CALL(0x016e)
+#define SMC_RMI_PSMMU_IRQ_NOTIFY		SMC_RMI_CALL(0x016f)
+#define SMC_RMI_ATTEST_PLAT_TOKEN_REFRESH	SMC_RMI_CALL(0x0170)
+
+#define SMC_RMI_PDEV_ABORT			SMC_RMI_CALL(0x0174)
+#define SMC_RMI_PDEV_COMMUNICATE		SMC_RMI_CALL(0x0175)
+#define SMC_RMI_PDEV_CREATE			SMC_RMI_CALL(0x0176)
+#define SMC_RMI_PDEV_DESTROY			SMC_RMI_CALL(0x0177)
+#define SMC_RMI_PDEV_GET_STATE			SMC_RMI_CALL(0x0178)
+
+#define SMC_RMI_PDEV_STREAM_KEY_REFRESH		SMC_RMI_CALL(0x017a)
+#define SMC_RMI_PDEV_SET_PUBKEY			SMC_RMI_CALL(0x017b)
+#define SMC_RMI_PDEV_STOP			SMC_RMI_CALL(0x017c)
+#define SMC_RMI_RTT_AUX_CREATE			SMC_RMI_CALL(0x017d)
+#define SMC_RMI_RTT_AUX_DESTROY			SMC_RMI_CALL(0x017e)
+#define SMC_RMI_RTT_AUX_FOLD			SMC_RMI_CALL(0x017f)
+
+#define SMC_RMI_VDEV_ABORT			SMC_RMI_CALL(0x0185)
+#define SMC_RMI_VDEV_COMMUNICATE		SMC_RMI_CALL(0x0186)
+#define SMC_RMI_VDEV_CREATE			SMC_RMI_CALL(0x0187)
+#define SMC_RMI_VDEV_DESTROY			SMC_RMI_CALL(0x0188)
+#define SMC_RMI_VDEV_GET_STATE			SMC_RMI_CALL(0x0189)
+#define SMC_RMI_VDEV_UNLOCK			SMC_RMI_CALL(0x018a)
+#define SMC_RMI_RTT_SET_S2AP			SMC_RMI_CALL(0x018b)
+
+#define SMC_RMI_VDEV_GET_INTERFACE_REPORT	SMC_RMI_CALL(0x01d0)
+#define SMC_RMI_VDEV_GET_MEASUREMENTS		SMC_RMI_CALL(0x01d1)
+#define SMC_RMI_VDEV_LOCK			SMC_RMI_CALL(0x01d2)
+#define SMC_RMI_VDEV_START			SMC_RMI_CALL(0x01d3)
+
+#define SMC_RMI_VSMMU_EVENT_HANDLE		SMC_RMI_CALL(0x01d6)
+#define SMC_RMI_PSMMU_ACTIVATE			SMC_RMI_CALL(0x01d7)
+#define SMC_RMI_PSMMU_DEACTIVATE		SMC_RMI_CALL(0x01d8)
+
+#define SMC_RMI_PSMMU_ST_L2_CREATE		SMC_RMI_CALL(0x01db)
+#define SMC_RMI_PSMMU_ST_L2_DESTROY		SMC_RMI_CALL(0x01dc)
+#define SMC_RMI_DPT_L0_CREATE			SMC_RMI_CALL(0x01dd)
+#define SMC_RMI_DPT_L0_DESTROY			SMC_RMI_CALL(0x01de)
+#define SMC_RMI_DPT_L1_CREATE			SMC_RMI_CALL(0x01df)
+#define SMC_RMI_DPT_L1_DESTROY			SMC_RMI_CALL(0x01e0)
+#define SMC_RMI_GRANULE_TRACKING_GET		SMC_RMI_CALL(0x01e1)
+
+#define SMC_RMI_GRANULE_TRACKING_SET		SMC_RMI_CALL(0x01e3)
+
+#define SMC_RMI_RMM_CONFIG_GET			SMC_RMI_CALL(0x01ec)
+
+#define SMC_RMI_RMM_STATE_GET			SMC_RMI_CALL(0x01ee)
+
+#define SMC_RMI_PSMMU_EVENT_CONSUME		SMC_RMI_CALL(0x01f0)
+#define SMC_RMI_GRANULE_RANGE_DELEGATE		SMC_RMI_CALL(0x01f1)
+#define SMC_RMI_GRANULE_RANGE_UNDELEGATE	SMC_RMI_CALL(0x01f2)
+#define SMC_RMI_GPT_L1_CREATE			SMC_RMI_CALL(0x01f3)
+#define SMC_RMI_GPT_L1_DESTROY			SMC_RMI_CALL(0x01f4)
+#define SMC_RMI_RTT_DATA_MAP			SMC_RMI_CALL(0x01f5)
+#define SMC_RMI_RTT_DATA_UNMAP			SMC_RMI_CALL(0x01f6)
+#define SMC_RMI_RTT_DEV_MAP			SMC_RMI_CALL(0x01f7)
+#define SMC_RMI_RTT_DEV_UNMAP			SMC_RMI_CALL(0x01f8)
+#define SMC_RMI_RTT_ARCH_DEV_MAP		SMC_RMI_CALL(0x01f9)
+#define SMC_RMI_RTT_ARCH_DEV_UNMAP		SMC_RMI_CALL(0x01fa)
+#define SMC_RMI_RTT_UNPROT_MAP			SMC_RMI_CALL(0x01fb)
+#define SMC_RMI_RTT_UNPROT_UNMAP		SMC_RMI_CALL(0x01fc)
+#define SMC_RMI_RTT_AUX_PROT_MAP		SMC_RMI_CALL(0x01fd)
+#define SMC_RMI_RTT_AUX_PROT_UNMAP		SMC_RMI_CALL(0x01fe)
+#define SMC_RMI_RTT_AUX_UNPROT_MAP		SMC_RMI_CALL(0x01ff)
+#define SMC_RMI_RTT_AUX_UNPROT_UNMAP		SMC_RMI_CALL(0x0200)
+#define SMC_RMI_REALM_TERMINATE			SMC_RMI_CALL(0x0201)
+#define SMC_RMI_RMM_ACTIVATE			SMC_RMI_CALL(0x0202)
+#define SMC_RMI_OP_CONTINUE			SMC_RMI_CALL(0x0203)
+#define SMC_RMI_PDEV_STREAM_CONNECT		SMC_RMI_CALL(0x0204)
+#define SMC_RMI_PDEV_STREAM_DISCONNECT		SMC_RMI_CALL(0x0205)
+#define SMC_RMI_PDEV_STREAM_COMPLETE		SMC_RMI_CALL(0x0206)
+#define SMC_RMI_PDEV_STREAM_KEY_PURGE		SMC_RMI_CALL(0x0207)
+#define SMC_RMI_OP_MEM_DONATE			SMC_RMI_CALL(0x0208)
+#define SMC_RMI_OP_MEM_RECLAIM			SMC_RMI_CALL(0x0209)
+#define SMC_RMI_OP_CANCEL			SMC_RMI_CALL(0x020a)
+#define SMC_RMI_VSMMU_FEATURES			SMC_RMI_CALL(0x020b)
+#define SMC_RMI_VSMMU_CMD_GET			SMC_RMI_CALL(0x020c)
+#define SMC_RMI_VSMMU_CMD_COMPLETE		SMC_RMI_CALL(0x020d)
+#define SMC_RMI_PSMMU_INFO			SMC_RMI_CALL(0x020e)
+#define SMC_RMI_RMM_DEACTIVATE			SMC_RMI_CALL(0x020f)
+#define SMC_RMI_PDEV_STREAM_INFO		SMC_RMI_CALL(0x0210)
+#define SMC_RMI_GPT_INFO			SMC_RMI_CALL(0x0211)
+
+#define RMI_ABI_MAJOR_VERSION	2
+#define RMI_ABI_MINOR_VERSION	0
+
+#define RMI_ABI_VERSION_MAJOR_MASK		GENMASK(30, 16)
+#define RMI_ABI_VERSION_MINOR_MASK		GENMASK(15, 0)
+
+#define RMI_ABI_VERSION_GET_MAJOR(v)	FIELD_GET(RMI_ABI_VERSION_MAJOR_MASK, (v))
+#define RMI_ABI_VERSION_GET_MINOR(v)	FIELD_GET(RMI_ABI_VERSION_MINOR_MASK, (v))
+#define RMI_ABI_VERSION(major, minor)				\
+	(FIELD_PREP(RMI_ABI_VERSION_MAJOR_MASK, (major)) |	\
+	 FIELD_PREP(RMI_ABI_VERSION_MINOR_MASK, (minor)))
+
+/* RmiResult Type definitions */
+#define RMI_RESULT_STATUS_MASK		GENMASK(7, 0)
+/* RmiResultDataLevel field */
+#define RMI_RESULT_DATA_LEVEL_MASK	GENMASK(15, 8)
+/* RmiResultDataIncomplete fields */
+#define RMI_RESULT_MEMREQ_MASK		GENMASK(9, 8)
+#define RMI_RESULT_CAN_CANCEL_MASK	BIT(10)
+
+#define RMI_RESULT_STATUS(ret)		FIELD_GET(RMI_RESULT_STATUS_MASK, ret)
+#define RMI_RESULT_DATA_LEVEL(ret)	FIELD_GET(RMI_RESULT_DATA_LEVEL_MASK, ret)
+#define RMI_RESULT_MEMREQ(ret)		FIELD_GET(RMI_RESULT_MEMREQ_MASK, ret)
+#define RMI_RESULT_CAN_CANCEL(ret)	FIELD_GET(RMI_RESULT_CAN_CANCEL_MASK, ret)
+
+#define RMI_SUCCESS			0
+#define RMI_ERROR_INPUT			1
+#define RMI_ERROR_REALM			2
+#define RMI_ERROR_REC			3
+#define RMI_ERROR_RTT			4
+#define RMI_ERROR_NOT_SUPPORTED		5
+#define RMI_ERROR_DEVICE		6
+#define RMI_ERROR_RTT_AUX		7
+#define RMI_ERROR_PSMMU_ST		8
+#define RMI_ERROR_DPT			9
+#define RMI_BUSY			10
+#define RMI_ERROR_GLOBAL		11
+#define RMI_ERROR_TRACKING		12
+#define RMI_INCOMPLETE			13
+#define RMI_BLOCKED			14
+#define RMI_ERROR_GPT			15
+#define RMI_ERROR_GRANULE		16
+
+#define RMI_CONTINUE_KEEP_GOING		0
+#define RMI_CONTINUE_STOP		1
+
+#define RMI_OP_MEM_REQ_NONE		0
+#define RMI_OP_MEM_REQ_DONATE		1
+#define RMI_OP_MEM_REQ_RECLAIM		2
+
+#define RMI_OP_CANNOT_CANCEL		0
+#define RMI_OP_CAN_CANCEL		1
+
+#define RMI_DONATE_STATE_MASK		GENMASK(18, 17)
+#define RMI_DONATE_CONTIG_MASK		BIT(16)
+#define RMI_DONATE_COUNT_MASK		GENMASK(15, 2)
+#define RMI_DONATE_BLOCK_SIZE_MASK	GENMASK(1, 0)
+
+#define RMI_DONATE_STATE(req)		FIELD_GET(RMI_DONATE_STATE_MASK, req)
+#define RMI_DONATE_CONTIG(req)		FIELD_GET(RMI_DONATE_CONTIG_MASK, req)
+#define RMI_DONATE_COUNT(req)		FIELD_GET(RMI_DONATE_COUNT_MASK, req)
+#define RMI_DONATE_BLOCK_SIZE(req)	FIELD_GET(RMI_DONATE_BLOCK_SIZE_MASK, req)
+
+#define RMI_OP_MEM_DELEGATED		0
+#define RMI_OP_MEM_UNDELEGATED		1
+#define RMI_OP_MEM_CONDITIONAL		2
+
+#define RMI_OP_MEM_NON_CONTIG		0
+#define RMI_OP_MEM_CONTIG		1
+
+#define RMI_ADDR_TYPE_NONE		0
+#define RMI_ADDR_TYPE_SINGLE		1
+#define RMI_ADDR_TYPE_LIST		2
+
+#define RMI_ADDR_RANGE_STATE_MASK	GENMASK_ULL(63, 62)
+#define RMI_ADDR_RANGE_ADDR_MASK	GENMASK_ULL(51, PAGE_SHIFT)
+#define RMI_ADDR_RANGE_COUNT_MASK	GENMASK(PAGE_SHIFT - 1, 2)
+#define RMI_ADDR_RANGE_BLOCK_SIZE_MASK	GENMASK(1, 0)
+
+#define RMI_ADDR_RANGE_STATE(r)		FIELD_GET(RMI_ADDR_RANGE_STATE_MASK, (r))
+#define RMI_ADDR_RANGE_ADDR(r)		((r) & RMI_ADDR_RANGE_ADDR_MASK)
+#define RMI_ADDR_RANGE_COUNT(r)		FIELD_GET(RMI_ADDR_RANGE_COUNT_MASK, (r))
+#define RMI_ADDR_RANGE_BLOCK_SIZE(r)	FIELD_GET(RMI_ADDR_RANGE_BLOCK_SIZE_MASK, (r))
+
+enum rmi_ripas {
+	RMI_EMPTY = 0,
+	RMI_RAM = 1,
+	RMI_DESTROYED = 2,
+	RMI_DEV = 3,
+};
+
+#define RMI_NO_MEASURE_CONTENT	0
+#define RMI_MEASURE_CONTENT	1
+
+#define RMI_FEATURE_REGISTER_0_S2OASZ			GENMASK_ULL(40, 33)
+#define RMI_FEATURE_REGISTER_0_L0GPT_BLOCK_DELEGATE	BIT_ULL(32)
+#define RMI_FEATURE_REGISTER_0_PMU_NUM_CTRS		GENMASK(31, 27)
+#define RMI_FEATURE_REGISTER_0_PMU			BIT(26)
+#define RMI_FEATURE_REGISTER_0_NUM_WPS			GENMASK(25, 20)
+#define RMI_FEATURE_REGISTER_0_NUM_BPS			GENMASK(19, 14)
+#define RMI_FEATURE_REGISTER_0_SVE_VL			GENMASK(13, 10)
+#define RMI_FEATURE_REGISTER_0_SVE			BIT(9)
+#define RMI_FEATURE_REGISTER_0_LPA2			BIT(8)
+#define RMI_FEATURE_REGISTER_0_S2SZ			GENMASK(7, 0)
+
+#define RMI_FEATURE_REGISTER_1_PPS			GENMASK(16, 14)
+#define RMI_FEATURE_REGISTER_1_L0GPTSZ			GENMASK(13, 10)
+#define RMI_FEATURE_REGISTER_1_MAX_RECS_ORDER		GENMASK(9, 6)
+#define RMI_FEATURE_REGISTER_1_HASH_SHA_512		BIT(5)
+#define RMI_FEATURE_REGISTER_1_HASH_SHA_384		BIT(4)
+#define RMI_FEATURE_REGISTER_1_HASH_SHA_256		BIT(3)
+#define RMI_FEATURE_REGISTER_1_RMI_GRAN_SZ_64KB		BIT(2)
+#define RMI_FEATURE_REGISTER_1_RMI_GRAN_SZ_16KB		BIT(1)
+#define RMI_FEATURE_REGISTER_1_RMI_GRAN_SZ_4KB		BIT(0)
+
+#define RMI_FEATURE_REGISTER_2_REALM_MAX_VDEVS_ORDER	GENMASK(14, 10)
+#define RMI_FEATURE_REGISTER_2_NON_TEE_STREAM		BIT(9)
+#define RMI_FEATURE_REGISTER_2_VDEV_KROU		BIT(8)
+#define RMI_FEATURE_REGISTER_2_PDEV_MAX_VDEVS_ORDER	GENMASK(7, 4)
+#define RMI_FEATURE_REGISTER_2_ATS			BIT(3)
+#define RMI_FEATURE_REGISTER_2_VSMMU			BIT(2)
+#define RMI_FEATURE_REGISTER_2_DA_COH			BIT(1)
+#define RMI_FEATURE_REGISTER_2_DA			BIT(0)
+
+#define RMI_FEATURE_REGISTER_3_RTT_S2AP_INDIRECT	BIT(6)
+#define RMI_FEATURE_REGISTER_3_RTT_PLANE		GENMASK(5, 4)
+#define RMI_FEATURE_REGISTER_3_MAX_NUM_AUX_PLANES	GENMASK(3, 0)
+
+#define RMI_FEATURE_REGISTER_4_MEC_COUNT		GENMASK_ULL(63, 0)
+
+#define RMI_MEM_CATEGORY_CONVENTIONAL		0
+#define RMI_MEM_CATEGORY_DEV_NCOH		1
+#define RMI_MEM_CATEGORY_DEV_COH		2
+#define RMI_MEM_CATEGORY_NONE			3
+
+#define RMI_TRACKING_RESERVED			0
+#define RMI_TRACKING_NONE			1
+#define RMI_TRACKING_FINE			2
+#define RMI_TRACKING_COARSE			3
+#define RMI_TRACKING_INTERMEDIATE		4
+
+#define RMI_GRANULE_SIZE_4KB	0
+#define RMI_GRANULE_SIZE_16KB	1
+#define RMI_GRANULE_SIZE_64KB	2
+
+#define RMI_GPT_PAR_RESERVED           0U
+#define RMI_GPT_PAR_PLAT               1U
+#define RMI_GPT_PAR_HOST_NOT_CREATED   2U
+#define RMI_GPT_PAR_HOST_CREATED       3U
+
+/*
+ * Note many of these fields are smaller than u64 but all fields have u64
+ * alignment, so use u64 to ensure correct alignment.
+ */
+struct rmm_config {
+	union { /* 0x0 */
+		struct {
+			u64 tracking_region_size;
+			u64 rmi_granule_size;
+		};
+		u8 sizer[SZ_4K];
+	};
+};
+
+static_assert(sizeof(struct rmm_config) == SZ_4K);
+
+#define RMI_REALM_PARAM_FLAG_MEC_POLICY_MASK	GENMASK(8, 7)
+#define RMI_REALM_PARAM_FLAG_LFA_POLICY_MASK	GENMASK(6, 5)
+#define RMI_REALM_PARAM_FLAG_DA			BIT(3)
+#define RMI_REALM_PARAM_FLAG_PMU		BIT(2)
+#define RMI_REALM_PARAM_FLAG_SVE		BIT(1)
+
+#define RMI_HASH_SHA_256			0
+#define RMI_HASH_SHA_512			1
+#define RMI_HASH_SHA_384			2
+
+struct realm_params {
+	union { /* 0x0 */
+		struct {
+			u64 flags0;
+			u64 s2sz;
+			u64 sve_vl;
+			u64 num_bps;
+			u64 num_wps;
+			u64 pmu_num_ctrs;
+			u64 hash_algo;
+			u64 num_aux_planes;
+		};
+		u8 padding0[0x400];
+	};
+	union { /* 0x400 */
+		struct {
+			u8 rpv[64];
+			u64 ats_plane;
+		};
+		u8 padding1[0x400];
+	};
+	union { /* 0x800 */
+		struct {
+			u64 padding2;
+			u64 rtt_base;
+			s64 rtt_level_start;
+			u64 rtt_num_start;
+			u64 flags1;
+			u64 max_num_vdevs;
+		};
+		u8 padding3[0x700];
+	};
+	union { /* 0xf00 */
+		struct {
+			u8 padding4[0x80];
+			u64 aux_rtt_base[3];
+		};
+		u8 padding5[0x100];
+	};
+};
+
+static_assert(sizeof(struct realm_params) == SZ_4K);
+
+/*
+ * The number of GPRs (starting from X0) that are configured by the host when
+ * a REC is created.
+ */
+#define REC_CREATE_NR_GPRS		8
+
+#define REC_PARAMS_FLAG_RUNNABLE	BIT(0)
+
+struct rec_params {
+	union { /* 0x0 */
+		u64 flags;
+		u8 padding0[0x100];
+	};
+	union { /* 0x100 */
+		u64 mpidr;
+		u8 padding1[0x100];
+	};
+	union { /* 0x200 */
+		u64 pc;
+		u8 padding2[0x100];
+	};
+	union { /* 0x300 */
+		u64 gprs[REC_CREATE_NR_GPRS];
+		u8 padding3[0xd00];
+	};
+};
+
+static_assert(sizeof(struct rec_params) == SZ_4K);
+
+#define REC_ENTER_FLAG_FORCE_P0		BIT(7)
+#define REC_ENTER_FLAG_DEV_MEM_RESPONSE	BIT(6)
+#define REC_ENTER_FLAG_S2AP_RESPONSE	BIT(5)
+#define REC_ENTER_FLAG_RIPAS_RESPONSE	BIT(4)
+#define REC_ENTER_FLAG_TRAP_WFE		BIT(3)
+#define REC_ENTER_FLAG_TRAP_WFI		BIT(2)
+#define REC_ENTER_FLAG_INJECT_SEA	BIT(1)
+#define REC_ENTER_FLAG_EMULATED_MMIO	BIT(0)
+
+#define REC_RUN_GPRS			31
+
+struct rec_enter {
+	union { /* 0x000 */
+		u64 flags;
+		u8 padding0[0x200];
+	};
+	union { /* 0x200 */
+		u64 gprs[REC_RUN_GPRS];
+		u8 padding1[0x600];
+	};
+};
+
+static_assert(sizeof(struct rec_enter) == SZ_2K);
+
+#define RMI_EXIT_SYNC			0x00
+#define RMI_EXIT_IRQ			0x01
+#define RMI_EXIT_FIQ			0x02
+#define RMI_EXIT_PSCI			0x03
+#define RMI_EXIT_RIPAS_CHANGE		0x04
+#define RMI_EXIT_HOST_CALL		0x05
+#define RMI_EXIT_SERROR			0x06
+#define RMI_EXIT_S2AP_CHANGE		0x07
+#define RMI_EXIT_VDEV_VALIDATE_MAPPING	0x08
+#define RMI_EXIT_VSMMU_COMMAND		0x0a
+
+struct rec_exit {
+	union { /* 0x000 */
+		u8 exit_reason;
+		u8 padding0[0x100];
+	};
+	union { /* 0x100 */
+		struct {
+			u64 esr;
+			u64 far;
+			u64 hpfar;
+			u64 rtt_tree;
+		};
+		u8 padding1[0x100];
+	};
+	union { /* 0x200 */
+		u64 gprs[REC_RUN_GPRS];
+		u8 padding2[0x100];
+	};
+	union { /* 0x300 */
+		u8 padding3[0x100];
+	};
+	union { /* 0x400 */
+		struct {
+			u64 cntp_ctl;
+			u64 cntp_cval;
+			u64 cntv_ctl;
+			u64 cntv_cval;
+		};
+		u8 padding4[0x100];
+	};
+	union { /* 0x500 */
+		struct {
+			u64 ripas_base;
+			u64 ripas_top;
+			u8 ripas_value;
+			u8 padding5[0xf];
+			u64 s2ap_base;
+			u64 s2ap_top;
+			u64 vdev_id_1;
+			u64 vdev_id_2;
+			u64 dev_mem_base;
+			u64 dev_mem_top;
+			u64 dev_mem_pa;
+		};
+		u8 padding6[0x100];
+	};
+	union { /* 0x600 */
+		struct {
+			u16 imm;
+			u8 padding7[0x6];
+			u64 plane;
+		};
+		u8 padding8[0x100];
+	};
+	union { /* 0x700 */
+		struct {
+			u8 pmu_ovf_status;
+			u8 padding9[0xf];
+			u64 vsmmu;
+		};
+		u8 padding10[0x100];
+	};
+};
+
+static_assert(sizeof(struct rec_exit) == SZ_2K);
+
+struct rec_run {
+	struct rec_enter enter;
+	struct rec_exit exit;
+};
+
+static_assert(sizeof(struct rec_run) == SZ_4K);
+
+/* RMI_RTT_UNPROT_MAP_FLAGS definitions */
+#define RMI_RTT_UNPROT_MAP_FLAGS_S2AP_MASK		GENMASK(22, 19)
+#define RMI_RTT_UNPROT_MAP_FLAGS_MEMATTR_MASK		GENMASK(18, 16)
+#define RMI_RTT_UNPROT_MAP_FLAGS_LIST_COUNT_MASK	GENMASK(15, 2)
+#define RMI_RTT_UNPROT_MAP_FLAGS_OADDR_TYPE_MASK	GENMASK(1, 0)
+
+/* RMI_RTT_PROT_MAP_FLAGS definitions */
+#define RMI_RTT_PROT_MAP_FLAGS_LIST_COUNT_MASK		GENMASK(15, 2)
+#define RMI_RTT_PROT_MAP_FLAGS_OADDR_TYPE_MASK		GENMASK(1, 0)
+
+/* S2AP Direct Encodings, used in RMI_RTT_UNPROT_MAP_FLAGS_S2AP */
+#define RMI_S2AP_DIRECT_READ			BIT(1)
+#define RMI_S2AP_DIRECT_WRITE			BIT(0)
+
+#endif /* __LINUX_ARM_SMCCC_RMI_H_ */

3/2 2026/09/26 19:11 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 29621
							output: 21
							thoughts: 591
						

Thoughts:
**Fuzzing Feasibility Evaluation: Patch Series**

Okay, so the task is to determine if this patch series is suitable for fuzzing with syzkaller. The first thing I need to do is get a handle on the target architecture and the changes within the patch. I see the target architecture is amd64. Alright, let's dive into the diff.

The patch includes changes to these files: `arch/arm64/Kconfig`, `arch/arm64/kernel/cpufeature.c`, `drivers/firmware/Kconfig`, `drivers/firmware/Makefile`, `drivers/firmware/arm_rmm/Kconfig`, `drivers/firmware/arm_rmm/Makefile`, `drivers/firmware/arm_rmm/rmi.c`, `include/linux/arm-rmi-cmds.h`, and `include/linux/arm-smccc-rmi.h`.

Specifically, I spot references to `ARM64` and `RMM` (Realm Management Monitor) features. Looking closer, there's `config ARM64` selecting `ARCH_SUPPORTS_RMM` in the Kconfig file for arm64. I also see RMI (Realm Management Interface) support being conditional on `ARCH_SUPPORTS_RMM`. Seems like some support for ARM RMM is being introduced, or altered.

**Hold on a second!** The target architecture is amd64, not arm64. This is a red flag. Can these `CONFIG_ARCH_SUPPORTS_RMM` or `CONFIG_ARM_RMM_RMI` options be enabled on amd64 (x86_64)? Let's check the `Kconfig` files again. I see `ARM_RMM_RMI` depends on `ARCH_SUPPORTS_RMM`, and I can trace it back to `arm64` using the Kconfig.

I need to confirm if `ARCH_SUPPORTS_RMM` is selected elsewhere. To be sure, I need to do a broader search across the codebase for the string `ARCH_SUPPORTS_RMM`. Looks like it's only selected under `arm64`. Given that, I think fuzzing the patch with syzkaller might be a waste of time, unless there's some conditional compilation going on that allows this code path to be hit on x86_64, which is unlikely given the context.



4/2 2026/09/26 19:11 tool grepper
0m Args:
{
  "Expression": "ARCH_SUPPORTS_RMM"
}

Results:
{
  "Output": "arch/arm64/Kconfig=2=config ARM64\n--\narch/arm64/Kconfig-40-\tselect ARCH_SUPPORTS_MSEAL_SYSTEM_MAPPINGS\narch/arm64/Kconfig:41:\tselect ARCH_SUPPORTS_RMM\narch/arm64/Kconfig-42-\tselect ARCH_HAS_NMI_SAFE_THIS_CPU_OPS\n--\ndrivers/firmware/arm_rmm/Kconfig-1-\ndrivers/firmware/arm_rmm/Kconfig:2:config ARCH_SUPPORTS_RMM\ndrivers/firmware/arm_rmm/Kconfig-3-\tbool\n--\ndrivers/firmware/arm_rmm/Kconfig=5=config ARM_RMM_RMI\ndrivers/firmware/arm_rmm/Kconfig-6-\tbool \"Realm Management Interface (RMI) Support\"\ndrivers/firmware/arm_rmm/Kconfig:7:\tdepends on ARCH_SUPPORTS_RMM\ndrivers/firmware/arm_rmm/Kconfig-8-\thelp\n"
}

5/2 2026/09/26 19:11 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 31019
							output: 30
							thoughts: 730
						

Thoughts:
**Analysis of the Patch for Fuzzing Suitability**

Okay, let's break this down. My initial instinct is to assess if this patch series, focusing on Arm CCA (RMI/RMM) support, is relevant for fuzzing on x86/amd64. We need to determine if any of the modified code could possibly be executed in an x86/amd64 environment.

First, I examined `rmi.c` and the header files `include/linux/arm-rmi-cmds.h` and `include/linux/arm-smccc-rmi.h`.  The inclusion of `<linux/cpufeature.h>`, `<linux/memblock.h>`, etc., while seemingly generic, doesn't automatically mean this code is platform-agnostic.  The presence of ARM64-specific defines, like `ARM64_HW_PGTABLE_LEVEL_SHIFT`, `SYS_ID_AA64PFR0_EL1`, and the functions associated with `arm_smccc_1_2` immediately raises a red flag.  The dependencies on `CONFIG_ARM_RMM_RMI` and `ARCH_SUPPORTS_RMM`, which are explicitly tied to the `ARM64` architecture, confirm this.

I then revisited the fuzzing guidelines. Specifically, the "negative criteria" for returning `WorthFuzzing=false` highlight "Code paths exclusive to architectures other than the target architecture," which is *exactly* what this appears to be. The target architecture is x86/amd64, and this patch series is introducing features explicitly for ARM64.

To be absolutely certain, I reviewed the diff listing all the files modified: `arch/arm64/Kconfig`, `arch/arm64/kernel/cpufeature.c`, the Kconfig and Makefiles in `drivers/firmware`, and specifically, `drivers/firmware/arm_rmm/rmi.c`, along with those critical include files. The fact that the path and contents of these files are arm64 specific underscores the point that they are exclusively targeted for arm64.

Finally, just to be thorough, I'll need to double-check if any of these headers are included in generic or architecture-independent code. I will run a quick `grep` for `arm-rmi-cmds.h` and `arm-smccc-rmi.h` to see if there is any potential cross-contamination.

Based on this preliminary analysis, I'm confident that this patch series is *not* suitable for fuzzing x86/amd64, and I would return `WorthFuzzing=false`. All code modifications are strictly confined to the ARM64 architecture.



6/2 2026/09/26 19:11 tool grepper
0m Args:
{
  "Expression": "arm-rmi-cmds\\.h|arm-smccc-rmi\\.h"
}

Results:
{
  "Output": "drivers/firmware/arm_rmm/rmi.c-8-#include \u003clinux/memory.h\u003e\ndrivers/firmware/arm_rmm/rmi.c:9:#include \u003clinux/arm-rmi-cmds.h\u003e\ndrivers/firmware/arm_rmm/rmi.c-10-#include \u003clinux/slab.h\u003e\n--\ninclude/linux/arm-rmi-cmds.h-8-\ninclude/linux/arm-rmi-cmds.h:9:#include \u003clinux/arm-smccc-rmi.h\u003e\ninclude/linux/arm-rmi-cmds.h-10-#include \u003clinux/bug.h\u003e\n"
}

7/2 2026/09/26 19:11 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 32518
							output: 113
							thoughts: 327
						

Thoughts:
**Analysis of RMI Firmware Patch and Fuzzing Relevance**

Okay, so let's break this down. The code in question is specifically tied to Arm's Realm Management Interface (RMI) firmware. I see that it's only included by `drivers/firmware/arm_rmm/rmi.c` and is built only if `CONFIG_ARM_RMM_RMI` is defined. That configuration option, `CONFIG_ARM_RMM_RMI`, is directly dependent on `ARCH_SUPPORTS_RMM`, which, as we know, is exclusively selected under `CONFIG_ARM64`.

Therefore, since we're targeting an `amd64` architecture, none of this RMI-related code is actually compiled or even reachable.

Now, according to the criteria in Section 2, we are explicitly told that a patch isn't worth fuzzing if *all* modified code falls into an "Unreachable execution paths" category. And here, we have a concrete example of code paths exclusive to architectures other than our target – the `arm64` architecture.

Given that the entire patch series is dedicated to RMI firmware for Arm CCA and arm64-specific cpufeature detection, and the code is conditional on `ARCH_SUPPORTS_RMM` / `ARM64`, it's not going to be present on the target architecture. The conclusion is straightforward:

*   `WorthFuzzing` should be `false`.
*   `FocusSymbols` should be an empty list, as there are no relevant symbols on this architecture.
*   `EnableConfigs` should also be an empty list, because this isn't relevant to our current architecture.
*   The `Reasoning` needs to clearly state that the code is architecture-specific, i.e., arm64 only.

I'm doing a quick sanity check to make sure that the set parameters are in the correct format. The parameters look correct:
*   `EnableConfigs`: string list (empty)
*   `FocusSymbols`: string list (empty)
*   `Reasoning`: String
*   `WorthFuzzing`: Boolean (false)

Everything is spot on. We're good to go.



8/2 2026/09/26 19:11 tool set-results
0m Args:
{
  "EnableConfigs": [],
  "FocusSymbols": [],
  "Reasoning": "The patch series introduces support for the Arm Realm Management Interface (RMI) and cpufeature handling for Arm Confidential Compute Architecture (CCA). The new driver and changes depend on ARCH_SUPPORTS_RMM, which is only selected by ARM64. Consequently, all modified and added code paths are exclusive to the arm64 architecture and are not compiled or executable on the target architecture (amd64).",
  "WorthFuzzing": false
}

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "Reasoning": "The patch series introduces support for the Arm Realm Management Interface (RMI) and cpufeature handling for Arm Confidential Compute Architecture (CCA). The new driver and changes depend on ARCH_SUPPORTS_RMM, which is only selected by ARM64. Consequently, all modified and added code paths are exclusive to the arm64 architecture and are not compiled or executable on the target architecture (amd64).",
  "WorthFuzzing": false
}

9/1 2026/09/26 19:11 action If
0m Args:
{
  "WorthFuzzing": false
}

Results:
null

LLM Calls Summary:
Total Calls Total Tokens Avg Tokens Total Duration (Seconds) Avg Duration (Seconds)
Tool Calls Summary:
Total Calls Total Duration (Seconds) Avg Duration (Seconds)