| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/07/15 21:29 | flow | patch-triage |
0mArgs:null Results: {
"EnableConfigs": null,
"FocusSymbols": [],
"Reasoning": "The patch adds host-side Realm Management Interface (RMI) library functions and boot-time initialization for KVM to use. However, KVM support for Realms is not included in this patch, making the exported library functions unreachable from user-space. Furthermore, the boot-time initialization requires specific hardware/firmware (RMM) support that is not present in standard virtualized environments, and it will simply bail out early. There are no syscalls, ioctls, or other user-space interfaces introduced that syzkaller could fuzz.",
"WorthFuzzing": false
} |
| 1/1 | 2026/07/15 21:29 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit 9ca347445c6cacc9ba0c808116fbcd8dc0790119\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Wed Jul 15 21:29:52 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig\nindex b3afe0688919bd..0f1956edf1307b 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/include/asm/rmi_cmds.h b/arch/arm64/include/asm/rmi_cmds.h\nnew file mode 100644\nindex 00000000000000..f13ecdb40ded41\n--- /dev/null\n+++ b/arch/arm64/include/asm/rmi_cmds.h\n@@ -0,0 +1,459 @@\n+/* SPDX-License-Identifier: GPL-2.0 */\n+/*\n+ * Copyright (C) 2023-2026 ARM Ltd.\n+ */\n+\n+#ifndef __ASM_RMI_CMDS_H\n+#define __ASM_RMI_CMDS_H\n+\n+#include \u003clinux/arm-rmi-cmds.h\u003e\n+#include \u003clinux/arm-smccc-rmi.h\u003e\n+\n+/**\n+ * rmi_rtt_data_map_init() - Create a protected mapping with data contents\n+ * @rd: PA of the RD\n+ * @data: PA of the target granule\n+ * @ipa: IPA at which the granule will be mapped in the guest\n+ * @src: PA of the source granule\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: RMI return code\n+ */\n+static inline int rmi_rtt_data_map_init(unsigned long rd, unsigned long data,\n+\t\t\t\t\tunsigned long ipa, unsigned long src,\n+\t\t\t\t\tunsigned 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 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 RMI return code\n+ */\n+static inline int 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+\tint ret;\n+\n+\tret = rmi_sro_execute(\u0026regs);\n+\n+\tif (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\n+ * @rd: PA of the RD for the target Realm\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: RMI return code\n+ */\n+static inline int 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+\tint ret;\n+\n+\tret = rmi_sro_execute(\u0026regs);\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 ret;\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 int rmi_psci_complete(unsigned long calling_rec,\n+\t\t\t\t unsigned long status)\n+{\n+\tstruct arm_smccc_res res;\n+\n+\tarm_smccc_1_1_invoke(SMC_RMI_PSCI_COMPLETE, calling_rec, status, \u0026res);\n+\n+\treturn res.a0;\n+}\n+\n+/**\n+ * rmi_realm_activate() - Active a realm\n+ * @rd: PA of the RD\n+ *\n+ * Mark a realm as Active signalling that creation is complete and allowing\n+ * execution of the realm.\n+ *\n+ * Return: RMI return code\n+ */\n+static inline int rmi_realm_activate(unsigned long rd)\n+{\n+\tstruct arm_smccc_res res;\n+\n+\tarm_smccc_1_1_invoke(SMC_RMI_REALM_ACTIVATE, rd, \u0026res);\n+\n+\treturn res.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 to be used\n+ *\n+ * Create a new realm using the given parameters.\n+ *\n+ * Return: RMI return code\n+ */\n+static inline int 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 to be used\n+ *\n+ * Terminates a realm, moving it into a ZOMBIE state\n+ *\n+ * Return: RMI return code\n+ */\n+static inline int 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 to be used\n+ *\n+ * Destroys a realm, all objects belonging to the realm must be destroyed first.\n+ *\n+ * Return: RMI return code\n+ */\n+static inline int 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: RMI return code\n+ */\n+static inline int 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+\tint ret;\n+\n+\t*sro = (struct rmi_sro_state){.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: RMI return code\n+ */\n+static inline int 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 int rmi_rec_enter(unsigned long rec, unsigned long run_ptr)\n+{\n+\tstruct arm_smccc_res res;\n+\n+\tarm_smccc_1_1_invoke(SMC_RMI_REC_ENTER, rec, run_ptr, \u0026res);\n+\n+\treturn res.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: RMI return code\n+ */\n+static inline int 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 for the target realm\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: RMI return code.\n+ */\n+static inline int 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+\tint ret = rmi_sro_execute(\u0026regs);\n+\n+\tif (out_rtt)\n+\t\t*out_rtt = regs.a1;\n+\tif (out_top)\n+\t\t*out_top = regs.a2;\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: RMI return code\n+ */\n+static inline int rmi_rtt_fold(unsigned long rd, unsigned long ipa,\n+\t\t\t long 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+\tint ret = rmi_sro_execute(\u0026regs);\n+\n+\tif (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: RMI return code\n+ */\n+static inline int 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+\tint ret = rmi_sro_execute(\u0026regs);\n+\n+\tif (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: RMI return code\n+ */\n+static inline int 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+\tint ret = rmi_sro_execute(\u0026regs);\n+\n+\tif (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: RMI return code\n+ */\n+static inline int 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+\tint ret = rmi_sro_execute(\u0026regs);\n+\n+\tif (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: RMI return code\n+ */\n+static inline int rmi_rtt_unprot_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_UNPROT_UNMAP, rd, base, top, flags, oaddr\n+\t};\n+\tint ret = rmi_sro_execute(\u0026regs);\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 ret;\n+}\n+\n+#endif /* __ASM_RMI_CMDS_H */\ndiff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c\nindex 9a22df0c5120f3..4ae63c89cda97d 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 12dc70254842ce..55542879fbb8ab 100644\n--- a/drivers/firmware/Kconfig\n+++ b/drivers/firmware/Kconfig\n@@ -301,5 +301,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 4ddec2820c96fa..07e8ca1789b49e 100644\n--- a/drivers/firmware/Makefile\n+++ b/drivers/firmware/Makefile\n@@ -38,3 +38,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 00000000000000..d224a72cca9089\n--- /dev/null\n+++ b/drivers/firmware/arm_rmm/Kconfig\n@@ -0,0 +1,22 @@\n+\n+config ARCH_SUPPORTS_RMM\n+\tbool\n+\n+config ARM_RMM\n+\tbool \"Realm Management Monitor (RMM) Support\"\n+\tdepends on ARCH_SUPPORTS_RMM\n+\tdefault y\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 in the Realm world and provides the Realm Management\n+\t Interface (RMI) used by a Normal World host to create, manage and run\n+\t protected virtual machines called Realms. This option builds the\n+\t host-side RMI support used by KVM to detect a compatible RMM,\n+\t configure it, manage delegated memory and enable 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 00000000000000..51aef5cc04b02c\n--- /dev/null\n+++ b/drivers/firmware/arm_rmm/Makefile\n@@ -0,0 +1,2 @@\n+\n+obj-$(CONFIG_ARM_RMM)\t= rmi.o\ndiff --git a/drivers/firmware/arm_rmm/rmi.c b/drivers/firmware/arm_rmm/rmi.c\nnew file mode 100644\nindex 00000000000000..d0c083bdf251f4\n--- /dev/null\n+++ b/drivers/firmware/arm_rmm/rmi.c\n@@ -0,0 +1,724 @@\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/arm-rmi-cmds.h\u003e\n+#include \u003clinux/processor.h\u003e\n+#include \u003clinux/slab.h\u003e\n+\n+#include \u003casm/memory.h\u003e\n+#include \u003casm/pgtable-hwdef.h\u003e\n+\n+static bool arm64_rmi_is_available;\n+\n+/* Currently only the first 2 registers are used by Linux */\n+#define RMI_FEAT_REG_COUNT\t2\n+static __ro_after_init unsigned long rmi_feat_reg_cache[RMI_FEAT_REG_COUNT];\n+\n+unsigned long rmi_feat_reg(unsigned long id)\n+{\n+\tif (WARN_ON(id \u003e= RMI_FEAT_REG_COUNT))\n+\t\treturn 0;\n+\n+\treturn rmi_feat_reg_cache[id];\n+}\n+EXPORT_SYMBOL_GPL(rmi_feat_reg);\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+\tunsigned long 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+\t\tif (ret == RMI_SUCCESS)\n+\t\t\tphys = out_top;\n+\t\telse if (ret == RMI_BUSY || ret == RMI_BLOCKED)\n+\t\t\tcpu_relax();\n+\t\telse\n+\t\t\tbreak;\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+int rmi_undelegate_range(phys_addr_t phys,\n+\t\t\t unsigned long size)\n+{\n+\tunsigned long 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+\t\tif (ret == RMI_SUCCESS)\n+\t\t\tphys = out_top;\n+\t\telse if (ret == RMI_BUSY || ret == RMI_BLOCKED)\n+\t\t\tcpu_relax();\n+\t\telse\n+\t\t\tbreak;\n+\t}\n+\n+\treturn ret;\n+}\n+EXPORT_SYMBOL_GPL(rmi_undelegate_range);\n+\n+static unsigned long donate_req_to_size(unsigned long donatereq)\n+{\n+\tunsigned long unit_size = RMI_DONATE_SIZE(donatereq);\n+\n+\treturn BIT(ARM64_HW_PGTABLE_LEVEL_SHIFT(3 - unit_size));\n+}\n+\n+static void rmi_smccc_invoke(struct arm_smccc_1_2_regs *regs_in,\n+\t\t\t struct arm_smccc_1_2_regs *regs_out)\n+{\n+\tstruct arm_smccc_1_2_regs regs = *regs_in;\n+\tunsigned long status;\n+\n+\twhile (1) {\n+\t\tarm_smccc_1_2_invoke(\u0026regs, regs_out);\n+\t\tstatus = RMI_RETURN_STATUS(regs_out-\u003ea0);\n+\t\tif (status != RMI_BUSY \u0026\u0026 status != RMI_BLOCKED)\n+\t\t\tbreak;\n+\t\tcpu_relax();\n+\t}\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+\tstruct arm_smccc_1_2_regs regs = {\n+\t\tSMC_RMI_OP_CONTINUE, sro_handle, flags\n+\t};\n+\n+\trmi_smccc_invoke(\u0026regs, 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+\tstruct arm_smccc_1_2_regs regs = {\n+\t\tSMC_RMI_OP_CANCEL, sro_handle\n+\t};\n+\n+\trmi_smccc_invoke(\u0026regs, 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+\tstruct arm_smccc_1_2_regs regs = {\n+\t\tSMC_RMI_OP_MEM_DONATE, sro_handle, list_addr, list_count, flags\n+\t};\n+\n+\trmi_smccc_invoke(\u0026regs, 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+\tstruct arm_smccc_1_2_regs regs = {\n+\t\tSMC_RMI_OP_MEM_RECLAIM, sro_handle, list_addr, list_count\n+\t};\n+\n+\trmi_smccc_invoke(\u0026regs, out_regs);\n+}\n+\n+int 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(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 unit_size = RMI_DONATE_SIZE(donatereq);\n+\tunsigned long unit_size_bytes = donate_req_to_size(donatereq);\n+\tunsigned long count = RMI_DONATE_COUNT(donatereq);\n+\tunsigned long state = RMI_DONATE_STATE(donatereq);\n+\tunsigned long size = unit_size_bytes * count;\n+\tunsigned long addr_range;\n+\tint ret;\n+\tvoid *virt;\n+\tphys_addr_t phys;\n+\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_SIZE(entry) == unit_size \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 out;\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_SIZE_MASK, \u0026addr_range, unit_size);\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+out:\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+\n+\tunsigned long donated_granules = out_regs-\u003ea1;\n+\tunsigned long donated_size = donated_granules \u003c\u003c PAGE_SHIFT;\n+\n+\tif (donated_granules == 0) {\n+\t\t/* No pages used by the RMM */\n+\t\tsro-\u003eaddr_count++;\n+\t} else if (donated_size \u003c size) {\n+\t\tphys = sro-\u003eaddr_list[sro-\u003eaddr_count] \u0026 RMI_ADDR_RANGE_ADDR_MASK;\n+\n+\t\t/* Not all granules used by the RMM, free the remaining pages */\n+\t\tfor (long i = donated_size; i \u003c size; i += PAGE_SIZE) {\n+\t\t\tif (state == RMI_OP_MEM_DELEGATED)\n+\t\t\t\tfree_delegated_page(phys + i);\n+\t\t\telse\n+\t\t\t\t__free_page(phys_to_page(phys + i));\n+\t\t}\n+\t}\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 unit_size = RMI_DONATE_SIZE(donatereq);\n+\tunsigned long unit_size_bytes = donate_req_to_size(donatereq);\n+\tunsigned long count = RMI_DONATE_COUNT(donatereq);\n+\tunsigned long state = RMI_DONATE_STATE(donatereq);\n+\tunsigned long found = 0;\n+\tunsigned long addr_list_start = sro-\u003eaddr_count;\n+\tint ret;\n+\n+\tfor (int i = 0; i \u003c addr_list_start \u0026\u0026 found \u003c count; i++) {\n+\t\tunsigned long entry = sro-\u003eaddr_list[i];\n+\n+\t\tif (RMI_ADDR_RANGE_SIZE(entry) == unit_size \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\ti--;\n+\t\t}\n+\t}\n+\n+\tret = rmi_sro_ensure_capacity(sro, count - found);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\twhile (found \u003c count) {\n+\t\tunsigned long addr_range;\n+\t\tvoid *virt = alloc_pages_exact(unit_size_bytes, 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, unit_size_bytes,\n+\t\t\t\t\t \u0026delegated_phys)) {\n+\t\t\t\tif (!rmi_undelegate_range(phys, delegated_phys - phys))\n+\t\t\t\t\tfree_pages_exact(virt, unit_size_bytes);\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_SIZE_MASK, \u0026addr_range, unit_size);\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\tfound++;\n+\t}\n+\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+\tunsigned long donated_granules = out_regs-\u003ea1;\n+\tunsigned long granules_per_unit = unit_size_bytes \u003e\u003e PAGE_SHIFT;\n+\tunsigned long consumed_units;\n+\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_unit))\n+\t\tdonated_granules = found * granules_per_unit;\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 unit-sized ranges. So divide to get\n+\t * the number of (complete) consumed units.\n+\t */\n+\tconsumed_units = donated_granules / granules_per_unit;\n+\tif (donated_granules % granules_per_unit) {\n+\t\t/*\n+\t\t * A unit 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_units];\n+\t\tphys_addr_t phys = RMI_ADDR_RANGE_ADDR(entry);\n+\t\tunsigned long donated_size =\n+\t\t\t(donated_granules % granules_per_unit) \u003c\u003c PAGE_SHIFT;\n+\n+\t\t/* Free the tail back */\n+\t\tfor (unsigned long i = donated_size; i \u003c unit_size_bytes;\n+\t\t i += PAGE_SIZE) {\n+\t\t\tif (state == RMI_OP_MEM_DELEGATED)\n+\t\t\t\tfree_delegated_page(phys + i);\n+\t\t\telse\n+\t\t\t\t__free_page(phys_to_page(phys + i));\n+\t\t}\n+\n+\t\t/*\n+\t\t * This unit is now fully 'consumed' (either held by the RMM or\n+\t\t * freed)\n+\t\t */\n+\t\tconsumed_units++;\n+\t}\n+\n+\t/* Keep just the units the RMM didn't use in addr_list */\n+\tfor (unsigned long i = consumed_units; i \u003c found; i++)\n+\t\tsro-\u003eaddr_list[addr_list_start + i - consumed_units] =\n+\t\t\tsro-\u003eaddr_list[addr_list_start + i];\n+\n+\tsro-\u003eaddr_count -= consumed_units;\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+\tunsigned long count = RMI_DONATE_COUNT(donatereq);\n+\n+\tif (WARN_ON_ONCE(!count))\n+\t\treturn 0;\n+\n+\tif (RMI_DONATE_CONTIG(donatereq)) {\n+\t\treturn rmi_sro_donate_contig(sro, sro_handle, donatereq,\n+\t\t\t\t\t regs, gfp);\n+\t} else {\n+\t\treturn rmi_sro_donate_noncontig(sro, sro_handle, donatereq,\n+\t\t\t\t\t\tregs, gfp);\n+\t}\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+\tint ret;\n+\n+\tret = rmi_sro_ensure_capacity(sro, 1);\n+\tif (ret)\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+\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+\tfor (int i = 0; i \u003c sro-\u003eaddr_count; i++) {\n+\t\tunsigned long entry = sro-\u003eaddr_list[i];\n+\t\tunsigned long addr = RMI_ADDR_RANGE_ADDR(entry);\n+\t\tunsigned long unit_size = RMI_ADDR_RANGE_SIZE(entry);\n+\t\tunsigned long count = RMI_ADDR_RANGE_COUNT(entry);\n+\t\tunsigned long state = RMI_ADDR_RANGE_STATE(entry);\n+\t\tunsigned long size = donate_req_to_size(unit_size) * count;\n+\n+\t\tif (state == RMI_OP_MEM_DELEGATED) {\n+\t\t\tif (WARN_ON_ONCE(rmi_undelegate_range(addr, size))) {\n+\t\t\t\t/* Leak the pages */\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\t\t}\n+\t\tfree_pages_exact(phys_to_virt(addr), size);\n+\t}\n+\n+\tsro-\u003eaddr_count = 0;\n+}\n+EXPORT_SYMBOL_GPL(rmi_sro_free);\n+\n+unsigned long rmi_sro_memxfer_execute(struct rmi_sro_state *sro, gfp_t gfp)\n+{\n+\tunsigned long sro_handle;\n+\tstruct arm_smccc_1_2_regs *regs = \u0026sro-\u003eregs;\n+\n+\trmi_smccc_invoke(regs, regs);\n+\n+\tsro_handle = regs-\u003ea1;\n+\n+\twhile (RMI_RETURN_STATUS(regs-\u003ea0) == RMI_INCOMPLETE) {\n+\t\tbool can_cancel = RMI_RETURN_CAN_CANCEL(regs-\u003ea0);\n+\t\tint ret = 0;\n+\n+\t\tswitch (RMI_RETURN_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\tret = WARN_ON_ONCE(1);\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\tif (ret) {\n+\t\t\tif (can_cancel)\n+\t\t\t\trmi_op_cancel(sro_handle, regs);\n+\n+\t\t\tif (WARN_ON_ONCE(RMI_RETURN_STATUS(regs-\u003ea0) != RMI_INCOMPLETE))\n+\t\t\t\treturn ret;\n+\t\t}\n+\t}\n+\n+\treturn regs-\u003ea0;\n+}\n+EXPORT_SYMBOL_GPL(rmi_sro_memxfer_execute);\n+\n+/* For RMI commands that are stateful but not memory-transferring */\n+unsigned long rmi_sro_execute(struct arm_smccc_1_2_regs *regs)\n+{\n+\tunsigned long sro_handle;\n+\n+\trmi_smccc_invoke(regs, regs);\n+\n+\tsro_handle = regs-\u003ea1;\n+\n+\twhile (RMI_RETURN_STATUS(regs-\u003ea0) == RMI_INCOMPLETE) {\n+\t\tbool can_cancel = RMI_RETURN_CAN_CANCEL(regs-\u003ea0);\n+\n+\t\tswitch (RMI_RETURN_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+\n+\t\t\trmi_op_cancel(sro_handle, regs);\n+\t\t}\n+\t}\n+\n+\treturn regs-\u003ea0;\n+}\n+EXPORT_SYMBOL_GPL(rmi_sro_execute);\n+\n+static int rmi_check_version(void)\n+{\n+\tstruct arm_smccc_res res;\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+\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+\tarm_smccc_1_1_invoke(SMC_RMI_VERSION, host_version, \u0026res);\n+\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 RMI_FEAT_REG_COUNT; i++) {\n+\t\tif (WARN_ON(rmi_features(i, \u0026rmi_feat_reg_cache[i])))\n+\t\t\treturn -EINVAL;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int rmi_configure(void)\n+{\n+\tunsigned long ret;\n+\tstruct rmm_config *config __free(free_page) = (struct rmm_config *)get_zeroed_page(GFP_KERNEL);\n+\tstruct rmi_sro_state *sro __free(kfree) = (struct rmi_sro_state *)kmalloc_obj(*sro);\n+\n+\tif (!config || !sro)\n+\t\treturn -ENOMEM;\n+\n+\tswitch (PAGE_SIZE) {\n+\tcase SZ_4K:\n+\t\tconfig-\u003ermi_granule_size = RMI_GRANULE_SIZE_4KB;\n+\t\tbreak;\n+\tcase SZ_16K:\n+\t\tconfig-\u003ermi_granule_size = RMI_GRANULE_SIZE_16KB;\n+\t\tbreak;\n+\tcase SZ_64K:\n+\t\tconfig-\u003ermi_granule_size = RMI_GRANULE_SIZE_64KB;\n+\t\tbreak;\n+\tdefault:\n+\t\tBUILD_BUG();\n+\t}\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\\n\");\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tret = rmi_rmm_activate(sro);\n+\tif (ret) {\n+\t\tpr_err(\"RMM activate failed\\n\");\n+\t\treturn -ENXIO;\n+\t}\n+\n+\treturn 0;\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 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\\n\",\n+\t\t\t start);\n+\t\t\treturn -ENODEV;\n+\t\t}\n+\t\tstart = next;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int rmi_create_gpts(phys_addr_t start, phys_addr_t end)\n+{\n+\tstruct rmi_sro_state *sro;\n+\tunsigned long l0gpt_sz;\n+\n+\tsro = kmalloc_obj(*sro, GFP_KERNEL);\n+\tif (!sro)\n+\t\treturn -ENOMEM;\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\tint ret = rmi_gpt_l1_create(start, sro, GFP_KERNEL);\n+\n+\t\t/*\n+\t\t * Make sure the L1 GPT tables are created for the region.\n+\t\t * RMI_ERROR_GPT indicates the L1 table already exists.\n+\t\t */\n+\t\tif (ret != RMI_SUCCESS \u0026\u0026 RMI_RETURN_STATUS(ret) != RMI_ERROR_GPT) {\n+\t\t\tpr_err(\"GPT Level1 table missing for %llx\\n\", start);\n+\t\t\tkfree(sro);\n+\t\t\treturn -ENOMEM;\n+\t\t}\n+\t\tstart += l0gpt_sz;\n+\t}\n+\n+\tkfree(sro);\n+\treturn 0;\n+}\n+\n+static int rmi_init_metadata(void)\n+{\n+\tphys_addr_t start, end;\n+\tconst struct memblock_region *r;\n+\n+\tfor_each_mem_region(r) {\n+\t\tint ret;\n+\n+\t\tstart = memblock_region_memory_base_pfn(r) \u003c\u003c PAGE_SHIFT;\n+\t\tend = memblock_region_memory_end_pfn(r) \u003c\u003c PAGE_SHIFT;\n+\t\tret = rmi_verify_memory_tracking(start, end);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t\tret = rmi_create_gpts(start, end);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+bool is_rmi_available(void)\n+{\n+\treturn arm64_rmi_is_available;\n+}\n+\n+static int __init arm64_init_rmi(void)\n+{\n+\tint ret;\n+\n+\t/* Continue without realm support if we can't agree on a version */\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+\tret = rmi_init_metadata();\n+\tif (ret)\n+\t\treturn ret;\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 00000000000000..138983ab4e3c22\n--- /dev/null\n+++ b/include/linux/arm-rmi-cmds.h\n@@ -0,0 +1,205 @@\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/types.h\u003e\n+\n+#include \u003casm/page.h\u003e\n+\n+struct rtt_entry {\n+\tunsigned long walk_level;\n+\tunsigned long desc;\n+\tint state;\n+\tint ripas;\n+};\n+\n+#define RMI_MAX_ADDR_LIST\t256\n+\n+struct rmi_sro_state {\n+\tstruct arm_smccc_1_2_regs regs;\n+\tunsigned long addr_count;\n+\tunsigned long addr_list[RMI_MAX_ADDR_LIST];\n+};\n+\n+unsigned long rmi_feat_reg(unsigned long id);\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 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+unsigned long rmi_sro_memxfer_execute(struct rmi_sro_state *sro, gfp_t gfp);\n+void rmi_sro_free(struct rmi_sro_state *sro);\n+unsigned long rmi_sro_execute(struct arm_smccc_1_2_regs *regs);\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 = (struct rmi_sro_state){ .regs = {__VA_ARGS__} };\t\\\n+\tint __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_rmm_config_set() - Configure the RMM\n+ * @cfg_ptr: PA of a struct rmm_config\n+ *\n+ * Sets configuration options on the RMM.\n+ *\n+ * Return: RMI return code\n+ */\n+static inline int rmi_rmm_config_set(unsigned long cfg_ptr)\n+{\n+\tstruct arm_smccc_res res;\n+\n+\tarm_smccc_1_1_invoke(SMC_RMI_RMM_CONFIG_SET, cfg_ptr, \u0026res);\n+\n+\treturn res.a0;\n+}\n+\n+/**\n+ * rmi_rmm_activate() - Activate the RMM\n+ * @sro: Preallocated SRO context to be used\n+ *\n+ * Return: RMI return code\n+ */\n+static inline int rmi_rmm_activate(struct rmi_sro_state *sro)\n+{\n+\treturn rmi_sro_memxfer_cmd(sro, GFP_KERNEL, SMC_RMI_RMM_ACTIVATE);\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 inline int rmi_granule_tracking_get(unsigned long start,\n+\t\t\t\t\t unsigned long end,\n+\t\t\t\t\t unsigned long *out_category,\n+\t\t\t\t\t unsigned long *out_state,\n+\t\t\t\t\t unsigned long *out_top)\n+{\n+\tstruct arm_smccc_res res;\n+\n+\tarm_smccc_1_1_invoke(SMC_RMI_GRANULE_TRACKING_GET, start, end, \u0026res);\n+\n+\tif (out_category)\n+\t\t*out_category = res.a1;\n+\tif (out_state)\n+\t\t*out_state = res.a2;\n+\tif (out_top)\n+\t\t*out_top = res.a3;\n+\n+\treturn res.a0;\n+}\n+\n+/**\n+ * rmi_gpt_l1_create() - Create a Level 1 GPT\n+ * @addr: Base of physical address region described by the L1GPT\n+ * @sro: Preallocated SRO context to be used\n+ * @gfp: Allocation flags for SRO memory donation requests\n+ *\n+ * Return: RMI return code\n+ */\n+static inline int rmi_gpt_l1_create(unsigned long addr,\n+\t\t\t\t struct rmi_sro_state *sro,\n+\t\t\t\t gfp_t gfp)\n+{\n+\treturn rmi_sro_memxfer_cmd(sro, gfp, SMC_RMI_GPT_L1_CREATE, addr);\n+}\n+\n+/**\n+ * rmi_features() - Read feature register\n+ * @index: Feature register index\n+ * @out: Feature register value is written to this pointer\n+ *\n+ * Return: RMI return code\n+ */\n+static inline int rmi_features(unsigned long index, unsigned long *out)\n+{\n+\tstruct arm_smccc_res res;\n+\n+\tarm_smccc_1_1_invoke(SMC_RMI_FEATURES, index, \u0026res);\n+\n+\tif (out)\n+\t\t*out = res.a1;\n+\treturn res.a0;\n+}\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: RMI return code\n+ */\n+static inline int rmi_granule_range_delegate(unsigned long base,\n+\t\t\t\t\t unsigned long top,\n+\t\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+\tint ret = rmi_sro_execute(\u0026regs);\n+\n+\tif (out_top)\n+\t\t*out_top = regs.a1;\n+\n+\treturn ret;\n+}\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 if\n+ * the granules are in use.\n+ *\n+ * Return: RMI return code\n+ */\n+static inline int rmi_granule_range_undelegate(unsigned long base,\n+\t\t\t\t\t unsigned long top,\n+\t\t\t\t\t unsigned long *out_top)\n+{\n+\tstruct arm_smccc_1_2_regs regs = {\n+\t\tSMC_RMI_GRANULE_RANGE_UNDELEGATE, base, top\n+\t};\n+\tint ret = rmi_sro_execute(\u0026regs);\n+\n+\tif (out_top)\n+\t\t*out_top = regs.a1;\n+\n+\treturn ret;\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 00000000000000..c965e8867518f3\n--- /dev/null\n+++ b/include/linux/arm-smccc-rmi.h\n@@ -0,0 +1,499 @@\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-bet2:\n+ * https://developer.arm.com/documentation/den0137/2-0bet2/\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+#include \u003casm/sysreg.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+#define SMC_RMI_VDEV_P2P_BIND\t\t\tSMC_RMI_CALL(0x01d4)\n+#define SMC_RMI_VDEV_P2P_UNBIND\t\t\tSMC_RMI_CALL(0x01d5)\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+#define SMC_RMI_CMEM_ADD_PDEV\t\t\tSMC_RMI_CALL(0x01e4)\n+#define SMC_RMI_CMEM_CREATE\t\t\tSMC_RMI_CALL(0x01e5)\n+#define SMC_RMI_CMEM_DESTROY\t\t\tSMC_RMI_CALL(0x01e6)\n+#define SMC_RMI_CMEM_POPULATE\t\t\tSMC_RMI_CALL(0x01e7)\n+#define SMC_RMI_CMEM_REMOVE_PDEV\t\tSMC_RMI_CALL(0x01e8)\n+#define SMC_RMI_CMEM_START\t\t\tSMC_RMI_CALL(0x01e9)\n+#define SMC_RMI_CMEM_STOP\t\t\tSMC_RMI_CALL(0x01ea)\n+#define SMC_RMI_CMEM_UNPOPULATE\t\t\tSMC_RMI_CALL(0x01eb)\n+#define SMC_RMI_RMM_CONFIG_GET\t\t\tSMC_RMI_CALL(0x01ec)\n+#define SMC_RMI_PDEV_MEC_REFRESH\t\tSMC_RMI_CALL(0x01ed)\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+\n+#define RMI_ABI_MAJOR_VERSION\t2\n+#define RMI_ABI_MINOR_VERSION\t0\n+\n+#define RMI_ABI_VERSION_GET_MAJOR(version) ((version) \u003e\u003e 16)\n+#define RMI_ABI_VERSION_GET_MINOR(version) ((version) \u0026 0xFFFF)\n+#define RMI_ABI_VERSION(major, minor) (((major) \u003c\u003c 16) | (minor))\n+\n+#define RMI_RETURN_STATUS_MASK\t\t(0xFFUL)\n+#define RMI_RETURN_INDEX_MASK\t\t(0xFFUL \u003c\u003c 8)\n+#define RMI_RETURN_MEMREQ_MASK\t\t(0x3UL \u003c\u003c 8)\n+#define RMI_RETURN_CAN_CANCEL_MASK\t(0x1UL \u003c\u003c 10)\n+\n+#define RMI_RETURN_STATUS(ret)\t\tFIELD_GET(RMI_RETURN_STATUS_MASK, ret)\n+#define RMI_RETURN_INDEX(ret)\t\tFIELD_GET(RMI_RETURN_INDEX_MASK, ret)\n+#define RMI_RETURN_MEMREQ(ret)\t\tFIELD_GET(RMI_RETURN_MEMREQ_MASK, ret)\n+#define RMI_RETURN_CAN_CANCEL(ret)\tFIELD_GET(RMI_RETURN_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_DONATE_SIZE_MASK\t\t3UL\n+#define RMI_DONATE_COUNT_MASK\t\tGENMASK(15, 2)\n+#define RMI_DONATE_CONTIG_MASK\t\tBIT(16)\n+#define RMI_DONATE_STATE_MASK\t\tBIT(17)\n+\n+#define RMI_DONATE_SIZE(req)\t\tFIELD_GET(RMI_DONATE_SIZE_MASK, req)\n+#define RMI_DONATE_COUNT(req)\t\tFIELD_GET(RMI_DONATE_COUNT_MASK, req)\n+#define RMI_DONATE_CONTIG(req)\t\tFIELD_GET(RMI_DONATE_CONTIG_MASK, req)\n+#define RMI_DONATE_STATE(req)\t\tFIELD_GET(RMI_DONATE_STATE_MASK, req)\n+\n+#define RMI_OP_MEM_DELEGATED\t\t0\n+#define RMI_OP_MEM_UNDELEGATED\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_SIZE_MASK\tGENMASK(1, 0)\n+#define RMI_ADDR_RANGE_COUNT_MASK\tGENMASK(PAGE_SHIFT - 1, 2)\n+#define RMI_ADDR_RANGE_ADDR_MASK\t(PAGE_MASK \u0026 GENMASK(51, 0))\n+#define RMI_ADDR_RANGE_STATE_MASK\tBIT(63)\n+\n+#define RMI_ADDR_RANGE_SIZE(ar)\t\t(FIELD_GET(RMI_ADDR_RANGE_SIZE_MASK, \\\n+\t\t\t\t\t\t (ar)))\n+#define RMI_ADDR_RANGE_COUNT(ar)\t(FIELD_GET(RMI_ADDR_RANGE_COUNT_MASK, \\\n+\t\t\t\t\t\t (ar)))\n+#define RMI_ADDR_RANGE_ADDR(ar)\t\t((ar) \u0026 RMI_ADDR_RANGE_ADDR_MASK)\n+#define RMI_ADDR_RANGE_STATE(ar)\t(FIELD_GET(RMI_ADDR_RANGE_STATE_MASK, \\\n+\t\t\t\t\t\t (ar)))\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_S2SZ\t\tGENMASK(7, 0)\n+#define RMI_FEATURE_REGISTER_0_LPA2\t\tBIT(8)\n+#define RMI_FEATURE_REGISTER_0_SVE\t\tBIT(9)\n+#define RMI_FEATURE_REGISTER_0_SVE_VL\t\tGENMASK(13, 10)\n+#define RMI_FEATURE_REGISTER_0_NUM_BPS\t\tGENMASK(19, 14)\n+#define RMI_FEATURE_REGISTER_0_NUM_WPS\t\tGENMASK(25, 20)\n+#define RMI_FEATURE_REGISTER_0_PMU\t\tBIT(26)\n+#define RMI_FEATURE_REGISTER_0_PMU_NUM_CTRS\tGENMASK(31, 27)\n+\n+#define RMI_FEATURE_REGISTER_1_RMI_GRAN_SZ_4KB\tBIT(0)\n+#define RMI_FEATURE_REGISTER_1_RMI_GRAN_SZ_16KB\tBIT(1)\n+#define RMI_FEATURE_REGISTER_1_RMI_GRAN_SZ_64KB\tBIT(2)\n+#define RMI_FEATURE_REGISTER_1_HASH_SHA_256\tBIT(3)\n+#define RMI_FEATURE_REGISTER_1_HASH_SHA_384\tBIT(4)\n+#define RMI_FEATURE_REGISTER_1_HASH_SHA_512\tBIT(5)\n+#define RMI_FEATURE_REGISTER_1_MAX_RECS_ORDER\tGENMASK(9, 6)\n+#define RMI_FEATURE_REGISTER_1_L0GPTSZ\t\tGENMASK(13, 10)\n+#define RMI_FEATURE_REGISTER_1_PPS\t\tGENMASK(16, 14)\n+\n+#define RMI_FEATURE_REGISTER_2_DA\t\tBIT(0)\n+#define RMI_FEATURE_REGISTER_2_DA_COH\t\tBIT(1)\n+#define RMI_FEATURE_REGISTER_2_VSMMU\t\tBIT(2)\n+#define RMI_FEATURE_REGISTER_2_ATS\t\tBIT(3)\n+#define RMI_FEATURE_REGISTER_2_MAX_VDEVS_ORDER\tGENMASK(7, 4)\n+#define RMI_FEATURE_REGISTER_2_VDEV_KROU\tBIT(8)\n+#define RMI_FEATURE_REGISTER_2_NON_TEE_STREAM\tBIT(9)\n+#define RMI_FEATURE_REGISTER_2_P2P\t\tBIT(10)\n+#define RMI_FEATURE_REGISTER_2_CMEM_CXL\t\tBIT(11)\n+#define RMI_FEATURE_REGISTER_2_MAX_CMEM\t\tGENMASK(19, 12)\n+#define RMI_FEATURE_REGISTER_2_CMEM_TSE_REQ\tBIT(20)\n+\n+#define RMI_FEATURE_REGISTER_3_MAX_NUM_AUX_PLANES\tGENMASK(3, 0)\n+#define RMI_FEATURE_REGISTER_3_RTT_PLANE\t\tGENMASK(5, 4)\n+#define RMI_FEATURE_REGISTER_3_RTT_S2AP_INDIRECT\tBIT(6)\n+\n+#define RMI_FEATURE_REGISTER_4_MEC_COUNT\t\tGENMASK(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+\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+\n+#define RMI_GRANULE_SIZE_4KB\t0\n+#define RMI_GRANULE_SIZE_16KB\t1\n+#define RMI_GRANULE_SIZE_64KB\t2\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_LPA2\t\tBIT(0)\n+#define RMI_REALM_PARAM_FLAG_SVE\t\tBIT(1)\n+#define RMI_REALM_PARAM_FLAG_PMU\t\tBIT(2)\n+#define RMI_REALM_PARAM_FLAG_DA\t\t\tBIT(3)\n+#define RMI_REALM_PARAM_FLAG_LFA_POLICY\t\tGENMASK(6, 5)\n+#define RMI_REALM_PARAM_FLAG_MEC_POLICY\t\tGENMASK(8, 7)\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 padding;\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 aux_rtt_base[3];\n+\t\t};\n+\t\tu8 padding2[0x800];\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\n+ * configured by the host when a REC is created.\n+ */\n+#define REC_CREATE_NR_GPRS\t\t8\n+\n+#define REC_PARAMS_FLAG_RUNNABLE\tBIT_ULL(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_EMULATED_MMIO\tBIT(0)\n+#define REC_ENTER_FLAG_INJECT_SEA\tBIT(1)\n+#define REC_ENTER_FLAG_TRAP_WFI\t\tBIT(2)\n+#define REC_ENTER_FLAG_TRAP_WFE\t\tBIT(3)\n+#define REC_ENTER_FLAG_RIPAS_RESPONSE\tBIT(4)\n+#define REC_ENTER_FLAG_S2AP_RESPONSE\tBIT(5)\n+#define REC_ENTER_FLAG_DEV_MEM_RESPONSE\tBIT(6)\n+#define REC_ENTER_FLAG_FORCE_P0\t\tBIT(7)\n+\n+#define REC_RUN_GPRS\t\t\t31\n+#define REC_MAX_GIC_NUM_LRS\t\t16\n+\n+#define RMI_PERMITTED_GICV3_HCR_BITS\t(ICH_HCR_EL2_UIE |\t\t\\\n+\t\t\t\t\t ICH_HCR_EL2_LRENPIE |\t\t\\\n+\t\t\t\t\t ICH_HCR_EL2_NPIE |\t\t\\\n+\t\t\t\t\t ICH_HCR_EL2_VGrp0EIE |\t\t\\\n+\t\t\t\t\t ICH_HCR_EL2_VGrp0DIE |\t\t\\\n+\t\t\t\t\t ICH_HCR_EL2_VGrp1EIE |\t\t\\\n+\t\t\t\t\t ICH_HCR_EL2_VGrp1DIE |\t\t\\\n+\t\t\t\t\t ICH_HCR_EL2_TDIR)\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[0x100];\n+\t};\n+\tu8 padding3[0x500];\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+#define RMI_EXIT_VDEV_P2P_BINDING\t0x0b\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 padding8[15];\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 padding5[0x100];\n+\t};\n+\tunion { /* 0x600 */\n+\t\tstruct {\n+\t\t\tu16 imm;\n+\t\t\tu16 padding9;\n+\t\t\tu64 plane;\n+\t\t};\n+\t\tu8 padding6[0x100];\n+\t};\n+\tunion { /* 0x700 */\n+\t\tstruct {\n+\t\t\tu8 pmu_ovf_status;\n+\t\t\tu8 padding10[15];\n+\t\t\tu64 vsmmu;\n+\t\t};\n+\t\tu8 padding7[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_OADDR_TYPE\tGENMASK(1, 0)\n+#define RMI_RTT_UNPROT_MAP_FLAGS_LIST_COUNT\tGENMASK(15, 2)\n+#define RMI_RTT_UNPROT_MAP_FLAGS_MEMATTR\tGENMASK(18, 16)\n+#define RMI_RTT_UNPROT_MAP_FLAGS_S2AP\t\tGENMASK(22, 19)\n+\n+/* RMI_RTT_DATA_MAP_FLAGS definitions */\n+#define RMI_RTT_PROT_MAP_FLAGS_OADDR_TYPE\tGENMASK(1, 0)\n+#define RMI_RTT_PROT_MAP_FLAGS_LIST_COUNT\tGENMASK(15, 2)\n+\n+/* S2AP Direct Encodings, used in RMI_RTT_UNPROT_MAP_FLAGS_S2AP */\n+#define RMI_S2AP_DIRECT_WRITE\t\t\tBIT(0)\n+#define RMI_S2AP_DIRECT_READ\t\t\tBIT(1)\n+\n+#endif /* __LINUX_ARM_SMCCC_RMI_H_ */\n"
} |
| 2/1 | 2026/07/15 21:29 | agent | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: {
"EnableConfigs": null,
"FocusSymbols": [],
"Reasoning": "The patch adds host-side Realm Management Interface (RMI) library functions and boot-time initialization for KVM to use. However, KVM support for Realms is not included in this patch, making the exported library functions unreachable from user-space. Furthermore, the boot-time initialization requires specific hardware/firmware (RMM) support that is not present in standard virtualized environments, and it will simply bail out early. There are no syscalls, ioctls, or other user-space interfaces introduced that syzkaller could fuzz.",
"WorthFuzzing": false
}Instruction: You are an expert Linux kernel maintainer. Your job is to review a provided patch series and determine if it makes functional changes to the kernel that should be fuzzed. IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in your workspace. Do NOT rely on your internal knowledge of the kernel. You must actively use your code access tools to examine the actual source code and confirm any assumptions. Return WorthFuzzing=false if the patch only contains: - Modifications to Documentation/, Kconfig files, or code comments. - Purely decorative changes, such as logging (e.g., pr_err, printk) or tracepoints. - Changes to numeric constants or macros that do not functionally alter execution flow. - Code paths that are impossible to reach in virtualized environments like GCE or QEMU, even when utilizing software-emulated hardware (e.g., usb gadget, mac80211_hwsim). If it modifies reachable core kernel logic, drivers, or architectures, use your code search tools to verify the code can be executed, then return WorthFuzzing=true. When returning WorthFuzzing=true, you MUST ALSO: 1. Extract any specific kernel functions that should be heavily fuzzed into FocusSymbols. Avoid listing generic hot-path functions to prevent skewed test distributions. 2. Identify any specific CONFIG_ options required to properly test this new/modified feature. Go and look into the Kconfig files and check for ifdefs around the code, do not make assumptions. Do not list too generic configs (we already have them enabled). Only list those that specifically cover the modified code. List them in the EnableConfigs output array, and DO NOT add a '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: For your convenience, here is the diff of the changes:
commit 9ca347445c6cacc9ba0c808116fbcd8dc0790119
Author: syz-cluster <triage@syzkaller.com>
Date: Wed Jul 15 21:29:52 2026 +0000
syz-cluster: applied patch under review
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index b3afe0688919bd..0f1956edf1307b 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/include/asm/rmi_cmds.h b/arch/arm64/include/asm/rmi_cmds.h
new file mode 100644
index 00000000000000..f13ecdb40ded41
--- /dev/null
+++ b/arch/arm64/include/asm/rmi_cmds.h
@@ -0,0 +1,459 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2023-2026 ARM Ltd.
+ */
+
+#ifndef __ASM_RMI_CMDS_H
+#define __ASM_RMI_CMDS_H
+
+#include <linux/arm-rmi-cmds.h>
+#include <linux/arm-smccc-rmi.h>
+
+/**
+ * rmi_rtt_data_map_init() - Create a protected mapping with data contents
+ * @rd: PA of the RD
+ * @data: PA of the target granule
+ * @ipa: IPA at which the granule will be mapped in the guest
+ * @src: PA of the source granule
+ * @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: RMI return code
+ */
+static inline int 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(®s);
+}
+
+/**
+ * rmi_rtt_data_map() - Create mappings in protected IPA 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 RMI return code
+ */
+static inline int 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
+ };
+ int ret;
+
+ ret = rmi_sro_execute(®s);
+
+ if (out_top)
+ *out_top = regs.a1;
+
+ return ret;
+}
+
+/**
+ * rmi_rtt_data_unmap() - Remove mappings to conventional memory
+ * @rd: PA of the RD for the target Realm
+ * @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: RMI return code
+ */
+static inline int 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
+ };
+ int ret;
+
+ ret = rmi_sro_execute(®s);
+
+ if (out_top)
+ *out_top = regs.a1;
+ if (out_range)
+ *out_range = regs.a2;
+ if (out_count)
+ *out_count = regs.a3;
+
+ return ret;
+}
+
+/**
+ * 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 int rmi_psci_complete(unsigned long calling_rec,
+ unsigned long status)
+{
+ struct arm_smccc_res res;
+
+ arm_smccc_1_1_invoke(SMC_RMI_PSCI_COMPLETE, calling_rec, status, &res);
+
+ return res.a0;
+}
+
+/**
+ * rmi_realm_activate() - Active a realm
+ * @rd: PA of the RD
+ *
+ * Mark a realm as Active signalling that creation is complete and allowing
+ * execution of the realm.
+ *
+ * Return: RMI return code
+ */
+static inline int rmi_realm_activate(unsigned long rd)
+{
+ struct arm_smccc_res res;
+
+ arm_smccc_1_1_invoke(SMC_RMI_REALM_ACTIVATE, rd, &res);
+
+ return res.a0;
+}
+
+/**
+ * rmi_realm_create() - Create a realm
+ * @rd: PA of the RD
+ * @params: PA of realm parameters
+ * @sro: Preallocated SRO context to be used
+ *
+ * Create a new realm using the given parameters.
+ *
+ * Return: RMI return code
+ */
+static inline int 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 to be used
+ *
+ * Terminates a realm, moving it into a ZOMBIE state
+ *
+ * Return: RMI return code
+ */
+static inline int 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 to be used
+ *
+ * Destroys a realm, all objects belonging to the realm must be destroyed first.
+ *
+ * Return: RMI return code
+ */
+static inline int 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: RMI return code
+ */
+static inline int rmi_rec_create(unsigned long rd,
+ unsigned long rec,
+ unsigned long params,
+ struct rmi_sro_state *sro)
+{
+ int ret;
+
+ *sro = (struct rmi_sro_state){.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: RMI return code
+ */
+static inline int 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 int rmi_rec_enter(unsigned long rec, unsigned long run_ptr)
+{
+ struct arm_smccc_res res;
+
+ arm_smccc_1_1_invoke(SMC_RMI_REC_ENTER, rec, run_ptr, &res);
+
+ return res.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: RMI return code
+ */
+static inline int 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(®s);
+}
+
+/**
+ * rmi_rtt_destroy() - Destroy an RTT
+ * @rd: PA of the RD for the target realm
+ * @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: RMI return code.
+ */
+static inline int 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
+ };
+ int ret = rmi_sro_execute(®s);
+
+ if (out_rtt)
+ *out_rtt = regs.a1;
+ if (out_top)
+ *out_top = regs.a2;
+
+ 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: RMI return code
+ */
+static inline int 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
+ };
+ int ret = rmi_sro_execute(®s);
+
+ if (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: RMI return code
+ */
+static inline int 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
+ };
+ int ret = rmi_sro_execute(®s);
+
+ if (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: RMI return code
+ */
+static inline int 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
+ };
+ int ret = rmi_sro_execute(®s);
+
+ if (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: RMI return code
+ */
+static inline int 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
+ };
+ int ret = rmi_sro_execute(®s);
+
+ if (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: RMI return code
+ */
+static inline int 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
+ };
+ int ret = rmi_sro_execute(®s);
+
+ if (out_top)
+ *out_top = regs.a1;
+ if (out_range)
+ *out_range = regs.a2;
+ if (out_count)
+ *out_count = regs.a3;
+
+ return ret;
+}
+
+#endif /* __ASM_RMI_CMDS_H */
diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
index 9a22df0c5120f3..4ae63c89cda97d 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 12dc70254842ce..55542879fbb8ab 100644
--- a/drivers/firmware/Kconfig
+++ b/drivers/firmware/Kconfig
@@ -301,5 +301,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 4ddec2820c96fa..07e8ca1789b49e 100644
--- a/drivers/firmware/Makefile
+++ b/drivers/firmware/Makefile
@@ -38,3 +38,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 00000000000000..d224a72cca9089
--- /dev/null
+++ b/drivers/firmware/arm_rmm/Kconfig
@@ -0,0 +1,22 @@
+
+config ARCH_SUPPORTS_RMM
+ bool
+
+config ARM_RMM
+ bool "Realm Management Monitor (RMM) Support"
+ depends on ARCH_SUPPORTS_RMM
+ default y
+ 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 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. 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 00000000000000..51aef5cc04b02c
--- /dev/null
+++ b/drivers/firmware/arm_rmm/Makefile
@@ -0,0 +1,2 @@
+
+obj-$(CONFIG_ARM_RMM) = rmi.o
diff --git a/drivers/firmware/arm_rmm/rmi.c b/drivers/firmware/arm_rmm/rmi.c
new file mode 100644
index 00000000000000..d0c083bdf251f4
--- /dev/null
+++ b/drivers/firmware/arm_rmm/rmi.c
@@ -0,0 +1,724 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023-2026 ARM Ltd.
+ */
+
+#include <linux/cpufeature.h>
+#include <linux/memblock.h>
+#include <linux/arm-rmi-cmds.h>
+#include <linux/processor.h>
+#include <linux/slab.h>
+
+#include <asm/memory.h>
+#include <asm/pgtable-hwdef.h>
+
+static bool arm64_rmi_is_available;
+
+/* Currently only the first 2 registers are used by Linux */
+#define RMI_FEAT_REG_COUNT 2
+static __ro_after_init unsigned long rmi_feat_reg_cache[RMI_FEAT_REG_COUNT];
+
+unsigned long rmi_feat_reg(unsigned long id)
+{
+ if (WARN_ON(id >= RMI_FEAT_REG_COUNT))
+ return 0;
+
+ return rmi_feat_reg_cache[id];
+}
+EXPORT_SYMBOL_GPL(rmi_feat_reg);
+
+int rmi_delegate_range(phys_addr_t phys,
+ unsigned long size,
+ phys_addr_t *out_phys)
+{
+ unsigned 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)
+ phys = out_top;
+ else if (ret == RMI_BUSY || ret == RMI_BLOCKED)
+ cpu_relax();
+ else
+ break;
+ }
+
+ if (out_phys)
+ *out_phys = phys;
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(rmi_delegate_range);
+
+int rmi_undelegate_range(phys_addr_t phys,
+ unsigned long size)
+{
+ unsigned 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)
+ phys = out_top;
+ else if (ret == RMI_BUSY || ret == RMI_BLOCKED)
+ cpu_relax();
+ else
+ break;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(rmi_undelegate_range);
+
+static unsigned long donate_req_to_size(unsigned long donatereq)
+{
+ unsigned long unit_size = RMI_DONATE_SIZE(donatereq);
+
+ return BIT(ARM64_HW_PGTABLE_LEVEL_SHIFT(3 - unit_size));
+}
+
+static void rmi_smccc_invoke(struct arm_smccc_1_2_regs *regs_in,
+ struct arm_smccc_1_2_regs *regs_out)
+{
+ struct arm_smccc_1_2_regs regs = *regs_in;
+ unsigned long status;
+
+ while (1) {
+ arm_smccc_1_2_invoke(®s, regs_out);
+ status = RMI_RETURN_STATUS(regs_out->a0);
+ if (status != RMI_BUSY && status != RMI_BLOCKED)
+ break;
+ cpu_relax();
+ }
+}
+
+static void rmi_op_continue(unsigned long sro_handle, unsigned long flags,
+ struct arm_smccc_1_2_regs *out_regs)
+{
+ struct arm_smccc_1_2_regs regs = {
+ SMC_RMI_OP_CONTINUE, sro_handle, flags
+ };
+
+ rmi_smccc_invoke(®s, out_regs);
+}
+
+static void rmi_op_cancel(unsigned long sro_handle,
+ struct arm_smccc_1_2_regs *out_regs)
+{
+ struct arm_smccc_1_2_regs regs = {
+ SMC_RMI_OP_CANCEL, sro_handle
+ };
+
+ rmi_smccc_invoke(®s, 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)
+{
+ struct arm_smccc_1_2_regs regs = {
+ SMC_RMI_OP_MEM_DONATE, sro_handle, list_addr, list_count, flags
+ };
+
+ rmi_smccc_invoke(®s, 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)
+{
+ struct arm_smccc_1_2_regs regs = {
+ SMC_RMI_OP_MEM_RECLAIM, sro_handle, list_addr, list_count
+ };
+
+ rmi_smccc_invoke(®s, out_regs);
+}
+
+int 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(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 unit_size = RMI_DONATE_SIZE(donatereq);
+ unsigned long unit_size_bytes = donate_req_to_size(donatereq);
+ unsigned long count = RMI_DONATE_COUNT(donatereq);
+ unsigned long state = RMI_DONATE_STATE(donatereq);
+ unsigned long size = unit_size_bytes * count;
+ unsigned long addr_range;
+ int ret;
+ void *virt;
+ phys_addr_t phys;
+
+ for (int i = 0; i < sro->addr_count; i++) {
+ unsigned long entry = sro->addr_list[i];
+
+ if (RMI_ADDR_RANGE_SIZE(entry) == unit_size &&
+ 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 out;
+ }
+ }
+
+ 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_SIZE_MASK, &addr_range, unit_size);
+ 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;
+
+out:
+ rmi_op_mem_donate(sro_handle,
+ virt_to_phys(&sro->addr_list[sro->addr_count]), 1,
+ 0, out_regs);
+
+ unsigned long donated_granules = out_regs->a1;
+ unsigned long donated_size = donated_granules << PAGE_SHIFT;
+
+ if (donated_granules == 0) {
+ /* No pages used by the RMM */
+ sro->addr_count++;
+ } else if (donated_size < size) {
+ phys = sro->addr_list[sro->addr_count] & RMI_ADDR_RANGE_ADDR_MASK;
+
+ /* Not all granules used by the RMM, free the remaining pages */
+ for (long i = donated_size; i < size; i += PAGE_SIZE) {
+ if (state == RMI_OP_MEM_DELEGATED)
+ free_delegated_page(phys + i);
+ else
+ __free_page(phys_to_page(phys + i));
+ }
+ }
+
+ 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 unit_size = RMI_DONATE_SIZE(donatereq);
+ unsigned long unit_size_bytes = donate_req_to_size(donatereq);
+ unsigned long count = RMI_DONATE_COUNT(donatereq);
+ unsigned long state = RMI_DONATE_STATE(donatereq);
+ unsigned long found = 0;
+ unsigned long addr_list_start = sro->addr_count;
+ int ret;
+
+ for (int i = 0; i < addr_list_start && found < count; i++) {
+ unsigned long entry = sro->addr_list[i];
+
+ if (RMI_ADDR_RANGE_SIZE(entry) == unit_size &&
+ 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++;
+ i--;
+ }
+ }
+
+ ret = rmi_sro_ensure_capacity(sro, count - found);
+ if (ret)
+ return ret;
+
+ while (found < count) {
+ unsigned long addr_range;
+ void *virt = alloc_pages_exact(unit_size_bytes, 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, unit_size_bytes,
+ &delegated_phys)) {
+ if (!rmi_undelegate_range(phys, delegated_phys - phys))
+ free_pages_exact(virt, unit_size_bytes);
+ return -ENXIO;
+ }
+ }
+
+ addr_range = phys & RMI_ADDR_RANGE_ADDR_MASK;
+ FIELD_MODIFY(RMI_ADDR_RANGE_SIZE_MASK, &addr_range, unit_size);
+ 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;
+ found++;
+ }
+
+ rmi_op_mem_donate(sro_handle,
+ virt_to_phys(&sro->addr_list[addr_list_start]),
+ found, 0, out_regs);
+
+ unsigned long donated_granules = out_regs->a1;
+ unsigned long granules_per_unit = unit_size_bytes >> PAGE_SHIFT;
+ unsigned long consumed_units;
+
+ /*
+ * The RMM shouldn't report more granules than we provided, but clamp
+ * just in case.
+ */
+ if (WARN_ON_ONCE(donated_granules > found * granules_per_unit))
+ donated_granules = found * granules_per_unit;
+
+ /*
+ * The RMM reports the consumed memory in terms of granules, but we
+ * track in the address lists in unit-sized ranges. So divide to get
+ * the number of (complete) consumed units.
+ */
+ consumed_units = donated_granules / granules_per_unit;
+ if (donated_granules % granules_per_unit) {
+ /*
+ * A unit 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_units];
+ phys_addr_t phys = RMI_ADDR_RANGE_ADDR(entry);
+ unsigned long donated_size =
+ (donated_granules % granules_per_unit) << PAGE_SHIFT;
+
+ /* Free the tail back */
+ for (unsigned long i = donated_size; i < unit_size_bytes;
+ i += PAGE_SIZE) {
+ if (state == RMI_OP_MEM_DELEGATED)
+ free_delegated_page(phys + i);
+ else
+ __free_page(phys_to_page(phys + i));
+ }
+
+ /*
+ * This unit is now fully 'consumed' (either held by the RMM or
+ * freed)
+ */
+ consumed_units++;
+ }
+
+ /* Keep just the units the RMM didn't use in addr_list */
+ for (unsigned long i = consumed_units; i < found; i++)
+ sro->addr_list[addr_list_start + i - consumed_units] =
+ sro->addr_list[addr_list_start + i];
+
+ sro->addr_count -= consumed_units;
+
+ 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)
+{
+ unsigned long count = RMI_DONATE_COUNT(donatereq);
+
+ if (WARN_ON_ONCE(!count))
+ return 0;
+
+ if (RMI_DONATE_CONTIG(donatereq)) {
+ 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;
+ int ret;
+
+ ret = rmi_sro_ensure_capacity(sro, 1);
+ if (ret)
+ 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);
+
+ 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)
+{
+ for (int i = 0; i < sro->addr_count; i++) {
+ unsigned long entry = sro->addr_list[i];
+ unsigned long addr = RMI_ADDR_RANGE_ADDR(entry);
+ unsigned long unit_size = RMI_ADDR_RANGE_SIZE(entry);
+ unsigned long count = RMI_ADDR_RANGE_COUNT(entry);
+ unsigned long state = RMI_ADDR_RANGE_STATE(entry);
+ unsigned long size = donate_req_to_size(unit_size) * count;
+
+ if (state == RMI_OP_MEM_DELEGATED) {
+ if (WARN_ON_ONCE(rmi_undelegate_range(addr, size))) {
+ /* Leak the pages */
+ continue;
+ }
+ }
+ free_pages_exact(phys_to_virt(addr), size);
+ }
+
+ sro->addr_count = 0;
+}
+EXPORT_SYMBOL_GPL(rmi_sro_free);
+
+unsigned long rmi_sro_memxfer_execute(struct rmi_sro_state *sro, gfp_t gfp)
+{
+ unsigned long sro_handle;
+ struct arm_smccc_1_2_regs *regs = &sro->regs;
+
+ rmi_smccc_invoke(regs, regs);
+
+ sro_handle = regs->a1;
+
+ while (RMI_RETURN_STATUS(regs->a0) == RMI_INCOMPLETE) {
+ bool can_cancel = RMI_RETURN_CAN_CANCEL(regs->a0);
+ int ret = 0;
+
+ switch (RMI_RETURN_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:
+ ret = WARN_ON_ONCE(1);
+ break;
+ }
+
+ if (ret) {
+ if (can_cancel)
+ rmi_op_cancel(sro_handle, regs);
+
+ if (WARN_ON_ONCE(RMI_RETURN_STATUS(regs->a0) != RMI_INCOMPLETE))
+ return ret;
+ }
+ }
+
+ return regs->a0;
+}
+EXPORT_SYMBOL_GPL(rmi_sro_memxfer_execute);
+
+/* For RMI commands that are stateful but not memory-transferring */
+unsigned long rmi_sro_execute(struct arm_smccc_1_2_regs *regs)
+{
+ unsigned long sro_handle;
+
+ rmi_smccc_invoke(regs, regs);
+
+ sro_handle = regs->a1;
+
+ while (RMI_RETURN_STATUS(regs->a0) == RMI_INCOMPLETE) {
+ bool can_cancel = RMI_RETURN_CAN_CANCEL(regs->a0);
+
+ switch (RMI_RETURN_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;
+
+ rmi_op_cancel(sro_handle, regs);
+ }
+ }
+
+ return regs->a0;
+}
+EXPORT_SYMBOL_GPL(rmi_sro_execute);
+
+static int rmi_check_version(void)
+{
+ struct arm_smccc_res res;
+ 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);
+
+ /* 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;
+
+ arm_smccc_1_1_invoke(SMC_RMI_VERSION, host_version, &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 < RMI_FEAT_REG_COUNT; i++) {
+ if (WARN_ON(rmi_features(i, &rmi_feat_reg_cache[i])))
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rmi_configure(void)
+{
+ unsigned long ret;
+ struct rmm_config *config __free(free_page) = (struct rmm_config *)get_zeroed_page(GFP_KERNEL);
+ struct rmi_sro_state *sro __free(kfree) = (struct rmi_sro_state *)kmalloc_obj(*sro);
+
+ if (!config || !sro)
+ return -ENOMEM;
+
+ switch (PAGE_SIZE) {
+ case SZ_4K:
+ config->rmi_granule_size = RMI_GRANULE_SIZE_4KB;
+ break;
+ case SZ_16K:
+ config->rmi_granule_size = RMI_GRANULE_SIZE_16KB;
+ break;
+ case SZ_64K:
+ config->rmi_granule_size = RMI_GRANULE_SIZE_64KB;
+ break;
+ default:
+ BUILD_BUG();
+ }
+
+ /*
+ * 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\n");
+ return -EINVAL;
+ }
+
+ ret = rmi_rmm_activate(sro);
+ if (ret) {
+ pr_err("RMM activate failed\n");
+ return -ENXIO;
+ }
+
+ return 0;
+}
+
+/*
+ * 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 ||
+ 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\n",
+ start);
+ return -ENODEV;
+ }
+ start = next;
+ }
+
+ return 0;
+}
+
+static int rmi_create_gpts(phys_addr_t start, phys_addr_t end)
+{
+ struct rmi_sro_state *sro;
+ unsigned long l0gpt_sz;
+
+ sro = kmalloc_obj(*sro, GFP_KERNEL);
+ if (!sro)
+ return -ENOMEM;
+
+ 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) {
+ int ret = rmi_gpt_l1_create(start, sro, GFP_KERNEL);
+
+ /*
+ * Make sure the L1 GPT tables are created for the region.
+ * RMI_ERROR_GPT indicates the L1 table already exists.
+ */
+ if (ret != RMI_SUCCESS && RMI_RETURN_STATUS(ret) != RMI_ERROR_GPT) {
+ pr_err("GPT Level1 table missing for %llx\n", start);
+ kfree(sro);
+ return -ENOMEM;
+ }
+ start += l0gpt_sz;
+ }
+
+ kfree(sro);
+ return 0;
+}
+
+static int rmi_init_metadata(void)
+{
+ phys_addr_t start, end;
+ const struct memblock_region *r;
+
+ for_each_mem_region(r) {
+ int ret;
+
+ start = memblock_region_memory_base_pfn(r) << PAGE_SHIFT;
+ end = memblock_region_memory_end_pfn(r) << PAGE_SHIFT;
+ ret = rmi_verify_memory_tracking(start, end);
+ if (ret)
+ return ret;
+ ret = rmi_create_gpts(start, end);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+bool is_rmi_available(void)
+{
+ return arm64_rmi_is_available;
+}
+
+static int __init arm64_init_rmi(void)
+{
+ int ret;
+
+ /* Continue without realm support if we can't agree on a version */
+ ret = rmi_check_version();
+ if (ret)
+ return ret;
+
+ ret = rmi_read_features();
+ if (ret)
+ return ret;
+
+ ret = rmi_configure();
+ if (ret)
+ return ret;
+
+ ret = rmi_init_metadata();
+ if (ret)
+ return ret;
+
+ arm64_rmi_is_available = true;
+ pr_info("RMI configured");
+
+ 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 00000000000000..138983ab4e3c22
--- /dev/null
+++ b/include/linux/arm-rmi-cmds.h
@@ -0,0 +1,205 @@
+/* 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/types.h>
+
+#include <asm/page.h>
+
+struct rtt_entry {
+ unsigned long walk_level;
+ unsigned long desc;
+ int state;
+ int ripas;
+};
+
+#define RMI_MAX_ADDR_LIST 256
+
+struct rmi_sro_state {
+ struct arm_smccc_1_2_regs regs;
+ unsigned long addr_count;
+ unsigned long addr_list[RMI_MAX_ADDR_LIST];
+};
+
+unsigned long rmi_feat_reg(unsigned long id);
+
+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 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);
+
+unsigned long rmi_sro_memxfer_execute(struct rmi_sro_state *sro, gfp_t gfp);
+void rmi_sro_free(struct rmi_sro_state *sro);
+unsigned long rmi_sro_execute(struct arm_smccc_1_2_regs *regs);
+
+#define rmi_sro_memxfer_cmd(sro, gfp, ...) ({ \
+ struct rmi_sro_state *__sro = (sro); \
+ *__sro = (struct rmi_sro_state){ .regs = {__VA_ARGS__} }; \
+ int __ret = rmi_sro_memxfer_execute(__sro, gfp); \
+ rmi_sro_free(__sro); \
+ __ret; \
+})
+
+/**
+ * rmi_rmm_config_set() - Configure the RMM
+ * @cfg_ptr: PA of a struct rmm_config
+ *
+ * Sets configuration options on the RMM.
+ *
+ * Return: RMI return code
+ */
+static inline int rmi_rmm_config_set(unsigned long cfg_ptr)
+{
+ struct arm_smccc_res res;
+
+ arm_smccc_1_1_invoke(SMC_RMI_RMM_CONFIG_SET, cfg_ptr, &res);
+
+ return res.a0;
+}
+
+/**
+ * rmi_rmm_activate() - Activate the RMM
+ * @sro: Preallocated SRO context to be used
+ *
+ * Return: RMI return code
+ */
+static inline int rmi_rmm_activate(struct rmi_sro_state *sro)
+{
+ return rmi_sro_memxfer_cmd(sro, GFP_KERNEL, SMC_RMI_RMM_ACTIVATE);
+}
+
+/**
+ * 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 inline 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_res res;
+
+ arm_smccc_1_1_invoke(SMC_RMI_GRANULE_TRACKING_GET, start, end, &res);
+
+ if (out_category)
+ *out_category = res.a1;
+ if (out_state)
+ *out_state = res.a2;
+ if (out_top)
+ *out_top = res.a3;
+
+ return res.a0;
+}
+
+/**
+ * rmi_gpt_l1_create() - Create a Level 1 GPT
+ * @addr: Base of physical address region described by the L1GPT
+ * @sro: Preallocated SRO context to be used
+ * @gfp: Allocation flags for SRO memory donation requests
+ *
+ * Return: RMI return code
+ */
+static inline int rmi_gpt_l1_create(unsigned long addr,
+ struct rmi_sro_state *sro,
+ gfp_t gfp)
+{
+ return rmi_sro_memxfer_cmd(sro, gfp, SMC_RMI_GPT_L1_CREATE, addr);
+}
+
+/**
+ * rmi_features() - Read feature register
+ * @index: Feature register index
+ * @out: Feature register value is written to this pointer
+ *
+ * Return: RMI return code
+ */
+static inline int rmi_features(unsigned long index, unsigned long *out)
+{
+ struct arm_smccc_res res;
+
+ arm_smccc_1_1_invoke(SMC_RMI_FEATURES, index, &res);
+
+ if (out)
+ *out = res.a1;
+ return res.a0;
+}
+
+/**
+ * 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: RMI return code
+ */
+static inline int 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
+ };
+ int ret = rmi_sro_execute(®s);
+
+ if (out_top)
+ *out_top = regs.a1;
+
+ return ret;
+}
+
+/**
+ * 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.
+ *
+ * Return: RMI return code
+ */
+static inline int 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
+ };
+ int ret = rmi_sro_execute(®s);
+
+ if (out_top)
+ *out_top = regs.a1;
+
+ return ret;
+}
+
+#endif
diff --git a/include/linux/arm-smccc-rmi.h b/include/linux/arm-smccc-rmi.h
new file mode 100644
index 00000000000000..c965e8867518f3
--- /dev/null
+++ b/include/linux/arm-smccc-rmi.h
@@ -0,0 +1,499 @@
+/* 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-bet2:
+ * https://developer.arm.com/documentation/den0137/2-0bet2/
+ */
+
+#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>
+#include <asm/sysreg.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_VDEV_P2P_BIND SMC_RMI_CALL(0x01d4)
+#define SMC_RMI_VDEV_P2P_UNBIND SMC_RMI_CALL(0x01d5)
+#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_CMEM_ADD_PDEV SMC_RMI_CALL(0x01e4)
+#define SMC_RMI_CMEM_CREATE SMC_RMI_CALL(0x01e5)
+#define SMC_RMI_CMEM_DESTROY SMC_RMI_CALL(0x01e6)
+#define SMC_RMI_CMEM_POPULATE SMC_RMI_CALL(0x01e7)
+#define SMC_RMI_CMEM_REMOVE_PDEV SMC_RMI_CALL(0x01e8)
+#define SMC_RMI_CMEM_START SMC_RMI_CALL(0x01e9)
+#define SMC_RMI_CMEM_STOP SMC_RMI_CALL(0x01ea)
+#define SMC_RMI_CMEM_UNPOPULATE SMC_RMI_CALL(0x01eb)
+#define SMC_RMI_RMM_CONFIG_GET SMC_RMI_CALL(0x01ec)
+#define SMC_RMI_PDEV_MEC_REFRESH SMC_RMI_CALL(0x01ed)
+#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 RMI_ABI_MAJOR_VERSION 2
+#define RMI_ABI_MINOR_VERSION 0
+
+#define RMI_ABI_VERSION_GET_MAJOR(version) ((version) >> 16)
+#define RMI_ABI_VERSION_GET_MINOR(version) ((version) & 0xFFFF)
+#define RMI_ABI_VERSION(major, minor) (((major) << 16) | (minor))
+
+#define RMI_RETURN_STATUS_MASK (0xFFUL)
+#define RMI_RETURN_INDEX_MASK (0xFFUL << 8)
+#define RMI_RETURN_MEMREQ_MASK (0x3UL << 8)
+#define RMI_RETURN_CAN_CANCEL_MASK (0x1UL << 10)
+
+#define RMI_RETURN_STATUS(ret) FIELD_GET(RMI_RETURN_STATUS_MASK, ret)
+#define RMI_RETURN_INDEX(ret) FIELD_GET(RMI_RETURN_INDEX_MASK, ret)
+#define RMI_RETURN_MEMREQ(ret) FIELD_GET(RMI_RETURN_MEMREQ_MASK, ret)
+#define RMI_RETURN_CAN_CANCEL(ret) FIELD_GET(RMI_RETURN_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_DONATE_SIZE_MASK 3UL
+#define RMI_DONATE_COUNT_MASK GENMASK(15, 2)
+#define RMI_DONATE_CONTIG_MASK BIT(16)
+#define RMI_DONATE_STATE_MASK BIT(17)
+
+#define RMI_DONATE_SIZE(req) FIELD_GET(RMI_DONATE_SIZE_MASK, req)
+#define RMI_DONATE_COUNT(req) FIELD_GET(RMI_DONATE_COUNT_MASK, req)
+#define RMI_DONATE_CONTIG(req) FIELD_GET(RMI_DONATE_CONTIG_MASK, req)
+#define RMI_DONATE_STATE(req) FIELD_GET(RMI_DONATE_STATE_MASK, req)
+
+#define RMI_OP_MEM_DELEGATED 0
+#define RMI_OP_MEM_UNDELEGATED 1
+
+#define RMI_ADDR_TYPE_NONE 0
+#define RMI_ADDR_TYPE_SINGLE 1
+#define RMI_ADDR_TYPE_LIST 2
+
+#define RMI_ADDR_RANGE_SIZE_MASK GENMASK(1, 0)
+#define RMI_ADDR_RANGE_COUNT_MASK GENMASK(PAGE_SHIFT - 1, 2)
+#define RMI_ADDR_RANGE_ADDR_MASK (PAGE_MASK & GENMASK(51, 0))
+#define RMI_ADDR_RANGE_STATE_MASK BIT(63)
+
+#define RMI_ADDR_RANGE_SIZE(ar) (FIELD_GET(RMI_ADDR_RANGE_SIZE_MASK, \
+ (ar)))
+#define RMI_ADDR_RANGE_COUNT(ar) (FIELD_GET(RMI_ADDR_RANGE_COUNT_MASK, \
+ (ar)))
+#define RMI_ADDR_RANGE_ADDR(ar) ((ar) & RMI_ADDR_RANGE_ADDR_MASK)
+#define RMI_ADDR_RANGE_STATE(ar) (FIELD_GET(RMI_ADDR_RANGE_STATE_MASK, \
+ (ar)))
+
+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_S2SZ GENMASK(7, 0)
+#define RMI_FEATURE_REGISTER_0_LPA2 BIT(8)
+#define RMI_FEATURE_REGISTER_0_SVE BIT(9)
+#define RMI_FEATURE_REGISTER_0_SVE_VL GENMASK(13, 10)
+#define RMI_FEATURE_REGISTER_0_NUM_BPS GENMASK(19, 14)
+#define RMI_FEATURE_REGISTER_0_NUM_WPS GENMASK(25, 20)
+#define RMI_FEATURE_REGISTER_0_PMU BIT(26)
+#define RMI_FEATURE_REGISTER_0_PMU_NUM_CTRS GENMASK(31, 27)
+
+#define RMI_FEATURE_REGISTER_1_RMI_GRAN_SZ_4KB BIT(0)
+#define RMI_FEATURE_REGISTER_1_RMI_GRAN_SZ_16KB BIT(1)
+#define RMI_FEATURE_REGISTER_1_RMI_GRAN_SZ_64KB BIT(2)
+#define RMI_FEATURE_REGISTER_1_HASH_SHA_256 BIT(3)
+#define RMI_FEATURE_REGISTER_1_HASH_SHA_384 BIT(4)
+#define RMI_FEATURE_REGISTER_1_HASH_SHA_512 BIT(5)
+#define RMI_FEATURE_REGISTER_1_MAX_RECS_ORDER GENMASK(9, 6)
+#define RMI_FEATURE_REGISTER_1_L0GPTSZ GENMASK(13, 10)
+#define RMI_FEATURE_REGISTER_1_PPS GENMASK(16, 14)
+
+#define RMI_FEATURE_REGISTER_2_DA BIT(0)
+#define RMI_FEATURE_REGISTER_2_DA_COH BIT(1)
+#define RMI_FEATURE_REGISTER_2_VSMMU BIT(2)
+#define RMI_FEATURE_REGISTER_2_ATS BIT(3)
+#define RMI_FEATURE_REGISTER_2_MAX_VDEVS_ORDER GENMASK(7, 4)
+#define RMI_FEATURE_REGISTER_2_VDEV_KROU BIT(8)
+#define RMI_FEATURE_REGISTER_2_NON_TEE_STREAM BIT(9)
+#define RMI_FEATURE_REGISTER_2_P2P BIT(10)
+#define RMI_FEATURE_REGISTER_2_CMEM_CXL BIT(11)
+#define RMI_FEATURE_REGISTER_2_MAX_CMEM GENMASK(19, 12)
+#define RMI_FEATURE_REGISTER_2_CMEM_TSE_REQ BIT(20)
+
+#define RMI_FEATURE_REGISTER_3_MAX_NUM_AUX_PLANES GENMASK(3, 0)
+#define RMI_FEATURE_REGISTER_3_RTT_PLANE GENMASK(5, 4)
+#define RMI_FEATURE_REGISTER_3_RTT_S2AP_INDIRECT BIT(6)
+
+#define RMI_FEATURE_REGISTER_4_MEC_COUNT GENMASK(63, 0)
+
+#define RMI_MEM_CATEGORY_CONVENTIONAL 0
+#define RMI_MEM_CATEGORY_DEV_NCOH 1
+#define RMI_MEM_CATEGORY_DEV_COH 2
+
+#define RMI_TRACKING_RESERVED 0
+#define RMI_TRACKING_NONE 1
+#define RMI_TRACKING_FINE 2
+#define RMI_TRACKING_COARSE 3
+
+#define RMI_GRANULE_SIZE_4KB 0
+#define RMI_GRANULE_SIZE_16KB 1
+#define RMI_GRANULE_SIZE_64KB 2
+
+/*
+ * 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_LPA2 BIT(0)
+#define RMI_REALM_PARAM_FLAG_SVE BIT(1)
+#define RMI_REALM_PARAM_FLAG_PMU BIT(2)
+#define RMI_REALM_PARAM_FLAG_DA BIT(3)
+#define RMI_REALM_PARAM_FLAG_LFA_POLICY GENMASK(6, 5)
+#define RMI_REALM_PARAM_FLAG_MEC_POLICY GENMASK(8, 7)
+
+#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 padding;
+ u64 rtt_base;
+ s64 rtt_level_start;
+ u64 rtt_num_start;
+ u64 flags1;
+ u64 aux_rtt_base[3];
+ };
+ u8 padding2[0x800];
+ };
+};
+
+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_ULL(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_EMULATED_MMIO BIT(0)
+#define REC_ENTER_FLAG_INJECT_SEA BIT(1)
+#define REC_ENTER_FLAG_TRAP_WFI BIT(2)
+#define REC_ENTER_FLAG_TRAP_WFE BIT(3)
+#define REC_ENTER_FLAG_RIPAS_RESPONSE BIT(4)
+#define REC_ENTER_FLAG_S2AP_RESPONSE BIT(5)
+#define REC_ENTER_FLAG_DEV_MEM_RESPONSE BIT(6)
+#define REC_ENTER_FLAG_FORCE_P0 BIT(7)
+
+#define REC_RUN_GPRS 31
+#define REC_MAX_GIC_NUM_LRS 16
+
+#define RMI_PERMITTED_GICV3_HCR_BITS (ICH_HCR_EL2_UIE | \
+ ICH_HCR_EL2_LRENPIE | \
+ ICH_HCR_EL2_NPIE | \
+ ICH_HCR_EL2_VGrp0EIE | \
+ ICH_HCR_EL2_VGrp0DIE | \
+ ICH_HCR_EL2_VGrp1EIE | \
+ ICH_HCR_EL2_VGrp1DIE | \
+ ICH_HCR_EL2_TDIR)
+
+struct rec_enter {
+ union { /* 0x000 */
+ u64 flags;
+ u8 padding0[0x200];
+ };
+ union { /* 0x200 */
+ u64 gprs[REC_RUN_GPRS];
+ u8 padding1[0x100];
+ };
+ u8 padding3[0x500];
+};
+
+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
+#define RMI_EXIT_VDEV_P2P_BINDING 0x0b
+
+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 padding8[15];
+ 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 padding5[0x100];
+ };
+ union { /* 0x600 */
+ struct {
+ u16 imm;
+ u16 padding9;
+ u64 plane;
+ };
+ u8 padding6[0x100];
+ };
+ union { /* 0x700 */
+ struct {
+ u8 pmu_ovf_status;
+ u8 padding10[15];
+ u64 vsmmu;
+ };
+ u8 padding7[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_OADDR_TYPE GENMASK(1, 0)
+#define RMI_RTT_UNPROT_MAP_FLAGS_LIST_COUNT GENMASK(15, 2)
+#define RMI_RTT_UNPROT_MAP_FLAGS_MEMATTR GENMASK(18, 16)
+#define RMI_RTT_UNPROT_MAP_FLAGS_S2AP GENMASK(22, 19)
+
+/* RMI_RTT_DATA_MAP_FLAGS definitions */
+#define RMI_RTT_PROT_MAP_FLAGS_OADDR_TYPE GENMASK(1, 0)
+#define RMI_RTT_PROT_MAP_FLAGS_LIST_COUNT GENMASK(15, 2)
+
+/* S2AP Direct Encodings, used in RMI_RTT_UNPROT_MAP_FLAGS_S2AP */
+#define RMI_S2AP_DIRECT_WRITE BIT(0)
+#define RMI_S2AP_DIRECT_READ BIT(1)
+
+#endif /* __LINUX_ARM_SMCCC_RMI_H_ */
|
| 3/2 | 2026/07/15 21:29 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 25827 output: 19 thoughts: 906 Thoughts: **Analyzing the Reachability of RMM/RMI Code in a Virtualized Environment** Okay, so we've got this patch adding Realm Management Monitor (RMM) support for ARM64. It seems pretty self-contained, adding the necessary header files and driver code: `arch/arm64/include/asm/rmi_cmds.h`, `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`. It introduces `CONFIG_ARM_RMM`, which is good, and it’s dependent on `ARCH_SUPPORTS_RMM`. It provides host-side RMI support, which is intended to be used by KVM for things like detecting a compatible RMM, configuring it, managing delegated memory, and enabling Realm guests. My initial thought is to assess its reachability within a virtualized setting, particularly QEMU. The code does a check for RME (Realm Management Extension) support in the CPU using the `ID_AA64PFR0_EL1` register. This is key, as RMI is contingent on RME. If RME isn't present, the initialization fails. QEMU does support RME emulation in its `virt` machine type, but it needs specific setup which probably includes TF-A and RMM. Syzkaller is a concern, as it often runs with default QEMU CPUs, such as `max` or `cortex-a57`. I need to verify whether the default QEMU CPUs are configured with RME support. The code itself, specifically `arm64_init_rmi`, is a `subsys_initcall`. It checks for the `ID_AA64PFR0_EL1_RME_SHIFT` bit. If RME isn't present, the initialization will return `-ENXIO`. If RME is present, it does SMC calls to RMM. If RMM is not present, the SMC calls return `SMCCC_RET_NOT_SUPPORTED`. So it's hardware- and firmware-dependent. However, syzkaller might be able to fuzz the SMC calls if there's a way to mock them. The problem statement mentions unreachable code paths in virtualized environments like GCE or QEMU, even with emulated hardware. So the question is: is RME supported in QEMU? Yes, QEMU has RME emulation. The patch itself just adds the RMI library functions and the initialization. There are no syscalls or ioctls exposed directly to user-space in *this* patch. These are exported using `EXPORT_SYMBOL_GPL` (e.g., `rmi_delegate_range`, `rmi_undelegate_range`, etc.). These functions are really meant for KVM. The patch description confirms this: "This option builds the host-side RMI support used by KVM...". Crucially, the *KVM* support itself isn't *in* this patch. The patch only *adds* the building blocks for KVM to use later. So without KVM using them, these exported functions are dead code. Given that the changes are ALREADY committed as HEAD, I should check whether there is a follow-up patch that adds the KVM code. I must search for where KVM uses these functions in the current tree. The prompt says the code is already in HEAD, so let me search for `rmi_delegate_range` or `rmi_sro_execute` in the tree. |
| 4/2 | 2026/07/15 21:30 | tool | grepper |
0mArgs:{
"Expression": "rmi_delegate_range"
}Results: {
"Output": "drivers/firmware/arm_rmm/rmi.c=28=EXPORT_SYMBOL_GPL(rmi_feat_reg);\ndrivers/firmware/arm_rmm/rmi.c-29-\ndrivers/firmware/arm_rmm/rmi.c:30:int rmi_delegate_range(phys_addr_t phys,\ndrivers/firmware/arm_rmm/rmi.c-31-\t\t unsigned long size,\n--\ndrivers/firmware/arm_rmm/rmi.c-52-}\ndrivers/firmware/arm_rmm/rmi.c:53:EXPORT_SYMBOL_GPL(rmi_delegate_range);\ndrivers/firmware/arm_rmm/rmi.c-54-\n--\ndrivers/firmware/arm_rmm/rmi.c=166=static int rmi_sro_donate_contig(struct rmi_sro_state *sro,\n--\ndrivers/firmware/arm_rmm/rmi.c-208-\ndrivers/firmware/arm_rmm/rmi.c:209:\t\tif (rmi_delegate_range(phys, size, \u0026delegated_phys)) {\ndrivers/firmware/arm_rmm/rmi.c-210-\t\t\tif (!rmi_undelegate_range(phys, delegated_phys - phys))\n--\ndrivers/firmware/arm_rmm/rmi.c=249=static int rmi_sro_donate_noncontig(struct rmi_sro_state *sro,\n--\ndrivers/firmware/arm_rmm/rmi.c-293-\ndrivers/firmware/arm_rmm/rmi.c:294:\t\t\tif (rmi_delegate_range(phys, unit_size_bytes,\ndrivers/firmware/arm_rmm/rmi.c-295-\t\t\t\t\t \u0026delegated_phys)) {\n--\ninclude/linux/arm-rmi-cmds.h=30=unsigned long rmi_feat_reg(unsigned long id);\ninclude/linux/arm-rmi-cmds.h-31-\ninclude/linux/arm-rmi-cmds.h:32:int rmi_delegate_range(phys_addr_t phys, unsigned long size,\ninclude/linux/arm-rmi-cmds.h-33-\t\t phys_addr_t *out_phys);\n--\ninclude/linux/arm-rmi-cmds.h=37=static inline int rmi_delegate_page(phys_addr_t phys)\ninclude/linux/arm-rmi-cmds.h-38-{\ninclude/linux/arm-rmi-cmds.h:39:\treturn rmi_delegate_range(phys, PAGE_SIZE, NULL);\ninclude/linux/arm-rmi-cmds.h-40-}\n"
} |
| 5/2 | 2026/07/15 21:30 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 28217 output: 19 thoughts: 41 Thoughts: **Investigating RMI Function Usage** Okay, so KVM isn't actively using this particular function. That's good to know, but doesn't rule out its overall significance. I should broaden the search. I need to make sure I'm not missing something subtly connected. Let's see if KVM might be indirectly leveraging any of these `rmi_` functions. Perhaps something like `rmi_realm_create` or a related function could be the key. I need to ensure a comprehensive overview of how it is used, or not used. I need to do a thorough check of the code base. |
| 6/2 | 2026/07/15 21:30 | tool | grepper |
0mArgs:{
"Expression": "rmi_realm_create"
}Results: {
"Output": "arch/arm64/include/asm/rmi_cmds.h=136=static inline int rmi_realm_activate(unsigned long rd)\n--\narch/arm64/include/asm/rmi_cmds.h-145-/**\narch/arm64/include/asm/rmi_cmds.h:146: * rmi_realm_create() - Create a realm\narch/arm64/include/asm/rmi_cmds.h-147- * @rd: PA of the RD\n--\narch/arm64/include/asm/rmi_cmds.h-154- */\narch/arm64/include/asm/rmi_cmds.h:155:static inline int rmi_realm_create(unsigned long rd, unsigned long params,\narch/arm64/include/asm/rmi_cmds.h-156-\t\t\t\t struct rmi_sro_state *sro)\n"
} |
| 7/2 | 2026/07/15 21:30 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 28637 output: 20 thoughts: 43 Thoughts: Okay, here's my take on those thoughts, framed as my own, given my expertise: **KVM Integration Concerns** Right, so I see this patch, and it looks like it's introducing the Remote Management Interface (RMI) library and its initialization, but it's not immediately clear how KVM is leveraging it in the current source tree. That's a bit of a red flag; you don't want to just add code that's not actively being used, it adds to the codebase bloat and potential vulnerabilities if it's not being used. My immediate thought is, "Okay, is this premature optimization, or is there a plan in place?" The next logical step is to dive in and look for callers of `rmi_sro_execute`. That's where I need to start digging to see *why* this has been added. Are there existing modules, drivers, or hypervisor components already slated to use it in future commits, or does this patch represent an unintegrated piece of the puzzle that needs further development? Understanding *where* this function is being called, and how it's being used, is going to inform my assessment of the patch's purpose and its readiness. Let's see if we can find some usage! |
| 8/2 | 2026/07/15 21:30 | tool | grepper |
0mArgs:{
"Expression": "rmi_sro_execute"
}Results: {
"Output": "arch/arm64/include/asm/rmi_cmds.h=25=static inline int rmi_rtt_data_map_init(unsigned long rd, unsigned long data,\n--\narch/arm64/include/asm/rmi_cmds.h-32-\narch/arm64/include/asm/rmi_cmds.h:33:\treturn rmi_sro_execute(\u0026regs);\narch/arm64/include/asm/rmi_cmds.h-34-}\n--\narch/arm64/include/asm/rmi_cmds.h=47=static inline int rmi_rtt_data_map(unsigned long rd,\n--\narch/arm64/include/asm/rmi_cmds.h-58-\narch/arm64/include/asm/rmi_cmds.h:59:\tret = rmi_sro_execute(\u0026regs);\narch/arm64/include/asm/rmi_cmds.h-60-\n--\narch/arm64/include/asm/rmi_cmds.h=82=static inline int rmi_rtt_data_unmap(unsigned long rd,\n--\narch/arm64/include/asm/rmi_cmds.h-95-\narch/arm64/include/asm/rmi_cmds.h:96:\tret = rmi_sro_execute(\u0026regs);\narch/arm64/include/asm/rmi_cmds.h-97-\n--\narch/arm64/include/asm/rmi_cmds.h=267=static inline int rmi_rtt_create(unsigned long rd, unsigned long rtt,\n--\narch/arm64/include/asm/rmi_cmds.h-273-\narch/arm64/include/asm/rmi_cmds.h:274:\treturn rmi_sro_execute(\u0026regs);\narch/arm64/include/asm/rmi_cmds.h-275-}\n--\narch/arm64/include/asm/rmi_cmds.h=291=static inline int rmi_rtt_destroy(unsigned long rd,\n--\narch/arm64/include/asm/rmi_cmds.h-299-\t};\narch/arm64/include/asm/rmi_cmds.h:300:\tint ret = rmi_sro_execute(\u0026regs);\narch/arm64/include/asm/rmi_cmds.h-301-\n--\narch/arm64/include/asm/rmi_cmds.h=322=static inline int rmi_rtt_fold(unsigned long rd, unsigned long ipa,\n--\narch/arm64/include/asm/rmi_cmds.h-327-\t};\narch/arm64/include/asm/rmi_cmds.h:328:\tint ret = rmi_sro_execute(\u0026regs);\narch/arm64/include/asm/rmi_cmds.h-329-\n--\narch/arm64/include/asm/rmi_cmds.h=347=static inline int rmi_rtt_init_ripas(unsigned long rd, unsigned long base,\n--\narch/arm64/include/asm/rmi_cmds.h-352-\t};\narch/arm64/include/asm/rmi_cmds.h:353:\tint ret = rmi_sro_execute(\u0026regs);\narch/arm64/include/asm/rmi_cmds.h-354-\n--\narch/arm64/include/asm/rmi_cmds.h=374=static inline int rmi_rtt_unprot_map(unsigned long rd,\n--\narch/arm64/include/asm/rmi_cmds.h-383-\t};\narch/arm64/include/asm/rmi_cmds.h:384:\tint ret = rmi_sro_execute(\u0026regs);\narch/arm64/include/asm/rmi_cmds.h-385-\n--\narch/arm64/include/asm/rmi_cmds.h=405=static inline int rmi_rtt_set_ripas(unsigned long rd, unsigned long rec,\n--\narch/arm64/include/asm/rmi_cmds.h-411-\t};\narch/arm64/include/asm/rmi_cmds.h:412:\tint ret = rmi_sro_execute(\u0026regs);\narch/arm64/include/asm/rmi_cmds.h-413-\n--\narch/arm64/include/asm/rmi_cmds.h=435=static inline int rmi_rtt_unprot_unmap(unsigned long rd,\n--\narch/arm64/include/asm/rmi_cmds.h-446-\t};\narch/arm64/include/asm/rmi_cmds.h:447:\tint ret = rmi_sro_execute(\u0026regs);\narch/arm64/include/asm/rmi_cmds.h-448-\n--\ndrivers/firmware/arm_rmm/rmi.c=478=EXPORT_SYMBOL_GPL(rmi_sro_memxfer_execute);\n--\ndrivers/firmware/arm_rmm/rmi.c-480-/* For RMI commands that are stateful but not memory-transferring */\ndrivers/firmware/arm_rmm/rmi.c:481:unsigned long rmi_sro_execute(struct arm_smccc_1_2_regs *regs)\ndrivers/firmware/arm_rmm/rmi.c-482-{\n--\ndrivers/firmware/arm_rmm/rmi.c-507-}\ndrivers/firmware/arm_rmm/rmi.c:508:EXPORT_SYMBOL_GPL(rmi_sro_execute);\ndrivers/firmware/arm_rmm/rmi.c-509-\n--\ninclude/linux/arm-rmi-cmds.h=50=void rmi_sro_free(struct rmi_sro_state *sro);\ninclude/linux/arm-rmi-cmds.h:51:unsigned long rmi_sro_execute(struct arm_smccc_1_2_regs *regs);\ninclude/linux/arm-rmi-cmds.h-52-\n--\ninclude/linux/arm-rmi-cmds.h=164=static inline int rmi_granule_range_delegate(unsigned long base,\n--\ninclude/linux/arm-rmi-cmds.h-170-\t};\ninclude/linux/arm-rmi-cmds.h:171:\tint ret = rmi_sro_execute(\u0026regs);\ninclude/linux/arm-rmi-cmds.h-172-\n--\ninclude/linux/arm-rmi-cmds.h=190=static inline int rmi_granule_range_undelegate(unsigned long base,\n--\ninclude/linux/arm-rmi-cmds.h-196-\t};\ninclude/linux/arm-rmi-cmds.h:197:\tint ret = rmi_sro_execute(\u0026regs);\ninclude/linux/arm-rmi-cmds.h-198-\n"
} |
| 9/2 | 2026/07/15 21:30 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 30701 output: 21 thoughts: 44 Thoughts: Okay, let's see what's going on here. **KVM Inquiry: Initial Confusion and Focused Investigation** Hmm, so it seems KVM isn't actively *in use* just yet. That’s a bit odd. But wait, `arm64_init_rmi` is getting called during the boot process. Okay, that sparks my interest. This suggests something is happening under the hood. Time to dig into `arm64_init_rmi` and understand precisely what role it plays. Let's see what we can find. I'm keen to understand how this is being initialized, even if KVM itself isn't yet visibly active. |
| 10/2 | 2026/07/15 21:30 | tool | grepper |
0mArgs:{
"Expression": "arm64_init_rmi"
}Results: {
"Output": "drivers/firmware/arm_rmm/rmi.c=687=bool is_rmi_available(void)\n--\ndrivers/firmware/arm_rmm/rmi.c-691-\ndrivers/firmware/arm_rmm/rmi.c:692:static int __init arm64_init_rmi(void)\ndrivers/firmware/arm_rmm/rmi.c-693-{\n--\ndrivers/firmware/arm_rmm/rmi.c-719-/*\ndrivers/firmware/arm_rmm/rmi.c:720: * Note arm64_init_rmi() must be called before kvm_init_rmi() otherwise KVM\ndrivers/firmware/arm_rmm/rmi.c-721- * will not support realm guests. subsys_initcall() is called before\n--\ndrivers/firmware/arm_rmm/rmi.c-723- */\ndrivers/firmware/arm_rmm/rmi.c:724:subsys_initcall(arm64_init_rmi);\n"
} |
| 11/2 | 2026/07/15 21:30 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 31176 output: 20 thoughts: 830 Thoughts: **RMI Initialization and Reachability Analysis** Okay, so I'm looking at this `arm64_init_rmi` function, and it's pretty straightforward: it calls a series of `rmi_*` functions for initialization during boot. Seems like this involves Secure Monitor Call (SMC) interactions. My immediate thought is, can this be fuzzed? Syzkaller is a fantastic tool for SMC fuzzing, but it usually relies on either intercepting the calls or fuzzing a hypervisor. Here, Linux *is* the host, calling into the RMM (Realm Management Monitor), which is firmware. Syzkaller doesn't typically target firmware directly unless there's a mock driver in place. However, I realize this is early boot code. If KVM isn't using it yet (which I suspect it isn't, given this is a new patch), the *only* place this code can be reached is the initialization sequence. A quick check of the code confirms this: if RME isn't supported, it returns `-ENXIO`. On a standard syzkaller VM, this is the expected outcome, since RME is not enabled in the default QEMU setup. So the question is, can we actually *reach* the rest of the code? It hinges on the CPU supporting RME. Syzkaller usually runs with `qemu-system-aarch64 -machine virt -cpu max`. Does `-cpu max` even *have* RME enabled? It looks like no, it requires `-machine virt,x-rme=on` and appropriate firmware (TF-A and RMM). Without the firmware, the SMC calls will just return `SMCCC_RET_NOT_SUPPORTED`. Even if KVM *was* using it, KVM would only use it if KVM itself was configured to support Realms, which this patch is not doing. Looking at the code confirms this is just initialization, and will likely exit immediately. The patch I'm looking at *adds* a library for KVM to use, and a initcall that KVM depends on. It looks like it's building infrastructure, not consuming it yet. So, the only code that's reachable is `arm64_init_rmi`, and *that* immediately bails out. This seems to align with the prompt's instruction: "Return WorthFuzzing=false if the patch only contains code paths that are impossible to reach in virtualized environments like GCE or QEMU, even when utilizing software-emulated hardware". But wait… is it *impossible*? If we pass `-machine virt,x-rme=on` to QEMU, could KVM *or* KVM unit tests reach it? But this patch doesn't add KVM support; it adds the building blocks. Let me double-check. I've already grepped for `rmi_delegate_range` and `rmi_sro_execute` and found no callers outside the newly added files. Let's make absolutely sure and double check `free_delegated_page` to be extra sure. |
| 12/2 | 2026/07/15 21:30 | tool | grepper |
0mArgs:{
"Expression": "free_delegated_page"
}Results: {
"Output": "drivers/firmware/arm_rmm/rmi.c=129=static void rmi_op_mem_reclaim(unsigned long sro_handle,\n--\ndrivers/firmware/arm_rmm/rmi.c-140-\ndrivers/firmware/arm_rmm/rmi.c:141:int free_delegated_page(phys_addr_t phys)\ndrivers/firmware/arm_rmm/rmi.c-142-{\n--\ndrivers/firmware/arm_rmm/rmi.c-151-}\ndrivers/firmware/arm_rmm/rmi.c:152:EXPORT_SYMBOL_GPL(free_delegated_page);\ndrivers/firmware/arm_rmm/rmi.c-153-\n--\ndrivers/firmware/arm_rmm/rmi.c=166=static int rmi_sro_donate_contig(struct rmi_sro_state *sro,\n--\ndrivers/firmware/arm_rmm/rmi.c-239-\t\t\tif (state == RMI_OP_MEM_DELEGATED)\ndrivers/firmware/arm_rmm/rmi.c:240:\t\t\t\tfree_delegated_page(phys + i);\ndrivers/firmware/arm_rmm/rmi.c-241-\t\t\telse\n--\ndrivers/firmware/arm_rmm/rmi.c=249=static int rmi_sro_donate_noncontig(struct rmi_sro_state *sro,\n--\ndrivers/firmware/arm_rmm/rmi.c-346-\t\t\tif (state == RMI_OP_MEM_DELEGATED)\ndrivers/firmware/arm_rmm/rmi.c:347:\t\t\t\tfree_delegated_page(phys + i);\ndrivers/firmware/arm_rmm/rmi.c-348-\t\t\telse\n--\ninclude/linux/arm-rmi-cmds.h=34=int rmi_undelegate_range(phys_addr_t phys, unsigned long size);\ninclude/linux/arm-rmi-cmds.h:35:int free_delegated_page(phys_addr_t phys);\ninclude/linux/arm-rmi-cmds.h-36-\n"
} |
| 13/2 | 2026/07/15 21:30 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 33238 output: 142 thoughts: 570 Thoughts: **Analysis of the RMI Code for Fuzzing Potential** Okay, so I've looked through the code, specifically the `drivers/firmware/arm_rmm/rmi.c` file. The initial assessment is clear: there are no entry points for external callers outside of this specific file. This immediately suggests that KVM support, which is what I'm primarily interested in, is *not* present here. My focus narrows to the `arm64_init_rmi()` function. It's the only code that appears to execute. Let's see if this is fuzzable. It's called once at boot. It checks if RME is supported by reading a CPU register, and if not it returns an error. If RME is supported, it calls `SMC_RMI_VERSION`. If that also fails, it gives an error. If that succeeds it reads features, configures RMM, and initializes metadata. The challenge here is obvious: it's a *one-time* boot initialization. It expects specific hardware and firmware – RMM. Standard Syzkaller environments, which is my primary fuzzing tool, don't typically have this. I can't trigger this code repeatedly from user-space, nor does it seem to expose any user-space interface, like a syscall, ioctl, or sysfs entry. Syzkaller works by executing syscalls, so this presents a fundamental problem. Wait, is there *any* device exposed? Nope. All I see is `arm64_rmi_is_available = true` being set, along with some symbol exports. This is completely useless for fuzzing. Syzkaller can't touch any of this. KVM support is missing. Let's double-check the Kconfig. The patch adds `CONFIG_ARM_RMM`, which defaults to "y" and depends on `ARCH_SUPPORTS_RMM`. Since KVM is *not* present, and KVM is the intended consumer, and KVM isn't using this library yet, this is purely dead code. It's library functions and a boot-time check that'll fail in our standard environments. It's not worth the effort to fuzz it. Okay, let's verify if KVM uses this through a different route. Was KVM merged, but this patch was missing? Nope, KVM's RME support is a large series, and this is one of the first patches. Therefore, my conclusion is that the code is not worth fuzzing. I'll return WorthFuzzing=false because KVM support is not yet present. The added RMI library functions are unreachable from user-space, and the boot-time initialization relies on specific firmware (RMM) not present in standard virtualized environments. |
| 14/2 | 2026/07/15 21:30 | tool | set-results |
0mArgs:{
"EnableConfigs": [],
"FocusSymbols": [],
"Reasoning": "The patch adds host-side Realm Management Interface (RMI) library functions and boot-time initialization for KVM to use. However, KVM support for Realms is not included in this patch, making the exported library functions unreachable from user-space. Furthermore, the boot-time initialization requires specific hardware/firmware (RMM) support that is not present in standard virtualized environments, and it will simply bail out early. There are no syscalls, ioctls, or other user-space interfaces introduced that syzkaller could fuzz.",
"WorthFuzzing": false
}Results: {
"EnableConfigs": null,
"FocusSymbols": [],
"Reasoning": "The patch adds host-side Realm Management Interface (RMI) library functions and boot-time initialization for KVM to use. However, KVM support for Realms is not included in this patch, making the exported library functions unreachable from user-space. Furthermore, the boot-time initialization requires specific hardware/firmware (RMM) support that is not present in standard virtualized environments, and it will simply bail out early. There are no syscalls, ioctls, or other user-space interfaces introduced that syzkaller could fuzz.",
"WorthFuzzing": false
} |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|