* [PATCH v17 1/7] firmware: arm_rmm: Add SMC definitions for calling the RMM
2026-09-07 9:59 [PATCH v17 0/7] firmware: arm_rmm: Add RMM v2.0 base RMI support Suzuki K Poulose
@ 2026-09-07 9:59 ` Suzuki K Poulose
2026-09-08 6:19 ` Gavin Shan
2026-09-07 9:59 ` [PATCH v17 2/7] firmware: arm_rmm: Check for RMI support at init Suzuki K Poulose
` (6 subsequent siblings)
7 siblings, 1 reply; 44+ messages in thread
From: Suzuki K Poulose @ 2026-09-07 9:59 UTC (permalink / raw)
To: kvm, kvmarm
Cc: maz, will, catalin.marinas, linux-kernel, linux-arm-kernel,
steven.price, aneesh.kumar, oupton, gshan, joey.gouly, tabba,
yuzenghui, linux-coco, gankulkarni, sdonthineni, alpergun,
fj0570is, WeiLin.Chang, lpieralisi, enju.kohei, Suzuki K Poulose
From: Steven Price <steven.price@arm.com>
The RMM (Realm Management Monitor) provides functionality that can be
accessed by SMC calls from the host.
The SMC definitions are based on DEN0137[1] version 2.0-bet3
[1] https://developer.arm.com/documentation/den0137/2-0bet3/
Signed-off-by: Steven Price <steven.price@arm.com>
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
Changes since v16:
* Updated definitions to RMM specification v2.0-bet3.
Changes since v15:
* Dropped unused symbols REC_MAX_GIC_NUM_LRS and RMI_PERMITTED_GICV3_HCR_BITS.
* Output is now (partially) generated from the spec source.
Changes since v14:
* Updated to RMM spec v2.0-bet2 but without the changes to move
metadata out of individual address range descriptors as this is
expected to be reverted in a future spec release.
Changes since v13:
* Updated to RMM spec v2.0-bet1
Changes since v12:
* Updated to RMM spec v2.0-bet0
Changes since v9:
* Corrected size of 'ripas_value' in struct rec_exit. The spec states
this is an 8-bit type with padding afterwards (rather than a u64).
Changes since v8:
* Added RMI_PERMITTED_GICV3_HCR_BITS to define which bits the RMM
permits to be modified.
Changes since v6:
* Renamed REC_ENTER_xxx defines to include 'FLAG' to make it obvious
these are flag values.
Changes since v5:
* Sorted the SMC #defines by value.
* Renamed SMI_RxI_CALL to SMI_RMI_CALL since the macro is only used for
RMI calls.
* Renamed REC_GIC_NUM_LRS to REC_MAX_GIC_NUM_LRS since the actual
number of available list registers could be lower.
* Provided a define for the reserved fields of FeatureRegister0.
* Fix inconsistent names for padding fields.
Changes since v4:
* Update to point to final released RMM spec.
* Minor rearrangements.
Changes since v3:
* Update to match RMM spec v1.0-rel0-rc1.
Changes since v2:
* Fix specification link.
* Rename rec_entry->rec_enter to match spec.
* Fix size of pmu_ovf_status to match spec.
---
include/linux/arm-smccc-rmi.h | 494 ++++++++++++++++++++++++++++++++++
1 file changed, 494 insertions(+)
create mode 100644 include/linux/arm-smccc-rmi.h
diff --git a/include/linux/arm-smccc-rmi.h b/include/linux/arm-smccc-rmi.h
new file mode 100644
index 0000000000000..3eb88caf40964
--- /dev/null
+++ b/include/linux/arm-smccc-rmi.h
@@ -0,0 +1,494 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2023-2026 ARM Ltd.
+ *
+ * The values and structures in this file are from the Realm Management Monitor
+ * specification (DEN0137) version 2.0-bet3:
+ * https://developer.arm.com/documentation/den0137/2-0bet3/
+ */
+
+#ifndef __LINUX_ARM_SMCCC_RMI_H_
+#define __LINUX_ARM_SMCCC_RMI_H_
+
+#include <linux/arm-smccc.h>
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/build_bug.h>
+#include <linux/sizes.h>
+
+#include <asm/page.h>
+
+#define SMC_RMI_CALL(func) \
+ ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \
+ ARM_SMCCC_SMC_64, \
+ ARM_SMCCC_OWNER_STANDARD, \
+ (func))
+
+#define SMC_RMI_VERSION SMC_RMI_CALL(0x0150)
+
+#define SMC_RMI_RTT_DATA_MAP_INIT SMC_RMI_CALL(0x0153)
+
+#define SMC_RMI_REALM_ACTIVATE SMC_RMI_CALL(0x0157)
+#define SMC_RMI_REALM_CREATE SMC_RMI_CALL(0x0158)
+#define SMC_RMI_REALM_DESTROY SMC_RMI_CALL(0x0159)
+#define SMC_RMI_REC_CREATE SMC_RMI_CALL(0x015a)
+#define SMC_RMI_REC_DESTROY SMC_RMI_CALL(0x015b)
+#define SMC_RMI_REC_ENTER SMC_RMI_CALL(0x015c)
+#define SMC_RMI_RTT_CREATE SMC_RMI_CALL(0x015d)
+#define SMC_RMI_RTT_DESTROY SMC_RMI_CALL(0x015e)
+
+#define SMC_RMI_RTT_READ_ENTRY SMC_RMI_CALL(0x0161)
+
+#define SMC_RMI_RTT_DEV_VALIDATE SMC_RMI_CALL(0x0163)
+#define SMC_RMI_PSCI_COMPLETE SMC_RMI_CALL(0x0164)
+#define SMC_RMI_FEATURES SMC_RMI_CALL(0x0165)
+#define SMC_RMI_RTT_FOLD SMC_RMI_CALL(0x0166)
+
+#define SMC_RMI_RTT_INIT_RIPAS SMC_RMI_CALL(0x0168)
+#define SMC_RMI_RTT_SET_RIPAS SMC_RMI_CALL(0x0169)
+#define SMC_RMI_VSMMU_CREATE SMC_RMI_CALL(0x016a)
+#define SMC_RMI_VSMMU_DESTROY SMC_RMI_CALL(0x016b)
+
+#define SMC_RMI_RMM_CONFIG_SET SMC_RMI_CALL(0x016e)
+#define SMC_RMI_PSMMU_IRQ_NOTIFY SMC_RMI_CALL(0x016f)
+#define SMC_RMI_ATTEST_PLAT_TOKEN_REFRESH SMC_RMI_CALL(0x0170)
+
+#define SMC_RMI_PDEV_ABORT SMC_RMI_CALL(0x0174)
+#define SMC_RMI_PDEV_COMMUNICATE SMC_RMI_CALL(0x0175)
+#define SMC_RMI_PDEV_CREATE SMC_RMI_CALL(0x0176)
+#define SMC_RMI_PDEV_DESTROY SMC_RMI_CALL(0x0177)
+#define SMC_RMI_PDEV_GET_STATE SMC_RMI_CALL(0x0178)
+
+#define SMC_RMI_PDEV_STREAM_KEY_REFRESH SMC_RMI_CALL(0x017a)
+#define SMC_RMI_PDEV_SET_PUBKEY SMC_RMI_CALL(0x017b)
+#define SMC_RMI_PDEV_STOP SMC_RMI_CALL(0x017c)
+#define SMC_RMI_RTT_AUX_CREATE SMC_RMI_CALL(0x017d)
+#define SMC_RMI_RTT_AUX_DESTROY SMC_RMI_CALL(0x017e)
+#define SMC_RMI_RTT_AUX_FOLD SMC_RMI_CALL(0x017f)
+
+#define SMC_RMI_VDEV_ABORT SMC_RMI_CALL(0x0185)
+#define SMC_RMI_VDEV_COMMUNICATE SMC_RMI_CALL(0x0186)
+#define SMC_RMI_VDEV_CREATE SMC_RMI_CALL(0x0187)
+#define SMC_RMI_VDEV_DESTROY SMC_RMI_CALL(0x0188)
+#define SMC_RMI_VDEV_GET_STATE SMC_RMI_CALL(0x0189)
+#define SMC_RMI_VDEV_UNLOCK SMC_RMI_CALL(0x018a)
+#define SMC_RMI_RTT_SET_S2AP SMC_RMI_CALL(0x018b)
+
+#define SMC_RMI_VDEV_GET_INTERFACE_REPORT SMC_RMI_CALL(0x01d0)
+#define SMC_RMI_VDEV_GET_MEASUREMENTS SMC_RMI_CALL(0x01d1)
+#define SMC_RMI_VDEV_LOCK SMC_RMI_CALL(0x01d2)
+#define SMC_RMI_VDEV_START SMC_RMI_CALL(0x01d3)
+
+#define SMC_RMI_VSMMU_EVENT_HANDLE SMC_RMI_CALL(0x01d6)
+#define SMC_RMI_PSMMU_ACTIVATE SMC_RMI_CALL(0x01d7)
+#define SMC_RMI_PSMMU_DEACTIVATE SMC_RMI_CALL(0x01d8)
+
+#define SMC_RMI_PSMMU_ST_L2_CREATE SMC_RMI_CALL(0x01db)
+#define SMC_RMI_PSMMU_ST_L2_DESTROY SMC_RMI_CALL(0x01dc)
+#define SMC_RMI_DPT_L0_CREATE SMC_RMI_CALL(0x01dd)
+#define SMC_RMI_DPT_L0_DESTROY SMC_RMI_CALL(0x01de)
+#define SMC_RMI_DPT_L1_CREATE SMC_RMI_CALL(0x01df)
+#define SMC_RMI_DPT_L1_DESTROY SMC_RMI_CALL(0x01e0)
+#define SMC_RMI_GRANULE_TRACKING_GET SMC_RMI_CALL(0x01e1)
+
+#define SMC_RMI_GRANULE_TRACKING_SET SMC_RMI_CALL(0x01e3)
+
+#define SMC_RMI_RMM_CONFIG_GET SMC_RMI_CALL(0x01ec)
+
+#define SMC_RMI_RMM_STATE_GET SMC_RMI_CALL(0x01ee)
+
+#define SMC_RMI_PSMMU_EVENT_CONSUME SMC_RMI_CALL(0x01f0)
+#define SMC_RMI_GRANULE_RANGE_DELEGATE SMC_RMI_CALL(0x01f1)
+#define SMC_RMI_GRANULE_RANGE_UNDELEGATE SMC_RMI_CALL(0x01f2)
+#define SMC_RMI_GPT_L1_CREATE SMC_RMI_CALL(0x01f3)
+#define SMC_RMI_GPT_L1_DESTROY SMC_RMI_CALL(0x01f4)
+#define SMC_RMI_RTT_DATA_MAP SMC_RMI_CALL(0x01f5)
+#define SMC_RMI_RTT_DATA_UNMAP SMC_RMI_CALL(0x01f6)
+#define SMC_RMI_RTT_DEV_MAP SMC_RMI_CALL(0x01f7)
+#define SMC_RMI_RTT_DEV_UNMAP SMC_RMI_CALL(0x01f8)
+#define SMC_RMI_RTT_ARCH_DEV_MAP SMC_RMI_CALL(0x01f9)
+#define SMC_RMI_RTT_ARCH_DEV_UNMAP SMC_RMI_CALL(0x01fa)
+#define SMC_RMI_RTT_UNPROT_MAP SMC_RMI_CALL(0x01fb)
+#define SMC_RMI_RTT_UNPROT_UNMAP SMC_RMI_CALL(0x01fc)
+#define SMC_RMI_RTT_AUX_PROT_MAP SMC_RMI_CALL(0x01fd)
+#define SMC_RMI_RTT_AUX_PROT_UNMAP SMC_RMI_CALL(0x01fe)
+#define SMC_RMI_RTT_AUX_UNPROT_MAP SMC_RMI_CALL(0x01ff)
+#define SMC_RMI_RTT_AUX_UNPROT_UNMAP SMC_RMI_CALL(0x0200)
+#define SMC_RMI_REALM_TERMINATE SMC_RMI_CALL(0x0201)
+#define SMC_RMI_RMM_ACTIVATE SMC_RMI_CALL(0x0202)
+#define SMC_RMI_OP_CONTINUE SMC_RMI_CALL(0x0203)
+#define SMC_RMI_PDEV_STREAM_CONNECT SMC_RMI_CALL(0x0204)
+#define SMC_RMI_PDEV_STREAM_DISCONNECT SMC_RMI_CALL(0x0205)
+#define SMC_RMI_PDEV_STREAM_COMPLETE SMC_RMI_CALL(0x0206)
+#define SMC_RMI_PDEV_STREAM_KEY_PURGE SMC_RMI_CALL(0x0207)
+#define SMC_RMI_OP_MEM_DONATE SMC_RMI_CALL(0x0208)
+#define SMC_RMI_OP_MEM_RECLAIM SMC_RMI_CALL(0x0209)
+#define SMC_RMI_OP_CANCEL SMC_RMI_CALL(0x020a)
+#define SMC_RMI_VSMMU_FEATURES SMC_RMI_CALL(0x020b)
+#define SMC_RMI_VSMMU_CMD_GET SMC_RMI_CALL(0x020c)
+#define SMC_RMI_VSMMU_CMD_COMPLETE SMC_RMI_CALL(0x020d)
+#define SMC_RMI_PSMMU_INFO SMC_RMI_CALL(0x020e)
+#define SMC_RMI_RMM_DEACTIVATE SMC_RMI_CALL(0x020f)
+#define SMC_RMI_PDEV_STREAM_INFO SMC_RMI_CALL(0x0210)
+#define SMC_RMI_GPT_INFO SMC_RMI_CALL(0x0211)
+
+#define RMI_ABI_MAJOR_VERSION 2
+#define RMI_ABI_MINOR_VERSION 0
+
+#define RMI_ABI_VERSION_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 GENMASK(18, 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_OP_MEM_CONDITIONAL 2
+
+#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 GENMASK(63, 62)
+
+#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_0_L0GPT_BLOCK_DELEGATE BIT(32)
+#define RMI_FEATURE_REGISTER_0_S2OASZ GENMASK(40, 33)
+
+#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_PDEV_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_REALM_MAX_VDEVS_ORDER GENMASK(14, 10)
+
+#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_MEM_CATEGORY_NONE 3
+
+#define RMI_TRACKING_RESERVED 0
+#define RMI_TRACKING_NONE 1
+#define RMI_TRACKING_FINE 2
+#define RMI_TRACKING_COARSE 3
+#define RMI_TRACKING_INTERMEDIATE 4
+
+#define RMI_GRANULE_SIZE_4KB 0
+#define RMI_GRANULE_SIZE_16KB 1
+#define RMI_GRANULE_SIZE_64KB 2
+
+#define RMI_GPT_PAR_RESERVED 0U
+#define RMI_GPT_PAR_PLAT 1U
+#define RMI_GPT_PAR_HOST_NOT_CREATED 2U
+#define RMI_GPT_PAR_HOST_CREATED 3U
+
+/*
+ * Note many of these fields are smaller than u64 but all fields have u64
+ * alignment, so use u64 to ensure correct alignment.
+ */
+struct rmm_config {
+ union { /* 0x0 */
+ struct {
+ u64 tracking_region_size;
+ u64 rmi_granule_size;
+ };
+ u8 sizer[SZ_4K];
+ };
+};
+
+static_assert(sizeof(struct rmm_config) == SZ_4K);
+
+#define RMI_REALM_PARAM_FLAG_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 padding2;
+ u64 rtt_base;
+ s64 rtt_level_start;
+ u64 rtt_num_start;
+ u64 flags1;
+ u64 max_num_vdevs;
+ };
+ u8 padding3[0x700];
+ };
+ union { /* 0xf00 */
+ struct {
+ u8 padding4[0x80];
+ u64 aux_rtt_base[3];
+ };
+ u8 padding5[0x100];
+ };
+};
+
+static_assert(sizeof(struct realm_params) == SZ_4K);
+
+/*
+ * The number of GPRs (starting from X0) that are
+ * configured by the host when a REC is created.
+ */
+#define REC_CREATE_NR_GPRS 8
+
+#define REC_PARAMS_FLAG_RUNNABLE BIT(0)
+
+struct rec_params {
+ union { /* 0x0 */
+ u64 flags;
+ u8 padding0[0x100];
+ };
+ union { /* 0x100 */
+ u64 mpidr;
+ u8 padding1[0x100];
+ };
+ union { /* 0x200 */
+ u64 pc;
+ u8 padding2[0x100];
+ };
+ union { /* 0x300 */
+ u64 gprs[REC_CREATE_NR_GPRS];
+ u8 padding3[0xd00];
+ };
+};
+
+static_assert(sizeof(struct rec_params) == SZ_4K);
+
+#define REC_ENTER_FLAG_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
+
+struct rec_enter {
+ union { /* 0x000 */
+ u64 flags;
+ u8 padding0[0x200];
+ };
+ union { /* 0x200 */
+ u64 gprs[REC_RUN_GPRS];
+ u8 padding1[0x600];
+ };
+};
+
+static_assert(sizeof(struct rec_enter) == SZ_2K);
+
+#define RMI_EXIT_SYNC 0x00
+#define RMI_EXIT_IRQ 0x01
+#define RMI_EXIT_FIQ 0x02
+#define RMI_EXIT_PSCI 0x03
+#define RMI_EXIT_RIPAS_CHANGE 0x04
+#define RMI_EXIT_HOST_CALL 0x05
+#define RMI_EXIT_SERROR 0x06
+#define RMI_EXIT_S2AP_CHANGE 0x07
+#define RMI_EXIT_VDEV_VALIDATE_MAPPING 0x08
+#define RMI_EXIT_VSMMU_COMMAND 0x0a
+
+struct rec_exit {
+ union { /* 0x000 */
+ u8 exit_reason;
+ u8 padding0[0x100];
+ };
+ union { /* 0x100 */
+ struct {
+ u64 esr;
+ u64 far;
+ u64 hpfar;
+ u64 rtt_tree;
+ };
+ u8 padding1[0x100];
+ };
+ union { /* 0x200 */
+ u64 gprs[REC_RUN_GPRS];
+ u8 padding2[0x100];
+ };
+ union { /* 0x300 */
+ u8 padding3[0x100];
+ };
+ union { /* 0x400 */
+ struct {
+ u64 cntp_ctl;
+ u64 cntp_cval;
+ u64 cntv_ctl;
+ u64 cntv_cval;
+ };
+ u8 padding4[0x100];
+ };
+ union { /* 0x500 */
+ struct {
+ u64 ripas_base;
+ u64 ripas_top;
+ u8 ripas_value;
+ u8 padding5[0xf];
+ u64 s2ap_base;
+ u64 s2ap_top;
+ u64 vdev_id_1;
+ u64 vdev_id_2;
+ u64 dev_mem_base;
+ u64 dev_mem_top;
+ u64 dev_mem_pa;
+ };
+ u8 padding6[0x100];
+ };
+ union { /* 0x600 */
+ struct {
+ u16 imm;
+ u8 padding7[0x6];
+ u64 plane;
+ };
+ u8 padding8[0x100];
+ };
+ union { /* 0x700 */
+ struct {
+ u8 pmu_ovf_status;
+ u8 padding9[0xf];
+ u64 vsmmu;
+ };
+ u8 padding10[0x100];
+ };
+};
+
+static_assert(sizeof(struct rec_exit) == SZ_2K);
+
+struct rec_run {
+ struct rec_enter enter;
+ struct rec_exit exit;
+};
+
+static_assert(sizeof(struct rec_run) == SZ_4K);
+
+/* RMI_RTT_UNPROT_MAP_FLAGS definitions */
+#define RMI_RTT_UNPROT_MAP_FLAGS_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_PROT_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_ */
--
2.43.0
^ permalink raw reply related [flat|nested] 44+ messages in thread* Re: [PATCH v17 1/7] firmware: arm_rmm: Add SMC definitions for calling the RMM
2026-09-07 9:59 ` [PATCH v17 1/7] firmware: arm_rmm: Add SMC definitions for calling the RMM Suzuki K Poulose
@ 2026-09-08 6:19 ` Gavin Shan
2026-09-08 10:37 ` Suzuki K Poulose
0 siblings, 1 reply; 44+ messages in thread
From: Gavin Shan @ 2026-09-08 6:19 UTC (permalink / raw)
To: Suzuki K Poulose, kvm, kvmarm
Cc: maz, will, catalin.marinas, linux-kernel, linux-arm-kernel,
steven.price, aneesh.kumar, oupton, joey.gouly, tabba, yuzenghui,
linux-coco, gankulkarni, sdonthineni, alpergun, fj0570is,
WeiLin.Chang, lpieralisi, enju.kohei
Hi Suzuki,
On 9/7/26 7:59 PM, Suzuki K Poulose wrote:
> From: Steven Price <steven.price@arm.com>
>
> The RMM (Realm Management Monitor) provides functionality that can be
> accessed by SMC calls from the host.
>
> The SMC definitions are based on DEN0137[1] version 2.0-bet3
>
> [1] https://developer.arm.com/documentation/den0137/2-0bet3/
>
> Signed-off-by: Steven Price <steven.price@arm.com>
> Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
> ---
> Changes since v16:
> * Updated definitions to RMM specification v2.0-bet3.
> Changes since v15:
> * Dropped unused symbols REC_MAX_GIC_NUM_LRS and RMI_PERMITTED_GICV3_HCR_BITS.
> * Output is now (partially) generated from the spec source.
> Changes since v14:
> * Updated to RMM spec v2.0-bet2 but without the changes to move
> metadata out of individual address range descriptors as this is
> expected to be reverted in a future spec release.
> Changes since v13:
> * Updated to RMM spec v2.0-bet1
> Changes since v12:
> * Updated to RMM spec v2.0-bet0
> Changes since v9:
> * Corrected size of 'ripas_value' in struct rec_exit. The spec states
> this is an 8-bit type with padding afterwards (rather than a u64).
> Changes since v8:
> * Added RMI_PERMITTED_GICV3_HCR_BITS to define which bits the RMM
> permits to be modified.
> Changes since v6:
> * Renamed REC_ENTER_xxx defines to include 'FLAG' to make it obvious
> these are flag values.
> Changes since v5:
> * Sorted the SMC #defines by value.
> * Renamed SMI_RxI_CALL to SMI_RMI_CALL since the macro is only used for
> RMI calls.
> * Renamed REC_GIC_NUM_LRS to REC_MAX_GIC_NUM_LRS since the actual
> number of available list registers could be lower.
> * Provided a define for the reserved fields of FeatureRegister0.
> * Fix inconsistent names for padding fields.
> Changes since v4:
> * Update to point to final released RMM spec.
> * Minor rearrangements.
> Changes since v3:
> * Update to match RMM spec v1.0-rel0-rc1.
> Changes since v2:
> * Fix specification link.
> * Rename rec_entry->rec_enter to match spec.
> * Fix size of pmu_ovf_status to match spec.
> ---
> include/linux/arm-smccc-rmi.h | 494 ++++++++++++++++++++++++++++++++++
> 1 file changed, 494 insertions(+)
> create mode 100644 include/linux/arm-smccc-rmi.h
>
Some nitpicks below, please pick up the commits that look reasonable :)
> diff --git a/include/linux/arm-smccc-rmi.h b/include/linux/arm-smccc-rmi.h
> new file mode 100644
> index 0000000000000..3eb88caf40964
> --- /dev/null
> +++ b/include/linux/arm-smccc-rmi.h
> @@ -0,0 +1,494 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2023-2026 ARM Ltd.
> + *
> + * The values and structures in this file are from the Realm Management Monitor
> + * specification (DEN0137) version 2.0-bet3:
> + * https://developer.arm.com/documentation/den0137/2-0bet3/
> + */
> +
> +#ifndef __LINUX_ARM_SMCCC_RMI_H_
> +#define __LINUX_ARM_SMCCC_RMI_H_
> +
> +#include <linux/arm-smccc.h>
> +#include <linux/bitfield.h>
> +#include <linux/bits.h>
> +#include <linux/build_bug.h>
> +#include <linux/sizes.h>
> +
> +#include <asm/page.h>
> +
> +#define SMC_RMI_CALL(func) \
> + ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \
> + ARM_SMCCC_SMC_64, \
> + ARM_SMCCC_OWNER_STANDARD, \
> + (func))
> +
> +#define SMC_RMI_VERSION SMC_RMI_CALL(0x0150)
> +
> +#define SMC_RMI_RTT_DATA_MAP_INIT SMC_RMI_CALL(0x0153)
> +
> +#define SMC_RMI_REALM_ACTIVATE SMC_RMI_CALL(0x0157)
> +#define SMC_RMI_REALM_CREATE SMC_RMI_CALL(0x0158)
> +#define SMC_RMI_REALM_DESTROY SMC_RMI_CALL(0x0159)
> +#define SMC_RMI_REC_CREATE SMC_RMI_CALL(0x015a)
> +#define SMC_RMI_REC_DESTROY SMC_RMI_CALL(0x015b)
> +#define SMC_RMI_REC_ENTER SMC_RMI_CALL(0x015c)
> +#define SMC_RMI_RTT_CREATE SMC_RMI_CALL(0x015d)
> +#define SMC_RMI_RTT_DESTROY SMC_RMI_CALL(0x015e)
> +
> +#define SMC_RMI_RTT_READ_ENTRY SMC_RMI_CALL(0x0161)
> +
> +#define SMC_RMI_RTT_DEV_VALIDATE SMC_RMI_CALL(0x0163)
> +#define SMC_RMI_PSCI_COMPLETE SMC_RMI_CALL(0x0164)
> +#define SMC_RMI_FEATURES SMC_RMI_CALL(0x0165)
> +#define SMC_RMI_RTT_FOLD SMC_RMI_CALL(0x0166)
> +
> +#define SMC_RMI_RTT_INIT_RIPAS SMC_RMI_CALL(0x0168)
> +#define SMC_RMI_RTT_SET_RIPAS SMC_RMI_CALL(0x0169)
> +#define SMC_RMI_VSMMU_CREATE SMC_RMI_CALL(0x016a)
> +#define SMC_RMI_VSMMU_DESTROY SMC_RMI_CALL(0x016b)
> +
> +#define SMC_RMI_RMM_CONFIG_SET SMC_RMI_CALL(0x016e)
> +#define SMC_RMI_PSMMU_IRQ_NOTIFY SMC_RMI_CALL(0x016f)
> +#define SMC_RMI_ATTEST_PLAT_TOKEN_REFRESH SMC_RMI_CALL(0x0170)
> +
> +#define SMC_RMI_PDEV_ABORT SMC_RMI_CALL(0x0174)
> +#define SMC_RMI_PDEV_COMMUNICATE SMC_RMI_CALL(0x0175)
> +#define SMC_RMI_PDEV_CREATE SMC_RMI_CALL(0x0176)
> +#define SMC_RMI_PDEV_DESTROY SMC_RMI_CALL(0x0177)
> +#define SMC_RMI_PDEV_GET_STATE SMC_RMI_CALL(0x0178)
> +
> +#define SMC_RMI_PDEV_STREAM_KEY_REFRESH SMC_RMI_CALL(0x017a)
> +#define SMC_RMI_PDEV_SET_PUBKEY SMC_RMI_CALL(0x017b)
> +#define SMC_RMI_PDEV_STOP SMC_RMI_CALL(0x017c)
> +#define SMC_RMI_RTT_AUX_CREATE SMC_RMI_CALL(0x017d)
> +#define SMC_RMI_RTT_AUX_DESTROY SMC_RMI_CALL(0x017e)
> +#define SMC_RMI_RTT_AUX_FOLD SMC_RMI_CALL(0x017f)
> +
> +#define SMC_RMI_VDEV_ABORT SMC_RMI_CALL(0x0185)
> +#define SMC_RMI_VDEV_COMMUNICATE SMC_RMI_CALL(0x0186)
> +#define SMC_RMI_VDEV_CREATE SMC_RMI_CALL(0x0187)
> +#define SMC_RMI_VDEV_DESTROY SMC_RMI_CALL(0x0188)
> +#define SMC_RMI_VDEV_GET_STATE SMC_RMI_CALL(0x0189)
> +#define SMC_RMI_VDEV_UNLOCK SMC_RMI_CALL(0x018a)
> +#define SMC_RMI_RTT_SET_S2AP SMC_RMI_CALL(0x018b)
> +
> +#define SMC_RMI_VDEV_GET_INTERFACE_REPORT SMC_RMI_CALL(0x01d0)
> +#define SMC_RMI_VDEV_GET_MEASUREMENTS SMC_RMI_CALL(0x01d1)
> +#define SMC_RMI_VDEV_LOCK SMC_RMI_CALL(0x01d2)
> +#define SMC_RMI_VDEV_START SMC_RMI_CALL(0x01d3)
> +
> +#define SMC_RMI_VSMMU_EVENT_HANDLE SMC_RMI_CALL(0x01d6)
> +#define SMC_RMI_PSMMU_ACTIVATE SMC_RMI_CALL(0x01d7)
> +#define SMC_RMI_PSMMU_DEACTIVATE SMC_RMI_CALL(0x01d8)
> +
> +#define SMC_RMI_PSMMU_ST_L2_CREATE SMC_RMI_CALL(0x01db)
> +#define SMC_RMI_PSMMU_ST_L2_DESTROY SMC_RMI_CALL(0x01dc)
> +#define SMC_RMI_DPT_L0_CREATE SMC_RMI_CALL(0x01dd)
> +#define SMC_RMI_DPT_L0_DESTROY SMC_RMI_CALL(0x01de)
> +#define SMC_RMI_DPT_L1_CREATE SMC_RMI_CALL(0x01df)
> +#define SMC_RMI_DPT_L1_DESTROY SMC_RMI_CALL(0x01e0)
> +#define SMC_RMI_GRANULE_TRACKING_GET SMC_RMI_CALL(0x01e1)
> +
> +#define SMC_RMI_GRANULE_TRACKING_SET SMC_RMI_CALL(0x01e3)
> +
> +#define SMC_RMI_RMM_CONFIG_GET SMC_RMI_CALL(0x01ec)
> +
> +#define SMC_RMI_RMM_STATE_GET SMC_RMI_CALL(0x01ee)
> +
> +#define SMC_RMI_PSMMU_EVENT_CONSUME SMC_RMI_CALL(0x01f0)
> +#define SMC_RMI_GRANULE_RANGE_DELEGATE SMC_RMI_CALL(0x01f1)
> +#define SMC_RMI_GRANULE_RANGE_UNDELEGATE SMC_RMI_CALL(0x01f2)
> +#define SMC_RMI_GPT_L1_CREATE SMC_RMI_CALL(0x01f3)
> +#define SMC_RMI_GPT_L1_DESTROY SMC_RMI_CALL(0x01f4)
> +#define SMC_RMI_RTT_DATA_MAP SMC_RMI_CALL(0x01f5)
> +#define SMC_RMI_RTT_DATA_UNMAP SMC_RMI_CALL(0x01f6)
> +#define SMC_RMI_RTT_DEV_MAP SMC_RMI_CALL(0x01f7)
> +#define SMC_RMI_RTT_DEV_UNMAP SMC_RMI_CALL(0x01f8)
> +#define SMC_RMI_RTT_ARCH_DEV_MAP SMC_RMI_CALL(0x01f9)
> +#define SMC_RMI_RTT_ARCH_DEV_UNMAP SMC_RMI_CALL(0x01fa)
> +#define SMC_RMI_RTT_UNPROT_MAP SMC_RMI_CALL(0x01fb)
> +#define SMC_RMI_RTT_UNPROT_UNMAP SMC_RMI_CALL(0x01fc)
> +#define SMC_RMI_RTT_AUX_PROT_MAP SMC_RMI_CALL(0x01fd)
> +#define SMC_RMI_RTT_AUX_PROT_UNMAP SMC_RMI_CALL(0x01fe)
> +#define SMC_RMI_RTT_AUX_UNPROT_MAP SMC_RMI_CALL(0x01ff)
> +#define SMC_RMI_RTT_AUX_UNPROT_UNMAP SMC_RMI_CALL(0x0200)
> +#define SMC_RMI_REALM_TERMINATE SMC_RMI_CALL(0x0201)
> +#define SMC_RMI_RMM_ACTIVATE SMC_RMI_CALL(0x0202)
> +#define SMC_RMI_OP_CONTINUE SMC_RMI_CALL(0x0203)
> +#define SMC_RMI_PDEV_STREAM_CONNECT SMC_RMI_CALL(0x0204)
> +#define SMC_RMI_PDEV_STREAM_DISCONNECT SMC_RMI_CALL(0x0205)
> +#define SMC_RMI_PDEV_STREAM_COMPLETE SMC_RMI_CALL(0x0206)
> +#define SMC_RMI_PDEV_STREAM_KEY_PURGE SMC_RMI_CALL(0x0207)
> +#define SMC_RMI_OP_MEM_DONATE SMC_RMI_CALL(0x0208)
> +#define SMC_RMI_OP_MEM_RECLAIM SMC_RMI_CALL(0x0209)
> +#define SMC_RMI_OP_CANCEL SMC_RMI_CALL(0x020a)
> +#define SMC_RMI_VSMMU_FEATURES SMC_RMI_CALL(0x020b)
> +#define SMC_RMI_VSMMU_CMD_GET SMC_RMI_CALL(0x020c)
> +#define SMC_RMI_VSMMU_CMD_COMPLETE SMC_RMI_CALL(0x020d)
> +#define SMC_RMI_PSMMU_INFO SMC_RMI_CALL(0x020e)
> +#define SMC_RMI_RMM_DEACTIVATE SMC_RMI_CALL(0x020f)
> +#define SMC_RMI_PDEV_STREAM_INFO SMC_RMI_CALL(0x0210)
> +#define SMC_RMI_GPT_INFO SMC_RMI_CALL(0x0211)
> +
> +#define RMI_ABI_MAJOR_VERSION 2
> +#define RMI_ABI_MINOR_VERSION 0
> +
> +#define RMI_ABI_VERSION_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)
> +
If I'm correct enough, RMI_RETURN_{STATUS, INDEX, MEMREQ, CAN_CANEL}_MASK are used
for once in this header file. So we needn't explicitly expose them and combine their
definitions with users to fetch the corresponding fields, as below.
#define RMI_RETURN_STATUS(ret) FIELD_GET(GENMASK(7, 0), ret)
#define RMI_RETURN_INDEX(ret) FIELD_GET(GENMASK(15, 8), ret)
#define RMI_RETURN_MEMREQ(ret) FIELD_GET(GENMASK(10, 8), ret)
#define RMI_RETURN_CAN_CANCEL(ret) FIELD_GEt(GENMASK(10, 10), 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 GENMASK(18, 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)
> +
As above, RMI_DONATE_{SIZE, COUNT, CONTIG, STATE}_MASK are used for once in this
header file. So their definitions can be dropped by modifying the followup macros,
as below. Some enhancements are also applicable: (a) Use 'BLOCK' to indicate the
unit encoded in the request. (b) The macros are put into order from MSB to LSB.
#define RMI_DONATE_STATE(req) FIELD_GET(GENMASK(18, 17), req)
#define RMI_DONATE_CONTIG(req) FIELD_GET(GENMASK(16, 16), req)
#define RMI_DONATE_BLOCK_COUNT(req) FIELD_GET(GENMASK(15, 2), req)
#define RMI_DONATE_BLOCK_SIZE(req) FIELD_GET(GENMASK(1, 0), req)
> +#define RMI_OP_MEM_DELEGATED 0
> +#define RMI_OP_MEM_UNDELEGATED 1
> +#define RMI_OP_MEM_CONDITIONAL 2
> +
B4.6.40 RmiOpMemContig type is missed here and they should be used in the c code.
#define RMI_OP_MEM_NON_CONTIG 0
#define RMI_OP_MEM_CONTIG 1
In the c code, we shall have:
if (RMI_DONATE_CONTIG(req) == RMI_OP_MEM_CONTIG)) {
...
} else {
...
}
Similarly, B4.6.39 RmiOpCanCancel type is missed here and they should be used in
the c code.
#define RMI_OP_CANNOT_CANCEL 0
#define RMI_OP_CAN_CANCEL 1
In the c code, we shall have:
if (RMI_RETURN_CAN_CANCEL(ret) == RMI_OP_CAN_CANCEL) {
...
} else {
...
}
> +#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 GENMASK(63, 62)
> +
> +#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)))
> +
I guess it wouldn't a bad idea to explictly indicate 'BLOCK' in the definitions, as
below. Some improvements are applicated either: (a) 'ar' is renamed to 'r'; (b) The
excessive parentheses are dropped to follow the patterns we already had; (c) For
RMI_ADDR_RANGE_ADDR(r), FIELD_GET() is used.
#define RMI_ADDR_RANGE_BLOCK_SIZE_MASK GENMASK(1, 0)
#define RMI_ADDR_RANGE_BLOCK_COUNT_MASK GENMASK(PAGE_SHIFT - 1, 2)
#define RMI_ADDR_RANGE_ADDR_MASK (PAGE_MASK & GENMASK(51, 0))
#define RMI_ADDR_RANGE_STATE_MASK GENMASK(63, 62)
#define RMI_ADDR_RANGE_BLOCK_SIZE(r) FIELD_GET(RMI_ADDR_RANGE_SIZE_MASK, r)
#define RMI_ADDR_RANGE_BLOCK_COUNT(r) FIELD_GET(RMI_ADDR_RANGE_COUNT_MASK, r)
#define RMI_ADDR_RANGE_ADDR(r) FIELD_GET(RMI_ADDR_RANGE_ADDR_MASK, r)
#define RMI_ADDR_RANGE_STATE(r) FIELD_GET(RMI_ADDR_RANGE_STATE_MASK, r)
> +enum rmi_ripas {
> + RMI_EMPTY = 0,
> + RMI_RAM = 1,
> + RMI_DESTROYED = 2,
> + RMI_DEV = 3,
> +};
> +
> +#define RMI_NO_MEASURE_CONTENT 0
> +#define RMI_MEASURE_CONTENT 1
> +
> +#define RMI_FEATURE_REGISTER_0_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_0_L0GPT_BLOCK_DELEGATE BIT(32)
> +#define RMI_FEATURE_REGISTER_0_S2OASZ GENMASK(40, 33)
> +
> +#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_PDEV_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_REALM_MAX_VDEVS_ORDER GENMASK(14, 10)
> +
> +#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)
> +
We would reorder those feature register definitions from MSB ro LSB :-)
> +#define RMI_MEM_CATEGORY_CONVENTIONAL 0
> +#define RMI_MEM_CATEGORY_DEV_NCOH 1
> +#define RMI_MEM_CATEGORY_DEV_COH 2
> +#define RMI_MEM_CATEGORY_NONE 3
> +
> +#define RMI_TRACKING_RESERVED 0
> +#define RMI_TRACKING_NONE 1
> +#define RMI_TRACKING_FINE 2
> +#define RMI_TRACKING_COARSE 3
> +#define RMI_TRACKING_INTERMEDIATE 4
> +
> +#define RMI_GRANULE_SIZE_4KB 0
> +#define RMI_GRANULE_SIZE_16KB 1
> +#define RMI_GRANULE_SIZE_64KB 2
> +
> +#define RMI_GPT_PAR_RESERVED 0U
> +#define RMI_GPT_PAR_PLAT 1U
> +#define RMI_GPT_PAR_HOST_NOT_CREATED 2U
> +#define RMI_GPT_PAR_HOST_CREATED 3U
> +
> +/*
> + * Note many of these fields are smaller than u64 but all fields have u64
> + * alignment, so use u64 to ensure correct alignment.
> + */
> +struct rmm_config {
> + union { /* 0x0 */
> + struct {
> + u64 tracking_region_size;
> + u64 rmi_granule_size;
> + };
> + u8 sizer[SZ_4K];
> + };
> +};
> +
> +static_assert(sizeof(struct rmm_config) == SZ_4K);
> +
> +#define RMI_REALM_PARAM_FLAG_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 padding2;
> + u64 rtt_base;
> + s64 rtt_level_start;
> + u64 rtt_num_start;
> + u64 flags1;
> + u64 max_num_vdevs;
> + };
> + u8 padding3[0x700];
> + };
> + union { /* 0xf00 */
> + struct {
> + u8 padding4[0x80];
> + u64 aux_rtt_base[3];
> + };
> + u8 padding5[0x100];
> + };
> +};
> +
> +static_assert(sizeof(struct realm_params) == SZ_4K);
> +
> +/*
> + * The number of GPRs (starting from X0) that are
> + * configured by the host when a REC is created.
> + */
> +#define REC_CREATE_NR_GPRS 8
> +
> +#define REC_PARAMS_FLAG_RUNNABLE BIT(0)
> +
> +struct rec_params {
> + union { /* 0x0 */
> + u64 flags;
> + u8 padding0[0x100];
> + };
> + union { /* 0x100 */
> + u64 mpidr;
> + u8 padding1[0x100];
> + };
> + union { /* 0x200 */
> + u64 pc;
> + u8 padding2[0x100];
> + };
> + union { /* 0x300 */
> + u64 gprs[REC_CREATE_NR_GPRS];
> + u8 padding3[0xd00];
> + };
> +};
> +
> +static_assert(sizeof(struct rec_params) == SZ_4K);
> +
> +#define REC_ENTER_FLAG_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
> +
> +struct rec_enter {
> + union { /* 0x000 */
> + u64 flags;
> + u8 padding0[0x200];
> + };
> + union { /* 0x200 */
> + u64 gprs[REC_RUN_GPRS];
> + u8 padding1[0x600];
> + };
> +};
> +
> +static_assert(sizeof(struct rec_enter) == SZ_2K);
> +
> +#define RMI_EXIT_SYNC 0x00
> +#define RMI_EXIT_IRQ 0x01
> +#define RMI_EXIT_FIQ 0x02
> +#define RMI_EXIT_PSCI 0x03
> +#define RMI_EXIT_RIPAS_CHANGE 0x04
> +#define RMI_EXIT_HOST_CALL 0x05
> +#define RMI_EXIT_SERROR 0x06
> +#define RMI_EXIT_S2AP_CHANGE 0x07
> +#define RMI_EXIT_VDEV_VALIDATE_MAPPING 0x08
> +#define RMI_EXIT_VSMMU_COMMAND 0x0a
> +
> +struct rec_exit {
> + union { /* 0x000 */
> + u8 exit_reason;
> + u8 padding0[0x100];
> + };
> + union { /* 0x100 */
> + struct {
> + u64 esr;
> + u64 far;
> + u64 hpfar;
> + u64 rtt_tree;
> + };
> + u8 padding1[0x100];
> + };
> + union { /* 0x200 */
> + u64 gprs[REC_RUN_GPRS];
> + u8 padding2[0x100];
> + };
> + union { /* 0x300 */
> + u8 padding3[0x100];
> + };
> + union { /* 0x400 */
> + struct {
> + u64 cntp_ctl;
> + u64 cntp_cval;
> + u64 cntv_ctl;
> + u64 cntv_cval;
> + };
> + u8 padding4[0x100];
> + };
> + union { /* 0x500 */
> + struct {
> + u64 ripas_base;
> + u64 ripas_top;
> + u8 ripas_value;
> + u8 padding5[0xf];
> + u64 s2ap_base;
> + u64 s2ap_top;
> + u64 vdev_id_1;
> + u64 vdev_id_2;
> + u64 dev_mem_base;
> + u64 dev_mem_top;
> + u64 dev_mem_pa;
> + };
> + u8 padding6[0x100];
> + };
> + union { /* 0x600 */
> + struct {
> + u16 imm;
> + u8 padding7[0x6];
> + u64 plane;
> + };
> + u8 padding8[0x100];
> + };
> + union { /* 0x700 */
> + struct {
> + u8 pmu_ovf_status;
> + u8 padding9[0xf];
> + u64 vsmmu;
> + };
> + u8 padding10[0x100];
> + };
> +};
> +
> +static_assert(sizeof(struct rec_exit) == SZ_2K);
> +
> +struct rec_run {
> + struct rec_enter enter;
> + struct rec_exit exit;
> +};
> +
> +static_assert(sizeof(struct rec_run) == SZ_4K);
> +
> +/* RMI_RTT_UNPROT_MAP_FLAGS definitions */
> +#define RMI_RTT_UNPROT_MAP_FLAGS_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_PROT_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_ */
Thanks,
Gavin
^ permalink raw reply [flat|nested] 44+ messages in thread* Re: [PATCH v17 1/7] firmware: arm_rmm: Add SMC definitions for calling the RMM
2026-09-08 6:19 ` Gavin Shan
@ 2026-09-08 10:37 ` Suzuki K Poulose
2026-09-08 22:41 ` Gavin Shan
0 siblings, 1 reply; 44+ messages in thread
From: Suzuki K Poulose @ 2026-09-08 10:37 UTC (permalink / raw)
To: Gavin Shan, kvm, kvmarm
Cc: maz, will, catalin.marinas, linux-kernel, linux-arm-kernel,
steven.price, aneesh.kumar, oupton, joey.gouly, tabba, yuzenghui,
linux-coco, gankulkarni, sdonthineni, alpergun, fj0570is,
WeiLin.Chang, lpieralisi, enju.kohei
On 08/09/2026 07:19, Gavin Shan wrote:
> Hi Suzuki,
>
> On 9/7/26 7:59 PM, Suzuki K Poulose wrote:
>> From: Steven Price <steven.price@arm.com>
>>
>> The RMM (Realm Management Monitor) provides functionality that can be
>> accessed by SMC calls from the host.
>>
>> The SMC definitions are based on DEN0137[1] version 2.0-bet3
>>
>> [1] https://developer.arm.com/documentation/den0137/2-0bet3/
>>
>> Signed-off-by: Steven Price <steven.price@arm.com>
>> Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
...
>> ---
>> include/linux/arm-smccc-rmi.h | 494 ++++++++++++++++++++++++++++++++++
>> 1 file changed, 494 insertions(+)
>> create mode 100644 include/linux/arm-smccc-rmi.h
>>
>
> Some nitpicks below, please pick up the commits that look reasonable :)
>
>> +
>> +#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)
>> +
>
> If I'm correct enough, RMI_RETURN_{STATUS, INDEX, MEMREQ, CAN_CANEL}
> _MASK are used
> for once in this header file. So we needn't explicitly expose them and
> combine their
> definitions with users to fetch the corresponding fields, as below.
>
> #define RMI_RETURN_STATUS(ret) FIELD_GET(GENMASK(7, 0), ret)
> #define RMI_RETURN_INDEX(ret) FIELD_GET(GENMASK(15, 8), ret)
> #define RMI_RETURN_MEMREQ(ret) FIELD_GET(GENMASK(10, 8), ret)
> #define RMI_RETURN_CAN_CANCEL(ret) FIELD_GEt(GENMASK(10, 10), ret)
I belive the header files were generated from a tool (by Steven) and
these would make it easier to compare with the generated header file for
any deviation from the spec. So, I am a bit reluctant make these
changes.
That said, I am happy to convert the mask definitions to GENMASK() for
consistency and readability. i.e., something like:
diff --git a/include/linux/arm-smccc-rmi.h b/include/linux/arm-smccc-rmi.h
index 3eb88caf40964..078197284415e 100644
--- a/include/linux/arm-smccc-rmi.h
+++ b/include/linux/arm-smccc-rmi.h
@@ -139,10 +139,10 @@
#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_MASK GENMASK(7, 0)
+#define RMI_RETURN_INDEX_MASK GENMASK(15, 8)
+#define RMI_RETURN_MEMREQ_MASK GENMASK(10, 8)
+#define RMI_RETURN_CAN_CANCEL_MASK BIT(10)
>
>> +#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 GENMASK(18, 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)
>> +
>
> As above, RMI_DONATE_{SIZE, COUNT, CONTIG, STATE}_MASK are used for once
> in this
> header file. So their definitions can be dropped by modifying the
> followup macros,
> as below. Some enhancements are also applicable: (a) Use 'BLOCK' to
> indicate the
> unit encoded in the request. (b) The macros are put into order from MSB
> to LSB.
>
> #define RMI_DONATE_STATE(req) FIELD_GET(GENMASK(18, 17), req)
> #define RMI_DONATE_CONTIG(req) FIELD_GET(GENMASK(16, 16), req)
> #define RMI_DONATE_BLOCK_COUNT(req) FIELD_GET(GENMASK(15, 2), req)
> #define RMI_DONATE_BLOCK_SIZE(req) FIELD_GET(GENMASK(1, 0), req)
Same as above.
>
>> +#define RMI_OP_MEM_DELEGATED 0
>> +#define RMI_OP_MEM_UNDELEGATED 1
>> +#define RMI_OP_MEM_CONDITIONAL 2
>> +
>
> B4.6.40 RmiOpMemContig type is missed here and they should be used in
> the c code.
>
> #define RMI_OP_MEM_NON_CONTIG 0
> #define RMI_OP_MEM_CONTIG 1
>
> In the c code, we shall have:
>
> if (RMI_DONATE_CONTIG(req) == RMI_OP_MEM_CONTIG)) {
> ...
> } else {
> ...
> }
>
Does it make much sense though ? I prefer
if (RMI_DONATE_CONTIG(req))
to
if (RMI_DONATE_CONTIG(req) == RMI_OP_MEM_CONTIG)) {
than having a field for a single bit values. I am happy to change
this if there is strong preference to the other.
> Similarly, B4.6.39 RmiOpCanCancel type is missed here and they should be
> used in
> the c code.
>
> #define RMI_OP_CANNOT_CANCEL 0
> #define RMI_OP_CAN_CANCEL 1
>
> In the c code, we shall have:
>
> if (RMI_RETURN_CAN_CANCEL(ret) == RMI_OP_CAN_CANCEL) {
> ...
> } else {
> ...
> }
>
Same as above.
>> +#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 GENMASK(63, 62)
>> +
>> +#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)))
>> +
>
> I guess it wouldn't a bad idea to explictly indicate 'BLOCK' in the
> definitions, as
> below. Some improvements are applicated either: (a) 'ar' is renamed to
> 'r'; (b) The
> excessive parentheses are dropped to follow the patterns we already had;
> (c) For
> RMI_ADDR_RANGE_ADDR(r), FIELD_GET() is used.
>
> #define RMI_ADDR_RANGE_BLOCK_SIZE_MASK GENMASK(1, 0)
> #define RMI_ADDR_RANGE_BLOCK_COUNT_MASK GENMASK(PAGE_SHIFT - 1, 2)
> #define RMI_ADDR_RANGE_ADDR_MASK (PAGE_MASK & GENMASK(51, 0))
> #define RMI_ADDR_RANGE_STATE_MASK GENMASK(63, 62)
>
> #define RMI_ADDR_RANGE_BLOCK_SIZE(r)
> FIELD_GET(RMI_ADDR_RANGE_SIZE_MASK, r)
> #define RMI_ADDR_RANGE_BLOCK_COUNT(r)
> FIELD_GET(RMI_ADDR_RANGE_COUNT_MASK, r)
> #define RMI_ADDR_RANGE_ADDR(r)
> FIELD_GET(RMI_ADDR_RANGE_ADDR_MASK, r)
> #define RMI_ADDR_RANGE_STATE(r)
> FIELD_GET(RMI_ADDR_RANGE_STATE_MASK, r)
Ack
>
>> +enum rmi_ripas {
>> + RMI_EMPTY = 0,
>> + RMI_RAM = 1,
>> + RMI_DESTROYED = 2,
>> + RMI_DEV = 3,
>> +};
>> +#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_PDEV_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_REALM_MAX_VDEVS_ORDER GENMASK(14, 10)
>> +
>> +#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)
>> +
>
> We would reorder those feature register definitions from MSB ro LSB :-)
Ack
Thanks for your review.
Cheers
Suzuki
^ permalink raw reply related [flat|nested] 44+ messages in thread* Re: [PATCH v17 1/7] firmware: arm_rmm: Add SMC definitions for calling the RMM
2026-09-08 10:37 ` Suzuki K Poulose
@ 2026-09-08 22:41 ` Gavin Shan
2026-09-09 8:39 ` Suzuki K Poulose
0 siblings, 1 reply; 44+ messages in thread
From: Gavin Shan @ 2026-09-08 22:41 UTC (permalink / raw)
To: Suzuki K Poulose, kvm, kvmarm
Cc: maz, will, catalin.marinas, linux-kernel, linux-arm-kernel,
steven.price, aneesh.kumar, oupton, joey.gouly, tabba, yuzenghui,
linux-coco, gankulkarni, sdonthineni, alpergun, fj0570is,
WeiLin.Chang, lpieralisi, enju.kohei
On 9/8/26 8:37 PM, Suzuki K Poulose wrote:
> On 08/09/2026 07:19, Gavin Shan wrote:
>> On 9/7/26 7:59 PM, Suzuki K Poulose wrote:
>>> From: Steven Price <steven.price@arm.com>
>>>
>>> The RMM (Realm Management Monitor) provides functionality that can be
>>> accessed by SMC calls from the host.
>>>
>>> The SMC definitions are based on DEN0137[1] version 2.0-bet3
>>>
>>> [1] https://developer.arm.com/documentation/den0137/2-0bet3/
>>>
>>> Signed-off-by: Steven Price <steven.price@arm.com>
>>> Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
>
> ...
>
>>> ---
>>> include/linux/arm-smccc-rmi.h | 494 ++++++++++++++++++++++++++++++++++
>>> 1 file changed, 494 insertions(+)
>>> create mode 100644 include/linux/arm-smccc-rmi.h
>>>
>>
>> Some nitpicks below, please pick up the commits that look reasonable :)
>>
>
>
>>> +
>>> +#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)
>>> +
>>
>> If I'm correct enough, RMI_RETURN_{STATUS, INDEX, MEMREQ, CAN_CANEL} _MASK are used
>> for once in this header file. So we needn't explicitly expose them and combine their
>> definitions with users to fetch the corresponding fields, as below.
>>
>> #define RMI_RETURN_STATUS(ret) FIELD_GET(GENMASK(7, 0), ret)
>> #define RMI_RETURN_INDEX(ret) FIELD_GET(GENMASK(15, 8), ret)
>> #define RMI_RETURN_MEMREQ(ret) FIELD_GET(GENMASK(10, 8), ret)
>> #define RMI_RETURN_CAN_CANCEL(ret) FIELD_GEt(GENMASK(10, 10), ret)
>
> I belive the header files were generated from a tool (by Steven) and
> these would make it easier to compare with the generated header file for
> any deviation from the spec. So, I am a bit reluctant make these
> changes.
>
> That said, I am happy to convert the mask definitions to GENMASK() for
> consistency and readability. i.e., something like:
>
> diff --git a/include/linux/arm-smccc-rmi.h b/include/linux/arm-smccc-rmi.h
> index 3eb88caf40964..078197284415e 100644
> --- a/include/linux/arm-smccc-rmi.h
> +++ b/include/linux/arm-smccc-rmi.h
> @@ -139,10 +139,10 @@
> #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_MASK GENMASK(7, 0)
> +#define RMI_RETURN_INDEX_MASK GENMASK(15, 8)
> +#define RMI_RETURN_MEMREQ_MASK GENMASK(10, 8)
> +#define RMI_RETURN_CAN_CANCEL_MASK BIT(10)
>
Ok.
>>
>>> +#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 GENMASK(18, 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)
>>> +
>>
>> As above, RMI_DONATE_{SIZE, COUNT, CONTIG, STATE}_MASK are used for once in this
>> header file. So their definitions can be dropped by modifying the followup macros,
>> as below. Some enhancements are also applicable: (a) Use 'BLOCK' to indicate the
>> unit encoded in the request. (b) The macros are put into order from MSB to LSB.
>>
>> #define RMI_DONATE_STATE(req) FIELD_GET(GENMASK(18, 17), req)
>> #define RMI_DONATE_CONTIG(req) FIELD_GET(GENMASK(16, 16), req)
>> #define RMI_DONATE_BLOCK_COUNT(req) FIELD_GET(GENMASK(15, 2), req)
>> #define RMI_DONATE_BLOCK_SIZE(req) FIELD_GET(GENMASK(1, 0), req)
>
> Same as above.
>
Please rename RMI_DONATE_{SIZE, COUNT} to RMI_DONATE_BLOCK_{SIZE, COUNT} if possible.
>>
>>> +#define RMI_OP_MEM_DELEGATED 0
>>> +#define RMI_OP_MEM_UNDELEGATED 1
>>> +#define RMI_OP_MEM_CONDITIONAL 2
>>> +
>>
>> B4.6.40 RmiOpMemContig type is missed here and they should be used in the c code.
>>
>> #define RMI_OP_MEM_NON_CONTIG 0
>> #define RMI_OP_MEM_CONTIG 1
>>
>> In the c code, we shall have:
>>
>> if (RMI_DONATE_CONTIG(req) == RMI_OP_MEM_CONTIG)) {
>> ...
>> } else {
>> ...
>> }
>>
>
> Does it make much sense though ? I prefer
>
> if (RMI_DONATE_CONTIG(req))
>
> to
>
>
> if (RMI_DONATE_CONTIG(req) == RMI_OP_MEM_CONTIG)) {
>
>
> than having a field for a single bit values. I am happy to change
> this if there is strong preference to the other.
>
The point is RMM-v2.0-bet3 expose RMI_OP_{MEM, MEM_NON}_CONTIG instead of true/false
to indicate if the requested memory blocks are physically contiguous or not. So the
expression 'if (RMI_DONATE_CONTIG(req) == RMI_OP_MEM_CONTIG)' is consistent to the
RMM-v2.0-bet3 spec.
>> Similarly, B4.6.39 RmiOpCanCancel type is missed here and they should be used in
>> the c code.
>>
>> #define RMI_OP_CANNOT_CANCEL 0
>> #define RMI_OP_CAN_CANCEL 1
>>
>> In the c code, we shall have:
>>
>> if (RMI_RETURN_CAN_CANCEL(ret) == RMI_OP_CAN_CANCEL) {
>> ...
>> } else {
>> ...
>> }
>>
>
> Same as above.
>
Please see above explanation.
>>> +#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 GENMASK(63, 62)
>>> +
>>> +#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)))
>>> +
>>
>> I guess it wouldn't a bad idea to explictly indicate 'BLOCK' in the definitions, as
>> below. Some improvements are applicated either: (a) 'ar' is renamed to 'r'; (b) The
>> excessive parentheses are dropped to follow the patterns we already had; (c) For
>> RMI_ADDR_RANGE_ADDR(r), FIELD_GET() is used.
>>
>> #define RMI_ADDR_RANGE_BLOCK_SIZE_MASK GENMASK(1, 0)
>> #define RMI_ADDR_RANGE_BLOCK_COUNT_MASK GENMASK(PAGE_SHIFT - 1, 2)
>> #define RMI_ADDR_RANGE_ADDR_MASK (PAGE_MASK & GENMASK(51, 0))
>> #define RMI_ADDR_RANGE_STATE_MASK GENMASK(63, 62)
>>
>> #define RMI_ADDR_RANGE_BLOCK_SIZE(r) FIELD_GET(RMI_ADDR_RANGE_SIZE_MASK, r)
>> #define RMI_ADDR_RANGE_BLOCK_COUNT(r) FIELD_GET(RMI_ADDR_RANGE_COUNT_MASK, r)
>> #define RMI_ADDR_RANGE_ADDR(r) FIELD_GET(RMI_ADDR_RANGE_ADDR_MASK, r)
>> #define RMI_ADDR_RANGE_STATE(r) FIELD_GET(RMI_ADDR_RANGE_STATE_MASK, r)
>
> Ack
>
>>
>>> +enum rmi_ripas {
>>> + RMI_EMPTY = 0,
>>> + RMI_RAM = 1,
>>> + RMI_DESTROYED = 2,
>>> + RMI_DEV = 3,
>>> +};
>
>>> +#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_PDEV_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_REALM_MAX_VDEVS_ORDER GENMASK(14, 10)
>>> +
>>> +#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)
>>> +
>>
>> We would reorder those feature register definitions from MSB ro LSB :-)
>
> Ack
>
> Thanks for your review.
>
> Cheers
> Suzuki
>
Thanks,
Gavin
^ permalink raw reply [flat|nested] 44+ messages in thread* Re: [PATCH v17 1/7] firmware: arm_rmm: Add SMC definitions for calling the RMM
2026-09-08 22:41 ` Gavin Shan
@ 2026-09-09 8:39 ` Suzuki K Poulose
2026-09-10 9:47 ` Gavin Shan
0 siblings, 1 reply; 44+ messages in thread
From: Suzuki K Poulose @ 2026-09-09 8:39 UTC (permalink / raw)
To: Gavin Shan, kvm, kvmarm
Cc: maz, will, catalin.marinas, linux-kernel, linux-arm-kernel,
steven.price, aneesh.kumar, oupton, joey.gouly, tabba, yuzenghui,
linux-coco, gankulkarni, sdonthineni, alpergun, fj0570is,
WeiLin.Chang, lpieralisi, enju.kohei
On 08/09/2026 23:41, Gavin Shan wrote:
> On 9/8/26 8:37 PM, Suzuki K Poulose wrote:
>> On 08/09/2026 07:19, Gavin Shan wrote:
>>> On 9/7/26 7:59 PM, Suzuki K Poulose wrote:
>>>> From: Steven Price <steven.price@arm.com>
>>>>
>>>> The RMM (Realm Management Monitor) provides functionality that can be
>>>> accessed by SMC calls from the host.
>>>>
>>>> The SMC definitions are based on DEN0137[1] version 2.0-bet3
>>>>
>>>> [1] https://developer.arm.com/documentation/den0137/2-0bet3/
>>>>
>>>> Signed-off-by: Steven Price <steven.price@arm.com>
>>>> Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
>>
>> ...
>>
>>>> ---
>>>> include/linux/arm-smccc-rmi.h | 494 ++++++++++++++++++++++++++++++
>>>> ++++
>>>> 1 file changed, 494 insertions(+)
>>>> create mode 100644 include/linux/arm-smccc-rmi.h
>>>>
>>>
>>> Some nitpicks below, please pick up the commits that look reasonable :)
>>>
>>
>>
>>>> +
>>>> +#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)
>>>> +
>>>
>>> If I'm correct enough, RMI_RETURN_{STATUS, INDEX, MEMREQ, CAN_CANEL}
>>> _MASK are used
>>> for once in this header file. So we needn't explicitly expose them
>>> and combine their
>>> definitions with users to fetch the corresponding fields, as below.
>>>
>>> #define RMI_RETURN_STATUS(ret) FIELD_GET(GENMASK(7, 0), ret)
>>> #define RMI_RETURN_INDEX(ret) FIELD_GET(GENMASK(15, 8), ret)
>>> #define RMI_RETURN_MEMREQ(ret) FIELD_GET(GENMASK(10, 8), ret)
>>> #define RMI_RETURN_CAN_CANCEL(ret) FIELD_GEt(GENMASK(10, 10), ret)
>>
>> I belive the header files were generated from a tool (by Steven) and
>> these would make it easier to compare with the generated header file for
>> any deviation from the spec. So, I am a bit reluctant make these
>> changes.
>>
>> That said, I am happy to convert the mask definitions to GENMASK() for
>> consistency and readability. i.e., something like:
>>
>> diff --git a/include/linux/arm-smccc-rmi.h b/include/linux/arm-smccc-
>> rmi.h
>> index 3eb88caf40964..078197284415e 100644
>> --- a/include/linux/arm-smccc-rmi.h
>> +++ b/include/linux/arm-smccc-rmi.h
>> @@ -139,10 +139,10 @@
>> #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_MASK GENMASK(7, 0)
>> +#define RMI_RETURN_INDEX_MASK GENMASK(15, 8)
>> +#define RMI_RETURN_MEMREQ_MASK GENMASK(10, 8)
>> +#define RMI_RETURN_CAN_CANCEL_MASK BIT(10)
>>
>
> Ok.
>
>>>
>>>> +#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 GENMASK(18, 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)
>>>> +
>>>
>>> As above, RMI_DONATE_{SIZE, COUNT, CONTIG, STATE}_MASK are used for
>>> once in this
>>> header file. So their definitions can be dropped by modifying the
>>> followup macros,
>>> as below. Some enhancements are also applicable: (a) Use 'BLOCK' to
>>> indicate the
>>> unit encoded in the request. (b) The macros are put into order from
>>> MSB to LSB.
>>>
>>> #define RMI_DONATE_STATE(req) FIELD_GET(GENMASK(18, 17), req)
>>> #define RMI_DONATE_CONTIG(req) FIELD_GET(GENMASK(16, 16), req)
>>> #define RMI_DONATE_BLOCK_COUNT(req) FIELD_GET(GENMASK(15, 2), req)
>>> #define RMI_DONATE_BLOCK_SIZE(req) FIELD_GET(GENMASK(1, 0), req)
>>
>> Same as above.
>>
>
> Please rename RMI_DONATE_{SIZE, COUNT} to RMI_DONATE_BLOCK_{SIZE, COUNT}
> if possible.
Sorry, forgot to add that, I have renamed RMI_{DONATE,ADDR_RANGE}_SIZE
=> RMI_{*}_BLOCK_SIZE, left the _COUNT as it is.
>
>>>
>>>> +#define RMI_OP_MEM_DELEGATED 0
>>>> +#define RMI_OP_MEM_UNDELEGATED 1
>>>> +#define RMI_OP_MEM_CONDITIONAL 2
>>>> +
>>>
>>> B4.6.40 RmiOpMemContig type is missed here and they should be used in
>>> the c code.
>>>
>>> #define RMI_OP_MEM_NON_CONTIG 0
>>> #define RMI_OP_MEM_CONTIG 1
>>>
>>> In the c code, we shall have:
>>>
>>> if (RMI_DONATE_CONTIG(req) == RMI_OP_MEM_CONTIG)) {
>>> ...
>>> } else {
>>> ...
>>> }
>>>
>>
>> Does it make much sense though ? I prefer
>>
>> if (RMI_DONATE_CONTIG(req))
>>
>> to
>>
>>
>> if (RMI_DONATE_CONTIG(req) == RMI_OP_MEM_CONTIG)) {
>>
>>
>> than having a field for a single bit values. I am happy to change
>> this if there is strong preference to the other.
>>
>
> The point is RMM-v2.0-bet3 expose RMI_OP_{MEM, MEM_NON}_CONTIG instead
> of true/false
> to indicate if the requested memory blocks are physically contiguous or
> not. So the
> expression 'if (RMI_DONATE_CONTIG(req) == RMI_OP_MEM_CONTIG)' is
> consistent to the
> RMM-v2.0-bet3 spec.
Agree, but the values are intuitively assigned to indicate whether it is
1/0. I personally think if (RMI_DONATE_CONTIG(req)) is good enough. But
to be consistent with the spec, I will change them.
Cheers
Suzuki
^ permalink raw reply [flat|nested] 44+ messages in thread* Re: [PATCH v17 1/7] firmware: arm_rmm: Add SMC definitions for calling the RMM
2026-09-09 8:39 ` Suzuki K Poulose
@ 2026-09-10 9:47 ` Gavin Shan
2026-09-10 9:54 ` Suzuki K Poulose
0 siblings, 1 reply; 44+ messages in thread
From: Gavin Shan @ 2026-09-10 9:47 UTC (permalink / raw)
To: Suzuki K Poulose, kvm, kvmarm
Cc: maz, will, catalin.marinas, linux-kernel, linux-arm-kernel,
steven.price, aneesh.kumar, oupton, joey.gouly, tabba, yuzenghui,
linux-coco, gankulkarni, sdonthineni, alpergun, fj0570is,
WeiLin.Chang, lpieralisi, enju.kohei
On 9/9/26 6:39 PM, Suzuki K Poulose wrote:
> On 08/09/2026 23:41, Gavin Shan wrote:
>> On 9/8/26 8:37 PM, Suzuki K Poulose wrote:
>>> On 08/09/2026 07:19, Gavin Shan wrote:
>>>> On 9/7/26 7:59 PM, Suzuki K Poulose wrote:
>>>>> From: Steven Price <steven.price@arm.com>
>>>>>
>>>>> The RMM (Realm Management Monitor) provides functionality that can be
>>>>> accessed by SMC calls from the host.
>>>>>
>>>>> The SMC definitions are based on DEN0137[1] version 2.0-bet3
>>>>>
>>>>> [1] https://developer.arm.com/documentation/den0137/2-0bet3/
>>>>>
>>>>> Signed-off-by: Steven Price <steven.price@arm.com>
>>>>> Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
>>>
>>> ...
>>>
>>>>> ---
>>>>> include/linux/arm-smccc-rmi.h | 494 ++++++++++++++++++++++++++++++ ++++
>>>>> 1 file changed, 494 insertions(+)
>>>>> create mode 100644 include/linux/arm-smccc-rmi.h
>>>>>
>>>>
>>>> Some nitpicks below, please pick up the commits that look reasonable :)
>>>>
>>>
>>>
>>>>> +
>>>>> +#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)
>>>>> +
>>>>
>>>> If I'm correct enough, RMI_RETURN_{STATUS, INDEX, MEMREQ, CAN_CANEL} _MASK are used
>>>> for once in this header file. So we needn't explicitly expose them and combine their
>>>> definitions with users to fetch the corresponding fields, as below.
>>>>
>>>> #define RMI_RETURN_STATUS(ret) FIELD_GET(GENMASK(7, 0), ret)
>>>> #define RMI_RETURN_INDEX(ret) FIELD_GET(GENMASK(15, 8), ret)
>>>> #define RMI_RETURN_MEMREQ(ret) FIELD_GET(GENMASK(10, 8), ret)
>>>> #define RMI_RETURN_CAN_CANCEL(ret) FIELD_GEt(GENMASK(10, 10), ret)
>>>
>>> I belive the header files were generated from a tool (by Steven) and
>>> these would make it easier to compare with the generated header file for
>>> any deviation from the spec. So, I am a bit reluctant make these
>>> changes.
>>>
>>> That said, I am happy to convert the mask definitions to GENMASK() for
>>> consistency and readability. i.e., something like:
>>>
>>> diff --git a/include/linux/arm-smccc-rmi.h b/include/linux/arm-smccc- rmi.h
>>> index 3eb88caf40964..078197284415e 100644
>>> --- a/include/linux/arm-smccc-rmi.h
>>> +++ b/include/linux/arm-smccc-rmi.h
>>> @@ -139,10 +139,10 @@
>>> #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_MASK GENMASK(7, 0)
>>> +#define RMI_RETURN_INDEX_MASK GENMASK(15, 8)
>>> +#define RMI_RETURN_MEMREQ_MASK GENMASK(10, 8)
>>> +#define RMI_RETURN_CAN_CANCEL_MASK BIT(10)
>>>
>>
>> Ok.
>>
>>>>
>>>>> +#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 GENMASK(18, 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)
>>>>> +
>>>>
>>>> As above, RMI_DONATE_{SIZE, COUNT, CONTIG, STATE}_MASK are used for once in this
>>>> header file. So their definitions can be dropped by modifying the followup macros,
>>>> as below. Some enhancements are also applicable: (a) Use 'BLOCK' to indicate the
>>>> unit encoded in the request. (b) The macros are put into order from MSB to LSB.
>>>>
>>>> #define RMI_DONATE_STATE(req) FIELD_GET(GENMASK(18, 17), req)
>>>> #define RMI_DONATE_CONTIG(req) FIELD_GET(GENMASK(16, 16), req)
>>>> #define RMI_DONATE_BLOCK_COUNT(req) FIELD_GET(GENMASK(15, 2), req)
>>>> #define RMI_DONATE_BLOCK_SIZE(req) FIELD_GET(GENMASK(1, 0), req)
>>>
>>> Same as above.
>>>
>>
>> Please rename RMI_DONATE_{SIZE, COUNT} to RMI_DONATE_BLOCK_{SIZE, COUNT} if possible.
>
> Sorry, forgot to add that, I have renamed RMI_{DONATE,ADDR_RANGE}_SIZE
> => RMI_{*}_BLOCK_SIZE, left the _COUNT as it is.
>
It's ok to me. The point is to have "BLOCK", indicating it's related to
a block. In rmi.c, 'unit' is also used and needs to be replaced with 'block',
consistent with the spec.
>>
>>>>
>>>>> +#define RMI_OP_MEM_DELEGATED 0
>>>>> +#define RMI_OP_MEM_UNDELEGATED 1
>>>>> +#define RMI_OP_MEM_CONDITIONAL 2
>>>>> +
>>>>
>>>> B4.6.40 RmiOpMemContig type is missed here and they should be used in the c code.
>>>>
>>>> #define RMI_OP_MEM_NON_CONTIG 0
>>>> #define RMI_OP_MEM_CONTIG 1
>>>>
>>>> In the c code, we shall have:
>>>>
>>>> if (RMI_DONATE_CONTIG(req) == RMI_OP_MEM_CONTIG)) {
>>>> ...
>>>> } else {
>>>> ...
>>>> }
>>>>
>>>
>>> Does it make much sense though ? I prefer
>>>
>>> if (RMI_DONATE_CONTIG(req))
>>>
>>> to
>>>
>>>
>>> if (RMI_DONATE_CONTIG(req) == RMI_OP_MEM_CONTIG)) {
>>>
>>>
>>> than having a field for a single bit values. I am happy to change
>>> this if there is strong preference to the other.
>>>
>>
>> The point is RMM-v2.0-bet3 expose RMI_OP_{MEM, MEM_NON}_CONTIG instead of true/false
>> to indicate if the requested memory blocks are physically contiguous or not. So the
>> expression 'if (RMI_DONATE_CONTIG(req) == RMI_OP_MEM_CONTIG)' is consistent to the
>> RMM-v2.0-bet3 spec.
>
> Agree, but the values are intuitively assigned to indicate whether it is
> 1/0. I personally think if (RMI_DONATE_CONTIG(req)) is good enough. But
> to be consistent with the spec, I will change them.
>
Ok.
Thanks,
Gavin
>
> Cheers
> Suzuki
>
^ permalink raw reply [flat|nested] 44+ messages in thread* Re: [PATCH v17 1/7] firmware: arm_rmm: Add SMC definitions for calling the RMM
2026-09-10 9:47 ` Gavin Shan
@ 2026-09-10 9:54 ` Suzuki K Poulose
0 siblings, 0 replies; 44+ messages in thread
From: Suzuki K Poulose @ 2026-09-10 9:54 UTC (permalink / raw)
To: Gavin Shan, kvm, kvmarm
Cc: maz, will, catalin.marinas, linux-kernel, linux-arm-kernel,
steven.price, aneesh.kumar, oupton, joey.gouly, tabba, yuzenghui,
linux-coco, gankulkarni, sdonthineni, alpergun, fj0570is,
WeiLin.Chang, lpieralisi, enju.kohei
On 10/09/2026 10:47, Gavin Shan wrote:
> On 9/9/26 6:39 PM, Suzuki K Poulose wrote:
>> On 08/09/2026 23:41, Gavin Shan wrote:
>>> On 9/8/26 8:37 PM, Suzuki K Poulose wrote:
>>>> On 08/09/2026 07:19, Gavin Shan wrote:
>>>>> On 9/7/26 7:59 PM, Suzuki K Poulose wrote:
>>>>>> From: Steven Price <steven.price@arm.com>
>>>>>>
>>>>>> The RMM (Realm Management Monitor) provides functionality that can be
>>>>>> accessed by SMC calls from the host.
>>>>>>
>>>>>> The SMC definitions are based on DEN0137[1] version 2.0-bet3
>>>>>>
>>>>>> [1] https://developer.arm.com/documentation/den0137/2-0bet3/
>>>>>>
>>>>>> Signed-off-by: Steven Price <steven.price@arm.com>
>>>>>> Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
>>>>
>>>> ...
>>>>
>>>>>> ---
>>>>>> include/linux/arm-smccc-rmi.h | 494 ++++++++++++++++++++++++++++
>>>>>> ++ ++++
>>>>>> 1 file changed, 494 insertions(+)
>>>>>> create mode 100644 include/linux/arm-smccc-rmi.h
>>>>>>
>>>>>
>>>>> Some nitpicks below, please pick up the commits that look
>>>>> reasonable :)
>>>>>
>>>>
>>>>
>>>>>> +
>>>>>> +#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)
>>>>>> +
>>>>>
>>>>> If I'm correct enough, RMI_RETURN_{STATUS, INDEX, MEMREQ,
>>>>> CAN_CANEL} _MASK are used
>>>>> for once in this header file. So we needn't explicitly expose them
>>>>> and combine their
>>>>> definitions with users to fetch the corresponding fields, as below.
>>>>>
>>>>> #define RMI_RETURN_STATUS(ret) FIELD_GET(GENMASK(7, 0), ret)
>>>>> #define RMI_RETURN_INDEX(ret) FIELD_GET(GENMASK(15, 8), ret)
>>>>> #define RMI_RETURN_MEMREQ(ret) FIELD_GET(GENMASK(10, 8), ret)
>>>>> #define RMI_RETURN_CAN_CANCEL(ret) FIELD_GEt(GENMASK(10, 10), ret)
>>>>
>>>> I belive the header files were generated from a tool (by Steven) and
>>>> these would make it easier to compare with the generated header file
>>>> for
>>>> any deviation from the spec. So, I am a bit reluctant make these
>>>> changes.
>>>>
>>>> That said, I am happy to convert the mask definitions to GENMASK() for
>>>> consistency and readability. i.e., something like:
>>>>
>>>> diff --git a/include/linux/arm-smccc-rmi.h b/include/linux/arm-
>>>> smccc- rmi.h
>>>> index 3eb88caf40964..078197284415e 100644
>>>> --- a/include/linux/arm-smccc-rmi.h
>>>> +++ b/include/linux/arm-smccc-rmi.h
>>>> @@ -139,10 +139,10 @@
>>>> #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_MASK GENMASK(7, 0)
>>>> +#define RMI_RETURN_INDEX_MASK GENMASK(15, 8)
>>>> +#define RMI_RETURN_MEMREQ_MASK GENMASK(10, 8)
>>>> +#define RMI_RETURN_CAN_CANCEL_MASK BIT(10)
>>>>
>>>
>>> Ok.
>>>
>>>>>
>>>>>> +#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 GENMASK(18, 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)
>>>>>> +
>>>>>
>>>>> As above, RMI_DONATE_{SIZE, COUNT, CONTIG, STATE}_MASK are used for
>>>>> once in this
>>>>> header file. So their definitions can be dropped by modifying the
>>>>> followup macros,
>>>>> as below. Some enhancements are also applicable: (a) Use 'BLOCK' to
>>>>> indicate the
>>>>> unit encoded in the request. (b) The macros are put into order from
>>>>> MSB to LSB.
>>>>>
>>>>> #define RMI_DONATE_STATE(req) FIELD_GET(GENMASK(18, 17), req)
>>>>> #define RMI_DONATE_CONTIG(req) FIELD_GET(GENMASK(16, 16), req)
>>>>> #define RMI_DONATE_BLOCK_COUNT(req) FIELD_GET(GENMASK(15, 2), req)
>>>>> #define RMI_DONATE_BLOCK_SIZE(req) FIELD_GET(GENMASK(1, 0), req)
>>>>
>>>> Same as above.
>>>>
>>>
>>> Please rename RMI_DONATE_{SIZE, COUNT} to RMI_DONATE_BLOCK_{SIZE,
>>> COUNT} if possible.
>>
>> Sorry, forgot to add that, I have renamed RMI_{DONATE,ADDR_RANGE}_SIZE
>> => RMI_{*}_BLOCK_SIZE, left the _COUNT as it is.
>>
>
> It's ok to me. The point is to have "BLOCK", indicating it's related to
> a block. In rmi.c, 'unit' is also used and needs to be replaced with
> 'block',
> consistent with the spec.
Yep, I could see why Steven went with "unit". But I have replaced them
with block everywhere to be consistent with the spec.
Cheers
Suzuki
^ permalink raw reply [flat|nested] 44+ messages in thread
* [PATCH v17 2/7] firmware: arm_rmm: Check for RMI support at init
2026-09-07 9:59 [PATCH v17 0/7] firmware: arm_rmm: Add RMM v2.0 base RMI support Suzuki K Poulose
2026-09-07 9:59 ` [PATCH v17 1/7] firmware: arm_rmm: Add SMC definitions for calling the RMM Suzuki K Poulose
@ 2026-09-07 9:59 ` Suzuki K Poulose
2026-09-08 6:46 ` Gavin Shan
2026-09-07 9:59 ` [PATCH v17 3/7] firmware: arm_rmm: Configure the RMM with the host's page size Suzuki K Poulose
` (5 subsequent siblings)
7 siblings, 1 reply; 44+ messages in thread
From: Suzuki K Poulose @ 2026-09-07 9:59 UTC (permalink / raw)
To: kvm, kvmarm
Cc: maz, will, catalin.marinas, linux-kernel, linux-arm-kernel,
steven.price, aneesh.kumar, oupton, gshan, joey.gouly, tabba,
yuzenghui, linux-coco, gankulkarni, sdonthineni, alpergun,
fj0570is, WeiLin.Chang, lpieralisi, enju.kohei, Suzuki K Poulose
From: Steven Price <steven.price@arm.com>
Query the RMI version number and check if it is a compatible version.
The first two feature registers are read and exposed for future code to
use.
Signed-off-by: Steven Price <steven.price@arm.com>
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
v17:
* Rename ARM_RMM to ARM_RMM_RMI to make it easier to add Guest facing RSI
support, which is also in progress
v16:
* Update Kconfig text to include PCIe TDISP.
* Export rmi_feat_reg() here rather than in a later commit.
v15:
* The code is moved again, this time into the 'firmware' directory.
v14:
* This moves the basic RMI setup into the 'kernel' directory. This is
because RMI will be used for some features outside of KVM so should
be available even if KVM isn't compiled in.
---
arch/arm64/Kconfig | 1 +
arch/arm64/kernel/cpufeature.c | 1 +
drivers/firmware/Kconfig | 1 +
drivers/firmware/Makefile | 1 +
drivers/firmware/arm_rmm/Kconfig | 26 ++++++++
drivers/firmware/arm_rmm/Makefile | 2 +
drivers/firmware/arm_rmm/rmi.c | 101 ++++++++++++++++++++++++++++++
include/linux/arm-rmi-cmds.h | 34 ++++++++++
8 files changed, 167 insertions(+)
create mode 100644 drivers/firmware/arm_rmm/Kconfig
create mode 100644 drivers/firmware/arm_rmm/Makefile
create mode 100644 drivers/firmware/arm_rmm/rmi.c
create mode 100644 include/linux/arm-rmi-cmds.h
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index b5a51b0ef9440..ff9565d3ffa59 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -38,6 +38,7 @@ config ARM64
select ARCH_HAS_MEMBARRIER_SYNC_CORE
select ARCH_HAS_MEM_ENCRYPT
select ARCH_SUPPORTS_MSEAL_SYSTEM_MAPPINGS
+ select ARCH_SUPPORTS_RMM
select ARCH_HAS_NMI_SAFE_THIS_CPU_OPS
select ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE
select ARCH_HAS_NONLEAF_PMD_YOUNG if ARM64_HAFT
diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
index 17b83a2518a8f..db26f6b0eaf5e 100644
--- a/arch/arm64/kernel/cpufeature.c
+++ b/arch/arm64/kernel/cpufeature.c
@@ -293,6 +293,7 @@ static const struct arm64_ftr_bits ftr_id_aa64isar3[] = {
static const struct arm64_ftr_bits ftr_id_aa64pfr0[] = {
ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64PFR0_EL1_CSV3_SHIFT, 4, 0),
ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64PFR0_EL1_CSV2_SHIFT, 4, 0),
+ ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64PFR0_EL1_RME_SHIFT, 4, 0),
ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64PFR0_EL1_DIT_SHIFT, 4, 0),
ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64PFR0_EL1_AMU_SHIFT, 4, 0),
ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64PFR0_EL1_MPAM_SHIFT, 4, 0),
diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig
index b7cc11e4fbfa6..62660bf520a8d 100644
--- a/drivers/firmware/Kconfig
+++ b/drivers/firmware/Kconfig
@@ -310,5 +310,6 @@ source "drivers/firmware/samsung/Kconfig"
source "drivers/firmware/smccc/Kconfig"
source "drivers/firmware/tegra/Kconfig"
source "drivers/firmware/xilinx/Kconfig"
+source "drivers/firmware/arm_rmm/Kconfig"
endmenu
diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile
index be46f1e1dc77f..196a650ccf025 100644
--- a/drivers/firmware/Makefile
+++ b/drivers/firmware/Makefile
@@ -39,3 +39,4 @@ obj-y += samsung/
obj-y += smccc/
obj-y += tegra/
obj-y += xilinx/
+obj-y += arm_rmm/
diff --git a/drivers/firmware/arm_rmm/Kconfig b/drivers/firmware/arm_rmm/Kconfig
new file mode 100644
index 0000000000000..82724ee23186d
--- /dev/null
+++ b/drivers/firmware/arm_rmm/Kconfig
@@ -0,0 +1,26 @@
+
+config ARCH_SUPPORTS_RMM
+ bool
+
+config ARM_RMM_RMI
+ bool "Realm Management Interface (RMI) 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 at EL2 in the Realm world and provides the Realm
+ Management Interface (RMI) used by a Normal World host to create,
+ manage and run protected virtual machines called Realms. The RMM can
+ also act as a TSM, as defined by the PCIe TDISP and can manage the
+ PCI IDE setup for securing the PCIe links.
+
+ This option builds the host-side RMI support used by KVM to detect a
+ compatible RMM, configure it, manage delegated memory and enable
+ Realm guests.
+
+ Selecting this option does not by itself make Realm guests available:
+ the system must also provide RME-capable hardware and firmware with a
+ compatible RMM implementation.
diff --git a/drivers/firmware/arm_rmm/Makefile b/drivers/firmware/arm_rmm/Makefile
new file mode 100644
index 0000000000000..65171988fdcae
--- /dev/null
+++ b/drivers/firmware/arm_rmm/Makefile
@@ -0,0 +1,2 @@
+
+obj-$(CONFIG_ARM_RMM_RMI) = rmi.o
diff --git a/drivers/firmware/arm_rmm/rmi.c b/drivers/firmware/arm_rmm/rmi.c
new file mode 100644
index 0000000000000..008a783407b4e
--- /dev/null
+++ b/drivers/firmware/arm_rmm/rmi.c
@@ -0,0 +1,101 @@
+// 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/slab.h>
+
+#include <asm/memory.h>
+#include <asm/pgtable-hwdef.h>
+
+/* 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);
+
+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 __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;
+
+ return 0;
+}
+
+/*
+ * Note arm64_init_rmi() must be called before kvm_init_rmi() otherwise KVM
+ * will not support realm guests. subsys_initcall() is called before
+ * module_init() (used for KVM) so this is OK.
+ */
+subsys_initcall(arm64_init_rmi);
diff --git a/include/linux/arm-rmi-cmds.h b/include/linux/arm-rmi-cmds.h
new file mode 100644
index 0000000000000..2fb1f7f86d71e
--- /dev/null
+++ b/include/linux/arm-rmi-cmds.h
@@ -0,0 +1,34 @@
+/* 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>
+
+unsigned long rmi_feat_reg(unsigned long id);
+
+/**
+ * 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 (res.a0 == RMI_SUCCESS && out)
+ *out = res.a1;
+
+ return res.a0;
+}
+
+#endif
--
2.43.0
^ permalink raw reply related [flat|nested] 44+ messages in thread* Re: [PATCH v17 2/7] firmware: arm_rmm: Check for RMI support at init
2026-09-07 9:59 ` [PATCH v17 2/7] firmware: arm_rmm: Check for RMI support at init Suzuki K Poulose
@ 2026-09-08 6:46 ` Gavin Shan
2026-09-08 9:49 ` Suzuki K Poulose
0 siblings, 1 reply; 44+ messages in thread
From: Gavin Shan @ 2026-09-08 6:46 UTC (permalink / raw)
To: Suzuki K Poulose, kvm, kvmarm
Cc: maz, will, catalin.marinas, linux-kernel, linux-arm-kernel,
steven.price, aneesh.kumar, oupton, joey.gouly, tabba, yuzenghui,
linux-coco, gankulkarni, sdonthineni, alpergun, fj0570is,
WeiLin.Chang, lpieralisi, enju.kohei
Hi Suzuki,
On 9/7/26 7:59 PM, Suzuki K Poulose wrote:
> From: Steven Price <steven.price@arm.com>
>
> Query the RMI version number and check if it is a compatible version.
> The first two feature registers are read and exposed for future code to
> use.
>
> Signed-off-by: Steven Price <steven.price@arm.com>
> Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
> ---
> v17:
> * Rename ARM_RMM to ARM_RMM_RMI to make it easier to add Guest facing RSI
> support, which is also in progress
> v16:
> * Update Kconfig text to include PCIe TDISP.
> * Export rmi_feat_reg() here rather than in a later commit.
> v15:
> * The code is moved again, this time into the 'firmware' directory.
> v14:
> * This moves the basic RMI setup into the 'kernel' directory. This is
> because RMI will be used for some features outside of KVM so should
> be available even if KVM isn't compiled in.
> ---
> arch/arm64/Kconfig | 1 +
> arch/arm64/kernel/cpufeature.c | 1 +
> drivers/firmware/Kconfig | 1 +
> drivers/firmware/Makefile | 1 +
> drivers/firmware/arm_rmm/Kconfig | 26 ++++++++
> drivers/firmware/arm_rmm/Makefile | 2 +
> drivers/firmware/arm_rmm/rmi.c | 101 ++++++++++++++++++++++++++++++
> include/linux/arm-rmi-cmds.h | 34 ++++++++++
> 8 files changed, 167 insertions(+)
> create mode 100644 drivers/firmware/arm_rmm/Kconfig
> create mode 100644 drivers/firmware/arm_rmm/Makefile
> create mode 100644 drivers/firmware/arm_rmm/rmi.c
> create mode 100644 include/linux/arm-rmi-cmds.h
>
> diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
> index b5a51b0ef9440..ff9565d3ffa59 100644
> --- a/arch/arm64/Kconfig
> +++ b/arch/arm64/Kconfig
> @@ -38,6 +38,7 @@ config ARM64
> select ARCH_HAS_MEMBARRIER_SYNC_CORE
> select ARCH_HAS_MEM_ENCRYPT
> select ARCH_SUPPORTS_MSEAL_SYSTEM_MAPPINGS
> + select ARCH_SUPPORTS_RMM
> select ARCH_HAS_NMI_SAFE_THIS_CPU_OPS
> select ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE
> select ARCH_HAS_NONLEAF_PMD_YOUNG if ARM64_HAFT
> diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
> index 17b83a2518a8f..db26f6b0eaf5e 100644
> --- a/arch/arm64/kernel/cpufeature.c
> +++ b/arch/arm64/kernel/cpufeature.c
> @@ -293,6 +293,7 @@ static const struct arm64_ftr_bits ftr_id_aa64isar3[] = {
> static const struct arm64_ftr_bits ftr_id_aa64pfr0[] = {
> ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64PFR0_EL1_CSV3_SHIFT, 4, 0),
> ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64PFR0_EL1_CSV2_SHIFT, 4, 0),
> + ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64PFR0_EL1_RME_SHIFT, 4, 0),
> ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64PFR0_EL1_DIT_SHIFT, 4, 0),
> ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64PFR0_EL1_AMU_SHIFT, 4, 0),
> ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64PFR0_EL1_MPAM_SHIFT, 4, 0),
> diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig
> index b7cc11e4fbfa6..62660bf520a8d 100644
> --- a/drivers/firmware/Kconfig
> +++ b/drivers/firmware/Kconfig
> @@ -310,5 +310,6 @@ source "drivers/firmware/samsung/Kconfig"
> source "drivers/firmware/smccc/Kconfig"
> source "drivers/firmware/tegra/Kconfig"
> source "drivers/firmware/xilinx/Kconfig"
> +source "drivers/firmware/arm_rmm/Kconfig"
>
> endmenu
> diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile
> index be46f1e1dc77f..196a650ccf025 100644
> --- a/drivers/firmware/Makefile
> +++ b/drivers/firmware/Makefile
> @@ -39,3 +39,4 @@ obj-y += samsung/
> obj-y += smccc/
> obj-y += tegra/
> obj-y += xilinx/
> +obj-y += arm_rmm/
> diff --git a/drivers/firmware/arm_rmm/Kconfig b/drivers/firmware/arm_rmm/Kconfig
> new file mode 100644
> index 0000000000000..82724ee23186d
> --- /dev/null
> +++ b/drivers/firmware/arm_rmm/Kconfig
> @@ -0,0 +1,26 @@
> +
> +config ARCH_SUPPORTS_RMM
> + bool
> +
> +config ARM_RMM_RMI
> + bool "Realm Management Interface (RMI) 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 at EL2 in the Realm world and provides the Realm
> + Management Interface (RMI) used by a Normal World host to create,
> + manage and run protected virtual machines called Realms. The RMM can
> + also act as a TSM, as defined by the PCIe TDISP and can manage the
> + PCI IDE setup for securing the PCIe links.
> +
> + This option builds the host-side RMI support used by KVM to detect a
> + compatible RMM, configure it, manage delegated memory and enable
> + Realm guests.
> +
> + Selecting this option does not by itself make Realm guests available:
> + the system must also provide RME-capable hardware and firmware with a
> + compatible RMM implementation.
> diff --git a/drivers/firmware/arm_rmm/Makefile b/drivers/firmware/arm_rmm/Makefile
> new file mode 100644
> index 0000000000000..65171988fdcae
> --- /dev/null
> +++ b/drivers/firmware/arm_rmm/Makefile
> @@ -0,0 +1,2 @@
> +
> +obj-$(CONFIG_ARM_RMM_RMI) = rmi.o
> diff --git a/drivers/firmware/arm_rmm/rmi.c b/drivers/firmware/arm_rmm/rmi.c
> new file mode 100644
> index 0000000000000..008a783407b4e
> --- /dev/null
> +++ b/drivers/firmware/arm_rmm/rmi.c
> @@ -0,0 +1,101 @@
> +// 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/slab.h>
> +
> +#include <asm/memory.h>
> +#include <asm/pgtable-hwdef.h>
> +
> +/* 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];
> +
I would drop RMI_FEAT_REG_COUNT and use ARRAY_SIZE(rmi_feat_reg_cache) in the code.
Besides, __ro_after_init is usually put at the last the declaration.
static unsigned long rmi_feat_reg_cache[RMI_FEAT_REG_COUNT] __ro_after_init;
> +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);
> +
I would suggest to rename 'id' to 'index' since it represents the feature
register index instead of feature register ID.
unsigned long rmi_feat_reg(unsigned long index)
{
:
}
EXPORT_SYMBOL_GPL(rmi_feat_reg);
> +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 __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;
> +
> + return 0;
> +}
> +
> +/*
> + * Note arm64_init_rmi() must be called before kvm_init_rmi() otherwise KVM
> + * will not support realm guests. subsys_initcall() is called before
> + * module_init() (used for KVM) so this is OK.
> + */
> +subsys_initcall(arm64_init_rmi);
> diff --git a/include/linux/arm-rmi-cmds.h b/include/linux/arm-rmi-cmds.h
> new file mode 100644
> index 0000000000000..2fb1f7f86d71e
> --- /dev/null
> +++ b/include/linux/arm-rmi-cmds.h
> @@ -0,0 +1,34 @@
> +/* 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>
> +
> +unsigned long rmi_feat_reg(unsigned long id);
> +
> +/**
> + * 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 (res.a0 == RMI_SUCCESS && out)
> + *out = res.a1;
> +
> + return res.a0;
> +}
> +
We needn't expose rmi_features() through include/linux/arm-rmi-cmds.h since it's
used for once in rmi.c::rmi_read_features(). I would suggest to drop it by combining
the code with that function.
> +#endif
#endif /* __LINUX_ARM_RMI_CMDS_H_ */
Thanks,
Gavin
^ permalink raw reply [flat|nested] 44+ messages in thread* Re: [PATCH v17 2/7] firmware: arm_rmm: Check for RMI support at init
2026-09-08 6:46 ` Gavin Shan
@ 2026-09-08 9:49 ` Suzuki K Poulose
0 siblings, 0 replies; 44+ messages in thread
From: Suzuki K Poulose @ 2026-09-08 9:49 UTC (permalink / raw)
To: Gavin Shan, kvm, kvmarm
Cc: maz, will, catalin.marinas, linux-kernel, linux-arm-kernel,
steven.price, aneesh.kumar, oupton, joey.gouly, tabba, yuzenghui,
linux-coco, gankulkarni, sdonthineni, alpergun, fj0570is,
WeiLin.Chang, lpieralisi, enju.kohei
On 08/09/2026 07:46, Gavin Shan wrote:
> Hi Suzuki,
>
> On 9/7/26 7:59 PM, Suzuki K Poulose wrote:
>> From: Steven Price <steven.price@arm.com>
>>
>> Query the RMI version number and check if it is a compatible version.
>> The first two feature registers are read and exposed for future code to
>> use.
>>
>> Signed-off-by: Steven Price <steven.price@arm.com>
>> Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
...
>> diff --git a/drivers/firmware/arm_rmm/rmi.c b/drivers/firmware/
>> arm_rmm/rmi.c
>> new file mode 100644
>> index 0000000000000..008a783407b4e
>> --- /dev/null
>> +++ b/drivers/firmware/arm_rmm/rmi.c
>> @@ -0,0 +1,101 @@
>> +// 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/slab.h>
>> +
>> +#include <asm/memory.h>
>> +#include <asm/pgtable-hwdef.h>
>> +
>> +/* 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];
>> +
>
> I would drop RMI_FEAT_REG_COUNT and use ARRAY_SIZE(rmi_feat_reg_cache)
> in the code.
> Besides, __ro_after_init is usually put at the last the declaration.
>
> static unsigned long rmi_feat_reg_cache[RMI_FEAT_REG_COUNT]
> __ro_after_init;
>
>> +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);
>> +
>
> I would suggest to rename 'id' to 'index' since it represents the feature
> register index instead of feature register ID.
>
> unsigned long rmi_feat_reg(unsigned long index)
> {
> :
> }
> EXPORT_SYMBOL_GPL(rmi_feat_reg);
>
>> +/*
>> + * Note arm64_init_rmi() must be called before kvm_init_rmi()
>> otherwise KVM
>> + * will not support realm guests. subsys_initcall() is called before
>> + * module_init() (used for KVM) so this is OK.
>> + */
>> +subsys_initcall(arm64_init_rmi);
>> diff --git a/include/linux/arm-rmi-cmds.h b/include/linux/arm-rmi-cmds.h
>> new file mode 100644
>> index 0000000000000..2fb1f7f86d71e
>> +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 (res.a0 == RMI_SUCCESS && out)
>> + *out = res.a1;
>> +
>> + return res.a0;
>> +}
>> +
>
> We needn't expose rmi_features() through include/linux/arm-rmi-cmds.h
> since it's
> used for once in rmi.c::rmi_read_features(). I would suggest to drop it
> by combining
> the code with that function.
Thanks for the review, I will address them in the next version.
Cheers
Suzuki
^ permalink raw reply [flat|nested] 44+ messages in thread
* [PATCH v17 3/7] firmware: arm_rmm: Configure the RMM with the host's page size
2026-09-07 9:59 [PATCH v17 0/7] firmware: arm_rmm: Add RMM v2.0 base RMI support Suzuki K Poulose
2026-09-07 9:59 ` [PATCH v17 1/7] firmware: arm_rmm: Add SMC definitions for calling the RMM Suzuki K Poulose
2026-09-07 9:59 ` [PATCH v17 2/7] firmware: arm_rmm: Check for RMI support at init Suzuki K Poulose
@ 2026-09-07 9:59 ` Suzuki K Poulose
2026-09-07 10:14 ` sashiko-bot
2026-09-08 7:04 ` Gavin Shan
2026-09-07 9:59 ` [PATCH v17 4/7] firmware: arm_rmm: Add support for SRO Suzuki K Poulose
` (4 subsequent siblings)
7 siblings, 2 replies; 44+ messages in thread
From: Suzuki K Poulose @ 2026-09-07 9:59 UTC (permalink / raw)
To: kvm, kvmarm
Cc: maz, will, catalin.marinas, linux-kernel, linux-arm-kernel,
steven.price, aneesh.kumar, oupton, gshan, joey.gouly, tabba,
yuzenghui, linux-coco, gankulkarni, sdonthineni, alpergun,
fj0570is, WeiLin.Chang, lpieralisi, enju.kohei, Suzuki K Poulose
From: Steven Price <steven.price@arm.com>
RMM v2.0 brings the ability to set the RMM's granule size. Check the
feature registers and configure the RMM so that it matches the host's
page size. This means that operations can be done with a granularity
equal to PAGE_SIZE.
Signed-off-by: Steven Price <steven.price@arm.com>
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
Changes since v15:
* Actually check the feature register for the host's page-size support.
Changes since v14:
* Move the implementation into drivers/firmware/arm_rmm.
Changes since v13:
* Moved out of KVM.
---
drivers/firmware/arm_rmm/rmi.c | 58 ++++++++++++++++++++++++++++++++++
include/linux/arm-rmi-cmds.h | 17 ++++++++++
2 files changed, 75 insertions(+)
diff --git a/drivers/firmware/arm_rmm/rmi.c b/drivers/firmware/arm_rmm/rmi.c
index 008a783407b4e..76f91c145e1fd 100644
--- a/drivers/firmware/arm_rmm/rmi.c
+++ b/drivers/firmware/arm_rmm/rmi.c
@@ -77,6 +77,60 @@ static int rmi_read_features(void)
return 0;
}
+static int rmi_configure(void)
+{
+ unsigned long granule_feature;
+ unsigned long granule_size;
+ int ret = 0;
+ struct rmm_config *config;
+
+ switch (PAGE_SIZE) {
+ case SZ_4K:
+ granule_size = RMI_GRANULE_SIZE_4KB;
+ granule_feature = RMI_FEATURE_REGISTER_1_RMI_GRAN_SZ_4KB;
+ break;
+ case SZ_16K:
+ granule_size = RMI_GRANULE_SIZE_16KB;
+ granule_feature = RMI_FEATURE_REGISTER_1_RMI_GRAN_SZ_16KB;
+ break;
+ case SZ_64K:
+ granule_size = RMI_GRANULE_SIZE_64KB;
+ granule_feature = RMI_FEATURE_REGISTER_1_RMI_GRAN_SZ_64KB;
+ break;
+ default:
+ BUILD_BUG();
+ }
+
+ if (!(rmi_feat_reg(1) & granule_feature)) {
+ pr_err("RMM does not support %luKB granules\n",
+ PAGE_SIZE >> 10);
+ return -ENXIO;
+ }
+
+ config = (struct rmm_config *)get_zeroed_page(GFP_KERNEL);
+ if (!config)
+ return -ENOMEM;
+
+ config->rmi_granule_size = granule_size;
+
+ /*
+ * For now we set the tracking_region_size to 0 which is the only option
+ * for 4KB PAGE_SIZE (1GB for 4KB PAGE_SIZE, 32MB/512MB for 16KB/64KB).
+ * TODO: Support other tracking sizes via Kconfig option for other
+ * PAGE_SIZES
+ */
+ config->tracking_region_size = 0;
+
+ ret = rmi_rmm_config_set(virt_to_phys(config));
+ if (ret) {
+ pr_err("RMM config set failed\n");
+ ret = -EINVAL;
+ }
+
+ free_page((unsigned long)config);
+ return ret;
+}
+
static int __init arm64_init_rmi(void)
{
int ret;
@@ -90,6 +144,10 @@ static int __init arm64_init_rmi(void)
if (ret)
return ret;
+ ret = rmi_configure();
+ if (ret)
+ return ret;
+
return 0;
}
diff --git a/include/linux/arm-rmi-cmds.h b/include/linux/arm-rmi-cmds.h
index 2fb1f7f86d71e..9aa27697e2377 100644
--- a/include/linux/arm-rmi-cmds.h
+++ b/include/linux/arm-rmi-cmds.h
@@ -12,6 +12,23 @@
unsigned long rmi_feat_reg(unsigned long id);
+/**
+ * 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_features() - Read feature register
* @index: Feature register index
--
2.43.0
^ permalink raw reply related [flat|nested] 44+ messages in thread* Re: [PATCH v17 3/7] firmware: arm_rmm: Configure the RMM with the host's page size
2026-09-07 9:59 ` [PATCH v17 3/7] firmware: arm_rmm: Configure the RMM with the host's page size Suzuki K Poulose
@ 2026-09-07 10:14 ` sashiko-bot
2026-09-07 12:02 ` Suzuki K Poulose
2026-09-08 7:04 ` Gavin Shan
1 sibling, 1 reply; 44+ messages in thread
From: sashiko-bot @ 2026-09-07 10:14 UTC (permalink / raw)
To: Suzuki K Poulose; +Cc: kvmarm, Marc Zyngier, kvm, Oliver Upton
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] The SMC call leaves parameters X2-X7 uninitialized, violating the RMM's RES0 requirement.
- [High] The `struct rmm_config` fields are populated using native endianness, which breaks on Big Endian kernels.
--
commit 1741e17729c2ff5e23bc8bfb00cc50a61f38ebb7
Author: Steven Price <steven.price@arm.com>
firmware: arm_rmm: Configure the RMM with the host's page size
This commit adds support to configure the RMM's granule size to match the
host's page size during kernel initialization. It reads the feature
registers to ensure the requested page size is supported and sends the
configuration to the RMM via an SMC call.
> diff --git a/drivers/firmware/arm_rmm/rmi.c b/drivers/firmware/arm_rmm/rmi.c
> index 008a783407b4e..76f91c145e1fd 100644
> --- a/drivers/firmware/arm_rmm/rmi.c
> +++ b/drivers/firmware/arm_rmm/rmi.c
> @@ -77,6 +77,60 @@ static int rmi_read_features(void)
> return 0;
> }
>
> +static int rmi_configure(void)
> +{
> + unsigned long granule_feature;
> + unsigned long granule_size;
> + int ret = 0;
> + struct rmm_config *config;
> +
> + switch (PAGE_SIZE) {
[ ... ]
> +
> + config = (struct rmm_config *)get_zeroed_page(GFP_KERNEL);
> + if (!config)
> + return -ENOMEM;
> +
> + config->rmi_granule_size = granule_size;
> +
> + /*
> + * For now we set the tracking_region_size to 0 which is the only option
> + * for 4KB PAGE_SIZE (1GB for 4KB PAGE_SIZE, 32MB/512MB for 16KB/64KB).
> + * TODO: Support other tracking sizes via Kconfig option for other
> + * PAGE_SIZES
> + */
> + config->tracking_region_size = 0;
[Severity: High]
Does this code handle endianness correctly?
The struct rmm_config fields are populated using native u64 assignments.
On an ARM64 kernel compiled with CONFIG_CPU_BIG_ENDIAN=y, this writes
big-endian bytes into the shared memory. The RMM, which runs in little-endian
at EL2, will misinterpret these fields.
Will this cause the RMM firmware to read invalid configuration values (like a
byte-swapped granule size) and fail to configure, breaking RMI support on Big
Endian systems?
> +
> + ret = rmi_rmm_config_set(virt_to_phys(config));
> + if (ret) {
> + pr_err("RMM config set failed\n");
> + ret = -EINVAL;
> + }
> +
> + free_page((unsigned long)config);
> + return ret;
> +}
[ ... ]
> diff --git a/include/linux/arm-rmi-cmds.h b/include/linux/arm-rmi-cmds.h
> index 2fb1f7f86d71e..9aa27697e2377 100644
> --- a/include/linux/arm-rmi-cmds.h
> +++ b/include/linux/arm-rmi-cmds.h
> @@ -12,6 +12,23 @@
>
> unsigned long rmi_feat_reg(unsigned long id);
>
> +/**
> + * 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);
[Severity: High]
Does this SMC call leave parameters x2 through x7 uninitialized?
The arm_smccc_1_1_invoke macro only generates inline assembly constraints
for the explicitly provided arguments. It does not zero-pad omitted arguments,
leaving registers x2 through x7 with whatever was previously in them.
Since the RMM firmware expects unused SMC parameters to be zero (RES0),
could passing uninitialized values cause the RMM to reject the command
with an INVALID_PARAMETER error and sporadically break RMM initialization?
> +
> + return res.a0;
> +}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260907095942.1140734-1-suzuki.poulose@arm.com?part=3
^ permalink raw reply [flat|nested] 44+ messages in thread* Re: [PATCH v17 3/7] firmware: arm_rmm: Configure the RMM with the host's page size
2026-09-07 10:14 ` sashiko-bot
@ 2026-09-07 12:02 ` Suzuki K Poulose
2026-09-07 22:40 ` Gavin Shan
0 siblings, 1 reply; 44+ messages in thread
From: Suzuki K Poulose @ 2026-09-07 12:02 UTC (permalink / raw)
To: sashiko-reviews; +Cc: kvmarm, Marc Zyngier, kvm, Oliver Upton
On 07/09/2026 11:14, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
> - [High] The SMC call leaves parameters X2-X7 uninitialized, violating the RMM's RES0 requirement.
> - [High] The `struct rmm_config` fields are populated using native endianness, which breaks on Big Endian kernels.
> --
>
> commit 1741e17729c2ff5e23bc8bfb00cc50a61f38ebb7
> Author: Steven Price <steven.price@arm.com>
> firmware: arm_rmm: Configure the RMM with the host's page size
>
> This commit adds support to configure the RMM's granule size to match the
> host's page size during kernel initialization. It reads the feature
> registers to ensure the requested page size is supported and sends the
> configuration to the RMM via an SMC call.
>
>> diff --git a/drivers/firmware/arm_rmm/rmi.c b/drivers/firmware/arm_rmm/rmi.c
>> index 008a783407b4e..76f91c145e1fd 100644
>> --- a/drivers/firmware/arm_rmm/rmi.c
>> +++ b/drivers/firmware/arm_rmm/rmi.c
>> @@ -77,6 +77,60 @@ static int rmi_read_features(void)
>> return 0;
>> }
>>
>> +static int rmi_configure(void)
>> +{
>> + unsigned long granule_feature;
>> + unsigned long granule_size;
>> + int ret = 0;
>> + struct rmm_config *config;
>> +
>> + switch (PAGE_SIZE) {
> [ ... ]
>> +
>> + config = (struct rmm_config *)get_zeroed_page(GFP_KERNEL);
>> + if (!config)
>> + return -ENOMEM;
>> +
>> + config->rmi_granule_size = granule_size;
>> +
>> + /*
>> + * For now we set the tracking_region_size to 0 which is the only option
>> + * for 4KB PAGE_SIZE (1GB for 4KB PAGE_SIZE, 32MB/512MB for 16KB/64KB).
>> + * TODO: Support other tracking sizes via Kconfig option for other
>> + * PAGE_SIZES
>> + */
>> + config->tracking_region_size = 0;
>
> [Severity: High]
> Does this code handle endianness correctly?
>
> The struct rmm_config fields are populated using native u64 assignments.
> On an ARM64 kernel compiled with CONFIG_CPU_BIG_ENDIAN=y, this writes
> big-endian bytes into the shared memory. The RMM, which runs in little-endian
> at EL2, will misinterpret these fields.
>
> Will this cause the RMM firmware to read invalid configuration values (like a
> byte-swapped granule size) and fail to configure, breaking RMI support on Big
> Endian systems?
>
We don't plan to support RMM for Big Endian Linux. I have now made the
ARCH_SUPPORTS_RMM to depend on !CPU_BIG_ENDIAN
>> +
>> + ret = rmi_rmm_config_set(virt_to_phys(config));
>> + if (ret) {
>> + pr_err("RMM config set failed\n");
>> + ret = -EINVAL;
>> + }
>> +
>> + free_page((unsigned long)config);
>> + return ret;
>> +}
> [ ... ]
>> diff --git a/include/linux/arm-rmi-cmds.h b/include/linux/arm-rmi-cmds.h
>> index 2fb1f7f86d71e..9aa27697e2377 100644
>> --- a/include/linux/arm-rmi-cmds.h
>> +++ b/include/linux/arm-rmi-cmds.h
>> @@ -12,6 +12,23 @@
>>
>> unsigned long rmi_feat_reg(unsigned long id);
>>
>> +/**
>> + * 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);
>
> [Severity: High]
> Does this SMC call leave parameters x2 through x7 uninitialized?
>
> The arm_smccc_1_1_invoke macro only generates inline assembly constraints
> for the explicitly provided arguments. It does not zero-pad omitted arguments,
> leaving registers x2 through x7 with whatever was previously in them.
>
> Since the RMM firmware expects unused SMC parameters to be zero (RES0),
> could passing uninitialized values cause the RMM to reject the command
> with an INVALID_PARAMETER error and sporadically break RMM initialization?
Correct, I have now moved all RMI calls to smccc_1_2, making sure that
all the undefined arguments are initialised to 0.
Cheers
Suzuki
>
>> +
>> + return res.a0;
>> +}
>
^ permalink raw reply [flat|nested] 44+ messages in thread* Re: [PATCH v17 3/7] firmware: arm_rmm: Configure the RMM with the host's page size
2026-09-07 12:02 ` Suzuki K Poulose
@ 2026-09-07 22:40 ` Gavin Shan
2026-09-08 9:58 ` Suzuki K Poulose
0 siblings, 1 reply; 44+ messages in thread
From: Gavin Shan @ 2026-09-07 22:40 UTC (permalink / raw)
To: Suzuki K Poulose, sashiko-reviews; +Cc: kvmarm, Marc Zyngier, kvm, Oliver Upton
Hi Suzuki,
On 9/7/26 10:02 PM, Suzuki K Poulose wrote:
> On 07/09/2026 11:14, sashiko-bot@kernel.org wrote:
>> Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
>> - [High] The SMC call leaves parameters X2-X7 uninitialized, violating the RMM's RES0 requirement.
>> - [High] The `struct rmm_config` fields are populated using native endianness, which breaks on Big Endian kernels.
>> --
>>
>> commit 1741e17729c2ff5e23bc8bfb00cc50a61f38ebb7
>> Author: Steven Price <steven.price@arm.com>
>> firmware: arm_rmm: Configure the RMM with the host's page size
>>
>> This commit adds support to configure the RMM's granule size to match the
>> host's page size during kernel initialization. It reads the feature
>> registers to ensure the requested page size is supported and sends the
>> configuration to the RMM via an SMC call.
>>
>>> diff --git a/drivers/firmware/arm_rmm/rmi.c b/drivers/firmware/arm_rmm/rmi.c
>>> index 008a783407b4e..76f91c145e1fd 100644
>>> --- a/drivers/firmware/arm_rmm/rmi.c
>>> +++ b/drivers/firmware/arm_rmm/rmi.c
>>> @@ -77,6 +77,60 @@ static int rmi_read_features(void)
>>> return 0;
>>> }
>>> +static int rmi_configure(void)
>>> +{
>>> + unsigned long granule_feature;
>>> + unsigned long granule_size;
>>> + int ret = 0;
>>> + struct rmm_config *config;
>>> +
>>> + switch (PAGE_SIZE) {
>> [ ... ]
>>> +
>>> + config = (struct rmm_config *)get_zeroed_page(GFP_KERNEL);
>>> + if (!config)
>>> + return -ENOMEM;
>>> +
>>> + config->rmi_granule_size = granule_size;
>>> +
>>> + /*
>>> + * For now we set the tracking_region_size to 0 which is the only option
>>> + * for 4KB PAGE_SIZE (1GB for 4KB PAGE_SIZE, 32MB/512MB for 16KB/64KB).
>>> + * TODO: Support other tracking sizes via Kconfig option for other
>>> + * PAGE_SIZES
>>> + */
>>> + config->tracking_region_size = 0;
>>
>> [Severity: High]
>> Does this code handle endianness correctly?
>>
>> The struct rmm_config fields are populated using native u64 assignments.
>> On an ARM64 kernel compiled with CONFIG_CPU_BIG_ENDIAN=y, this writes
>> big-endian bytes into the shared memory. The RMM, which runs in little-endian
>> at EL2, will misinterpret these fields.
>>
>> Will this cause the RMM firmware to read invalid configuration values (like a
>> byte-swapped granule size) and fail to configure, breaking RMI support on Big
>> Endian systems?
>>
>
> We don't plan to support RMM for Big Endian Linux. I have now made the ARCH_SUPPORTS_RMM to depend on !CPU_BIG_ENDIAN
>
You probably need to avoid referring to CPU_BIG_ENDIAN as it's being dropped
by PATCH[v2 13/14] in Will's series to removed unused big-endian support on
arm64 [1].
[1] [PATCH v2 00/14] arm64: Remove unused big-endian support
(https://lore.kernel.org/linux-arm-kernel/20260907163726.17104-14-will@kernel.org/)
Thanks,
Gavin
>>> +
>>> + ret = rmi_rmm_config_set(virt_to_phys(config));
>>> + if (ret) {
>>> + pr_err("RMM config set failed\n");
>>> + ret = -EINVAL;
>>> + }
>>> +
>>> + free_page((unsigned long)config);
>>> + return ret;
>>> +}
>> [ ... ]
>>> diff --git a/include/linux/arm-rmi-cmds.h b/include/linux/arm-rmi-cmds.h
>>> index 2fb1f7f86d71e..9aa27697e2377 100644
>>> --- a/include/linux/arm-rmi-cmds.h
>>> +++ b/include/linux/arm-rmi-cmds.h
>>> @@ -12,6 +12,23 @@
>>> unsigned long rmi_feat_reg(unsigned long id);
>>> +/**
>>> + * 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);
>>
>> [Severity: High]
>> Does this SMC call leave parameters x2 through x7 uninitialized?
>>
>> The arm_smccc_1_1_invoke macro only generates inline assembly constraints
>> for the explicitly provided arguments. It does not zero-pad omitted arguments,
>> leaving registers x2 through x7 with whatever was previously in them.
>>
>> Since the RMM firmware expects unused SMC parameters to be zero (RES0),
>> could passing uninitialized values cause the RMM to reject the command
>> with an INVALID_PARAMETER error and sporadically break RMM initialization?
>
> Correct, I have now moved all RMI calls to smccc_1_2, making sure that
> all the undefined arguments are initialised to 0.
>
> Cheers
> Suzuki
>
>
>>
>>> +
>>> + return res.a0;
>>> +}
>>
>
>
^ permalink raw reply [flat|nested] 44+ messages in thread* Re: [PATCH v17 3/7] firmware: arm_rmm: Configure the RMM with the host's page size
2026-09-07 22:40 ` Gavin Shan
@ 2026-09-08 9:58 ` Suzuki K Poulose
0 siblings, 0 replies; 44+ messages in thread
From: Suzuki K Poulose @ 2026-09-08 9:58 UTC (permalink / raw)
To: Gavin Shan, sashiko-reviews; +Cc: kvmarm, Marc Zyngier, kvm, Oliver Upton
On 07/09/2026 23:40, Gavin Shan wrote:
> Hi Suzuki,
>
> On 9/7/26 10:02 PM, Suzuki K Poulose wrote:
>> On 07/09/2026 11:14, sashiko-bot@kernel.org wrote:
>>> Thank you for your contribution! Sashiko AI review found 2 potential
>>> issue(s) to consider:
>>> - [High] The SMC call leaves parameters X2-X7 uninitialized,
>>> violating the RMM's RES0 requirement.
>>> - [High] The `struct rmm_config` fields are populated using native
>>> endianness, which breaks on Big Endian kernels.
>>> --
>>>
>>> commit 1741e17729c2ff5e23bc8bfb00cc50a61f38ebb7
>>> Author: Steven Price <steven.price@arm.com>
>>> firmware: arm_rmm: Configure the RMM with the host's page size
>>>
>>> This commit adds support to configure the RMM's granule size to match
>>> the
>>> host's page size during kernel initialization. It reads the feature
>>> registers to ensure the requested page size is supported and sends the
>>> configuration to the RMM via an SMC call.
>>>
>>>> diff --git a/drivers/firmware/arm_rmm/rmi.c b/drivers/firmware/
>>>> arm_rmm/rmi.c
>>>> index 008a783407b4e..76f91c145e1fd 100644
>>>> --- a/drivers/firmware/arm_rmm/rmi.c
>>>> +++ b/drivers/firmware/arm_rmm/rmi.c
>>>> @@ -77,6 +77,60 @@ static int rmi_read_features(void)
>>>> return 0;
>>>> }
>>>> +static int rmi_configure(void)
>>>> +{
>>>> + unsigned long granule_feature;
>>>> + unsigned long granule_size;
>>>> + int ret = 0;
>>>> + struct rmm_config *config;
>>>> +
>>>> + switch (PAGE_SIZE) {
>>> [ ... ]
>>>> +
>>>> + config = (struct rmm_config *)get_zeroed_page(GFP_KERNEL);
>>>> + if (!config)
>>>> + return -ENOMEM;
>>>> +
>>>> + config->rmi_granule_size = granule_size;
>>>> +
>>>> + /*
>>>> + * For now we set the tracking_region_size to 0 which is the
>>>> only option
>>>> + * for 4KB PAGE_SIZE (1GB for 4KB PAGE_SIZE, 32MB/512MB for
>>>> 16KB/64KB).
>>>> + * TODO: Support other tracking sizes via Kconfig option for other
>>>> + * PAGE_SIZES
>>>> + */
>>>> + config->tracking_region_size = 0;
>>>
>>> [Severity: High]
>>> Does this code handle endianness correctly?
>>>
>>> The struct rmm_config fields are populated using native u64 assignments.
>>> On an ARM64 kernel compiled with CONFIG_CPU_BIG_ENDIAN=y, this writes
>>> big-endian bytes into the shared memory. The RMM, which runs in
>>> little-endian
>>> at EL2, will misinterpret these fields.
>>>
>>> Will this cause the RMM firmware to read invalid configuration values
>>> (like a
>>> byte-swapped granule size) and fail to configure, breaking RMI
>>> support on Big
>>> Endian systems?
>>>
>>
>> We don't plan to support RMM for Big Endian Linux. I have now made the
>> ARCH_SUPPORTS_RMM to depend on !CPU_BIG_ENDIAN
>>
>
> You probably need to avoid referring to CPU_BIG_ENDIAN as it's being
> dropped
> by PATCH[v2 13/14] in Will's series to removed unused big-endian support on
> arm64 [1].
>
> [1] [PATCH v2 00/14] arm64: Remove unused big-endian support
> (https://lore.kernel.org/linux-arm-kernel/20260907163726.17104-14-
> will@kernel.org/)
Thanks for the heads up, I will just ignore this one from Sashiko.
Cheers
Suzuki
>
> Thanks,
> Gavin
>
>>>> +
>>>> + ret = rmi_rmm_config_set(virt_to_phys(config));
>>>> + if (ret) {
>>>> + pr_err("RMM config set failed\n");
>>>> + ret = -EINVAL;
>>>> + }
>>>> +
>>>> + free_page((unsigned long)config);
>>>> + return ret;
>>>> +}
>>> [ ... ]
>>>> diff --git a/include/linux/arm-rmi-cmds.h b/include/linux/arm-rmi-
>>>> cmds.h
>>>> index 2fb1f7f86d71e..9aa27697e2377 100644
>>>> --- a/include/linux/arm-rmi-cmds.h
>>>> +++ b/include/linux/arm-rmi-cmds.h
>>>> @@ -12,6 +12,23 @@
>>>> unsigned long rmi_feat_reg(unsigned long id);
>>>> +/**
>>>> + * 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);
>>>
>>> [Severity: High]
>>> Does this SMC call leave parameters x2 through x7 uninitialized?
>>>
>>> The arm_smccc_1_1_invoke macro only generates inline assembly
>>> constraints
>>> for the explicitly provided arguments. It does not zero-pad omitted
>>> arguments,
>>> leaving registers x2 through x7 with whatever was previously in them.
>>>
>>> Since the RMM firmware expects unused SMC parameters to be zero (RES0),
>>> could passing uninitialized values cause the RMM to reject the command
>>> with an INVALID_PARAMETER error and sporadically break RMM
>>> initialization?
>>
>> Correct, I have now moved all RMI calls to smccc_1_2, making sure that
>> all the undefined arguments are initialised to 0.
>>
>> Cheers
>> Suzuki
>>
>>
>>>
>>>> +
>>>> + return res.a0;
>>>> +}
>>>
>>
>>
>
^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH v17 3/7] firmware: arm_rmm: Configure the RMM with the host's page size
2026-09-07 9:59 ` [PATCH v17 3/7] firmware: arm_rmm: Configure the RMM with the host's page size Suzuki K Poulose
2026-09-07 10:14 ` sashiko-bot
@ 2026-09-08 7:04 ` Gavin Shan
2026-09-08 8:00 ` Kohei Enju
2026-09-08 10:43 ` Suzuki K Poulose
1 sibling, 2 replies; 44+ messages in thread
From: Gavin Shan @ 2026-09-08 7:04 UTC (permalink / raw)
To: Suzuki K Poulose, kvm, kvmarm
Cc: maz, will, catalin.marinas, linux-kernel, linux-arm-kernel,
steven.price, aneesh.kumar, oupton, joey.gouly, tabba, yuzenghui,
linux-coco, gankulkarni, sdonthineni, alpergun, fj0570is,
WeiLin.Chang, lpieralisi, enju.kohei
Hi Suzuki,
On 9/7/26 7:59 PM, Suzuki K Poulose wrote:
> From: Steven Price <steven.price@arm.com>
>
> RMM v2.0 brings the ability to set the RMM's granule size. Check the
> feature registers and configure the RMM so that it matches the host's
> page size. This means that operations can be done with a granularity
> equal to PAGE_SIZE.
>
> Signed-off-by: Steven Price <steven.price@arm.com>
> Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
> ---
> Changes since v15:
> * Actually check the feature register for the host's page-size support.
> Changes since v14:
> * Move the implementation into drivers/firmware/arm_rmm.
> Changes since v13:
> * Moved out of KVM.
> ---
> drivers/firmware/arm_rmm/rmi.c | 58 ++++++++++++++++++++++++++++++++++
> include/linux/arm-rmi-cmds.h | 17 ++++++++++
> 2 files changed, 75 insertions(+)
>
> diff --git a/drivers/firmware/arm_rmm/rmi.c b/drivers/firmware/arm_rmm/rmi.c
> index 008a783407b4e..76f91c145e1fd 100644
> --- a/drivers/firmware/arm_rmm/rmi.c
> +++ b/drivers/firmware/arm_rmm/rmi.c
> @@ -77,6 +77,60 @@ static int rmi_read_features(void)
> return 0;
> }
>
> +static int rmi_configure(void)
> +{
> + unsigned long granule_feature;
> + unsigned long granule_size;
> + int ret = 0;
> + struct rmm_config *config;
> +
> + switch (PAGE_SIZE) {
> + case SZ_4K:
> + granule_size = RMI_GRANULE_SIZE_4KB;
> + granule_feature = RMI_FEATURE_REGISTER_1_RMI_GRAN_SZ_4KB;
> + break;
> + case SZ_16K:
> + granule_size = RMI_GRANULE_SIZE_16KB;
> + granule_feature = RMI_FEATURE_REGISTER_1_RMI_GRAN_SZ_16KB;
> + break;
> + case SZ_64K:
> + granule_size = RMI_GRANULE_SIZE_64KB;
> + granule_feature = RMI_FEATURE_REGISTER_1_RMI_GRAN_SZ_64KB;
> + break;
> + default:
> + BUILD_BUG();
> + }
> +
> + if (!(rmi_feat_reg(1) & granule_feature)) {
> + pr_err("RMM does not support %luKB granules\n",
> + PAGE_SIZE >> 10);
> + return -ENXIO;
> + }
> +
> + config = (struct rmm_config *)get_zeroed_page(GFP_KERNEL);
> + if (!config)
> + return -ENOMEM;
An error message is needed here.
if (!config) {
pr_err("Unable to alloc RMM config memory\n");
return -ENOMEM;
}
> +
> + config->rmi_granule_size = granule_size;
> +
> + /*
> + * For now we set the tracking_region_size to 0 which is the only option
> + * for 4KB PAGE_SIZE (1GB for 4KB PAGE_SIZE, 32MB/512MB for 16KB/64KB).
> + * TODO: Support other tracking sizes via Kconfig option for other
> + * PAGE_SIZES
> + */
> + config->tracking_region_size = 0;
> +
> + ret = rmi_rmm_config_set(virt_to_phys(config));
> + if (ret) {
> + pr_err("RMM config set failed\n");
> + ret = -EINVAL;
> + }
The error code from rmi_rmm_config_set() is indicative sometimes. Also, -ENXIO
would be more appropriate than -EINVAL?
if (ret) {
pr_err("RMM config set failed (%d)\n", ret);
ret = -ENXIO;
}
> +
> + free_page((unsigned long)config);
> + return ret;
> +}
> +
> static int __init arm64_init_rmi(void)
> {
> int ret;
> @@ -90,6 +144,10 @@ static int __init arm64_init_rmi(void)
> if (ret)
> return ret;
>
> + ret = rmi_configure();
> + if (ret)
> + return ret;
> +
> return 0;
> }
>
> diff --git a/include/linux/arm-rmi-cmds.h b/include/linux/arm-rmi-cmds.h
> index 2fb1f7f86d71e..9aa27697e2377 100644
> --- a/include/linux/arm-rmi-cmds.h
> +++ b/include/linux/arm-rmi-cmds.h
> @@ -12,6 +12,23 @@
>
> unsigned long rmi_feat_reg(unsigned long id);
>
> +/**
> + * 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_config_set() is used for once by rmi.c::rmi_configure(), I would not expose
rmi_rmm_config_set() by combining the logic to rmi.c::rmi_configure().
> /**
> * rmi_features() - Read feature register
> * @index: Feature register index
Thanks,
Gavin
^ permalink raw reply [flat|nested] 44+ messages in thread* Re: [PATCH v17 3/7] firmware: arm_rmm: Configure the RMM with the host's page size
2026-09-08 7:04 ` Gavin Shan
@ 2026-09-08 8:00 ` Kohei Enju
2026-09-08 10:59 ` Gavin Shan
2026-09-08 10:43 ` Suzuki K Poulose
1 sibling, 1 reply; 44+ messages in thread
From: Kohei Enju @ 2026-09-08 8:00 UTC (permalink / raw)
To: Gavin Shan
Cc: Suzuki K Poulose, kvm, kvmarm, maz, will, catalin.marinas,
linux-kernel, linux-arm-kernel, steven.price, aneesh.kumar,
oupton, joey.gouly, tabba, yuzenghui, linux-coco, gankulkarni,
sdonthineni, alpergun, fj0570is, WeiLin.Chang, lpieralisi
Hi Gavin,
On 09/08 17:04, Gavin Shan wrote:
> Hi Suzuki,
>
> On 9/7/26 7:59 PM, Suzuki K Poulose wrote:
> > From: Steven Price <steven.price@arm.com>
> >
> > RMM v2.0 brings the ability to set the RMM's granule size. Check the
> > feature registers and configure the RMM so that it matches the host's
> > page size. This means that operations can be done with a granularity
> > equal to PAGE_SIZE.
> >
> > Signed-off-by: Steven Price <steven.price@arm.com>
> > Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
> > ---
> > Changes since v15:
> > * Actually check the feature register for the host's page-size support.
> > Changes since v14:
> > * Move the implementation into drivers/firmware/arm_rmm.
> > Changes since v13:
> > * Moved out of KVM.
> > ---
> > drivers/firmware/arm_rmm/rmi.c | 58 ++++++++++++++++++++++++++++++++++
> > include/linux/arm-rmi-cmds.h | 17 ++++++++++
> > 2 files changed, 75 insertions(+)
> >
> > diff --git a/drivers/firmware/arm_rmm/rmi.c b/drivers/firmware/arm_rmm/rmi.c
> > index 008a783407b4e..76f91c145e1fd 100644
> > --- a/drivers/firmware/arm_rmm/rmi.c
> > +++ b/drivers/firmware/arm_rmm/rmi.c
> > @@ -77,6 +77,60 @@ static int rmi_read_features(void)
> > return 0;
> > }
> > +static int rmi_configure(void)
> > +{
> > + unsigned long granule_feature;
> > + unsigned long granule_size;
> > + int ret = 0;
> > + struct rmm_config *config;
> > +
> > + switch (PAGE_SIZE) {
> > + case SZ_4K:
> > + granule_size = RMI_GRANULE_SIZE_4KB;
> > + granule_feature = RMI_FEATURE_REGISTER_1_RMI_GRAN_SZ_4KB;
> > + break;
> > + case SZ_16K:
> > + granule_size = RMI_GRANULE_SIZE_16KB;
> > + granule_feature = RMI_FEATURE_REGISTER_1_RMI_GRAN_SZ_16KB;
> > + break;
> > + case SZ_64K:
> > + granule_size = RMI_GRANULE_SIZE_64KB;
> > + granule_feature = RMI_FEATURE_REGISTER_1_RMI_GRAN_SZ_64KB;
> > + break;
> > + default:
> > + BUILD_BUG();
> > + }
> > +
> > + if (!(rmi_feat_reg(1) & granule_feature)) {
> > + pr_err("RMM does not support %luKB granules\n",
> > + PAGE_SIZE >> 10);
> > + return -ENXIO;
> > + }
> > +
> > + config = (struct rmm_config *)get_zeroed_page(GFP_KERNEL);
> > + if (!config)
> > + return -ENOMEM;
>
> An error message is needed here.
>
> if (!config) {
> pr_err("Unable to alloc RMM config memory\n");
> return -ENOMEM;
> }
I largely agree with your suggestions, and they look reasonable to me.
However, wouldn't this be redundant? Since __GFP_NOWARN is not set, the
page allocator would normally emit an allocation failure warning with a
stack trace anyway.
I don't have a strong preference, but as per "14) Allocating memory" in
the coding style, I believe it would be considered unnecessary.
Thanks,
Kohei
>
> > +
> > + config->rmi_granule_size = granule_size;
> > +
> > + /*
> > + * For now we set the tracking_region_size to 0 which is the only option
> > + * for 4KB PAGE_SIZE (1GB for 4KB PAGE_SIZE, 32MB/512MB for 16KB/64KB).
> > + * TODO: Support other tracking sizes via Kconfig option for other
> > + * PAGE_SIZES
> > + */
> > + config->tracking_region_size = 0;
> > +
> > + ret = rmi_rmm_config_set(virt_to_phys(config));
> > + if (ret) {
> > + pr_err("RMM config set failed\n");
> > + ret = -EINVAL;
> > + }
>
> The error code from rmi_rmm_config_set() is indicative sometimes. Also, -ENXIO
> would be more appropriate than -EINVAL?
>
> if (ret) {
> pr_err("RMM config set failed (%d)\n", ret);
> ret = -ENXIO;
> }
>
> > +
> > + free_page((unsigned long)config);
> > + return ret;
> > +}
> > +
> > static int __init arm64_init_rmi(void)
> > {
> > int ret;
> > @@ -90,6 +144,10 @@ static int __init arm64_init_rmi(void)
> > if (ret)
> > return ret;
> > + ret = rmi_configure();
> > + if (ret)
> > + return ret;
> > +
> > return 0;
> > }
> > diff --git a/include/linux/arm-rmi-cmds.h b/include/linux/arm-rmi-cmds.h
> > index 2fb1f7f86d71e..9aa27697e2377 100644
> > --- a/include/linux/arm-rmi-cmds.h
> > +++ b/include/linux/arm-rmi-cmds.h
> > @@ -12,6 +12,23 @@
> > unsigned long rmi_feat_reg(unsigned long id);
> > +/**
> > + * 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_config_set() is used for once by rmi.c::rmi_configure(), I would not expose
> rmi_rmm_config_set() by combining the logic to rmi.c::rmi_configure().
>
> > /**
> > * rmi_features() - Read feature register
> > * @index: Feature register index
>
> Thanks,
> Gavin
>
^ permalink raw reply [flat|nested] 44+ messages in thread* Re: [PATCH v17 3/7] firmware: arm_rmm: Configure the RMM with the host's page size
2026-09-08 8:00 ` Kohei Enju
@ 2026-09-08 10:59 ` Gavin Shan
2026-09-09 2:01 ` Kohei Enju
0 siblings, 1 reply; 44+ messages in thread
From: Gavin Shan @ 2026-09-08 10:59 UTC (permalink / raw)
To: Kohei Enju
Cc: Suzuki K Poulose, kvm, kvmarm, maz, will, catalin.marinas,
linux-kernel, linux-arm-kernel, steven.price, aneesh.kumar,
oupton, joey.gouly, tabba, yuzenghui, linux-coco, gankulkarni,
sdonthineni, alpergun, fj0570is, WeiLin.Chang, lpieralisi
Hi Kohei,
On 9/8/26 6:00 PM, Kohei Enju wrote:
> Hi Gavin,
>
> On 09/08 17:04, Gavin Shan wrote:
>> Hi Suzuki,
>>
>> On 9/7/26 7:59 PM, Suzuki K Poulose wrote:
>>> From: Steven Price <steven.price@arm.com>
>>>
>>> RMM v2.0 brings the ability to set the RMM's granule size. Check the
>>> feature registers and configure the RMM so that it matches the host's
>>> page size. This means that operations can be done with a granularity
>>> equal to PAGE_SIZE.
>>>
>>> Signed-off-by: Steven Price <steven.price@arm.com>
>>> Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
>>> ---
>>> Changes since v15:
>>> * Actually check the feature register for the host's page-size support.
>>> Changes since v14:
>>> * Move the implementation into drivers/firmware/arm_rmm.
>>> Changes since v13:
>>> * Moved out of KVM.
>>> ---
>>> drivers/firmware/arm_rmm/rmi.c | 58 ++++++++++++++++++++++++++++++++++
>>> include/linux/arm-rmi-cmds.h | 17 ++++++++++
>>> 2 files changed, 75 insertions(+)
>>>
>>> diff --git a/drivers/firmware/arm_rmm/rmi.c b/drivers/firmware/arm_rmm/rmi.c
>>> index 008a783407b4e..76f91c145e1fd 100644
>>> --- a/drivers/firmware/arm_rmm/rmi.c
>>> +++ b/drivers/firmware/arm_rmm/rmi.c
>>> @@ -77,6 +77,60 @@ static int rmi_read_features(void)
>>> return 0;
>>> }
>>> +static int rmi_configure(void)
>>> +{
>>> + unsigned long granule_feature;
>>> + unsigned long granule_size;
>>> + int ret = 0;
>>> + struct rmm_config *config;
>>> +
>>> + switch (PAGE_SIZE) {
>>> + case SZ_4K:
>>> + granule_size = RMI_GRANULE_SIZE_4KB;
>>> + granule_feature = RMI_FEATURE_REGISTER_1_RMI_GRAN_SZ_4KB;
>>> + break;
>>> + case SZ_16K:
>>> + granule_size = RMI_GRANULE_SIZE_16KB;
>>> + granule_feature = RMI_FEATURE_REGISTER_1_RMI_GRAN_SZ_16KB;
>>> + break;
>>> + case SZ_64K:
>>> + granule_size = RMI_GRANULE_SIZE_64KB;
>>> + granule_feature = RMI_FEATURE_REGISTER_1_RMI_GRAN_SZ_64KB;
>>> + break;
>>> + default:
>>> + BUILD_BUG();
>>> + }
>>> +
>>> + if (!(rmi_feat_reg(1) & granule_feature)) {
>>> + pr_err("RMM does not support %luKB granules\n",
>>> + PAGE_SIZE >> 10);
>>> + return -ENXIO;
>>> + }
>>> +
>>> + config = (struct rmm_config *)get_zeroed_page(GFP_KERNEL);
>>> + if (!config)
>>> + return -ENOMEM;
>>
>> An error message is needed here.
>>
>> if (!config) {
>> pr_err("Unable to alloc RMM config memory\n");
>> return -ENOMEM;
>> }
>
> I largely agree with your suggestions, and they look reasonable to me.
>
> However, wouldn't this be redundant? Since __GFP_NOWARN is not set, the
> page allocator would normally emit an allocation failure warning with a
> stack trace anyway.
>
> I don't have a strong preference, but as per "14) Allocating memory" in
> the coding style, I believe it would be considered unnecessary.
>
You're correct that the error message is most likely redundant here and we
needn't it. However, it's notable the allocation failure warning and stack
trace isn't 100% raised when looking at mm/page_alloc.c::warn_alloc(). At least,
it depends on !__ratelimit(&nopage_rs) even it's less likely to happen. Similarly,
there are conditions to raise a warning and stack track in case of the injected
allocation error in lib/fault-inject.c::fail_dump().
Thanks,
Gavin
> Thanks,
> Kohei
>
>>
>>> +
>>> + config->rmi_granule_size = granule_size;
>>> +
>>> + /*
>>> + * For now we set the tracking_region_size to 0 which is the only option
>>> + * for 4KB PAGE_SIZE (1GB for 4KB PAGE_SIZE, 32MB/512MB for 16KB/64KB).
>>> + * TODO: Support other tracking sizes via Kconfig option for other
>>> + * PAGE_SIZES
>>> + */
>>> + config->tracking_region_size = 0;
>>> +
>>> + ret = rmi_rmm_config_set(virt_to_phys(config));
>>> + if (ret) {
>>> + pr_err("RMM config set failed\n");
>>> + ret = -EINVAL;
>>> + }
>>
>> The error code from rmi_rmm_config_set() is indicative sometimes. Also, -ENXIO
>> would be more appropriate than -EINVAL?
>>
>> if (ret) {
>> pr_err("RMM config set failed (%d)\n", ret);
>> ret = -ENXIO;
>> }
>>
>>> +
>>> + free_page((unsigned long)config);
>>> + return ret;
>>> +}
>>> +
>>> static int __init arm64_init_rmi(void)
>>> {
>>> int ret;
>>> @@ -90,6 +144,10 @@ static int __init arm64_init_rmi(void)
>>> if (ret)
>>> return ret;
>>> + ret = rmi_configure();
>>> + if (ret)
>>> + return ret;
>>> +
>>> return 0;
>>> }
>>> diff --git a/include/linux/arm-rmi-cmds.h b/include/linux/arm-rmi-cmds.h
>>> index 2fb1f7f86d71e..9aa27697e2377 100644
>>> --- a/include/linux/arm-rmi-cmds.h
>>> +++ b/include/linux/arm-rmi-cmds.h
>>> @@ -12,6 +12,23 @@
>>> unsigned long rmi_feat_reg(unsigned long id);
>>> +/**
>>> + * 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_config_set() is used for once by rmi.c::rmi_configure(), I would not expose
>> rmi_rmm_config_set() by combining the logic to rmi.c::rmi_configure().
>>
>>> /**
>>> * rmi_features() - Read feature register
>>> * @index: Feature register index
>>
>> Thanks,
>> Gavin
>>
>
^ permalink raw reply [flat|nested] 44+ messages in thread* Re: [PATCH v17 3/7] firmware: arm_rmm: Configure the RMM with the host's page size
2026-09-08 10:59 ` Gavin Shan
@ 2026-09-09 2:01 ` Kohei Enju
0 siblings, 0 replies; 44+ messages in thread
From: Kohei Enju @ 2026-09-09 2:01 UTC (permalink / raw)
To: Gavin Shan
Cc: Suzuki K Poulose, kvm, kvmarm, maz, will, catalin.marinas,
linux-kernel, linux-arm-kernel, steven.price, aneesh.kumar,
oupton, joey.gouly, tabba, yuzenghui, linux-coco, gankulkarni,
sdonthineni, alpergun, fj0570is, WeiLin.Chang, lpieralisi
Good day Gavin,
On 09/08 20:59, Gavin Shan wrote:
> Hi Kohei,
>
> On 9/8/26 6:00 PM, Kohei Enju wrote:
> > Hi Gavin,
> >
> > On 09/08 17:04, Gavin Shan wrote:
> > > Hi Suzuki,
> > >
> > > On 9/7/26 7:59 PM, Suzuki K Poulose wrote:
> > > > From: Steven Price <steven.price@arm.com>
> > > >
> > > > RMM v2.0 brings the ability to set the RMM's granule size. Check the
> > > > feature registers and configure the RMM so that it matches the host's
> > > > page size. This means that operations can be done with a granularity
> > > > equal to PAGE_SIZE.
> > > >
> > > > Signed-off-by: Steven Price <steven.price@arm.com>
> > > > Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
> > > > ---
> > > > Changes since v15:
> > > > * Actually check the feature register for the host's page-size support.
> > > > Changes since v14:
> > > > * Move the implementation into drivers/firmware/arm_rmm.
> > > > Changes since v13:
> > > > * Moved out of KVM.
> > > > ---
> > > > drivers/firmware/arm_rmm/rmi.c | 58 ++++++++++++++++++++++++++++++++++
> > > > include/linux/arm-rmi-cmds.h | 17 ++++++++++
> > > > 2 files changed, 75 insertions(+)
> > > >
> > > > diff --git a/drivers/firmware/arm_rmm/rmi.c b/drivers/firmware/arm_rmm/rmi.c
> > > > index 008a783407b4e..76f91c145e1fd 100644
> > > > --- a/drivers/firmware/arm_rmm/rmi.c
> > > > +++ b/drivers/firmware/arm_rmm/rmi.c
> > > > @@ -77,6 +77,60 @@ static int rmi_read_features(void)
> > > > return 0;
> > > > }
> > > > +static int rmi_configure(void)
> > > > +{
> > > > + unsigned long granule_feature;
> > > > + unsigned long granule_size;
> > > > + int ret = 0;
> > > > + struct rmm_config *config;
> > > > +
> > > > + switch (PAGE_SIZE) {
> > > > + case SZ_4K:
> > > > + granule_size = RMI_GRANULE_SIZE_4KB;
> > > > + granule_feature = RMI_FEATURE_REGISTER_1_RMI_GRAN_SZ_4KB;
> > > > + break;
> > > > + case SZ_16K:
> > > > + granule_size = RMI_GRANULE_SIZE_16KB;
> > > > + granule_feature = RMI_FEATURE_REGISTER_1_RMI_GRAN_SZ_16KB;
> > > > + break;
> > > > + case SZ_64K:
> > > > + granule_size = RMI_GRANULE_SIZE_64KB;
> > > > + granule_feature = RMI_FEATURE_REGISTER_1_RMI_GRAN_SZ_64KB;
> > > > + break;
> > > > + default:
> > > > + BUILD_BUG();
> > > > + }
> > > > +
> > > > + if (!(rmi_feat_reg(1) & granule_feature)) {
> > > > + pr_err("RMM does not support %luKB granules\n",
> > > > + PAGE_SIZE >> 10);
> > > > + return -ENXIO;
> > > > + }
> > > > +
> > > > + config = (struct rmm_config *)get_zeroed_page(GFP_KERNEL);
> > > > + if (!config)
> > > > + return -ENOMEM;
> > >
> > > An error message is needed here.
> > >
> > > if (!config) {
> > > pr_err("Unable to alloc RMM config memory\n");
> > > return -ENOMEM;
> > > }
> >
> > I largely agree with your suggestions, and they look reasonable to me.
> >
> > However, wouldn't this be redundant? Since __GFP_NOWARN is not set, the
> > page allocator would normally emit an allocation failure warning with a
> > stack trace anyway.
> >
> > I don't have a strong preference, but as per "14) Allocating memory" in
> > the coding style, I believe it would be considered unnecessary.
> >
>
> You're correct that the error message is most likely redundant here and we
> needn't it. However, it's notable the allocation failure warning and stack
> trace isn't 100% raised when looking at mm/page_alloc.c::warn_alloc(). At least,
> it depends on !__ratelimit(&nopage_rs) even it's less likely to happen. Similarly,
Indeed. I understand your point.
> there are conditions to raise a warning and stack track in case of the injected
> allocation error in lib/fault-inject.c::fail_dump().
Thanks for the detailed explanation.
Thanks,
Kohei
>
> Thanks,
> Gavin
>
> > Thanks,
> > Kohei
> >
> > >
> > > > +
> > > > + config->rmi_granule_size = granule_size;
> > > > +
> > > > + /*
> > > > + * For now we set the tracking_region_size to 0 which is the only option
> > > > + * for 4KB PAGE_SIZE (1GB for 4KB PAGE_SIZE, 32MB/512MB for 16KB/64KB).
> > > > + * TODO: Support other tracking sizes via Kconfig option for other
> > > > + * PAGE_SIZES
> > > > + */
> > > > + config->tracking_region_size = 0;
> > > > +
> > > > + ret = rmi_rmm_config_set(virt_to_phys(config));
> > > > + if (ret) {
> > > > + pr_err("RMM config set failed\n");
> > > > + ret = -EINVAL;
> > > > + }
> > >
> > > The error code from rmi_rmm_config_set() is indicative sometimes. Also, -ENXIO
> > > would be more appropriate than -EINVAL?
> > >
> > > if (ret) {
> > > pr_err("RMM config set failed (%d)\n", ret);
> > > ret = -ENXIO;
> > > }
> > >
> > > > +
> > > > + free_page((unsigned long)config);
> > > > + return ret;
> > > > +}
> > > > +
> > > > static int __init arm64_init_rmi(void)
> > > > {
> > > > int ret;
> > > > @@ -90,6 +144,10 @@ static int __init arm64_init_rmi(void)
> > > > if (ret)
> > > > return ret;
> > > > + ret = rmi_configure();
> > > > + if (ret)
> > > > + return ret;
> > > > +
> > > > return 0;
> > > > }
> > > > diff --git a/include/linux/arm-rmi-cmds.h b/include/linux/arm-rmi-cmds.h
> > > > index 2fb1f7f86d71e..9aa27697e2377 100644
> > > > --- a/include/linux/arm-rmi-cmds.h
> > > > +++ b/include/linux/arm-rmi-cmds.h
> > > > @@ -12,6 +12,23 @@
> > > > unsigned long rmi_feat_reg(unsigned long id);
> > > > +/**
> > > > + * 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_config_set() is used for once by rmi.c::rmi_configure(), I would not expose
> > > rmi_rmm_config_set() by combining the logic to rmi.c::rmi_configure().
> > >
> > > > /**
> > > > * rmi_features() - Read feature register
> > > > * @index: Feature register index
> > >
> > > Thanks,
> > > Gavin
> > >
> >
>
^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH v17 3/7] firmware: arm_rmm: Configure the RMM with the host's page size
2026-09-08 7:04 ` Gavin Shan
2026-09-08 8:00 ` Kohei Enju
@ 2026-09-08 10:43 ` Suzuki K Poulose
1 sibling, 0 replies; 44+ messages in thread
From: Suzuki K Poulose @ 2026-09-08 10:43 UTC (permalink / raw)
To: Gavin Shan, kvm, kvmarm
Cc: maz, will, catalin.marinas, linux-kernel, linux-arm-kernel,
steven.price, aneesh.kumar, oupton, joey.gouly, tabba, yuzenghui,
linux-coco, gankulkarni, sdonthineni, alpergun, fj0570is,
WeiLin.Chang, lpieralisi, enju.kohei
On 08/09/2026 08:04, Gavin Shan wrote:
> Hi Suzuki,
>
> On 9/7/26 7:59 PM, Suzuki K Poulose wrote:
>> From: Steven Price <steven.price@arm.com>
>>
>> RMM v2.0 brings the ability to set the RMM's granule size. Check the
>> feature registers and configure the RMM so that it matches the host's
>> page size. This means that operations can be done with a granularity
>> equal to PAGE_SIZE.
>>
>> Signed-off-by: Steven Price <steven.price@arm.com>
>> Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
>> ---
>> Changes since v15:
>> * Actually check the feature register for the host's page-size
>> support.
>> Changes since v14:
>> * Move the implementation into drivers/firmware/arm_rmm.
>> Changes since v13:
>> * Moved out of KVM.
>> ---
>> drivers/firmware/arm_rmm/rmi.c | 58 ++++++++++++++++++++++++++++++++++
>> include/linux/arm-rmi-cmds.h | 17 ++++++++++
>> 2 files changed, 75 insertions(+)
>>
>> diff --git a/drivers/firmware/arm_rmm/rmi.c b/drivers/firmware/
>> arm_rmm/rmi.c
>> index 008a783407b4e..76f91c145e1fd 100644
>> --- a/drivers/firmware/arm_rmm/rmi.c
>> +++ b/drivers/firmware/arm_rmm/rmi.c
>> @@ -77,6 +77,60 @@ static int rmi_read_features(void)
>> return 0;
>> }
>> +static int rmi_configure(void)
>> +{
>> + unsigned long granule_feature;
>> + unsigned long granule_size;
>> + int ret = 0;
>> + struct rmm_config *config;
>> +
>> + switch (PAGE_SIZE) {
>> + case SZ_4K:
>> + granule_size = RMI_GRANULE_SIZE_4KB;
>> + granule_feature = RMI_FEATURE_REGISTER_1_RMI_GRAN_SZ_4KB;
>> + break;
>> + case SZ_16K:
>> + granule_size = RMI_GRANULE_SIZE_16KB;
>> + granule_feature = RMI_FEATURE_REGISTER_1_RMI_GRAN_SZ_16KB;
>> + break;
>> + case SZ_64K:
>> + granule_size = RMI_GRANULE_SIZE_64KB;
>> + granule_feature = RMI_FEATURE_REGISTER_1_RMI_GRAN_SZ_64KB;
>> + break;
>> + default:
>> + BUILD_BUG();
>> + }
>> +
>> + if (!(rmi_feat_reg(1) & granule_feature)) {
>> + pr_err("RMM does not support %luKB granules\n",
>> + PAGE_SIZE >> 10);
>> + return -ENXIO;
>> + }
>> +
>> + config = (struct rmm_config *)get_zeroed_page(GFP_KERNEL);
>> + if (!config)
>> + return -ENOMEM;
>
> An error message is needed here.
>
> if (!config) {
> pr_err("Unable to alloc RMM config memory\n");
> return -ENOMEM;
> }
>
I can add that, doesn't hurt.
>> +
>> + config->rmi_granule_size = granule_size;
>> +
>> + /*
>> + * For now we set the tracking_region_size to 0 which is the only
>> option
>> + * for 4KB PAGE_SIZE (1GB for 4KB PAGE_SIZE, 32MB/512MB for
>> 16KB/64KB).
>> + * TODO: Support other tracking sizes via Kconfig option for other
>> + * PAGE_SIZES
>> + */
>> + config->tracking_region_size = 0;
>> +
>> + ret = rmi_rmm_config_set(virt_to_phys(config));
>> + if (ret) {
>> + pr_err("RMM config set failed\n");
>> + ret = -EINVAL;
>> + }
>
> The error code from rmi_rmm_config_set() is indicative sometimes. Also,
> -ENXIO
> would be more appropriate than -EINVAL?
>
> if (ret) {
> pr_err("RMM config set failed (%d)\n", ret);
> ret = -ENXIO;
> }
>
Ack
>> diff --git a/include/linux/arm-rmi-cmds.h b/include/linux/arm-rmi-cmds.h
>> index 2fb1f7f86d71e..9aa27697e2377 100644
>> --- a/include/linux/arm-rmi-cmds.h
>> +++ b/include/linux/arm-rmi-cmds.h
>> @@ -12,6 +12,23 @@
>> unsigned long rmi_feat_reg(unsigned long id);
>> +/**
>> + * 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_config_set() is used for once by rmi.c::rmi_configure(), I would
> not expose
> rmi_rmm_config_set() by combining the logic to rmi.c::rmi_configure().
Ack
Thank you for the review
Cheers
Suzuki
^ permalink raw reply [flat|nested] 44+ messages in thread
* [PATCH v17 4/7] firmware: arm_rmm: Add support for SRO
2026-09-07 9:59 [PATCH v17 0/7] firmware: arm_rmm: Add RMM v2.0 base RMI support Suzuki K Poulose
` (2 preceding siblings ...)
2026-09-07 9:59 ` [PATCH v17 3/7] firmware: arm_rmm: Configure the RMM with the host's page size Suzuki K Poulose
@ 2026-09-07 9:59 ` Suzuki K Poulose
2026-09-07 10:14 ` sashiko-bot
2026-09-09 4:10 ` Gavin Shan
2026-09-07 9:59 ` [PATCH v17 5/7] firmware: arm_rmm: Activate the RMM Suzuki K Poulose
` (3 subsequent siblings)
7 siblings, 2 replies; 44+ messages in thread
From: Suzuki K Poulose @ 2026-09-07 9:59 UTC (permalink / raw)
To: kvm, kvmarm
Cc: maz, will, catalin.marinas, linux-kernel, linux-arm-kernel,
steven.price, aneesh.kumar, oupton, gshan, joey.gouly, tabba,
yuzenghui, linux-coco, gankulkarni, sdonthineni, alpergun,
fj0570is, WeiLin.Chang, lpieralisi, enju.kohei, Suzuki K Poulose
From: Steven Price <steven.price@arm.com>
RMM v2.0 introduces the concept of "Stateful RMI Operations" (SRO). This
means that an SMC can return with an operation still in progress. The
host is expected to continue the operation until it reaches a conclusion
(either success or failure). During this process the RMM can request
additional memory ('donate') or hand memory back to the host
('reclaim'). The host can request an in progress operation is cancelled,
but still continue the operation until it has completed (otherwise the
incomplete operation may cause future RMM operations to fail).
The SRO is tracked using a struct rmi_sro_state object which keeps track
of any memory which has been allocated but not yet consumed by the RMM
or reclaimed from the RMM. This allows the memory to be reused in a
future request within the same operation. It will also permit an
operation to be done in a context where memory allocation may be
difficult (e.g. atomic context) with the option to abort the operation
and retry the memory allocation outside of the atomic context. The
memory stored in the struct rmi_sro_state object can then be reused on
the subsequent attempt.
Wrappers for SRO RMI commands are also provided here because they depend
on the rmi_sro_execute() implementation added by this patch.
Delegate/undelegate handles are also added here because they now use the
SRO/stateful command infrastructure.
Signed-off-by: Steven Price <steven.price@arm.com>
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
v17:
* Handle buggy RMM firmware to avoid looping forever for non-cancellable SROs.
* Add comment (for the AI agents) to clarify that all memory donating SROs are
cancellable.
v16:
* Wrappers for realm guests split into a separate patch.
* Better support for cancellation - previously a cancelled operation
could be treated as successful.
* Consistently use a signed type for wrapper return values so that
Linux error codes can be returned as well as RMI return values.
v15:
* Wrappers for SRO RMI functions are provided in this patch due to
their dependency on the SRO infrastructure.
* Fold the range delegate/undelegate wrappers into this patch because
they depend on the stateful command infrastructure.
* Add cpu_relax() calls when RMI_BUSY/RMI_BLOCKED is returned.
* Various fixes.
v14:
* SRO support has improved although is still not fully complete. The
infrastructure has been moved out of KVM.
---
drivers/firmware/arm_rmm/rmi.c | 508 +++++++++++++++++++++++++++++++++
include/linux/arm-rmi-cmds.h | 88 ++++++
2 files changed, 596 insertions(+)
diff --git a/drivers/firmware/arm_rmm/rmi.c b/drivers/firmware/arm_rmm/rmi.c
index 76f91c145e1fd..42c973c3a98bb 100644
--- a/drivers/firmware/arm_rmm/rmi.c
+++ b/drivers/firmware/arm_rmm/rmi.c
@@ -6,6 +6,7 @@
#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>
@@ -24,6 +25,513 @@ unsigned long rmi_feat_reg(unsigned long id)
}
EXPORT_SYMBOL_GPL(rmi_feat_reg);
+int rmi_delegate_range(phys_addr_t phys,
+ unsigned long size,
+ phys_addr_t *out_phys)
+{
+ long ret = 0;
+ unsigned long top = phys + size;
+ unsigned long out_top;
+
+ while (phys < top) {
+ ret = rmi_granule_range_delegate(phys, top, &out_top);
+ if (ret == RMI_SUCCESS)
+ 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)
+{
+ 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
+ };
+
+ /*
+ * The output donated count (a1) is always valid, irrespective
+ * of the return result. i.e., 0 if there was an error
+ */
+ 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;
+
+ /*
+ * The RMM specification requires contiguous allocations are always a
+ * power of 2
+ */
+ if (WARN_ON_ONCE(!is_power_of_2(size)))
+ return -EINVAL;
+
+ 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)
+{
+ if (WARN_ON_ONCE(!RMI_DONATE_COUNT(donatereq)))
+ return -EINVAL;
+
+ 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);
+
+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;
+ bool cancelled = false;
+
+ 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) {
+ /*
+ * All memory donating SROs must be cancellable. So a
+ * failure in memory allocation shouldn't be an issue.
+ * However, if we encounter a random failure (e.g.,
+ * buggy RMM), don't loop forever, just give up.
+ */
+ if (WARN_ON_ONCE(!can_cancel))
+ return ret;
+
+ rmi_op_cancel(sro_handle, regs);
+ cancelled = true;
+
+ if (WARN_ON_ONCE(RMI_RETURN_STATUS(regs->a0) != RMI_INCOMPLETE))
+ return ret;
+ }
+ }
+
+ if (cancelled)
+ return -ECANCELED;
+
+ return regs->a0;
+}
+EXPORT_SYMBOL_GPL(rmi_sro_memxfer_execute);
+
+/* For RMI commands that are stateful but not memory-transferring */
+long rmi_sro_execute(struct arm_smccc_1_2_regs *regs)
+{
+ unsigned long sro_handle;
+ bool cancelled = false;
+
+ 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;
+
+ cancelled = true;
+ rmi_op_cancel(sro_handle, regs);
+ }
+ }
+
+ if (cancelled)
+ return -ECANCELED;
+
+ return regs->a0;
+}
+EXPORT_SYMBOL_GPL(rmi_sro_execute);
+
static int rmi_check_version(void)
{
struct arm_smccc_res res;
diff --git a/include/linux/arm-rmi-cmds.h b/include/linux/arm-rmi-cmds.h
index 9aa27697e2377..fed0f3421895d 100644
--- a/include/linux/arm-rmi-cmds.h
+++ b/include/linux/arm-rmi-cmds.h
@@ -10,8 +10,43 @@
#include <linux/bug.h>
#include <linux/types.h>
+#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);
+}
+
+long rmi_sro_memxfer_execute(struct rmi_sro_state *sro, gfp_t gfp);
+void rmi_sro_free(struct rmi_sro_state *sro);
+long rmi_sro_execute(struct arm_smccc_1_2_regs *regs);
+
+#define rmi_sro_memxfer_cmd(sro, gfp, ...) ({ \
+ struct rmi_sro_state *__sro = (sro); \
+ *__sro = (struct rmi_sro_state){ .regs = {__VA_ARGS__} }; \
+ long __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
@@ -48,4 +83,57 @@ static inline int rmi_features(unsigned long index, unsigned long *out)
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: 0 on success, positive RMI result code or negative Linux error code
+ */
+static inline long rmi_granule_range_delegate(unsigned long base,
+ unsigned long top,
+ unsigned long *out_top)
+{
+ struct arm_smccc_1_2_regs regs = {
+ SMC_RMI_GRANULE_RANGE_DELEGATE, base, top
+ };
+ long ret = rmi_sro_execute(®s);
+
+ if (ret == RMI_SUCCESS && 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: 0 on success, positive RMI result code or negative Linux error code
+ */
+static inline long rmi_granule_range_undelegate(unsigned long base,
+ unsigned long top,
+ unsigned long *out_top)
+{
+ struct arm_smccc_1_2_regs regs = {
+ SMC_RMI_GRANULE_RANGE_UNDELEGATE, base, top
+ };
+ long ret = rmi_sro_execute(®s);
+
+ if (ret == RMI_SUCCESS && out_top)
+ *out_top = regs.a1;
+
+ return ret;
+}
+
#endif
--
2.43.0
^ permalink raw reply related [flat|nested] 44+ messages in thread* Re: [PATCH v17 4/7] firmware: arm_rmm: Add support for SRO
2026-09-07 9:59 ` [PATCH v17 4/7] firmware: arm_rmm: Add support for SRO Suzuki K Poulose
@ 2026-09-07 10:14 ` sashiko-bot
2026-09-08 22:10 ` Suzuki K Poulose
2026-09-09 4:10 ` Gavin Shan
1 sibling, 1 reply; 44+ messages in thread
From: sashiko-bot @ 2026-09-07 10:14 UTC (permalink / raw)
To: Suzuki K Poulose; +Cc: Oliver Upton, Marc Zyngier, kvmarm, kvm
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [High] Infinite loop during SRO cancellation if the RMM continues to return an unexpected MEMREQ.
- [High] Missing bounds check on `out_regs->a1` in `rmi_sro_donate_contig` allows an integer overflow that can erroneously free actively consumed memory.
- [High] The error handler for `sro->addr_count` corruption in `rmi_sro_ensure_capacity` triggers an out-of-bounds read and arbitrary free.
- [High] Potential infinite loop in `rmi_delegate_range` and `rmi_undelegate_range` on buggy RMM.
--
commit c980046a949a7ff8e2698f0f01373297576d9896
Author: Steven Price <steven.price@arm.com>
Subject: firmware: arm_rmm: Add support for SRO
This commit introduces support for RMM v2.0 Stateful RMI Operations (SRO), allowing SMCs to return with operations still in progress. It adds struct rmi_sro_state to track memory allocated but not yet consumed or reclaimed, and provides wrappers for SRO RMI commands.
> diff --git a/drivers/firmware/arm_rmm/rmi.c b/drivers/firmware/arm_rmm/rmi.c
> index 76f91c145e1fd..42c973c3a98bb 100644
> --- a/drivers/firmware/arm_rmm/rmi.c
> +++ b/drivers/firmware/arm_rmm/rmi.c
> @@ -24,6 +25,513 @@ unsigned long rmi_feat_reg(unsigned long id)
> }
> EXPORT_SYMBOL_GPL(rmi_feat_reg);
>
> +int rmi_delegate_range(phys_addr_t phys,
> + unsigned long size,
> + phys_addr_t *out_phys)
> +{
> + long ret = 0;
> + unsigned long top = phys + size;
> + unsigned long out_top;
> +
> + while (phys < top) {
> + ret = rmi_granule_range_delegate(phys, top, &out_top);
> + if (ret == RMI_SUCCESS)
> + phys = out_top;
[Severity: High]
Could this loop indefinitely if a buggy RMM firmware returns RMI_SUCCESS but
does not advance out_top beyond phys?
> + 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)
> +{
> + 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;
[Severity: High]
Does this undelegate loop carry the same infinite loop risk as the delegate path
if the hypervisor fails to advance out_top?
> + else if (ret == RMI_BUSY || ret == RMI_BLOCKED)
> + cpu_relax();
> + else
> + break;
> + }
> +
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(rmi_undelegate_range);
[ ... ]
> +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)
> +{
[ ... ]
> + 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;
[Severity: High]
Can donated_granules (out_regs->a1) be an untrusted large value from a
compromised or buggy RMM?
If out_regs->a1 is excessively large, donated_granules << PAGE_SHIFT could
overflow into a small integer value.
> +
> + 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) {
[Severity: High]
If the integer overflow described above occurs, this check
(donated_size < size) will incorrectly pass. Would this lead to the host
allocator erroneously freeing memory that the RMM might still be actively
using?
> + 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_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);
[Severity: High]
Is it safe to pass sro to rmi_sro_free() here when rmi_sro_ensure_capacity()
failed?
If rmi_sro_ensure_capacity() detects corruption and returns -EOVERFLOW because
sro->addr_count > RMI_MAX_ADDR_LIST, calling rmi_sro_free() passes that same
corrupted addr_count to the cleanup loop.
> +
> + 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++) {
[Severity: High]
If sro->addr_count is corrupted and greater than RMI_MAX_ADDR_LIST, as caught
in the error path above, won't this loop use it as an upper bound, leading to
out-of-bounds array reads and arbitrary memory frees?
> + 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);
[ ... ]
> +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;
> + bool cancelled = false;
> +
> + 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)) {
[ ... ]
> + }
> +
> + if (ret) {
> + /*
> + * All memory donating SROs must be cancellable. So a
> + * failure in memory allocation shouldn't be an issue.
> + * However, if we encounter a random failure (e.g.,
> + * buggy RMM), don't loop forever, just give up.
> + */
> + if (WARN_ON_ONCE(!can_cancel))
> + return ret;
> +
> + rmi_op_cancel(sro_handle, regs);
> + cancelled = true;
> +
> + if (WARN_ON_ONCE(RMI_RETURN_STATUS(regs->a0) != RMI_INCOMPLETE))
> + return ret;
[Severity: High]
Does this sequence create an infinite loop on a buggy RMM?
If the RMM returns an unexpected MEMREQ and does not clear the can_cancel bit in
its return status after a cancellation is requested, the loop unconditionally
re-issues the cancel command because it does not check the local cancelled state.
> + }
> + }
> +
> + if (cancelled)
> + return -ECANCELED;
> +
> + return regs->a0;
> +}
> +EXPORT_SYMBOL_GPL(rmi_sro_memxfer_execute);
> +
> +/* For RMI commands that are stateful but not memory-transferring */
> +long rmi_sro_execute(struct arm_smccc_1_2_regs *regs)
> +{
> + unsigned long sro_handle;
> + bool cancelled = false;
> +
> + 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;
> +
> + cancelled = true;
> + rmi_op_cancel(sro_handle, regs);
[Severity: High]
Could the same infinite loop vulnerability occur here for non-memory-transferring
operations if the RMM continuously returns RMI_INCOMPLETE and an unexpected MEMREQ
without clearing can_cancel?
> + }
> + }
> +
> + if (cancelled)
> + return -ECANCELED;
> +
> + return regs->a0;
> +}
> +EXPORT_SYMBOL_GPL(rmi_sro_execute);
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260907095942.1140734-1-suzuki.poulose@arm.com?part=4
^ permalink raw reply [flat|nested] 44+ messages in thread* Re: [PATCH v17 4/7] firmware: arm_rmm: Add support for SRO
2026-09-07 10:14 ` sashiko-bot
@ 2026-09-08 22:10 ` Suzuki K Poulose
0 siblings, 0 replies; 44+ messages in thread
From: Suzuki K Poulose @ 2026-09-08 22:10 UTC (permalink / raw)
To: sashiko-reviews; +Cc: Oliver Upton, Marc Zyngier, kvmarm, kvm
On 07/09/2026 11:14, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
> - [High] Infinite loop during SRO cancellation if the RMM continues to return an unexpected MEMREQ.
> - [High] Missing bounds check on `out_regs->a1` in `rmi_sro_donate_contig` allows an integer overflow that can erroneously free actively consumed memory.
> - [High] The error handler for `sro->addr_count` corruption in `rmi_sro_ensure_capacity` triggers an out-of-bounds read and arbitrary free.
> - [High] Potential infinite loop in `rmi_delegate_range` and `rmi_undelegate_range` on buggy RMM.
> --
>
> commit c980046a949a7ff8e2698f0f01373297576d9896
> Author: Steven Price <steven.price@arm.com>
> Subject: firmware: arm_rmm: Add support for SRO
>
> This commit introduces support for RMM v2.0 Stateful RMI Operations (SRO), allowing SMCs to return with operations still in progress. It adds struct rmi_sro_state to track memory allocated but not yet consumed or reclaimed, and provides wrappers for SRO RMI commands.
>
>> diff --git a/drivers/firmware/arm_rmm/rmi.c b/drivers/firmware/arm_rmm/rmi.c
>> index 76f91c145e1fd..42c973c3a98bb 100644
>> --- a/drivers/firmware/arm_rmm/rmi.c
>> +++ b/drivers/firmware/arm_rmm/rmi.c
>> @@ -24,6 +25,513 @@ unsigned long rmi_feat_reg(unsigned long id)
>> }
>> EXPORT_SYMBOL_GPL(rmi_feat_reg);
>>
>> +int rmi_delegate_range(phys_addr_t phys,
>> + unsigned long size,
>> + phys_addr_t *out_phys)
>> +{
>> + long ret = 0;
>> + unsigned long top = phys + size;
>> + unsigned long out_top;
>> +
>> + while (phys < top) {
>> + ret = rmi_granule_range_delegate(phys, top, &out_top);
>> + if (ret == RMI_SUCCESS)
>> + phys = out_top;
>
> [Severity: High]
> Could this loop indefinitely if a buggy RMM firmware returns RMI_SUCCESS but
> does not advance out_top beyond phys?
>
...
>> +int rmi_undelegate_range(phys_addr_t phys,
>> + unsigned long size)
>> +{
>> + long ret = 0;
>> + unsigned long top = phys + size;
>> + unsigned long out_top;
>> +
>> + while (phys < top) {
>> + ret = rmi_granule_range_undelegate(phys, top, &out_top);
>> + if (ret == RMI_SUCCESS)
>> + phys = out_top;
>
> [Severity: High]
> Does this undelegate loop carry the same infinite loop risk as the delegate path
> if the hypervisor fails to advance out_top?
I have added a WARN_ON() in case these are not advanced and break out
from the code.
>
>> + else if (ret == RMI_BUSY || ret == RMI_BLOCKED)
>> + cpu_relax();
>> + else
>> + break;
>> + }
>> +
>> + return ret;
>> +}
>> +EXPORT_SYMBOL_GPL(rmi_undelegate_range);
>
> [ ... ]
>
>> +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)
>> +{
>
> [ ... ]
>
>> + 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;
>
> [Severity: High]
> Can donated_granules (out_regs->a1) be an untrusted large value from a
> compromised or buggy RMM?
>
> If out_regs->a1 is excessively large, donated_granules << PAGE_SHIFT could
> overflow into a small integer value.
I have added a check to make sure that the donated_granules <= the
granules we passed and clamp it to the size.
>
>> +
>> + 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) {
>
> [Severity: High]
> If the integer overflow described above occurs, this check
> (donated_size < size) will incorrectly pass. Would this lead to the host
> allocator erroneously freeing memory that the RMM might still be actively
> using?
> ]
Addressed as above.
>> + 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_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);
>
> [Severity: High]
> Is it safe to pass sro to rmi_sro_free() here when rmi_sro_ensure_capacity()
> failed?
>
> If rmi_sro_ensure_capacity() detects corruption and returns -EOVERFLOW because
> sro->addr_count > RMI_MAX_ADDR_LIST, calling rmi_sro_free() passes that same
> corrupted addr_count to the cleanup loop.
Added a WARN_ON() and clamped it down to the RMI_MAX_ADDR_LIST in
overflow cases.
>
>> +
>> + capacity = RMI_MAX_ADDR_LIST - sro->addr_count;
>> +
...
>> +void rmi_sro_free(struct rmi_sro_state *sro)
>> +{
>> + for (int i = 0; i < sro->addr_count; i++) {
>
> [Severity: High]
> If sro->addr_count is corrupted and greater than RMI_MAX_ADDR_LIST, as caught
> in the error path above, won't this loop use it as an upper bound, leading to
> out-of-bounds array reads and arbitrary memory frees?
As above.
>
> [ ... ]
>
>> +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;
>> + bool cancelled = false;
>> +
>> + 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)) {
>
> [ ... ]
>
>> + }
>> +
>> + if (ret) {
>> + /*
>> + * All memory donating SROs must be cancellable. So a
>> + * failure in memory allocation shouldn't be an issue.
>> + * However, if we encounter a random failure (e.g.,
>> + * buggy RMM), don't loop forever, just give up.
>> + */
>> + if (WARN_ON_ONCE(!can_cancel))
>> + return ret;
>> +
>> + rmi_op_cancel(sro_handle, regs);
>> + cancelled = true;
>> +
>> + if (WARN_ON_ONCE(RMI_RETURN_STATUS(regs->a0) != RMI_INCOMPLETE))
>> + return ret;
>
> [Severity: High]
> Does this sequence create an infinite loop on a buggy RMM?
>
> If the RMM returns an unexpected MEMREQ and does not clear the can_cancel bit in
> its return status after a cancellation is requested, the loop unconditionally
> re-issues the cancel command because it does not check the local cancelled state.
I have added the following hunk to handle this case.
-
+ /*
+ * If we have already cancelled, and came back
here due
+ * to an error in RECLAIM, then there is no point
+ * in going in loops.
+ */
+ if (WARN_ON_ONCE(cancelled))
+ break;
rmi_op_cancel(sro_handle, regs);
cancelled = true;
>> + }
>> + }
>> +
>> + if (cancelled)
>> + return -ECANCELED;
>> +
>> + return regs->a0;
>> +}
>> +EXPORT_SYMBOL_GPL(rmi_sro_memxfer_execute);
>> +
>> +/* For RMI commands that are stateful but not memory-transferring */
>> +long rmi_sro_execute(struct arm_smccc_1_2_regs *regs)
>> +{
>> + unsigned long sro_handle;
>> + bool cancelled = false;
>> +
>> + 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;
>> +
>> + cancelled = true;
>> + rmi_op_cancel(sro_handle, regs);
>
> [Severity: High]
> Could the same infinite loop vulnerability occur here for non-memory-transferring
> operations if the RMM continuously returns RMI_INCOMPLETE and an unexpected MEMREQ
> without clearing can_cancel?
No, a NON memory transferring SRO will always be that category. So a
MEMREQ cannot come. I have added a similar check as above here too.
Suzuki
^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH v17 4/7] firmware: arm_rmm: Add support for SRO
2026-09-07 9:59 ` [PATCH v17 4/7] firmware: arm_rmm: Add support for SRO Suzuki K Poulose
2026-09-07 10:14 ` sashiko-bot
@ 2026-09-09 4:10 ` Gavin Shan
2026-09-10 9:51 ` Suzuki K Poulose
1 sibling, 1 reply; 44+ messages in thread
From: Gavin Shan @ 2026-09-09 4:10 UTC (permalink / raw)
To: Suzuki K Poulose, kvm, kvmarm
Cc: maz, will, catalin.marinas, linux-kernel, linux-arm-kernel,
steven.price, aneesh.kumar, oupton, joey.gouly, tabba, yuzenghui,
linux-coco, gankulkarni, sdonthineni, alpergun, fj0570is,
WeiLin.Chang, lpieralisi, enju.kohei
Hi Suzuki,
On 9/7/26 7:59 PM, Suzuki K Poulose wrote:
> From: Steven Price <steven.price@arm.com>
>
> RMM v2.0 introduces the concept of "Stateful RMI Operations" (SRO). This
> means that an SMC can return with an operation still in progress. The
> host is expected to continue the operation until it reaches a conclusion
> (either success or failure). During this process the RMM can request
> additional memory ('donate') or hand memory back to the host
> ('reclaim'). The host can request an in progress operation is cancelled,
> but still continue the operation until it has completed (otherwise the
> incomplete operation may cause future RMM operations to fail).
>
> The SRO is tracked using a struct rmi_sro_state object which keeps track
> of any memory which has been allocated but not yet consumed by the RMM
> or reclaimed from the RMM. This allows the memory to be reused in a
> future request within the same operation. It will also permit an
> operation to be done in a context where memory allocation may be
> difficult (e.g. atomic context) with the option to abort the operation
> and retry the memory allocation outside of the atomic context. The
> memory stored in the struct rmi_sro_state object can then be reused on
> the subsequent attempt.
>
> Wrappers for SRO RMI commands are also provided here because they depend
> on the rmi_sro_execute() implementation added by this patch.
> Delegate/undelegate handles are also added here because they now use the
> SRO/stateful command infrastructure.
>
> Signed-off-by: Steven Price <steven.price@arm.com>
> Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
> ---
> v17:
> * Handle buggy RMM firmware to avoid looping forever for non-cancellable SROs.
> * Add comment (for the AI agents) to clarify that all memory donating SROs are
> cancellable.
> v16:
> * Wrappers for realm guests split into a separate patch.
> * Better support for cancellation - previously a cancelled operation
> could be treated as successful.
> * Consistently use a signed type for wrapper return values so that
> Linux error codes can be returned as well as RMI return values.
> v15:
> * Wrappers for SRO RMI functions are provided in this patch due to
> their dependency on the SRO infrastructure.
> * Fold the range delegate/undelegate wrappers into this patch because
> they depend on the stateful command infrastructure.
> * Add cpu_relax() calls when RMI_BUSY/RMI_BLOCKED is returned.
> * Various fixes.
> v14:
> * SRO support has improved although is still not fully complete. The
> infrastructure has been moved out of KVM.
> ---
> drivers/firmware/arm_rmm/rmi.c | 508 +++++++++++++++++++++++++++++++++
> include/linux/arm-rmi-cmds.h | 88 ++++++
> 2 files changed, 596 insertions(+)
>
> diff --git a/drivers/firmware/arm_rmm/rmi.c b/drivers/firmware/arm_rmm/rmi.c
> index 76f91c145e1fd..42c973c3a98bb 100644
> --- a/drivers/firmware/arm_rmm/rmi.c
> +++ b/drivers/firmware/arm_rmm/rmi.c
> @@ -6,6 +6,7 @@
> #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>
> @@ -24,6 +25,513 @@ unsigned long rmi_feat_reg(unsigned long id)
> }
> EXPORT_SYMBOL_GPL(rmi_feat_reg);
>
> +int rmi_delegate_range(phys_addr_t phys,
> + unsigned long size,
> + phys_addr_t *out_phys)
> +{
> + long ret = 0;
> + unsigned long top = phys + size;
> + unsigned long out_top;
> +
> + while (phys < top) {
> + ret = rmi_granule_range_delegate(phys, top, &out_top);
> + if (ret == RMI_SUCCESS)
> + 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);
> +
rmi_granule_range_delegate() can't return RMI_BUSY or RMI_BLOCKED as those two
error statuses are filtered out by inner call rmi_smccc_invoke(). So it's not
needed to have "else if (ret == RMI_BUSY || ret == RMI_BLOCKED) cpu_relax()"
here.
Besides, 'long ret' is truncated to 'int' by 'return ret'. I think we need a
helper to convert RMI error status to the linux error code, something like
below. The newly added helper rmi_to_linux_errno() is used by rmi_sro_memxfer_execute()
and rmi_sro_execute() where the return values are 'int' (not 'long' any more).
static int rmi_errno_map[] = {
[RMI_SUCCESS] = 0,
[RMI_ERROR_INPUT] = -EINVAL,
[RMI_ERROR_REALM] = -EBADF,
[RMI_ERROR_REC] = -EBADF,
[RMI_ERROR_RTT] = -EBADF,
[RMI_ERROR_NOT_SUPPORTED] = -EOPNOTSUPP,
[RMI_ERROR_DEVICE] = -EBADF,
[RMI_ERROR_RTT_AUX] = -EBADF,
[RMI_ERROR_PSMMU_ST] = -EBADF,
[RMI_ERROR_DPT] = -EBADF,
[RMI_BUSY] = -EBUSY,
[RMI_ERROR_GLOBAL] = -ENOSYS,
[RMI_ERROR_TRACKING] = -EBADF,
[RMI_INCOMPLETE] = -EINPROGRESS,
[RMI_BLOCKED] = -EAGAIN,
[RMI_ERROR_GPT] = -EBADF,
[RMI_ERROR_GRANULE] = -EBADF,
};
int rmi_to_linux_errno(unsigned long status)
{
status = RMI_RETURN_STATUS(status);
return (status < ARRAY_SIZE(rmi_errno_maps)) ? rmi_errno_map[status] : -EINVAL;
}
EXPORT_SYMBOL_GPL(rmi_to_linux_errno);
> +int rmi_undelegate_range(phys_addr_t phys,
> + unsigned long size)
> +{
> + long ret = 0;
> + unsigned long top = phys + size;
> + unsigned long out_top;
> +
> + while (phys < top) {
> + ret = rmi_granule_range_undelegate(phys, top, &out_top);
> + if (ret == RMI_SUCCESS)
> + 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));
> +}
> +
I would rename this helper to explicitly indicate it's going to get
the block size.
static unsigned long donate_req_to_block_size(unsigned long req)
{
unsigned long block_size_encode = RMI_DONATE_BLOCK_SIZE(req);
return BIT(ARM64_HW_PGTABLE_LEVEL_SHIFT(3 - block_size_encode));
}
> +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
> + };
> +
> + /*
> + * The output donated count (a1) is always valid, irrespective
> + * of the return result. i.e., 0 if there was an error
> + */
> + 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);
> +}
> +
The local variable @regs in rmi_op_{continue, cancel}() and rmi_op_mem_{donate, recliam}() can
be avoided since rmi_smccc_invoke() has two variables for input and output separately. So
rmi_op_continue() can be improved as below. Other 3 functions can be improved in similar ways.
static void rmi_op_continue(unsigned long sro_handle, unsigned long flags,
struct arm_smccc_1_2_regs *out_regs)
{
out_regs->a0 = SMC_RMI_OP_CONTINUE;
out_regs->a1 = sro_handle;
out_regs->a2 = flags;
rmi_smccc_invoke(out_regs, 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);
> +
How about renaming this to rmi_free_delegated_page()? It seems all functions exposed by
rmi.c have prefix 'rmi'.
> +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;
> +
s/unit_size/block_size_encode
s/unit_size_bytes/block_size
Please move 'donated_size' to the begining of this function.
unsigned long addr_range, donated_size;
> + /*
> + * The RMM specification requires contiguous allocations are always a
> + * power of 2
> + */
> + if (WARN_ON_ONCE(!is_power_of_2(size)))
> + return -EINVAL;
> +
> + 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;
> + }
> + }
> +
The search in the address range array may deserve a comment, but I doubt how much
benefits (hit ratio) the array can give to us :-)
/* Reuse the cached address range if we have one */
Besides, 'int i' needs to be 'unsigned long i' because 'struct mi_sro_state::addr_count'
is 'unsigned long'. Alternative, we may change 'struct mi_sro_state::addr_count' to
'int'.
> + 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;
> +
We actually requires that 'phys' in the range specified by RMI_ADDR_RANGE_ADDR_MASK, so:
WARN_ON_ONCE(phys & ~RMI_ADDR_RANGE_ADDR_MASK);
addr_range = phys;
> +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));
> + }
> + }
> +
'i' was used previouly and I would avoid using it again. I would suggest to simplify
this chunk of code, as below. Another question is if we need to check if RMI_SUCCESS
is returned from rmi_op_mem_donate()?
donated_size = PFN_PHYS(out_regs->a1);
/* All granules are consumed by RMM */
if (donated_size == size)
return 0;
/* No granules are consumed by RMM, cache all granules */
if (donated_size == 0) {
sro->addr_count++;
return 0;
}
/*
* The granules are partially consumed by RMM, delegate and release
* the unused granules.
*/
phys = sro->addr_list[sro->addr_count] & RMI_ADDR_RANGE_ADDR_MASK;
while (donated_size < size) {
if (state == RMI_OP_MEM_DELEGATED)
rmi_free_delegated_page(phys + donated_size);
else
__free_page(phys_to_page(phys + donated_size));
donated_size += PAGE_SIZE;
}
return 0;
> + 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;
> +
s/unit_size/block_size_encode
s/unit_size/block_size
> + 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--;
> + }
> + }
> +
The type of 'i' is different to 'addr_list_start' and 'sro->addr_count'.
> + 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);
> +
The local variable 'found' looks redundant and can be dropped. With 'found' dropped,
we need:
while (sro->addr_count - addr_list_start < count) {
:
}
rmi_op_mem_donate(sro_handle,
virt_to_phys(&sro->addr_list[addr_list_start]),
count, 0, out_regs);
> + unsigned long donated_granules = out_regs->a1;
> + unsigned long granules_per_unit = unit_size_bytes >> PAGE_SHIFT;
> + unsigned long consumed_units;
> +
s/granules_per_unit/granules_per_block
s/consumed_units/consumed_blocks
> + /*
> + * 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)
> +{
> + if (WARN_ON_ONCE(!RMI_DONATE_COUNT(donatereq)))
> + return -EINVAL;
> +
> + 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);
> + }
The nested if statements can be avoided by:
if (state == RMI_OP_MEM_DELEGATED &&
WARN_ON_ONCE(rmi_undelegate_range(addr, size)) {
/* Leak the granules */
continue;
}
free_pages_exact(phys_to_virt(addr), size);
> +
> + sro->addr_count = 0;
> +}
> +EXPORT_SYMBOL_GPL(rmi_sro_free);
> +
> +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;
> + bool cancelled = false;
> +
> + 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;
> +
Strictly speaking, we need to refresh the SRO handle after every RMI call.
bool can_cancel = RMI_RETURN_CAN_CANCEL(regs->a0);
unsigned long sro_handle = regs->a1;
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) {
> + /*
> + * All memory donating SROs must be cancellable. So a
> + * failure in memory allocation shouldn't be an issue.
> + * However, if we encounter a random failure (e.g.,
> + * buggy RMM), don't loop forever, just give up.
> + */
> + if (WARN_ON_ONCE(!can_cancel))
> + return ret;
> +
> + rmi_op_cancel(sro_handle, regs);
> + cancelled = true;
> +
> + if (WARN_ON_ONCE(RMI_RETURN_STATUS(regs->a0) != RMI_INCOMPLETE))
> + return ret;
> + }
> + }
> +
> + if (cancelled)
> + return -ECANCELED;
> +
> + return regs->a0;
> +}
> +EXPORT_SYMBOL_GPL(rmi_sro_memxfer_execute);
> +
> +/* For RMI commands that are stateful but not memory-transferring */
> +long rmi_sro_execute(struct arm_smccc_1_2_regs *regs)
> +{
> + unsigned long sro_handle;
> + bool cancelled = false;
> +
> + 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);
> +
Strictly speaking, we need to refresh the SRO handle after every RMI call.
bool can_cancel = RMI_RETURN_CAN_CANCEL(regs->a0);
unsigned long sro_handle = regs->a1;
> + 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;
> +
> + cancelled = true;
> + rmi_op_cancel(sro_handle, regs);
> + }
> + }
> +
> + if (cancelled)
> + return -ECANCELED;
> +
> + return regs->a0;
> +}
> +EXPORT_SYMBOL_GPL(rmi_sro_execute);
> +
> static int rmi_check_version(void)
> {
> struct arm_smccc_res res;
> diff --git a/include/linux/arm-rmi-cmds.h b/include/linux/arm-rmi-cmds.h
> index 9aa27697e2377..fed0f3421895d 100644
> --- a/include/linux/arm-rmi-cmds.h
> +++ b/include/linux/arm-rmi-cmds.h
> @@ -10,8 +10,43 @@
> #include <linux/bug.h>
> #include <linux/types.h>
>
> +#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);
> +}
> +
> +long rmi_sro_memxfer_execute(struct rmi_sro_state *sro, gfp_t gfp);
> +void rmi_sro_free(struct rmi_sro_state *sro);
> +long rmi_sro_execute(struct arm_smccc_1_2_regs *regs);
> +
> +#define rmi_sro_memxfer_cmd(sro, gfp, ...) ({ \
> + struct rmi_sro_state *__sro = (sro); \
> + *__sro = (struct rmi_sro_state){ .regs = {__VA_ARGS__} }; \
> + long __ret = rmi_sro_memxfer_execute(__sro, gfp); \
> + rmi_sro_free(__sro); \
> + __ret; \
> +})
> +
The temporary storage space for 'struct rmi_sro_state' in the stack due to
'*__sro = (struct rmi_sro_state){ .regs = {__VA_ARGS__} };' can be avoided
by:
__sro->regs = {__VA_ARGS__};
> /**
> * rmi_rmm_config_set() - Configure the RMM
> * @cfg_ptr: PA of a struct rmm_config
> @@ -48,4 +83,57 @@ static inline int rmi_features(unsigned long index, unsigned long *out)
> 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: 0 on success, positive RMI result code or negative Linux error code
> + */
> +static inline long rmi_granule_range_delegate(unsigned long base,
> + unsigned long top,
> + unsigned long *out_top)
> +{
> + struct arm_smccc_1_2_regs regs = {
> + SMC_RMI_GRANULE_RANGE_DELEGATE, base, top
> + };
> + long ret = rmi_sro_execute(®s);
> +
> + if (ret == RMI_SUCCESS && out_top)
> + *out_top = regs.a1;
> +
> + return ret;
> +}
> +
It seems rmi_granule_range_delegate() is used for once by rmi.c::rmi_delegate_range().
If so, we needn't to expose the function. The logic here can be combined to
rmi.c::rmi_delegate_range().
> +/**
> + * 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: 0 on success, positive RMI result code or negative Linux error code
> + */
> +static inline long rmi_granule_range_undelegate(unsigned long base,
> + unsigned long top,
> + unsigned long *out_top)
> +{
> + struct arm_smccc_1_2_regs regs = {
> + SMC_RMI_GRANULE_RANGE_UNDELEGATE, base, top
> + };
> + long ret = rmi_sro_execute(®s);
> +
> + if (ret == RMI_SUCCESS && out_top)
> + *out_top = regs.a1;
> +
> + return ret;
> +}
> +
It seems rmi_granule_range_undelegate() is used for once by rmi.c::rmi_undelegate_range().
If so, we needn't expose the function. The logic here can be combined to rmi_undelegate_range().
> #endif
Thanks,
Gavin
^ permalink raw reply [flat|nested] 44+ messages in thread* Re: [PATCH v17 4/7] firmware: arm_rmm: Add support for SRO
2026-09-09 4:10 ` Gavin Shan
@ 2026-09-10 9:51 ` Suzuki K Poulose
0 siblings, 0 replies; 44+ messages in thread
From: Suzuki K Poulose @ 2026-09-10 9:51 UTC (permalink / raw)
To: Gavin Shan, kvm, kvmarm
Cc: maz, will, catalin.marinas, linux-kernel, linux-arm-kernel,
steven.price, aneesh.kumar, oupton, joey.gouly, tabba, yuzenghui,
linux-coco, gankulkarni, sdonthineni, alpergun, fj0570is,
WeiLin.Chang, lpieralisi, enju.kohei
Hi Gavin
Thank you for the the review, much appreciated. Responses inline.
On 09/09/2026 05:10, Gavin Shan wrote:
> Hi Suzuki,
>
> On 9/7/26 7:59 PM, Suzuki K Poulose wrote:
>> From: Steven Price <steven.price@arm.com>
>>
>> RMM v2.0 introduces the concept of "Stateful RMI Operations" (SRO). This
>> means that an SMC can return with an operation still in progress. The
>> host is expected to continue the operation until it reaches a conclusion
>> (either success or failure). During this process the RMM can request
>> additional memory ('donate') or hand memory back to the host
>> ('reclaim'). The host can request an in progress operation is cancelled,
>> but still continue the operation until it has completed (otherwise the
>> incomplete operation may cause future RMM operations to fail).
>>
>> The SRO is tracked using a struct rmi_sro_state object which keeps track
>> of any memory which has been allocated but not yet consumed by the RMM
>> or reclaimed from the RMM. This allows the memory to be reused in a
>> future request within the same operation. It will also permit an
>> operation to be done in a context where memory allocation may be
>> difficult (e.g. atomic context) with the option to abort the operation
>> and retry the memory allocation outside of the atomic context. The
>> memory stored in the struct rmi_sro_state object can then be reused on
>> the subsequent attempt.
>>
>> Wrappers for SRO RMI commands are also provided here because they depend
>> on the rmi_sro_execute() implementation added by this patch.
>> Delegate/undelegate handles are also added here because they now use the
>> SRO/stateful command infrastructure.
>>
>> Signed-off-by: Steven Price <steven.price@arm.com>
>> Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
>> ---
...
>>
>> diff --git a/drivers/firmware/arm_rmm/rmi.c b/drivers/firmware/
>> arm_rmm/rmi.c
>> index 76f91c145e1fd..42c973c3a98bb 100644
>> --- a/drivers/firmware/arm_rmm/rmi.c
>> +++ b/drivers/firmware/arm_rmm/rmi.c
...
>> +int rmi_delegate_range(phys_addr_t phys,
>> + unsigned long size,
>> + phys_addr_t *out_phys)
>> +{
>> + long ret = 0;
>> + unsigned long top = phys + size;
>> + unsigned long out_top;
>> +
>> + while (phys < top) {
>> + ret = rmi_granule_range_delegate(phys, top, &out_top);
>> + if (ret == RMI_SUCCESS)
>> + 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);
>> +
>
> rmi_granule_range_delegate() can't return RMI_BUSY or RMI_BLOCKED as
> those two
> error statuses are filtered out by inner call rmi_smccc_invoke(). So
> it's not
> needed to have "else if (ret == RMI_BUSY || ret == RMI_BLOCKED)
> cpu_relax()"
> here.
Ack
>
> Besides, 'long ret' is truncated to 'int' by 'return ret'. I think we
> need a
> helper to convert RMI error status to the linux error code, something like
> below. The newly added helper rmi_to_linux_errno() is used by
> rmi_sro_memxfer_execute()
> and rmi_sro_execute() where the return values are 'int' (not 'long' any
> more).
SRO operations return the full set of results from the execution in the
out_regs/sro->regs. So they can make the full use of the results, if
required.
As for the truncation, we don't return the partial results to the
callers of the rmi_*delegate_range helpers, but they are consumed
internally (e.g., by the SRO execution). The callers care about the
return "status" which is still an 8bit field. We could extend this
if we need it in the future.
>
> static int rmi_errno_map[] = {
> [RMI_SUCCESS] = 0,
> [RMI_ERROR_INPUT] = -EINVAL,
> [RMI_ERROR_REALM] = -EBADF,
> [RMI_ERROR_REC] = -EBADF,
> [RMI_ERROR_RTT] = -EBADF,
> [RMI_ERROR_NOT_SUPPORTED] = -EOPNOTSUPP,
> [RMI_ERROR_DEVICE] = -EBADF,
> [RMI_ERROR_RTT_AUX] = -EBADF,
> [RMI_ERROR_PSMMU_ST] = -EBADF,
> [RMI_ERROR_DPT] = -EBADF,
> [RMI_BUSY] = -EBUSY,
> [RMI_ERROR_GLOBAL] = -ENOSYS,
> [RMI_ERROR_TRACKING] = -EBADF,
> [RMI_INCOMPLETE] = -EINPROGRESS,
> [RMI_BLOCKED] = -EAGAIN,
> [RMI_ERROR_GPT] = -EBADF,
> [RMI_ERROR_GRANULE] = -EBADF,
> };
>
This may be useful, but don't see the need for that now.
> int rmi_to_linux_errno(unsigned long status)
> {
> status = RMI_RETURN_STATUS(status);
>
> return (status < ARRAY_SIZE(rmi_errno_maps)) ?
> rmi_errno_map[status] : -EINVAL;
> }
> EXPORT_SYMBOL_GPL(rmi_to_linux_errno);
>
>> +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));
>> +}
>> +
>
> I would rename this helper to explicitly indicate it's going to get
> the block size.
>
> static unsigned long donate_req_to_block_size(unsigned long req)
> {
> unsigned long block_size_encode = RMI_DONATE_BLOCK_SIZE(req);
>
> return BIT(ARM64_HW_PGTABLE_LEVEL_SHIFT(3 - block_size_encode));
> }
>
Ack:
I have cleaned this up a little bit by explicitly calling out the
conversion of RmiAddresBlockSize in a wrapper. i.e.,
/*
* Convert the RmiAddrBlockSize to actual size. This is used in
RmiDonateReq
* and RmiAddrRangeDesc*.
*/
static unsigned long rmi_addr_block_size_to_bytes(unsigned long
block_size_fld)
{
return BIT(ARM64_HW_PGTABLE_LEVEL_SHIFT(3 - block_size_fld));
}
static unsigned long donate_req_to_block_size(unsigned long donatereq)
{
return
rmi_addr_block_size_to_bytes(RMI_DONATE_BLOCK_SIZE(donatereq));
}
So that it doesn't look awkward to do something like :
>> + unsigned long size = donate_req_to_size(unit_size) * count;
instead:
unsigned long size = rmi_addr_block_size_to_bytes(unit_size) * count;
>> +static void rmi_smccc_invoke(struct arm_smccc_1_2_regs *regs_in,
...
>> +}
>> +
>
> The local variable @regs in rmi_op_{continue, cancel}() and
> rmi_op_mem_{donate, recliam}() can
> be avoided since rmi_smccc_invoke() has two variables for input and
> output separately. So
> rmi_op_continue() can be improved as below. Other 3 functions can be
> improved in similar ways.
>
> static void rmi_op_continue(unsigned long sro_handle, unsigned long flags,
> struct arm_smccc_1_2_regs *out_regs)
> {
> out_regs->a0 = SMC_RMI_OP_CONTINUE;
> out_regs->a1 = sro_handle;
> out_regs->a2 = flags;
This may not be sufficient, as we need to zero out the entire arguments
for RES0 requirement. But could do something like :
*out_regs = (struct arm_smccc_1_2_regs) { SMC_RMI_OP_CONTINUE,
sro_handle, flags };
>
> rmi_smccc_invoke(out_regs, 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);
>> +
>
> How about renaming this to rmi_free_delegated_page()? It seems all
> functions exposed by
> rmi.c have prefix 'rmi'.
This is in my internal tree for the next version already, thanks for
pointing that out.
>
>> +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;
>> +
>
> s/unit_size/block_size_encode
chose, block_size_fld
> s/unit_size_bytes/block_size
> Please move 'donated_size' to the begining of this function.
>
> unsigned long addr_range, donated_size;
Ack
>
>> + /*
>> + * The RMM specification requires contiguous allocations are
>> always a
>> + * power of 2
>> + */
>> + if (WARN_ON_ONCE(!is_power_of_2(size)))
>> + return -EINVAL;
>> +
>> + 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;
>> + }
>> + }
>> +
>
> The search in the address range array may deserve a comment, but I doubt
> how much
> benefits (hit ratio) the array can give to us :-)
>
> /* Reuse the cached address range if we have one */
Ack
>
> Besides, 'int i' needs to be 'unsigned long i' because 'struct
> mi_sro_state::addr_count'
> is 'unsigned long'. Alternative, we may change 'struct
> mi_sro_state::addr_count' to
> 'int'.
I have changed the addr_count to "int". That gives us a better way to
handle corruptions in the sro->addr_count. i.e, addr_count < 0 vs
addr_count > RMI_MAX_ADDR_LIST
>
>> + 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;
>> +
>
> We actually requires that 'phys' in the range specified by
> RMI_ADDR_RANGE_ADDR_MASK, so:
>
> WARN_ON_ONCE(phys & ~RMI_ADDR_RANGE_ADDR_MASK);
> addr_range = phys;
Do we support more than 52bit ? Also this comes from virt_to_phys(),
which means the kernel has to first support the phys > 52bit. And the
ADDR_MASK is based on PAGE_SHIFT. So, if that breaks, we have bigger
problems.
>
>> +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));
>> + }
>> + }
>> +
>
> 'i' was used previouly and I would avoid using it again. I would suggest
> to simplify
> this chunk of code, as below.
I have dropped that hunk and replaced them with a new helper :
free_addr_range_entry(), that can free an address range, bit more
efficiently (batching granule_undelegate) and even handle a partially
consumed entry. This will be reused for contig/non-contig and the
rmi_sro_free().
Another question is if we need to check if
> RMI_SUCCESS
> is returned from rmi_op_mem_donate()?
>
> donated_size = PFN_PHYS(out_regs->a1);
>
> /* All granules are consumed by RMM */
> if (donated_size == size)
> return 0;
>
> /* No granules are consumed by RMM, cache all granules */
> if (donated_size == 0) {
> sro->addr_count++;
> return 0;
> }
>
Ack, that is much clearer.
> /*
> * The granules are partially consumed by RMM, delegate and release
> * the unused granules.
> */
> phys = sro->addr_list[sro->addr_count] & RMI_ADDR_RANGE_ADDR_MASK;
> while (donated_size < size) {
> if (state == RMI_OP_MEM_DELEGATED)
> rmi_free_delegated_page(phys + donated_size);
> else
> __free_page(phys_to_page(phys + donated_size));
>
> donated_size += PAGE_SIZE;
> }
>
> return 0;
>
>> + 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;
>> +
>
> s/unit_size/block_size_encode
> s/unit_size/block_size
Ack, same as above.
>
>> + 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--;
>> + }
>> + }
>> +
>
> The type of 'i' is different to 'addr_list_start' and 'sro->addr_count'.
As above, switched to i, and I will move addr_list_start to int.
>
>> + 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);
>> +
>
> The local variable 'found' looks redundant and can be dropped. With
> 'found' dropped,
> we need:
May be, but it is easier to reason with that variable. So I prefer to
keep it for the loop.
>
> while (sro->addr_count - addr_list_start < count) {
> :
> }
>
> rmi_op_mem_donate(sro_handle,
> virt_to_phys(&sro->addr_list[addr_list_start]),
> count, 0, out_regs);
Ack for the switch to count here.
>
>> + unsigned long donated_granules = out_regs->a1;
>> + unsigned long granules_per_unit = unit_size_bytes >> PAGE_SHIFT;
>> + unsigned long consumed_units;
>> +
>
> s/granules_per_unit/granules_per_block
> s/consumed_units/consumed_blocks
Ack
>> +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);
>> + }
>
> The nested if statements can be avoided by:
>
> if (state == RMI_OP_MEM_DELEGATED &&
> WARN_ON_ONCE(rmi_undelegate_range(addr, size)) {
> /* Leak the granules */
> continue;
> }
>
> free_pages_exact(phys_to_virt(addr), size);
As mentioned above, this is now in a common helper.
>
>> +
>> + sro->addr_count = 0;
>> +}
>> +EXPORT_SYMBOL_GPL(rmi_sro_free);
>> +
>> +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;
>> + bool cancelled = false;
>> +
>> + 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;
>> +
>
> Strictly speaking, we need to refresh the SRO handle after every RMI call.
>
> bool can_cancel = RMI_RETURN_CAN_CANCEL(regs->a0);
> unsigned long sro_handle = regs->a1;
> int ret = 0;
>
Ack for both instances
>> +
>> + 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);
>> +
>
> Strictly speaking, we need to refresh the SRO handle after every RMI call.
>
> bool can_cancel = RMI_RETURN_CAN_CANCEL(regs->a0);
> unsigned long sro_handle = regs->a1;
>
>
as above.
/types.h>
>> +#define RMI_MAX_ADDR_LIST 256
>> +
ng 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__} }; \
>> + long __ret = rmi_sro_memxfer_execute(__sro, gfp); \
>> + rmi_sro_free(__sro); \
>> + __ret; \
>> +})
>> +
>
> The temporary storage space for 'struct rmi_sro_state' in the stack due to
> '*__sro = (struct rmi_sro_state){ .regs = {__VA_ARGS__} };' can be avoided
> by:
>
> __sro->regs = {__VA_ARGS__};
Have got this already in, based on Sashiko review.
>> +static inline long rmi_granule_range_delegate(unsigned long base,
>> + unsigned long top,
>> + unsigned long *out_top)
>> +{
>> + struct arm_smccc_1_2_regs regs = {
>> + SMC_RMI_GRANULE_RANGE_DELEGATE, base, top
>> + };
>> + long ret = rmi_sro_execute(®s);
>> +
>> + if (ret == RMI_SUCCESS && out_top)
>> + *out_top = regs.a1;
>> +
>> + return ret;
>> +}
>> +
>
> It seems rmi_granule_range_delegate() is used for once by
> rmi.c::rmi_delegate_range().
> If so, we needn't to expose the function. The logic here can be combined to
> rmi.c::rmi_delegate_range().
>
>> +/**
>> + * rmi_granule_range_undelegate() - Undelegate a range of granules
...
>> +
>
> It seems rmi_granule_range_undelegate() is used for once by
> rmi.c::rmi_undelegate_range().
> If so, we needn't expose the function. The logic here can be combined to
> rmi_undelegate_range().
Ack
Thanks
Suzuki
^ permalink raw reply [flat|nested] 44+ messages in thread
* [PATCH v17 5/7] firmware: arm_rmm: Activate the RMM
2026-09-07 9:59 [PATCH v17 0/7] firmware: arm_rmm: Add RMM v2.0 base RMI support Suzuki K Poulose
` (3 preceding siblings ...)
2026-09-07 9:59 ` [PATCH v17 4/7] firmware: arm_rmm: Add support for SRO Suzuki K Poulose
@ 2026-09-07 9:59 ` Suzuki K Poulose
2026-09-07 10:17 ` sashiko-bot
2026-09-09 4:29 ` Gavin Shan
2026-09-07 9:59 ` [PATCH v17 6/7] firmware: arm_rmm: Ensure the RMM has GPT entries for memory Suzuki K Poulose
` (2 subsequent siblings)
7 siblings, 2 replies; 44+ messages in thread
From: Suzuki K Poulose @ 2026-09-07 9:59 UTC (permalink / raw)
To: kvm, kvmarm
Cc: maz, will, catalin.marinas, linux-kernel, linux-arm-kernel,
steven.price, aneesh.kumar, oupton, gshan, joey.gouly, tabba,
yuzenghui, linux-coco, gankulkarni, sdonthineni, alpergun,
fj0570is, WeiLin.Chang, lpieralisi, enju.kohei, Suzuki K Poulose
From: Steven Price <steven.price@arm.com>
Activate the RMM after the basic configuration. This is a memory transferring,
stateful operation.
Signed-off-by: Steven Price <steven.price@arm.com>
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
Changes since v16:
* Split into a new patch
---
drivers/firmware/arm_rmm/rmi.c | 17 +++++++++++++++--
include/linux/arm-rmi-cmds.h | 11 +++++++++++
2 files changed, 26 insertions(+), 2 deletions(-)
diff --git a/drivers/firmware/arm_rmm/rmi.c b/drivers/firmware/arm_rmm/rmi.c
index 42c973c3a98bb..d969c8738efde 100644
--- a/drivers/firmware/arm_rmm/rmi.c
+++ b/drivers/firmware/arm_rmm/rmi.c
@@ -641,7 +641,8 @@ static int rmi_configure(void)
static int __init arm64_init_rmi(void)
{
- int ret;
+ int ret = 0;
+ struct rmi_sro_state *sro = NULL;
/* Continue without realm support if we can't agree on a version */
ret = rmi_check_version();
@@ -656,7 +657,19 @@ static int __init arm64_init_rmi(void)
if (ret)
return ret;
- return 0;
+ /* Activate the RMM */
+ sro = kmalloc_obj(*sro);
+ if (!sro)
+ return -ENOMEM;
+
+ ret = rmi_rmm_activate(sro);
+ if (ret) {
+ pr_err("RMM activate failed\n");
+ ret = ret < 0 ? ret : -ENXIO;
+ }
+
+ kfree(sro);
+ return ret;
}
/*
diff --git a/include/linux/arm-rmi-cmds.h b/include/linux/arm-rmi-cmds.h
index fed0f3421895d..dea7c7004d35f 100644
--- a/include/linux/arm-rmi-cmds.h
+++ b/include/linux/arm-rmi-cmds.h
@@ -64,6 +64,17 @@ static inline int rmi_rmm_config_set(unsigned long cfg_ptr)
return res.a0;
}
+/**
+ * rmi_rmm_activate() - Activate the RMM
+ * @sro: Preallocated SRO context to be used
+ *
+ * Return: 0 on success, positive RMI result code or negative Linux error code
+ */
+static inline long rmi_rmm_activate(struct rmi_sro_state *sro)
+{
+ return rmi_sro_memxfer_cmd(sro, GFP_KERNEL, SMC_RMI_RMM_ACTIVATE);
+}
+
/**
* rmi_features() - Read feature register
* @index: Feature register index
--
2.43.0
^ permalink raw reply related [flat|nested] 44+ messages in thread* Re: [PATCH v17 5/7] firmware: arm_rmm: Activate the RMM
2026-09-07 9:59 ` [PATCH v17 5/7] firmware: arm_rmm: Activate the RMM Suzuki K Poulose
@ 2026-09-07 10:17 ` sashiko-bot
2026-09-07 16:16 ` Suzuki K Poulose
2026-09-09 4:29 ` Gavin Shan
1 sibling, 1 reply; 44+ messages in thread
From: sashiko-bot @ 2026-09-07 10:17 UTC (permalink / raw)
To: Suzuki K Poulose; +Cc: Oliver Upton, kvm, Marc Zyngier, kvmarm
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] The macro `rmi_sro_memxfer_cmd` uses a large compound literal to initialize the SRO state, forcing the compiler to allocate a ~2.2KB temporary object on the stack.
--
commit c773f8f03d602782404ec2f0103977a8f86f3b3e
Author: Steven Price <steven.price@arm.com>
firmware: arm_rmm: Activate the RMM
Activate the RMM after the basic configuration. This is a memory transferring,
stateful operation.
> diff --git a/drivers/firmware/arm_rmm/rmi.c b/drivers/firmware/arm_rmm/rmi.c
> index 42c973c3a98bb..d969c8738efde 100644
> --- a/drivers/firmware/arm_rmm/rmi.c
> +++ b/drivers/firmware/arm_rmm/rmi.c
[ ... ]
> @@ -656,7 +657,19 @@ static int __init arm64_init_rmi(void)
> if (ret)
> return ret;
>
> - return 0;
> + /* Activate the RMM */
> + sro = kmalloc_obj(*sro);
> + if (!sro)
> + return -ENOMEM;
> +
> + ret = rmi_rmm_activate(sro);
[Severity: Medium]
Does expanding rmi_rmm_activate() here cause stack frame bloat in
arm64_init_rmi()?
Looking at include/linux/arm-rmi-cmds.h, rmi_rmm_activate() calls the
rmi_sro_memxfer_cmd() macro, which evaluates a large compound literal:
*__sro = (struct rmi_sro_state){ .regs = {__VA_ARGS__} };
This forces the compiler to allocate a ~2.2KB temporary object on the stack
before copying it to the heap object, which defeats the purpose of the
explicit heap allocation above. Does this risk exceeding the typical
CONFIG_FRAME_WARN threshold of 2048 bytes on arm64 and lead to build
warnings?
> + if (ret) {
> + pr_err("RMM activate failed\n");
> + ret = ret < 0 ? ret : -ENXIO;
> + }
> +
> + kfree(sro);
> + return ret;
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260907095942.1140734-1-suzuki.poulose@arm.com?part=5
^ permalink raw reply [flat|nested] 44+ messages in thread* Re: [PATCH v17 5/7] firmware: arm_rmm: Activate the RMM
2026-09-07 10:17 ` sashiko-bot
@ 2026-09-07 16:16 ` Suzuki K Poulose
0 siblings, 0 replies; 44+ messages in thread
From: Suzuki K Poulose @ 2026-09-07 16:16 UTC (permalink / raw)
To: sashiko-reviews; +Cc: Oliver Upton, kvm, Marc Zyngier, kvmarm
On 07/09/2026 11:17, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
> - [Medium] The macro `rmi_sro_memxfer_cmd` uses a large compound literal to initialize the SRO state, forcing the compiler to allocate a ~2.2KB temporary object on the stack.
> --
>
> commit c773f8f03d602782404ec2f0103977a8f86f3b3e
> Author: Steven Price <steven.price@arm.com>
>
> firmware: arm_rmm: Activate the RMM
>
> Activate the RMM after the basic configuration. This is a memory transferring,
> stateful operation.
>
>> diff --git a/drivers/firmware/arm_rmm/rmi.c b/drivers/firmware/arm_rmm/rmi.c
>> index 42c973c3a98bb..d969c8738efde 100644
>> --- a/drivers/firmware/arm_rmm/rmi.c
>> +++ b/drivers/firmware/arm_rmm/rmi.c
> [ ... ]
>> @@ -656,7 +657,19 @@ static int __init arm64_init_rmi(void)
>> if (ret)
>> return ret;
>>
>> - return 0;
>> + /* Activate the RMM */
>> + sro = kmalloc_obj(*sro);
>> + if (!sro)
>> + return -ENOMEM;
>> +
>> + ret = rmi_rmm_activate(sro);
>
> [Severity: Medium]
> Does expanding rmi_rmm_activate() here cause stack frame bloat in
> arm64_init_rmi()?
>
> Looking at include/linux/arm-rmi-cmds.h, rmi_rmm_activate() calls the
> rmi_sro_memxfer_cmd() macro, which evaluates a large compound literal:
>
> *__sro = (struct rmi_sro_state){ .regs = {__VA_ARGS__} };
>
> This forces the compiler to allocate a ~2.2KB temporary object on the stack
> before copying it to the heap object, which defeats the purpose of the
> explicit heap allocation above. Does this risk exceeding the typical
> CONFIG_FRAME_WARN threshold of 2048 bytes on arm64 and lead to build
> warnings?
I have switched to using memset() to initialise the sro and then
initialize the regs from the __VA_ARGS__
diff --git a/include/linux/arm-rmi-cmds.h b/include/linux/arm-rmi-cmds.h
index 47c31808b8a7e..1e4dd02124ebd 100644
--- a/include/linux/arm-rmi-cmds.h
+++ b/include/linux/arm-rmi-cmds.h
@@ -8,6 +8,7 @@
#include <linux/arm-smccc-rmi.h>
#include <linux/bug.h>
+#include <linux/string.h>
#include <linux/types.h>
#define RMI_MAX_ADDR_LIST 256
@@ -43,7 +44,8 @@ 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__} }; \
+ memset(__sro, 0, sizeof(*__sro)); \
+ __sro->regs = (struct arm_smccc_1_2_regs){ __VA_ARGS__ }; \
long __ret = rmi_sro_memxfer_execute(__sro, gfp); \
rmi_sro_free(__sro); \
__ret; \
Cheers
Suzuki
>
>> + if (ret) {
>> + pr_err("RMM activate failed\n");
>> + ret = ret < 0 ? ret : -ENXIO;
>> + }
>> +
>> + kfree(sro);
>> + return ret;
>> }
>
^ permalink raw reply related [flat|nested] 44+ messages in thread
* Re: [PATCH v17 5/7] firmware: arm_rmm: Activate the RMM
2026-09-07 9:59 ` [PATCH v17 5/7] firmware: arm_rmm: Activate the RMM Suzuki K Poulose
2026-09-07 10:17 ` sashiko-bot
@ 2026-09-09 4:29 ` Gavin Shan
2026-09-09 8:25 ` Suzuki K Poulose
1 sibling, 1 reply; 44+ messages in thread
From: Gavin Shan @ 2026-09-09 4:29 UTC (permalink / raw)
To: Suzuki K Poulose, kvm, kvmarm
Cc: maz, will, catalin.marinas, linux-kernel, linux-arm-kernel,
steven.price, aneesh.kumar, oupton, joey.gouly, tabba, yuzenghui,
linux-coco, gankulkarni, sdonthineni, alpergun, fj0570is,
WeiLin.Chang, lpieralisi, enju.kohei
Hi Suzuki,
On 9/7/26 7:59 PM, Suzuki K Poulose wrote:
> From: Steven Price <steven.price@arm.com>
>
> Activate the RMM after the basic configuration. This is a memory transferring,
> stateful operation.
>
> Signed-off-by: Steven Price <steven.price@arm.com>
> Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
> ---
> Changes since v16:
> * Split into a new patch
> ---
> drivers/firmware/arm_rmm/rmi.c | 17 +++++++++++++++--
> include/linux/arm-rmi-cmds.h | 11 +++++++++++
> 2 files changed, 26 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/firmware/arm_rmm/rmi.c b/drivers/firmware/arm_rmm/rmi.c
> index 42c973c3a98bb..d969c8738efde 100644
> --- a/drivers/firmware/arm_rmm/rmi.c
> +++ b/drivers/firmware/arm_rmm/rmi.c
> @@ -641,7 +641,8 @@ static int rmi_configure(void)
>
> static int __init arm64_init_rmi(void)
> {
> - int ret;
> + int ret = 0;
> + struct rmi_sro_state *sro = NULL;
Why we need to initialize those local variables ('ret' and 'sro')? :-)
Also, we can use the scope-based cleanup function supported by cleanup.h
for @sro.
struct smi_sro_state *sro __free(kfree) = NULL;
With the scope-based cleanup in place, we needn't explict 'kfree(sro)' at
end of this function.
>
> /* Continue without realm support if we can't agree on a version */
> ret = rmi_check_version();
> @@ -656,7 +657,19 @@ static int __init arm64_init_rmi(void)
> if (ret)
> return ret;
>
> - return 0;
> + /* Activate the RMM */
> + sro = kmalloc_obj(*sro);
> + if (!sro)
> + return -ENOMEM;
if (!sro) {
pr_err("Unable to alloc SRO object for RMM activation\n");
return -ENOMEM;
}
> +
> + ret = rmi_rmm_activate(sro);
> + if (ret) {
> + pr_err("RMM activate failed\n");
> + ret = ret < 0 ? ret : -ENXIO;
> + }
> +
> + kfree(sro);
> + return ret;
> }
>
> /*
> diff --git a/include/linux/arm-rmi-cmds.h b/include/linux/arm-rmi-cmds.h
> index fed0f3421895d..dea7c7004d35f 100644
> --- a/include/linux/arm-rmi-cmds.h
> +++ b/include/linux/arm-rmi-cmds.h
> @@ -64,6 +64,17 @@ static inline int rmi_rmm_config_set(unsigned long cfg_ptr)
> return res.a0;
> }
>
> +/**
> + * rmi_rmm_activate() - Activate the RMM
> + * @sro: Preallocated SRO context to be used
> + *
> + * Return: 0 on success, positive RMI result code or negative Linux error code
> + */
> +static inline long rmi_rmm_activate(struct rmi_sro_state *sro)
> +{
> + return rmi_sro_memxfer_cmd(sro, GFP_KERNEL, SMC_RMI_RMM_ACTIVATE);
> +}
> +
rmi_rmm_acvivate() would be used for once by rmi.c::arm64_init_rmi(). Lets
not expose the function by combining the logic to rmi.c::arm64_init_rmi().
> /**
> * rmi_features() - Read feature register
> * @index: Feature register index
Thanks,
Gavin
^ permalink raw reply [flat|nested] 44+ messages in thread* Re: [PATCH v17 5/7] firmware: arm_rmm: Activate the RMM
2026-09-09 4:29 ` Gavin Shan
@ 2026-09-09 8:25 ` Suzuki K Poulose
0 siblings, 0 replies; 44+ messages in thread
From: Suzuki K Poulose @ 2026-09-09 8:25 UTC (permalink / raw)
To: Gavin Shan, kvm, kvmarm
Cc: maz, will, catalin.marinas, linux-kernel, linux-arm-kernel,
steven.price, aneesh.kumar, oupton, joey.gouly, tabba, yuzenghui,
linux-coco, gankulkarni, sdonthineni, alpergun, fj0570is,
WeiLin.Chang, lpieralisi, enju.kohei
On 09/09/2026 05:29, Gavin Shan wrote:
> Hi Suzuki,
>
> On 9/7/26 7:59 PM, Suzuki K Poulose wrote:
>> From: Steven Price <steven.price@arm.com>
>>
>> Activate the RMM after the basic configuration. This is a memory
>> transferring,
>> stateful operation.
>>
>> Signed-off-by: Steven Price <steven.price@arm.com>
>> Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
>> ---
>> Changes since v16:
>> * Split into a new patch
>> ---
>> drivers/firmware/arm_rmm/rmi.c | 17 +++++++++++++++--
>> include/linux/arm-rmi-cmds.h | 11 +++++++++++
>> 2 files changed, 26 insertions(+), 2 deletions(-)
>>
>> diff --git a/drivers/firmware/arm_rmm/rmi.c b/drivers/firmware/
>> arm_rmm/rmi.c
>> index 42c973c3a98bb..d969c8738efde 100644
>> --- a/drivers/firmware/arm_rmm/rmi.c
>> +++ b/drivers/firmware/arm_rmm/rmi.c
>> @@ -641,7 +641,8 @@ static int rmi_configure(void)
>> static int __init arm64_init_rmi(void)
>> {
>> - int ret;
>> + int ret = 0;
>> + struct rmi_sro_state *sro = NULL;
>
> Why we need to initialize those local variables ('ret' and 'sro')? :-)
> Also, we can use the scope-based cleanup function supported by cleanup.h
> for @sro.
>
> struct smi_sro_state *sro __free(kfree) = NULL;
>
> With the scope-based cleanup in place, we needn't explict 'kfree(sro)' at
> end of this function.
>
Ack.
>> /* Continue without realm support if we can't agree on a version */
>> ret = rmi_check_version();
>> @@ -656,7 +657,19 @@ static int __init arm64_init_rmi(void)
>> if (ret)
>> return ret;
>> - return 0;
>> + /* Activate the RMM */
>> + sro = kmalloc_obj(*sro);
>> + if (!sro)
>> + return -ENOMEM;
>
> if (!sro) {
> pr_err("Unable to alloc SRO object for RMM activation\n");
> return -ENOMEM;
> }
>
Ack
>> +
>> + ret = rmi_rmm_activate(sro);
>> + if (ret) {
>> + pr_err("RMM activate failed\n");
>> + ret = ret < 0 ? ret : -ENXIO;
>> + }
>> +
>> + kfree(sro);
>> + return ret;
>> }
>> /*
>> diff --git a/include/linux/arm-rmi-cmds.h b/include/linux/arm-rmi-cmds.h
>> index fed0f3421895d..dea7c7004d35f 100644
>> --- a/include/linux/arm-rmi-cmds.h
>> +++ b/include/linux/arm-rmi-cmds.h
>> @@ -64,6 +64,17 @@ static inline int rmi_rmm_config_set(unsigned long
>> cfg_ptr)
>> return res.a0;
>> }
>> +/**
>> + * rmi_rmm_activate() - Activate the RMM
>> + * @sro: Preallocated SRO context to be used
>> + *
>> + * Return: 0 on success, positive RMI result code or negative Linux
>> error code
>> + */
>> +static inline long rmi_rmm_activate(struct rmi_sro_state *sro)
>> +{
>> + return rmi_sro_memxfer_cmd(sro, GFP_KERNEL, SMC_RMI_RMM_ACTIVATE);
>> +}
>> +
>
> rmi_rmm_acvivate() would be used for once by rmi.c::arm64_init_rmi(). Lets
> not expose the function by combining the logic to rmi.c::arm64_init_rmi().
Have done that already in my local tree.
Thanks for the review.
Cheers
Suzuki
^ permalink raw reply [flat|nested] 44+ messages in thread
* [PATCH v17 6/7] firmware: arm_rmm: Ensure the RMM has GPT entries for memory
2026-09-07 9:59 [PATCH v17 0/7] firmware: arm_rmm: Add RMM v2.0 base RMI support Suzuki K Poulose
` (4 preceding siblings ...)
2026-09-07 9:59 ` [PATCH v17 5/7] firmware: arm_rmm: Activate the RMM Suzuki K Poulose
@ 2026-09-07 9:59 ` Suzuki K Poulose
2026-09-09 6:40 ` Gavin Shan
2026-09-07 9:59 ` [PATCH v17 7/7] firmware: arm_rmm: Add wrappers for Realm related RMI commands Suzuki K Poulose
2026-09-08 4:09 ` [PATCH v17 0/7] firmware: arm_rmm: Add RMM v2.0 base RMI support Kohei Enju
7 siblings, 1 reply; 44+ messages in thread
From: Suzuki K Poulose @ 2026-09-07 9:59 UTC (permalink / raw)
To: kvm, kvmarm
Cc: maz, will, catalin.marinas, linux-kernel, linux-arm-kernel,
steven.price, aneesh.kumar, oupton, gshan, joey.gouly, tabba,
yuzenghui, linux-coco, gankulkarni, sdonthineni, alpergun,
fj0570is, WeiLin.Chang, lpieralisi, enju.kohei, Suzuki K Poulose
From: Steven Price <steven.price@arm.com>
The RMM maintains the state of all the granules in the system to make
sure that the host is abiding by the rules. This state can be maintained
at different granularity, per page (TRACKING_FINE) or per region
(TRACKING_COARSE or TRACKING_INTERMEDIATE). The region size depends on the
underlying "RMI_GRANULE_SIZE". For a "coarse"/"intermediate" region, all pages
in the region must be of the same state, this implies we need to have "fine"
tracking for DRAM, so that we can delegate individual pages.
For now we only support a statically carved out memory for tracking
granules for the "fine" regions. This can be extended in the future to
allow modifying the tracking granularity and remove the need for a
static allocation by the firmware.
Similarly, the firmware may create L0 GPT entries describing the total
address space. But if we change the "PAS" (Physical Address Space) of a
granule, then the firmware may need to create L1 tables to track the PAS
at a finer granularity. Linux therefore checks if the platform firmware manages
the PAR region. i.e., the firmware is in charge of managing the L1 GPTs
(creation and the required memory for the GPT tables - via static carveouts)
without host intervention. Support for dynamic GPT creation by the host will be
added later.
If the firmware requires us to manage the tracking or GPT memory, Deactivate
the RMM and reclaim any memory donated at RMM activation.
Apply the same checks when hotplugged memory is brought online.
Signed-off-by: Steven Price <steven.price@arm.com>
[ Switch to RMI_GPT_L1_INFO for checking GPTs and deactivate RMM ]
Co-Developed-by: Suzuki K Poulose <suzuki.poulose@arm.com>
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
Changes since v16:
* Check fine tracking and create L1 GPTs for hotplug-added memory.
* Clarify the L1 GPT setup and move the explanatory comment.
* Switch to using RMI_GPT_INFO command for checking the GPTs.
* Deactivate the RMM and reclaim the memory if we can't proceed.
Changes since v15:
* Skip firmware-reserved NOMAP memory in rmi_init_metadata()
* Handle negative error codes from wrappers.
Changes since v14:
* Move the implementation into drivers/firmware/arm_rmm.
Changes since v13:
* Moved out of KVM
---
drivers/firmware/arm_rmm/rmi.c | 139 +++++++++++++++++++++++++++++++++
include/linux/arm-rmi-cmds.h | 75 ++++++++++++++++++
2 files changed, 214 insertions(+)
diff --git a/drivers/firmware/arm_rmm/rmi.c b/drivers/firmware/arm_rmm/rmi.c
index d969c8738efde..34058e34188d3 100644
--- a/drivers/firmware/arm_rmm/rmi.c
+++ b/drivers/firmware/arm_rmm/rmi.c
@@ -5,6 +5,7 @@
#include <linux/cpufeature.h>
#include <linux/memblock.h>
+#include <linux/memory.h>
#include <linux/arm-rmi-cmds.h>
#include <linux/processor.h>
#include <linux/slab.h>
@@ -12,6 +13,8 @@
#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];
@@ -639,6 +642,124 @@ static int rmi_configure(void)
return ret;
}
+/*
+ * Make sure the area is tracked by RMM at FINE granularity.
+ * We do not support changing the tracking yet.
+ */
+static int rmi_verify_memory_tracking(phys_addr_t start, phys_addr_t end)
+{
+ while (start < end) {
+ unsigned long ret, category, state, next;
+
+ ret = rmi_granule_tracking_get(start, end, &category, &state, &next);
+ if (ret != RMI_SUCCESS)
+ return -ENOMEM;
+
+ if (state != RMI_TRACKING_FINE ||
+ category != RMI_MEM_CATEGORY_CONVENTIONAL) {
+ /* TODO: Set granule tracking in this case */
+ pr_err("Granule tracking for region isn't fine/conventional: %llx-%lx\n",
+ start, next);
+ return -ENODEV;
+ }
+ start = next;
+ }
+
+ return 0;
+}
+
+/*
+ * We do not support creating L1 GPTs yet. So, make sure that
+ * all the regions are managed by the firmware.
+ */
+static int rmi_verify_gpt_firmware_managed(phys_addr_t start, phys_addr_t end)
+{
+ unsigned long l0gpt_sz;
+ unsigned long next, par_state;
+
+ l0gpt_sz = 1UL << (30 + FIELD_GET(RMI_FEATURE_REGISTER_1_L0GPTSZ,
+ rmi_feat_reg(1)));
+ start = ALIGN_DOWN(start, l0gpt_sz);
+ end = ALIGN(end, l0gpt_sz);
+
+ while (start < end) {
+ long ret = rmi_gpt_info(start, end, &next, &par_state);
+
+ if (ret != RMI_SUCCESS)
+ return -ENOMEM;
+
+ if (par_state != RMI_GPT_PAR_PLAT) {
+ pr_err("GPT for the region is not managed by firmware %llx-%lx\n",
+ start, next);
+ return -ENOMEM;
+ }
+ start = next;
+ }
+
+ return 0;
+}
+
+static int rmi_prepare_memory(phys_addr_t start, phys_addr_t end)
+{
+ int ret;
+
+ ret = rmi_verify_memory_tracking(start, end);
+ if (ret)
+ return ret;
+
+ return rmi_verify_gpt_firmware_managed(start, end);
+}
+
+static int rmi_init_metadata(void)
+{
+ phys_addr_t start, end;
+ struct memblock_region *r;
+
+ for_each_mem_region(r) {
+ int ret;
+
+ /* Firmware-reserved NOMAP regions are not usable system RAM */
+ if (memblock_is_nomap(r))
+ continue;
+
+ start = memblock_region_memory_base_pfn(r) << PAGE_SHIFT;
+ end = memblock_region_memory_end_pfn(r) << PAGE_SHIFT;
+
+ ret = rmi_prepare_memory(start, end);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int rmi_memory_notifier(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct memory_notify *arg = data;
+ phys_addr_t start, end;
+ int ret;
+
+ if (action != MEM_GOING_ONLINE)
+ return NOTIFY_DONE;
+
+ start = PFN_PHYS(arg->start_pfn);
+ end = PFN_PHYS(arg->start_pfn + arg->nr_pages);
+ ret = rmi_prepare_memory(start, end);
+
+ return notifier_from_errno(ret);
+}
+
+static struct notifier_block rmi_memory_nb = {
+ .notifier_call = rmi_memory_notifier,
+};
+
+bool is_rmi_available(void)
+{
+ return arm64_rmi_is_available;
+}
+EXPORT_SYMBOL_GPL(is_rmi_available);
+
static int __init arm64_init_rmi(void)
{
int ret = 0;
@@ -666,8 +787,26 @@ static int __init arm64_init_rmi(void)
if (ret) {
pr_err("RMM activate failed\n");
ret = ret < 0 ? ret : -ENXIO;
+ goto out_free_sro;
}
+ ret = rmi_init_metadata();
+ if (ret)
+ goto out_deactivate;
+
+ ret = register_memory_notifier(&rmi_memory_nb);
+ if (ret)
+ goto out_deactivate;
+
+ arm64_rmi_is_available = true;
+ pr_info("RMI configured\n");
+ kfree(sro);
+
+ return 0;
+
+out_deactivate:
+ rmi_rmm_deactivate(sro);
+out_free_sro:
kfree(sro);
return ret;
}
diff --git a/include/linux/arm-rmi-cmds.h b/include/linux/arm-rmi-cmds.h
index dea7c7004d35f..79e2c1f165112 100644
--- a/include/linux/arm-rmi-cmds.h
+++ b/include/linux/arm-rmi-cmds.h
@@ -35,6 +35,8 @@ static inline int rmi_undelegate_page(phys_addr_t phys)
return rmi_undelegate_range(phys, PAGE_SIZE);
}
+bool is_rmi_available(void);
+
long rmi_sro_memxfer_execute(struct rmi_sro_state *sro, gfp_t gfp);
void rmi_sro_free(struct rmi_sro_state *sro);
long rmi_sro_execute(struct arm_smccc_1_2_regs *regs);
@@ -64,6 +66,19 @@ static inline int rmi_rmm_config_set(unsigned long cfg_ptr)
return res.a0;
}
+/**
+ * rmi_rmm_deactivate() - Deactivate the RMM and reclaim any memory donated at
+ * rmi_rmm_activate()
+ *
+ * @sro: Preallocated SRO context to be used
+ *
+ * Return: 0 on success, positive RMI result code or negative Linux error code
+ */
+static inline long rmi_rmm_deactivate(struct rmi_sro_state *sro)
+{
+ return rmi_sro_memxfer_cmd(sro, GFP_KERNEL, SMC_RMI_RMM_DEACTIVATE);
+}
+
/**
* rmi_rmm_activate() - Activate the RMM
* @sro: Preallocated SRO context to be used
@@ -75,6 +90,66 @@ static inline long 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 (res.a0 == RMI_SUCCESS) {
+ 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_info - Query the GPT info for the given PAR.
+ * @base: Base of the physical address region
+ * @top: Top of the physical address region
+ * @out_top: Top of the phyiscal address region for which
+ * the GPT @out_gpt_par_state is valid
+ * @out_gpt_par_state: State of the GPT covered by [base, out_top)
+ */
+static inline long rmi_gpt_info(unsigned long base, unsigned long end,
+ unsigned long *out_top,
+ unsigned long *out_gpt_par_state)
+{
+ struct arm_smccc_1_2_regs regs = {
+ SMC_RMI_GPT_INFO, base, end,
+ };
+
+ long ret = rmi_sro_execute(®s);
+
+ if (ret == RMI_SUCCESS) {
+ if (out_top)
+ *out_top = regs.a1;
+ if (out_gpt_par_state)
+ *out_gpt_par_state = regs.a2;
+ }
+
+ return ret;
+}
+
/**
* rmi_features() - Read feature register
* @index: Feature register index
--
2.43.0
^ permalink raw reply related [flat|nested] 44+ messages in thread* Re: [PATCH v17 6/7] firmware: arm_rmm: Ensure the RMM has GPT entries for memory
2026-09-07 9:59 ` [PATCH v17 6/7] firmware: arm_rmm: Ensure the RMM has GPT entries for memory Suzuki K Poulose
@ 2026-09-09 6:40 ` Gavin Shan
2026-09-09 8:33 ` Suzuki K Poulose
0 siblings, 1 reply; 44+ messages in thread
From: Gavin Shan @ 2026-09-09 6:40 UTC (permalink / raw)
To: Suzuki K Poulose, kvm, kvmarm
Cc: maz, will, catalin.marinas, linux-kernel, linux-arm-kernel,
steven.price, aneesh.kumar, oupton, joey.gouly, tabba, yuzenghui,
linux-coco, gankulkarni, sdonthineni, alpergun, fj0570is,
WeiLin.Chang, lpieralisi, enju.kohei
On 9/7/26 7:59 PM, Suzuki K Poulose wrote:
> From: Steven Price <steven.price@arm.com>
>
> The RMM maintains the state of all the granules in the system to make
> sure that the host is abiding by the rules. This state can be maintained
> at different granularity, per page (TRACKING_FINE) or per region
> (TRACKING_COARSE or TRACKING_INTERMEDIATE). The region size depends on the
> underlying "RMI_GRANULE_SIZE". For a "coarse"/"intermediate" region, all pages
> in the region must be of the same state, this implies we need to have "fine"
> tracking for DRAM, so that we can delegate individual pages.
>
> For now we only support a statically carved out memory for tracking
> granules for the "fine" regions. This can be extended in the future to
> allow modifying the tracking granularity and remove the need for a
> static allocation by the firmware.
>
> Similarly, the firmware may create L0 GPT entries describing the total
> address space. But if we change the "PAS" (Physical Address Space) of a
> granule, then the firmware may need to create L1 tables to track the PAS
> at a finer granularity. Linux therefore checks if the platform firmware manages
> the PAR region. i.e., the firmware is in charge of managing the L1 GPTs
> (creation and the required memory for the GPT tables - via static carveouts)
> without host intervention. Support for dynamic GPT creation by the host will be
> added later.
>
> If the firmware requires us to manage the tracking or GPT memory, Deactivate
> the RMM and reclaim any memory donated at RMM activation.
>
> Apply the same checks when hotplugged memory is brought online.
>
> Signed-off-by: Steven Price <steven.price@arm.com>
> [ Switch to RMI_GPT_L1_INFO for checking GPTs and deactivate RMM ]
> Co-Developed-by: Suzuki K Poulose <suzuki.poulose@arm.com>
> Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
> ---
> Changes since v16:
> * Check fine tracking and create L1 GPTs for hotplug-added memory.
> * Clarify the L1 GPT setup and move the explanatory comment.
> * Switch to using RMI_GPT_INFO command for checking the GPTs.
> * Deactivate the RMM and reclaim the memory if we can't proceed.
> Changes since v15:
> * Skip firmware-reserved NOMAP memory in rmi_init_metadata()
> * Handle negative error codes from wrappers.
> Changes since v14:
> * Move the implementation into drivers/firmware/arm_rmm.
> Changes since v13:
> * Moved out of KVM
> ---
> drivers/firmware/arm_rmm/rmi.c | 139 +++++++++++++++++++++++++++++++++
> include/linux/arm-rmi-cmds.h | 75 ++++++++++++++++++
> 2 files changed, 214 insertions(+)
>
> diff --git a/drivers/firmware/arm_rmm/rmi.c b/drivers/firmware/arm_rmm/rmi.c
> index d969c8738efde..34058e34188d3 100644
> --- a/drivers/firmware/arm_rmm/rmi.c
> +++ b/drivers/firmware/arm_rmm/rmi.c
> @@ -5,6 +5,7 @@
>
> #include <linux/cpufeature.h>
> #include <linux/memblock.h>
> +#include <linux/memory.h>
> #include <linux/arm-rmi-cmds.h>
> #include <linux/processor.h>
> #include <linux/slab.h>
> @@ -12,6 +13,8 @@
> #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];
> @@ -639,6 +642,124 @@ static int rmi_configure(void)
> return ret;
> }
>
> +/*
> + * Make sure the area is tracked by RMM at FINE granularity.
> + * We do not support changing the tracking yet.
> + */
> +static int rmi_verify_memory_tracking(phys_addr_t start, phys_addr_t end)
> +{
> + while (start < end) {
> + unsigned long ret, category, state, next;
> +
> + ret = rmi_granule_tracking_get(start, end, &category, &state, &next);
> + if (ret != RMI_SUCCESS)
> + return -ENOMEM;
> +
> + if (state != RMI_TRACKING_FINE ||
> + category != RMI_MEM_CATEGORY_CONVENTIONAL) {
> + /* TODO: Set granule tracking in this case */
> + pr_err("Granule tracking for region isn't fine/conventional: %llx-%lx\n",
> + start, next);
> + return -ENODEV;
> + }
> + start = next;
> + }
> +
> + return 0;
> +}
> +
> +/*
> + * We do not support creating L1 GPTs yet. So, make sure that
> + * all the regions are managed by the firmware.
> + */
> +static int rmi_verify_gpt_firmware_managed(phys_addr_t start, phys_addr_t end)
> +{
> + unsigned long l0gpt_sz;
> + unsigned long next, par_state;
> +
> + l0gpt_sz = 1UL << (30 + FIELD_GET(RMI_FEATURE_REGISTER_1_L0GPTSZ,
> + rmi_feat_reg(1)));
> + start = ALIGN_DOWN(start, l0gpt_sz);
> + end = ALIGN(end, l0gpt_sz);
> +
> + while (start < end) {
> + long ret = rmi_gpt_info(start, end, &next, &par_state);
> +
> + if (ret != RMI_SUCCESS)
> + return -ENOMEM;
> +
> + if (par_state != RMI_GPT_PAR_PLAT) {
> + pr_err("GPT for the region is not managed by firmware %llx-%lx\n",
> + start, next);
> + return -ENOMEM;
I guess -ENODEV is more appropriate:
return -ENODEV
> + }
> + start = next;
> + }
> +
> + return 0;
> +}
> +
> +static int rmi_prepare_memory(phys_addr_t start, phys_addr_t end)
> +{
> + int ret;
> +
> + ret = rmi_verify_memory_tracking(start, end);
> + if (ret)
> + return ret;
> +
> + return rmi_verify_gpt_firmware_managed(start, end);
> +}
> +
> +static int rmi_init_metadata(void)
> +{
> + phys_addr_t start, end;
> + struct memblock_region *r;
> +
> + for_each_mem_region(r) {
> + int ret;
> +
> + /* Firmware-reserved NOMAP regions are not usable system RAM */
> + if (memblock_is_nomap(r))
> + continue;
> +
> + start = memblock_region_memory_base_pfn(r) << PAGE_SHIFT;
> + end = memblock_region_memory_end_pfn(r) << PAGE_SHIFT;
> +
> + ret = rmi_prepare_memory(start, end);
> + if (ret)
> + return ret;
The local variable 'start' and 'end' can be dropped:
ret = rmi_prepare_memory(PFN_PHYS(memblock_region_memory_base_pfn(r)),
PFN_PHYS(memblock_region_memory_end_pfn(r)));
if (ret)
return ret;
> + }
> +
> + return 0;
> +}
> +
> +static int rmi_memory_notifier(struct notifier_block *nb,
> + unsigned long action, void *data)
> +{
> + struct memory_notify *arg = data;
> + phys_addr_t start, end;
> + int ret;
> +
> + if (action != MEM_GOING_ONLINE)
> + return NOTIFY_DONE;
> +
> + start = PFN_PHYS(arg->start_pfn);
> + end = PFN_PHYS(arg->start_pfn + arg->nr_pages);
> + ret = rmi_prepare_memory(start, end);
> +
> + return notifier_from_errno(ret);
> +}
> +
> +static struct notifier_block rmi_memory_nb = {
> + .notifier_call = rmi_memory_notifier,
> +};
> +
> +bool is_rmi_available(void)
> +{
> + return arm64_rmi_is_available;
> +}
> +EXPORT_SYMBOL_GPL(is_rmi_available);
> +
> static int __init arm64_init_rmi(void)
> {
> int ret = 0;
> @@ -666,8 +787,26 @@ static int __init arm64_init_rmi(void)
> if (ret) {
> pr_err("RMM activate failed\n");
> ret = ret < 0 ? ret : -ENXIO;
> + goto out_free_sro;
> }
>
> + ret = rmi_init_metadata();
> + if (ret)
> + goto out_deactivate;
> +
> + ret = register_memory_notifier(&rmi_memory_nb);
> + if (ret)
> + goto out_deactivate;
> +
> + arm64_rmi_is_available = true;
> + pr_info("RMI configured\n");
> + kfree(sro);
> +
> + return 0;
> +
> +out_deactivate:
> + rmi_rmm_deactivate(sro);
> +out_free_sro:
> kfree(sro);
> return ret;
> }
> diff --git a/include/linux/arm-rmi-cmds.h b/include/linux/arm-rmi-cmds.h
> index dea7c7004d35f..79e2c1f165112 100644
> --- a/include/linux/arm-rmi-cmds.h
> +++ b/include/linux/arm-rmi-cmds.h
> @@ -35,6 +35,8 @@ static inline int rmi_undelegate_page(phys_addr_t phys)
> return rmi_undelegate_range(phys, PAGE_SIZE);
> }
>
> +bool is_rmi_available(void);
> +
> long rmi_sro_memxfer_execute(struct rmi_sro_state *sro, gfp_t gfp);
> void rmi_sro_free(struct rmi_sro_state *sro);
> long rmi_sro_execute(struct arm_smccc_1_2_regs *regs);
> @@ -64,6 +66,19 @@ static inline int rmi_rmm_config_set(unsigned long cfg_ptr)
> return res.a0;
> }
>
> +/**
> + * rmi_rmm_deactivate() - Deactivate the RMM and reclaim any memory donated at
> + * rmi_rmm_activate()
> + *
> + * @sro: Preallocated SRO context to be used
> + *
> + * Return: 0 on success, positive RMI result code or negative Linux error code
> + */
> +static inline long rmi_rmm_deactivate(struct rmi_sro_state *sro)
> +{
> + return rmi_sro_memxfer_cmd(sro, GFP_KERNEL, SMC_RMI_RMM_DEACTIVATE);
> +}
> +
It seems rmi_rmm_deactivate() is used for once by rmi.c::arm64_init_rmi(). If so,
we needn't to expose this function and just combine the logics to rmi.c::arm64_init_rmi().
> /**
> * rmi_rmm_activate() - Activate the RMM
> * @sro: Preallocated SRO context to be used
> @@ -75,6 +90,66 @@ static inline long 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 (res.a0 == RMI_SUCCESS) {
> + 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_granule_tracking_get() is used for once by rmi.c::rmi_verify_memory_tracking(). We needn't
expose rmi_granule_tracking_get() and combine its logic into rmi.c::rmi_verify_memory_tracking().
> +/*
> + * rmi_gpt_info - Query the GPT info for the given PAR.
> + * @base: Base of the physical address region
> + * @top: Top of the physical address region
> + * @out_top: Top of the phyiscal address region for which
> + * the GPT @out_gpt_par_state is valid
> + * @out_gpt_par_state: State of the GPT covered by [base, out_top)
> + */
> +static inline long rmi_gpt_info(unsigned long base, unsigned long end,
> + unsigned long *out_top,
> + unsigned long *out_gpt_par_state)
> +{
> + struct arm_smccc_1_2_regs regs = {
> + SMC_RMI_GPT_INFO, base, end,
> + };
> +
> + long ret = rmi_sro_execute(®s);
> +
> + if (ret == RMI_SUCCESS) {
> + if (out_top)
> + *out_top = regs.a1;
> + if (out_gpt_par_state)
> + *out_gpt_par_state = regs.a2;
> + }
> +
> + return ret;
> +}
> +
Similarly, rmi_gpt_info() is used for once by rmi.c::rmi_verify_gpt_firmware_managed(). We
needn't expose this function and can combine the logic to rmi.c::rmi_verify_gpt_firmware_managed().
> /**
> * rmi_features() - Read feature register
> * @index: Feature register index
Thanks,
Gavin
^ permalink raw reply [flat|nested] 44+ messages in thread* Re: [PATCH v17 6/7] firmware: arm_rmm: Ensure the RMM has GPT entries for memory
2026-09-09 6:40 ` Gavin Shan
@ 2026-09-09 8:33 ` Suzuki K Poulose
0 siblings, 0 replies; 44+ messages in thread
From: Suzuki K Poulose @ 2026-09-09 8:33 UTC (permalink / raw)
To: Gavin Shan, kvm, kvmarm
Cc: maz, will, catalin.marinas, linux-kernel, linux-arm-kernel,
steven.price, aneesh.kumar, oupton, joey.gouly, tabba, yuzenghui,
linux-coco, gankulkarni, sdonthineni, alpergun, fj0570is,
WeiLin.Chang, lpieralisi, enju.kohei
On 09/09/2026 07:40, Gavin Shan wrote:
> On 9/7/26 7:59 PM, Suzuki K Poulose wrote:
>> From: Steven Price <steven.price@arm.com>
>>
>> The RMM maintains the state of all the granules in the system to make
>> sure that the host is abiding by the rules. This state can be maintained
>> at different granularity, per page (TRACKING_FINE) or per region
>> (TRACKING_COARSE or TRACKING_INTERMEDIATE). The region size depends on
>> the
>> underlying "RMI_GRANULE_SIZE". For a "coarse"/"intermediate" region,
>> all pages
>> in the region must be of the same state, this implies we need to have
>> "fine"
>> tracking for DRAM, so that we can delegate individual pages.
>>
>> For now we only support a statically carved out memory for tracking
>> granules for the "fine" regions. This can be extended in the future to
>> allow modifying the tracking granularity and remove the need for a
>> static allocation by the firmware.
>>
>> Similarly, the firmware may create L0 GPT entries describing the total
>> address space. But if we change the "PAS" (Physical Address Space) of a
>> granule, then the firmware may need to create L1 tables to track the PAS
>> at a finer granularity. Linux therefore checks if the platform
>> firmware manages
>> the PAR region. i.e., the firmware is in charge of managing the L1 GPTs
>> (creation and the required memory for the GPT tables - via static
>> carveouts)
>> without host intervention. Support for dynamic GPT creation by the
>> host will be
>> added later.
>>
>> If the firmware requires us to manage the tracking or GPT memory,
>> Deactivate
>> the RMM and reclaim any memory donated at RMM activation.
>>
>> Apply the same checks when hotplugged memory is brought online.
>>
>> Signed-off-by: Steven Price <steven.price@arm.com>
>> [ Switch to RMI_GPT_L1_INFO for checking GPTs and deactivate RMM ]
>> Co-Developed-by: Suzuki K Poulose <suzuki.poulose@arm.com>
>> Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
>> ---
>> Changes since v16:
>> * Check fine tracking and create L1 GPTs for hotplug-added memory.
>> * Clarify the L1 GPT setup and move the explanatory comment.
>> * Switch to using RMI_GPT_INFO command for checking the GPTs.
>> * Deactivate the RMM and reclaim the memory if we can't proceed.
>> Changes since v15:
>> * Skip firmware-reserved NOMAP memory in rmi_init_metadata()
>> * Handle negative error codes from wrappers.
>> Changes since v14:
>> * Move the implementation into drivers/firmware/arm_rmm.
>> Changes since v13:
>> * Moved out of KVM
>> ---
>> drivers/firmware/arm_rmm/rmi.c | 139 +++++++++++++++++++++++++++++++++
>> include/linux/arm-rmi-cmds.h | 75 ++++++++++++++++++
>> 2 files changed, 214 insertions(+)
>>
>> diff --git a/drivers/firmware/arm_rmm/rmi.c b/drivers/firmware/
>> arm_rmm/rmi.c
>> index d969c8738efde..34058e34188d3 100644
>> --- a/drivers/firmware/arm_rmm/rmi.c
>> +++ b/drivers/firmware/arm_rmm/rmi.c
>> @@ -5,6 +5,7 @@
>> #include <linux/cpufeature.h>
>> #include <linux/memblock.h>
>> +#include <linux/memory.h>
>> #include <linux/arm-rmi-cmds.h>
>> #include <linux/processor.h>
>> #include <linux/slab.h>
>> @@ -12,6 +13,8 @@
>> #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];
>> @@ -639,6 +642,124 @@ static int rmi_configure(void)
>> return ret;
>> }
>> +/*
>> + * Make sure the area is tracked by RMM at FINE granularity.
>> + * We do not support changing the tracking yet.
>> + */
>> +static int rmi_verify_memory_tracking(phys_addr_t start, phys_addr_t
>> end)
>> +{
>> + while (start < end) {
>> + unsigned long ret, category, state, next;
>> +
>> + ret = rmi_granule_tracking_get(start, end, &category, &state,
>> &next);
>> + if (ret != RMI_SUCCESS)
>> + return -ENOMEM;
>> +
>> + if (state != RMI_TRACKING_FINE ||
>> + category != RMI_MEM_CATEGORY_CONVENTIONAL) {
>> + /* TODO: Set granule tracking in this case */
>> + pr_err("Granule tracking for region isn't fine/
>> conventional: %llx-%lx\n",
>> + start, next);
>> + return -ENODEV;
>> + }
>> + start = next;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +/*
>> + * We do not support creating L1 GPTs yet. So, make sure that
>> + * all the regions are managed by the firmware.
>> + */
>> +static int rmi_verify_gpt_firmware_managed(phys_addr_t start,
>> phys_addr_t end)
>> +{
>> + unsigned long l0gpt_sz;
>> + unsigned long next, par_state;
>> +
>> + l0gpt_sz = 1UL << (30 + FIELD_GET(RMI_FEATURE_REGISTER_1_L0GPTSZ,
>> + rmi_feat_reg(1)));
>> + start = ALIGN_DOWN(start, l0gpt_sz);
>> + end = ALIGN(end, l0gpt_sz);
>> +
>> + while (start < end) {
>> + long ret = rmi_gpt_info(start, end, &next, &par_state);
>> +
>> + if (ret != RMI_SUCCESS)
>> + return -ENOMEM;
>> +
>> + if (par_state != RMI_GPT_PAR_PLAT) {
>> + pr_err("GPT for the region is not managed by firmware
>> %llx-%lx\n",
>> + start, next);
>> + return -ENOMEM;
>
> I guess -ENODEV is more appropriate:
>
> return -ENODEV
Ack
>
>> + }
>> + start = next;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static int rmi_prepare_memory(phys_addr_t start, phys_addr_t end)
>> +{
>> + int ret;
>> +
>> + ret = rmi_verify_memory_tracking(start, end);
>> + if (ret)
>> + return ret;
>> +
>> + return rmi_verify_gpt_firmware_managed(start, end);
>> +}
>> +
>> +static int rmi_init_metadata(void)
>> +{
>> + phys_addr_t start, end;
>> + struct memblock_region *r;
>> +
>> + for_each_mem_region(r) {
>> + int ret;
>> +
>> + /* Firmware-reserved NOMAP regions are not usable system RAM */
>> + if (memblock_is_nomap(r))
>> + continue;
>> +
>> + start = memblock_region_memory_base_pfn(r) << PAGE_SHIFT;
>> + end = memblock_region_memory_end_pfn(r) << PAGE_SHIFT;
>> +
>> + ret = rmi_prepare_memory(start, end);
>> + if (ret)
>> + return ret;
>
> The local variable 'start' and 'end' can be dropped:
>
> ret =
> rmi_prepare_memory(PFN_PHYS(memblock_region_memory_base_pfn(r)),
> PFN_PHYS(memblock_region_memory_end_pfn(r)));
> if (ret)
> return ret;
It makes the code a bit more easier to read, so I would keep it.
>
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static int rmi_memory_notifier(struct notifier_block *nb,
>> + unsigned long action, void *data)
>> +{
>> + struct memory_notify *arg = data;
>> + phys_addr_t start, end;
>> + int ret;
>> +
>> + if (action != MEM_GOING_ONLINE)
>> + return NOTIFY_DONE;
>> +
>> + start = PFN_PHYS(arg->start_pfn);
>> + end = PFN_PHYS(arg->start_pfn + arg->nr_pages);
>> + ret = rmi_prepare_memory(start, end);
>> +
>> + return notifier_from_errno(ret);
>> +}
>> +
>> +static struct notifier_block rmi_memory_nb = {
>> + .notifier_call = rmi_memory_notifier,
>> +};
>> +
>> +bool is_rmi_available(void)
>> +{
>> + return arm64_rmi_is_available;
>> +}
>> +EXPORT_SYMBOL_GPL(is_rmi_available);
>> +
>> static int __init arm64_init_rmi(void)
>> {
>> int ret = 0;
>> @@ -666,8 +787,26 @@ static int __init arm64_init_rmi(void)
>> if (ret) {
>> pr_err("RMM activate failed\n");
>> ret = ret < 0 ? ret : -ENXIO;
>> + goto out_free_sro;
>> }
>> + ret = rmi_init_metadata();
>> + if (ret)
>> + goto out_deactivate;
>> +
>> + ret = register_memory_notifier(&rmi_memory_nb);
>> + if (ret)
>> + goto out_deactivate;
>> +
>> + arm64_rmi_is_available = true;
>> + pr_info("RMI configured\n");
>> + kfree(sro);
>> +
>> + return 0;
>> +
>> +out_deactivate:
>> + rmi_rmm_deactivate(sro);
>> +out_free_sro:
>> kfree(sro);
>> return ret;
>> }
>> diff --git a/include/linux/arm-rmi-cmds.h b/include/linux/arm-rmi-cmds.h
>> index dea7c7004d35f..79e2c1f165112 100644
>> --- a/include/linux/arm-rmi-cmds.h
>> +++ b/include/linux/arm-rmi-cmds.h
>> @@ -35,6 +35,8 @@ static inline int rmi_undelegate_page(phys_addr_t phys)
>> return rmi_undelegate_range(phys, PAGE_SIZE);
>> }
>> +bool is_rmi_available(void);
>> +
>> long rmi_sro_memxfer_execute(struct rmi_sro_state *sro, gfp_t gfp);
>> void rmi_sro_free(struct rmi_sro_state *sro);
>> long rmi_sro_execute(struct arm_smccc_1_2_regs *regs);
>> @@ -64,6 +66,19 @@ static inline int rmi_rmm_config_set(unsigned long
>> cfg_ptr)
>> return res.a0;
>> }
>> +/**
>> + * rmi_rmm_deactivate() - Deactivate the RMM and reclaim any memory
>> donated at
>> + * rmi_rmm_activate()
>> + *
>> + * @sro: Preallocated SRO context to be used
>> + *
>> + * Return: 0 on success, positive RMI result code or negative Linux
>> error code
>> + */
>> +static inline long rmi_rmm_deactivate(struct rmi_sro_state *sro)
>> +{
>> + return rmi_sro_memxfer_cmd(sro, GFP_KERNEL, SMC_RMI_RMM_DEACTIVATE);
>> +}
>> +
>
> It seems rmi_rmm_deactivate() is used for once by
> rmi.c::arm64_init_rmi(). If so,
> we needn't to expose this function and just combine the logics to
> rmi.c::arm64_init_rmi().
True, for now. But we might want to deactivate the RMM from, e.g., say
kexec in the future. I could move it in for now, and expose this when
we get to use it.
>
>> /**
>> * rmi_rmm_activate() - Activate the RMM
>> * @sro: Preallocated SRO context to be used
>> @@ -75,6 +90,66 @@ static inline long rmi_rmm_activate(struct
>> rmi_sro_state *sro)
>> return rmi_sro_memxfer_cmd(sro, GFP_KERNEL, SMC_RMI_RMM_ACTIVATE);
>
>> +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)
>> +{
>> +
>> + return res.a0;
>> +}
>> +
>
> rmi_granule_tracking_get() is used for once by
> rmi.c::rmi_verify_memory_tracking(). We needn't
> expose rmi_granule_tracking_get() and combine its logic into
> rmi.c::rmi_verify_memory_tracking().
Ack
>
>> +/*
>> + * rmi_gpt_info - Query the GPT info for the given PAR.
>> + * @base: Base of the physical address region
>> + * @top: Top of the physical address region
>> + * @out_top: Top of the phyiscal address region for which
>> + * the GPT @out_gpt_par_state is valid
>> + * @out_gpt_par_state: State of the GPT covered by [base, out_top)
>> + */
>> +static inline long rmi_gpt_info(unsigned long base, unsigned long end,
>> + unsigned long *out_top,
>> + unsigned long *out_gpt_par_state)
>> +{
>> + return ret;
>> +}
>> +
>
> Similarly, rmi_gpt_info() is used for once by
> rmi.c::rmi_verify_gpt_firmware_managed(). We
> needn't expose this function and can combine the logic to
> rmi.c::rmi_verify_gpt_firmware_managed().
Ack
Suzuki
^ permalink raw reply [flat|nested] 44+ messages in thread
* [PATCH v17 7/7] firmware: arm_rmm: Add wrappers for Realm related RMI commands
2026-09-07 9:59 [PATCH v17 0/7] firmware: arm_rmm: Add RMM v2.0 base RMI support Suzuki K Poulose
` (5 preceding siblings ...)
2026-09-07 9:59 ` [PATCH v17 6/7] firmware: arm_rmm: Ensure the RMM has GPT entries for memory Suzuki K Poulose
@ 2026-09-07 9:59 ` Suzuki K Poulose
2026-09-07 10:10 ` sashiko-bot
2026-09-09 7:15 ` Gavin Shan
2026-09-08 4:09 ` [PATCH v17 0/7] firmware: arm_rmm: Add RMM v2.0 base RMI support Kohei Enju
7 siblings, 2 replies; 44+ messages in thread
From: Suzuki K Poulose @ 2026-09-07 9:59 UTC (permalink / raw)
To: kvm, kvmarm
Cc: maz, will, catalin.marinas, linux-kernel, linux-arm-kernel,
steven.price, aneesh.kumar, oupton, gshan, joey.gouly, tabba,
yuzenghui, linux-coco, gankulkarni, sdonthineni, alpergun,
fj0570is, WeiLin.Chang, lpieralisi, enju.kohei, Suzuki K Poulose
From: Steven Price <steven.price@arm.com>
Introduce wrappers for the RMI functions needed for creating and
managing realm guests. This will be used by the KVM to manage the
Realms
Signed-off-by: Steven Price <steven.price@arm.com>
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
Changes since v16:
* Split into a separate patch and move away from arch/arm64 to
include/linux/.
* Also moved into the firmware_rmm series from the KVM CCA support.
This is done in a hope to reduce the merge conflicts and make
the KVM CCA upstreaming in independent parallel chunks
---
include/linux/arm-rmi-cmds.h | 453 +++++++++++++++++++++++++++++++++++
1 file changed, 453 insertions(+)
diff --git a/include/linux/arm-rmi-cmds.h b/include/linux/arm-rmi-cmds.h
index 79e2c1f165112..746257d77dd61 100644
--- a/include/linux/arm-rmi-cmds.h
+++ b/include/linux/arm-rmi-cmds.h
@@ -222,4 +222,457 @@ static inline long rmi_granule_range_undelegate(unsigned long base,
return ret;
}
+/**
+ * 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: 0 on success, positive RMI result code or negative Linux error code
+ */
+static inline long rmi_rtt_data_map_init(unsigned long rd, unsigned long data,
+ unsigned long ipa, unsigned long src,
+ unsigned long flags)
+{
+ struct arm_smccc_1_2_regs regs = {
+ SMC_RMI_RTT_DATA_MAP_INIT, rd, data, ipa, src, flags
+ };
+
+ return rmi_sro_execute(®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: 0 on success, positive RMI result code or negative Linux error code
+ */
+static inline long rmi_rtt_data_map(unsigned long rd,
+ unsigned long base,
+ unsigned long top,
+ unsigned long flags,
+ unsigned long oaddr,
+ unsigned long *out_top)
+{
+ struct arm_smccc_1_2_regs regs = {
+ SMC_RMI_RTT_DATA_MAP, rd, base, top, flags, oaddr
+ };
+ long ret;
+
+ ret = rmi_sro_execute(®s);
+
+ if (ret == RMI_SUCCESS && 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: 0 on success, positive RMI result code or negative Linux error code
+ */
+static inline long rmi_rtt_data_unmap(unsigned long rd,
+ unsigned long base,
+ unsigned long top,
+ unsigned long flags,
+ unsigned long oaddr,
+ unsigned long *out_top,
+ unsigned long *out_range,
+ unsigned long *out_count)
+{
+ struct arm_smccc_1_2_regs regs = {
+ SMC_RMI_RTT_DATA_UNMAP, rd, base, top, flags, oaddr
+ };
+ long ret;
+
+ ret = rmi_sro_execute(®s);
+
+ if (ret == RMI_SUCCESS) {
+ 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 long 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 long 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: 0 on success, positive RMI result code or negative Linux error code
+ */
+static inline long rmi_realm_create(unsigned long rd, unsigned long params,
+ struct rmi_sro_state *sro)
+{
+ return rmi_sro_memxfer_cmd(sro, GFP_KERNEL,
+ SMC_RMI_REALM_CREATE, rd, params);
+}
+
+/**
+ * rmi_realm_terminate() - Terminate a realm
+ * @rd: PA of the RD
+ * @sro: Preallocated SRO context to be used
+ *
+ * Terminates a realm, moving it into a ZOMBIE state
+ *
+ * Return: 0 on success, positive RMI result code or negative Linux error code
+ */
+static inline long rmi_realm_terminate(unsigned long rd,
+ struct rmi_sro_state *sro)
+{
+ return rmi_sro_memxfer_cmd(sro, GFP_KERNEL,
+ SMC_RMI_REALM_TERMINATE, rd);
+}
+
+/**
+ * rmi_realm_destroy() - Destroy a realm
+ * @rd: PA of the RD
+ * @sro: Preallocated SRO context to be used
+ *
+ * Destroys a realm, all objects belonging to the realm must be destroyed first.
+ *
+ * Return: 0 on success, positive RMI result code or negative Linux error code
+ */
+static inline long rmi_realm_destroy(unsigned long rd,
+ struct rmi_sro_state *sro)
+{
+ return rmi_sro_memxfer_cmd(sro, GFP_KERNEL,
+ SMC_RMI_REALM_DESTROY, rd);
+}
+
+/**
+ * rmi_rec_create() - Create a REC
+ * @rd: PA of the RD
+ * @rec: PA of the target REC
+ * @params: PA of REC parameters
+ * @sro: Allocated SRO context to be used
+ *
+ * Create a REC using the parameters specified in the struct rec_params pointed
+ * to by @params.
+ *
+ * Return: 0 on success, positive RMI result code or negative Linux error code
+ */
+static inline long rmi_rec_create(unsigned long rd,
+ unsigned long rec,
+ unsigned long params,
+ struct rmi_sro_state *sro)
+{
+ long ret;
+
+ *sro = (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: 0 on success, positive RMI result code or negative Linux error code
+ */
+static inline long rmi_rec_destroy(unsigned long rec,
+ struct rmi_sro_state *sro)
+{
+ return rmi_sro_memxfer_cmd(sro, GFP_KERNEL, SMC_RMI_REC_DESTROY, rec);
+}
+
+/**
+ * rmi_rec_enter() - Enter a REC
+ * @rec: PA of the target REC
+ * @run_ptr: PA of RecRun structure
+ *
+ * Starts (or continues) execution within a REC.
+ *
+ * Return: RMI return code
+ */
+static inline long rmi_rec_enter(unsigned long rec, unsigned long run_ptr)
+{
+ struct arm_smccc_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: 0 on success, positive RMI result code or negative Linux error code
+ */
+static inline long rmi_rtt_create(unsigned long rd, unsigned long rtt,
+ unsigned long ipa, long level)
+{
+ struct arm_smccc_1_2_regs regs = {
+ SMC_RMI_RTT_CREATE, rd, rtt, ipa, level
+ };
+
+ return rmi_sro_execute(®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: 0 on success, positive RMI result code or negative Linux error code.
+ */
+static inline long rmi_rtt_destroy(unsigned long rd,
+ unsigned long ipa,
+ long level,
+ unsigned long *out_rtt,
+ unsigned long *out_top)
+{
+ struct arm_smccc_1_2_regs regs = {
+ SMC_RMI_RTT_DESTROY, rd, ipa, level
+ };
+ long ret = rmi_sro_execute(®s);
+
+ if (ret == RMI_SUCCESS) {
+ 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: 0 on success, positive RMI result code or negative Linux error code
+ */
+static inline long rmi_rtt_fold(unsigned long rd, unsigned long ipa,
+ long level, unsigned long *out_rtt)
+{
+ struct arm_smccc_1_2_regs regs = {
+ SMC_RMI_RTT_FOLD, rd, ipa, level
+ };
+ long ret = rmi_sro_execute(®s);
+
+ if (ret == RMI_SUCCESS && out_rtt)
+ *out_rtt = regs.a1;
+
+ return ret;
+}
+
+/**
+ * rmi_rtt_init_ripas() - Set RIPAS for new realm
+ * @rd: PA of the RD
+ * @base: Base of target IPA region
+ * @top: Top of target IPA region
+ * @out_top: Top IPA of range whose RIPAS was modified
+ *
+ * Sets the RIPAS of a target IPA range to RAM, for a realm in the NEW state.
+ *
+ * Return: 0 on success, positive RMI result code or negative Linux error code
+ */
+static inline long rmi_rtt_init_ripas(unsigned long rd, unsigned long base,
+ unsigned long top, unsigned long *out_top)
+{
+ struct arm_smccc_1_2_regs regs = {
+ SMC_RMI_RTT_INIT_RIPAS, rd, base, top
+ };
+ long ret = rmi_sro_execute(®s);
+
+ if (ret == RMI_SUCCESS && out_top)
+ *out_top = regs.a1;
+
+ return ret;
+}
+
+/**
+ * rmi_rtt_unprot_map() - Map unprotected granules into a realm
+ * @rd: PA of the RD
+ * @base: Base IPA of the mapping
+ * @top: Top of the target IPA range
+ * @flags: Flags
+ * @oaddr: Output address set descriptor
+ * @out_top: Top IPA of range which has been mapped
+ *
+ * Create mappings to memory within a target unprotected IPA range.
+ *
+ * Return: 0 on success, positive RMI result code or negative Linux error code
+ */
+static inline long rmi_rtt_unprot_map(unsigned long rd,
+ unsigned long base,
+ unsigned long top,
+ unsigned long flags,
+ unsigned long oaddr,
+ unsigned long *out_top)
+{
+ struct arm_smccc_1_2_regs regs = {
+ SMC_RMI_RTT_UNPROT_MAP, rd, base, top, flags, oaddr
+ };
+ long ret = rmi_sro_execute(®s);
+
+ if (ret == RMI_SUCCESS && out_top)
+ *out_top = regs.a1;
+
+ return ret;
+}
+
+/**
+ * rmi_rtt_set_ripas() - Set RIPAS for an running realm
+ * @rd: PA of the RD
+ * @rec: PA of the REC making the request
+ * @base: Base of target IPA region
+ * @top: Top of target IPA region
+ * @out_top: Pointer to write top IPA of range whose RIPAS was modified
+ *
+ * Completes a request made by the realm to change the RIPAS of a target IPA
+ * range.
+ *
+ * Return: 0 on success, positive RMI result code or negative Linux error code
+ */
+static inline long rmi_rtt_set_ripas(unsigned long rd, unsigned long rec,
+ unsigned long base, unsigned long top,
+ unsigned long *out_top)
+{
+ struct arm_smccc_1_2_regs regs = {
+ SMC_RMI_RTT_SET_RIPAS, rd, rec, base, top
+ };
+ long ret = rmi_sro_execute(®s);
+
+ if (ret == RMI_SUCCESS && out_top)
+ *out_top = regs.a1;
+
+ return ret;
+}
+
+/**
+ * rmi_rtt_unprot_unmap() - Remove mappings within an unprotected IPA range
+ * @rd: PA of the RD
+ * @base: Base IPA of the mapping
+ * @top: Top of the target IPA range
+ * @flags: Flags
+ * @oaddr: Output address set descriptor
+ * @out_top: Top IPA which has been unmapped
+ * @out_range: Output address range
+ * @out_count: Number of entries in output address list
+ *
+ * Removes mappings to memory within a target unprotected IPA range.
+ *
+ * Return: 0 on success, positive RMI result code or negative Linux error code
+ */
+static inline long rmi_rtt_unprot_unmap(unsigned long rd,
+ unsigned long base,
+ unsigned long top,
+ unsigned long flags,
+ unsigned long oaddr,
+ unsigned long *out_top,
+ unsigned long *out_range,
+ unsigned long *out_count)
+{
+ struct arm_smccc_1_2_regs regs = {
+ SMC_RMI_RTT_UNPROT_UNMAP, rd, base, top, flags, oaddr
+ };
+ long ret = rmi_sro_execute(®s);
+
+ if (ret == RMI_SUCCESS) {
+ if (out_top)
+ *out_top = regs.a1;
+ if (out_range)
+ *out_range = regs.a2;
+ if (out_count)
+ *out_count = regs.a3;
+ }
+
+ return ret;
+}
+
#endif
--
2.43.0
^ permalink raw reply related [flat|nested] 44+ messages in thread* Re: [PATCH v17 7/7] firmware: arm_rmm: Add wrappers for Realm related RMI commands
2026-09-07 9:59 ` [PATCH v17 7/7] firmware: arm_rmm: Add wrappers for Realm related RMI commands Suzuki K Poulose
@ 2026-09-07 10:10 ` sashiko-bot
2026-09-07 12:20 ` Suzuki K Poulose
2026-09-09 7:15 ` Gavin Shan
1 sibling, 1 reply; 44+ messages in thread
From: sashiko-bot @ 2026-09-07 10:10 UTC (permalink / raw)
To: Suzuki K Poulose; +Cc: kvm, Oliver Upton, Marc Zyngier, kvmarm
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] RMI wrapper functions silently discard output registers on SMC call errors, preventing error recovery and leaking memory.
--
commit a6c85c25d264d328a0bb8014d88418e614ccef39
Author: Steven Price <steven.price@arm.com>
firmware: arm_rmm: Add wrappers for Realm related RMI commands
Introduce wrappers for the RMI functions needed for creating and managing
realm guests. This will be used by the KVM to manage the Realms.
> diff --git a/include/linux/arm-rmi-cmds.h b/include/linux/arm-rmi-cmds.h
> --- a/include/linux/arm-rmi-cmds.h
> +++ b/include/linux/arm-rmi-cmds.h
[ ... ]
> +static inline long rmi_rtt_data_map(unsigned long rd,
[ ... ]
> +{
[ ... ]
> + ret = rmi_sro_execute(®s);
> +
> + if (ret == RMI_SUCCESS && out_top)
> + *out_top = regs.a1;
[Severity: High]
If the RMM returns an error like RMI_ERROR_RTT partway through, does this
discard the intermediate progress in regs.a1 by gating it on RMI_SUCCESS?
If out_top is lost, it seems KVM wouldn't know where the operation stopped,
making proper error recovery or partial mapping cleanup impossible.
[ ... ]
> +static inline long rmi_rtt_data_unmap(unsigned long rd,
[ ... ]
> +{
[ ... ]
> + ret = rmi_sro_execute(®s);
> +
> + if (ret == RMI_SUCCESS) {
> + if (out_top)
> + *out_top = regs.a1;
> + if (out_range)
> + *out_range = regs.a2;
> + if (out_count)
> + *out_count = regs.a3;
> + }
[Severity: High]
Can this lead to a leak of physical pages?
When an error occurs (e.g., RMI_ERROR_RTT due to state conflicts), the RMM
populates the output registers with the number of successfully unmapped
pages. Because this is gated behind RMI_SUCCESS, losing out_count means KVM
loses track of how many physical pages were written to the output address
list.
[ ... ]
> +static inline long rmi_rtt_destroy(unsigned long rd,
[ ... ]
> +{
[ ... ]
> + long ret = rmi_sro_execute(®s);
> +
> + if (ret == RMI_SUCCESS) {
> + if (out_rtt)
> + *out_rtt = regs.a1;
> + if (out_top)
> + *out_top = regs.a2;
> + }
[Severity: High]
Does discarding these outputs on error prevent diagnosing the destruction
failure?
If an RMI_ERROR_RTT occurs, the live IPA location is returned in the output
registers, but it looks like it gets dropped here since the return code is
not RMI_SUCCESS.
[ ... ]
> +static inline long rmi_rtt_unprot_unmap(unsigned long rd,
[ ... ]
> +{
[ ... ]
> + long ret = rmi_sro_execute(®s);
> +
> + if (ret == RMI_SUCCESS) {
> + if (out_top)
> + *out_top = regs.a1;
> + if (out_range)
> + *out_range = regs.a2;
> + if (out_count)
> + *out_count = regs.a3;
> + }
[Severity: High]
Similar to the data unmap wrapper above, does dropping out_count on a
non-success return code leak physical pages for unprotected memory?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260907095942.1140734-1-suzuki.poulose@arm.com?part=7
^ permalink raw reply [flat|nested] 44+ messages in thread* Re: [PATCH v17 7/7] firmware: arm_rmm: Add wrappers for Realm related RMI commands
2026-09-07 10:10 ` sashiko-bot
@ 2026-09-07 12:20 ` Suzuki K Poulose
0 siblings, 0 replies; 44+ messages in thread
From: Suzuki K Poulose @ 2026-09-07 12:20 UTC (permalink / raw)
To: sashiko-reviews; +Cc: kvm, Oliver Upton, Marc Zyngier, kvmarm
On 07/09/2026 11:10, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
> - [High] RMI wrapper functions silently discard output registers on SMC call errors, preventing error recovery and leaking memory.
> --
>
> commit a6c85c25d264d328a0bb8014d88418e614ccef39
> Author: Steven Price <steven.price@arm.com>
>
> firmware: arm_rmm: Add wrappers for Realm related RMI commands
>
> Introduce wrappers for the RMI functions needed for creating and managing
> realm guests. This will be used by the KVM to manage the Realms.
>
>> diff --git a/include/linux/arm-rmi-cmds.h b/include/linux/arm-rmi-cmds.h
>> --- a/include/linux/arm-rmi-cmds.h
>> +++ b/include/linux/arm-rmi-cmds.h
>
> [ ... ]
>
>> +static inline long rmi_rtt_data_map(unsigned long rd,
> [ ... ]
>> +{
> [ ... ]
>> + ret = rmi_sro_execute(®s);
>> +
>> + if (ret == RMI_SUCCESS && out_top)
>> + *out_top = regs.a1;
>
> [Severity: High]
> If the RMM returns an error like RMI_ERROR_RTT partway through, does this
> discard the intermediate progress in regs.a1 by gating it on RMI_SUCCESS?
>
> If out_top is lost, it seems KVM wouldn't know where the operation stopped,
> making proper error recovery or partial mapping cleanup impossible.
>
> [ ... ]
>
>> +static inline long rmi_rtt_data_unmap(unsigned long rd,
> [ ... ]
>> +{
> [ ... ]
>> + ret = rmi_sro_execute(®s);
>> +
>> + if (ret == RMI_SUCCESS) {
>> + if (out_top)
>> + *out_top = regs.a1;
>> + if (out_range)
>> + *out_range = regs.a2;
>> + if (out_count)
>> + *out_count = regs.a3;
>> + }
>
> [Severity: High]
> Can this lead to a leak of physical pages?
>
> When an error occurs (e.g., RMI_ERROR_RTT due to state conflicts), the RMM
> populates the output registers with the number of successfully unmapped
> pages. Because this is gated behind RMI_SUCCESS, losing out_count means KVM
> loses track of how many physical pages were written to the output address
> list.
>
The output parameters are only valid when the result is RMI_SUCCESS as
described by the success conditions.
> [ ... ]
>
>> +static inline long rmi_rtt_destroy(unsigned long rd,
> [ ... ]
>> +{
> [ ... ]
>> + long ret = rmi_sro_execute(®s);
>> +
>> + if (ret == RMI_SUCCESS) {
>> + if (out_rtt)
>> + *out_rtt = regs.a1;
>> + if (out_top)
>> + *out_top = regs.a2;
>> + }
>
> [Severity: High]
> Does discarding these outputs on error prevent diagnosing the destruction
> failure?
>
> If an RMI_ERROR_RTT occurs, the live IPA location is returned in the output
> registers, but it looks like it gets dropped here since the return code is
> not RMI_SUCCESS.
No, the RMM only returns the out_top when the error is RMI_SUCCESS.
>
> [ ... ]
>
>> +static inline long rmi_rtt_unprot_unmap(unsigned long rd,
> [ ... ]
>> +{
> [ ... ]
>> + long ret = rmi_sro_execute(®s);
>> +
>> + if (ret == RMI_SUCCESS) {
>> + if (out_top)
>> + *out_top = regs.a1;
>> + if (out_range)
>> + *out_range = regs.a2;
>> + if (out_count)
>> + *out_count = regs.a3;
>> + }
>
> [Severity: High]
> Similar to the data unmap wrapper above, does dropping out_count on a
> non-success return code leak physical pages for unprotected memory?
>
Same here.
Suzuki
^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH v17 7/7] firmware: arm_rmm: Add wrappers for Realm related RMI commands
2026-09-07 9:59 ` [PATCH v17 7/7] firmware: arm_rmm: Add wrappers for Realm related RMI commands Suzuki K Poulose
2026-09-07 10:10 ` sashiko-bot
@ 2026-09-09 7:15 ` Gavin Shan
2026-09-09 8:55 ` Suzuki K Poulose
1 sibling, 1 reply; 44+ messages in thread
From: Gavin Shan @ 2026-09-09 7:15 UTC (permalink / raw)
To: Suzuki K Poulose, kvm, kvmarm
Cc: maz, will, catalin.marinas, linux-kernel, linux-arm-kernel,
steven.price, aneesh.kumar, oupton, joey.gouly, tabba, yuzenghui,
linux-coco, gankulkarni, sdonthineni, alpergun, fj0570is,
WeiLin.Chang, lpieralisi, enju.kohei
On 9/7/26 7:59 PM, Suzuki K Poulose wrote:
> From: Steven Price <steven.price@arm.com>
>
> Introduce wrappers for the RMI functions needed for creating and
> managing realm guests. This will be used by the KVM to manage the
> Realms
>
> Signed-off-by: Steven Price <steven.price@arm.com>
> Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
> ---
> Changes since v16:
> * Split into a separate patch and move away from arch/arm64 to
> include/linux/.
> * Also moved into the firmware_rmm series from the KVM CCA support.
> This is done in a hope to reduce the merge conflicts and make
> the KVM CCA upstreaming in independent parallel chunks
> ---
> include/linux/arm-rmi-cmds.h | 453 +++++++++++++++++++++++++++++++++++
> 1 file changed, 453 insertions(+)
>
> diff --git a/include/linux/arm-rmi-cmds.h b/include/linux/arm-rmi-cmds.h
> index 79e2c1f165112..746257d77dd61 100644
> --- a/include/linux/arm-rmi-cmds.h
> +++ b/include/linux/arm-rmi-cmds.h
> @@ -222,4 +222,457 @@ static inline long rmi_granule_range_undelegate(unsigned long base,
> return ret;
> }
>
> +/**
> + * rmi_rtt_data_map_init() - Create a protected mapping with data contents
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
s/Create a protected mapping with data contents/Create a mappings in protected IPA with known contents
With this, it's consistently counterpart of the comments for rmi_rtt_data_map().
> + * @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: 0 on success, positive RMI result code or negative Linux error code
> + */
> +static inline long rmi_rtt_data_map_init(unsigned long rd, unsigned long data,
> + unsigned long ipa, unsigned long src,
> + unsigned long flags)
> +{
> + struct arm_smccc_1_2_regs regs = {
> + SMC_RMI_RTT_DATA_MAP_INIT, rd, data, ipa, src, flags
> + };
> +
> + return rmi_sro_execute(®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: 0 on success, positive RMI result code or negative Linux error code
> + */
> +static inline long rmi_rtt_data_map(unsigned long rd,
> + unsigned long base,
> + unsigned long top,
> + unsigned long flags,
> + unsigned long oaddr,
> + unsigned long *out_top)
> +{
> + struct arm_smccc_1_2_regs regs = {
> + SMC_RMI_RTT_DATA_MAP, rd, base, top, flags, oaddr
> + };
> + long ret;
> +
> + ret = rmi_sro_execute(®s);
> +
> + if (ret == RMI_SUCCESS && 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: 0 on success, positive RMI result code or negative Linux error code
> + */
It would be worthwhile to mention 'in protect IPA' where the mappings are
teared down. Besides, 'for the target Realm' can be dropped from the comments
for @rd.
/**
* rmi_rtt_data_unmap() - Remove mappings to conventional memory in protected IPA
* @rd: PA of the RD
* :
*/
> +static inline long rmi_rtt_data_unmap(unsigned long rd,
> + unsigned long base,
> + unsigned long top,
> + unsigned long flags,
> + unsigned long oaddr,
> + unsigned long *out_top,
> + unsigned long *out_range,
> + unsigned long *out_count)
> +{
> + struct arm_smccc_1_2_regs regs = {
> + SMC_RMI_RTT_DATA_UNMAP, rd, base, top, flags, oaddr
> + };
> + long ret;
> +
> + ret = rmi_sro_execute(®s);
> +
> + if (ret == RMI_SUCCESS) {
> + if (out_top)
> + *out_top = regs.a1;
> + if (out_range)
> + *out_range = regs.a2;
> + if (out_count)
> + *out_count = regs.a3;
> + }
> +
> + return ret;
> +}
The nested if statements can be avoided if we have:
if (ret != RMI_SUCCESS)
return ret;
if (out_top)
*out_top = regs.a1;
if (out_range)
*out_range = regs.a2;
if (out_count)
*out_count = regs.a3;
return RMI_SUCCESS;
> +
> +/**
> + * rmi_psci_complete() - Complete pending PSCI command
> + * @calling_rec: PA of the calling REC
> + * @status: Status of the PSCI request
> + *
> + * Completes a pending PSCI command.
> + *
> + * Return: RMI return code
> + */
> +static inline long rmi_psci_complete(unsigned long calling_rec,
> + unsigned long status)
> +{
> + struct arm_smccc_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
^^^^^^^^
s/complete/completed ?
> + * execution of the realm.
> + *
> + * Return: RMI return code
> + */
> +static inline long 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
We needn't to have 'to be used. This comment applies to other helpers
like rmi_realm_terminate(), rmi_realm_destroy(), rmi_rec_create(),
rmi_rec_destroy() where a preallocated SRO context is needed.
* @sro: Preallocated SRO context
> + *
> + * Create a new realm using the given parameters.
> + *
> + * Return: 0 on success, positive RMI result code or negative Linux error code
> + */
> +static inline long rmi_realm_create(unsigned long rd, unsigned long params,
> + struct rmi_sro_state *sro)
> +{
> + return rmi_sro_memxfer_cmd(sro, GFP_KERNEL,
> + SMC_RMI_REALM_CREATE, rd, params);
> +}
> +
> +/**
> + * rmi_realm_terminate() - Terminate a realm
> + * @rd: PA of the RD
> + * @sro: Preallocated SRO context to be used
> + *
> + * Terminates a realm, moving it into a ZOMBIE state
> + *
> + * Return: 0 on success, positive RMI result code or negative Linux error code
> + */
> +static inline long rmi_realm_terminate(unsigned long rd,
> + struct rmi_sro_state *sro)
> +{
> + return rmi_sro_memxfer_cmd(sro, GFP_KERNEL,
> + SMC_RMI_REALM_TERMINATE, rd);
> +}
> +
> +/**
> + * rmi_realm_destroy() - Destroy a realm
> + * @rd: PA of the RD
> + * @sro: Preallocated SRO context to be used
> + *
> + * Destroys a realm, all objects belonging to the realm must be destroyed first.
> + *
> + * Return: 0 on success, positive RMI result code or negative Linux error code
> + */
> +static inline long rmi_realm_destroy(unsigned long rd,
> + struct rmi_sro_state *sro)
> +{
> + return rmi_sro_memxfer_cmd(sro, GFP_KERNEL,
> + SMC_RMI_REALM_DESTROY, rd);
> +}
> +
> +/**
> + * rmi_rec_create() - Create a REC
> + * @rd: PA of the RD
> + * @rec: PA of the target REC
> + * @params: PA of REC parameters
> + * @sro: Allocated SRO context to be used
* @sro: Preallocated SRO context
> + *
> + * Create a REC using the parameters specified in the struct rec_params pointed
> + * to by @params.
> + *
> + * Return: 0 on success, positive RMI result code or negative Linux error code
> + */
> +static inline long rmi_rec_create(unsigned long rd,
> + unsigned long rec,
> + unsigned long params,
> + struct rmi_sro_state *sro)
> +{
> + long ret;
> +
> + *sro = (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
* @sro: Preallocated SRO context
> + *
> + * Destroys a REC. The REC must not be running.
> + *
> + * Return: 0 on success, positive RMI result code or negative Linux error code
> + */
> +static inline long rmi_rec_destroy(unsigned long rec,
> + struct rmi_sro_state *sro)
> +{
> + return rmi_sro_memxfer_cmd(sro, GFP_KERNEL, SMC_RMI_REC_DESTROY, rec);
> +}
> +
> +/**
> + * rmi_rec_enter() - Enter a REC
> + * @rec: PA of the target REC
> + * @run_ptr: PA of RecRun structure
> + *
> + * Starts (or continues) execution within a REC.
> + *
> + * Return: RMI return code
> + */
> +static inline long rmi_rec_enter(unsigned long rec, unsigned long run_ptr)
> +{
> + struct arm_smccc_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: 0 on success, positive RMI result code or negative Linux error code
> + */
> +static inline long rmi_rtt_create(unsigned long rd, unsigned long rtt,
> + unsigned long ipa, long level)
> +{
> + struct arm_smccc_1_2_regs regs = {
> + SMC_RMI_RTT_CREATE, rd, rtt, ipa, level
> + };
> +
> + return rmi_sro_execute(®s);
> +}
> +
> +/**
> + * rmi_rtt_destroy() - Destroy an RTT
> + * @rd: PA of the RD for the target realm
* @rd: PA of the RD
> + * @ipa: Base of the IPA range described by the RTT
> + * @level: RTT level
> + * @out_rtt: Pointer to write the PA of the RTT which was destroyed
> + * @out_top: Pointer to write the top IPA of non-live RTT entries, from entry
> + * at which the RTT walk terminated.
> + *
> + * Destroys an RTT. The RTT must be non-live, i.e. none of the entries in the
> + * table are in ASSIGNED or TABLE state.
> + *
> + * Return: 0 on success, positive RMI result code or negative Linux error code.
> + */
> +static inline long rmi_rtt_destroy(unsigned long rd,
> + unsigned long ipa,
> + long level,
> + unsigned long *out_rtt,
> + unsigned long *out_top)
> +{
> + struct arm_smccc_1_2_regs regs = {
> + SMC_RMI_RTT_DESTROY, rd, ipa, level
> + };
> + long ret = rmi_sro_execute(®s);
> +
> + if (ret == RMI_SUCCESS) {
> + if (out_rtt)
> + *out_rtt = regs.a1;
> + if (out_top)
> + *out_top = regs.a2;
> + }
> +
> + return ret;
> +}
> +
The nested if statements can be avoided if we have:
if (ret != RMI_SUCCESS)
return ret;
if (out_rtt)
*out_rtt = regs.a1;
if (out_top)
*out_top = regs.a2;
return RMI_SUCCESS;
> +/**
> + * rmi_rtt_fold() - Fold an RTT
> + * @rd: PA of the RD
> + * @ipa: Base of the IPA range described by the RTT
> + * @level: Depth of the RTT within the tree
> + * @out_rtt: Pointer to write the PA of the RTT which was destroyed
> + *
> + * Folds an RTT. If all entries with the RTT are 'homogeneous' the RTT can be
> + * folded into the parent and the RTT destroyed.
> + *
> + * Return: 0 on success, positive RMI result code or negative Linux error code
> + */
> +static inline long rmi_rtt_fold(unsigned long rd, unsigned long ipa,
> + long level, unsigned long *out_rtt)
> +{
> + struct arm_smccc_1_2_regs regs = {
> + SMC_RMI_RTT_FOLD, rd, ipa, level
> + };
> + long ret = rmi_sro_execute(®s);
> +
> + if (ret == RMI_SUCCESS && out_rtt)
> + *out_rtt = regs.a1;
> +
> + return ret;
> +}
> +
> +/**
> + * rmi_rtt_init_ripas() - Set RIPAS for new realm
> + * @rd: PA of the RD
> + * @base: Base of target IPA region
> + * @top: Top of target IPA region
> + * @out_top: Top IPA of range whose RIPAS was modified
> + *
> + * Sets the RIPAS of a target IPA range to RAM, for a realm in the NEW state.
> + *
> + * Return: 0 on success, positive RMI result code or negative Linux error code
> + */
> +static inline long rmi_rtt_init_ripas(unsigned long rd, unsigned long base,
> + unsigned long top, unsigned long *out_top)
> +{
> + struct arm_smccc_1_2_regs regs = {
> + SMC_RMI_RTT_INIT_RIPAS, rd, base, top
> + };
> + long ret = rmi_sro_execute(®s);
> +
> + if (ret == RMI_SUCCESS && out_top)
> + *out_top = regs.a1;
> +
> + return ret;
> +}
> +
> +/**
> + * rmi_rtt_unprot_map() - Map unprotected granules into a realm
> + * @rd: PA of the RD
> + * @base: Base IPA of the mapping
> + * @top: Top of the target IPA range
> + * @flags: Flags
> + * @oaddr: Output address set descriptor
> + * @out_top: Top IPA of range which has been mapped
> + *
> + * Create mappings to memory within a target unprotected IPA range.
> + *
> + * Return: 0 on success, positive RMI result code or negative Linux error code
> + */
> +static inline long rmi_rtt_unprot_map(unsigned long rd,
> + unsigned long base,
> + unsigned long top,
> + unsigned long flags,
> + unsigned long oaddr,
> + unsigned long *out_top)
> +{
> + struct arm_smccc_1_2_regs regs = {
> + SMC_RMI_RTT_UNPROT_MAP, rd, base, top, flags, oaddr
> + };
> + long ret = rmi_sro_execute(®s);
> +
> + if (ret == RMI_SUCCESS && out_top)
> + *out_top = regs.a1;
> +
> + return ret;
> +}
> +
> +/**
> + * rmi_rtt_set_ripas() - Set RIPAS for an running realm
> + * @rd: PA of the RD
> + * @rec: PA of the REC making the request
> + * @base: Base of target IPA region
> + * @top: Top of target IPA region
> + * @out_top: Pointer to write top IPA of range whose RIPAS was modified
> + *
> + * Completes a request made by the realm to change the RIPAS of a target IPA
> + * range.
> + *
> + * Return: 0 on success, positive RMI result code or negative Linux error code
> + */
> +static inline long rmi_rtt_set_ripas(unsigned long rd, unsigned long rec,
> + unsigned long base, unsigned long top,
> + unsigned long *out_top)
> +{
> + struct arm_smccc_1_2_regs regs = {
> + SMC_RMI_RTT_SET_RIPAS, rd, rec, base, top
> + };
> + long ret = rmi_sro_execute(®s);
> +
> + if (ret == RMI_SUCCESS && out_top)
> + *out_top = regs.a1;
> +
> + return ret;
> +}
> +
> +/**
> + * rmi_rtt_unprot_unmap() - Remove mappings within an unprotected IPA range
> + * @rd: PA of the RD
> + * @base: Base IPA of the mapping
> + * @top: Top of the target IPA range
> + * @flags: Flags
> + * @oaddr: Output address set descriptor
> + * @out_top: Top IPA which has been unmapped
> + * @out_range: Output address range
> + * @out_count: Number of entries in output address list
> + *
> + * Removes mappings to memory within a target unprotected IPA range.
> + *
> + * Return: 0 on success, positive RMI result code or negative Linux error code
> + */
> +static inline long rmi_rtt_unprot_unmap(unsigned long rd,
> + unsigned long base,
> + unsigned long top,
> + unsigned long flags,
> + unsigned long oaddr,
> + unsigned long *out_top,
> + unsigned long *out_range,
> + unsigned long *out_count)
> +{
> + struct arm_smccc_1_2_regs regs = {
> + SMC_RMI_RTT_UNPROT_UNMAP, rd, base, top, flags, oaddr
> + };
> + long ret = rmi_sro_execute(®s);
> +
> + if (ret == RMI_SUCCESS) {
> + if (out_top)
> + *out_top = regs.a1;
> + if (out_range)
> + *out_range = regs.a2;
> + if (out_count)
> + *out_count = regs.a3;
> + }
> +
> + return ret;
> +}
> +
The nested if statements can be avoided if we have:
if (ret != RMI_SUCCESS)
return ret;
if (out_top)
*out_top = regs.a1;
if (out_range)
*out_range = regs.a2;
if (out_count)
*out_count = regs.a3;
return RMI_SUCCESS;
> #endif
Thanks,
Gavin
^ permalink raw reply [flat|nested] 44+ messages in thread* Re: [PATCH v17 7/7] firmware: arm_rmm: Add wrappers for Realm related RMI commands
2026-09-09 7:15 ` Gavin Shan
@ 2026-09-09 8:55 ` Suzuki K Poulose
0 siblings, 0 replies; 44+ messages in thread
From: Suzuki K Poulose @ 2026-09-09 8:55 UTC (permalink / raw)
To: Gavin Shan, kvm, kvmarm
Cc: maz, will, catalin.marinas, linux-kernel, linux-arm-kernel,
steven.price, aneesh.kumar, oupton, joey.gouly, tabba, yuzenghui,
linux-coco, gankulkarni, sdonthineni, alpergun, fj0570is,
WeiLin.Chang, lpieralisi, enju.kohei
On 09/09/2026 08:15, Gavin Shan wrote:
> On 9/7/26 7:59 PM, Suzuki K Poulose wrote:
>> From: Steven Price <steven.price@arm.com>
>>
>> Introduce wrappers for the RMI functions needed for creating and
>> managing realm guests. This will be used by the KVM to manage the
>> Realms
>>
>> Signed-off-by: Steven Price <steven.price@arm.com>
>> Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
>> ---
>> Changes since v16:
>> * Split into a separate patch and move away from arch/arm64 to
>> include/linux/.
>> * Also moved into the firmware_rmm series from the KVM CCA support.
>> This is done in a hope to reduce the merge conflicts and make
>> the KVM CCA upstreaming in independent parallel chunks
>> ---
>> include/linux/arm-rmi-cmds.h | 453 +++++++++++++++++++++++++++++++++++
>> 1 file changed, 453 insertions(+)
>>
>> diff --git a/include/linux/arm-rmi-cmds.h b/include/linux/arm-rmi-cmds.h
>> index 79e2c1f165112..746257d77dd61 100644
>> --- a/include/linux/arm-rmi-cmds.h
>> +++ b/include/linux/arm-rmi-cmds.h
>> @@ -222,4 +222,457 @@ static inline long
>> rmi_granule_range_undelegate(unsigned long base,
>> return ret;
>> }
>> +/**
>> + * rmi_rtt_data_map_init() - Create a protected mapping with data
>> contents
>
> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>
> s/Create a protected mapping with data contents/Create a mappings in
> protected IPA with known contents
Ack.
>
> With this, it's consistently counterpart of the comments for
> rmi_rtt_data_map().
>
>
>
...
>
> The nested if statements can be avoided if we have:
>
> if (ret != RMI_SUCCESS)
> return ret;
>
> if (out_top)
> *out_top = regs.a1;
> if (out_range)
> *out_range = regs.a2;
> if (out_count)
> *out_count = regs.a3;
>
> return RMI_SUCCESS;
Done for all of them.
...
>> +/**
>> + * rmi_realm_activate() - Active a realm
>> + * @rd: PA of the RD
>> + *
>> + * Mark a realm as Active signalling that creation is complete and
>> allowing
> ^^^^^^^^
>
> s/complete/completed ?
I have changed this :
/**
- * rmi_realm_activate() - Active a realm
+ * rmi_realm_activate() - Activate a realm
* @rd: PA of the RD
*
- * Mark a realm as Active signalling that creation is complete and allowing
+ * Mark a realm as Active, signalling that creation is completed, allowing
* execution of the realm.
Addressed the rest of the comments.
Thank you for your detailed look at the patches, much appreciated !
Cheers
Suzuki
^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH v17 0/7] firmware: arm_rmm: Add RMM v2.0 base RMI support
2026-09-07 9:59 [PATCH v17 0/7] firmware: arm_rmm: Add RMM v2.0 base RMI support Suzuki K Poulose
` (6 preceding siblings ...)
2026-09-07 9:59 ` [PATCH v17 7/7] firmware: arm_rmm: Add wrappers for Realm related RMI commands Suzuki K Poulose
@ 2026-09-08 4:09 ` Kohei Enju
2026-09-08 5:46 ` Suzuki K Poulose
2026-09-09 10:52 ` Gavin Shan
7 siblings, 2 replies; 44+ messages in thread
From: Kohei Enju @ 2026-09-08 4:09 UTC (permalink / raw)
To: Suzuki K Poulose
Cc: kvm, kvmarm, maz, will, catalin.marinas, linux-kernel,
linux-arm-kernel, steven.price, aneesh.kumar, oupton, gshan,
joey.gouly, tabba, yuzenghui, linux-coco, gankulkarni,
sdonthineni, alpergun, fj0570is, WeiLin.Chang, lpieralisi
Hi Suzuki,
On 09/07 10:59, Suzuki K Poulose wrote:
> This series adds the generic firmware layer for talking to the Realm
> Management Monitor (RMM), as specified by the RMM v2.0-bet3
> specification[1]. It is the first part of the Arm CCA host support that
> was previously posted as part of the larger KVM series.
>
> The split allows this RMM support to be used as a base for other work,
> including Aneesh's PCI IDE support with Arm CCA RMM as the TSM,
> without depending on the KVM Realm support that will follow as separate
> series. (See more on that below)
>
> The series adds:
>
> * The RMI SMC definitions and direct-call wrappers.
>
> * RMM discovery and version checks during firmware init.
>
> * RMM host configuration, including the host page size.
>
> * Stateful RMI Operation (SRO) infrastructure for commands which the RMM
> can complete across multiple SMC calls while requesting or returning
> memory to the host.
>
> * Verification that granule tracking is available at fine granularity.
> Fine-grained tracking allows each granule in the system to be tracked
> independently, which is required before individual granules can be
> delegated. A future series will add support for dynamically supplying
> memory to the RMM for this tracking.
>
> * Support for fully firmware-managed systems, where the Granule Protection
> Tables for memory regions are allocated and managed by firmware. RMM v2.0
> also allows dynamic GPT creation on demand; support for that will be added
> in a later series.
>
> * Wrappers for the RMI commands that are used for managing the "Realm VM"
> lifecycle. This is added in to make it easier for the on-going KVM support
> to evolve in parallel pieces.
>
> If the platform firmware cannot manage the granule tracking or the GPTs, we
> bail out and deactivate the RMM, reclaiming any memory that we have donated.
>
> The RMM v2.0 spec introduces Stateful RMI Operations (SROs), which allow
> the RMM to complete an operation over several SMC calls while requesting
> or returning memory to the host. This allows interrupts to be handled in
> the middle of an operation and lets the RMM dynamically allocate memory
> for internal tracking purposes. For example, RMI_REC_CREATE no longer
> needs auxiliary granules to be provided up front, and can instead
> request memory during the operation.
>
> This series applies on v7.3-rc1 and a branch is available at [2]. The KVM
> CCA support that builds on this series is available at [3] as an integration
> branch. The KVM support depends on guest-memfd-in-place conversion support v12
> from Ackerley [4], we plan to split that into parts, which apply cleanly on
> v7.3-rcx without any dependency and is in progress. This will be made available
> as soon as it is ready. Until then [3] shows how this base series enables KVM
> CCA support. You may find the tf-RMM [5] and kvmtool support [6] below.
>
> [1] RMM spec : https://support.arm.com/documentation/den0137/2-0bet3/
> [2] This series: https://gitlab.arm.com/linux-arm/linux-cca.git cca/cca-host/fw_rmm/v17
> [3] KVM CCA v17 integration branch: https://gitlab.arm.com/linux-arm/linux-cca.git cca/cca-host/kvm-integration/v17
> [4] Gmem inplace conversion https://github.com/googleprodkernel/linux-cc/tree/guest_memfd-inplace-conversion-v12
> [5] TF-RMM https://git.trustedfirmware.org/TF-RMM/tf-rmm.git main (commit: 134266ae)
I understand that QEMU-virt is not intended to model a realistic
production platform and that QEMU-SBSA is generally preferred. However,
I sometimes use QEMU-virt to test functionality quickly because it runs
slightly faster than QEMU-SBSA.
I tested this series with QEMU-virt and found that RMM initialization
failed during boot:
[...]
INFO: BL31: Initializing RMM
INFO: RMM init start.
ERROR: RMM init failed: -8
WARNING: BL31: RMM initialization failed
The following TF-A revision was used:
https://git.trustedfirmware.org/TF-A/trusted-firmware-a.git master (38269bb)
After investigating, I found that the failure was caused by the lack of
a plat_rmmd_reserve_memory() implementation for the QEMU-virt platform
in TF-A. Adding an implementation equivalent to the QEMU-SBSA one
resolved the issue:
https://git.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a.git/+/9d594743aa4d4e8160cdf025ea7538041648972c
Is QEMU virt still intended to be supported as an RME emulation
platform? In particular, is this functionality simply not implemented
yet, or is there a reason why RME support is not planned for QEMU virt?
If this is not the appropriate place to discuss this, please let me
know :)
Thanks,
Kohei
> [6] kvmtool https://gitlab.arm.com/linux-arm/kvmtool-cca.git cca/kvm-v17
>
> Known issues: RMMv2.0
> * RmiOpMemDonateReq:count (Uint14) is incompatible with RmiAddrRangeDesc4KB
> (Uint10) and RmiAddrRangeDesc16KB (Uint12). i.e., a larger contiguous request
> may not be satisfiable by the host. Arm is aware of this defect and a spec fix
> is in progress.
>
>
> Steven Price (7):
> firmware: arm_rmm: Add SMC definitions for calling the RMM
> firmware: arm_rmm: Check for RMI support at init
> firmware: arm_rmm: Configure the RMM with the host's page size
> firmware: arm_rmm: Add support for SRO
> firmware: arm_rmm: Activate the RMM
> firmware: arm_rmm: Ensure the RMM has GPT entries for memory
> firmware: arm_rmm: Add wrappers for Realm related RMI commands
>
> arch/arm64/Kconfig | 1 +
> arch/arm64/kernel/cpufeature.c | 1 +
> drivers/firmware/Kconfig | 1 +
> drivers/firmware/Makefile | 1 +
> drivers/firmware/arm_rmm/Kconfig | 26 +
> drivers/firmware/arm_rmm/Makefile | 2 +
> drivers/firmware/arm_rmm/rmi.c | 819 ++++++++++++++++++++++++++++++
> include/linux/arm-rmi-cmds.h | 678 +++++++++++++++++++++++++
> include/linux/arm-smccc-rmi.h | 494 ++++++++++++++++++
> 9 files changed, 2023 insertions(+)
> create mode 100644 drivers/firmware/arm_rmm/Kconfig
> create mode 100644 drivers/firmware/arm_rmm/Makefile
> create mode 100644 drivers/firmware/arm_rmm/rmi.c
> create mode 100644 include/linux/arm-rmi-cmds.h
> create mode 100644 include/linux/arm-smccc-rmi.h
>
> --
> 2.43.0
>
^ permalink raw reply [flat|nested] 44+ messages in thread* Re: [PATCH v17 0/7] firmware: arm_rmm: Add RMM v2.0 base RMI support
2026-09-08 4:09 ` [PATCH v17 0/7] firmware: arm_rmm: Add RMM v2.0 base RMI support Kohei Enju
@ 2026-09-08 5:46 ` Suzuki K Poulose
2026-09-08 7:30 ` Kohei Enju
2026-09-09 10:52 ` Gavin Shan
1 sibling, 1 reply; 44+ messages in thread
From: Suzuki K Poulose @ 2026-09-08 5:46 UTC (permalink / raw)
To: Kohei Enju
Cc: kvm, kvmarm, maz, will, catalin.marinas, linux-kernel,
linux-arm-kernel, steven.price, aneesh.kumar, oupton, gshan,
joey.gouly, tabba, yuzenghui, linux-coco, gankulkarni,
sdonthineni, alpergun, fj0570is, WeiLin.Chang, lpieralisi,
Mathieu Poirier
Hi Kohei-San
Linaro is best suited to answer this. I have Cc'ed Mathieu (and
Lorenzo), who may be able to help you here.
On 08/09/2026 05:09, Kohei Enju wrote:
> Hi Suzuki,
>
> On 09/07 10:59, Suzuki K Poulose wrote:
>> This series adds the generic firmware layer for talking to the Realm
>> Management Monitor (RMM), as specified by the RMM v2.0-bet3
>> specification[1]. It is the first part of the Arm CCA host support that
>> was previously posted as part of the larger KVM series.
>>
>> The split allows this RMM support to be used as a base for other work,
>> including Aneesh's PCI IDE support with Arm CCA RMM as the TSM,
>> without depending on the KVM Realm support that will follow as separate
>> series. (See more on that below)
>>
>> The series adds:
>>
>> * The RMI SMC definitions and direct-call wrappers.
>>
>> * RMM discovery and version checks during firmware init.
>>
>> * RMM host configuration, including the host page size.
>>
>> * Stateful RMI Operation (SRO) infrastructure for commands which the RMM
>> can complete across multiple SMC calls while requesting or returning
>> memory to the host.
>>
>> * Verification that granule tracking is available at fine granularity.
>> Fine-grained tracking allows each granule in the system to be tracked
>> independently, which is required before individual granules can be
>> delegated. A future series will add support for dynamically supplying
>> memory to the RMM for this tracking.
>>
>> * Support for fully firmware-managed systems, where the Granule Protection
>> Tables for memory regions are allocated and managed by firmware. RMM v2.0
>> also allows dynamic GPT creation on demand; support for that will be added
>> in a later series.
>>
>> * Wrappers for the RMI commands that are used for managing the "Realm VM"
>> lifecycle. This is added in to make it easier for the on-going KVM support
>> to evolve in parallel pieces.
>>
>> If the platform firmware cannot manage the granule tracking or the GPTs, we
>> bail out and deactivate the RMM, reclaiming any memory that we have donated.
>>
>> The RMM v2.0 spec introduces Stateful RMI Operations (SROs), which allow
>> the RMM to complete an operation over several SMC calls while requesting
>> or returning memory to the host. This allows interrupts to be handled in
>> the middle of an operation and lets the RMM dynamically allocate memory
>> for internal tracking purposes. For example, RMI_REC_CREATE no longer
>> needs auxiliary granules to be provided up front, and can instead
>> request memory during the operation.
>>
>> This series applies on v7.3-rc1 and a branch is available at [2]. The KVM
>> CCA support that builds on this series is available at [3] as an integration
>> branch. The KVM support depends on guest-memfd-in-place conversion support v12
>> from Ackerley [4], we plan to split that into parts, which apply cleanly on
>> v7.3-rcx without any dependency and is in progress. This will be made available
>> as soon as it is ready. Until then [3] shows how this base series enables KVM
>> CCA support. You may find the tf-RMM [5] and kvmtool support [6] below.
>>
>> [1] RMM spec : https://support.arm.com/documentation/den0137/2-0bet3/
>> [2] This series: https://gitlab.arm.com/linux-arm/linux-cca.git cca/cca-host/fw_rmm/v17
>> [3] KVM CCA v17 integration branch: https://gitlab.arm.com/linux-arm/linux-cca.git cca/cca-host/kvm-integration/v17
>> [4] Gmem inplace conversion https://github.com/googleprodkernel/linux-cc/tree/guest_memfd-inplace-conversion-v12
>> [5] TF-RMM https://git.trustedfirmware.org/TF-RMM/tf-rmm.git main (commit: 134266ae)
>
> I understand that QEMU-virt is not intended to model a realistic
> production platform and that QEMU-SBSA is generally preferred. However,
> I sometimes use QEMU-virt to test functionality quickly because it runs
> slightly faster than QEMU-SBSA.
>
> I tested this series with QEMU-virt and found that RMM initialization
> failed during boot:
>
> [...]
> INFO: BL31: Initializing RMM
> INFO: RMM init start.
> ERROR: RMM init failed: -8
> WARNING: BL31: RMM initialization failed
>
> The following TF-A revision was used:
> https://git.trustedfirmware.org/TF-A/trusted-firmware-a.git master (38269bb)
>
> After investigating, I found that the failure was caused by the lack of
> a plat_rmmd_reserve_memory() implementation for the QEMU-virt platform
> in TF-A. Adding an implementation equivalent to the QEMU-SBSA one
> resolved the issue:
> https://git.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a.git/+/9d594743aa4d4e8160cdf025ea7538041648972c
>
> Is QEMU virt still intended to be supported as an RME emulation
> platform? In particular, is this functionality simply not implemented
> yet, or is there a reason why RME support is not planned for QEMU virt?
>
> If this is not the appropriate place to discuss this, please let me
Mathieu, Lorenzo,
Over to you folks :-)
Cheers
Suzuki
> know :)
>
> Thanks,
> Kohei
>
>> [6] kvmtool https://gitlab.arm.com/linux-arm/kvmtool-cca.git cca/kvm-v17
>>
>> Known issues: RMMv2.0
>> * RmiOpMemDonateReq:count (Uint14) is incompatible with RmiAddrRangeDesc4KB
>> (Uint10) and RmiAddrRangeDesc16KB (Uint12). i.e., a larger contiguous request
>> may not be satisfiable by the host. Arm is aware of this defect and a spec fix
>> is in progress.
>>
>>
>> Steven Price (7):
>> firmware: arm_rmm: Add SMC definitions for calling the RMM
>> firmware: arm_rmm: Check for RMI support at init
>> firmware: arm_rmm: Configure the RMM with the host's page size
>> firmware: arm_rmm: Add support for SRO
>> firmware: arm_rmm: Activate the RMM
>> firmware: arm_rmm: Ensure the RMM has GPT entries for memory
>> firmware: arm_rmm: Add wrappers for Realm related RMI commands
>>
>> arch/arm64/Kconfig | 1 +
>> arch/arm64/kernel/cpufeature.c | 1 +
>> drivers/firmware/Kconfig | 1 +
>> drivers/firmware/Makefile | 1 +
>> drivers/firmware/arm_rmm/Kconfig | 26 +
>> drivers/firmware/arm_rmm/Makefile | 2 +
>> drivers/firmware/arm_rmm/rmi.c | 819 ++++++++++++++++++++++++++++++
>> include/linux/arm-rmi-cmds.h | 678 +++++++++++++++++++++++++
>> include/linux/arm-smccc-rmi.h | 494 ++++++++++++++++++
>> 9 files changed, 2023 insertions(+)
>> create mode 100644 drivers/firmware/arm_rmm/Kconfig
>> create mode 100644 drivers/firmware/arm_rmm/Makefile
>> create mode 100644 drivers/firmware/arm_rmm/rmi.c
>> create mode 100644 include/linux/arm-rmi-cmds.h
>> create mode 100644 include/linux/arm-smccc-rmi.h
>>
>> --
>> 2.43.0
>>
^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH v17 0/7] firmware: arm_rmm: Add RMM v2.0 base RMI support
2026-09-08 5:46 ` Suzuki K Poulose
@ 2026-09-08 7:30 ` Kohei Enju
0 siblings, 0 replies; 44+ messages in thread
From: Kohei Enju @ 2026-09-08 7:30 UTC (permalink / raw)
To: Suzuki K Poulose
Cc: kvm, kvmarm, maz, will, catalin.marinas, linux-kernel,
linux-arm-kernel, steven.price, aneesh.kumar, oupton, gshan,
joey.gouly, tabba, yuzenghui, linux-coco, gankulkarni,
sdonthineni, alpergun, fj0570is, WeiLin.Chang, lpieralisi,
Mathieu Poirier
On 09/08 06:46, Suzuki K Poulose wrote:
> Hi Kohei-San
>
> Linaro is best suited to answer this. I have Cc'ed Mathieu (and
> Lorenzo), who may be able to help you here.
Thank you for pointing me in the right direction and Ccing Mathieu and
Lorenzo.
I'll wait for their response.
>
> On 08/09/2026 05:09, Kohei Enju wrote:
> > Hi Suzuki,
> >
> > On 09/07 10:59, Suzuki K Poulose wrote:
> > > This series adds the generic firmware layer for talking to the Realm
> > > Management Monitor (RMM), as specified by the RMM v2.0-bet3
> > > specification[1]. It is the first part of the Arm CCA host support that
> > > was previously posted as part of the larger KVM series.
> > >
> > > The split allows this RMM support to be used as a base for other work,
> > > including Aneesh's PCI IDE support with Arm CCA RMM as the TSM,
> > > without depending on the KVM Realm support that will follow as separate
> > > series. (See more on that below)
> > >
> > > The series adds:
> > >
> > > * The RMI SMC definitions and direct-call wrappers.
> > >
> > > * RMM discovery and version checks during firmware init.
> > >
> > > * RMM host configuration, including the host page size.
> > >
> > > * Stateful RMI Operation (SRO) infrastructure for commands which the RMM
> > > can complete across multiple SMC calls while requesting or returning
> > > memory to the host.
> > >
> > > * Verification that granule tracking is available at fine granularity.
> > > Fine-grained tracking allows each granule in the system to be tracked
> > > independently, which is required before individual granules can be
> > > delegated. A future series will add support for dynamically supplying
> > > memory to the RMM for this tracking.
> > >
> > > * Support for fully firmware-managed systems, where the Granule Protection
> > > Tables for memory regions are allocated and managed by firmware. RMM v2.0
> > > also allows dynamic GPT creation on demand; support for that will be added
> > > in a later series.
> > >
> > > * Wrappers for the RMI commands that are used for managing the "Realm VM"
> > > lifecycle. This is added in to make it easier for the on-going KVM support
> > > to evolve in parallel pieces.
> > >
> > > If the platform firmware cannot manage the granule tracking or the GPTs, we
> > > bail out and deactivate the RMM, reclaiming any memory that we have donated.
> > >
> > > The RMM v2.0 spec introduces Stateful RMI Operations (SROs), which allow
> > > the RMM to complete an operation over several SMC calls while requesting
> > > or returning memory to the host. This allows interrupts to be handled in
> > > the middle of an operation and lets the RMM dynamically allocate memory
> > > for internal tracking purposes. For example, RMI_REC_CREATE no longer
> > > needs auxiliary granules to be provided up front, and can instead
> > > request memory during the operation.
> > >
> > > This series applies on v7.3-rc1 and a branch is available at [2]. The KVM
> > > CCA support that builds on this series is available at [3] as an integration
> > > branch. The KVM support depends on guest-memfd-in-place conversion support v12
> > > from Ackerley [4], we plan to split that into parts, which apply cleanly on
> > > v7.3-rcx without any dependency and is in progress. This will be made available
> > > as soon as it is ready. Until then [3] shows how this base series enables KVM
> > > CCA support. You may find the tf-RMM [5] and kvmtool support [6] below.
> > >
> > > [1] RMM spec : https://support.arm.com/documentation/den0137/2-0bet3/
> > > [2] This series: https://gitlab.arm.com/linux-arm/linux-cca.git cca/cca-host/fw_rmm/v17
> > > [3] KVM CCA v17 integration branch: https://gitlab.arm.com/linux-arm/linux-cca.git cca/cca-host/kvm-integration/v17
> > > [4] Gmem inplace conversion https://github.com/googleprodkernel/linux-cc/tree/guest_memfd-inplace-conversion-v12
> > > [5] TF-RMM https://git.trustedfirmware.org/TF-RMM/tf-rmm.git main (commit: 134266ae)
> >
> > I understand that QEMU-virt is not intended to model a realistic
> > production platform and that QEMU-SBSA is generally preferred. However,
> > I sometimes use QEMU-virt to test functionality quickly because it runs
> > slightly faster than QEMU-SBSA.
> >
> > I tested this series with QEMU-virt and found that RMM initialization
> > failed during boot:
> >
> > [...]
> > INFO: BL31: Initializing RMM
> > INFO: RMM init start.
> > ERROR: RMM init failed: -8
> > WARNING: BL31: RMM initialization failed
> >
> > The following TF-A revision was used:
> > https://git.trustedfirmware.org/TF-A/trusted-firmware-a.git master (38269bb)
> >
> > After investigating, I found that the failure was caused by the lack of
> > a plat_rmmd_reserve_memory() implementation for the QEMU-virt platform
> > in TF-A. Adding an implementation equivalent to the QEMU-SBSA one
> > resolved the issue:
> > https://git.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a.git/+/9d594743aa4d4e8160cdf025ea7538041648972c
> >
> > Is QEMU virt still intended to be supported as an RME emulation
> > platform? In particular, is this functionality simply not implemented
> > yet, or is there a reason why RME support is not planned for QEMU virt?
> >
> > If this is not the appropriate place to discuss this, please let me
>
> Mathieu, Lorenzo,
>
> Over to you folks :-)
>
> Cheers
> Suzuki
>
>
> > know :)
> >
> > Thanks,
> > Kohei
> >
> > > [6] kvmtool https://gitlab.arm.com/linux-arm/kvmtool-cca.git cca/kvm-v17
> > >
> > > Known issues: RMMv2.0
> > > * RmiOpMemDonateReq:count (Uint14) is incompatible with RmiAddrRangeDesc4KB
> > > (Uint10) and RmiAddrRangeDesc16KB (Uint12). i.e., a larger contiguous request
> > > may not be satisfiable by the host. Arm is aware of this defect and a spec fix
> > > is in progress.
> > >
> > >
> > > Steven Price (7):
> > > firmware: arm_rmm: Add SMC definitions for calling the RMM
> > > firmware: arm_rmm: Check for RMI support at init
> > > firmware: arm_rmm: Configure the RMM with the host's page size
> > > firmware: arm_rmm: Add support for SRO
> > > firmware: arm_rmm: Activate the RMM
> > > firmware: arm_rmm: Ensure the RMM has GPT entries for memory
> > > firmware: arm_rmm: Add wrappers for Realm related RMI commands
> > >
> > > arch/arm64/Kconfig | 1 +
> > > arch/arm64/kernel/cpufeature.c | 1 +
> > > drivers/firmware/Kconfig | 1 +
> > > drivers/firmware/Makefile | 1 +
> > > drivers/firmware/arm_rmm/Kconfig | 26 +
> > > drivers/firmware/arm_rmm/Makefile | 2 +
> > > drivers/firmware/arm_rmm/rmi.c | 819 ++++++++++++++++++++++++++++++
> > > include/linux/arm-rmi-cmds.h | 678 +++++++++++++++++++++++++
> > > include/linux/arm-smccc-rmi.h | 494 ++++++++++++++++++
> > > 9 files changed, 2023 insertions(+)
> > > create mode 100644 drivers/firmware/arm_rmm/Kconfig
> > > create mode 100644 drivers/firmware/arm_rmm/Makefile
> > > create mode 100644 drivers/firmware/arm_rmm/rmi.c
> > > create mode 100644 include/linux/arm-rmi-cmds.h
> > > create mode 100644 include/linux/arm-smccc-rmi.h
> > >
> > > --
> > > 2.43.0
> > >
>
^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH v17 0/7] firmware: arm_rmm: Add RMM v2.0 base RMI support
2026-09-08 4:09 ` [PATCH v17 0/7] firmware: arm_rmm: Add RMM v2.0 base RMI support Kohei Enju
2026-09-08 5:46 ` Suzuki K Poulose
@ 2026-09-09 10:52 ` Gavin Shan
2026-09-10 4:51 ` Kohei Enju
1 sibling, 1 reply; 44+ messages in thread
From: Gavin Shan @ 2026-09-09 10:52 UTC (permalink / raw)
To: Kohei Enju, Suzuki K Poulose
Cc: kvm, kvmarm, maz, will, catalin.marinas, linux-kernel,
linux-arm-kernel, steven.price, aneesh.kumar, oupton, joey.gouly,
tabba, yuzenghui, linux-coco, gankulkarni, sdonthineni, alpergun,
fj0570is, WeiLin.Chang, lpieralisi
[-- Attachment #1: Type: text/plain, Size: 1222 bytes --]
Hi Kohei,
On 9/8/26 2:09 PM, Kohei Enju wrote:
[...]
>>
>> [1] RMM spec : https://support.arm.com/documentation/den0137/2-0bet3/
>> [2] This series: https://gitlab.arm.com/linux-arm/linux-cca.git cca/cca-host/fw_rmm/v17
>> [3] KVM CCA v17 integration branch: https://gitlab.arm.com/linux-arm/linux-cca.git cca/cca-host/kvm-integration/v17
>> [4] Gmem inplace conversion https://github.com/googleprodkernel/linux-cc/tree/guest_memfd-inplace-conversion-v12
>> [5] TF-RMM https://git.trustedfirmware.org/TF-RMM/tf-rmm.git main (commit: 134266ae)
>
> I understand that QEMU-virt is not intended to model a realistic
> production platform and that QEMU-SBSA is generally preferred. However,
> I sometimes use QEMU-virt to test functionality quickly because it runs
> slightly faster than QEMU-SBSA.
>
> I tested this series with QEMU-virt and found that RMM initialization
> failed during boot:
>
> [...]
> INFO: BL31: Initializing RMM
> INFO: RMM init start.
> ERROR: RMM init failed: -8
> WARNING: BL31: RMM initialization failed
>
I also ran into same issue and attached TF-A patch leads to a successful
RMM initialization, please have a try to see if it can resolve your issue.
Thanks,
Gavin
[-- Attachment #2: tf-a.patch --]
[-- Type: text/x-patch, Size: 2620 bytes --]
From c22ddc4368650550845f862fc07cb35b998fbb23 Mon Sep 17 00:00:00 2001
From: Gavin Shan <gshan@redhat.com>
Date: Wed, 9 Sep 2026 19:58:37 +1000
Subject: [PATCH] plat/qemu: add plat_rmmd_reserve_memory() for VIRT platform
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Gavin Shan <gshan@redhat.com>
---
plat/qemu/qemu/include/platform_def.h | 3 +++
plat/qemu/qemu/plat_rmm_mem_carveout.c | 28 ++++++++++++++++++++++++++
plat/qemu/qemu/platform.mk | 4 ++++
3 files changed, 35 insertions(+)
create mode 100644 plat/qemu/qemu/plat_rmm_mem_carveout.c
diff --git a/plat/qemu/qemu/include/platform_def.h b/plat/qemu/qemu/include/platform_def.h
index 06018e7b2..c3ed986cf 100644
--- a/plat/qemu/qemu/include/platform_def.h
+++ b/plat/qemu/qemu/include/platform_def.h
@@ -368,6 +368,9 @@ CASSERT((PLAT_QEMU_L0_GPT_BASE & (PLAT_QEMU_L0_GPT_SIZE - 1)) == 0,
#define RMM_SHARED_BASE (RMM_LIMIT)
#define RMM_SHARED_SIZE PLAT_QEMU_RMM_SHARED_SIZE
+#define PLAT_ARM_RMM_PAYLOAD_SIZE UL(0x600000) /* 2 * 3MB */
+#define RMM_PAYLOAD_LIMIT (RMM_BASE + PLAT_ARM_RMM_PAYLOAD_SIZE)
+
/*
* We add the RMM_SHARED size to RMM mapping to map the region as a block.
* Else we end up requiring more pagetables in BL2 for ROMLIB build.
diff --git a/plat/qemu/qemu/plat_rmm_mem_carveout.c b/plat/qemu/qemu/plat_rmm_mem_carveout.c
new file mode 100644
index 000000000..c68ca243d
--- /dev/null
+++ b/plat/qemu/qemu/plat_rmm_mem_carveout.c
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2026, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <lib/spinlock.h>
+#include <plat/common/platform.h>
+
+static spinlock_t mem_reserve_lock;
+static uintptr_t top_mem = RMM_LIMIT;
+
+uintptr_t plat_rmmd_reserve_memory(size_t size, unsigned long alignment)
+{
+ uint64_t align_mask = alignment - 1;
+ uintptr_t addr;
+
+ spin_lock(&mem_reserve_lock);
+ addr = (top_mem - size) & ~align_mask;
+ if (addr >= RMM_PAYLOAD_LIMIT) {
+ top_mem = addr;
+ } else {
+ addr = 0;
+ }
+ spin_unlock(&mem_reserve_lock);
+
+ return addr;
+}
diff --git a/plat/qemu/qemu/platform.mk b/plat/qemu/qemu/platform.mk
index 2e12f6bca..655cc683a 100644
--- a/plat/qemu/qemu/platform.mk
+++ b/plat/qemu/qemu/platform.mk
@@ -175,6 +175,10 @@ BL31_SOURCES += plat/common/plat_spmd_manifest.c \
${FDT_WRAPPERS_SOURCES}
endif
+ifeq (${ENABLE_RMM},1)
+BL31_SOURCES += ${PLAT_QEMU_PATH}/plat_rmm_mem_carveout.c
+endif
+
ifneq (${ENABLE_FEAT_RNG_TRAP},0)
BL31_SOURCES += plat/qemu/qemu/qemu_sync_traps.c
endif
--
2.55.0
^ permalink raw reply related [flat|nested] 44+ messages in thread* Re: [PATCH v17 0/7] firmware: arm_rmm: Add RMM v2.0 base RMI support
2026-09-09 10:52 ` Gavin Shan
@ 2026-09-10 4:51 ` Kohei Enju
0 siblings, 0 replies; 44+ messages in thread
From: Kohei Enju @ 2026-09-10 4:51 UTC (permalink / raw)
To: Gavin Shan
Cc: Suzuki K Poulose, kvm, kvmarm, maz, will, catalin.marinas,
linux-kernel, linux-arm-kernel, steven.price, aneesh.kumar,
oupton, joey.gouly, tabba, yuzenghui, linux-coco, gankulkarni,
sdonthineni, alpergun, fj0570is, WeiLin.Chang, lpieralisi
Hi Gavin,
On 09/09 20:52, Gavin Shan wrote:
> Hi Kohei,
>
> On 9/8/26 2:09 PM, Kohei Enju wrote:
>
> [...]
>
> > >
> > > [1] RMM spec : https://support.arm.com/documentation/den0137/2-0bet3/
> > > [2] This series: https://gitlab.arm.com/linux-arm/linux-cca.git cca/cca-host/fw_rmm/v17
> > > [3] KVM CCA v17 integration branch: https://gitlab.arm.com/linux-arm/linux-cca.git cca/cca-host/kvm-integration/v17
> > > [4] Gmem inplace conversion https://github.com/googleprodkernel/linux-cc/tree/guest_memfd-inplace-conversion-v12
> > > [5] TF-RMM https://git.trustedfirmware.org/TF-RMM/tf-rmm.git main (commit: 134266ae)
> >
> > I understand that QEMU-virt is not intended to model a realistic
> > production platform and that QEMU-SBSA is generally preferred. However,
> > I sometimes use QEMU-virt to test functionality quickly because it runs
> > slightly faster than QEMU-SBSA.
> >
> > I tested this series with QEMU-virt and found that RMM initialization
> > failed during boot:
> >
> > [...]
> > INFO: BL31: Initializing RMM
> > INFO: RMM init start.
> > ERROR: RMM init failed: -8
> > WARNING: BL31: RMM initialization failed
> >
> I also ran into same issue and attached TF-A patch leads to a successful
> RMM initialization, please have a try to see if it can resolve your issue.
Thanks for the patch! It works perfectly for me on QEMU-virt.
Much appreciated.
Thanks,
Kohei
>
>
> Thanks,
> Gavin
^ permalink raw reply [flat|nested] 44+ messages in thread