* [RFC PATCH v4 01/16] iommu/arm-smmu-v3: Discover RME support and realm IRQ topology
2026-04-27 8:53 [RFC PATCH v4 00/16] coco/TSM: Implement host-side support for Arm CCA TDISP setup Aneesh Kumar K.V (Arm)
@ 2026-04-27 8:53 ` Aneesh Kumar K.V (Arm)
2026-08-29 18:22 ` Nicolin Chen
2026-04-27 8:53 ` [RFC PATCH v4 02/16] iommu/arm-smmu-v3: Save the programmed MSI message in msi_desc Aneesh Kumar K.V (Arm)
` (15 subsequent siblings)
16 siblings, 1 reply; 40+ messages in thread
From: Aneesh Kumar K.V (Arm) @ 2026-04-27 8:53 UTC (permalink / raw)
To: linux-coco, kvmarm, linux-arm-kernel, linux-kernel
Cc: Aneesh Kumar K.V (Arm), Alexey Kardashevskiy, Catalin Marinas,
Dan Williams, Jason Gunthorpe, Joerg Roedel, Jonathan Cameron,
Marc Zyngier, Nicolin Chen, Pranjal Shrivastava, Robin Murphy,
Samuel Ortiz, Steven Price, Suzuki K Poulose, Will Deacon,
Xu Yilun
Detect RME-capable SMMUv3 instances from IDR0.RME_IMPL and record the
capability in arm_smmu_device.
When RMM is active, query RMI_PSMMU_INFO to discover how realm-side
notifications are delivered. For pSMMUs that expose realm interrupts, store
the reported evtq/gerror/priq IRQs, reserve extra MSI vectors when RMM uses
MSI delivery, and register threaded handlers that acknowledge notifications
through RMI_PSMMU_IRQ_NOTIFY / RMI_PSMMU_EVENT_CONSUME.
Also add the RMI command/structure definitions needed for PSMMU_INFO and
interrupt notification, along with arm_smmu_device state for the physical
base address and realm IRQs.
If RMM reports a CMDQ sync interrupt requirement, keep the IRQ plumbing
but leave ARM_SMMU_FEAT_RME disabled.
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
arch/arm64/include/asm/rmi_cmds.h | 52 ++++++++
arch/arm64/include/asm/rmi_smc.h | 32 ++++-
drivers/iommu/arm/arm-smmu-v3/Makefile | 2 +-
.../iommu/arm/arm-smmu-v3/arm-smmu-v3-realm.c | 124 ++++++++++++++++++
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 81 +++++++++++-
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h | 10 ++
6 files changed, 297 insertions(+), 4 deletions(-)
create mode 100644 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-realm.c
diff --git a/arch/arm64/include/asm/rmi_cmds.h b/arch/arm64/include/asm/rmi_cmds.h
index c82d4d9cbc06..75eb59d4fa84 100644
--- a/arch/arm64/include/asm/rmi_cmds.h
+++ b/arch/arm64/include/asm/rmi_cmds.h
@@ -811,4 +811,56 @@ static inline unsigned long rmi_pdev_stream_disconnect(unsigned long pdev1_phys,
return res.a0;
}
+static inline unsigned long rmi_psmmu_info(unsigned long psmmu_phys,
+ unsigned long psmmu_info_phys)
+{
+ struct arm_smccc_res res;
+
+ arm_smccc_1_1_invoke(SMC_RMI_PSMMU_INFO,
+ psmmu_phys, psmmu_info_phys, &res);
+
+ return res.a0;
+}
+
+struct rmi_psmmu_event_details {
+ u64 flags;
+ u64 event_num;
+ u64 stream_id; /* valid only if we have VSMMU event */
+ u64 fetch_addr;
+ u64 input_addr;
+ u64 syndrome;
+};
+
+static inline unsigned long rmi_psmmu_irq_notify(unsigned long psmmu_phys,
+ unsigned long irqs, struct rmi_psmmu_event_details *event)
+{
+ struct arm_smccc_1_2_regs regs = {
+ .a0 = SMC_RMI_PSMMU_IRQ_NOTIFY,
+ .a1 = psmmu_phys,
+ .a2 = irqs,
+ };
+
+ arm_smccc_1_2_invoke(®s, ®s);
+
+ event->flags = regs.a1;
+ event->event_num = regs.a2;
+ event->stream_id = regs.a3;
+ event->fetch_addr = regs.a4;
+ event->input_addr = regs.a5;
+ event->syndrome = regs.a6;
+
+ return regs.a0;
+}
+
+static inline unsigned long rmi_psmmu_event_consume(unsigned long psmmu_phys,
+ unsigned long irqs)
+{
+ struct arm_smccc_res res;
+
+ arm_smccc_1_1_invoke(SMC_RMI_PSMMU_EVENT_CONSUME,
+ psmmu_phys, irqs, &res);
+
+ return res.a0;
+}
+
#endif /* __ASM_RMI_CMDS_H */
diff --git a/arch/arm64/include/asm/rmi_smc.h b/arch/arm64/include/asm/rmi_smc.h
index 7b16f1540a0e..be1b1e95a937 100644
--- a/arch/arm64/include/asm/rmi_smc.h
+++ b/arch/arm64/include/asm/rmi_smc.h
@@ -101,7 +101,7 @@
#define SMC_RMI_PDEV_MEC_UPDATE SMC_RMI_CALL(0x01ed)
#define SMC_RMI_VSMMU_EVENT_COMPLETE SMC_RMI_CALL(0x01ee)
-#define SMC_RMI_PSMMU_EVENT_DISCARD SMC_RMI_CALL(0x01f0)
+#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)
@@ -129,6 +129,7 @@
#define SMC_RMI_OP_MEM_RECLAIM SMC_RMI_CALL(0x0209)
#define SMC_RMI_OP_CANCEL SMC_RMI_CALL(0x020a)
#define SMC_RMI_PDEV_SET_PROT SMC_RMI_CALL(0x020b)
+#define SMC_RMI_PSMMU_INFO SMC_RMI_CALL(0x020e)
#define RMI_ABI_MAJOR_VERSION 2
#define RMI_ABI_MINOR_VERSION 0
@@ -595,4 +596,33 @@ struct rmi_pdev_stream_params {
};
};
+#define RMI_PSMMU_IRQCFG_IRQ_DISABLED 0x0
+#define RMI_PSMMU_IRQCFG_IRQ_WIRED 0x1
+#define RMI_PSMMU_IRQCFG_IRQ_MSI 0x2
+#define RMI_PSMMU_IRQCFG_MASK GENMASK(1, 0)
+struct rmi_psmmu_info {
+ union {
+ struct {
+ u64 flags;
+ union {
+ u32 gerror_intr_num;
+ u8 padding1[8];
+ };
+ union {
+ u32 eventq_intr_num;
+ u8 padding2[8];
+ };
+ union {
+ u32 priq_intr_num;
+ u8 padding3[8];
+ };
+ union {
+ u32 cmdq_sync_intr_num;
+ u8 padding4[8];
+ };
+ };
+ u8 padding5[0x1000];
+ };
+};
+
#endif /* __ASM_RMI_SMC_H */
diff --git a/drivers/iommu/arm/arm-smmu-v3/Makefile b/drivers/iommu/arm/arm-smmu-v3/Makefile
index 493a659cc66b..23bd794ebeda 100644
--- a/drivers/iommu/arm/arm-smmu-v3/Makefile
+++ b/drivers/iommu/arm/arm-smmu-v3/Makefile
@@ -1,6 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_ARM_SMMU_V3) += arm_smmu_v3.o
-arm_smmu_v3-y := arm-smmu-v3.o
+arm_smmu_v3-y := arm-smmu-v3.o arm-smmu-v3-realm.o
arm_smmu_v3-$(CONFIG_ARM_SMMU_V3_IOMMUFD) += arm-smmu-v3-iommufd.o
arm_smmu_v3-$(CONFIG_ARM_SMMU_V3_SVA) += arm-smmu-v3-sva.o
arm_smmu_v3-$(CONFIG_TEGRA241_CMDQV) += tegra241-cmdqv.o
diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-realm.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-realm.c
new file mode 100644
index 000000000000..fec1a32de53c
--- /dev/null
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-realm.c
@@ -0,0 +1,124 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2026 ARM Ltd.
+ */
+
+#include <linux/interrupt.h>
+#include <asm/rmi_smc.h>
+#include <asm/rmi_cmds.h>
+
+#include "arm-smmu-v3.h"
+
+#define RMI_PSMMU_IRQ_GERROR BIT(0)
+#define RMI_PSMMU_IRQ_EVENTQ BIT(1)
+#define RMI_PSMMU_IRQ_PRIQ BIT(2)
+#define RMI_PSMMU_IRQ_CMDQ BIT(3)
+
+#define RMI_PSMMU_IRQ_EVENT_NONE 0
+#define RMI_PSMMU_IRQ_EVENT_ERROR 1
+#define RMI_PSMMU_IRQ_EVENT_PSMMU 2
+#define RMI_PSMMU_IRQ_EVENT_VSMMU 3
+#define RMI_PSMMU_IRQ_EVENT_MASK GENMASK(2, 1)
+#define RMI_PSMMU_IRQ_EVENT_SHIFT 1
+#define RMI_PSMMU_IRQ_EVENT_PENDING 0x1
+
+static irqreturn_t arm_smmu_realm_notify_thread(int irq, void *dev)
+{
+ int rmi_psmmu_event;
+ unsigned long notify_flags;
+ struct arm_smmu_device *smmu = dev;
+ struct rmi_psmmu_event_details event;
+
+ if (irq == smmu->realm_evtq_irq)
+ notify_flags = RMI_PSMMU_IRQ_EVENTQ;
+ else if (irq == smmu->realm_gerr_irq)
+ notify_flags = RMI_PSMMU_IRQ_GERROR;
+ else if (irq == smmu->realm_pri_irq)
+ notify_flags = RMI_PSMMU_IRQ_PRIQ;
+ else
+ return IRQ_HANDLED;
+
+ do {
+ if (rmi_psmmu_irq_notify(smmu->base_phys,
+ notify_flags, &event)) {
+ dev_warn(smmu->dev,
+ "failed to notify RMM of a SMMU event\n");
+ /* there is nothing much we could do. Mark it handled. */
+ return IRQ_HANDLED;
+ }
+ rmi_psmmu_event = (event.flags & RMI_PSMMU_IRQ_EVENT_MASK) >>
+ RMI_PSMMU_IRQ_EVENT_SHIFT;
+ switch (rmi_psmmu_event) {
+ case RMI_PSMMU_IRQ_EVENT_NONE:
+ break;
+ case RMI_PSMMU_IRQ_EVENT_ERROR:
+ dev_warn(smmu->dev, "SMMU Error reported\n");
+ rmi_psmmu_event_consume(smmu->base_phys, notify_flags);
+ break;
+ case RMI_PSMMU_IRQ_EVENT_PSMMU:
+ dev_warn(smmu->dev,
+ "SMMU event (event num: 0x%llx syndrome 0x%llx "
+ "fetch_addr 0x%llx input_addr 0x%llx) reported\n",
+ event.event_num, event.syndrome,
+ event.fetch_addr, event.input_addr);
+ rmi_psmmu_event_consume(smmu->base_phys, notify_flags);
+ break;
+ case RMI_PSMMU_IRQ_EVENT_VSMMU:
+ dev_warn(smmu->dev, "Wrong VSMMU event on stream 0x%llx, ignoring\n",
+ event.stream_id);
+ rmi_psmmu_event_consume(smmu->base_phys, notify_flags);
+ break;
+ }
+
+ } while (event.flags & RMI_PSMMU_IRQ_EVENT_PENDING);
+
+ return IRQ_HANDLED;
+}
+
+void arm_smmu_setup_realm_irqs(struct arm_smmu_device *smmu)
+{
+ int irq, ret;
+
+ irq = smmu->realm_evtq_irq;
+ if (irq) {
+ ret = devm_request_threaded_irq(smmu->dev, irq, NULL,
+ arm_smmu_realm_notify_thread,
+ IRQF_ONESHOT,
+ "arm-smmu-v3-realm-evtq",
+ smmu);
+ if (ret < 0)
+ dev_warn(smmu->dev, "failed to enable realm evtq irq\n");
+ } else {
+ dev_warn(smmu->dev, "no realm evtq irq - events will not be reported!\n");
+ }
+
+ irq = smmu->realm_gerr_irq;
+ if (irq) {
+ ret = devm_request_threaded_irq(smmu->dev, irq, NULL,
+ arm_smmu_realm_notify_thread,
+ IRQF_ONESHOT,
+ "arm-smmu-v3-realm-gerror",
+ smmu);
+ if (ret < 0)
+ dev_warn(smmu->dev, "failed to enable realm gerror irq\n");
+ } else {
+ dev_warn(smmu->dev, "no realm gerr irq - errors will not be reported!\n");
+ }
+
+ if (smmu->features & ARM_SMMU_FEAT_PRI) {
+ irq = smmu->realm_pri_irq;
+ if (irq) {
+
+ ret = devm_request_threaded_irq(smmu->dev, irq, NULL,
+ arm_smmu_realm_notify_thread,
+ IRQF_ONESHOT,
+ "arm-smmu-v3-realm-priq",
+ smmu);
+ if (ret < 0)
+ dev_warn(smmu->dev,
+ "failed to enable realm priq irq\n");
+ } else {
+ dev_warn(smmu->dev, "no realm priq irq - PRI will be broken\n");
+ }
+ }
+}
diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
index 4d00d796f078..d5b9ab95beea 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
@@ -29,6 +29,9 @@
#include <linux/string_choices.h>
#include <kunit/visibility.h>
#include <uapi/linux/iommufd.h>
+#include <linux/irq.h>
+#include <linux/msi.h>
+#include <asm/rmi_cmds.h>
#include "arm-smmu-v3.h"
#include "../../dma-iommu.h"
@@ -4000,10 +4003,20 @@ static void arm_smmu_free_msis(void *data)
static void arm_smmu_write_msi_msg(struct msi_desc *desc, struct msi_msg *msg)
{
+ int max_config_index = GERROR_MSI_INDEX;
phys_addr_t doorbell;
struct device *dev = msi_desc_to_dev(desc);
struct arm_smmu_device *smmu = dev_get_drvdata(dev);
- phys_addr_t *cfg = arm_smmu_msi_cfg[desc->msi_index];
+ phys_addr_t *cfg;
+
+ if (smmu->features & ARM_SMMU_FEAT_PRI)
+ max_config_index = PRIQ_MSI_INDEX;
+
+ /* Don't try to config for Realm interrupts. */
+ if (desc->msi_index > max_config_index)
+ return;
+
+ cfg = arm_smmu_msi_cfg[desc->msi_index];
doorbell = (((u64)msg->address_hi) << 32) | msg->address_lo;
doorbell &= MSI_CFG0_ADDR_MASK;
@@ -4015,6 +4028,7 @@ static void arm_smmu_write_msi_msg(struct msi_desc *desc, struct msi_msg *msg)
static void arm_smmu_setup_msis(struct arm_smmu_device *smmu)
{
+ int irq_index;
int ret, nvec = ARM_SMMU_MAX_MSIS;
struct device *dev = smmu->dev;
@@ -4035,6 +4049,13 @@ static void arm_smmu_setup_msis(struct arm_smmu_device *smmu)
return;
}
+ /*
+ * Request for realm side non secure interrupts too. Should this be condition
+ * on non-secure gic?
+ */
+ if (smmu->features & ARM_SMMU_FEAT_RME_MSI)
+ nvec = nvec * 2;
+
/* Allocate MSIs for evtq, gerror and priq. Ignore cmdq */
ret = platform_device_msi_init_and_alloc_irqs(dev, nvec, arm_smmu_write_msi_msg);
if (ret) {
@@ -4044,7 +4065,19 @@ static void arm_smmu_setup_msis(struct arm_smmu_device *smmu)
smmu->evtq.q.irq = msi_get_virq(dev, EVTQ_MSI_INDEX);
smmu->gerr_irq = msi_get_virq(dev, GERROR_MSI_INDEX);
- smmu->priq.q.irq = msi_get_virq(dev, PRIQ_MSI_INDEX);
+ irq_index = 2;
+ if (smmu->features & ARM_SMMU_FEAT_PRI) {
+ smmu->priq.q.irq = msi_get_virq(dev, PRIQ_MSI_INDEX);
+ irq_index++;
+ }
+
+ if (smmu->features & ARM_SMMU_FEAT_RME_MSI) {
+ smmu->realm_evtq_irq = msi_get_virq(dev, irq_index++);
+ smmu->realm_gerr_irq = msi_get_virq(dev, irq_index++);
+ // fixme, we should check for pri rmm capability
+ if (smmu->features & ARM_SMMU_FEAT_PRI)
+ smmu->realm_pri_irq = msi_get_virq(dev, irq_index++);
+ }
/* Add callback to free MSIs on teardown */
devm_add_action_or_reset(dev, arm_smmu_free_msis, dev);
@@ -4094,6 +4127,9 @@ static void arm_smmu_setup_unique_irqs(struct arm_smmu_device *smmu)
dev_warn(smmu->dev, "no priq irq - PRI will be broken\n");
}
}
+
+ if (smmu->features & ARM_SMMU_FEAT_RME_IRQ)
+ arm_smmu_setup_realm_irqs(smmu);
}
static int arm_smmu_setup_irqs(struct arm_smmu_device *smmu)
@@ -4464,6 +4500,9 @@ static int arm_smmu_device_hw_probe(struct arm_smmu_device *smmu)
smmu->asid_bits = reg & IDR0_ASID16 ? 16 : 8;
smmu->vmid_bits = reg & IDR0_VMID16 ? 16 : 8;
+ if (reg & IDR0_RME_IMPL)
+ smmu->features |= ARM_SMMU_FEAT_RME;
+
/* IDR1 */
reg = readl_relaxed(smmu->base + ARM_SMMU_IDR1);
if (reg & (IDR1_TABLES_PRESET | IDR1_QUEUES_PRESET | IDR1_REL)) {
@@ -4852,6 +4891,7 @@ static int arm_smmu_device_probe(struct platform_device *pdev)
return -EINVAL;
}
ioaddr = res->start;
+ smmu->base_phys = ioaddr;
/*
* Don't map the IMPLEMENTATION DEFINED regions, since they may contain
@@ -4893,6 +4933,43 @@ static int arm_smmu_device_probe(struct platform_device *pdev)
if (ret)
return ret;
+ if (rmm_is_active()) {
+ struct rmi_psmmu_info *psmmu_info;
+
+ psmmu_info = (struct rmi_psmmu_info *)get_zeroed_page(GFP_KERNEL);
+ if (!psmmu_info)
+ goto skip_rmm_config;
+
+ if (rmi_psmmu_info(smmu->base_phys, virt_to_phys(psmmu_info)))
+ smmu->features &= ~ARM_SMMU_FEAT_RME;
+
+ if ((psmmu_info->flags & RMI_PSMMU_IRQCFG_MASK) ==
+ RMI_PSMMU_IRQCFG_IRQ_DISABLED) {
+ free_page((unsigned long)psmmu_info);
+ goto skip_rmm_config;
+ }
+
+ smmu->features |= ARM_SMMU_FEAT_RME_IRQ;
+
+ if ((psmmu_info->flags & RMI_PSMMU_IRQCFG_MASK) ==
+ RMI_PSMMU_IRQCFG_IRQ_WIRED) {
+ smmu->realm_gerr_irq = psmmu_info->gerror_intr_num;
+ smmu->realm_evtq_irq = psmmu_info->eventq_intr_num;
+ smmu->realm_pri_irq = psmmu_info->priq_intr_num;
+
+ /* Disable RME FEAT because RMM need cmdq sync interrupt*/
+ if (psmmu_info->cmdq_sync_intr_num)
+ smmu->features &= ~ARM_SMMU_FEAT_RME;
+
+ } else if ((psmmu_info->flags & RMI_PSMMU_IRQCFG_MASK) ==
+ RMI_PSMMU_IRQCFG_IRQ_MSI) {
+ smmu->features |= ARM_SMMU_FEAT_RME_MSI;
+ }
+
+ free_page((unsigned long)psmmu_info);
+ }
+skip_rmm_config:
+
/* Initialise in-memory data structures */
ret = arm_smmu_init_structures(smmu);
if (ret)
diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
index 3c6d65d36164..6680516b571b 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
@@ -20,6 +20,7 @@ struct arm_vsmmu;
/* MMIO registers */
#define ARM_SMMU_IDR0 0x0
+#define IDR0_RME_IMPL (1 << 30)
#define IDR0_ST_LVL GENMASK(28, 27)
#define IDR0_ST_LVL_2LVL 1
#define IDR0_STALL_MODEL GENMASK(25, 24)
@@ -739,6 +740,7 @@ struct arm_smmu_device {
struct device *impl_dev;
const struct arm_smmu_impl_ops *impl_ops;
+ phys_addr_t base_phys;
void __iomem *base;
void __iomem *page1;
@@ -767,6 +769,9 @@ struct arm_smmu_device {
#define ARM_SMMU_FEAT_HD (1 << 22)
#define ARM_SMMU_FEAT_S2FWB (1 << 23)
#define ARM_SMMU_FEAT_BBML2 (1 << 24)
+#define ARM_SMMU_FEAT_RME (1 << 25)
+#define ARM_SMMU_FEAT_RME_IRQ (1 << 26)
+#define ARM_SMMU_FEAT_RME_MSI (1 << 27)
u32 features;
#define ARM_SMMU_OPT_SKIP_PREFETCH (1 << 0)
@@ -782,6 +787,9 @@ struct arm_smmu_device {
int gerr_irq;
int combined_irq;
+ int realm_gerr_irq;
+ int realm_evtq_irq;
+ int realm_pri_irq;
unsigned long oas; /* PA */
unsigned long pgsize_bitmap;
@@ -1096,4 +1104,6 @@ static inline int arm_vmaster_report_event(struct arm_smmu_vmaster *vmaster,
}
#endif /* CONFIG_ARM_SMMU_V3_IOMMUFD */
+void arm_smmu_setup_realm_irqs(struct arm_smmu_device *smmu);
+
#endif /* _ARM_SMMU_V3_H */
--
2.43.0
^ permalink raw reply related [flat|nested] 40+ messages in thread* Re: [RFC PATCH v4 01/16] iommu/arm-smmu-v3: Discover RME support and realm IRQ topology
2026-04-27 8:53 ` [RFC PATCH v4 01/16] iommu/arm-smmu-v3: Discover RME support and realm IRQ topology Aneesh Kumar K.V (Arm)
@ 2026-08-29 18:22 ` Nicolin Chen
2026-09-01 8:46 ` Aneesh Kumar K.V
0 siblings, 1 reply; 40+ messages in thread
From: Nicolin Chen @ 2026-08-29 18:22 UTC (permalink / raw)
To: Aneesh Kumar K.V (Arm)
Cc: linux-coco, kvmarm, linux-arm-kernel, linux-kernel,
Alexey Kardashevskiy, Catalin Marinas, Dan Williams,
Jason Gunthorpe, Joerg Roedel, Jonathan Cameron, Marc Zyngier,
Pranjal Shrivastava, Robin Murphy, Samuel Ortiz, Steven Price,
Suzuki K Poulose, Will Deacon, Xu Yilun
On Mon, Apr 27, 2026 at 02:23:29PM +0530, Aneesh Kumar K.V (Arm) wrote:
> +++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-realm.c
> @@ -0,0 +1,124 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (C) 2026 ARM Ltd.
> + */
> +
> +#include <linux/interrupt.h>
> +#include <asm/rmi_smc.h>
> +#include <asm/rmi_cmds.h>
> +
> +#include "arm-smmu-v3.h"
> +
> +#define RMI_PSMMU_IRQ_GERROR BIT(0)
> +#define RMI_PSMMU_IRQ_EVENTQ BIT(1)
> +#define RMI_PSMMU_IRQ_PRIQ BIT(2)
> +#define RMI_PSMMU_IRQ_CMDQ BIT(3)
[...]
> + if (irq == smmu->realm_evtq_irq)
> + notify_flags = RMI_PSMMU_IRQ_EVENTQ;
> + else if (irq == smmu->realm_gerr_irq)
> + notify_flags = RMI_PSMMU_IRQ_GERROR;
> + else if (irq == smmu->realm_pri_irq)
> + notify_flags = RMI_PSMMU_IRQ_PRIQ;
These are defined as BIT(x)...
> + if (rmi_psmmu_irq_notify(smmu->base_phys,
> + notify_flags, &event)) {
[...]
> + rmi_psmmu_event_consume(smmu->base_phys, notify_flags);
.. and passed to RMI_PSMMU_IRQ_NOTIFY and RMI_PSMMU_EVENT_CONSUME.
RMI_PSMMU_IRQ_NOTIFY takes RmiPsmmuIrqSet type, which is a 4-bit
field that has a range of [BIT(0), BIT(3)]. So this is correct.
However, RMI_PSMMU_EVENT_CONSUME takes RmiPsmmuIrq type, which is
a 2-bit field that has a range of [0x0, 0x3]. So this seems wrong.
> +void arm_smmu_setup_realm_irqs(struct arm_smmu_device *smmu)
> +{
> + int irq, ret;
> +
> + irq = smmu->realm_evtq_irq;
> + if (irq) {
> + ret = devm_request_threaded_irq(smmu->dev, irq, NULL,
> + arm_smmu_realm_notify_thread,
> + IRQF_ONESHOT,
> + "arm-smmu-v3-realm-evtq",
> + smmu);
These IRQ numbers are raw numbers forwarded by firmware and then
returned by RMI_PSMMU_INFO:
+ if ((psmmu_info->flags & RMI_PSMMU_IRQCFG_MASK) ==
+ RMI_PSMMU_IRQCFG_IRQ_WIRED) {
+ smmu->realm_gerr_irq = psmmu_info->gerror_intr_num;
+ smmu->realm_evtq_irq = psmmu_info->eventq_intr_num;
+ smmu->realm_pri_irq = psmmu_info->priq_intr_num;
Should they be converted to Linux IRQ numbers before forwarded to
devm_request_threaded_irq?
> @@ -782,6 +787,9 @@ struct arm_smmu_device {
>
> int gerr_irq;
> int combined_irq;
> + int realm_gerr_irq;
> + int realm_evtq_irq;
> + int realm_pri_irq;
Nit: this series adds a few realm-specific things in the top SMMU
structure. Maybe a "struct arm_realm_psmmu" can make them clearer.
Thanks
Nicolin
^ permalink raw reply [flat|nested] 40+ messages in thread* Re: [RFC PATCH v4 01/16] iommu/arm-smmu-v3: Discover RME support and realm IRQ topology
2026-08-29 18:22 ` Nicolin Chen
@ 2026-09-01 8:46 ` Aneesh Kumar K.V
2026-09-01 14:32 ` Jason Gunthorpe
0 siblings, 1 reply; 40+ messages in thread
From: Aneesh Kumar K.V @ 2026-09-01 8:46 UTC (permalink / raw)
To: Nicolin Chen
Cc: linux-coco, kvmarm, linux-arm-kernel, linux-kernel,
Alexey Kardashevskiy, Catalin Marinas, Dan Williams,
Jason Gunthorpe, Joerg Roedel, Jonathan Cameron, Marc Zyngier,
Pranjal Shrivastava, Robin Murphy, Samuel Ortiz, Steven Price,
Suzuki K Poulose, Will Deacon, Xu Yilun
Nicolin Chen <nicolinc@nvidia.com> writes:
> On Mon, Apr 27, 2026 at 02:23:29PM +0530, Aneesh Kumar K.V (Arm) wrote:
>> +++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-realm.c
>> @@ -0,0 +1,124 @@
>> +// SPDX-License-Identifier: GPL-2.0-only
>> +/*
>> + * Copyright (C) 2026 ARM Ltd.
>> + */
>> +
>> +#include <linux/interrupt.h>
>> +#include <asm/rmi_smc.h>
>> +#include <asm/rmi_cmds.h>
>> +
>> +#include "arm-smmu-v3.h"
>> +
>> +#define RMI_PSMMU_IRQ_GERROR BIT(0)
>> +#define RMI_PSMMU_IRQ_EVENTQ BIT(1)
>> +#define RMI_PSMMU_IRQ_PRIQ BIT(2)
>> +#define RMI_PSMMU_IRQ_CMDQ BIT(3)
> [...]
>> + if (irq == smmu->realm_evtq_irq)
>> + notify_flags = RMI_PSMMU_IRQ_EVENTQ;
>> + else if (irq == smmu->realm_gerr_irq)
>> + notify_flags = RMI_PSMMU_IRQ_GERROR;
>> + else if (irq == smmu->realm_pri_irq)
>> + notify_flags = RMI_PSMMU_IRQ_PRIQ;
>
> These are defined as BIT(x)...
>
>> + if (rmi_psmmu_irq_notify(smmu->base_phys,
>> + notify_flags, &event)) {
> [...]
>> + rmi_psmmu_event_consume(smmu->base_phys, notify_flags);
>
> .. and passed to RMI_PSMMU_IRQ_NOTIFY and RMI_PSMMU_EVENT_CONSUME.
>
> RMI_PSMMU_IRQ_NOTIFY takes RmiPsmmuIrqSet type, which is a 4-bit
> field that has a range of [BIT(0), BIT(3)]. So this is correct.
>
> However, RMI_PSMMU_EVENT_CONSUME takes RmiPsmmuIrq type, which is
> a 2-bit field that has a range of [0x0, 0x3]. So this seems wrong.
>
Updated that to
#define RMI_PSMMU_IRQ_PEND_GERROR BIT(0)
#define RMI_PSMMU_IRQ_PEND_EVENTQ BIT(1)
#define RMI_PSMMU_IRQ_PEND_PRIQ BIT(2)
#define RMI_PSMMU_IRQ_PEND_CMDQ BIT(3)
#define RMI_PSMMU_IRQ_GERROR 0
#define RMI_PSMMU_IRQ_EVENTQ 1
#define RMI_PSMMU_IRQ_PRIQ 2
#define RMI_PSMMU_IRQ_CMDQ 3
and
if (irq == smmu->realm.evtq_irq) {
notify_flags = RMI_PSMMU_IRQ_PEND_EVENTQ;
psmmu_irq = RMI_PSMMU_IRQ_EVENTQ;
} else if (irq == smmu->realm.gerr_irq) {
...
rmi_psmmu_event_consume(smmu->base_phys, psmmu_irq);
>
>> +void arm_smmu_setup_realm_irqs(struct arm_smmu_device *smmu)
>> +{
>> + int irq, ret;
>> +
>> + irq = smmu->realm_evtq_irq;
>> + if (irq) {
>> + ret = devm_request_threaded_irq(smmu->dev, irq, NULL,
>> + arm_smmu_realm_notify_thread,
>> + IRQF_ONESHOT,
>> + "arm-smmu-v3-realm-evtq",
>> + smmu);
>
> These IRQ numbers are raw numbers forwarded by firmware and then
> returned by RMI_PSMMU_INFO:
>
> + if ((psmmu_info->flags & RMI_PSMMU_IRQCFG_MASK) ==
> + RMI_PSMMU_IRQCFG_IRQ_WIRED) {
> + smmu->realm_gerr_irq = psmmu_info->gerror_intr_num;
> + smmu->realm_evtq_irq = psmmu_info->eventq_intr_num;
> + smmu->realm_pri_irq = psmmu_info->priq_intr_num;
>
> Should they be converted to Linux IRQ numbers before forwarded to
> devm_request_threaded_irq?
>
Yes, that needs conversion. I guess we can use the irq_domain associated
with the non-Realm IRQ number to create the mappings?
Something like:
+static struct irq_domain *arm_smmu_get_wired_irq_domain(struct arm_smmu_device *smmu)
+{
+ int irqs[] = {
+ smmu->combined_irq,
+ smmu->evtq.q.irq,
+ smmu->gerr_irq,
+ smmu->priq.q.irq,
+ };
+ struct irq_domain *domain = NULL;
+ struct irq_data *irq_data;
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(irqs); i++) {
+ if (irqs[i] <= 0)
+ continue;
+
+ irq_data = irq_get_irq_data(irqs[i]);
+ if (!irq_data || !irq_data->domain)
+ return ERR_PTR(-EINVAL);
+
+ if (domain && domain != irq_data->domain)
+ return ERR_PTR(-EINVAL);
+
+ domain = irq_data->domain;
+ }
+
+ return domain ?: ERR_PTR(-ENXIO);
+}
>
>> @@ -782,6 +787,9 @@ struct arm_smmu_device {
>>
>> int gerr_irq;
>> int combined_irq;
>> + int realm_gerr_irq;
>> + int realm_evtq_irq;
>> + int realm_pri_irq;
>
> Nit: this series adds a few realm-specific things in the top SMMU
> structure. Maybe a "struct arm_realm_psmmu" can make them clearer.
>
Added
struct arm_smmu_realm {
int gerr_irq;
int evtq_irq;
int pri_irq;
struct mutex mutex;
refcount_t users;
};
-aneesh
^ permalink raw reply [flat|nested] 40+ messages in thread* Re: [RFC PATCH v4 01/16] iommu/arm-smmu-v3: Discover RME support and realm IRQ topology
2026-09-01 8:46 ` Aneesh Kumar K.V
@ 2026-09-01 14:32 ` Jason Gunthorpe
0 siblings, 0 replies; 40+ messages in thread
From: Jason Gunthorpe @ 2026-09-01 14:32 UTC (permalink / raw)
To: Aneesh Kumar K.V
Cc: Nicolin Chen, linux-coco, kvmarm, linux-arm-kernel, linux-kernel,
Alexey Kardashevskiy, Catalin Marinas, Dan Williams, Joerg Roedel,
Jonathan Cameron, Marc Zyngier, Pranjal Shrivastava, Robin Murphy,
Samuel Ortiz, Steven Price, Suzuki K Poulose, Will Deacon,
Xu Yilun
On Tue, Sep 01, 2026 at 02:16:10PM +0530, Aneesh Kumar K.V wrote:
> +static struct irq_domain *arm_smmu_get_wired_irq_domain(struct arm_smmu_device *smmu)
> +{
> + int irqs[] = {
> + smmu->combined_irq,
> + smmu->evtq.q.irq,
> + smmu->gerr_irq,
> + smmu->priq.q.irq,
> + };
> + struct irq_domain *domain = NULL;
> + struct irq_data *irq_data;
> + unsigned int i;
> +
> + for (i = 0; i < ARRAY_SIZE(irqs); i++) {
> + if (irqs[i] <= 0)
> + continue;
> +
> + irq_data = irq_get_irq_data(irqs[i]);
> + if (!irq_data || !irq_data->domain)
> + return ERR_PTR(-EINVAL);
> +
> + if (domain && domain != irq_data->domain)
> + return ERR_PTR(-EINVAL);
> +
> + domain = irq_data->domain;
> + }
> +
> + return domain ?: ERR_PTR(-ENXIO);
I'm not excited to see code like this in a driver. Please put a helper
function someplace else to translate whatever the RMM value is into a
normal linux irq number?
But this feels wrong to me, interrupts need to come through the FW
side, ACPI or DT. Having RMM pass the value outside that
infrastructure is going to be painful. None of this is trusted, why is
it working like this vs just adding more interrupt lines to the FW
description?
A naked integer is not enough to describe an interrupt in the system,
and guessing that it is connected to other interrupts in the psmmu
seems like an uncomfortable leap to me.
Jason
^ permalink raw reply [flat|nested] 40+ messages in thread
* [RFC PATCH v4 02/16] iommu/arm-smmu-v3: Save the programmed MSI message in msi_desc
2026-04-27 8:53 [RFC PATCH v4 00/16] coco/TSM: Implement host-side support for Arm CCA TDISP setup Aneesh Kumar K.V (Arm)
2026-04-27 8:53 ` [RFC PATCH v4 01/16] iommu/arm-smmu-v3: Discover RME support and realm IRQ topology Aneesh Kumar K.V (Arm)
@ 2026-04-27 8:53 ` Aneesh Kumar K.V (Arm)
2026-04-27 8:53 ` [RFC PATCH v4 03/16] iommu/arm-smmu-v3: Add initial pSMMU realm viommu plumbing Aneesh Kumar K.V (Arm)
` (14 subsequent siblings)
16 siblings, 0 replies; 40+ messages in thread
From: Aneesh Kumar K.V (Arm) @ 2026-04-27 8:53 UTC (permalink / raw)
To: linux-coco, kvmarm, linux-arm-kernel, linux-kernel
Cc: Aneesh Kumar K.V (Arm), Alexey Kardashevskiy, Catalin Marinas,
Dan Williams, Jason Gunthorpe, Joerg Roedel, Jonathan Cameron,
Marc Zyngier, Nicolin Chen, Pranjal Shrivastava, Robin Murphy,
Samuel Ortiz, Steven Price, Suzuki K Poulose, Will Deacon,
Xu Yilun
Cache the MSI message in desc->msg from arm_smmu_write_msi_msg(). The
realm support code later reads the MSI address and data through
irq_get_msi_desc(), so it needs the descriptor to reflect the last
programmed message.
This matches the caching done by __pci_write_msi_msg().
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
index d5b9ab95beea..17fd99887aab 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
@@ -4012,6 +4012,9 @@ static void arm_smmu_write_msi_msg(struct msi_desc *desc, struct msi_msg *msg)
if (smmu->features & ARM_SMMU_FEAT_PRI)
max_config_index = PRIQ_MSI_INDEX;
+ /* save the programmed msi message details */
+ desc->msg = *msg;
+
/* Don't try to config for Realm interrupts. */
if (desc->msi_index > max_config_index)
return;
--
2.43.0
^ permalink raw reply related [flat|nested] 40+ messages in thread* [RFC PATCH v4 03/16] iommu/arm-smmu-v3: Add initial pSMMU realm viommu plumbing
2026-04-27 8:53 [RFC PATCH v4 00/16] coco/TSM: Implement host-side support for Arm CCA TDISP setup Aneesh Kumar K.V (Arm)
2026-04-27 8:53 ` [RFC PATCH v4 01/16] iommu/arm-smmu-v3: Discover RME support and realm IRQ topology Aneesh Kumar K.V (Arm)
2026-04-27 8:53 ` [RFC PATCH v4 02/16] iommu/arm-smmu-v3: Save the programmed MSI message in msi_desc Aneesh Kumar K.V (Arm)
@ 2026-04-27 8:53 ` Aneesh Kumar K.V (Arm)
2026-08-29 20:00 ` Nicolin Chen
2026-04-27 8:53 ` [RFC PATCH v4 04/16] iommu/arm-smmu-v3: Track realm pSMMU users with refcount_t Aneesh Kumar K.V (Arm)
` (13 subsequent siblings)
16 siblings, 1 reply; 40+ messages in thread
From: Aneesh Kumar K.V (Arm) @ 2026-04-27 8:53 UTC (permalink / raw)
To: linux-coco, kvmarm, linux-arm-kernel, linux-kernel
Cc: Aneesh Kumar K.V (Arm), Alexey Kardashevskiy, Catalin Marinas,
Dan Williams, Jason Gunthorpe, Joerg Roedel, Jonathan Cameron,
Marc Zyngier, Nicolin Chen, Pranjal Shrivastava, Robin Murphy,
Samuel Ortiz, Steven Price, Suzuki K Poulose, Will Deacon,
Xu Yilun
Add initial plumbing for Realm pSMMU integration in the arm-smmu-v3 iommufd
path.
Changes include:
- add RMI SMC IDs and helper wrappers for pSMMU activate and ST_L2 create/destroy
- add RMI pSMMU parameter structure definitions
- add IOMMU_VIOMMU_TYPE_ARM_REALM_SMMUV3 UAPI/internal type support
- add arm-smmu-v3 realm viommu init/vdevice hooks
- store SMMU MMIO physical base and realm initialization state in arm_smmu_device
This enables basic realm pSMMU setup and vdevice stream-table operations.
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
arch/arm64/include/asm/rmi_cmds.h | 7 +
arch/arm64/include/asm/rmi_smc.h | 18 +++
arch/arm64/kernel/rmi.c | 39 +++++
.../arm/arm-smmu-v3/arm-smmu-v3-iommufd.c | 7 +
.../iommu/arm/arm-smmu-v3/arm-smmu-v3-realm.c | 148 ++++++++++++++++++
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 1 +
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h | 4 +
include/uapi/linux/iommufd.h | 1 +
8 files changed, 225 insertions(+)
diff --git a/arch/arm64/include/asm/rmi_cmds.h b/arch/arm64/include/asm/rmi_cmds.h
index 75eb59d4fa84..659d68ad5f1d 100644
--- a/arch/arm64/include/asm/rmi_cmds.h
+++ b/arch/arm64/include/asm/rmi_cmds.h
@@ -863,4 +863,11 @@ static inline unsigned long rmi_psmmu_event_consume(unsigned long psmmu_phys,
return res.a0;
}
+int rmi_psmmu_activate(unsigned long psmmu_phys,
+ unsigned long psmmu_params_phys, unsigned long *rmi_ret);
+int rmi_psmmu_st_l2_create(unsigned long psmmu_phys,
+ unsigned long stream_id, unsigned long *rmi_ret);
+int rmi_psmmu_st_l2_destroy(unsigned long psmmu_phys,
+ unsigned long stream_id, unsigned long *rmi_ret);
+
#endif /* __ASM_RMI_CMDS_H */
diff --git a/arch/arm64/include/asm/rmi_smc.h b/arch/arm64/include/asm/rmi_smc.h
index be1b1e95a937..5b540d25914e 100644
--- a/arch/arm64/include/asm/rmi_smc.h
+++ b/arch/arm64/include/asm/rmi_smc.h
@@ -625,4 +625,22 @@ struct rmi_psmmu_info {
};
};
+#define RMI_PSMMU_FLAG_MSI BIT(0)
+#define RMI_PSMMU_FLAG_ATS BIT(1)
+#define RMI_PSMMU_FLAG_PRI BIT(2)
+struct rmi_psmmu_params {
+ union {
+ struct {
+ u64 flags;
+ u64 grr_addr;
+ u64 grr_data;
+ u64 eventq_addr;
+ u64 eventq_data;
+ u64 priq_addr;
+ u64 priq_data;
+ };
+ u8 padding5[0x1000];
+ };
+};
+
#endif /* __ASM_RMI_SMC_H */
diff --git a/arch/arm64/kernel/rmi.c b/arch/arm64/kernel/rmi.c
index da4707981548..cc4050db5a6a 100644
--- a/arch/arm64/kernel/rmi.c
+++ b/arch/arm64/kernel/rmi.c
@@ -423,6 +423,45 @@ unsigned long rmi_sro_execute(struct rmi_sro_state *sro)
return regs.a0;
}
+int rmi_psmmu_activate(unsigned long psmmu_phys,
+ unsigned long psmmu_params_phys, unsigned long *rmi_ret)
+{
+ struct rmi_sro_state *sro __free(sro) =
+ rmi_sro_init(SMC_RMI_PSMMU_ACTIVATE, psmmu_phys, psmmu_params_phys);
+ if (!sro)
+ return -ENOMEM;
+
+ *rmi_ret = rmi_sro_execute(sro);
+
+ return 0;
+}
+
+int rmi_psmmu_st_l2_create(unsigned long psmmu_phys,
+ unsigned long stream_id, unsigned long *rmi_ret)
+{
+ struct rmi_sro_state *sro __free(sro) =
+ rmi_sro_init(SMC_RMI_PSMMU_ST_L2_CREATE, psmmu_phys, stream_id);
+ if (!sro)
+ return -ENOMEM;
+
+ *rmi_ret = rmi_sro_execute(sro);
+
+ return 0;
+}
+
+int rmi_psmmu_st_l2_destroy(unsigned long psmmu_phys,
+ unsigned long stream_id, unsigned long *rmi_ret)
+{
+ struct rmi_sro_state *sro __free(sro) =
+ rmi_sro_init(SMC_RMI_PSMMU_ST_L2_DESTROY, psmmu_phys, stream_id);
+ if (!sro)
+ return -ENOMEM;
+
+ *rmi_ret = rmi_sro_execute(sro);
+
+ return 0;
+}
+
static int rmi_configure(void)
{
struct rmm_config *config __free(free_page) = NULL;
diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c
index ddae0b07c76b..c98e91b3ca13 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c
@@ -437,6 +437,9 @@ size_t arm_smmu_get_viommu_size(struct device *dev,
if (viommu_type == IOMMU_VIOMMU_TYPE_ARM_SMMUV3)
return VIOMMU_STRUCT_SIZE(struct arm_vsmmu, core);
+ if (viommu_type == IOMMU_VIOMMU_TYPE_ARM_REALM_SMMUV3)
+ return VIOMMU_STRUCT_SIZE(struct arm_vsmmu, core);
+
if (!smmu->impl_ops || !smmu->impl_ops->get_viommu_size)
return 0;
return smmu->impl_ops->get_viommu_size(viommu_type);
@@ -464,6 +467,10 @@ int arm_vsmmu_init(struct iommufd_viommu *viommu,
return 0;
}
+ if (viommu->type == IOMMU_VIOMMU_TYPE_ARM_REALM_SMMUV3)
+ return arm_realm_smmu_v3_init(viommu, user_data);
+
+
return smmu->impl_ops->vsmmu_init(vsmmu, user_data);
}
diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-realm.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-realm.c
index fec1a32de53c..6f8de7cead9d 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-realm.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-realm.c
@@ -6,6 +6,7 @@
#include <linux/interrupt.h>
#include <asm/rmi_smc.h>
#include <asm/rmi_cmds.h>
+#include <asm/kvm_emulate.h>
#include "arm-smmu-v3.h"
@@ -122,3 +123,150 @@ void arm_smmu_setup_realm_irqs(struct arm_smmu_device *smmu)
}
}
}
+
+static void arm_realm_smmu_v3_destroy(struct iommufd_viommu *viommu)
+{
+ /* When we add refcount psmmu deactivate here. */
+}
+
+static void arm_realm_smmu_v3_vdevice_destroy(struct iommufd_vdevice *vdev)
+{
+ struct device *dev = iommufd_vdevice_to_device(vdev);
+ struct arm_smmu_master *master = dev_iommu_priv_get(dev);
+ /* FIXME which stream to pick */
+ /* At this moment, iommufd only supports PCI device that has one SID */
+ struct arm_smmu_stream *stream = &master->streams[0];
+ struct arm_smmu_device *smmu = master->smmu;
+ unsigned long rmi_ret = 0;
+ int ret;
+
+ if (!smmu->realm_initialized)
+ return;
+
+ ret = rmi_psmmu_st_l2_destroy(smmu->base_phys,
+ ALIGN_DOWN(stream->id, STRTAB_NUM_L2_STES),
+ &rmi_ret);
+ if (ret || rmi_ret) {
+
+ /* Table in use */
+ if (RMI_RETURN_STATUS(rmi_ret) == RMI_ERROR_PSMMU_ST &&
+ RMI_RETURN_INDEX(rmi_ret) == 2)
+ return;
+
+ dev_warn(dev, "failed to destroy realm stream mapping\n");
+ }
+}
+
+static int arm_realm_smmu_v3_vdevice_init(struct iommufd_vdevice *vdev)
+{
+ struct device *dev = iommufd_vdevice_to_device(vdev);
+ struct arm_smmu_master *master = dev_iommu_priv_get(dev);
+ // fixme which stream to pick
+ /* At this moment, iommufd only supports PCI device that has one SID */
+ struct arm_smmu_stream *stream = &master->streams[0];
+ struct arm_smmu_device *smmu = master->smmu;
+ unsigned long rmi_ret = 0;
+ int ret;
+
+ if (!smmu->realm_initialized)
+ return -EINVAL;
+
+ ret = rmi_psmmu_st_l2_create(smmu->base_phys,
+ ALIGN_DOWN(stream->id, STRTAB_NUM_L2_STES),
+ &rmi_ret);
+ if (ret || rmi_ret) {
+ if (!ret)
+ return -EIO;
+ if (RMI_RETURN_STATUS(rmi_ret) == RMI_ERROR_PSMMU_ST &&
+ RMI_RETURN_INDEX(rmi_ret) == 2) {
+ /* table already exist */
+ vdev->destroy = arm_realm_smmu_v3_vdevice_destroy;
+ return 0;
+ }
+ dev_warn(dev, "failed to create realm stream mapping\n");
+ return -EIO;
+ }
+ vdev->destroy = arm_realm_smmu_v3_vdevice_destroy;
+ return 0;
+}
+
+static const struct iommufd_viommu_ops arm_realm_smmu_v3_ops = {
+ .destroy = arm_realm_smmu_v3_destroy,
+ .alloc_domain_nested = arm_vsmmu_alloc_domain_nested,
+ .cache_invalidate = arm_vsmmu_cache_invalidate,
+ .vdevice_init = arm_realm_smmu_v3_vdevice_init,
+};
+
+static int get_irq_data(int irq, u64 *msi_addr, u64 *msi_data)
+{
+ struct msi_desc *desc;
+
+ desc = irq_get_msi_desc(irq);
+ if (!desc)
+ return -EINVAL;
+
+ *msi_addr = (((u64)desc->msg.address_hi) << 32) | desc->msg.address_lo;
+ *msi_data = desc->msg.data;
+ return 0;
+}
+
+int arm_realm_smmu_v3_init(struct iommufd_viommu *viommu,
+ const struct iommu_user_data *user_data)
+{
+ int ret = 0;
+ struct kvm *kvm = viommu->kvm;
+ struct rmi_psmmu_params *params;
+ struct arm_smmu_device *smmu =
+ container_of(viommu->iommu_dev, struct arm_smmu_device, iommu);
+ unsigned long rmi_ret;
+
+ if (!kvm)
+ return -EINVAL;
+
+ if (!kvm_is_realm(kvm))
+ return -EINVAL;
+
+ if (!(smmu->features & ARM_SMMU_FEAT_RME))
+ return -EOPNOTSUPP;
+
+ if (smmu->realm_initialized)
+ goto psmmu_already_active;
+
+ params = (struct rmi_psmmu_params *)get_zeroed_page(GFP_KERNEL);
+ if (!params)
+ return -ENOMEM;
+
+ /* No ATS and PRI support */
+ if (!(smmu->features & ARM_SMMU_FEAT_MSI))
+ goto psmmu_activate;
+
+ params->flags = RMI_PSMMU_FLAG_MSI;
+ if (get_irq_data(smmu->realm_gerr_irq,
+ ¶ms->grr_addr, ¶ms->grr_data)) {
+ ret = -EINVAL;
+ goto out_free;
+ }
+ if (get_irq_data(smmu->realm_evtq_irq,
+ ¶ms->eventq_addr, ¶ms->eventq_data)) {
+ ret = -EINVAL;
+ goto out_free;
+ }
+
+psmmu_activate:
+ ret = rmi_psmmu_activate(smmu->base_phys, virt_to_phys(params),
+ &rmi_ret);
+ if (ret || rmi_ret) {
+ if (!ret)
+ ret = -EIO;
+ dev_warn(smmu->dev, "failed to activate realm pSMMU\n");
+ ret = -EIO;
+ } else {
+ smmu->realm_initialized = true;
+ }
+out_free:
+ free_page((unsigned long)params);
+psmmu_already_active:
+ if (!ret)
+ viommu->ops = &arm_realm_smmu_v3_ops;
+ return ret;
+}
diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
index 17fd99887aab..1e3d4d682e32 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
@@ -3956,6 +3956,7 @@ static int arm_smmu_init_structures(struct arm_smmu_device *smmu)
if (ret)
return ret;
+ smmu->realm_initialized = false;
if (smmu->impl_ops && smmu->impl_ops->init_structures)
return smmu->impl_ops->init_structures(smmu);
diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
index 6680516b571b..d528b3212d38 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
@@ -811,6 +811,8 @@ struct arm_smmu_device {
struct rb_root streams;
struct mutex streams_mutex;
+
+ bool realm_initialized;
};
struct arm_smmu_stream {
@@ -1073,6 +1075,8 @@ arm_vsmmu_alloc_domain_nested(struct iommufd_viommu *viommu, u32 flags,
const struct iommu_user_data *user_data);
int arm_vsmmu_cache_invalidate(struct iommufd_viommu *viommu,
struct iommu_user_data_array *array);
+int arm_realm_smmu_v3_init(struct iommufd_viommu *viommu,
+ const struct iommu_user_data *user_data);
#else
#define arm_smmu_get_viommu_size NULL
#define arm_smmu_hw_info NULL
diff --git a/include/uapi/linux/iommufd.h b/include/uapi/linux/iommufd.h
index 47213663c0c1..74afc9967c3e 100644
--- a/include/uapi/linux/iommufd.h
+++ b/include/uapi/linux/iommufd.h
@@ -1055,6 +1055,7 @@ enum iommu_viommu_type {
IOMMU_VIOMMU_TYPE_DEFAULT = 0,
IOMMU_VIOMMU_TYPE_ARM_SMMUV3 = 1,
IOMMU_VIOMMU_TYPE_TEGRA241_CMDQV = 2,
+ IOMMU_VIOMMU_TYPE_ARM_REALM_SMMUV3 = 3,
};
/**
--
2.43.0
^ permalink raw reply related [flat|nested] 40+ messages in thread* Re: [RFC PATCH v4 03/16] iommu/arm-smmu-v3: Add initial pSMMU realm viommu plumbing
2026-04-27 8:53 ` [RFC PATCH v4 03/16] iommu/arm-smmu-v3: Add initial pSMMU realm viommu plumbing Aneesh Kumar K.V (Arm)
@ 2026-08-29 20:00 ` Nicolin Chen
2026-09-01 9:17 ` Aneesh Kumar K.V
0 siblings, 1 reply; 40+ messages in thread
From: Nicolin Chen @ 2026-08-29 20:00 UTC (permalink / raw)
To: Aneesh Kumar K.V (Arm)
Cc: linux-coco, kvmarm, linux-arm-kernel, linux-kernel,
Alexey Kardashevskiy, Catalin Marinas, Dan Williams,
Jason Gunthorpe, Joerg Roedel, Jonathan Cameron, Marc Zyngier,
Pranjal Shrivastava, Robin Murphy, Samuel Ortiz, Steven Price,
Suzuki K Poulose, Will Deacon, Xu Yilun
On Mon, Apr 27, 2026 at 02:23:31PM +0530, Aneesh Kumar K.V (Arm) wrote:
> +static const struct iommufd_viommu_ops arm_realm_smmu_v3_ops = {
> + .destroy = arm_realm_smmu_v3_destroy,
> + .alloc_domain_nested = arm_vsmmu_alloc_domain_nested,
> + .cache_invalidate = arm_vsmmu_cache_invalidate,
I don't think realm vsmmu should include NS nested domain ops. I
wonder if adding here is for some covert reason that prevents us
from registering viommu/vdevice objects?
> +static int arm_realm_smmu_v3_vdevice_init(struct iommufd_vdevice *vdev)
> +{
> + struct device *dev = iommufd_vdevice_to_device(vdev);
> + struct arm_smmu_master *master = dev_iommu_priv_get(dev);
> + // fixme which stream to pick
> + /* At this moment, iommufd only supports PCI device that has one SID */
> + struct arm_smmu_stream *stream = &master->streams[0];
> + struct arm_smmu_device *smmu = master->smmu;
> + unsigned long rmi_ret = 0;
> + int ret;
> +
> + if (!smmu->realm_initialized)
> + return -EINVAL;
> +
> + ret = rmi_psmmu_st_l2_create(smmu->base_phys,
> + ALIGN_DOWN(stream->id, STRTAB_NUM_L2_STES),
> + &rmi_ret);
The "vdevice" is for a PSMMU stream table allocation..
> +int arm_realm_smmu_v3_init(struct iommufd_viommu *viommu,
> + const struct iommu_user_data *user_data)
> +{
[...]
> +psmmu_activate:
> + ret = rmi_psmmu_activate(smmu->base_phys, virt_to_phys(params),
> + &rmi_ret);
.. and the "viommu" is also for PSMMU activation...
> +++ b/include/uapi/linux/iommufd.h
> @@ -1055,6 +1055,7 @@ enum iommu_viommu_type {
> IOMMU_VIOMMU_TYPE_DEFAULT = 0,
> IOMMU_VIOMMU_TYPE_ARM_SMMUV3 = 1,
> IOMMU_VIOMMU_TYPE_TEGRA241_CMDQV = 2,
> + IOMMU_VIOMMU_TYPE_ARM_REALM_SMMUV3 = 3,
.. and we demand userspace (VMM) to use IOMMU_VIOMMU_ALLOC ioctl,
even if VMM does not actually expose a guest-level SMMU instance.
Thus, no user_data.
I can get the reasoning behind the flow using this viommu/vdevice.
But, on the other hand, I can imagine that a Realm VSMMU would add
a new flag with a user_data to this VIOMMU. Then, this flow would
give some troubles to VMM (QEMU for example):
- For VM with a guest-level SMMU, QEMU creates a realm instance
where IOMMU_VIOMMU_TYPE_ARM_REALM_SMMUV3 (with vsmmu) can be
allocated.
- For VM w/o a guest-level SMMU, QEMU won't create such a realm
instance, while still required to invoke the ioctl (w/o vsmmu).
Taking a step back, I wonder if we really need to use iommufd for
PSMMU activation and its stream table allocations?
Here are some facts:
- An iommufd has a ctx, that's one per VM. Similarly, a Realm
has an RD.
- For an RMI command that needs an RD, it makes sense to be per
iommufd ctx, e.g. RMI_VSMMU_* or RMI_VDEV_* commands.
- PSMMU commands are global; they don't need RD. So they don't
seem necessary to tie to an iommufd ctx.
Instead, could the PSMMU activation be done after RMI_PSMMU_INFO
check? Is there any reason not to do that? A safer timing might
be at the device assignment stage?
Speaking of which, RMI_PSMMU_ST_L2_CREATE doesn't seem necessary
to be invoked in a vdevice context either. Maybe it should align
with iommufd idev's lifecycle?
Nicolin
^ permalink raw reply [flat|nested] 40+ messages in thread* Re: [RFC PATCH v4 03/16] iommu/arm-smmu-v3: Add initial pSMMU realm viommu plumbing
2026-08-29 20:00 ` Nicolin Chen
@ 2026-09-01 9:17 ` Aneesh Kumar K.V
2026-09-01 10:06 ` Aneesh Kumar K.V
2026-09-01 17:36 ` Nicolin Chen
0 siblings, 2 replies; 40+ messages in thread
From: Aneesh Kumar K.V @ 2026-09-01 9:17 UTC (permalink / raw)
To: Nicolin Chen
Cc: linux-coco, kvmarm, linux-arm-kernel, linux-kernel,
Alexey Kardashevskiy, Catalin Marinas, Dan Williams,
Jason Gunthorpe, Joerg Roedel, Jonathan Cameron, Marc Zyngier,
Pranjal Shrivastava, Robin Murphy, Samuel Ortiz, Steven Price,
Suzuki K Poulose, Will Deacon, Xu Yilun
Nicolin Chen <nicolinc@nvidia.com> writes:
> On Mon, Apr 27, 2026 at 02:23:31PM +0530, Aneesh Kumar K.V (Arm) wrote:
>> +static const struct iommufd_viommu_ops arm_realm_smmu_v3_ops = {
>> + .destroy = arm_realm_smmu_v3_destroy,
>> + .alloc_domain_nested = arm_vsmmu_alloc_domain_nested,
>> + .cache_invalidate = arm_vsmmu_cache_invalidate,
>
> I don't think realm vsmmu should include NS nested domain ops. I
> wonder if adding here is for some covert reason that prevents us
> from registering viommu/vdevice objects?
>
>> +static int arm_realm_smmu_v3_vdevice_init(struct iommufd_vdevice *vdev)
>> +{
>> + struct device *dev = iommufd_vdevice_to_device(vdev);
>> + struct arm_smmu_master *master = dev_iommu_priv_get(dev);
>> + // fixme which stream to pick
>> + /* At this moment, iommufd only supports PCI device that has one SID */
>> + struct arm_smmu_stream *stream = &master->streams[0];
>> + struct arm_smmu_device *smmu = master->smmu;
>> + unsigned long rmi_ret = 0;
>> + int ret;
>> +
>> + if (!smmu->realm_initialized)
>> + return -EINVAL;
>> +
>> + ret = rmi_psmmu_st_l2_create(smmu->base_phys,
>> + ALIGN_DOWN(stream->id, STRTAB_NUM_L2_STES),
>> + &rmi_ret);
>
> The "vdevice" is for a PSMMU stream table allocation..
>
>> +int arm_realm_smmu_v3_init(struct iommufd_viommu *viommu,
>> + const struct iommu_user_data *user_data)
>> +{
> [...]
>> +psmmu_activate:
>> + ret = rmi_psmmu_activate(smmu->base_phys, virt_to_phys(params),
>> + &rmi_ret);
>
> .. and the "viommu" is also for PSMMU activation...
>
>> +++ b/include/uapi/linux/iommufd.h
>> @@ -1055,6 +1055,7 @@ enum iommu_viommu_type {
>> IOMMU_VIOMMU_TYPE_DEFAULT = 0,
>> IOMMU_VIOMMU_TYPE_ARM_SMMUV3 = 1,
>> IOMMU_VIOMMU_TYPE_TEGRA241_CMDQV = 2,
>> + IOMMU_VIOMMU_TYPE_ARM_REALM_SMMUV3 = 3,
>
> .. and we demand userspace (VMM) to use IOMMU_VIOMMU_ALLOC ioctl,
> even if VMM does not actually expose a guest-level SMMU instance.
> Thus, no user_data.
>
> I can get the reasoning behind the flow using this viommu/vdevice.
>
> But, on the other hand, I can imagine that a Realm VSMMU would add
> a new flag with a user_data to this VIOMMU. Then, this flow would
> give some troubles to VMM (QEMU for example):
>
> - For VM with a guest-level SMMU, QEMU creates a realm instance
> where IOMMU_VIOMMU_TYPE_ARM_REALM_SMMUV3 (with vsmmu) can be
> allocated.
> - For VM w/o a guest-level SMMU, QEMU won't create such a realm
> instance, while still required to invoke the ioctl (w/o vsmmu).
>
> Taking a step back, I wonder if we really need to use iommufd for
> PSMMU activation and its stream table allocations?
>
> Here are some facts:
> - An iommufd has a ctx, that's one per VM. Similarly, a Realm
> has an RD.
> - For an RMI command that needs an RD, it makes sense to be per
> iommufd ctx, e.g. RMI_VSMMU_* or RMI_VDEV_* commands.
> - PSMMU commands are global; they don't need RD. So they don't
> seem necessary to tie to an iommufd ctx.
>
> Instead, could the PSMMU activation be done after RMI_PSMMU_INFO
> check? Is there any reason not to do that? A safer timing might
> be at the device assignment stage?
>
One of the reasons I added IOMMU_VIOMMU_TYPE_ARM_REALM_SMMUV3 was to
avoid creating a psmmu object when we are not using a PCI passthrough
VM. That is also the reason for all the refcounting around the psmmu
objects.
If we are okay with creating psmmu objects early, then I guess we can go
with the above approach.
>
> Speaking of which, RMI_PSMMU_ST_L2_CREATE doesn't seem necessary
> to be invoked in a vdevice context either. Maybe it should align
> with iommufd idev's lifecycle?
>
-aneesh
^ permalink raw reply [flat|nested] 40+ messages in thread* Re: [RFC PATCH v4 03/16] iommu/arm-smmu-v3: Add initial pSMMU realm viommu plumbing
2026-09-01 9:17 ` Aneesh Kumar K.V
@ 2026-09-01 10:06 ` Aneesh Kumar K.V
2026-09-01 14:34 ` Jason Gunthorpe
2026-09-01 17:36 ` Nicolin Chen
1 sibling, 1 reply; 40+ messages in thread
From: Aneesh Kumar K.V @ 2026-09-01 10:06 UTC (permalink / raw)
To: Nicolin Chen
Cc: linux-coco, kvmarm, linux-arm-kernel, linux-kernel,
Alexey Kardashevskiy, Catalin Marinas, Dan Williams,
Jason Gunthorpe, Joerg Roedel, Jonathan Cameron, Marc Zyngier,
Pranjal Shrivastava, Robin Murphy, Samuel Ortiz, Steven Price,
Suzuki K Poulose, Will Deacon, Xu Yilun
Aneesh Kumar K.V <aneesh.kumar@kernel.org> writes:
> Nicolin Chen <nicolinc@nvidia.com> writes:
>
>> On Mon, Apr 27, 2026 at 02:23:31PM +0530, Aneesh Kumar K.V (Arm) wrote:
...
>> .. and we demand userspace (VMM) to use IOMMU_VIOMMU_ALLOC ioctl,
>> even if VMM does not actually expose a guest-level SMMU instance.
>> Thus, no user_data.
>>
>> I can get the reasoning behind the flow using this viommu/vdevice.
>>
>> But, on the other hand, I can imagine that a Realm VSMMU would add
>> a new flag with a user_data to this VIOMMU. Then, this flow would
>> give some troubles to VMM (QEMU for example):
>>
>> - For VM with a guest-level SMMU, QEMU creates a realm instance
>> where IOMMU_VIOMMU_TYPE_ARM_REALM_SMMUV3 (with vsmmu) can be
>> allocated.
>> - For VM w/o a guest-level SMMU, QEMU won't create such a realm
>> instance, while still required to invoke the ioctl (w/o vsmmu).
>>
>> Taking a step back, I wonder if we really need to use iommufd for
>> PSMMU activation and its stream table allocations?
>>
>> Here are some facts:
>> - An iommufd has a ctx, that's one per VM. Similarly, a Realm
>> has an RD.
>> - For an RMI command that needs an RD, it makes sense to be per
>> iommufd ctx, e.g. RMI_VSMMU_* or RMI_VDEV_* commands.
>> - PSMMU commands are global; they don't need RD. So they don't
>> seem necessary to tie to an iommufd ctx.
>>
>> Instead, could the PSMMU activation be done after RMI_PSMMU_INFO
>> check? Is there any reason not to do that? A safer timing might
>> be at the device assignment stage?
>>
>
> One of the reasons I added IOMMU_VIOMMU_TYPE_ARM_REALM_SMMUV3 was to
> avoid creating a psmmu object when we are not using a PCI passthrough
> VM. That is also the reason for all the refcounting around the psmmu
> objects.
>
> If we are okay with creating psmmu objects early, then I guess we can go
> with the above approach.
>
How about?
modified drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c
@@ -437,9 +437,6 @@
if (viommu_type == IOMMU_VIOMMU_TYPE_ARM_SMMUV3)
return VIOMMU_STRUCT_SIZE(struct arm_vsmmu, core);
- if (viommu_type == IOMMU_VIOMMU_TYPE_ARM_REALM_SMMUV3)
- return VIOMMU_STRUCT_SIZE(struct arm_vsmmu, core);
-
if (!smmu->impl_ops || !smmu->impl_ops->get_viommu_size)
return 0;
return smmu->impl_ops->get_viommu_size(viommu_type);
@@ -463,14 +460,13 @@
vsmmu->vmid = s2_parent->s2_cfg.vmid;
if (viommu->type == IOMMU_VIOMMU_TYPE_ARM_SMMUV3) {
+ if (arm_smmu_is_realm_viommu(viommu))
+ return arm_realm_smmu_v3_init(viommu, user_data);
+
viommu->ops = &arm_vsmmu_ops;
return 0;
}
- if (viommu->type == IOMMU_VIOMMU_TYPE_ARM_REALM_SMMUV3)
- return arm_realm_smmu_v3_init(viommu, user_data);
-
-
return smmu->impl_ops->vsmmu_init(vsmmu, user_data);
}
modified drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-realm.c
@@ -239,21 +239,27 @@
return 0;
}
+bool arm_smmu_is_realm_viommu(struct iommufd_viommu *viommu)
+{
+ struct kvm *kvm;
+
+ if (!viommu->kvm_file)
+ return false;
+
+ kvm = viommu->kvm_file->private_data;
+ return kvm_is_realm(kvm);
+}
+
int arm_realm_smmu_v3_init(struct iommufd_viommu *viommu,
const struct iommu_user_data *user_data)
{
int ret = 0;
- struct kvm *kvm;
struct rmi_psmmu_params *params;
struct arm_smmu_device *smmu =
container_of(viommu->iommu_dev, struct arm_smmu_device, iommu);
unsigned long rmi_ret;
- if (!viommu->kvm_file)
- return -EINVAL;
-
- kvm = viommu->kvm_file->private_data;
- if (!kvm_is_realm(kvm))
+ if (!arm_smmu_is_realm_viommu(viommu))
return -EINVAL;
if (!(smmu->features & ARM_SMMU_FEAT_RME))
modified drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
@@ -4515,8 +4515,6 @@
int ret;
mutex_init(&smmu->streams_mutex);
- mutex_init(&smmu->realm.mutex);
- refcount_set(&smmu->realm.users, 0);
smmu->streams = RB_ROOT;
ret = arm_smmu_init_queues(smmu);
@@ -5578,6 +5576,8 @@
return -ENODEV;
}
+ mutex_init(&smmu->realm.mutex);
+ refcount_set(&smmu->realm.users, 0);
smmu->features |= ARM_SMMU_FEAT_RME_IRQ;
return 0;
modified drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
@@ -1282,6 +1282,7 @@
const struct iommu_user_data *user_data);
int arm_vsmmu_cache_invalidate(struct iommufd_viommu *viommu,
struct iommu_user_data_array *array);
+bool arm_smmu_is_realm_viommu(struct iommufd_viommu *viommu);
int arm_realm_smmu_v3_init(struct iommufd_viommu *viommu,
const struct iommu_user_data *user_data);
#else
modified include/uapi/linux/iommufd.h
@@ -1068,7 +1068,6 @@
* VMM must wire the HYP_OWN bit to 0 in guest VINTF_CONFIG register
*/
IOMMU_VIOMMU_TYPE_TEGRA241_CMDQV = 2,
- IOMMU_VIOMMU_TYPE_ARM_REALM_SMMUV3 = 3,
};
/**
^ permalink raw reply [flat|nested] 40+ messages in thread* Re: [RFC PATCH v4 03/16] iommu/arm-smmu-v3: Add initial pSMMU realm viommu plumbing
2026-09-01 10:06 ` Aneesh Kumar K.V
@ 2026-09-01 14:34 ` Jason Gunthorpe
2026-09-01 17:13 ` Nicolin Chen
2026-09-02 9:00 ` Aneesh Kumar K.V
0 siblings, 2 replies; 40+ messages in thread
From: Jason Gunthorpe @ 2026-09-01 14:34 UTC (permalink / raw)
To: Aneesh Kumar K.V
Cc: Nicolin Chen, linux-coco, kvmarm, linux-arm-kernel, linux-kernel,
Alexey Kardashevskiy, Catalin Marinas, Dan Williams, Joerg Roedel,
Jonathan Cameron, Marc Zyngier, Pranjal Shrivastava, Robin Murphy,
Samuel Ortiz, Steven Price, Suzuki K Poulose, Will Deacon,
Xu Yilun
On Tue, Sep 01, 2026 at 03:36:37PM +0530, Aneesh Kumar K.V wrote:
> @@ -463,14 +460,13 @@
> vsmmu->vmid = s2_parent->s2_cfg.vmid;
>
> if (viommu->type == IOMMU_VIOMMU_TYPE_ARM_SMMUV3) {
> + if (arm_smmu_is_realm_viommu(viommu))
> + return arm_realm_smmu_v3_init(viommu, user_data);
> +
I think the realm vsmmu is going to require a different info struct
than the normal psmmu case, isn't it?
If so it needs its own enum value.
It would be nice to see a draft patch showing how the real vsmmu works
on top of the RMM spec for it. If we are using a viommu object then
non-vsmmu case should be identical just with an option in the info
struct to not create the vsmmu object.
Jason
^ permalink raw reply [flat|nested] 40+ messages in thread* Re: [RFC PATCH v4 03/16] iommu/arm-smmu-v3: Add initial pSMMU realm viommu plumbing
2026-09-01 14:34 ` Jason Gunthorpe
@ 2026-09-01 17:13 ` Nicolin Chen
2026-09-01 17:18 ` Nicolin Chen
` (2 more replies)
2026-09-02 9:00 ` Aneesh Kumar K.V
1 sibling, 3 replies; 40+ messages in thread
From: Nicolin Chen @ 2026-09-01 17:13 UTC (permalink / raw)
To: Jason Gunthorpe
Cc: Aneesh Kumar K.V, linux-coco, kvmarm, linux-arm-kernel,
linux-kernel, Alexey Kardashevskiy, Catalin Marinas, Dan Williams,
Joerg Roedel, Jonathan Cameron, Marc Zyngier, Pranjal Shrivastava,
Robin Murphy, Samuel Ortiz, Steven Price, Suzuki K Poulose,
Will Deacon, Xu Yilun
On Tue, Sep 01, 2026 at 11:34:45AM -0300, Jason Gunthorpe wrote:
> On Tue, Sep 01, 2026 at 03:36:37PM +0530, Aneesh Kumar K.V wrote:
>
> > @@ -463,14 +460,13 @@
> > vsmmu->vmid = s2_parent->s2_cfg.vmid;
> >
> > if (viommu->type == IOMMU_VIOMMU_TYPE_ARM_SMMUV3) {
> > + if (arm_smmu_is_realm_viommu(viommu))
> > + return arm_realm_smmu_v3_init(viommu, user_data);
> > +
>
> I think the realm vsmmu is going to require a different info struct
> than the normal psmmu case, isn't it?
>
> If so it needs its own enum value.
>
> It would be nice to see a draft patch showing how the real vsmmu works
> on top of the RMM spec for it. If we are using a viommu object then
> non-vsmmu case should be identical just with an option in the info
> struct to not create the vsmmu object.
Actually, the PSMMU-only viommu object in this series only adds a
type with no viommu data structure.
I've drafted the vsmmu case on top of the series, so it would add
a structure reusing the same IOMMU_VIOMMU_TYPE_ARM_SMMUV3 with a
flag:
+/**
+ * enum iommu_viommu_arm_realm_vsmmuv3_flags - Flags for ARM SMMUv3 Realm
+ * @IOMMU_VIOMMU_ARM_REALM_SMMUV3_FLAGS_VSMMU: Indicates whether the Realm has
+ * a guest-visible VSMMU instance
+ */
+enum iommu_viommu_arm_realm_vsmmuv3_flags {
+ IOMMU_VIOMMU_ARM_REALM_SMMUV3_FLAGS_VSMMU = 1 << 0,
+};
+
+/**
+ * struct iommu_viommu_arm_realm_vsmmuv3 - ARM Realm VSMMUv3 parameters
+ * (IOMMU_VIOMMU_TYPE_ARM_REALM_SMMUV3)
+ * @flags: Combination of enum iommu_viommu_arm_realm_vsmmuv3_flags
+ * @reg_base: MMIO base address of the VSMMU in the guest VM
+ * @reg_top: MMIO top address of the VSMMU in the guest VM
+ * @aidr: AIDR register value of the VSMMU in the guest VM
+ * @idr: IDR register values of the VSMMU in the guest VM
+ */
+struct iommu_viommu_arm_realm_vsmmuv3 {
+ __aligned_u64 flags;
+ __aligned_le64 reg_base;
+ __aligned_le64 reg_top;
+ __aligned_le64 aidr;
+ __aligned_le64 idr[7];
+};
I think this should work. But I still feel awkward that a non-vsmmu
case has to allocate a viommu object for a set of RMI commands that
don't need an Realm Descriptor. Things could be cleaner if we allow
RMI_PSMMU_ACTIVATE and RMI_PSMMU_ST_L2_CREATE to be independent on
a viommu; then leave IOMMU_VIOMMU_TYPE_ARM_SMMUV3 to vsmmu-visiable
case.
Nicolin
^ permalink raw reply [flat|nested] 40+ messages in thread* Re: [RFC PATCH v4 03/16] iommu/arm-smmu-v3: Add initial pSMMU realm viommu plumbing
2026-09-01 17:13 ` Nicolin Chen
@ 2026-09-01 17:18 ` Nicolin Chen
2026-09-01 17:45 ` Jason Gunthorpe
2026-09-01 17:42 ` Jason Gunthorpe
2026-09-02 13:10 ` Aneesh Kumar K.V
2 siblings, 1 reply; 40+ messages in thread
From: Nicolin Chen @ 2026-09-01 17:18 UTC (permalink / raw)
To: Jason Gunthorpe
Cc: Aneesh Kumar K.V, linux-coco, kvmarm, linux-arm-kernel,
linux-kernel, Alexey Kardashevskiy, Catalin Marinas, Dan Williams,
Joerg Roedel, Jonathan Cameron, Marc Zyngier, Pranjal Shrivastava,
Robin Murphy, Samuel Ortiz, Steven Price, Suzuki K Poulose,
Will Deacon, Xu Yilun
On Tue, Sep 01, 2026 at 10:13:11AM -0700, Nicolin Chen wrote:
> On Tue, Sep 01, 2026 at 11:34:45AM -0300, Jason Gunthorpe wrote:
> > On Tue, Sep 01, 2026 at 03:36:37PM +0530, Aneesh Kumar K.V wrote:
> >
> > > @@ -463,14 +460,13 @@
> > > vsmmu->vmid = s2_parent->s2_cfg.vmid;
> > >
> > > if (viommu->type == IOMMU_VIOMMU_TYPE_ARM_SMMUV3) {
> > > + if (arm_smmu_is_realm_viommu(viommu))
> > > + return arm_realm_smmu_v3_init(viommu, user_data);
> > > +
> >
> > I think the realm vsmmu is going to require a different info struct
> > than the normal psmmu case, isn't it?
> >
> > If so it needs its own enum value.
> >
> > It would be nice to see a draft patch showing how the real vsmmu works
> > on top of the RMM spec for it. If we are using a viommu object then
> > non-vsmmu case should be identical just with an option in the info
> > struct to not create the vsmmu object.
>
> Actually, the PSMMU-only viommu object in this series only adds a
> type with no viommu data structure.
>
> I've drafted the vsmmu case on top of the series, so it would add
> a structure reusing the same IOMMU_VIOMMU_TYPE_ARM_SMMUV3 with a
> flag:
Oh. I replied too fast that I didn't notice this is under the
standard IOMMU_VIOMMU_TYPE_ARM_SMMUV3 used by non-secure vSTE.
That doesn't look correct for a no-vsmmu case..
Nicolin
^ permalink raw reply [flat|nested] 40+ messages in thread* Re: [RFC PATCH v4 03/16] iommu/arm-smmu-v3: Add initial pSMMU realm viommu plumbing
2026-09-01 17:18 ` Nicolin Chen
@ 2026-09-01 17:45 ` Jason Gunthorpe
0 siblings, 0 replies; 40+ messages in thread
From: Jason Gunthorpe @ 2026-09-01 17:45 UTC (permalink / raw)
To: Nicolin Chen
Cc: Aneesh Kumar K.V, linux-coco, kvmarm, linux-arm-kernel,
linux-kernel, Alexey Kardashevskiy, Catalin Marinas, Dan Williams,
Joerg Roedel, Jonathan Cameron, Marc Zyngier, Pranjal Shrivastava,
Robin Murphy, Samuel Ortiz, Steven Price, Suzuki K Poulose,
Will Deacon, Xu Yilun
On Tue, Sep 01, 2026 at 10:18:33AM -0700, Nicolin Chen wrote:
> On Tue, Sep 01, 2026 at 10:13:11AM -0700, Nicolin Chen wrote:
> > On Tue, Sep 01, 2026 at 11:34:45AM -0300, Jason Gunthorpe wrote:
> > > On Tue, Sep 01, 2026 at 03:36:37PM +0530, Aneesh Kumar K.V wrote:
> > >
> > > > @@ -463,14 +460,13 @@
> > > > vsmmu->vmid = s2_parent->s2_cfg.vmid;
> > > >
> > > > if (viommu->type == IOMMU_VIOMMU_TYPE_ARM_SMMUV3) {
> > > > + if (arm_smmu_is_realm_viommu(viommu))
> > > > + return arm_realm_smmu_v3_init(viommu, user_data);
> > > > +
> > >
> > > I think the realm vsmmu is going to require a different info struct
> > > than the normal psmmu case, isn't it?
> > >
> > > If so it needs its own enum value.
> > >
> > > It would be nice to see a draft patch showing how the real vsmmu works
> > > on top of the RMM spec for it. If we are using a viommu object then
> > > non-vsmmu case should be identical just with an option in the info
> > > struct to not create the vsmmu object.
> >
> > Actually, the PSMMU-only viommu object in this series only adds a
> > type with no viommu data structure.
> >
> > I've drafted the vsmmu case on top of the series, so it would add
> > a structure reusing the same IOMMU_VIOMMU_TYPE_ARM_SMMUV3 with a
> > flag:
>
> Oh. I replied too fast that I didn't notice this is under the
> standard IOMMU_VIOMMU_TYPE_ARM_SMMUV3 used by non-secure vSTE.
The original patches were OK:
@@ -1055,6 +1055,7 @@ enum iommu_viommu_type {
IOMMU_VIOMMU_TYPE_DEFAULT = 0,
IOMMU_VIOMMU_TYPE_ARM_SMMUV3 = 1,
IOMMU_VIOMMU_TYPE_TEGRA241_CMDQV = 2,
+ IOMMU_VIOMMU_TYPE_ARM_REALM_SMMUV3 = 3,
That's where everything should be placed, including your new struct
above.
Jason
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [RFC PATCH v4 03/16] iommu/arm-smmu-v3: Add initial pSMMU realm viommu plumbing
2026-09-01 17:13 ` Nicolin Chen
2026-09-01 17:18 ` Nicolin Chen
@ 2026-09-01 17:42 ` Jason Gunthorpe
2026-09-01 19:08 ` Nicolin Chen
2026-09-02 13:10 ` Aneesh Kumar K.V
2 siblings, 1 reply; 40+ messages in thread
From: Jason Gunthorpe @ 2026-09-01 17:42 UTC (permalink / raw)
To: Nicolin Chen
Cc: Aneesh Kumar K.V, linux-coco, kvmarm, linux-arm-kernel,
linux-kernel, Alexey Kardashevskiy, Catalin Marinas, Dan Williams,
Joerg Roedel, Jonathan Cameron, Marc Zyngier, Pranjal Shrivastava,
Robin Murphy, Samuel Ortiz, Steven Price, Suzuki K Poulose,
Will Deacon, Xu Yilun
On Tue, Sep 01, 2026 at 10:13:04AM -0700, Nicolin Chen wrote:
> +/**
> + * enum iommu_viommu_arm_realm_vsmmuv3_flags - Flags for ARM SMMUv3 Realm
> + * @IOMMU_VIOMMU_ARM_REALM_SMMUV3_FLAGS_VSMMU: Indicates whether the Realm has
> + * a guest-visible VSMMU instance
> + */
> +enum iommu_viommu_arm_realm_vsmmuv3_flags {
> + IOMMU_VIOMMU_ARM_REALM_SMMUV3_FLAGS_VSMMU = 1 << 0,
> +};
> +
> +/**
> + * struct iommu_viommu_arm_realm_vsmmuv3 - ARM Realm VSMMUv3 parameters
> + * (IOMMU_VIOMMU_TYPE_ARM_REALM_SMMUV3)
> + * @flags: Combination of enum iommu_viommu_arm_realm_vsmmuv3_flags
> + * @reg_base: MMIO base address of the VSMMU in the guest VM
> + * @reg_top: MMIO top address of the VSMMU in the guest VM
> + * @aidr: AIDR register value of the VSMMU in the guest VM
> + * @idr: IDR register values of the VSMMU in the guest VM
> + */
> +struct iommu_viommu_arm_realm_vsmmuv3 {
> + __aligned_u64 flags;
> + __aligned_le64 reg_base;
> + __aligned_le64 reg_top;
> + __aligned_le64 aidr;
> + __aligned_le64 idr[7];
> +};
Yeah, broadly what I would expect. Pass everything needed to execute
RMI_VSMMU_CREATE through this struct.
Is there anything more than RMI_VSMMU_CREATE needed from a RMM
perspective? What about that dpt/ats stuff?
> I think this should work. But I still feel awkward that a non-vsmmu
> case has to allocate a viommu object for a set of RMI commands that
> don't need an Realm Descriptor.
It is for the RMI_VDEV_CREATE which needs the RD:
case IOMMU_VDEVICE_TSM_BIND:
rc = tsm_bind(vdev->idev->dev, kvm, vdev->virt_id);
break;
It has to be tied to a vdevice on a viommu to pick up the kvm and
vSID.
If we don't do that we need a new way to get the virt_id and kvm into
the flow, which doesn't really seem worthwhile to me.
> Things could be cleaner if we allow RMI_PSMMU_ACTIVATE and
> RMI_PSMMU_ST_L2_CREATE to be independent on a viommu; then leave
> IOMMU_VIOMMU_TYPE_ARM_SMMUV3 to vsmmu-visiable case.
This is why I asked in the other message if RMM spec is clear that
PSMMU and STE are not required for anything but VDEV_CREATE. If so,
the PDEV create and SPDM stuff is fuly independent.
Which is why I'm saying the split doesn't make sense. PSMMU,
interrupts, STE, VDEV are all related objects that should be managed
together by the SMMUv3 driver. You need a PDEV to create a STE, and
you need a STE to create a VDEV.
PDEV is the SPDM channel and should be managed by the TSM driver.
So, I think the IOMMU_VDEVICE_TSM_BIND is not justified. "BIND" should
happen when the SMMUv3 realm viommu ops create the vdevice. The same
way the vcmdq sets up the VSID tables when the vdevice is created. Is
there a reason to have it in its own command?
Further the implementation of IOMMU_VDEVICE_TSM_BIND in this series
*requires* a viommu to work. So OK, let's lean into that. (to be clear
I am saying delete tsm_bind)
It means the other arches will have to implement viommu APIs in their
iommu drivers before they have really defined their actual secure
vIOMMU definitions. That seems manageable.
Jason
^ permalink raw reply [flat|nested] 40+ messages in thread* Re: [RFC PATCH v4 03/16] iommu/arm-smmu-v3: Add initial pSMMU realm viommu plumbing
2026-09-01 17:42 ` Jason Gunthorpe
@ 2026-09-01 19:08 ` Nicolin Chen
2026-09-02 0:21 ` Nicolin Chen
2026-09-02 1:51 ` Jason Gunthorpe
0 siblings, 2 replies; 40+ messages in thread
From: Nicolin Chen @ 2026-09-01 19:08 UTC (permalink / raw)
To: Jason Gunthorpe
Cc: Aneesh Kumar K.V, linux-coco, kvmarm, linux-arm-kernel,
linux-kernel, Alexey Kardashevskiy, Catalin Marinas, Dan Williams,
Joerg Roedel, Jonathan Cameron, Marc Zyngier, Pranjal Shrivastava,
Robin Murphy, Samuel Ortiz, Steven Price, Suzuki K Poulose,
Will Deacon, Xu Yilun
On Tue, Sep 01, 2026 at 02:42:30PM -0300, Jason Gunthorpe wrote:
> On Tue, Sep 01, 2026 at 10:13:04AM -0700, Nicolin Chen wrote:
>
> > +/**
> > + * enum iommu_viommu_arm_realm_vsmmuv3_flags - Flags for ARM SMMUv3 Realm
> > + * @IOMMU_VIOMMU_ARM_REALM_SMMUV3_FLAGS_VSMMU: Indicates whether the Realm has
> > + * a guest-visible VSMMU instance
> > + */
> > +enum iommu_viommu_arm_realm_vsmmuv3_flags {
> > + IOMMU_VIOMMU_ARM_REALM_SMMUV3_FLAGS_VSMMU = 1 << 0,
> > +};
> > +
> > +/**
> > + * struct iommu_viommu_arm_realm_vsmmuv3 - ARM Realm VSMMUv3 parameters
> > + * (IOMMU_VIOMMU_TYPE_ARM_REALM_SMMUV3)
> > + * @flags: Combination of enum iommu_viommu_arm_realm_vsmmuv3_flags
> > + * @reg_base: MMIO base address of the VSMMU in the guest VM
> > + * @reg_top: MMIO top address of the VSMMU in the guest VM
> > + * @aidr: AIDR register value of the VSMMU in the guest VM
> > + * @idr: IDR register values of the VSMMU in the guest VM
> > + */
> > +struct iommu_viommu_arm_realm_vsmmuv3 {
> > + __aligned_u64 flags;
> > + __aligned_le64 reg_base;
> > + __aligned_le64 reg_top;
> > + __aligned_le64 aidr;
> > + __aligned_le64 idr[7];
> > +};
>
> Yeah, broadly what I would expect. Pass everything needed to execute
> RMI_VSMMU_CREATE through this struct.
>
> Is there anything more than RMI_VSMMU_CREATE needed from a RMM
> perspective? What about that dpt/ats stuff?
DPT is a bit hacky currently..
Prior to RMM v2.0 ABI, DPT was allocated statically in RMM; there
was no RMI command for DPT allocations.
Now, with RMM v2.0 ABI, DPT can be managed via RMIs. I am making
it follow GPT at this point, similar to the static idea. But, in
the long run, I will think of decoupling, but I haven't looked at
that closely.
> > I think this should work. But I still feel awkward that a non-vsmmu
> > case has to allocate a viommu object for a set of RMI commands that
> > don't need an Realm Descriptor.
>
> It is for the RMI_VDEV_CREATE which needs the RD:
>
> case IOMMU_VDEVICE_TSM_BIND:
> rc = tsm_bind(vdev->idev->dev, kvm, vdev->virt_id);
> break;
>
> It has to be tied to a vdevice on a viommu to pick up the kvm and
> vSID.
>
> If we don't do that we need a new way to get the virt_id and kvm into
> the flow, which doesn't really seem worthwhile to me.
Okay. I see the gap now...
For tsm_bind: idev has dev, idev has kvm, but idev has no vBDF.
Maybe we could allow vdevice to have no viommu? In which way, VMM
can forward vBDF independently. If VMM has a vsmmu instance, then
it can allocate vdevice in the traditional way.
The viommu object here is an empty vehicle that almost has no use
but to allow another vehicle (vdevice) on top to forward vBDF. It
makes VMM code a bit awkward: there is no iommu instance, so VMM
doesn't enable iommu driver, then there is no good place to alloc
the viomu object for VIOMMU_TYPE_ARM_REALM_SMMUV3.
> > Things could be cleaner if we allow RMI_PSMMU_ACTIVATE and
> > RMI_PSMMU_ST_L2_CREATE to be independent on a viommu; then leave
> > IOMMU_VIOMMU_TYPE_ARM_SMMUV3 to vsmmu-visiable case.
>
> This is why I asked in the other message if RMM spec is clear that
> PSMMU and STE are not required for anything but VDEV_CREATE. If so,
> the PDEV create and SPDM stuff is fuly independent.
I think the spec is clear. And you are right PDEV is independent,
only linked to PSMMU when VDEV is created.
> Which is why I'm saying the split doesn't make sense. PSMMU,
> interrupts, STE, VDEV are all related objects that should be managed
> together by the SMMUv3 driver. You need a PDEV to create a STE, and
> you need a STE to create a VDEV.
>
> PDEV is the SPDM channel and should be managed by the TSM driver.
>
> So, I think the IOMMU_VDEVICE_TSM_BIND is not justified. "BIND" should
> happen when the SMMUv3 realm viommu ops create the vdevice. The same
> way the vcmdq sets up the VSID tables when the vdevice is created. Is
> there a reason to have it in its own command?
>
> Further the implementation of IOMMU_VDEVICE_TSM_BIND in this series
> *requires* a viommu to work. So OK, let's lean into that. (to be clear
> I am saying delete tsm_bind)
Yea, IOMMU_VDEVICE_TSM_BIND feels redundant..
Thanks
Nicolin
^ permalink raw reply [flat|nested] 40+ messages in thread* Re: [RFC PATCH v4 03/16] iommu/arm-smmu-v3: Add initial pSMMU realm viommu plumbing
2026-09-01 19:08 ` Nicolin Chen
@ 2026-09-02 0:21 ` Nicolin Chen
2026-09-02 1:51 ` Jason Gunthorpe
1 sibling, 0 replies; 40+ messages in thread
From: Nicolin Chen @ 2026-09-02 0:21 UTC (permalink / raw)
To: Jason Gunthorpe
Cc: Aneesh Kumar K.V, linux-coco, kvmarm, linux-arm-kernel,
linux-kernel, Alexey Kardashevskiy, Catalin Marinas, Dan Williams,
Joerg Roedel, Jonathan Cameron, Marc Zyngier, Pranjal Shrivastava,
Robin Murphy, Samuel Ortiz, Steven Price, Suzuki K Poulose,
Will Deacon, Xu Yilun
On Tue, Sep 01, 2026 at 12:12:22PM -0700, Nicolin Chen wrote:
> On Tue, Sep 01, 2026 at 02:42:30PM -0300, Jason Gunthorpe wrote:
> > On Tue, Sep 01, 2026 at 10:13:04AM -0700, Nicolin Chen wrote:
> > Is there anything more than RMI_VSMMU_CREATE needed from a RMM
> > perspective? What about that dpt/ats stuff?
>
> DPT is a bit hacky currently..
>
> Prior to RMM v2.0 ABI, DPT was allocated statically in RMM; there
> was no RMI command for DPT allocations.
>
> Now, with RMM v2.0 ABI, DPT can be managed via RMIs. I am making
> it follow GPT at this point, similar to the static idea. But, in
> the long run, I will think of decoupling, but I haven't looked at
> that closely.
Did a quick check. It's clear.
DPT is a system-wise global thing, not per SMMU or per realm. Each
table or entry is indexed by part of 48-bit in PA corresponding to
to a protected IPA. One thing that is related to a realm is a VMID
in the leaf entry. So, it should unlikely be coupling with VSMMU.
The other stuff is to map VSMMU's MMIO region in its IPA space but
it should be a part of the VSMMU-CREATE package.
Nicolin
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [RFC PATCH v4 03/16] iommu/arm-smmu-v3: Add initial pSMMU realm viommu plumbing
2026-09-01 19:08 ` Nicolin Chen
2026-09-02 0:21 ` Nicolin Chen
@ 2026-09-02 1:51 ` Jason Gunthorpe
1 sibling, 0 replies; 40+ messages in thread
From: Jason Gunthorpe @ 2026-09-02 1:51 UTC (permalink / raw)
To: Nicolin Chen
Cc: Aneesh Kumar K.V, linux-coco, kvmarm, linux-arm-kernel,
linux-kernel, Alexey Kardashevskiy, Catalin Marinas, Dan Williams,
Joerg Roedel, Jonathan Cameron, Marc Zyngier, Pranjal Shrivastava,
Robin Murphy, Samuel Ortiz, Steven Price, Suzuki K Poulose,
Will Deacon, Xu Yilun
On Tue, Sep 01, 2026 at 12:08:38PM -0700, Nicolin Chen wrote:
> The viommu object here is an empty vehicle that almost has no use
> but to allow another vehicle (vdevice) on top to forward vBDF. It
> makes VMM code a bit awkward: there is no iommu instance, so VMM
> doesn't enable iommu driver, then there is no good place to alloc
> the viomu object for VIOMMU_TYPE_ARM_REALM_SMMUV3.
It would have to allocate singletons viommu for each psmmu as part of
setting up the vdevices.
Somehow I'd rather the vmm deal with that then invent a whole new set
of uapis and flows..
At best maybe we can invent a kernel side helper to create the
singletons like the auto domains, but IDK if it is really so hard for
the VMM (especially compared with how hard everything else is in CC)
Jason
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [RFC PATCH v4 03/16] iommu/arm-smmu-v3: Add initial pSMMU realm viommu plumbing
2026-09-01 17:13 ` Nicolin Chen
2026-09-01 17:18 ` Nicolin Chen
2026-09-01 17:42 ` Jason Gunthorpe
@ 2026-09-02 13:10 ` Aneesh Kumar K.V
2 siblings, 0 replies; 40+ messages in thread
From: Aneesh Kumar K.V @ 2026-09-02 13:10 UTC (permalink / raw)
To: Nicolin Chen, Jason Gunthorpe
Cc: linux-coco, kvmarm, linux-arm-kernel, linux-kernel,
Alexey Kardashevskiy, Catalin Marinas, Dan Williams, Joerg Roedel,
Jonathan Cameron, Marc Zyngier, Pranjal Shrivastava, Robin Murphy,
Samuel Ortiz, Steven Price, Suzuki K Poulose, Will Deacon,
Xu Yilun
Nicolin Chen <nicolinc@nvidia.com> writes:
> On Tue, Sep 01, 2026 at 11:34:45AM -0300, Jason Gunthorpe wrote:
>> On Tue, Sep 01, 2026 at 03:36:37PM +0530, Aneesh Kumar K.V wrote:
>>
>> > @@ -463,14 +460,13 @@
>> > vsmmu->vmid = s2_parent->s2_cfg.vmid;
>> >
>> > if (viommu->type == IOMMU_VIOMMU_TYPE_ARM_SMMUV3) {
>> > + if (arm_smmu_is_realm_viommu(viommu))
>> > + return arm_realm_smmu_v3_init(viommu, user_data);
>> > +
>>
>> I think the realm vsmmu is going to require a different info struct
>> than the normal psmmu case, isn't it?
>>
>> If so it needs its own enum value.
>>
>> It would be nice to see a draft patch showing how the real vsmmu works
>> on top of the RMM spec for it. If we are using a viommu object then
>> non-vsmmu case should be identical just with an option in the info
>> struct to not create the vsmmu object.
>
> Actually, the PSMMU-only viommu object in this series only adds a
> type with no viommu data structure.
>
> I've drafted the vsmmu case on top of the series, so it would add
> a structure reusing the same IOMMU_VIOMMU_TYPE_ARM_SMMUV3 with a
> flag:
>
> +/**
> + * enum iommu_viommu_arm_realm_vsmmuv3_flags - Flags for ARM SMMUv3 Realm
> + * @IOMMU_VIOMMU_ARM_REALM_SMMUV3_FLAGS_VSMMU: Indicates whether the Realm has
> + * a guest-visible VSMMU instance
> + */
> +enum iommu_viommu_arm_realm_vsmmuv3_flags {
> + IOMMU_VIOMMU_ARM_REALM_SMMUV3_FLAGS_VSMMU = 1 << 0,
> +};
> +
> +/**
> + * struct iommu_viommu_arm_realm_vsmmuv3 - ARM Realm VSMMUv3 parameters
> + * (IOMMU_VIOMMU_TYPE_ARM_REALM_SMMUV3)
> + * @flags: Combination of enum iommu_viommu_arm_realm_vsmmuv3_flags
> + * @reg_base: MMIO base address of the VSMMU in the guest VM
> + * @reg_top: MMIO top address of the VSMMU in the guest VM
> + * @aidr: AIDR register value of the VSMMU in the guest VM
> + * @idr: IDR register values of the VSMMU in the guest VM
> + */
> +struct iommu_viommu_arm_realm_vsmmuv3 {
> + __aligned_u64 flags;
> + __aligned_le64 reg_base;
> + __aligned_le64 reg_top;
> + __aligned_le64 aidr;
> + __aligned_le64 idr[7];
> +};
>
> I think this should work. But I still feel awkward that a non-vsmmu
> case has to allocate a viommu object for a set of RMI commands that
> don't need an Realm Descriptor. Things could be cleaner if we allow
> RMI_PSMMU_ACTIVATE and RMI_PSMMU_ST_L2_CREATE to be independent on
> a viommu; then leave IOMMU_VIOMMU_TYPE_ARM_SMMUV3 to vsmmu-visiable
> case.
RMI_VSMMU_CREATE requires an RD, RD won't be available during
viomm_alloc right?
-aneesh
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [RFC PATCH v4 03/16] iommu/arm-smmu-v3: Add initial pSMMU realm viommu plumbing
2026-09-01 14:34 ` Jason Gunthorpe
2026-09-01 17:13 ` Nicolin Chen
@ 2026-09-02 9:00 ` Aneesh Kumar K.V
2026-09-02 12:17 ` Jason Gunthorpe
1 sibling, 1 reply; 40+ messages in thread
From: Aneesh Kumar K.V @ 2026-09-02 9:00 UTC (permalink / raw)
To: Jason Gunthorpe
Cc: Nicolin Chen, linux-coco, kvmarm, linux-arm-kernel, linux-kernel,
Alexey Kardashevskiy, Catalin Marinas, Dan Williams, Joerg Roedel,
Jonathan Cameron, Marc Zyngier, Pranjal Shrivastava, Robin Murphy,
Samuel Ortiz, Steven Price, Suzuki K Poulose, Will Deacon,
Xu Yilun
Jason Gunthorpe <jgg@ziepe.ca> writes:
> On Tue, Sep 01, 2026 at 03:36:37PM +0530, Aneesh Kumar K.V wrote:
>
>> @@ -463,14 +460,13 @@
>> vsmmu->vmid = s2_parent->s2_cfg.vmid;
>>
>> if (viommu->type == IOMMU_VIOMMU_TYPE_ARM_SMMUV3) {
>> + if (arm_smmu_is_realm_viommu(viommu))
>> + return arm_realm_smmu_v3_init(viommu, user_data);
>> +
>
> I think the realm vsmmu is going to require a different info struct
> than the normal psmmu case, isn't it?
>
> If so it needs its own enum value.
>
> It would be nice to see a draft patch showing how the real vsmmu works
> on top of the RMM spec for it. If we are using a viommu object then
> non-vsmmu case should be identical just with an option in the info
> struct to not create the vsmmu object.
>
Based on feedback on other emails in this thread, I have now implemented
this without using a vdevice or viommu. This should make the CCA and
non-CCA cases similar.
This ends up adding:
modified drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
@@ -4324,6 +4324,8 @@ static const struct iommu_ops arm_smmu_ops = {
.def_domain_type = arm_smmu_def_domain_type,
.get_viommu_size = arm_smmu_get_viommu_size,
.viommu_init = arm_vsmmu_init,
+ .tsm_bind = arm_smmu_realm_tsm_bind,
+ .tsm_unbind = arm_smmu_realm_tsm_unbind,
.user_pasid_table = 1,
.owner = THIS_MODULE,
.default_domain_ops = &(const struct iommu_domain_ops) {
and
+int arm_smmu_realm_tsm_bind(struct device *dev, struct kvm *kvm)
+{
+ struct arm_smmu_master *master = dev_iommu_priv_get(dev);
+ int ret;
+
+ if (!kvm_is_realm(kvm))
+ return 0;
+
+ ret = arm_realm_smmu_get(master->smmu);
+ if (ret)
+ return ret;
+
+ ret = arm_realm_smmu_stream_get(master);
+ if (ret)
+ arm_realm_smmu_put(master->smmu);
+ return ret;
+}
+
+void arm_smmu_realm_tsm_unbind(struct device *dev, struct kvm *kvm)
+{
+ struct arm_smmu_master *master = dev_iommu_priv_get(dev);
+
+ if (!kvm_is_realm(kvm))
+ return;
+
+ arm_realm_smmu_stream_put(master);
+ arm_realm_smmu_put(master->smmu);
+}
and tsm op iotcl now becomes
iommufd_device_tsm_op_ioctl()
switch (cmd->type) {
case IOMMU_DEVICE_TSM_BIND:
if (!idev->tsm_iommu_bound && ops->tsm_bind) {
if (WARN_ON_ONCE(!ops->tsm_unbind)) {
ret = -EOPNOTSUPP;
break;
}
ret = ops->tsm_bind(idev->dev, kvm);
if (ret)
break;
idev->tsm_iommu_bound = true;
iommu_bound = true;
}
ret = tsm_bind(idev->dev, kvm, cmd->tdi_id);
if (ret && iommu_bound) {
ops->tsm_unbind(idev->dev, kvm);
idev->tsm_iommu_bound = false;
}
break;
case IOMMU_DEVICE_TSM_UNBIND:
__iommufd_device_tsm_unbind(idev);
ret = 0;
break;
default:
ret = -EINVAL;
break;
}
I am yet to clean up the changes. I just wanted to share that we can
possibly drop the vdevice/viommu requirement.
-aneesh
^ permalink raw reply [flat|nested] 40+ messages in thread* Re: [RFC PATCH v4 03/16] iommu/arm-smmu-v3: Add initial pSMMU realm viommu plumbing
2026-09-02 9:00 ` Aneesh Kumar K.V
@ 2026-09-02 12:17 ` Jason Gunthorpe
2026-09-02 13:15 ` Aneesh Kumar K.V
0 siblings, 1 reply; 40+ messages in thread
From: Jason Gunthorpe @ 2026-09-02 12:17 UTC (permalink / raw)
To: Aneesh Kumar K.V
Cc: Nicolin Chen, linux-coco, kvmarm, linux-arm-kernel, linux-kernel,
Alexey Kardashevskiy, Catalin Marinas, Dan Williams, Joerg Roedel,
Jonathan Cameron, Marc Zyngier, Pranjal Shrivastava, Robin Murphy,
Samuel Ortiz, Steven Price, Suzuki K Poulose, Will Deacon,
Xu Yilun
On Wed, Sep 02, 2026 at 02:30:00PM +0530, Aneesh Kumar K.V wrote:
> Jason Gunthorpe <jgg@ziepe.ca> writes:
>
> > On Tue, Sep 01, 2026 at 03:36:37PM +0530, Aneesh Kumar K.V wrote:
> >
> >> @@ -463,14 +460,13 @@
> >> vsmmu->vmid = s2_parent->s2_cfg.vmid;
> >>
> >> if (viommu->type == IOMMU_VIOMMU_TYPE_ARM_SMMUV3) {
> >> + if (arm_smmu_is_realm_viommu(viommu))
> >> + return arm_realm_smmu_v3_init(viommu, user_data);
> >> +
> >
> > I think the realm vsmmu is going to require a different info struct
> > than the normal psmmu case, isn't it?
> >
> > If so it needs its own enum value.
> >
> > It would be nice to see a draft patch showing how the real vsmmu works
> > on top of the RMM spec for it. If we are using a viommu object then
> > non-vsmmu case should be identical just with an option in the info
> > struct to not create the vsmmu object.
>
> Based on feedback on other emails in this thread, I have now implemented
> this without using a vdevice or viommu. This should make the CCA and
> non-CCA cases similar.
That wasn't the feedback. The feedback was to use the viommu and not
make a bunch of new stuff..
> +int arm_smmu_realm_tsm_bind(struct device *dev, struct kvm *kvm)
> +{
> + struct arm_smmu_master *master = dev_iommu_priv_get(dev);
> + int ret;
> +
> + if (!kvm_is_realm(kvm))
> + return 0;
> +
> + ret = arm_realm_smmu_get(master->smmu);
> + if (ret)
> + return ret;
> +
> + ret = arm_realm_smmu_stream_get(master);
> + if (ret)
> + arm_realm_smmu_put(master->smmu);
> + return ret;
It still makes no sense this doesn't do the VDEV_CREATE too.
> iommufd_device_tsm_op_ioctl()
>
> switch (cmd->type) {
> case IOMMU_DEVICE_TSM_BIND:
> if (!idev->tsm_iommu_bound && ops->tsm_bind) {
> if (WARN_ON_ONCE(!ops->tsm_unbind)) {
> ret = -EOPNOTSUPP;
> break;
> }
> ret = ops->tsm_bind(idev->dev, kvm);
> if (ret)
> break;
> idev->tsm_iommu_bound = true;
> iommu_bound = true;
> }
> ret = tsm_bind(idev->dev, kvm, cmd->tdi_id);
Yuk! Now this uAPI doesn't make any sense when you have an actual
viommu involved, we can't take tdi_id from userspace, it must come
from the vdevice.
I don't want two confusingly different flows, this stuff is hard
enough to keep straight.
Your first version was better, we just need to commit to using the
viommu for everyone on every arch and drop the the tsm_bind() API and
IOMMU_DEVICE_TSM_BIND interface.
The only draw back is the VMM has to manage a litte bit more.
Jason
^ permalink raw reply [flat|nested] 40+ messages in thread* Re: [RFC PATCH v4 03/16] iommu/arm-smmu-v3: Add initial pSMMU realm viommu plumbing
2026-09-02 12:17 ` Jason Gunthorpe
@ 2026-09-02 13:15 ` Aneesh Kumar K.V
2026-09-02 16:39 ` Aneesh Kumar K.V
0 siblings, 1 reply; 40+ messages in thread
From: Aneesh Kumar K.V @ 2026-09-02 13:15 UTC (permalink / raw)
To: Jason Gunthorpe
Cc: Nicolin Chen, linux-coco, kvmarm, linux-arm-kernel, linux-kernel,
Alexey Kardashevskiy, Catalin Marinas, Dan Williams, Joerg Roedel,
Jonathan Cameron, Marc Zyngier, Pranjal Shrivastava, Robin Murphy,
Samuel Ortiz, Steven Price, Suzuki K Poulose, Will Deacon,
Xu Yilun
Jason Gunthorpe <jgg@ziepe.ca> writes:
> On Wed, Sep 02, 2026 at 02:30:00PM +0530, Aneesh Kumar K.V wrote:
>> Jason Gunthorpe <jgg@ziepe.ca> writes:
>>
>> > On Tue, Sep 01, 2026 at 03:36:37PM +0530, Aneesh Kumar K.V wrote:
>> >
>> >> @@ -463,14 +460,13 @@
>> >> vsmmu->vmid = s2_parent->s2_cfg.vmid;
>> >>
>> >> if (viommu->type == IOMMU_VIOMMU_TYPE_ARM_SMMUV3) {
>> >> + if (arm_smmu_is_realm_viommu(viommu))
>> >> + return arm_realm_smmu_v3_init(viommu, user_data);
>> >> +
>> >
>> > I think the realm vsmmu is going to require a different info struct
>> > than the normal psmmu case, isn't it?
>> >
>> > If so it needs its own enum value.
>> >
>> > It would be nice to see a draft patch showing how the real vsmmu works
>> > on top of the RMM spec for it. If we are using a viommu object then
>> > non-vsmmu case should be identical just with an option in the info
>> > struct to not create the vsmmu object.
>>
>> Based on feedback on other emails in this thread, I have now implemented
>> this without using a vdevice or viommu. This should make the CCA and
>> non-CCA cases similar.
>
> That wasn't the feedback. The feedback was to use the viommu and not
> make a bunch of new stuff..
>
That rework was done before I saw your discussion with Nicolin.
To reiterate, for this configuration:
- The viommu will use a stage-1 bypass configuration.
- A new IOMMU_VIOMMU_TYPE_ARM_REALM_SMMUV3 type will create the viommu.
The psmmu will be activated at this point to avoid creating psmmu
objects early. We will reference-count it to ensure that the same
psmmu is shared across realm guests.
- Creating a vdevice will invoke SMC_RMI_PSMMU_ST_L2_CREATE.
I am unclear about the vdev_create suggestion. Creating a vdevice
requires an RD, which is created later in the flow above. How do you
suggest linking vdevice_alloc to vdev_create?
From the RMM's perspective, the sequence is as follows:
viommu alloc
[ rmm ] SMC_RMI_PSMMU_ACTIVATE 2b400000 8819bb000 > RMI_INCOMPLETE 0 10008
[ rmm ] SMC_RMI_OP_MEM_DONATE 0 881bac098 1 > RMI_INCOMPLETE 2 10004
[ rmm ] SMC_RMI_OP_MEM_DONATE 0 881bac098 1 > RMI_INCOMPLETE 1 10004
[ rmm ] SMC_RMI_OP_MEM_DONATE 0 881bac098 1 > RMI_INCOMPLETE 1 0
[ rmm ] L1 StrTab: PA 0x881baa000 VA 0x80003c0000 size 0x2000
[ rmm ] CMDQ: PA 0x88066a000 VA 0x80003c2000
[ rmm ] EVTQ: PA 0x8815c5000 VA 0x80003c3000
[ rmm ] PSMMU 0x2b400000 activated
[ rmm ] SMC_RMI_OP_CONTINUE 0 0 > RMI_SUCCESS 0 0
[ rmm ] SMC_RMI_PSMMU_ST_L2_CREATE 2b400000 300 > RMI_INCOMPLETE 0 10004
[ rmm ] SMC_RMI_OP_MEM_DONATE 0 881bac098 1 > RMI_INCOMPLETE 1 0
[ rmm ] smmu->strtab_base[12] 0x0 @0x80003c0060
[ rmm ] L1STD[12] 0x8819bb007 for SID 0x300: L2 table VA 0x80003d0000 PA 0x8819bb000
[ rmm ] SMC_RMI_OP_CONTINUE 0 0 > RMI_SUCCESS 0 0
vdevice alloc
[ rmm ] SMC_RMI_PSMMU_ST_L2_CREATE 2b400000 200 > RMI_INCOMPLETE 0 10004
[ rmm ] SMC_RMI_OP_MEM_DONATE 0 882648098 1 > RMI_INCOMPLETE 1 0
[ rmm ] smmu->strtab_base[8] 0x0 @0x80003c0040
[ rmm ] L1STD[8] 0x882506007 for SID 0x200: L2 table VA 0x80003cc000 PA 0x882506000
[ rmm ] SMC_RMI_OP_CONTINUE 0 0 > RMI_SUCCESS 0 0
RD gets allocated here
[ rmm ] SMC_RMI_REALM_CREATE 881b03000 8819a1000 > RMI_INCOMPLETE 0 24
[ rmm ] SMC_RMI_OP_MEM_DONATE 0 881605098 9 > RMI_INCOMPLETE 9 0
[ rmm ] SMC_RMI_OP_CONTINUE 0 0 > RMI_SUCCESS 0 0
-aneesh
^ permalink raw reply [flat|nested] 40+ messages in thread* Re: [RFC PATCH v4 03/16] iommu/arm-smmu-v3: Add initial pSMMU realm viommu plumbing
2026-09-02 13:15 ` Aneesh Kumar K.V
@ 2026-09-02 16:39 ` Aneesh Kumar K.V
0 siblings, 0 replies; 40+ messages in thread
From: Aneesh Kumar K.V @ 2026-09-02 16:39 UTC (permalink / raw)
To: Jason Gunthorpe
Cc: Nicolin Chen, linux-coco, kvmarm, linux-arm-kernel, linux-kernel,
Alexey Kardashevskiy, Catalin Marinas, Dan Williams, Joerg Roedel,
Jonathan Cameron, Marc Zyngier, Pranjal Shrivastava, Robin Murphy,
Samuel Ortiz, Steven Price, Suzuki K Poulose, Will Deacon,
Xu Yilun
Aneesh Kumar K.V <aneesh.kumar@kernel.org> writes:
> Jason Gunthorpe <jgg@ziepe.ca> writes:
>
>> On Wed, Sep 02, 2026 at 02:30:00PM +0530, Aneesh Kumar K.V wrote:
>>> Jason Gunthorpe <jgg@ziepe.ca> writes:
>>>
>>> > On Tue, Sep 01, 2026 at 03:36:37PM +0530, Aneesh Kumar K.V wrote:
>>> >
>>> >> @@ -463,14 +460,13 @@
>>> >> vsmmu->vmid = s2_parent->s2_cfg.vmid;
>>> >>
>>> >> if (viommu->type == IOMMU_VIOMMU_TYPE_ARM_SMMUV3) {
>>> >> + if (arm_smmu_is_realm_viommu(viommu))
>>> >> + return arm_realm_smmu_v3_init(viommu, user_data);
>>> >> +
>>> >
>>> > I think the realm vsmmu is going to require a different info struct
>>> > than the normal psmmu case, isn't it?
>>> >
>>> > If so it needs its own enum value.
>>> >
>>> > It would be nice to see a draft patch showing how the real vsmmu works
>>> > on top of the RMM spec for it. If we are using a viommu object then
>>> > non-vsmmu case should be identical just with an option in the info
>>> > struct to not create the vsmmu object.
>>>
>>> Based on feedback on other emails in this thread, I have now implemented
>>> this without using a vdevice or viommu. This should make the CCA and
>>> non-CCA cases similar.
>>
>> That wasn't the feedback. The feedback was to use the viommu and not
>> make a bunch of new stuff..
>>
>
> That rework was done before I saw your discussion with Nicolin.
>
> To reiterate, for this configuration:
>
> - The viommu will use a stage-1 bypass configuration.
> - A new IOMMU_VIOMMU_TYPE_ARM_REALM_SMMUV3 type will create the viommu.
> The psmmu will be activated at this point to avoid creating psmmu
> objects early. We will reference-count it to ensure that the same
> psmmu is shared across realm guests.
> - Creating a vdevice will invoke SMC_RMI_PSMMU_ST_L2_CREATE.
>
> I am unclear about the vdev_create suggestion. Creating a vdevice
> requires an RD, which is created later in the flow above. How do you
> suggest linking vdevice_alloc to vdev_create?
>
I was able to prototype the following flow:
- The viommu uses a stage-1 bypass configuration.
- A new IOMMU_VIOMMU_TYPE_ARM_REALM_SMMUV3 type creates the viommu.
The pSMMU is activated at this point to avoid creating pSMMU objects
early. It is reference-counted so that the same pSMMU can be shared
across realm guests.
- The realm is created during viommu allocation, ensuring that the
vSMMU can be created here.
- Creating a vdevice invokes SMC_RMI_PSMMU_ST_L2_CREATE.
- This is followed by tsm_bind() if the device has an established TSM
link.
static int arm_realm_smmu_v3_vdevice_init(struct iommufd_vdevice *vdev)
{
struct device *dev = iommufd_vdevice_to_device(vdev);
struct kvm *kvm = vdev->viommu->kvm_file->private_data;
struct arm_smmu_device *smmu;
struct arm_smmu_stream *stream;
struct arm_smmu_master *master;
unsigned long rmi_ret = 0;
unsigned long l2_sid;
int ret;
if (!tsm_is_configured(dev))
return 0;
master = dev_iommu_priv_get(dev);
/* FIXME which stream to pick */
/* At this moment, iommufd only supports PCI device that has one SID */
stream = &master->streams[0];
smmu = master->smmu;
l2_sid = ALIGN_DOWN(stream->id, STRTAB_NUM_L2_STES);
{
guard(mutex)(&smmu->realm.mutex);
if (!arm_realm_smmu_active(smmu))
return -EINVAL;
ret = rmi_psmmu_st_l2_create(smmu->base_phys, l2_sid,
&rmi_ret);
if (ret || rmi_ret) {
if (!ret)
return -EIO;
if (RMI_RETURN_STATUS(rmi_ret) != RMI_ERROR_PSMMU_ST ||
RMI_RETURN_INDEX(rmi_ret) != 2) {
dev_warn(dev, "failed to create realm stream mapping\n");
return -EIO;
}
/* The L2 stream table already exists. */
}
}
vdev->destroy = arm_realm_smmu_v3_vdevice_destroy;
return tsm_bind(dev, kvm, vdev->virt_id);
}
- tsm_bind() calls cca_tsm_bind(), which in turn calls vdev_create().
- After boot, the guest locks the device. This generates an RHI request
that reaches cca_tsm_guest_req() with RHI_DA_TDI_CONFIG_LOCKED.
- cca_tsm_guest_req() now handles TSM_REQ_SET_TDI_STATE requests for
the unlocked, locked, and running states.
@@ -513,10 +514,16 @@ static ssize_t cca_tsm_guest_req(struct pci_tdi *tdi,
if (copy_from_user((void *)&req_obj, req.user, req_len))
return -EFAULT;
- if (req_obj.tdi_state != RHI_DA_TDI_CONFIG_RUN)
+ switch (req_obj.tdi_state) {
+ case RHI_DA_TDI_CONFIG_UNLOCKED:
+ return cca_vdev_device_unlock(pdev);
+ case RHI_DA_TDI_CONFIG_LOCKED:
+ return cca_vdev_device_lock(pdev);
+ case RHI_DA_TDI_CONFIG_RUN:
+ return cca_vdev_device_start(pdev);
+ default:
return -EINVAL;
-
- return cca_vdev_device_start(pdev);
+ }
}
-aneesh
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [RFC PATCH v4 03/16] iommu/arm-smmu-v3: Add initial pSMMU realm viommu plumbing
2026-09-01 9:17 ` Aneesh Kumar K.V
2026-09-01 10:06 ` Aneesh Kumar K.V
@ 2026-09-01 17:36 ` Nicolin Chen
1 sibling, 0 replies; 40+ messages in thread
From: Nicolin Chen @ 2026-09-01 17:36 UTC (permalink / raw)
To: Aneesh Kumar K.V
Cc: linux-coco, kvmarm, linux-arm-kernel, linux-kernel,
Alexey Kardashevskiy, Catalin Marinas, Dan Williams,
Jason Gunthorpe, Joerg Roedel, Jonathan Cameron, Marc Zyngier,
Pranjal Shrivastava, Robin Murphy, Samuel Ortiz, Steven Price,
Suzuki K Poulose, Will Deacon, Xu Yilun
On Tue, Sep 01, 2026 at 02:47:04PM +0530, Aneesh Kumar K.V wrote:
> Nicolin Chen <nicolinc@nvidia.com> writes:
> > But, on the other hand, I can imagine that a Realm VSMMU would add
> > a new flag with a user_data to this VIOMMU. Then, this flow would
> > give some troubles to VMM (QEMU for example):
> >
> > - For VM with a guest-level SMMU, QEMU creates a realm instance
> > where IOMMU_VIOMMU_TYPE_ARM_REALM_SMMUV3 (with vsmmu) can be
> > allocated.
> > - For VM w/o a guest-level SMMU, QEMU won't create such a realm
> > instance, while still required to invoke the ioctl (w/o vsmmu).
> >
> > Taking a step back, I wonder if we really need to use iommufd for
> > PSMMU activation and its stream table allocations?
> >
> > Here are some facts:
> > - An iommufd has a ctx, that's one per VM. Similarly, a Realm
> > has an RD.
> > - For an RMI command that needs an RD, it makes sense to be per
> > iommufd ctx, e.g. RMI_VSMMU_* or RMI_VDEV_* commands.
> > - PSMMU commands are global; they don't need RD. So they don't
> > seem necessary to tie to an iommufd ctx.
> >
> > Instead, could the PSMMU activation be done after RMI_PSMMU_INFO
> > check? Is there any reason not to do that? A safer timing might
> > be at the device assignment stage?
> >
>
> One of the reasons I added IOMMU_VIOMMU_TYPE_ARM_REALM_SMMUV3 was to
> avoid creating a psmmu object when we are not using a PCI passthrough
> VM. That is also the reason for all the refcounting around the psmmu
> objects.
>
> If we are okay with creating psmmu objects early, then I guess we can go
> with the above approach.
Do you see any trouble if we implement in this way:
- Add "struct arm_smmu_realm *psmmu" in struct arm_smmu_device
- RMI_PSMMU_INFO in probe():
+ allocate a smmu->psmmu on success
+ keep smmu->psmmu->users = 0; psmmu is not activated
- Device binds to iommufd, creating idev
+ invoke an iommu op to a driver-level callback:
psmmu = smmu->psmmu;
if (!psmmu)
return 0;
guard(mutex)(&psmmu->mutex);
if (!smmu->psmmu->users)
rmi_psmmu_activate();
psmmu->users++;
rmi_psmmu_st_l2_create();
?
Nicolin
^ permalink raw reply [flat|nested] 40+ messages in thread
* [RFC PATCH v4 04/16] iommu/arm-smmu-v3: Track realm pSMMU users with refcount_t
2026-04-27 8:53 [RFC PATCH v4 00/16] coco/TSM: Implement host-side support for Arm CCA TDISP setup Aneesh Kumar K.V (Arm)
` (2 preceding siblings ...)
2026-04-27 8:53 ` [RFC PATCH v4 03/16] iommu/arm-smmu-v3: Add initial pSMMU realm viommu plumbing Aneesh Kumar K.V (Arm)
@ 2026-04-27 8:53 ` Aneesh Kumar K.V (Arm)
2026-04-27 8:53 ` [RFC PATCH v4 05/16] coco: host: arm64: Add support for virtual device communication Aneesh Kumar K.V (Arm)
` (12 subsequent siblings)
16 siblings, 0 replies; 40+ messages in thread
From: Aneesh Kumar K.V (Arm) @ 2026-04-27 8:53 UTC (permalink / raw)
To: linux-coco, kvmarm, linux-arm-kernel, linux-kernel
Cc: Aneesh Kumar K.V (Arm), Alexey Kardashevskiy, Catalin Marinas,
Dan Williams, Jason Gunthorpe, Joerg Roedel, Jonathan Cameron,
Marc Zyngier, Nicolin Chen, Pranjal Shrivastava, Robin Murphy,
Samuel Ortiz, Steven Price, Suzuki K Poulose, Will Deacon,
Xu Yilun
Replace the realm pSMMU active-state boolean with a refcount so
activation/deactivation is tied to actual Realm vIOMMU lifetime.
- add realm_mutex and realm_users (refcount_t) to struct arm_smmu_device
- on first Realm init, activate pSMMU and set realm_users to 1
- on subsequent Realm inits, increment realm_users
- on Realm viommu destroy, decrement realm_users and call
rmi_psmmu_deactivate() when the last user drops the count to 0
This removes duplicated state tracking and ensures pSMMU is deactivated only
after the last Realm user is gone.
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
arch/arm64/include/asm/rmi_cmds.h | 1 +
arch/arm64/kernel/rmi.c | 12 ++++++
.../iommu/arm/arm-smmu-v3/arm-smmu-v3-realm.c | 39 +++++++++++++++----
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 3 +-
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h | 4 +-
5 files changed, 50 insertions(+), 9 deletions(-)
diff --git a/arch/arm64/include/asm/rmi_cmds.h b/arch/arm64/include/asm/rmi_cmds.h
index 659d68ad5f1d..205fc200d1db 100644
--- a/arch/arm64/include/asm/rmi_cmds.h
+++ b/arch/arm64/include/asm/rmi_cmds.h
@@ -865,6 +865,7 @@ static inline unsigned long rmi_psmmu_event_consume(unsigned long psmmu_phys,
int rmi_psmmu_activate(unsigned long psmmu_phys,
unsigned long psmmu_params_phys, unsigned long *rmi_ret);
+int rmi_psmmu_deactivate(unsigned long psmmu_phys, unsigned long *rmi_ret);
int rmi_psmmu_st_l2_create(unsigned long psmmu_phys,
unsigned long stream_id, unsigned long *rmi_ret);
int rmi_psmmu_st_l2_destroy(unsigned long psmmu_phys,
diff --git a/arch/arm64/kernel/rmi.c b/arch/arm64/kernel/rmi.c
index cc4050db5a6a..884ab3f99f2f 100644
--- a/arch/arm64/kernel/rmi.c
+++ b/arch/arm64/kernel/rmi.c
@@ -436,6 +436,18 @@ int rmi_psmmu_activate(unsigned long psmmu_phys,
return 0;
}
+int rmi_psmmu_deactivate(unsigned long psmmu_phys, unsigned long *rmi_ret)
+{
+ struct rmi_sro_state *sro __free(sro) =
+ rmi_sro_init(SMC_RMI_PSMMU_DEACTIVATE, psmmu_phys);
+ if (!sro)
+ return -ENOMEM;
+
+ *rmi_ret = rmi_sro_execute(sro);
+
+ return 0;
+}
+
int rmi_psmmu_st_l2_create(unsigned long psmmu_phys,
unsigned long stream_id, unsigned long *rmi_ret)
{
diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-realm.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-realm.c
index 6f8de7cead9d..dfff493f96d0 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-realm.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-realm.c
@@ -124,9 +124,25 @@ void arm_smmu_setup_realm_irqs(struct arm_smmu_device *smmu)
}
}
+static bool arm_realm_smmu_active(struct arm_smmu_device *smmu)
+{
+ lockdep_assert_held(&smmu->realm_mutex);
+ return refcount_read(&smmu->realm_users) > 0;
+}
+
static void arm_realm_smmu_v3_destroy(struct iommufd_viommu *viommu)
{
- /* When we add refcount psmmu deactivate here. */
+ unsigned long rmi_ret;
+ struct arm_smmu_device *smmu =
+ container_of(viommu->iommu_dev, struct arm_smmu_device, iommu);
+
+ guard(mutex)(&smmu->realm_mutex);
+ if (WARN_ON(!arm_realm_smmu_active(smmu)))
+ return;
+
+ if (refcount_dec_and_test(&smmu->realm_users) &&
+ (rmi_psmmu_deactivate(smmu->base_phys, &rmi_ret) || rmi_ret))
+ dev_warn(smmu->dev, "failed to deactivate realm pSMMU\n");
}
static void arm_realm_smmu_v3_vdevice_destroy(struct iommufd_vdevice *vdev)
@@ -140,7 +156,8 @@ static void arm_realm_smmu_v3_vdevice_destroy(struct iommufd_vdevice *vdev)
unsigned long rmi_ret = 0;
int ret;
- if (!smmu->realm_initialized)
+ guard(mutex)(&smmu->realm_mutex);
+ if (!arm_realm_smmu_active(smmu))
return;
ret = rmi_psmmu_st_l2_destroy(smmu->base_phys,
@@ -168,7 +185,8 @@ static int arm_realm_smmu_v3_vdevice_init(struct iommufd_vdevice *vdev)
unsigned long rmi_ret = 0;
int ret;
- if (!smmu->realm_initialized)
+ guard(mutex)(&smmu->realm_mutex);
+ if (!arm_realm_smmu_active(smmu))
return -EINVAL;
ret = rmi_psmmu_st_l2_create(smmu->base_phys,
@@ -229,12 +247,17 @@ int arm_realm_smmu_v3_init(struct iommufd_viommu *viommu,
if (!(smmu->features & ARM_SMMU_FEAT_RME))
return -EOPNOTSUPP;
- if (smmu->realm_initialized)
+ mutex_lock(&smmu->realm_mutex);
+ if (arm_realm_smmu_active(smmu)) {
+ refcount_inc(&smmu->realm_users);
goto psmmu_already_active;
+ }
params = (struct rmi_psmmu_params *)get_zeroed_page(GFP_KERNEL);
- if (!params)
- return -ENOMEM;
+ if (!params) {
+ ret = -ENOMEM;
+ goto out_unlock;
+ }
/* No ATS and PRI support */
if (!(smmu->features & ARM_SMMU_FEAT_MSI))
@@ -261,12 +284,14 @@ int arm_realm_smmu_v3_init(struct iommufd_viommu *viommu,
dev_warn(smmu->dev, "failed to activate realm pSMMU\n");
ret = -EIO;
} else {
- smmu->realm_initialized = true;
+ refcount_set(&smmu->realm_users, 1);
}
out_free:
free_page((unsigned long)params);
psmmu_already_active:
if (!ret)
viommu->ops = &arm_realm_smmu_v3_ops;
+out_unlock:
+ mutex_unlock(&smmu->realm_mutex);
return ret;
}
diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
index 1e3d4d682e32..e458c3818c34 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
@@ -3946,6 +3946,8 @@ static int arm_smmu_init_structures(struct arm_smmu_device *smmu)
int ret;
mutex_init(&smmu->streams_mutex);
+ mutex_init(&smmu->realm_mutex);
+ refcount_set(&smmu->realm_users, 0);
smmu->streams = RB_ROOT;
ret = arm_smmu_init_queues(smmu);
@@ -3956,7 +3958,6 @@ static int arm_smmu_init_structures(struct arm_smmu_device *smmu)
if (ret)
return ret;
- smmu->realm_initialized = false;
if (smmu->impl_ops && smmu->impl_ops->init_structures)
return smmu->impl_ops->init_structures(smmu);
diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
index d528b3212d38..b5d0e1341236 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
@@ -13,6 +13,7 @@
#include <linux/iommufd.h>
#include <linux/kernel.h>
#include <linux/mmzone.h>
+#include <linux/refcount.h>
#include <linux/sizes.h>
struct arm_smmu_device;
@@ -812,7 +813,8 @@ struct arm_smmu_device {
struct rb_root streams;
struct mutex streams_mutex;
- bool realm_initialized;
+ struct mutex realm_mutex;
+ refcount_t realm_users;
};
struct arm_smmu_stream {
--
2.43.0
^ permalink raw reply related [flat|nested] 40+ messages in thread* [RFC PATCH v4 05/16] coco: host: arm64: Add support for virtual device communication
2026-04-27 8:53 [RFC PATCH v4 00/16] coco/TSM: Implement host-side support for Arm CCA TDISP setup Aneesh Kumar K.V (Arm)
` (3 preceding siblings ...)
2026-04-27 8:53 ` [RFC PATCH v4 04/16] iommu/arm-smmu-v3: Track realm pSMMU users with refcount_t Aneesh Kumar K.V (Arm)
@ 2026-04-27 8:53 ` Aneesh Kumar K.V (Arm)
2026-04-27 8:53 ` [RFC PATCH v4 06/16] coco: host: arm64: Add support for RMM vdev objects Aneesh Kumar K.V (Arm)
` (11 subsequent siblings)
16 siblings, 0 replies; 40+ messages in thread
From: Aneesh Kumar K.V (Arm) @ 2026-04-27 8:53 UTC (permalink / raw)
To: linux-coco, kvmarm, linux-arm-kernel, linux-kernel
Cc: Aneesh Kumar K.V (Arm), Alexey Kardashevskiy, Catalin Marinas,
Dan Williams, Jason Gunthorpe, Joerg Roedel, Jonathan Cameron,
Marc Zyngier, Nicolin Chen, Pranjal Shrivastava, Robin Murphy,
Samuel Ortiz, Steven Price, Suzuki K Poulose, Will Deacon,
Xu Yilun
Add support for vdev_communicate with RMM.
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
arch/arm64/include/asm/rmi_cmds.h | 32 ++++++++++
arch/arm64/include/asm/rmi_smc.h | 7 +++
drivers/virt/coco/arm-cca-host/rmi-da.c | 83 ++++++++++++++++++++++---
drivers/virt/coco/arm-cca-host/rmi-da.h | 20 ++++++
4 files changed, 135 insertions(+), 7 deletions(-)
diff --git a/arch/arm64/include/asm/rmi_cmds.h b/arch/arm64/include/asm/rmi_cmds.h
index 205fc200d1db..2925abde3882 100644
--- a/arch/arm64/include/asm/rmi_cmds.h
+++ b/arch/arm64/include/asm/rmi_cmds.h
@@ -871,4 +871,36 @@ int rmi_psmmu_st_l2_create(unsigned long psmmu_phys,
int rmi_psmmu_st_l2_destroy(unsigned long psmmu_phys,
unsigned long stream_id, unsigned long *rmi_ret);
+static inline unsigned long rmi_vdev_communicate(unsigned long rd_phys,
+ unsigned long pdev_phys, unsigned long vdev_phys,
+ unsigned long vdev_comm_data_phys)
+{
+ struct arm_smccc_res res;
+
+ arm_smccc_1_1_invoke(SMC_RMI_VDEV_COMMUNICATE, rd_phys, pdev_phys,
+ vdev_phys, vdev_comm_data_phys, &res);
+
+ return res.a0;
+}
+
+static inline unsigned long rmi_vdev_get_state(unsigned long vdev_phys,
+ enum rmi_vdev_state *state)
+{
+ struct arm_smccc_res res;
+
+ arm_smccc_1_1_invoke(SMC_RMI_VDEV_GET_STATE, vdev_phys, &res);
+
+ *state = res.a1;
+ return res.a0;
+}
+
+static inline unsigned long rmi_vdev_abort(unsigned long vdev_phys)
+{
+ struct arm_smccc_res res;
+
+ arm_smccc_1_1_invoke(SMC_RMI_VDEV_ABORT, vdev_phys, &res);
+
+ return res.a0;
+}
+
#endif /* __ASM_RMI_CMDS_H */
diff --git a/arch/arm64/include/asm/rmi_smc.h b/arch/arm64/include/asm/rmi_smc.h
index 5b540d25914e..72e4a53b74b0 100644
--- a/arch/arm64/include/asm/rmi_smc.h
+++ b/arch/arm64/include/asm/rmi_smc.h
@@ -643,4 +643,11 @@ struct rmi_psmmu_params {
};
};
+enum rmi_vdev_state {
+ RMI_VDEV_NEW,
+ RMI_VDEV_UNLOCKED,
+ RMI_VDEV_LOCKED,
+ RMI_VDEV_STARTED,
+ RMI_VDEV_ERROR,
+};
#endif /* __ASM_RMI_SMC_H */
diff --git a/drivers/virt/coco/arm-cca-host/rmi-da.c b/drivers/virt/coco/arm-cca-host/rmi-da.c
index 33a2551fd09f..d61c3191c038 100644
--- a/drivers/virt/coco/arm-cca-host/rmi-da.c
+++ b/drivers/virt/coco/arm-cca-host/rmi-da.c
@@ -11,6 +11,8 @@
#include <crypto/internal/rsa.h>
#include <keys/asymmetric-type.h>
#include <keys/x509-parser.h>
+#include <linux/kvm_types.h>
+#include <asm/kvm_rmi.h>
#include "rmi-da.h"
@@ -217,6 +219,7 @@ static int _do_dev_communicate(enum dev_comm_type type, struct pci_tsm *tsm, int
gfp_t cache_alloc_flags;
int nbytes, cp_len;
struct cache_object **cache_objp, *cache_obj;
+ struct cca_host_tdi *host_tdi = to_cca_host_tdi(tsm->pdev);
struct cca_host_pdev_dsc *pdev_dsc = to_cca_pdev_dsc(tsm->dsm_dev);
struct cca_host_comm_data *comm_data = to_cca_comm_data(tsm->pdev);
struct rmi_dev_comm_enter *io_enter = &comm_data->io_params->enter;
@@ -228,7 +231,10 @@ static int _do_dev_communicate(enum dev_comm_type type, struct pci_tsm *tsm, int
rmi_ret = rmi_pdev_communicate(virt_to_phys(pdev_dsc->rmm_pdev),
virt_to_phys(comm_data->io_params));
else
- rmi_ret = RMI_ERROR_INPUT;
+ rmi_ret = rmi_vdev_communicate(virt_to_phys(host_tdi->realm->rd),
+ virt_to_phys(pdev_dsc->rmm_pdev),
+ virt_to_phys(host_tdi->rmm_vdev),
+ virt_to_phys(comm_data->io_params));
if (rmi_ret != RMI_SUCCESS) {
if (rmi_ret == RMI_BUSY)
return -EBUSY;
@@ -252,6 +258,12 @@ static int _do_dev_communicate(enum dev_comm_type type, struct pci_tsm *tsm, int
case RMI_DEV_CERTIFICATE:
cache_objp = &pf0_ep_dsc->cert_chain.cache;
break;
+ case RMI_DEV_INTERFACE_REPORT:
+ cache_objp = &host_tdi->interface_report;
+ break;
+ case RMI_DEV_MEASUREMENTS:
+ cache_objp = &host_tdi->measurements;
+ break;
default:
return -EINVAL;
}
@@ -355,9 +367,11 @@ static int _do_dev_communicate(enum dev_comm_type type, struct pci_tsm *tsm, int
static int do_dev_communicate(enum dev_comm_type type,
struct pci_tsm *tsm, unsigned long error_state, int *stream_wait)
{
- int ret, state = error_state;
+ int ret, state;
+ unsigned long rmi_ret;
struct rmi_dev_comm_enter *io_enter;
struct cca_host_pdev_dsc *pdev_dsc = to_cca_pdev_dsc(tsm->dsm_dev);
+ struct cca_host_tdi *host_tdi = to_cca_host_tdi(tsm->pdev);
io_enter = &pdev_dsc->comm_data.io_params->enter;
io_enter->resp_len = 0;
@@ -369,16 +383,23 @@ static int do_dev_communicate(enum dev_comm_type type,
if (ret) {
if (type == PDEV_COMMUNICATE)
rmi_pdev_abort(virt_to_phys(pdev_dsc->rmm_pdev));
+ else
+ rmi_vdev_abort(virt_to_phys(host_tdi->rmm_vdev));
+
+ state = error_state;
} else {
/*
* Some device communication error will transition the
* device to error state. Report that.
*/
- if (type == PDEV_COMMUNICATE) {
- if (rmi_pdev_get_state(virt_to_phys(pdev_dsc->rmm_pdev),
- (enum rmi_pdev_state *)&state))
- state = error_state;
- }
+ if (type == PDEV_COMMUNICATE)
+ rmi_ret = rmi_pdev_get_state(virt_to_phys(pdev_dsc->rmm_pdev),
+ (enum rmi_pdev_state *)&state);
+ else
+ rmi_ret = rmi_vdev_get_state(virt_to_phys(host_tdi->rmm_vdev),
+ (enum rmi_vdev_state *)&state);
+ if (rmi_ret)
+ state = error_state;
}
if (state == error_state)
@@ -408,6 +429,11 @@ static int wait_for_pdev_state(struct pci_tsm *tsm, enum rmi_pdev_state target_s
return wait_for_dev_state(PDEV_COMMUNICATE, tsm, target_state, RMI_PDEV_ERROR);
}
+static int wait_for_vdev_state(struct pci_tsm *tsm, enum rmi_vdev_state target_state)
+{
+ return wait_for_dev_state(VDEV_COMMUNICATE, tsm, target_state, RMI_VDEV_ERROR);
+}
+
static int parse_certificate_chain(struct pci_tsm *tsm)
{
struct cca_host_pf0_ep_dsc *pf0_ep_dsc;
@@ -603,6 +629,49 @@ static int submit_pdev_state_transition_work(struct pci_dev *pdev,
return 0;
}
+static void vdev_state_transition_workfn(struct work_struct *work)
+{
+ unsigned long state;
+ struct pci_tsm *tsm;
+ struct dev_comm_work *setup_work;
+ struct cca_host_pdev_dsc *pdev_dsc;
+
+ setup_work = container_of(work, struct dev_comm_work, work);
+ tsm = setup_work->tsm;
+
+ pdev_dsc = to_cca_pdev_dsc(tsm->dsm_dev);
+ guard(mutex)(&pdev_dsc->object_lock);
+
+ state = wait_for_vdev_state(tsm, setup_work->target_state);
+ WARN_ON(state != setup_work->target_state);
+}
+
+static int __maybe_unused submit_vdev_state_transition_work(struct pci_dev *pdev, int target_state)
+{
+ enum rmi_vdev_state state;
+ struct dev_comm_work comm_work;
+ struct cca_host_comm_data *comm_data = to_cca_comm_data(pdev);
+ struct cca_host_tdi *host_tdi = to_cca_host_tdi(pdev);
+
+ INIT_WORK_ONSTACK(&comm_work.work, vdev_state_transition_workfn);
+ comm_work.tsm = pdev->tsm;
+ comm_work.target_state = target_state;
+
+ queue_work(comm_data->work_queue, &comm_work.work);
+
+ flush_work(&comm_work.work);
+ destroy_work_on_stack(&comm_work.work);
+
+ /* check if we reached target state */
+ if (rmi_vdev_get_state(virt_to_phys(host_tdi->rmm_vdev), &state))
+ return -ENXIO;
+
+ if (state != target_state)
+ /* Protocol didn't take it to expected target state */
+ return -EPROTO;
+ return 0;
+}
+
static void pdev_collect_identity_workfn(struct work_struct *work)
{
struct pci_tsm *tsm;
diff --git a/drivers/virt/coco/arm-cca-host/rmi-da.h b/drivers/virt/coco/arm-cca-host/rmi-da.h
index 798a8ed7505f..88fa428f788e 100644
--- a/drivers/virt/coco/arm-cca-host/rmi-da.h
+++ b/drivers/virt/coco/arm-cca-host/rmi-da.h
@@ -116,6 +116,16 @@ struct cca_host_fn_dsc {
enum dev_comm_type {
PDEV_COMMUNICATE = 0x1,
+ VDEV_COMMUNICATE = 0x2,
+};
+
+struct cca_host_tdi {
+ struct pci_tdi tdi;
+ struct realm *realm;
+ void *rmm_vdev;
+ /* protected by cca_host_pdev_dsc.object_lock */
+ struct cache_object *interface_report;
+ struct cache_object *measurements;
};
static inline int insert_addr_range_sorted(struct rmi_addr_range *addr_range,
@@ -203,6 +213,16 @@ static inline struct cca_host_comm_data *to_cca_comm_data(struct pci_dev *pdev)
return NULL;
}
+static inline struct cca_host_tdi *to_cca_host_tdi(struct pci_dev *pdev)
+{
+ struct pci_tsm *tsm = pdev->tsm;
+
+ if (!tsm || !tsm->tdi)
+ return NULL;
+
+ return container_of(tsm->tdi, struct cca_host_tdi, tdi);
+}
+
int cca_pdev_create(struct pci_dev *pdev);
int cca_pdev_collect_identity(struct pci_dev *pdev);
bool cca_pdev_needs_key(struct pci_dev *pdev);
--
2.43.0
^ permalink raw reply related [flat|nested] 40+ messages in thread* [RFC PATCH v4 06/16] coco: host: arm64: Add support for RMM vdev objects
2026-04-27 8:53 [RFC PATCH v4 00/16] coco/TSM: Implement host-side support for Arm CCA TDISP setup Aneesh Kumar K.V (Arm)
` (4 preceding siblings ...)
2026-04-27 8:53 ` [RFC PATCH v4 05/16] coco: host: arm64: Add support for virtual device communication Aneesh Kumar K.V (Arm)
@ 2026-04-27 8:53 ` Aneesh Kumar K.V (Arm)
2026-04-27 8:53 ` [RFC PATCH v4 07/16] coco: host: arm64: Add pdev stream key refresh and purge helpers Aneesh Kumar K.V (Arm)
` (10 subsequent siblings)
16 siblings, 0 replies; 40+ messages in thread
From: Aneesh Kumar K.V (Arm) @ 2026-04-27 8:53 UTC (permalink / raw)
To: linux-coco, kvmarm, linux-arm-kernel, linux-kernel
Cc: Aneesh Kumar K.V (Arm), Alexey Kardashevskiy, Catalin Marinas,
Dan Williams, Jason Gunthorpe, Joerg Roedel, Jonathan Cameron,
Marc Zyngier, Nicolin Chen, Pranjal Shrivastava, Robin Murphy,
Samuel Ortiz, Steven Price, Suzuki K Poulose, Will Deacon,
Xu Yilun
An RMM vdev object represents the binding between a device function and
a Realm. For example, a vdev can represent a physical function of a PCIe
device or a virtual function of a multi-function PCIe device. Each vdev
is associated with one pdev.
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
arch/arm64/include/asm/rmi_cmds.h | 22 +++++
arch/arm64/include/asm/rmi_smc.h | 22 +++++
drivers/virt/coco/arm-cca-host/arm-cca.c | 27 +++++-
drivers/virt/coco/arm-cca-host/rmi-da.c | 104 +++++++++++++++++++++++
drivers/virt/coco/arm-cca-host/rmi-da.h | 2 +
5 files changed, 176 insertions(+), 1 deletion(-)
diff --git a/arch/arm64/include/asm/rmi_cmds.h b/arch/arm64/include/asm/rmi_cmds.h
index 2925abde3882..242ce2fac14e 100644
--- a/arch/arm64/include/asm/rmi_cmds.h
+++ b/arch/arm64/include/asm/rmi_cmds.h
@@ -903,4 +903,26 @@ static inline unsigned long rmi_vdev_abort(unsigned long vdev_phys)
return res.a0;
}
+static inline unsigned long rmi_vdev_create(unsigned long rd,
+ unsigned long pdev_phys, unsigned long vdev_phys,
+ unsigned long vdev_params_phys)
+{
+ struct arm_smccc_res res;
+
+ arm_smccc_1_1_invoke(SMC_RMI_VDEV_CREATE, rd, pdev_phys,
+ vdev_phys, vdev_params_phys, &res);
+
+ return res.a0;
+}
+
+static inline unsigned long rmi_vdev_lock(unsigned long rd,
+ unsigned long pdev_phys, unsigned long vdev_phys)
+{
+ struct arm_smccc_res res;
+
+ arm_smccc_1_1_invoke(SMC_RMI_VDEV_LOCK, rd, pdev_phys, vdev_phys, &res);
+
+ return res.a0;
+}
+
#endif /* __ASM_RMI_CMDS_H */
diff --git a/arch/arm64/include/asm/rmi_smc.h b/arch/arm64/include/asm/rmi_smc.h
index 72e4a53b74b0..d14d13a9f169 100644
--- a/arch/arm64/include/asm/rmi_smc.h
+++ b/arch/arm64/include/asm/rmi_smc.h
@@ -650,4 +650,26 @@ enum rmi_vdev_state {
RMI_VDEV_STARTED,
RMI_VDEV_ERROR,
};
+
+#define MAX_VDEV_ADDR_RANGE 8
+
+struct rmi_vdev_params {
+ union {
+ struct {
+ u64 flags;
+ u64 vdev_id;
+ u64 tdi_id;
+ u64 padding1;
+ u64 vsmmu_addr;
+ u64 vsid;
+ u64 num_addr_range;
+ };
+ u8 padding2[0x200];
+ };
+ union { /* 0x200 */
+ struct rmi_addr_range addr_range[MAX_VDEV_ADDR_RANGE];
+ u8 padding3[0x1000 - 0x200];
+ };
+};
+
#endif /* __ASM_RMI_SMC_H */
diff --git a/drivers/virt/coco/arm-cca-host/arm-cca.c b/drivers/virt/coco/arm-cca-host/arm-cca.c
index 8b1182620872..5930a30dd16f 100644
--- a/drivers/virt/coco/arm-cca-host/arm-cca.c
+++ b/drivers/virt/coco/arm-cca-host/arm-cca.c
@@ -12,7 +12,8 @@
#include <linux/vmalloc.h>
#include <linux/cleanup.h>
#include <linux/pci-doe.h>
-
+#include <linux/pci.h>
+#include <linux/kvm_host.h>
#include "rmi-da.h"
@@ -449,11 +450,35 @@ static void cca_tsm_disconnect(struct pci_dev *pdev)
}
}
+static struct pci_tdi *cca_tsm_bind(struct pci_dev *pdev, struct kvm *kvm, u32 tdi_id)
+{
+ void *rmm_vdev;
+ struct pci_dev *dsm_dev = pdev->tsm->dsm_dev;
+ struct realm *realm = &kvm->arch.realm;
+
+ struct cca_host_tdi *host_tdi __free(kfree) =
+ kzalloc(sizeof(struct cca_host_tdi), GFP_KERNEL);
+ if (!host_tdi)
+ return ERR_PTR(-ENOMEM);
+
+ pci_tsm_tdi_constructor(pdev, &host_tdi->tdi, kvm, tdi_id);
+ /* Assign the tdi such that vdev_create can use that to lookup */
+ pdev->tsm->tdi = &host_tdi->tdi;
+ rmm_vdev = cca_vdev_create(realm, pdev, dsm_dev, tdi_id);
+ if (IS_ERR_OR_NULL(rmm_vdev)) {
+ pdev->tsm->tdi = NULL;
+ return rmm_vdev;
+ }
+
+ return &no_free_ptr(host_tdi)->tdi;
+}
+
static struct pci_tsm_ops cca_link_pci_ops = {
.probe = cca_tsm_pci_probe,
.remove = cca_tsm_pci_remove,
.connect = cca_tsm_connect,
.disconnect = cca_tsm_disconnect,
+ .bind = cca_tsm_bind,
};
static void cca_link_tsm_remove(void *tsm_dev)
diff --git a/drivers/virt/coco/arm-cca-host/rmi-da.c b/drivers/virt/coco/arm-cca-host/rmi-da.c
index d61c3191c038..84f0b2211cd1 100644
--- a/drivers/virt/coco/arm-cca-host/rmi-da.c
+++ b/drivers/virt/coco/arm-cca-host/rmi-da.c
@@ -934,3 +934,107 @@ int cca_pdev_disconnect_stream(struct pci_dev *pdev1,
return submit_stream_work(pdev1, pdev2, stream_handle);
}
+
+static unsigned long pci_get_tdi_id(struct pci_dev *pdev)
+{
+ /* requester segment is marked reserved. */
+ return pci_dev_id(pdev);
+}
+
+static void init_vdev_params_mmio_range(struct pci_dev *pdev,
+ struct rmi_vdev_params *params)
+{
+ int index = 0;
+
+ for (int i = 0; i < PCI_STD_NUM_BARS; i++) {
+ struct resource *res = &pdev->resource[i];
+
+ if (!(res->flags & IORESOURCE_MEM))
+ continue;
+
+ if (resource_size(res) == 0)
+ continue;
+
+ index = insert_addr_range_sorted(params->addr_range, index,
+ res->start, res->end + 1);
+ }
+
+ params->num_addr_range = index;
+}
+
+
+void *cca_vdev_create(struct realm *realm, struct pci_dev *pdev,
+ struct pci_dev *pf0_dev, u32 guest_rid)
+{
+ phys_addr_t rd_phys = virt_to_phys(realm->rd);
+ struct rmi_vdev_params *params = NULL;
+ struct cca_host_pdev_dsc *pdev_dsc;
+ struct cca_host_tdi *host_tdi;
+ phys_addr_t rmm_pdev_phys;
+ phys_addr_t rmm_vdev_phys;
+ bool should_free = true;
+ void *rmm_vdev;
+ int ret;
+
+ pdev_dsc = to_cca_pdev_dsc(pf0_dev);
+ if (!pdev_dsc->rmm_pdev) {
+ ret = -EINVAL;
+ goto err_out;
+ }
+
+ rmm_vdev = (void *)get_zeroed_page(GFP_KERNEL);
+ if (!rmm_vdev) {
+ ret = -ENOMEM;
+ goto err_out;
+ }
+
+ rmm_vdev_phys = virt_to_phys(rmm_vdev);
+ if (rmi_delegate_page(rmm_vdev_phys)) {
+ ret = -ENXIO;
+ goto err_granule_delegate;
+ }
+
+ params = (struct rmi_vdev_params *)get_zeroed_page(GFP_KERNEL);
+ if (!params) {
+ ret = -ENOMEM;
+ goto err_params_alloc;
+ }
+
+ params->flags = 0;
+ params->vdev_id = guest_rid;
+ params->tdi_id = pci_get_tdi_id(pdev);
+
+ init_vdev_params_mmio_range(pdev, params);
+
+ rmm_pdev_phys = virt_to_phys(pdev_dsc->rmm_pdev);
+ if (rmi_vdev_create(rd_phys, rmm_pdev_phys,
+ rmm_vdev_phys, virt_to_phys(params))) {
+ ret = -ENXIO;
+ goto err_vdev_create;
+ }
+
+ /* setup host_tdi before call to device communicate */
+ host_tdi = to_cca_host_tdi(pdev);
+ host_tdi->rmm_vdev = rmm_vdev;
+ host_tdi->realm = realm;
+
+ submit_vdev_state_transition_work(pdev, RMI_VDEV_UNLOCKED);
+
+ ret = rmi_vdev_lock(rd_phys, rmm_pdev_phys, rmm_vdev_phys);
+
+ submit_vdev_state_transition_work(pdev, RMI_VDEV_LOCKED);
+
+ free_page((unsigned long)params);
+ return rmm_vdev;
+
+err_vdev_create:
+ free_page((unsigned long)params);
+err_params_alloc:
+ if (rmi_undelegate_page(rmm_vdev_phys))
+ should_free = false;
+err_granule_delegate:
+ if (should_free)
+ free_page((unsigned long)rmm_vdev);
+err_out:
+ return ERR_PTR(ret);
+}
diff --git a/drivers/virt/coco/arm-cca-host/rmi-da.h b/drivers/virt/coco/arm-cca-host/rmi-da.h
index 88fa428f788e..cd13cbf650d5 100644
--- a/drivers/virt/coco/arm-cca-host/rmi-da.h
+++ b/drivers/virt/coco/arm-cca-host/rmi-da.h
@@ -233,5 +233,7 @@ int cca_pdev_stream_connect(struct pci_dev *pdev1, struct pci_dev *pdev2,
unsigned long *stream_handle);
int cca_pdev_disconnect_stream(struct pci_dev *pdev1,
struct pci_dev *pdev2, unsigned long stream_handle);
+void *cca_vdev_create(struct realm *realm, struct pci_dev *pdev,
+ struct pci_dev *pf0_dev, u32 guest_rid);
#endif
--
2.43.0
^ permalink raw reply related [flat|nested] 40+ messages in thread* [RFC PATCH v4 07/16] coco: host: arm64: Add pdev stream key refresh and purge helpers
2026-04-27 8:53 [RFC PATCH v4 00/16] coco/TSM: Implement host-side support for Arm CCA TDISP setup Aneesh Kumar K.V (Arm)
` (5 preceding siblings ...)
2026-04-27 8:53 ` [RFC PATCH v4 06/16] coco: host: arm64: Add support for RMM vdev objects Aneesh Kumar K.V (Arm)
@ 2026-04-27 8:53 ` Aneesh Kumar K.V (Arm)
2026-04-27 8:53 ` [RFC PATCH v4 08/16] coco: host: arm64: Add helpers to unlock and destroy RMM vdev Aneesh Kumar K.V (Arm)
` (9 subsequent siblings)
16 siblings, 0 replies; 40+ messages in thread
From: Aneesh Kumar K.V (Arm) @ 2026-04-27 8:53 UTC (permalink / raw)
To: linux-coco, kvmarm, linux-arm-kernel, linux-kernel
Cc: Aneesh Kumar K.V (Arm), Alexey Kardashevskiy, Catalin Marinas,
Dan Williams, Jason Gunthorpe, Joerg Roedel, Jonathan Cameron,
Marc Zyngier, Nicolin Chen, Pranjal Shrivastava, Robin Murphy,
Samuel Ortiz, Steven Price, Suzuki K Poulose, Will Deacon,
Xu Yilun
Add RMI command wrappers for PDEV stream key refresh and key purge,
and plumb them into arm-cca host helper functions.
The new helpers follow the existing stream operation pattern: issue the
RMI command for the local and optional peer pdev, then run the shared
stream synchronization work before returning.
This prepares the arm-cca host code to refresh or purge stream keys
during later vdev and stream state transitions.
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
arch/arm64/include/asm/rmi_cmds.h | 24 +++++++++++++++++
drivers/virt/coco/arm-cca-host/rmi-da.c | 35 +++++++++++++++++++++++++
drivers/virt/coco/arm-cca-host/rmi-da.h | 4 +++
3 files changed, 63 insertions(+)
diff --git a/arch/arm64/include/asm/rmi_cmds.h b/arch/arm64/include/asm/rmi_cmds.h
index 242ce2fac14e..03dffba763e1 100644
--- a/arch/arm64/include/asm/rmi_cmds.h
+++ b/arch/arm64/include/asm/rmi_cmds.h
@@ -925,4 +925,28 @@ static inline unsigned long rmi_vdev_lock(unsigned long rd,
return res.a0;
}
+static inline unsigned long rmi_pdev_stream_key_refresh(unsigned long pdev1_phys,
+ unsigned long pdev2_phys, unsigned long stream_handle)
+{
+
+ struct arm_smccc_res res;
+
+ arm_smccc_1_1_invoke(SMC_RMI_PDEV_STREAM_KEY_REFRESH, pdev1_phys,
+ pdev2_phys, stream_handle, &res);
+
+ return res.a0;
+}
+
+static inline unsigned long rmi_pdev_stream_key_purge(unsigned long pdev1_phys,
+ unsigned long pdev2_phys, unsigned long stream_handle)
+{
+
+ struct arm_smccc_res res;
+
+ arm_smccc_1_1_invoke(SMC_RMI_PDEV_STREAM_KEY_PURGE, pdev1_phys,
+ pdev2_phys, stream_handle, &res);
+
+ return res.a0;
+}
+
#endif /* __ASM_RMI_CMDS_H */
diff --git a/drivers/virt/coco/arm-cca-host/rmi-da.c b/drivers/virt/coco/arm-cca-host/rmi-da.c
index 84f0b2211cd1..128079d5b993 100644
--- a/drivers/virt/coco/arm-cca-host/rmi-da.c
+++ b/drivers/virt/coco/arm-cca-host/rmi-da.c
@@ -1038,3 +1038,38 @@ void *cca_vdev_create(struct realm *realm, struct pci_dev *pdev,
err_out:
return ERR_PTR(ret);
}
+
+int cca_pdev_refresh_stream_key(struct pci_dev *pdev1,
+ struct pci_dev *pdev2, unsigned long stream_handle)
+{
+
+ phys_addr_t rmm_pdev2_phys = 0;
+ struct cca_host_pdev_dsc *pdev_dsc1 = to_cca_pdev_dsc(pdev1);
+
+ if (pdev2)
+ rmm_pdev2_phys = virt_to_phys(to_cca_pdev_dsc(pdev2)->rmm_pdev);
+
+ if (rmi_pdev_stream_key_refresh(virt_to_phys(pdev_dsc1->rmm_pdev),
+ rmm_pdev2_phys, stream_handle))
+ return -EIO;
+
+ return submit_stream_work(pdev1, pdev2, stream_handle);
+}
+
+
+int cca_pdev_purge_stream_key(struct pci_dev *pdev1,
+ struct pci_dev *pdev2, unsigned long stream_handle)
+{
+
+ phys_addr_t rmm_pdev2_phys = 0;
+ struct cca_host_pdev_dsc *pdev_dsc1 = to_cca_pdev_dsc(pdev1);
+
+ if (pdev2)
+ rmm_pdev2_phys = virt_to_phys(to_cca_pdev_dsc(pdev2)->rmm_pdev);
+
+ if (rmi_pdev_stream_key_purge(virt_to_phys(pdev_dsc1->rmm_pdev),
+ rmm_pdev2_phys, stream_handle))
+ return -EIO;
+
+ return submit_stream_work(pdev1, pdev2, stream_handle);
+}
diff --git a/drivers/virt/coco/arm-cca-host/rmi-da.h b/drivers/virt/coco/arm-cca-host/rmi-da.h
index cd13cbf650d5..d6cdbc638d6d 100644
--- a/drivers/virt/coco/arm-cca-host/rmi-da.h
+++ b/drivers/virt/coco/arm-cca-host/rmi-da.h
@@ -235,5 +235,9 @@ int cca_pdev_disconnect_stream(struct pci_dev *pdev1,
struct pci_dev *pdev2, unsigned long stream_handle);
void *cca_vdev_create(struct realm *realm, struct pci_dev *pdev,
struct pci_dev *pf0_dev, u32 guest_rid);
+int cca_pdev_refresh_stream_key(struct pci_dev *pdev1,
+ struct pci_dev *pdev2, unsigned long stream_handle);
+int cca_pdev_purge_stream_key(struct pci_dev *pdev1,
+ struct pci_dev *pdev2, unsigned long stream_handle);
#endif
--
2.43.0
^ permalink raw reply related [flat|nested] 40+ messages in thread* [RFC PATCH v4 08/16] coco: host: arm64: Add helpers to unlock and destroy RMM vdev
2026-04-27 8:53 [RFC PATCH v4 00/16] coco/TSM: Implement host-side support for Arm CCA TDISP setup Aneesh Kumar K.V (Arm)
` (6 preceding siblings ...)
2026-04-27 8:53 ` [RFC PATCH v4 07/16] coco: host: arm64: Add pdev stream key refresh and purge helpers Aneesh Kumar K.V (Arm)
@ 2026-04-27 8:53 ` Aneesh Kumar K.V (Arm)
2026-04-27 8:53 ` [RFC PATCH v4 09/16] coco: host: arm64: Add support for da object read RHI handling Aneesh Kumar K.V (Arm)
` (8 subsequent siblings)
16 siblings, 0 replies; 40+ messages in thread
From: Aneesh Kumar K.V (Arm) @ 2026-04-27 8:53 UTC (permalink / raw)
To: linux-coco, kvmarm, linux-arm-kernel, linux-kernel
Cc: Aneesh Kumar K.V (Arm), Alexey Kardashevskiy, Catalin Marinas,
Dan Williams, Jason Gunthorpe, Joerg Roedel, Jonathan Cameron,
Marc Zyngier, Nicolin Chen, Pranjal Shrivastava, Robin Murphy,
Samuel Ortiz, Steven Price, Suzuki K Poulose, Will Deacon,
Xu Yilun
- define the SMCCC IDs and inline wrappers for RMI_VDEV_UNLOCK and
RMI_VDEV_DESTROY
- extend vdev_create() to treat communication failures as fatal and
tear down the newly created vdev
- provide vdev_unlock_and_destroy() that drives the vdev back to the
unlocked state, issues the destroy call, and frees the delegated granule
- hook the new helper into the TSM unbind path so host cleanup always
unlock and destroy RMM vdev and releases cached buffers
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
arch/arm64/include/asm/rmi_cmds.h | 20 +++++++
arch/arm64/include/asm/rmi_smc.h | 2 +
drivers/virt/coco/arm-cca-host/arm-cca.c | 25 +++++++++
drivers/virt/coco/arm-cca-host/rmi-da.c | 69 ++++++++++++++++++++++--
drivers/virt/coco/arm-cca-host/rmi-da.h | 3 ++
5 files changed, 116 insertions(+), 3 deletions(-)
diff --git a/arch/arm64/include/asm/rmi_cmds.h b/arch/arm64/include/asm/rmi_cmds.h
index 03dffba763e1..aa7ef9f07517 100644
--- a/arch/arm64/include/asm/rmi_cmds.h
+++ b/arch/arm64/include/asm/rmi_cmds.h
@@ -949,4 +949,24 @@ static inline unsigned long rmi_pdev_stream_key_purge(unsigned long pdev1_phys,
return res.a0;
}
+static inline unsigned long rmi_vdev_unlock(unsigned long rd,
+ unsigned long pdev_phys, unsigned long vdev_phys)
+{
+ struct arm_smccc_res res;
+
+ arm_smccc_1_1_invoke(SMC_RMI_VDEV_UNLOCK, rd, pdev_phys, vdev_phys, &res);
+
+ return res.a0;
+}
+
+static inline unsigned long rmi_vdev_destroy(unsigned long rd,
+ unsigned long pdev_phys, unsigned long vdev_phys)
+{
+ struct arm_smccc_res res;
+
+ arm_smccc_1_1_invoke(SMC_RMI_VDEV_DESTROY, rd, pdev_phys, vdev_phys, &res);
+
+ return res.a0;
+}
+
#endif /* __ASM_RMI_CMDS_H */
diff --git a/arch/arm64/include/asm/rmi_smc.h b/arch/arm64/include/asm/rmi_smc.h
index d14d13a9f169..6cd5439f56ec 100644
--- a/arch/arm64/include/asm/rmi_smc.h
+++ b/arch/arm64/include/asm/rmi_smc.h
@@ -649,6 +649,8 @@ enum rmi_vdev_state {
RMI_VDEV_LOCKED,
RMI_VDEV_STARTED,
RMI_VDEV_ERROR,
+ RMI_VDEV_KEY_REFRESH,
+ RMI_VDEV_KEY_PURGE,
};
#define MAX_VDEV_ADDR_RANGE 8
diff --git a/drivers/virt/coco/arm-cca-host/arm-cca.c b/drivers/virt/coco/arm-cca-host/arm-cca.c
index 5930a30dd16f..b75fa20513a9 100644
--- a/drivers/virt/coco/arm-cca-host/arm-cca.c
+++ b/drivers/virt/coco/arm-cca-host/arm-cca.c
@@ -473,12 +473,37 @@ static struct pci_tdi *cca_tsm_bind(struct pci_dev *pdev, struct kvm *kvm, u32 t
return &no_free_ptr(host_tdi)->tdi;
}
+/*
+ * All device memory should be unmapped by now.
+ * 1. A pci device destroy will cause a driver remove (vfio) which will have
+ * done a dmabuf based unmap
+ * 2. A vdevice/idevice destroy from VMM should have done a unmap_private_range
+ * vm ioctl before
+ * 3. A guest unlock request should have done a rsi_invalidiate_mem_mapping
+ * before unlock rhi
+ * 4. vfio_pci_core_close_device() should trigger tsm unbind if vdevice is not
+ * already distroyed and that path involves vfio_pci_dma_buf_cleanup() which
+ * should get kvm to unmap the devmap
+ */
+static void cca_tsm_unbind(struct pci_tdi *tdi)
+{
+ struct cca_host_tdi *host_tdi;
+ struct realm *realm = &tdi->kvm->arch.realm;
+
+ host_tdi = container_of(tdi, struct cca_host_tdi, tdi);
+ cca_vdev_unlock_and_destroy(realm, tdi->pdev, tdi->pdev->tsm->dsm_dev);
+ kvfree(host_tdi->interface_report);
+ kvfree(host_tdi->measurements);
+ kfree(host_tdi);
+}
+
static struct pci_tsm_ops cca_link_pci_ops = {
.probe = cca_tsm_pci_probe,
.remove = cca_tsm_pci_remove,
.connect = cca_tsm_connect,
.disconnect = cca_tsm_disconnect,
.bind = cca_tsm_bind,
+ .unbind = cca_tsm_unbind,
};
static void cca_link_tsm_remove(void *tsm_dev)
diff --git a/drivers/virt/coco/arm-cca-host/rmi-da.c b/drivers/virt/coco/arm-cca-host/rmi-da.c
index 128079d5b993..ef25392562e0 100644
--- a/drivers/virt/coco/arm-cca-host/rmi-da.c
+++ b/drivers/virt/coco/arm-cca-host/rmi-da.c
@@ -1018,15 +1018,25 @@ void *cca_vdev_create(struct realm *realm, struct pci_dev *pdev,
host_tdi->rmm_vdev = rmm_vdev;
host_tdi->realm = realm;
- submit_vdev_state_transition_work(pdev, RMI_VDEV_UNLOCKED);
+ ret = submit_vdev_state_transition_work(pdev, RMI_VDEV_UNLOCKED);
+ /* failure is treated as rmi_vdev_create failure */
+ if (ret)
+ goto err_vdev_comm;
- ret = rmi_vdev_lock(rd_phys, rmm_pdev_phys, rmm_vdev_phys);
+ if (rmi_vdev_lock(rd_phys, rmm_pdev_phys, rmm_vdev_phys)) {
+ ret = -ENXIO;
+ goto err_vdev_comm;
+ }
- submit_vdev_state_transition_work(pdev, RMI_VDEV_LOCKED);
+ ret = submit_vdev_state_transition_work(pdev, RMI_VDEV_LOCKED);
+ if (ret)
+ goto err_vdev_comm;
free_page((unsigned long)params);
return rmm_vdev;
+err_vdev_comm:
+ rmi_vdev_destroy(rd_phys, rmm_pdev_phys, rmm_vdev_phys);
err_vdev_create:
free_page((unsigned long)params);
err_params_alloc:
@@ -1073,3 +1083,56 @@ int cca_pdev_purge_stream_key(struct pci_dev *pdev1,
return submit_stream_work(pdev1, pdev2, stream_handle);
}
+
+void cca_vdev_unlock_and_destroy(struct realm *realm,
+ struct pci_dev *pdev, struct pci_dev *pf0_dev)
+{
+ int ret;
+ phys_addr_t rmm_pdev_phys;
+ phys_addr_t rmm_vdev_phys;
+ struct cca_host_pdev_dsc *pdev_dsc;
+ struct cca_host_tdi *host_tdi;
+ phys_addr_t rd_phys = virt_to_phys(realm->rd);
+
+ host_tdi = to_cca_host_tdi(pdev);
+ rmm_vdev_phys = virt_to_phys(host_tdi->rmm_vdev);
+
+ pdev_dsc = to_cca_pdev_dsc(pf0_dev);
+ rmm_pdev_phys = virt_to_phys(pdev_dsc->rmm_pdev);
+ if (rmi_vdev_unlock(rd_phys, rmm_pdev_phys, rmm_vdev_phys)) {
+ pci_err(pdev, "failed to unlock vdev\n");
+ goto unlock_err;
+ }
+
+ if (rmm_has_reg2_feature(RMI_FEATURE_REGISTER_2_VDEV_KROU)) {
+ struct pci_dev *rp = pcie_find_root_port(pf0_dev);
+ struct cca_host_pf0_ep_dsc *pf0_ep_dsc = to_cca_pf0_ep_dsc(pf0_dev);
+
+ ret = submit_vdev_state_transition_work(pdev, RMI_VDEV_KEY_REFRESH);
+ if (ret)
+ pci_err(pdev, "failed to transition vdev to KEY_REFRESH state (%d)\n", ret);
+
+ ret = cca_pdev_refresh_stream_key(pf0_dev, rp, pf0_ep_dsc->stream_handle);
+ if (ret)
+ pci_err(pf0_dev, "failed to refresh pdev stream key (%d)\n", ret);
+
+ ret = cca_pdev_purge_stream_key(pf0_dev, rp, pf0_ep_dsc->stream_handle);
+ if (ret)
+ pci_err(pf0_dev, "failed to purge pdev stream key (%d)\n", ret);
+ }
+
+ ret = submit_vdev_state_transition_work(pdev, RMI_VDEV_UNLOCKED);
+ if (ret)
+ pci_err(pdev, "failed to unlock vdev (%d)\n", ret);
+
+unlock_err:
+ /* Try to destroy even in case of error */
+ if (rmi_vdev_destroy(rd_phys, rmm_pdev_phys, rmm_vdev_phys))
+ pci_err(pdev, "failed to destroy vdev\n");
+
+ if (!rmi_undelegate_page(rmm_vdev_phys))
+ free_page((unsigned long)host_tdi->rmm_vdev);
+
+ host_tdi->rmm_vdev = NULL;
+ host_tdi->realm = NULL;
+}
diff --git a/drivers/virt/coco/arm-cca-host/rmi-da.h b/drivers/virt/coco/arm-cca-host/rmi-da.h
index d6cdbc638d6d..97f7eaf1f779 100644
--- a/drivers/virt/coco/arm-cca-host/rmi-da.h
+++ b/drivers/virt/coco/arm-cca-host/rmi-da.h
@@ -15,6 +15,7 @@
#include <linux/wait.h>
#include <asm/rmi_cmds.h>
#include <asm/rmi_smc.h>
+#include <asm/rhi.h>
#define MAX_CACHE_OBJ_SIZE SZ_16M
#define CACHE_CHUNK_SIZE SZ_4K
@@ -239,5 +240,7 @@ int cca_pdev_refresh_stream_key(struct pci_dev *pdev1,
struct pci_dev *pdev2, unsigned long stream_handle);
int cca_pdev_purge_stream_key(struct pci_dev *pdev1,
struct pci_dev *pdev2, unsigned long stream_handle);
+void cca_vdev_unlock_and_destroy(struct realm *realm, struct pci_dev *pdev,
+ struct pci_dev *pf0_dev);
#endif
--
2.43.0
^ permalink raw reply related [flat|nested] 40+ messages in thread* [RFC PATCH v4 09/16] coco: host: arm64: Add support for da object read RHI handling
2026-04-27 8:53 [RFC PATCH v4 00/16] coco/TSM: Implement host-side support for Arm CCA TDISP setup Aneesh Kumar K.V (Arm)
` (7 preceding siblings ...)
2026-04-27 8:53 ` [RFC PATCH v4 08/16] coco: host: arm64: Add helpers to unlock and destroy RMM vdev Aneesh Kumar K.V (Arm)
@ 2026-04-27 8:53 ` Aneesh Kumar K.V (Arm)
2026-04-27 8:53 ` [RFC PATCH v4 10/16] coco: host: arm64: Add helper for cached object fetches Aneesh Kumar K.V (Arm)
` (7 subsequent siblings)
16 siblings, 0 replies; 40+ messages in thread
From: Aneesh Kumar K.V (Arm) @ 2026-04-27 8:53 UTC (permalink / raw)
To: linux-coco, kvmarm, linux-arm-kernel, linux-kernel
Cc: Aneesh Kumar K.V (Arm), Alexey Kardashevskiy, Catalin Marinas,
Dan Williams, Jason Gunthorpe, Joerg Roedel, Jonathan Cameron,
Marc Zyngier, Nicolin Chen, Pranjal Shrivastava, Robin Murphy,
Samuel Ortiz, Steven Price, Suzuki K Poulose, Will Deacon,
Xu Yilun
Device assignment-related RHI calls result in a REC exit, which is
handled by the tsm guest_request callback.
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
arch/arm64/include/uapi/asm/rmi-da.h | 21 ++++++
drivers/virt/coco/arm-cca-host/arm-cca.c | 74 ++++++++++++++++++
drivers/virt/coco/arm-cca-host/rmi-da.c | 95 ++++++++++++++++++++++++
drivers/virt/coco/arm-cca-host/rmi-da.h | 3 +
4 files changed, 193 insertions(+)
create mode 100644 arch/arm64/include/uapi/asm/rmi-da.h
diff --git a/arch/arm64/include/uapi/asm/rmi-da.h b/arch/arm64/include/uapi/asm/rmi-da.h
new file mode 100644
index 000000000000..5ec3413dce94
--- /dev/null
+++ b/arch/arm64/include/uapi/asm/rmi-da.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+
+#ifndef _UAPI__ASM_RMI_DA_H
+#define _UAPI__ASM_RMI_DA_H
+
+#include <linux/types.h>
+
+struct arm64_vdev_object_size_guest_req {
+ __u32 req_type;
+ __u32 object_type;
+};
+#define __RHI_DA_OBJECT_SIZE 0x1
+
+struct arm64_vdev_object_read_guest_req {
+ __u32 req_type;
+ __u32 object_type;
+ __aligned_u64 offset;
+};
+#define __RHI_DA_OBJECT_READ 0x2
+
+#endif
diff --git a/drivers/virt/coco/arm-cca-host/arm-cca.c b/drivers/virt/coco/arm-cca-host/arm-cca.c
index b75fa20513a9..4bf1f1b394af 100644
--- a/drivers/virt/coco/arm-cca-host/arm-cca.c
+++ b/drivers/virt/coco/arm-cca-host/arm-cca.c
@@ -14,6 +14,7 @@
#include <linux/pci-doe.h>
#include <linux/pci.h>
#include <linux/kvm_host.h>
+#include <asm/rmi-da.h>
#include "rmi-da.h"
@@ -497,6 +498,78 @@ static void cca_tsm_unbind(struct pci_tdi *tdi)
kfree(host_tdi);
}
+static ssize_t cca_tsm_guest_req(struct pci_tdi *tdi, enum pci_tsm_req_scope scope,
+ sockptr_t req, size_t req_len, sockptr_t resp,
+ size_t resp_len, u64 *tsm_code)
+{
+ struct pci_dev *pdev = tdi->pdev;
+
+ if (req.is_kernel || resp.is_kernel)
+ return -EINVAL;
+
+ switch (scope) {
+ case PCI_TSM_REQ_INFO: {
+ u32 req_type;
+
+ if (get_user(req_type, (u32 __user *)req.user))
+ return -EFAULT;
+
+ switch (req_type) {
+ case __RHI_DA_OBJECT_SIZE: {
+ int object_size;
+ struct arm64_vdev_object_size_guest_req req_obj;
+
+ if (req_len != sizeof(req_obj))
+ return -EINVAL;
+
+ if (copy_from_user((void *)&req_obj, req.user, req_len))
+ return -EFAULT;
+ object_size = cca_vdev_get_object_size(pdev, req_obj.object_type);
+ if (object_size > 0) {
+ if (resp_len < sizeof(object_size))
+ return -EINVAL;
+ if (copy_to_user(resp.user, &object_size, sizeof(object_size)))
+ return -EFAULT;
+
+ if (resp_len != sizeof(object_size))
+ return resp_len - sizeof(object_size);
+ return 0;
+ }
+ /* error */
+ return object_size;
+ }
+ case __RHI_DA_OBJECT_READ:
+ {
+ int len;
+ struct arm64_vdev_object_read_guest_req req_obj;
+
+ if (req_len != sizeof(req_obj))
+ return -EINVAL;
+
+ if (copy_from_user((void *)&req_obj, req.user, req_len))
+ return -EFAULT;
+
+ len = cca_vdev_read_cached_object(pdev,
+ req_obj.object_type,
+ req_obj.offset,
+ resp_len, resp.user);
+ if (len > 0) {
+ if (resp_len != len)
+ return resp_len - len;
+ return 0;
+ }
+ /* error */
+ return len;
+ }
+ default:
+ return -EINVAL;
+ }
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
static struct pci_tsm_ops cca_link_pci_ops = {
.probe = cca_tsm_pci_probe,
.remove = cca_tsm_pci_remove,
@@ -504,6 +577,7 @@ static struct pci_tsm_ops cca_link_pci_ops = {
.disconnect = cca_tsm_disconnect,
.bind = cca_tsm_bind,
.unbind = cca_tsm_unbind,
+ .guest_req = cca_tsm_guest_req,
};
static void cca_link_tsm_remove(void *tsm_dev)
diff --git a/drivers/virt/coco/arm-cca-host/rmi-da.c b/drivers/virt/coco/arm-cca-host/rmi-da.c
index ef25392562e0..3db42c21dab0 100644
--- a/drivers/virt/coco/arm-cca-host/rmi-da.c
+++ b/drivers/virt/coco/arm-cca-host/rmi-da.c
@@ -12,6 +12,7 @@
#include <keys/asymmetric-type.h>
#include <keys/x509-parser.h>
#include <linux/kvm_types.h>
+#include <linux/kvm_host.h>
#include <asm/kvm_rmi.h>
#include "rmi-da.h"
@@ -1136,3 +1137,97 @@ void cca_vdev_unlock_and_destroy(struct realm *realm,
host_tdi->rmm_vdev = NULL;
host_tdi->realm = NULL;
}
+
+int cca_vdev_get_object_size(struct pci_dev *pdev, int type)
+{
+ long len;
+ struct cca_host_tdi *host_tdi;
+ struct cca_host_pf0_ep_dsc *pf0_ep_dsc;
+ struct pci_tsm *tsm = pdev->tsm;
+ struct cca_host_pdev_dsc *pdev_dsc;
+
+ if (!tsm)
+ return -EINVAL;
+
+ pdev_dsc = to_cca_pdev_dsc(tsm->dsm_dev);
+ pf0_ep_dsc = to_cca_pf0_ep_dsc(tsm->dsm_dev);
+ host_tdi = to_cca_host_tdi(pdev);
+
+ guard(mutex)(&pdev_dsc->object_lock);
+ /* Determine the buffer that should be used */
+ if (type == RHI_DA_OBJECT_INTERFACE_REPORT) {
+ if (!host_tdi->interface_report)
+ return -EINVAL;
+ len = host_tdi->interface_report->offset;
+ } else if (type == RHI_DA_OBJECT_MEASUREMENT) {
+ if (!host_tdi->measurements)
+ return -EINVAL;
+ len = host_tdi->measurements->offset;
+ } else if (type == RHI_DA_OBJECT_CERTIFICATE) {
+ if (!pf0_ep_dsc->cert_chain.cache)
+ return -EINVAL;
+ len = pf0_ep_dsc->cert_chain.cache->offset;
+ } else if (type == RHI_DA_OBJECT_VCA) {
+ if (!pf0_ep_dsc->vca)
+ return -EINVAL;
+ len = pf0_ep_dsc->vca->offset;
+ } else {
+ return -EINVAL;
+ }
+
+ return len;
+}
+
+int cca_vdev_read_cached_object(struct pci_dev *pdev, int type,
+ unsigned long offset, unsigned long max_len,
+ void __user *user_buf)
+{
+ void *buf;
+ unsigned long len;
+ struct cca_host_tdi *host_tdi;
+ struct cca_host_pf0_ep_dsc *pf0_ep_dsc;
+ struct pci_tsm *tsm = pdev->tsm;
+ struct cca_host_pdev_dsc *pdev_dsc;
+
+ if (!tsm)
+ return -EINVAL;
+
+ pdev_dsc = to_cca_pdev_dsc(tsm->dsm_dev);
+ pf0_ep_dsc = to_cca_pf0_ep_dsc(tsm->dsm_dev);
+ host_tdi = to_cca_host_tdi(pdev);
+
+ guard(mutex)(&pdev_dsc->object_lock);
+ /* Determine the buffer that should be used */
+ if (type == RHI_DA_OBJECT_INTERFACE_REPORT) {
+ if (!host_tdi->interface_report)
+ return -EINVAL;
+ len = host_tdi->interface_report->offset;
+ buf = host_tdi->interface_report->buf;
+ } else if (type == RHI_DA_OBJECT_MEASUREMENT) {
+ if (!host_tdi->measurements)
+ return -EINVAL;
+ len = host_tdi->measurements->offset;
+ buf = host_tdi->measurements->buf;
+ } else if (type == RHI_DA_OBJECT_CERTIFICATE) {
+ if (!pf0_ep_dsc->cert_chain.cache)
+ return -EINVAL;
+ len = pf0_ep_dsc->cert_chain.cache->offset;
+ buf = pf0_ep_dsc->cert_chain.cache->buf;
+ } else if (type == RHI_DA_OBJECT_VCA) {
+ if (!pf0_ep_dsc->vca)
+ return -EINVAL;
+ len = pf0_ep_dsc->vca->offset;
+ buf = pf0_ep_dsc->vca->buf;
+ } else {
+ return -EINVAL;
+ }
+
+ /* Assume that the buffer is large enough for the whole report */
+ if (max_len < len)
+ return -E2BIG;
+
+ if (copy_to_user(user_buf, buf + offset, len))
+ return -EIO;
+
+ return len;
+}
diff --git a/drivers/virt/coco/arm-cca-host/rmi-da.h b/drivers/virt/coco/arm-cca-host/rmi-da.h
index 97f7eaf1f779..4f1a61a5dcfa 100644
--- a/drivers/virt/coco/arm-cca-host/rmi-da.h
+++ b/drivers/virt/coco/arm-cca-host/rmi-da.h
@@ -242,5 +242,8 @@ int cca_pdev_purge_stream_key(struct pci_dev *pdev1,
struct pci_dev *pdev2, unsigned long stream_handle);
void cca_vdev_unlock_and_destroy(struct realm *realm, struct pci_dev *pdev,
struct pci_dev *pf0_dev);
+int cca_vdev_get_object_size(struct pci_dev *pdev, int type);
+int cca_vdev_read_cached_object(struct pci_dev *pdev, int type, unsigned long offset,
+ unsigned long max_len, void __user *user_buf);
#endif
--
2.43.0
^ permalink raw reply related [flat|nested] 40+ messages in thread* [RFC PATCH v4 10/16] coco: host: arm64: Add helper for cached object fetches
2026-04-27 8:53 [RFC PATCH v4 00/16] coco/TSM: Implement host-side support for Arm CCA TDISP setup Aneesh Kumar K.V (Arm)
` (8 preceding siblings ...)
2026-04-27 8:53 ` [RFC PATCH v4 09/16] coco: host: arm64: Add support for da object read RHI handling Aneesh Kumar K.V (Arm)
@ 2026-04-27 8:53 ` Aneesh Kumar K.V (Arm)
2026-04-27 8:53 ` [RFC PATCH v4 11/16] coco: host: arm64: Fetch interface report via RMI Aneesh Kumar K.V (Arm)
` (6 subsequent siblings)
16 siblings, 0 replies; 40+ messages in thread
From: Aneesh Kumar K.V (Arm) @ 2026-04-27 8:53 UTC (permalink / raw)
To: linux-coco, kvmarm, linux-arm-kernel, linux-kernel
Cc: Aneesh Kumar K.V (Arm), Alexey Kardashevskiy, Catalin Marinas,
Dan Williams, Jason Gunthorpe, Joerg Roedel, Jonathan Cameron,
Marc Zyngier, Nicolin Chen, Pranjal Shrivastava, Robin Murphy,
Samuel Ortiz, Steven Price, Suzuki K Poulose, Will Deacon,
Xu Yilun
Introduce vdev_fetch_object_work() so we have a single workqueue handler
that refreshes any cached Realm object (interface report, measurements,
certificates). The helper receives the cache buffer/offset/size via
dev_comm_work, clears the existing contents under dsm_dev.object_lock,
performs the VDEV_COMMUNICATE call, and uses the updated size to signal
failures back to the caller once the work completes.
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
drivers/virt/coco/arm-cca-host/rmi-da.c | 26 +++++++++++++++++++++++++
drivers/virt/coco/arm-cca-host/rmi-da.h | 3 +++
2 files changed, 29 insertions(+)
diff --git a/drivers/virt/coco/arm-cca-host/rmi-da.c b/drivers/virt/coco/arm-cca-host/rmi-da.c
index 3db42c21dab0..63b20c8aef54 100644
--- a/drivers/virt/coco/arm-cca-host/rmi-da.c
+++ b/drivers/virt/coco/arm-cca-host/rmi-da.c
@@ -1138,6 +1138,32 @@ void cca_vdev_unlock_and_destroy(struct realm *realm,
host_tdi->realm = NULL;
}
+static void __maybe_unused vdev_fetch_object_workfn(struct work_struct *work)
+{
+ int state;
+ struct pci_tsm *tsm;
+ struct cca_host_pdev_dsc *pdev_dsc;
+ struct dev_comm_work *setup_work;
+
+ setup_work = container_of(work, struct dev_comm_work, work);
+ tsm = setup_work->tsm;
+ pdev_dsc = to_cca_pdev_dsc(tsm->dsm_dev);
+
+ guard(mutex)(&pdev_dsc->object_lock);
+
+ if (setup_work->cache_size) {
+ memset(setup_work->cache_buf, 0, setup_work->cache_size);
+ *setup_work->cache_offset = 0;
+ }
+ state = do_dev_communicate(VDEV_COMMUNICATE, tsm, RMI_VDEV_ERROR, NULL);
+ /* return status through dev_comm_work.cache_cache */
+ if (state == RMI_VDEV_ERROR)
+ setup_work->cache_size = 0;
+ else
+ /* indicate success. This value is not used. */
+ setup_work->cache_size = CACHE_CHUNK_SIZE;
+}
+
int cca_vdev_get_object_size(struct pci_dev *pdev, int type)
{
long len;
diff --git a/drivers/virt/coco/arm-cca-host/rmi-da.h b/drivers/virt/coco/arm-cca-host/rmi-da.h
index 4f1a61a5dcfa..c1fc7c01943e 100644
--- a/drivers/virt/coco/arm-cca-host/rmi-da.h
+++ b/drivers/virt/coco/arm-cca-host/rmi-da.h
@@ -28,6 +28,9 @@ struct cache_object {
struct dev_comm_work {
struct pci_tsm *tsm;
int target_state;
+ u8 *cache_buf;
+ int *cache_offset;
+ int cache_size;
struct work_struct work;
};
--
2.43.0
^ permalink raw reply related [flat|nested] 40+ messages in thread* [RFC PATCH v4 11/16] coco: host: arm64: Fetch interface report via RMI
2026-04-27 8:53 [RFC PATCH v4 00/16] coco/TSM: Implement host-side support for Arm CCA TDISP setup Aneesh Kumar K.V (Arm)
` (9 preceding siblings ...)
2026-04-27 8:53 ` [RFC PATCH v4 10/16] coco: host: arm64: Add helper for cached object fetches Aneesh Kumar K.V (Arm)
@ 2026-04-27 8:53 ` Aneesh Kumar K.V (Arm)
2026-04-27 8:53 ` [RFC PATCH v4 12/16] coco: host: arm64: Fetch device measurements " Aneesh Kumar K.V (Arm)
` (5 subsequent siblings)
16 siblings, 0 replies; 40+ messages in thread
From: Aneesh Kumar K.V (Arm) @ 2026-04-27 8:53 UTC (permalink / raw)
To: linux-coco, kvmarm, linux-arm-kernel, linux-kernel
Cc: Aneesh Kumar K.V (Arm), Alexey Kardashevskiy, Catalin Marinas,
Dan Williams, Jason Gunthorpe, Joerg Roedel, Jonathan Cameron,
Marc Zyngier, Nicolin Chen, Pranjal Shrivastava, Robin Murphy,
Samuel Ortiz, Steven Price, Suzuki K Poulose, Will Deacon,
Xu Yilun
- define __RHI_DA_VDEV_GET_INTERFACE_REPORT for guest requests and
expose the RMI SMC ID/wrapper for RMI_VDEV_GET_INTERFACE_REPORT
- teach the CCA host driver to handle the new guest request by fetching
the report from RMM using rmi_vdev_get_interface_report() and
refreshing the cached buffer
- add a helper that submits a DOE work to pull the latest report into
the cache
This lets guests request up-to-date interface reports via RHI
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
arch/arm64/include/asm/rmi_cmds.h | 12 ++++++
arch/arm64/include/uapi/asm/rmi-da.h | 3 ++
drivers/virt/coco/arm-cca-host/arm-cca.c | 4 ++
drivers/virt/coco/arm-cca-host/rmi-da.c | 54 +++++++++++++++++++++++-
drivers/virt/coco/arm-cca-host/rmi-da.h | 1 +
5 files changed, 73 insertions(+), 1 deletion(-)
diff --git a/arch/arm64/include/asm/rmi_cmds.h b/arch/arm64/include/asm/rmi_cmds.h
index aa7ef9f07517..b3c04029bb47 100644
--- a/arch/arm64/include/asm/rmi_cmds.h
+++ b/arch/arm64/include/asm/rmi_cmds.h
@@ -969,4 +969,16 @@ static inline unsigned long rmi_vdev_destroy(unsigned long rd,
return res.a0;
}
+static inline unsigned long rmi_vdev_get_interface_report(unsigned long rd,
+ unsigned long pdev_phys,
+ unsigned long vdev_phys)
+{
+ struct arm_smccc_res res;
+
+ arm_smccc_1_1_invoke(SMC_RMI_VDEV_GET_INTERFACE_REPORT,
+ rd, pdev_phys, vdev_phys, &res);
+
+ return res.a0;
+}
+
#endif /* __ASM_RMI_CMDS_H */
diff --git a/arch/arm64/include/uapi/asm/rmi-da.h b/arch/arm64/include/uapi/asm/rmi-da.h
index 5ec3413dce94..8d36a4c59849 100644
--- a/arch/arm64/include/uapi/asm/rmi-da.h
+++ b/arch/arm64/include/uapi/asm/rmi-da.h
@@ -18,4 +18,7 @@ struct arm64_vdev_object_read_guest_req {
};
#define __RHI_DA_OBJECT_READ 0x2
+/* No arguments to this guest request */
+#define __RHI_DA_VDEV_UPDATE_INTERFACE_REPORT 0x3
+
#endif
diff --git a/drivers/virt/coco/arm-cca-host/arm-cca.c b/drivers/virt/coco/arm-cca-host/arm-cca.c
index 4bf1f1b394af..2955993d29ac 100644
--- a/drivers/virt/coco/arm-cca-host/arm-cca.c
+++ b/drivers/virt/coco/arm-cca-host/arm-cca.c
@@ -561,6 +561,10 @@ static ssize_t cca_tsm_guest_req(struct pci_tdi *tdi, enum pci_tsm_req_scope sco
/* error */
return len;
}
+ case __RHI_DA_VDEV_UPDATE_INTERFACE_REPORT:
+ {
+ return cca_vdev_update_interface_report(pdev);
+ }
default:
return -EINVAL;
}
diff --git a/drivers/virt/coco/arm-cca-host/rmi-da.c b/drivers/virt/coco/arm-cca-host/rmi-da.c
index 63b20c8aef54..1862e4ff8cbb 100644
--- a/drivers/virt/coco/arm-cca-host/rmi-da.c
+++ b/drivers/virt/coco/arm-cca-host/rmi-da.c
@@ -1138,7 +1138,7 @@ void cca_vdev_unlock_and_destroy(struct realm *realm,
host_tdi->realm = NULL;
}
-static void __maybe_unused vdev_fetch_object_workfn(struct work_struct *work)
+static void vdev_fetch_object_workfn(struct work_struct *work)
{
int state;
struct pci_tsm *tsm;
@@ -1257,3 +1257,55 @@ int cca_vdev_read_cached_object(struct pci_dev *pdev, int type,
return len;
}
+
+static int vdev_update_interface_report_cache(struct pci_dev *pdev)
+{
+ struct dev_comm_work comm_work;
+ struct cca_host_tdi *host_tdi = to_cca_host_tdi(pdev);
+ struct cca_host_comm_data *comm_data = to_cca_comm_data(pdev);
+
+ INIT_WORK_ONSTACK(&comm_work.work, vdev_fetch_object_workfn);
+ comm_work.tsm = pdev->tsm;
+ if (host_tdi->interface_report) {
+ comm_work.cache_buf = host_tdi->interface_report->buf;
+ comm_work.cache_offset = &host_tdi->interface_report->offset;
+ comm_work.cache_size = host_tdi->interface_report->size;
+ } else {
+ comm_work.cache_buf = NULL;
+ comm_work.cache_offset = NULL;
+ comm_work.cache_size = 0;
+ }
+
+ queue_work(comm_data->work_queue, &comm_work.work);
+ flush_work(&comm_work.work);
+ destroy_work_on_stack(&comm_work.work);
+
+ if (comm_work.cache_size == 0)
+ return -ENXIO;
+ return 0;
+}
+
+int cca_vdev_update_interface_report(struct pci_dev *pdev)
+{
+ phys_addr_t rmm_pdev_phys;
+ phys_addr_t rmm_vdev_phys;
+ struct cca_host_pdev_dsc *pdev_dsc;
+ struct cca_host_tdi *host_tdi;
+ struct realm *realm;
+ phys_addr_t rd_phys;
+
+ host_tdi = to_cca_host_tdi(pdev);
+ rmm_vdev_phys = virt_to_phys(host_tdi->rmm_vdev);
+ realm = &host_tdi->tdi.kvm->arch.realm;
+ rd_phys = virt_to_phys(realm->rd);
+
+ pdev_dsc = to_cca_pdev_dsc(pdev->tsm->dsm_dev);
+ rmm_pdev_phys = virt_to_phys(pdev_dsc->rmm_pdev);
+
+ if (rmi_vdev_get_interface_report(rd_phys,
+ rmm_pdev_phys, rmm_vdev_phys))
+ return -ENXIO;
+
+ /* get and update the interface report cache. */
+ return vdev_update_interface_report_cache(pdev);
+}
diff --git a/drivers/virt/coco/arm-cca-host/rmi-da.h b/drivers/virt/coco/arm-cca-host/rmi-da.h
index c1fc7c01943e..b114bf4d4202 100644
--- a/drivers/virt/coco/arm-cca-host/rmi-da.h
+++ b/drivers/virt/coco/arm-cca-host/rmi-da.h
@@ -248,5 +248,6 @@ void cca_vdev_unlock_and_destroy(struct realm *realm, struct pci_dev *pdev,
int cca_vdev_get_object_size(struct pci_dev *pdev, int type);
int cca_vdev_read_cached_object(struct pci_dev *pdev, int type, unsigned long offset,
unsigned long max_len, void __user *user_buf);
+int cca_vdev_update_interface_report(struct pci_dev *pdev);
#endif
--
2.43.0
^ permalink raw reply related [flat|nested] 40+ messages in thread* [RFC PATCH v4 12/16] coco: host: arm64: Fetch device measurements via RMI
2026-04-27 8:53 [RFC PATCH v4 00/16] coco/TSM: Implement host-side support for Arm CCA TDISP setup Aneesh Kumar K.V (Arm)
` (10 preceding siblings ...)
2026-04-27 8:53 ` [RFC PATCH v4 11/16] coco: host: arm64: Fetch interface report via RMI Aneesh Kumar K.V (Arm)
@ 2026-04-27 8:53 ` Aneesh Kumar K.V (Arm)
2026-04-27 8:53 ` [RFC PATCH v4 13/16] coco: host: KVM: arm64: Handle vdev validate-mapping exits Aneesh Kumar K.V (Arm)
` (4 subsequent siblings)
16 siblings, 0 replies; 40+ messages in thread
From: Aneesh Kumar K.V (Arm) @ 2026-04-27 8:53 UTC (permalink / raw)
To: linux-coco, kvmarm, linux-arm-kernel, linux-kernel
Cc: Aneesh Kumar K.V (Arm), Alexey Kardashevskiy, Catalin Marinas,
Dan Williams, Jason Gunthorpe, Joerg Roedel, Jonathan Cameron,
Marc Zyngier, Nicolin Chen, Pranjal Shrivastava, Robin Murphy,
Samuel Ortiz, Steven Price, Suzuki K Poulose, Will Deacon,
Xu Yilun
- define __RHI_DA_VDEV_GET_MEASUREMENTS for guest requests and
expose the RMI SMC ID/wrapper for RMI_VDEV_GET_DEV_MEASUREMENTS
- teach the CCA host driver to handle the new guest request by fetching
the device measurements from RMM using rmi_vdev_get_device_measurements()
and refreshing the cached buffer
- add a helper that submits a DOE work to pull the latest device
measurements into the cache
This lets guests request up-to-date device measurements via RHI
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
arch/arm64/include/asm/rmi_cmds.h | 12 +++++
arch/arm64/include/asm/rmi_smc.h | 13 +++++
arch/arm64/include/uapi/asm/rmi-da.h | 8 +++
drivers/virt/coco/arm-cca-host/arm-cca.c | 16 ++++++
drivers/virt/coco/arm-cca-host/rmi-da.c | 68 ++++++++++++++++++++++++
drivers/virt/coco/arm-cca-host/rmi-da.h | 1 +
6 files changed, 118 insertions(+)
diff --git a/arch/arm64/include/asm/rmi_cmds.h b/arch/arm64/include/asm/rmi_cmds.h
index b3c04029bb47..350fd9bc93a4 100644
--- a/arch/arm64/include/asm/rmi_cmds.h
+++ b/arch/arm64/include/asm/rmi_cmds.h
@@ -981,4 +981,16 @@ static inline unsigned long rmi_vdev_get_interface_report(unsigned long rd,
return res.a0;
}
+static inline unsigned long
+rmi_vdev_get_device_measurements(unsigned long rd, unsigned long pdev_phys,
+ unsigned long vdev_phys,
+ unsigned long param_phys)
+{
+ struct arm_smccc_res res;
+
+ arm_smccc_1_1_invoke(SMC_RMI_VDEV_GET_MEASUREMENTS,
+ rd, pdev_phys, vdev_phys, param_phys, &res);
+
+ return res.a0;
+}
#endif /* __ASM_RMI_CMDS_H */
diff --git a/arch/arm64/include/asm/rmi_smc.h b/arch/arm64/include/asm/rmi_smc.h
index 6cd5439f56ec..29dbe4e0dfb0 100644
--- a/arch/arm64/include/asm/rmi_smc.h
+++ b/arch/arm64/include/asm/rmi_smc.h
@@ -674,4 +674,17 @@ struct rmi_vdev_params {
};
};
+#define RMI_VDEV_MEASURE_HASH 0x0
+#define RMI_VDEV_MEASURE_RAW 0x1
+struct rmi_vdev_measurement_params {
+ union {
+ u64 flags;
+ u8 padding0[256];
+ };
+ union {
+ u8 nonce[32];
+ u8 padding1[256];
+ };
+};
+
#endif /* __ASM_RMI_SMC_H */
diff --git a/arch/arm64/include/uapi/asm/rmi-da.h b/arch/arm64/include/uapi/asm/rmi-da.h
index 8d36a4c59849..97648928f763 100644
--- a/arch/arm64/include/uapi/asm/rmi-da.h
+++ b/arch/arm64/include/uapi/asm/rmi-da.h
@@ -21,4 +21,12 @@ struct arm64_vdev_object_read_guest_req {
/* No arguments to this guest request */
#define __RHI_DA_VDEV_UPDATE_INTERFACE_REPORT 0x3
+struct arm64_vdev_device_measurement_guest_req {
+ __u32 req_type;
+ __u32 reserved;
+ __aligned_u64 flags;
+ __aligned_u64 nonce;
+};
+#define __RHI_DA_VDEV_UPDATE_MEASUREMENTS 0x4
+
#endif
diff --git a/drivers/virt/coco/arm-cca-host/arm-cca.c b/drivers/virt/coco/arm-cca-host/arm-cca.c
index 2955993d29ac..855427935f2d 100644
--- a/drivers/virt/coco/arm-cca-host/arm-cca.c
+++ b/drivers/virt/coco/arm-cca-host/arm-cca.c
@@ -565,6 +565,22 @@ static ssize_t cca_tsm_guest_req(struct pci_tdi *tdi, enum pci_tsm_req_scope sco
{
return cca_vdev_update_interface_report(pdev);
}
+ case __RHI_DA_VDEV_UPDATE_MEASUREMENTS:
+ {
+ int ret;
+ struct arm64_vdev_device_measurement_guest_req req_obj;
+
+ if (req_len != sizeof(req_obj))
+ return -EINVAL;
+
+ if (copy_from_user((void *)&req_obj, req.user, req_len))
+ return -EFAULT;
+
+ ret = cca_vdev_update_device_measurements(pdev,
+ req_obj.flags,
+ (u8 *)req_obj.nonce);
+ return ret;
+ }
default:
return -EINVAL;
}
diff --git a/drivers/virt/coco/arm-cca-host/rmi-da.c b/drivers/virt/coco/arm-cca-host/rmi-da.c
index 1862e4ff8cbb..ec7701ff7e03 100644
--- a/drivers/virt/coco/arm-cca-host/rmi-da.c
+++ b/drivers/virt/coco/arm-cca-host/rmi-da.c
@@ -1309,3 +1309,71 @@ int cca_vdev_update_interface_report(struct pci_dev *pdev)
/* get and update the interface report cache. */
return vdev_update_interface_report_cache(pdev);
}
+
+static int vdev_update_device_measurements_cache(struct pci_dev *pdev)
+{
+ struct dev_comm_work comm_work;
+ struct cca_host_tdi *host_tdi = to_cca_host_tdi(pdev);
+ struct cca_host_comm_data *comm_data = to_cca_comm_data(pdev);
+
+ INIT_WORK_ONSTACK(&comm_work.work, vdev_fetch_object_workfn);
+ comm_work.tsm = pdev->tsm;
+ if (host_tdi->measurements) {
+ comm_work.cache_buf = host_tdi->measurements->buf;
+ comm_work.cache_offset = &host_tdi->measurements->offset;
+ comm_work.cache_size = host_tdi->measurements->size;
+ } else {
+ comm_work.cache_buf = NULL;
+ comm_work.cache_offset = NULL;
+ comm_work.cache_size = 0;
+ }
+
+ queue_work(comm_data->work_queue, &comm_work.work);
+ flush_work(&comm_work.work);
+ destroy_work_on_stack(&comm_work.work);
+
+ if (comm_work.cache_size == 0)
+ return -ENXIO;
+ return 0;
+}
+
+static inline void vdev_measurement_param_free(struct rmi_vdev_measurement_params *param)
+{
+ return free_page((unsigned long)param);
+}
+DEFINE_FREE(measurement_param_free, struct rmi_vdev_measurement_params *, if (_T) vdev_measurement_param_free(_T))
+
+int cca_vdev_update_device_measurements(struct pci_dev *pdev, unsigned long flags, u8 *nonce)
+{
+ struct realm *realm;
+ phys_addr_t rd_phys;
+ phys_addr_t rmm_pdev_phys;
+ phys_addr_t rmm_vdev_phys;
+ struct cca_host_tdi *host_tdi;
+ struct cca_host_pdev_dsc *pdev_dsc;
+
+ host_tdi = to_cca_host_tdi(pdev);
+ rmm_vdev_phys = virt_to_phys(host_tdi->rmm_vdev);
+ realm = &host_tdi->tdi.kvm->arch.realm;
+ rd_phys = virt_to_phys(realm->rd);
+
+ pdev_dsc = to_cca_pdev_dsc(pdev->tsm->dsm_dev);
+ rmm_pdev_phys = virt_to_phys(pdev_dsc->rmm_pdev);
+
+ struct rmi_vdev_measurement_params *params __free(measurement_param_free) =
+ (struct rmi_vdev_measurement_params *)get_zeroed_page(GFP_KERNEL_ACCOUNT);
+ if (!params)
+ return -ENOMEM;
+
+ params->flags = flags;
+
+ if (copy_from_user(params->nonce, nonce, sizeof(params->nonce)))
+ return -EFAULT;
+
+ if (rmi_vdev_get_device_measurements(rd_phys, rmm_pdev_phys,
+ rmm_vdev_phys, virt_to_phys(params)))
+ return -ENXIO;
+
+ /* get and update the interface report cache. */
+ return vdev_update_device_measurements_cache(pdev);
+}
diff --git a/drivers/virt/coco/arm-cca-host/rmi-da.h b/drivers/virt/coco/arm-cca-host/rmi-da.h
index b114bf4d4202..621e0858f0c6 100644
--- a/drivers/virt/coco/arm-cca-host/rmi-da.h
+++ b/drivers/virt/coco/arm-cca-host/rmi-da.h
@@ -249,5 +249,6 @@ int cca_vdev_get_object_size(struct pci_dev *pdev, int type);
int cca_vdev_read_cached_object(struct pci_dev *pdev, int type, unsigned long offset,
unsigned long max_len, void __user *user_buf);
int cca_vdev_update_interface_report(struct pci_dev *pdev);
+int cca_vdev_update_device_measurements(struct pci_dev *pdev, unsigned long flags, u8 *nonce);
#endif
--
2.43.0
^ permalink raw reply related [flat|nested] 40+ messages in thread* [RFC PATCH v4 13/16] coco: host: KVM: arm64: Handle vdev validate-mapping exits
2026-04-27 8:53 [RFC PATCH v4 00/16] coco/TSM: Implement host-side support for Arm CCA TDISP setup Aneesh Kumar K.V (Arm)
` (11 preceding siblings ...)
2026-04-27 8:53 ` [RFC PATCH v4 12/16] coco: host: arm64: Fetch device measurements " Aneesh Kumar K.V (Arm)
@ 2026-04-27 8:53 ` Aneesh Kumar K.V (Arm)
2026-04-27 8:53 ` [RFC PATCH v4 14/16] KVM: arm64: Unmap device mappings when a private granule is destroyed Aneesh Kumar K.V (Arm)
` (3 subsequent siblings)
16 siblings, 0 replies; 40+ messages in thread
From: Aneesh Kumar K.V (Arm) @ 2026-04-27 8:53 UTC (permalink / raw)
To: linux-coco, kvmarm, linux-arm-kernel, linux-kernel
Cc: Aneesh Kumar K.V (Arm), Alexey Kardashevskiy, Catalin Marinas,
Dan Williams, Jason Gunthorpe, Joerg Roedel, Jonathan Cameron,
Marc Zyngier, Nicolin Chen, Pranjal Shrivastava, Robin Murphy,
Samuel Ortiz, Steven Price, Suzuki K Poulose, Will Deacon,
Xu Yilun
Add the RMM/RHI definitions needed for device-memory mapping exits and
plumb them through the arm64 Realm host stack.
Teach KVM to handle RMI_EXIT_VDEV_VALIDATE_MAPPING by exposing the request
to userspace as KVM_EXIT_ARM64_TIO, carrying the vdev id together with the
GPA range and host PA supplied by RMM. On re-entry, complete the request
with RMI_RTT_DEV_VALIDATE.
Also add realm_dev_mem_map() so the host CCA driver can install
device-memory mappings for a vdev, and wire the PCI TSM state-change
request path to call it.
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
Documentation/virt/kvm/api.rst | 20 +++
arch/arm64/include/asm/kvm_rmi.h | 4 +
arch/arm64/include/asm/rmi_smc.h | 2 +
arch/arm64/include/uapi/asm/rmi-da.h | 9 ++
arch/arm64/kvm/rmi-exit.c | 37 +++++
arch/arm64/kvm/rmi.c | 189 +++++++++++++++++++++++
drivers/virt/coco/arm-cca-host/arm-cca.c | 27 ++++
drivers/virt/coco/arm-cca-host/rmi-da.c | 21 +++
drivers/virt/coco/arm-cca-host/rmi-da.h | 2 +
include/uapi/linux/kvm.h | 11 ++
10 files changed, 322 insertions(+)
diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst
index 5dfaafae14b6..4df99bb2857f 100644
--- a/Documentation/virt/kvm/api.rst
+++ b/Documentation/virt/kvm/api.rst
@@ -7454,6 +7454,26 @@ the ``KVM_EXIT_ARM_SEA_FLAG_GPA_VALID`` flag is set. Otherwise, the value of
``gpa`` is unknown.
::
+ /* KVM_EXIT_ARM64_TIO*/
+ struct {
+ __u64 flags;
+ __u64 nr;
+ __u64 vdev_id;
+ __u64 gpa_base;
+ __u64 gpa_top;
+ __u64 pa_base;
+ __u64 response;
+ } cca_exit;
+
+Used on arm64 systems. When the VM capability ``KVM_CAP_ARM_RMI`` is
+enabled, KVM generates a VM exit whenever the guest needs host assistance
+to validate a device-memory GPA-to-PA mapping. The ``nr`` field records
+the exit reason; currently the following values are defined:
+
+* ``RMI_EXIT_VDEV_VALIDATE_MAPPING``: the guest wants the host to validate or install a
+ device-memory mapping.
+
+The ``flags`` field must be zero.
/* Fix the size of the union. */
char padding[256];
diff --git a/arch/arm64/include/asm/kvm_rmi.h b/arch/arm64/include/asm/kvm_rmi.h
index e1f5523c2dfa..f49988fe182e 100644
--- a/arch/arm64/include/asm/kvm_rmi.h
+++ b/arch/arm64/include/asm/kvm_rmi.h
@@ -126,4 +126,8 @@ static inline bool kvm_realm_is_private_address(struct realm *realm,
return !(addr & BIT(realm->ia_bits - 1));
}
+int realm_dev_mem_map(struct kvm *kvm, unsigned long pdev_phys,
+ unsigned long vdev_phys, unsigned long start_ipa,
+ unsigned long end_ipa, unsigned long start_pa);
+
#endif /* __ASM_KVM_RMI_H */
diff --git a/arch/arm64/include/asm/rmi_smc.h b/arch/arm64/include/asm/rmi_smc.h
index 29dbe4e0dfb0..6bbabcd853bd 100644
--- a/arch/arm64/include/asm/rmi_smc.h
+++ b/arch/arm64/include/asm/rmi_smc.h
@@ -328,6 +328,7 @@ struct rec_params {
#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_DEV_MEM_RESPONSE BIT(6)
#define REC_RUN_GPRS 31
#define REC_MAX_GIC_NUM_LRS 16
@@ -360,6 +361,7 @@ struct rec_enter {
#define RMI_EXIT_RIPAS_CHANGE 0x04
#define RMI_EXIT_HOST_CALL 0x05
#define RMI_EXIT_SERROR 0x06
+#define RMI_EXIT_VDEV_VALIDATE_MAPPING 0x09
struct rec_exit {
union { /* 0x000 */
diff --git a/arch/arm64/include/uapi/asm/rmi-da.h b/arch/arm64/include/uapi/asm/rmi-da.h
index 97648928f763..572afb4095f2 100644
--- a/arch/arm64/include/uapi/asm/rmi-da.h
+++ b/arch/arm64/include/uapi/asm/rmi-da.h
@@ -29,4 +29,13 @@ struct arm64_vdev_device_measurement_guest_req {
};
#define __RHI_DA_VDEV_UPDATE_MEASUREMENTS 0x4
+struct arm64_vdev_device_memmap_guest_req {
+ __u32 req_type;
+ __u32 reserved;
+ __aligned_u64 gpa_base;
+ __aligned_u64 gpa_top;
+ __aligned_u64 pa_base;
+};
+#define __REC_DA_VDEV_MAP 0x5
+
#endif
diff --git a/arch/arm64/kvm/rmi-exit.c b/arch/arm64/kvm/rmi-exit.c
index 7eff6967530c..8c7cf716ce3c 100644
--- a/arch/arm64/kvm/rmi-exit.c
+++ b/arch/arm64/kvm/rmi-exit.c
@@ -129,6 +129,41 @@ static int rec_exit_host_call(struct kvm_vcpu *vcpu)
return kvm_smccc_call_handler(vcpu);
}
+static inline void kvm_prepare_vdev_validate_mapping_exit(struct kvm_vcpu *vcpu,
+ gpa_t gpa_base, gpa_t gpa_top,
+ hpa_t pa_base, unsigned long vdev_id)
+{
+ vcpu->run->exit_reason = KVM_EXIT_ARM64_TIO;
+ vcpu->run->cca_exit.nr = RMI_EXIT_VDEV_VALIDATE_MAPPING;
+ vcpu->run->cca_exit.vdev_id = vdev_id;
+ vcpu->run->cca_exit.flags = 0;
+ vcpu->run->cca_exit.gpa_base = gpa_base;
+ vcpu->run->cca_exit.gpa_top = gpa_top;
+ vcpu->run->cca_exit.pa_base = pa_base;
+ vcpu->run->cca_exit.response = 0;
+}
+
+static int rec_exit_vdev_validate_mapping(struct kvm_vcpu *vcpu)
+{
+ struct kvm *kvm = vcpu->kvm;
+ struct realm *realm = &kvm->arch.realm;
+ struct realm_rec *rec = &vcpu->arch.rec;
+ unsigned long base = rec->run->exit.dev_mem_base;
+ unsigned long top = rec->run->exit.dev_mem_top;
+
+ if (!kvm_realm_is_private_address(realm, base) ||
+ !kvm_realm_is_private_address(realm, top - 1)) {
+
+ vcpu->run->cca_exit.response = -EINVAL;
+ /* return to guest */
+ return 1;
+ }
+
+ kvm_prepare_vdev_validate_mapping_exit(vcpu, base, top, rec->run->exit.dev_mem_pa,
+ rec->run->exit.vdev_id_1);
+ return 0;
+}
+
static void update_arch_timer_irq_lines(struct kvm_vcpu *vcpu)
{
struct realm_rec *rec = &vcpu->arch.rec;
@@ -198,6 +233,8 @@ int handle_rec_exit(struct kvm_vcpu *vcpu, int rec_run_ret)
return rec_exit_ripas_change(vcpu);
case RMI_EXIT_HOST_CALL:
return rec_exit_host_call(vcpu);
+ case RMI_EXIT_VDEV_VALIDATE_MAPPING:
+ return rec_exit_vdev_validate_mapping(vcpu);
}
kvm_pr_unimpl("Unsupported exit reason: %u\n",
diff --git a/arch/arm64/kvm/rmi.c b/arch/arm64/kvm/rmi.c
index f33d17ca855d..3a549dc87906 100644
--- a/arch/arm64/kvm/rmi.c
+++ b/arch/arm64/kvm/rmi.c
@@ -1283,6 +1283,192 @@ static void kvm_complete_ripas_change(struct kvm_vcpu *vcpu)
rec->run->exit.ripas_base = base;
}
+static int rmi_rtt_dev_map(unsigned long rd_phys, unsigned long vdev_phys,
+ unsigned long base, unsigned long top, unsigned long flags,
+ unsigned long oaddr, unsigned long *out_top, unsigned long *rmi_ret)
+{
+ struct rmi_sro_state *sro __free(sro) =
+ rmi_sro_init(SMC_RMI_RTT_DEV_MAP, rd_phys, vdev_phys, base, top, flags, oaddr);
+ if (!sro)
+ return -ENOMEM;
+
+ *rmi_ret = rmi_sro_execute(sro);
+ if (*rmi_ret)
+ return 0;
+
+ *out_top = sro->regs.a1;
+
+ return 0;
+}
+
+static int rmi_rtt_dev_validate(unsigned long rd_phys, unsigned long rec_phys,
+ unsigned long base, unsigned long top, unsigned long *out_top,
+ unsigned long *rmi_ret)
+{
+ struct rmi_sro_state *sro __free(sro) =
+ rmi_sro_init(SMC_RMI_RTT_DEV_VALIDATE, rd_phys,
+ rec_phys, base, top);
+ if (!sro)
+ return -ENOMEM;
+
+ *rmi_ret = rmi_sro_execute(sro);
+ if (*rmi_ret)
+ return 0;
+
+ *out_top = sro->regs.a1;
+
+ return 0;
+}
+
+/*
+ * Even though we can map larger block, since we need to delegate each granule.
+ * We map granule size and fold
+ */
+static int __realm_dev_mem_map(struct kvm *kvm, struct kvm_mmu_memory_cache *cache,
+ unsigned long pdev_phys, unsigned long vdev_phys,
+ unsigned long start_ipa, unsigned long end_ipa,
+ phys_addr_t phys, unsigned long *top_ipa)
+{
+ int ret = 0;
+ unsigned long rmi_ret;
+ unsigned long ipa = start_ipa, next_ipa;
+ struct realm *realm = &kvm->arch.realm;
+ phys_addr_t rd_phys = virt_to_phys(realm->rd);
+
+ if (rmi_delegate_range(phys, end_ipa - start_ipa))
+ return -EINVAL;
+
+ while (ipa < end_ipa) {
+ unsigned long flags = RMI_ADDR_TYPE_SINGLE;
+ unsigned long range_desc = addr_range_desc(phys, end_ipa - ipa);
+
+ ret = rmi_rtt_dev_map(rd_phys, vdev_phys, ipa, end_ipa, flags,
+ range_desc, &next_ipa, &rmi_ret);
+ if (ret)
+ goto err_undelegate_tail;
+
+ if (RMI_RETURN_STATUS(rmi_ret) == RMI_ERROR_RTT) {
+ /* Create missing RTTs and retry */
+ int level = RMI_RETURN_INDEX(rmi_ret);
+
+ WARN_ON(level == RMM_RTT_MAX_LEVEL);
+
+ if (kvm_mmu_memory_cache_nr_free_objects(cache) <
+ (RMM_RTT_MAX_LEVEL - level)) {
+ ret = -ENOMEM;
+ goto err_undelegate_tail;
+ }
+
+ ret = realm_create_rtt_levels(realm, ipa, level,
+ RMM_RTT_MAX_LEVEL,
+ cache);
+ if (ret)
+ goto err_undelegate_tail;
+
+ ret = rmi_rtt_dev_map(rd_phys, vdev_phys, ipa, end_ipa, flags,
+ range_desc, &next_ipa, &rmi_ret);
+ if (ret)
+ goto err_undelegate_tail;
+ }
+
+ if (WARN_ON(rmi_ret != RMI_SUCCESS)) {
+ ret = -EIO;
+ goto err_undelegate_tail;
+ }
+
+ phys += next_ipa - ipa;
+ ipa = next_ipa;
+ }
+ /*
+ * successfully mapped the provided range, return the top_ipa
+ */
+ *top_ipa = end_ipa;
+ return 0;
+
+err_undelegate_tail:
+ *top_ipa = ipa;
+ /*
+ * undelegate the tail range. Rest will be done by the caller.
+ */
+ if (end_ipa > ipa)
+ WARN_ON(rmi_undelegate_range(phys, end_ipa - ipa));
+
+ return ret;
+}
+
+int realm_dev_mem_map(struct kvm *kvm, unsigned long pdev_phys,
+ unsigned long vdev_phys, unsigned long start_ipa,
+ unsigned long end_ipa, unsigned long start_pa)
+{
+ int ret;
+ unsigned long top_ipa;
+ unsigned long base_ipa = start_ipa;
+ struct kvm_s2_mmu *mmu = &kvm->arch.mmu;
+ struct kvm_mmu_memory_cache cache = { .gfp_zero = __GFP_ZERO };
+
+ do {
+ ret = kvm_mmu_topup_memory_cache(&cache,
+ kvm_mmu_cache_min_pages(mmu));
+ if (ret)
+ break;
+
+ write_lock(&kvm->mmu_lock);
+ ret = __realm_dev_mem_map(kvm, &cache, pdev_phys, vdev_phys,
+ start_ipa, end_ipa, start_pa, &top_ipa);
+ write_unlock(&kvm->mmu_lock);
+
+ /* update base before we break out of loop*/
+ start_pa += top_ipa - start_ipa;
+ start_ipa = top_ipa;
+ if (ret && ret != -ENOMEM)
+ break;
+ } while (start_ipa < end_ipa);
+
+ kvm_mmu_free_memory_cache(&cache);
+
+ if (!ret) {
+ /* fold rtts if we can */
+ for (start_ipa = ALIGN(base_ipa, RMM_L2_BLOCK_SIZE);
+ ((start_ipa + RMM_L2_BLOCK_SIZE) < end_ipa); start_ipa += RMM_L2_BLOCK_SIZE)
+ fold_rtt(&kvm->arch.realm, start_ipa, RMM_RTT_BLOCK_LEVEL);
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(realm_dev_mem_map);
+
+static void kvm_complete_vdev_map_validate(struct kvm_vcpu *vcpu)
+{
+ struct kvm *kvm = vcpu->kvm;
+ struct realm_rec *rec = &vcpu->arch.rec;
+ struct kvm_run *run = vcpu->run;
+ struct realm *realm = &kvm->arch.realm;
+ phys_addr_t rd_phys = virt_to_phys(realm->rd);
+ phys_addr_t rec_phys = virt_to_phys(rec->rec_page);
+
+ /* reject the vdev_map validate request */
+ if (run->cca_exit.response) {
+ rec->run->enter.flags = REC_ENTER_FLAG_DEV_MEM_RESPONSE;
+ } else {
+ unsigned long next_ipa;
+ unsigned long start_ipa = run->cca_exit.gpa_base;
+
+ while (start_ipa < run->cca_exit.gpa_top) {
+ int ret;
+ unsigned long rmi_ret;
+
+ ret = rmi_rtt_dev_validate(rd_phys, rec_phys, start_ipa,
+ run->cca_exit.gpa_top, &next_ipa,
+ &rmi_ret);
+ if (ret || rmi_ret) {
+ rec->run->enter.flags = REC_ENTER_FLAG_DEV_MEM_RESPONSE;
+ break;
+ }
+ start_ipa = next_ipa;
+ }
+ }
+}
+
/*
* kvm_rec_pre_enter - Complete operations before entering a REC
*
@@ -1311,6 +1497,9 @@ int kvm_rec_pre_enter(struct kvm_vcpu *vcpu)
case RMI_EXIT_RIPAS_CHANGE:
kvm_complete_ripas_change(vcpu);
break;
+ case RMI_EXIT_VDEV_VALIDATE_MAPPING:
+ kvm_complete_vdev_map_validate(vcpu);
+ break;
}
return 1;
diff --git a/drivers/virt/coco/arm-cca-host/arm-cca.c b/drivers/virt/coco/arm-cca-host/arm-cca.c
index 855427935f2d..66e0acadf743 100644
--- a/drivers/virt/coco/arm-cca-host/arm-cca.c
+++ b/drivers/virt/coco/arm-cca-host/arm-cca.c
@@ -585,6 +585,33 @@ static ssize_t cca_tsm_guest_req(struct pci_tdi *tdi, enum pci_tsm_req_scope sco
return -EINVAL;
}
}
+ case PCI_TSM_REQ_STATE_CHANGE:
+ {
+ u32 req_type;
+
+ if (get_user(req_type, (u32 __user *)req.user))
+ return -EFAULT;
+
+ switch (req_type) {
+
+ case __REC_DA_VDEV_MAP:
+ {
+ struct arm64_vdev_device_memmap_guest_req req_obj;
+
+ if (req_len != sizeof(req_obj))
+ return -EINVAL;
+
+ if (copy_from_user((void *)&req_obj, req.user, req_len))
+ return -EFAULT;
+
+ return cca_vdev_device_map(pdev, req_obj.gpa_base,
+ req_obj.gpa_top,
+ req_obj.pa_base);
+ }
+ default:
+ return -EINVAL;
+ }
+ }
default:
return -EINVAL;
}
diff --git a/drivers/virt/coco/arm-cca-host/rmi-da.c b/drivers/virt/coco/arm-cca-host/rmi-da.c
index ec7701ff7e03..543c40fb1160 100644
--- a/drivers/virt/coco/arm-cca-host/rmi-da.c
+++ b/drivers/virt/coco/arm-cca-host/rmi-da.c
@@ -1377,3 +1377,24 @@ int cca_vdev_update_device_measurements(struct pci_dev *pdev, unsigned long flag
/* get and update the interface report cache. */
return vdev_update_device_measurements_cache(pdev);
}
+
+int cca_vdev_device_map(struct pci_dev *pdev, unsigned long gpa_base,
+ unsigned long gpa_top, unsigned long pa_base)
+{
+ struct kvm *kvm;
+ struct realm *realm;
+ phys_addr_t rmm_pdev_phys;
+ phys_addr_t rmm_vdev_phys;
+ struct cca_host_tdi *host_tdi;
+ struct cca_host_pdev_dsc *pdev_dsc;
+
+ host_tdi = to_cca_host_tdi(pdev);
+ pdev_dsc = to_cca_pdev_dsc(pdev->tsm->dsm_dev);
+ kvm = host_tdi->tdi.kvm;
+ realm = &kvm->arch.realm;
+ rmm_vdev_phys = virt_to_phys(host_tdi->rmm_vdev);
+ rmm_pdev_phys = virt_to_phys(pdev_dsc->rmm_pdev);
+
+ return realm_dev_mem_map(kvm, rmm_pdev_phys, rmm_vdev_phys,
+ gpa_base, gpa_top, pa_base);
+}
diff --git a/drivers/virt/coco/arm-cca-host/rmi-da.h b/drivers/virt/coco/arm-cca-host/rmi-da.h
index 621e0858f0c6..3dfb6b3cc2ef 100644
--- a/drivers/virt/coco/arm-cca-host/rmi-da.h
+++ b/drivers/virt/coco/arm-cca-host/rmi-da.h
@@ -250,5 +250,7 @@ int cca_vdev_read_cached_object(struct pci_dev *pdev, int type, unsigned long of
unsigned long max_len, void __user *user_buf);
int cca_vdev_update_interface_report(struct pci_dev *pdev);
int cca_vdev_update_device_measurements(struct pci_dev *pdev, unsigned long flags, u8 *nonce);
+int cca_vdev_device_map(struct pci_dev *pdev, unsigned long gpa_base,
+ unsigned long gpa_top, unsigned long pa_base);
#endif
diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
index 309f058cf2f8..bac41f2b13e4 100644
--- a/include/uapi/linux/kvm.h
+++ b/include/uapi/linux/kvm.h
@@ -192,6 +192,7 @@ struct kvm_exit_snp_req_certs {
#define KVM_EXIT_ARM_SEA 41
#define KVM_EXIT_ARM_LDST64B 42
#define KVM_EXIT_SNP_REQ_CERTS 43
+#define KVM_EXIT_ARM64_TIO 44
/* For KVM_EXIT_INTERNAL_ERROR */
/* Emulate instruction failed. */
@@ -496,6 +497,16 @@ struct kvm_run {
} arm_sea;
/* KVM_EXIT_SNP_REQ_CERTS */
struct kvm_exit_snp_req_certs snp_req_certs;
+ /* KVM_EXIT_ARM64_TIO*/
+ struct {
+ __u64 flags;
+ __u64 nr;
+ __u64 vdev_id;
+ __u64 gpa_base;
+ __u64 gpa_top; /* input and output */
+ __u64 pa_base;
+ __u64 response;
+ } cca_exit;
/* Fix the size of the union. */
char padding[256];
};
--
2.43.0
^ permalink raw reply related [flat|nested] 40+ messages in thread* [RFC PATCH v4 14/16] KVM: arm64: Unmap device mappings when a private granule is destroyed
2026-04-27 8:53 [RFC PATCH v4 00/16] coco/TSM: Implement host-side support for Arm CCA TDISP setup Aneesh Kumar K.V (Arm)
` (12 preceding siblings ...)
2026-04-27 8:53 ` [RFC PATCH v4 13/16] coco: host: KVM: arm64: Handle vdev validate-mapping exits Aneesh Kumar K.V (Arm)
@ 2026-04-27 8:53 ` Aneesh Kumar K.V (Arm)
2026-04-27 8:53 ` [RFC PATCH v4 15/16] coco: host: arm64: Transition vdevs to TDISP RUN state Aneesh Kumar K.V (Arm)
` (2 subsequent siblings)
16 siblings, 0 replies; 40+ messages in thread
From: Aneesh Kumar K.V (Arm) @ 2026-04-27 8:53 UTC (permalink / raw)
To: linux-coco, kvmarm, linux-arm-kernel, linux-kernel
Cc: Aneesh Kumar K.V (Arm), Alexey Kardashevskiy, Catalin Marinas,
Dan Williams, Jason Gunthorpe, Joerg Roedel, Jonathan Cameron,
Marc Zyngier, Nicolin Chen, Pranjal Shrivastava, Robin Murphy,
Samuel Ortiz, Steven Price, Suzuki K Poulose, Will Deacon,
Xu Yilun
Ensure tearing down a private granule also tears down any RMM device
mapping by reading the RTT entry, invoking the new RMI_VDEV_MEM_UNMAP,
and remembering the entry’s RIPAS so we only free RAM pages.
Drive the device-unmap path when RIPAS transitions to EMPTY. Also roll
back partially built device maps when errors occur.
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
arch/arm64/include/asm/rmi_smc.h | 1 +
arch/arm64/kvm/rmi.c | 87 ++++++++++++++++++++++++++++++--
2 files changed, 83 insertions(+), 5 deletions(-)
diff --git a/arch/arm64/include/asm/rmi_smc.h b/arch/arm64/include/asm/rmi_smc.h
index 6bbabcd853bd..f3ad545d68b7 100644
--- a/arch/arm64/include/asm/rmi_smc.h
+++ b/arch/arm64/include/asm/rmi_smc.h
@@ -199,6 +199,7 @@ enum rmi_ripas {
RMI_EMPTY = 0,
RMI_RAM = 1,
RMI_DESTROYED = 2,
+ RMI_DEV = 3,
};
#define RMI_NO_MEASURE_CONTENT 0
diff --git a/arch/arm64/kvm/rmi.c b/arch/arm64/kvm/rmi.c
index 3a549dc87906..cc9e045dcae9 100644
--- a/arch/arm64/kvm/rmi.c
+++ b/arch/arm64/kvm/rmi.c
@@ -720,6 +720,11 @@ static int realm_create_rd(struct kvm *kvm)
return r;
}
+static int rmi_rtt_dev_unmap(unsigned long rd_phys,
+ unsigned long base, unsigned long top,
+ unsigned long *out_ipa, unsigned long *out_desc,
+ unsigned long *rmi_ret);
+
static void realm_unmap_private_range(struct kvm *kvm,
unsigned long start,
unsigned long end,
@@ -728,16 +733,33 @@ static void realm_unmap_private_range(struct kvm *kvm,
struct realm *realm = &kvm->arch.realm;
unsigned long rd = virt_to_phys(realm->rd);
unsigned long next_addr, addr;
+ struct rtt_entry rtt_entry;
int ret;
+ /* Called with mmu_lock held, so RTT entry can't change. */
+ lockdep_assert_held_write(&kvm->mmu_lock);
+
+ /* An unmap request won't mix different RIPAS ranges. */
+ if (rmi_rtt_read_entry(rd, start, RMM_RTT_MAX_LEVEL, &rtt_entry))
+ return;
+
for (addr = start; addr < end; addr = next_addr) {
+ unsigned long rmi_ret;
unsigned long out_range;
unsigned long flags = RMI_ADDR_TYPE_SINGLE;
/* TODO: Optimise using RMI_ADDR_TYPE_LIST */
retry:
- ret = rmi_rtt_data_unmap(rd, addr, end, flags, 0,
- &next_addr, &out_range, NULL);
+ if (rtt_entry.ripas == RMI_DEV)
+ ret = rmi_rtt_dev_unmap(rd, addr, end,
+ &next_addr, &out_range,
+ &rmi_ret);
+ else
+ ret = rmi_rtt_data_unmap(rd, addr, end, flags, 0,
+ &next_addr, &out_range, NULL);
+
+ if (!ret && rtt_entry.ripas == RMI_DEV)
+ ret = rmi_ret;
if (RMI_RETURN_STATUS(ret) == RMI_ERROR_RTT) {
phys_addr_t rtt;
@@ -763,6 +785,7 @@ static void realm_unmap_private_range(struct kvm *kvm,
if (WARN_ON(ret))
break;
+ //FIXME!! where are we freeing the private page?
if (may_block)
cond_resched_rwlock_write(&kvm->mmu_lock);
}
@@ -1152,10 +1175,27 @@ static int realm_set_ipa_state(struct kvm_vcpu *vcpu,
unsigned long *top_ipa)
{
struct kvm *kvm = vcpu->kvm;
- int ret = ripas_change(kvm, vcpu, start, end, RIPAS_SET, top_ipa);
+ int ret;
- if (ripas == RMI_EMPTY && *top_ipa != start)
- realm_unmap_private_range(kvm, start, *top_ipa, false);
+ /*
+ * We use the RIPAS value to decide between a data_destroy or a
+ * dev_mem_unmap. Hence call realm_unmap_private_range() before
+ * ripas_change().
+ *
+ * Technically, for private RAM, we don't need to call
+ * realm_unmap_private_range(), because any RIPAS change via RSI would
+ * trigger a memory fault exit. That would, in turn, invalidate the
+ * guest's memfd range, which then triggers realm_unmap_private_range()
+ * automatically.
+ *
+ * However, this doesn’t apply to RIPAS_DEV, because we currently
+ * lack a user-space API to call realm_dev_mem_unmap() in response to a
+ * memory fault exit. Therefore, the unmap must happen explicitly before
+ * the RIPAS change.
+ */
+ if (ripas == RMI_EMPTY)
+ realm_unmap_private_range(kvm, start, end, false);
+ ret = ripas_change(kvm, vcpu, start, end, RIPAS_SET, top_ipa);
return ret;
}
@@ -1301,6 +1341,27 @@ static int rmi_rtt_dev_map(unsigned long rd_phys, unsigned long vdev_phys,
return 0;
}
+static int rmi_rtt_dev_unmap(unsigned long rd_phys,
+ unsigned long base, unsigned long top,
+ unsigned long *out_ipa, unsigned long *out_desc,
+ unsigned long *rmi_ret)
+{
+ unsigned long flags = RMI_ADDR_TYPE_SINGLE;
+ struct rmi_sro_state *sro __free(sro) =
+ rmi_sro_init(SMC_RMI_RTT_DEV_UNMAP, rd_phys, base, top, flags, NULL);
+ if (!sro)
+ return -ENOMEM;
+
+ *rmi_ret = rmi_sro_execute(sro);
+ if (*rmi_ret)
+ return 0;
+
+ *out_ipa = sro->regs.a1;
+ *out_desc = sro->regs.a2;
+
+ return 0;
+}
+
static int rmi_rtt_dev_validate(unsigned long rd_phys, unsigned long rec_phys,
unsigned long base, unsigned long top, unsigned long *out_top,
unsigned long *rmi_ret)
@@ -1401,9 +1462,12 @@ int realm_dev_mem_map(struct kvm *kvm, unsigned long pdev_phys,
unsigned long end_ipa, unsigned long start_pa)
{
int ret;
+ unsigned long rmi_ret;
unsigned long top_ipa;
unsigned long base_ipa = start_ipa;
+ struct realm *realm = &kvm->arch.realm;
struct kvm_s2_mmu *mmu = &kvm->arch.mmu;
+ phys_addr_t rd_phys = virt_to_phys(realm->rd);
struct kvm_mmu_memory_cache cache = { .gfp_zero = __GFP_ZERO };
do {
@@ -1431,6 +1495,19 @@ int realm_dev_mem_map(struct kvm *kvm, unsigned long pdev_phys,
for (start_ipa = ALIGN(base_ipa, RMM_L2_BLOCK_SIZE);
((start_ipa + RMM_L2_BLOCK_SIZE) < end_ipa); start_ipa += RMM_L2_BLOCK_SIZE)
fold_rtt(&kvm->arch.realm, start_ipa, RMM_RTT_BLOCK_LEVEL);
+ } else {
+ /* unmap the partial mapping. [base_ipa, start_ipa) */
+ while (start_ipa > base_ipa) {
+ unsigned long out_ipa;
+ unsigned long out_range;
+
+ ret = rmi_rtt_dev_unmap(rd_phys, base_ipa, start_ipa,
+ &out_ipa, &out_range, &rmi_ret);
+ if (ret || (rmi_ret != RMI_SUCCESS))
+ break;
+ WARN_ON(undelegate_range_desc(out_range));
+ base_ipa = out_ipa;
+ }
}
return ret;
--
2.43.0
^ permalink raw reply related [flat|nested] 40+ messages in thread* [RFC PATCH v4 15/16] coco: host: arm64: Transition vdevs to TDISP RUN state
2026-04-27 8:53 [RFC PATCH v4 00/16] coco/TSM: Implement host-side support for Arm CCA TDISP setup Aneesh Kumar K.V (Arm)
` (13 preceding siblings ...)
2026-04-27 8:53 ` [RFC PATCH v4 14/16] KVM: arm64: Unmap device mappings when a private granule is destroyed Aneesh Kumar K.V (Arm)
@ 2026-04-27 8:53 ` Aneesh Kumar K.V (Arm)
2026-04-27 8:53 ` [RFC PATCH v4 16/16] KVM: arm64: CCA: enable DA in realm create parameters Aneesh Kumar K.V (Arm)
2026-08-31 18:08 ` [RFC PATCH v4 00/16] coco/TSM: Implement host-side support for Arm CCA TDISP setup Jason Gunthorpe
16 siblings, 0 replies; 40+ messages in thread
From: Aneesh Kumar K.V (Arm) @ 2026-04-27 8:53 UTC (permalink / raw)
To: linux-coco, kvmarm, linux-arm-kernel, linux-kernel
Cc: Aneesh Kumar K.V (Arm), Alexey Kardashevskiy, Catalin Marinas,
Dan Williams, Jason Gunthorpe, Joerg Roedel, Jonathan Cameron,
Marc Zyngier, Nicolin Chen, Pranjal Shrivastava, Robin Murphy,
Samuel Ortiz, Steven Price, Suzuki K Poulose, Will Deacon,
Xu Yilun
Add host-side support for guest requests that move a vdev into the TDISP
RUN state.
Introduce the RMI helper for VDEV_START and a matching guest request
payload for VDEV_SET_TDI_STATE. In the host CCA TSM request handler, accept
only RHI_DA_TDI_CONFIG_RUN on the state-change path and invoke a new
cca_vdev_device_start() helper.
The start helper issues RMI_VDEV_START for the bound pdev/vdev pair and
then waits until firmware reports the vdev in the RMI_VDEV_STARTED state
before returning to the caller.
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
arch/arm64/include/asm/rmi_cmds.h | 11 +++++++++++
arch/arm64/include/uapi/asm/rmi-da.h | 6 ++++++
drivers/virt/coco/arm-cca-host/arm-cca.c | 15 +++++++++++++++
drivers/virt/coco/arm-cca-host/rmi-da.c | 22 ++++++++++++++++++++++
drivers/virt/coco/arm-cca-host/rmi-da.h | 1 +
5 files changed, 55 insertions(+)
diff --git a/arch/arm64/include/asm/rmi_cmds.h b/arch/arm64/include/asm/rmi_cmds.h
index 350fd9bc93a4..19eba97a6c7b 100644
--- a/arch/arm64/include/asm/rmi_cmds.h
+++ b/arch/arm64/include/asm/rmi_cmds.h
@@ -993,4 +993,15 @@ rmi_vdev_get_device_measurements(unsigned long rd, unsigned long pdev_phys,
return res.a0;
}
+
+static inline unsigned long rmi_vdev_start(unsigned long rd, unsigned long pdev_phys,
+ unsigned long vdev_phys)
+{
+ struct arm_smccc_res res;
+
+ arm_smccc_1_1_invoke(SMC_RMI_VDEV_START, rd, pdev_phys, vdev_phys, &res);
+
+ return res.a0;
+}
+
#endif /* __ASM_RMI_CMDS_H */
diff --git a/arch/arm64/include/uapi/asm/rmi-da.h b/arch/arm64/include/uapi/asm/rmi-da.h
index 572afb4095f2..c0cfcadfae47 100644
--- a/arch/arm64/include/uapi/asm/rmi-da.h
+++ b/arch/arm64/include/uapi/asm/rmi-da.h
@@ -38,4 +38,10 @@ struct arm64_vdev_device_memmap_guest_req {
};
#define __REC_DA_VDEV_MAP 0x5
+struct arm64_vdev_set_tdi_state_guest_req {
+ __u32 req_type;
+ __u32 tdi_state;
+};
+#define __RHI_DA_VDEV_SET_TDI_STATE 0x6
+
#endif
diff --git a/drivers/virt/coco/arm-cca-host/arm-cca.c b/drivers/virt/coco/arm-cca-host/arm-cca.c
index 66e0acadf743..3a682352fb68 100644
--- a/drivers/virt/coco/arm-cca-host/arm-cca.c
+++ b/drivers/virt/coco/arm-cca-host/arm-cca.c
@@ -608,6 +608,21 @@ static ssize_t cca_tsm_guest_req(struct pci_tdi *tdi, enum pci_tsm_req_scope sco
req_obj.gpa_top,
req_obj.pa_base);
}
+ case __RHI_DA_VDEV_SET_TDI_STATE:
+ {
+ struct arm64_vdev_set_tdi_state_guest_req req_obj;
+
+ if (req_len != sizeof(req_obj))
+ return -EINVAL;
+
+ if (copy_from_user((void *)&req_obj, req.user, req_len))
+ return -EFAULT;
+
+ if (req_obj.tdi_state != RHI_DA_TDI_CONFIG_RUN)
+ return -EINVAL;
+
+ return cca_vdev_device_start(pdev);
+ }
default:
return -EINVAL;
}
diff --git a/drivers/virt/coco/arm-cca-host/rmi-da.c b/drivers/virt/coco/arm-cca-host/rmi-da.c
index 543c40fb1160..60b750c961ea 100644
--- a/drivers/virt/coco/arm-cca-host/rmi-da.c
+++ b/drivers/virt/coco/arm-cca-host/rmi-da.c
@@ -1398,3 +1398,25 @@ int cca_vdev_device_map(struct pci_dev *pdev, unsigned long gpa_base,
return realm_dev_mem_map(kvm, rmm_pdev_phys, rmm_vdev_phys,
gpa_base, gpa_top, pa_base);
}
+
+int cca_vdev_device_start(struct pci_dev *pdev)
+{
+ phys_addr_t rmm_pdev_phys;
+ phys_addr_t rmm_vdev_phys;
+ struct cca_host_pdev_dsc *pdev_dsc;
+ struct cca_host_tdi *host_tdi;
+ struct realm *realm;
+ phys_addr_t rd_phys;
+
+ host_tdi = to_cca_host_tdi(pdev);
+ rmm_vdev_phys = virt_to_phys(host_tdi->rmm_vdev);
+ realm = &host_tdi->tdi.kvm->arch.realm;
+ rd_phys = virt_to_phys(realm->rd);
+
+ pdev_dsc = to_cca_pdev_dsc(pdev->tsm->dsm_dev);
+ rmm_pdev_phys = virt_to_phys(pdev_dsc->rmm_pdev);
+
+ if (rmi_vdev_start(rd_phys, rmm_pdev_phys, rmm_vdev_phys))
+ return -ENXIO;
+ return submit_vdev_state_transition_work(pdev, RMI_VDEV_STARTED);
+}
diff --git a/drivers/virt/coco/arm-cca-host/rmi-da.h b/drivers/virt/coco/arm-cca-host/rmi-da.h
index 3dfb6b3cc2ef..3082166038c3 100644
--- a/drivers/virt/coco/arm-cca-host/rmi-da.h
+++ b/drivers/virt/coco/arm-cca-host/rmi-da.h
@@ -252,5 +252,6 @@ int cca_vdev_update_interface_report(struct pci_dev *pdev);
int cca_vdev_update_device_measurements(struct pci_dev *pdev, unsigned long flags, u8 *nonce);
int cca_vdev_device_map(struct pci_dev *pdev, unsigned long gpa_base,
unsigned long gpa_top, unsigned long pa_base);
+int cca_vdev_device_start(struct pci_dev *pdev);
#endif
--
2.43.0
^ permalink raw reply related [flat|nested] 40+ messages in thread* [RFC PATCH v4 16/16] KVM: arm64: CCA: enable DA in realm create parameters
2026-04-27 8:53 [RFC PATCH v4 00/16] coco/TSM: Implement host-side support for Arm CCA TDISP setup Aneesh Kumar K.V (Arm)
` (14 preceding siblings ...)
2026-04-27 8:53 ` [RFC PATCH v4 15/16] coco: host: arm64: Transition vdevs to TDISP RUN state Aneesh Kumar K.V (Arm)
@ 2026-04-27 8:53 ` Aneesh Kumar K.V (Arm)
2026-08-31 18:08 ` [RFC PATCH v4 00/16] coco/TSM: Implement host-side support for Arm CCA TDISP setup Jason Gunthorpe
16 siblings, 0 replies; 40+ messages in thread
From: Aneesh Kumar K.V (Arm) @ 2026-04-27 8:53 UTC (permalink / raw)
To: linux-coco, kvmarm, linux-arm-kernel, linux-kernel
Cc: Aneesh Kumar K.V (Arm), Alexey Kardashevskiy, Catalin Marinas,
Dan Williams, Jason Gunthorpe, Joerg Roedel, Jonathan Cameron,
Marc Zyngier, Nicolin Chen, Pranjal Shrivastava, Robin Murphy,
Samuel Ortiz, Steven Price, Suzuki K Poulose, Will Deacon,
Xu Yilun
Now that we have all the required steps for DA in-place, enable
DA while creating realm.
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
arch/arm64/include/asm/rmi_smc.h | 1 +
arch/arm64/kvm/rmi.c | 3 +++
2 files changed, 4 insertions(+)
diff --git a/arch/arm64/include/asm/rmi_smc.h b/arch/arm64/include/asm/rmi_smc.h
index f3ad545d68b7..c02e2f087b1c 100644
--- a/arch/arm64/include/asm/rmi_smc.h
+++ b/arch/arm64/include/asm/rmi_smc.h
@@ -268,6 +268,7 @@ struct rmm_config {
#define RMI_REALM_PARAM_FLAG_LPA2 BIT(0)
#define RMI_REALM_PARAM_FLAG_SVE BIT(1)
#define RMI_REALM_PARAM_FLAG_PMU BIT(2)
+#define RMI_REALM_PARAM_FLAG_DA BIT(3)
struct realm_params {
union { /* 0x0 */
diff --git a/arch/arm64/kvm/rmi.c b/arch/arm64/kvm/rmi.c
index cc9e045dcae9..e041c4caee79 100644
--- a/arch/arm64/kvm/rmi.c
+++ b/arch/arm64/kvm/rmi.c
@@ -691,6 +691,9 @@ static int realm_create_rd(struct kvm *kvm)
if (r)
goto out_undelegate_tables;
+ /* For now default enable DA */
+ if (rmm_has_reg2_feature(RMI_FEATURE_REGISTER_2_DA))
+ params->flags |= RMI_REALM_PARAM_FLAG_DA;
params_phys = virt_to_phys(params);
if (rmi_realm_create(rd_phys, params_phys)) {
--
2.43.0
^ permalink raw reply related [flat|nested] 40+ messages in thread* Re: [RFC PATCH v4 00/16] coco/TSM: Implement host-side support for Arm CCA TDISP setup
2026-04-27 8:53 [RFC PATCH v4 00/16] coco/TSM: Implement host-side support for Arm CCA TDISP setup Aneesh Kumar K.V (Arm)
` (15 preceding siblings ...)
2026-04-27 8:53 ` [RFC PATCH v4 16/16] KVM: arm64: CCA: enable DA in realm create parameters Aneesh Kumar K.V (Arm)
@ 2026-08-31 18:08 ` Jason Gunthorpe
2026-09-01 12:44 ` Aneesh Kumar K.V
16 siblings, 1 reply; 40+ messages in thread
From: Jason Gunthorpe @ 2026-08-31 18:08 UTC (permalink / raw)
To: Aneesh Kumar K.V (Arm)
Cc: linux-coco, kvmarm, linux-arm-kernel, linux-kernel,
Alexey Kardashevskiy, Catalin Marinas, Dan Williams, Joerg Roedel,
Jonathan Cameron, Marc Zyngier, Nicolin Chen, Pranjal Shrivastava,
Robin Murphy, Samuel Ortiz, Steven Price, Suzuki K Poulose,
Will Deacon, Xu Yilun
On Mon, Apr 27, 2026 at 02:23:28PM +0530, Aneesh Kumar K.V (Arm) wrote:
>
> This patch series implements the host-side changes needed for end-to-end
> Arm CCA TDISP setup. It adds the RMI/RHI plumbing required to create and
> manage Realm vdev objects, service device-attestation object requests, and
> complete the KVM/RMM flows needed for device run-time transitions.
So this is enough for the realm to see a physical PCI device inside it
without any vSMMU inside the realm?
It is really weird to see a viommu for a case where there is no
viommu..
It doesn't do anything except manage memory for the RMM..
It feels wrong that the arm-cca-guest module is calling
RMI_PDEV_CREATE and RMI_VDEV_CREATE while the viommu is allocating STE
memory for the PDEV. That doesn't make alot of sense? The STE is
needed before VDEV_CREATE, right? So why not place it there in the
flow?
If that's changed then the only thing the viommu does is manage the
PSMMU, which again, seems like something VDEV_CREATE needs, so why is
a viommu involved at all?
The smmu driver involvment would be much smaller if it was only the
interrupt routing and some helper to return the psmmu addr for a
struct device that the arm-cc-guest module can call to manage the
psmmu?
But I'm also sitting here scratching my head a bit, did the tsm_ops
design go the wrong way? Should we have run more of that through a
viommu instead of tsm_ops? Bind is sort of an illogical operation
without a viommu, even if it is a nop viommu.
I suppose it depends what it looks like when a real vsmmu is
created.. That probably needs a special viommu object, and do we get
into order problems if the lifecylce becomes split to tsm and viommu?
> RHI v1.0 BET1 specification [5].
>
> At a high level, the series adds support for:
> - host-side vdev communication and lifecycle management
> - host handling of RHI DA object read/size requests
> - host-side fetching and caching of interface reports and measurements
> - KVM handling of vdev request/complete exits
> - KVM handling of map/validation exits and teardown on granule destroy
> - vdev transition to TDISP RUN state
> - enabling DA in Realm create parameters
>
> The series builds upon the TSM framework patches posted at [2] and depends on
> the KVM CCA patchset [3]. A git repository containing all related changes is
> available at [4]. kvmtool repo is at [6]
It looks like it also needs the series that adds bind to iommufd too
Jason
^ permalink raw reply [flat|nested] 40+ messages in thread* Re: [RFC PATCH v4 00/16] coco/TSM: Implement host-side support for Arm CCA TDISP setup
2026-08-31 18:08 ` [RFC PATCH v4 00/16] coco/TSM: Implement host-side support for Arm CCA TDISP setup Jason Gunthorpe
@ 2026-09-01 12:44 ` Aneesh Kumar K.V
2026-09-01 13:07 ` Jason Gunthorpe
0 siblings, 1 reply; 40+ messages in thread
From: Aneesh Kumar K.V @ 2026-09-01 12:44 UTC (permalink / raw)
To: Jason Gunthorpe
Cc: linux-coco, kvmarm, linux-arm-kernel, linux-kernel,
Alexey Kardashevskiy, Catalin Marinas, Dan Williams, Joerg Roedel,
Jonathan Cameron, Marc Zyngier, Nicolin Chen, Pranjal Shrivastava,
Robin Murphy, Samuel Ortiz, Steven Price, Suzuki K Poulose,
Will Deacon, Xu Yilun
Jason Gunthorpe <jgg@nvidia.com> writes:
> On Mon, Apr 27, 2026 at 02:23:28PM +0530, Aneesh Kumar K.V (Arm) wrote:
>>
>> This patch series implements the host-side changes needed for end-to-end
>> Arm CCA TDISP setup. It adds the RMI/RHI plumbing required to create and
>> manage Realm vdev objects, service device-attestation object requests, and
>> complete the KVM/RMM flows needed for device run-time transitions.
>
> So this is enough for the realm to see a physical PCI device inside it
> without any vSMMU inside the realm?
>
There are multiple series, and this is the final one:
1. arm-cca-guest series
2. arm-cca-host IDE setup series
3. iommufd interface for TSM operations
4. arm-cca-host TDISP support (this series)
>
> It is really weird to see a viommu for a case where there is no
> viommu..
>
We ended up with a viommu despite having no stage-1 SMMU or vSMMU
because it held the KVM reference needed by item (3) above [1]. With the
recent changes in that series [2], we now inherit the KVM details from
VFIO through iommufd_device_bind. I can possibly look at using the idev
for this instead.
[1] https://lore.kernel.org/all/20260309111704.2330479-2-aneesh.kumar@kernel.org
[2] https://lore.kernel.org/all/20260525154816.1029642-1-aneesh.kumar@kernel.org
> It doesn't do anything except manage memory for the RMM..
>
> It feels wrong that the arm-cca-guest module is calling
> RMI_PDEV_CREATE and RMI_VDEV_CREATE while the viommu is allocating STE
> memory for the PDEV. That doesn't make alot of sense? The STE is
> needed before VDEV_CREATE, right? So why not place it there in the
> flow?
>
> If that's changed then the only thing the viommu does is manage the
> PSMMU, which again, seems like something VDEV_CREATE needs, so why is
> a viommu involved at all?
>
We do not have a separate vdev-create operation; instead, we have
tsm_bind. The required iommufd objects (idev/viommu) are set up before
tsm_bind.
Currently, the primary reason for having a viommu is to obtain the KVM
reference. Now that the KVM details are inherited from VFIO through
iommufd_device_bind(), I can look at using the idev instead and dropping
the viommu requirement.
>
> The smmu driver involvment would be much smaller if it was only the
> interrupt routing and some helper to return the psmmu addr for a
> struct device that the arm-cc-guest module can call to manage the
> psmmu?
>
I designed this so that the PSMMU details are managed by the arm-smmu
driver, with minimal involvement from arm-cca-host. If the arm-smmu
driver does not set up the PSMMU correctly, tsm_bind, which is handled
by arm-cca-host, can fail.
> But I'm also sitting here scratching my head a bit, did the tsm_ops
> design go the wrong way? Should we have run more of that through a
> viommu instead of tsm_ops? Bind is sort of an illogical operation
> without a viommu, even if it is a nop viommu.
>
> I suppose it depends what it looks like when a real vsmmu is
> created.. That probably needs a special viommu object, and do we get
> into order problems if the lifecylce becomes split to tsm and viommu?
>
>> RHI v1.0 BET1 specification [5].
>>
>> At a high level, the series adds support for:
>> - host-side vdev communication and lifecycle management
>> - host handling of RHI DA object read/size requests
>> - host-side fetching and caching of interface reports and measurements
>> - KVM handling of vdev request/complete exits
>> - KVM handling of map/validation exits and teardown on granule destroy
>> - vdev transition to TDISP RUN state
>> - enabling DA in Realm create parameters
>>
>> The series builds upon the TSM framework patches posted at [2] and depends on
>> the KVM CCA patchset [3]. A git repository containing all related changes is
>> available at [4]. kvmtool repo is at [6]
>
> It looks like it also needs the series that adds bind to iommufd too
>
> Jason
-aneesh
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [RFC PATCH v4 00/16] coco/TSM: Implement host-side support for Arm CCA TDISP setup
2026-09-01 12:44 ` Aneesh Kumar K.V
@ 2026-09-01 13:07 ` Jason Gunthorpe
0 siblings, 0 replies; 40+ messages in thread
From: Jason Gunthorpe @ 2026-09-01 13:07 UTC (permalink / raw)
To: Aneesh Kumar K.V
Cc: linux-coco, kvmarm, linux-arm-kernel, linux-kernel,
Alexey Kardashevskiy, Catalin Marinas, Dan Williams, Joerg Roedel,
Jonathan Cameron, Marc Zyngier, Nicolin Chen, Pranjal Shrivastava,
Robin Murphy, Samuel Ortiz, Steven Price, Suzuki K Poulose,
Will Deacon, Xu Yilun
On Tue, Sep 01, 2026 at 06:14:19PM +0530, Aneesh Kumar K.V wrote:
> > It is really weird to see a viommu for a case where there is no
> > viommu..
>
> We ended up with a viommu despite having no stage-1 SMMU or vSMMU
> because it held the KVM reference needed by item (3) above [1]. With the
> recent changes in that series [2], we now inherit the KVM details from
> VFIO through iommufd_device_bind. I can possibly look at using the idev
> for this instead.
Okay, that's a good reason, but the whole thing just doesn't seem
split right. There is code in arm-cca-guest and code in the viommu
that are all working on the same thing. I don't think it makes logical
sense split like this.
The viommu should be creating the vdev at the logical point in its
flow, when the it creates its vdevice. It shouldn't be disjoint and
separated.
> > It doesn't do anything except manage memory for the RMM..
> >
> > It feels wrong that the arm-cca-guest module is calling
> > RMI_PDEV_CREATE and RMI_VDEV_CREATE while the viommu is allocating STE
> > memory for the PDEV. That doesn't make alot of sense? The STE is
> > needed before VDEV_CREATE, right? So why not place it there in the
> > flow?
> >
> > If that's changed then the only thing the viommu does is manage the
> > PSMMU, which again, seems like something VDEV_CREATE needs, so why is
> > a viommu involved at all?
>
> We do not have a separate vdev-create operation; instead, we have
> tsm_bind. The required iommufd objects (idev/viommu) are set up before
> tsm_bind.
You end up with *two* "vdev creation" operations, one through the new
bind iommufd call and another through the actual vdevice create
iommufd call, and they go do different modules. That seems like a
messy flow.
If we are commited to a viommu then lets use the viommu for everything
and have less code in arm-cca-guest. Get rid of bind as a tsm
operation. Have the smmu driver do it when it creates the vdevice.
You can't call RMI_VDEV_CREATE outside the viommu anyhow as the
vdev_id comes from the iommufd vdevice object's vsid.
This is probably going to work better long term anyhow as we can
construct the vsmmu with exactly the same API surface, and a non-vsmmu
case is really just some special variation of the viommu object that
doesn't create do a RMI_VSMMU_CREATE.
Also, is it really OK to setup the psmmu after creating the pdev? RMM
spec explicitly says that order is allowed? It is a litle bit counter
intuitive.
Jason
^ permalink raw reply [flat|nested] 40+ messages in thread