Skip to contentSkip to conversation

coco/TSM: Implement host-side support for Arm CCA TDISP setup

RFC PATCH In review Aneesh Kumar K.V (Arm) 7 participants 60 messages Apr 27 – Sep 18

Subsystems (7)
Documentation KVM arm64 IOMMU +3 more subsystems
lore
Share & tools
lore
#0 lore raw

v4 cover coco/TSM: Implement host-side support for Arm CCA TDISP setup

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.

The series is based on the RMM 2.0bet1 specification [1] and the 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]

Previous posting:
Changes from v3:
https://lore.kernel.org/all/[email protected]
* updated the patches to follow the RMM 2.0bet1 specification
* moved vdev request-exit handling to the last patch in the series. This is expected to be dropped once the corresponding spec update lands
* dropped the vdev and pdev arguments from rmi_rtt_dev_validate(). The spec update for that change is still pending. The validation call is now made from the REC enter path
* added a response field to KVM_EXIT_ARM64_TIO so the VMM can pass the ioctl return status back to the exit handler
* dropped vcpu_fd from arm64_vdev_device_memmap_guest_req. Once vdev request handling is removed from the series, the ioctl path will no longer need vcpu_fd
* reworked the host-side vdev lifecycle to better match the RMM 2.0bet1 flow,
* updated the vdev flows to match the revised interfaces: populate MMIO BAR ranges in rmi_vdev_create(), rename the interface-report and measurement update commands, and drop vcpu_fd from the guest MMIO map request path

Changes from v1:
* rebase to latest kernel and core TSM changes
* address review feedback

rfc-v1: https://lore.kernel.org/all/[email protected]
There is no rfc-v2 posting. This series is marked rfc-v3 to stay aligned with the rest of the CCA patchsets that are being posted as v3.

[1] https://developer.arm.com/documentation/den0137/2-0bet1/
[2] https://lore.kernel.org/all/[email protected]
[3] https://lore.kernel.org/all/[email protected]
[4] https://gitlab.arm.com/linux-arm/linux-cca.git cca/topics/cca-tdisp-upstream-rfc-v4
[5] https://developer.arm.com/documentation/den0148/latest/
[6] https://gitlab.arm.com/linux-arm/kvmtool-cca.git cca/topics/cca-tdisp-upstream-rfc-v4

Cc Alexey Kardashevskiy <[email protected]>
Cc Catalin Marinas <[email protected]>
Cc Dan Williams <[email protected]>
Cc Jason Gunthorpe <[email protected]>
Cc Joerg Roedel <[email protected]>
Cc Jonathan Cameron <[email protected]>
Cc Marc Zyngier <[email protected]>
Cc Nicolin Chen <[email protected]>
Cc Pranjal Shrivastava <[email protected]>
Cc Robin Murphy <[email protected]>
Cc Samuel Ortiz <[email protected]>
Cc Steven Price <[email protected]>
Cc Suzuki K Poulose <[email protected]>
Cc Will Deacon <[email protected]>
Cc Xu Yilun <[email protected]>

Aneesh Kumar K.V (Arm) (16):
iommu/arm-smmu-v3: Discover RME support and realm IRQ topology iommu/arm-smmu-v3: Save the programmed MSI message in msi_desc iommu/arm-smmu-v3: Add initial pSMMU realm viommu plumbing iommu/arm-smmu-v3: Track realm pSMMU users with refcount_t
coco: host: arm64: Add support for virtual device communication
coco: host: arm64: Add support for RMM vdev objects
coco: host: arm64: Add pdev stream key refresh and purge helpers
coco: host: arm64: Add helpers to unlock and destroy RMM vdev
coco: host: arm64: Add support for da object read RHI handling
coco: host: arm64: Add helper for cached object fetches
coco: host: arm64: Fetch interface report via RMI
coco: host: arm64: Fetch device measurements via RMI
coco: host: KVM: arm64: Handle vdev validate-mapping exits
KVM: arm64: Unmap device mappings when a private granule is destroyed
coco: host: arm64: Transition vdevs to TDISP RUN state
KVM: arm64: CCA: enable DA in realm create parameters

 Documentation/virt/kvm/api.rst                |  20 +
 arch/arm64/include/asm/kvm_rmi.h              |   4 +
 arch/arm64/include/asm/rmi_cmds.h             | 193 ++++++
 arch/arm64/include/asm/rmi_smc.h              |  98 ++-
 arch/arm64/include/uapi/asm/rmi-da.h          |  47 ++
 arch/arm64/kernel/rmi.c                       |  51 ++
 arch/arm64/kvm/rmi-exit.c                     |  37 ++
 arch/arm64/kvm/rmi.c                          | 279 ++++++++-
 drivers/iommu/arm/arm-smmu-v3/Makefile        |   2 +-
 .../arm/arm-smmu-v3/arm-smmu-v3-iommufd.c     |   7 +
 .../iommu/arm/arm-smmu-v3/arm-smmu-v3-realm.c | 297 +++++++++
 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c   |  86 ++-
 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h   |  16 +
 drivers/virt/coco/arm-cca-host/arm-cca.c      | 188 +++++-
 drivers/virt/coco/arm-cca-host/rmi-da.c       | 569 +++++++++++++++++-
 drivers/virt/coco/arm-cca-host/rmi-da.h       |  40 ++
 include/uapi/linux/iommufd.h                  |   1 +
 include/uapi/linux/kvm.h                      |  11 +
 18 files changed, 1929 insertions(+), 17 deletions(-)
 create mode 100644 arch/arm64/include/uapi/asm/rmi-da.h
 create mode 100644 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-realm.c
Signature · 1 line
2.43.0
#1 lore raw

RFC PATCH v4 1/16 iommu/arm-smmu-v3: Discover RME support and realm IRQ topology

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.

---
 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
arch/arm64/include/asm/rmi_cmds.h +52 -0
index c82d4d9cbc06..75eb59d4fa84 100644
@@ -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(&regs, &regs);
+
+	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 */
arch/arm64/include/asm/rmi_smc.h +31 -1
index 7b16f1540a0e..be1b1e95a937 100644
@@ -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 */
drivers/iommu/arm/arm-smmu-v3/Makefile +1 -1
index 493a659cc66b..23bd794ebeda 100644
@@ -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
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-realm.c new file +124 -0

124 lines not on this page · raw message

drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +79 -2

138 lines not on this page · raw message

drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h +10 -0
index 3c6d65d36164..6680516b571b 100644
@@ -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 */
Signature · 1 line
2.43.0
#2 lore raw

Re: [RFC PATCH v4 01/16] iommu/arm-smmu-v3: Discover RME support and realm IRQ topology

Quoted 17 lines · Aneesh Kumar K.V (Arm)

+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-realm.c

drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-realm.c new file +15 -0
@@ -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)

[...]

drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-realm.c line 37

Quoted 6 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-realm.c new file +6 -0
+	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)...

Quoted 2 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-realm.c new file +2 -0
+		if (rmi_psmmu_irq_notify(smmu->base_phys,
+					 notify_flags, &event)) {

[...]

Review

Quoted 1 line
(quoted diff) +1 -0
+			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.

drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-realm.c line 88

Quoted 11 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-realm.c new file +11 -0
+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?

drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h struct arm_smmu_device { · line 792

Quoted 7 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h +3 -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;

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

#3 lore raw

Re: [RFC PATCH v4 01/16] iommu/arm-smmu-v3: Discover RME support and realm IRQ topology

Quoted 35 lines · Nicolin Chen
Quoted 17 lines · Aneesh Kumar K.V (Arm)

+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-realm.c

drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-realm.c new file +15 -0
@@ -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)

[...]

drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-realm.c line 37

Quoted 6 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-realm.c new file +6 -0
+	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)...

Quoted 2 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-realm.c new file +2 -0
+		if (rmi_psmmu_irq_notify(smmu->base_phys,
+					 notify_flags, &event)) {

[...]

Review

Quoted 1 line
(quoted diff) +1 -0
+			rmi_psmmu_event_consume(smmu->base_phys, notify_flags);

.. and passed to RMI_PSMMU_IRQ_NOTIFY and RMI_PSMMU_EVENT_CONSUME.

Quoted 6 lines

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);
Quoted 22 lines

drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-realm.c line 88

Quoted 12 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-realm.c new file +11 -0
+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:

drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +5 -0
+		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;
Quoted 3 lines

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);
+}

Quoted 12 lines

drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h struct arm_smmu_device { · line 792

Quoted 8 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h +3 -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;

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

#4 lore raw

Re: [RFC PATCH v4 01/16] iommu/arm-smmu-v3: Discover RME support and realm IRQ topology

Quoted 17 lines · Aneesh Kumar K.V
(quoted diff) +17 -0
+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]);

Review

Quoted 10 lines
(quoted diff) +10 -0
+		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

#5 lore raw

RFC PATCH v4 2/16 iommu/arm-smmu-v3: Save the programmed MSI message in msi_desc

In reply to Aneesh Kumar K.V (Arm)

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().

---
 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 3 +++
 1 file changed, 3 insertions(+)
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +3 -0
index d5b9ab95beea..17fd99887aab 100644
@@ -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;
Signature · 1 line
2.43.0
#6 lore raw

RFC PATCH v4 3/16 iommu/arm-smmu-v3: Add initial pSMMU realm viommu plumbing

In reply to Aneesh Kumar K.V (Arm)

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.

---
 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(+)
arch/arm64/include/asm/rmi_cmds.h +7 -0
index 75eb59d4fa84..659d68ad5f1d 100644
@@ -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 */
arch/arm64/include/asm/rmi_smc.h +18 -0
index be1b1e95a937..5b540d25914e 100644
@@ -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 */
arch/arm64/kernel/rmi.c +39 -0
index da4707981548..cc4050db5a6a 100644
@@ -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;
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c +7 -0
index ddae0b07c76b..c98e91b3ca13 100644
@@ -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);
 }
 
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-realm.c +148 -0

157 lines not on this page · raw message

drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +1 -0
index 17fd99887aab..1e3d4d682e32 100644
@@ -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);
 
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h +4 -0
index 6680516b571b..d528b3212d38 100644
@@ -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
include/uapi/linux/iommufd.h +1 -0
index 47213663c0c1..74afc9967c3e 100644
@@ -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,
 };
 
 /**
Signature · 1 line
2.43.0
#7 lore raw

Re: [RFC PATCH v4 03/16] iommu/arm-smmu-v3: Add initial pSMMU realm viommu plumbing

drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-realm.c void arm_smmu_setup_realm_irqs(struct arm_smmu_device *smmu) · line 196

Quoted 4 lines · Aneesh Kumar K.V (Arm)
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-realm.c +4 -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,

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?

Quoted 7 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-realm.c +7 -0
+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];

drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-realm.c void arm_smmu_setup_realm_irqs(struct arm_smmu_device *smmu) · line 176

Quoted 10 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-realm.c +10 -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..

Quoted 3 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-realm.c +3 -0
+int arm_realm_smmu_v3_init(struct iommufd_viommu *viommu,
+			   const struct iommu_user_data *user_data)
+{

[...]

drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-realm.c void arm_smmu_setup_realm_irqs(struct arm_smmu_device *smmu) · line 257

Quoted 3 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-realm.c +3 -0
+psmmu_activate:
+	ret = rmi_psmmu_activate(smmu->base_phys, virt_to_phys(params),
+				 &rmi_ret);

.. and the "viommu" is also for PSMMU activation...

include/uapi/linux/iommufd.h enum iommu_viommu_type { · line 1058

Quoted 6 lines

+++ b/include/uapi/linux/iommufd.h

include/uapi/linux/iommufd.h +1 -0
@@ -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

#8 lore raw

Re: [RFC PATCH v4 03/16] iommu/arm-smmu-v3: Add initial pSMMU realm viommu plumbing

Quoted 74 lines · Nicolin Chen

drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-realm.c void arm_smmu_setup_realm_irqs(struct arm_smmu_device *smmu) · line 196

Quoted 4 lines · Aneesh Kumar K.V (Arm)
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-realm.c +4 -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,

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?

Quoted 7 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-realm.c +7 -0
+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];

drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-realm.c void arm_smmu_setup_realm_irqs(struct arm_smmu_device *smmu) · line 176

Quoted 10 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-realm.c +10 -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..

Quoted 3 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-realm.c +3 -0
+int arm_realm_smmu_v3_init(struct iommufd_viommu *viommu,
+			   const struct iommu_user_data *user_data)
+{

[...]

drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-realm.c void arm_smmu_setup_realm_irqs(struct arm_smmu_device *smmu) · line 257

Quoted 3 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-realm.c +3 -0
+psmmu_activate:
+	ret = rmi_psmmu_activate(smmu->base_phys, virt_to_phys(params),
+				 &rmi_ret);

.. and the "viommu" is also for PSMMU activation...

include/uapi/linux/iommufd.h enum iommu_viommu_type { · line 1058

Quoted 6 lines

+++ b/include/uapi/linux/iommufd.h

include/uapi/linux/iommufd.h +1 -0
@@ -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.

Quoted 4 lines

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.

Quoted 5 lines

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

#9 lore raw

Re: [RFC PATCH v4 03/16] iommu/arm-smmu-v3: Add initial pSMMU realm viommu plumbing

Quoted 3 lines · Aneesh Kumar K.V
Quoted 1 line · Nicolin Chen

On Mon, Apr 27, 2026 at 02:23:31PM +0530, Aneesh Kumar K.V (Arm) wrote:

...

Quoted 32 lines
Quoted 27 lines

.. 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.

Quoted 4 lines

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?

Quoted 8 lines

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,
};

/**

#10 lore raw

Re: [RFC PATCH v4 03/16] iommu/arm-smmu-v3: Add initial pSMMU realm viommu plumbing

drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c int arm_vsmmu_init(struct iommufd_viommu *viommu, · line 465

Quoted 7 lines · Aneesh Kumar K.V
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c +3 -0
@@ -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

#11 lore raw

Re: [RFC PATCH v4 03/16] iommu/arm-smmu-v3: Add initial pSMMU realm viommu plumbing

Quoted 13 lines · Jason Gunthorpe

drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c int arm_vsmmu_init(struct iommufd_viommu *viommu, · line 465

Quoted 7 lines · Aneesh Kumar K.V
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c +3 -0
@@ -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?

Quoted 6 lines

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

#12 lore raw

Re: [RFC PATCH v4 03/16] iommu/arm-smmu-v3: Add initial pSMMU realm viommu plumbing

Quoted 21 lines · Nicolin Chen
Quoted 13 lines · Jason Gunthorpe

drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c int arm_vsmmu_init(struct iommufd_viommu *viommu, · line 465

Quoted 7 lines · Aneesh Kumar K.V
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c +3 -0
@@ -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?

Quoted 6 lines

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.

Quoted 6 lines

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

#13 lore raw

Re: [RFC PATCH v4 03/16] iommu/arm-smmu-v3: Add initial pSMMU realm viommu plumbing

Quoted 25 lines · Nicolin Chen
Quoted 24 lines · Nicolin Chen
Quoted 13 lines · Jason Gunthorpe

Review line 465

Quoted 7 lines · Aneesh Kumar K.V
(quoted diff) +3 -0
@@ -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?

Quoted 6 lines

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.

Quoted 6 lines
Quoted 3 lines

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

#14 lore raw

Re: [RFC PATCH v4 03/16] iommu/arm-smmu-v3: Add initial pSMMU realm viommu plumbing

In reply to Nicolin Chen
Quoted 15 lines · Nicolin Chen
(quoted diff) +15 -0
+/**
+ * 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

Review

Quoted 10 lines
(quoted diff) +10 -0
+ * @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?

Quoted 3 lines

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.

Quoted 3 lines

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

#15 lore raw

Re: [RFC PATCH v4 03/16] iommu/arm-smmu-v3: Add initial pSMMU realm viommu plumbing

Quoted 31 lines · Jason Gunthorpe
Quoted 15 lines · Nicolin Chen
(quoted diff) +15 -0
+/**
+ * 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

Review

Quoted 10 lines
(quoted diff) +10 -0
+ * @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.

Quoted 2 lines

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.

Quoted 10 lines
Quoted 3 lines

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;
Quoted 5 lines

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.

Quoted 7 lines
Quoted 3 lines

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.

Quoted 7 lines

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.

Quoted 8 lines

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

#16 lore raw

Re: [RFC PATCH v4 03/16] iommu/arm-smmu-v3: Add initial pSMMU realm viommu plumbing

Quoted 7 lines · Nicolin Chen
Quoted 3 lines · Jason Gunthorpe

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..

Quoted 7 lines

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

#17 lore raw

Re: [RFC PATCH v4 03/16] iommu/arm-smmu-v3: Add initial pSMMU realm viommu plumbing

In reply to Nicolin Chen
Quoted 5 lines · Nicolin Chen

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

#18 lore raw

Re: [RFC PATCH v4 03/16] iommu/arm-smmu-v3: Add initial pSMMU realm viommu plumbing

In reply to Nicolin Chen
Quoted 54 lines · Nicolin Chen
Quoted 13 lines · Jason Gunthorpe

drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c int arm_vsmmu_init(struct iommufd_viommu *viommu, · line 465

Quoted 7 lines · Aneesh Kumar K.V
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c +3 -0
@@ -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?

Quoted 6 lines

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:

(quoted diff) +25 -0
+/**
+ * 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];
+};
Quoted 6 lines

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

#19 lore raw

Re: [RFC PATCH v4 03/16] iommu/arm-smmu-v3: Add initial pSMMU realm viommu plumbing

In reply to Jason Gunthorpe
Quoted 13 lines · Jason Gunthorpe

drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c int arm_vsmmu_init(struct iommufd_viommu *viommu, · line 465

Quoted 7 lines · Aneesh Kumar K.V
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c +3 -0
@@ -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?

Quoted 7 lines

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

#20 lore raw

Re: [RFC PATCH v4 03/16] iommu/arm-smmu-v3: Add initial pSMMU realm viommu plumbing

Quoted 17 lines · Aneesh Kumar K.V
Quoted 15 lines · Jason Gunthorpe

drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c int arm_vsmmu_init(struct iommufd_viommu *viommu, · line 465

Quoted 7 lines · Aneesh Kumar K.V
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c +3 -0
@@ -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.

Quoted 8 lines
Quoted 4 lines

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..

Quoted 6 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-realm.c +6 -0
+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))

drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-realm.c void arm_smmu_setup_realm_irqs(struct arm_smmu_device *smmu) · line 271

Quoted 10 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-realm.c +10 -0
+		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.

Quoted 16 lines

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

#21 lore raw

Re: [RFC PATCH v4 03/16] iommu/arm-smmu-v3: Add initial pSMMU realm viommu plumbing

Quoted 23 lines · Jason Gunthorpe
Quoted 22 lines · Aneesh Kumar K.V
Quoted 19 lines · Jason Gunthorpe

Review line 465

Quoted 7 lines · Aneesh Kumar K.V
(quoted diff) +3 -0
@@ -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.

Quoted 7 lines
Quoted 3 lines

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

#22 lore raw

Re: [RFC PATCH v4 03/16] iommu/arm-smmu-v3: Add initial pSMMU realm viommu plumbing

Quoted 44 lines · Aneesh Kumar K.V
Quoted 23 lines · Jason Gunthorpe
Quoted 22 lines · Aneesh Kumar K.V
Quoted 19 lines · Jason Gunthorpe

Review line 465

Quoted 7 lines · Aneesh Kumar K.V
(quoted diff) +3 -0
@@ -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.

Quoted 7 lines
Quoted 3 lines

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.

Quoted 4 lines

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

#23 lore raw

Re: [RFC PATCH v4 03/16] iommu/arm-smmu-v3: Add initial pSMMU realm viommu plumbing

Quoted 36 lines · Aneesh Kumar K.V

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");
Quoted 8 lines
				return -EIO;
			}
			/* The L2 stream table already exists. */
		}
	}

	vdev->destroy = arm_realm_smmu_v3_vdevice_destroy;
	return tsm_bind(dev, kvm, vdev->virt_id);

I think we should drop tsm_bind() as an abstraction. It doesn't make sense to take that round about path when we are calling RMIs directly above. It was intended to be an abstraction, but it isn't working out with this viommu based abstraction.

Quoted 8 lines
(quoted diff) +3 -1
@@ -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);

Review static ssize_t cca_tsm_guest_req(struct pci_tdi *tdi, · line 527

Quoted 10 lines
(quoted diff) +6 -2
+		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);
+		}
 	}

This stuff cannot flow through sysfs. The VMM must support running in a sandbox so it cannot easially call out to sysfs while the VM is running. That makes the sandboxing more complex and ugly. The flow we have now relies on fd passing from the launcher into the sandbox to get things like vfio and iommufd into the VMM.

So these actions really should work the same way unless there is a strong reason to do otherwise.

Given these are all acting on bound devices, and those can only be created by iommufd, it makes more sense to feed the operations through iommufd into the viommu and vdevice ops. AMD wanted to create such general command ops anyhow for their viommu emulation (non cc).

And.. then you don't need struct pci_tdi. The vdevice is effectively the tdi and the existing locking scheme in iommufd for vdevice takes care of everything the tsm code was trying to do, except in a way that applies to every viommu out there.. This actually makes a lot of sense because the tdi is not really separable from vfio. You cannot have a tdi without a kvm and you cannot link a pci dev to a kvm without vfio.

Finally, there is really nothing about the viommu_ops that has much to do with smmuv3. It would be fairly straightforward for the tsm_ops to be able to create the viommu and provide the viommu_ops. We can get there based on the IOMMU_VIOMMU_TYPE_ARM_REALM_SMMUV3.

This then would open up a much nicer split where arm-cca-guest.ko can provide a realm smmuv3, and inside those viommu_ops are all the realm & tdi related ops, psmmu, vsmmu, vdevice, "guest req".

The arm-cca-guest can just make a simple function call to SMMUv3 to get the phys and interrupts. Somehow I think Will would like this better than adding to SMMUv3.

Now that the viommu stuff is more developed on the iommufd end, and the RMM spec is more complete with vsmmu, I think this arrangement becomes visible.

What do you think?

Jason

#24 lore raw

Re: [RFC PATCH v4 03/16] iommu/arm-smmu-v3: Add initial pSMMU realm viommu plumbing

Quoted 43 lines · Jason Gunthorpe
Quoted 42 lines · Aneesh Kumar K.V

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. */
		}
	}
Quoted 8 lines
Quoted 2 lines
	vdev->destroy = arm_realm_smmu_v3_vdevice_destroy;
	return tsm_bind(dev, kvm, vdev->virt_id);

I think we should drop tsm_bind() as an abstraction. It doesn't make sense to take that round about path when we are calling RMIs directly above. It was intended to be an abstraction, but it isn't working out with this viommu based abstraction.

I was considering using the viommu only for explicit pSMMU/vSMMU setup, SID/STE management, and realm stream-table creation. I expected other operations, such as vdevice creation and lock/run state transitions, to be driven by IOMMUFD ioctls and dispatched to the TSM backend through abstractions such as tsm_bind() and tsm_guest_req(). This results in the following split:

- Arm SMMU: pSMMU/vSMMU setup, SID/STE management, and Realm stream-table creation.
- PCI TSM: device association, bind lifetime, DSM lookup, and TDI state.
- arm-cca-host: PDEV/VDEV operations, TDISP transitions, reports, measurements, and IDE interaction.
- IOMMUFD: route userspace requests using the vdevice ID.

Quoted 17 lines
Quoted 17 lines
(quoted diff) +8 -3
@@ -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);
Quoted 9 lines

Review static ssize_t cca_tsm_guest_req(struct pci_tdi *tdi, · line 527

Quoted 2 lines
(quoted diff) +1 -0
+		}
 	}

This stuff cannot flow through sysfs. The VMM must support running in a sandbox so it cannot easially call out to sysfs while the VM is running. That makes the sandboxing more complex and ugly. The flow we have now relies on fd passing from the launcher into the sandbox to get things like vfio and iommufd into the VMM.

This does not go through sysfs. It uses the following IOMMUFD ioctl:

IOCTL_OP(IOMMU_VDEVICE_TSM_REQ, iommufd_vdevice_tsm_req_ioctl,
struct iommu_vdevice_tsm_req, tsm_code),

I need to spend more time considering your suggestion to handle guest requests through viommu_ops rather than as TSM backend operations. The split described below seemed more natural to me.

- Arm SMMU: pSMMU/vSMMU setup, SID/STE management, and Realm stream-table creation.
- PCI TSM: device association, bind lifetime, DSM lookup, and TDI state.
- arm-cca-host: PDEV/VDEV operations, TDISP transitions, reports, measurements, and IDE interaction.
- IOMMUFD: route userspace requests using the vdevice ID.

It is not yet clear to me whether operations such as MMIO validation (TSM_REQ_VALIDATE_MMIO), setting the TDI lock/unlock/run state (TSM_REQ_SET_TDI_STATE), querying TDISP object details (TSM_REQ_OBJECT_INFO), and reading or regenerating TDISP objects (TSM_REQ_READ_OBJECT and TSM_REQ_REGEN_OBJECT) belong in viommu_ops.

Meanwhile, I will clean up my changes and post them as a patch series so that we can review them more closely?

Quoted 29 lines

So these actions really should work the same way unless there is a strong reason to do otherwise.

Given these are all acting on bound devices, and those can only be created by iommufd, it makes more sense to feed the operations through iommufd into the viommu and vdevice ops. AMD wanted to create such general command ops anyhow for their viommu emulation (non cc).

And.. then you don't need struct pci_tdi. The vdevice is effectively the tdi and the existing locking scheme in iommufd for vdevice takes care of everything the tsm code was trying to do, except in a way that applies to every viommu out there.. This actually makes a lot of sense because the tdi is not really separable from vfio. You cannot have a tdi without a kvm and you cannot link a pci dev to a kvm without vfio.

Finally, there is really nothing about the viommu_ops that has much to do with smmuv3. It would be fairly straightforward for the tsm_ops to be able to create the viommu and provide the viommu_ops. We can get there based on the IOMMU_VIOMMU_TYPE_ARM_REALM_SMMUV3.

This then would open up a much nicer split where arm-cca-guest.ko can provide a realm smmuv3, and inside those viommu_ops are all the realm & tdi related ops, psmmu, vsmmu, vdevice, "guest req".

The arm-cca-guest can just make a simple function call to SMMUv3 to get the phys and interrupts. Somehow I think Will would like this better than adding to SMMUv3.

Quoted 7 lines

Now that the viommu stuff is more developed on the iommufd end, and the RMM spec is more complete with vsmmu, I think this arrangement becomes visible.

What do you think?

Jason

-aneesh

#25 lore raw

Re: [RFC PATCH v4 03/16] iommu/arm-smmu-v3: Add initial pSMMU realm viommu plumbing

Quoted 16 lines · Aneesh Kumar K.V
Quoted 8 lines
Quoted 2 lines
	vdev->destroy = arm_realm_smmu_v3_vdevice_destroy;
	return tsm_bind(dev, kvm, vdev->virt_id);

I think we should drop tsm_bind() as an abstraction. It doesn't make sense to take that round about path when we are calling RMIs directly above. It was intended to be an abstraction, but it isn't working out with this viommu based abstraction.

I was considering using the viommu only for explicit pSMMU/vSMMU setup, SID/STE management, and realm stream-table creation. I expected other operations, such as vdevice creation and lock/run state transitions, to be driven by IOMMUFD ioctls and dispatched to the TSM backend through abstractions such as tsm_bind() and tsm_guest_req(). This results in the following split:

Quoted 6 lines

- Arm SMMU: pSMMU/vSMMU setup, SID/STE management, and Realm stream-table creation.
- PCI TSM: device association, bind lifetime, DSM lookup, and TDI state.
- arm-cca-host: PDEV/VDEV operations, TDISP transitions, reports, measurements, and IDE interaction.
- IOMMUFD: route userspace requests using the vdevice ID.

I don't really like it, I think the viommu code should handle the VDEV and VSMU, TSM should handle SPDM.

"TDI state" is the existance of a iommufd vdevice, it doesn't make sense to have a seperate "TDI state" concept and a parallel set of APIs out side the iommufd object model that concretely defines the lifecylce of the VDEV.

Is there a reason to have this split aside from it matches the tsm prototypes that were sketched?

Quoted 26 lines
Quoted 16 lines
Quoted 16 lines
(quoted diff) +8 -3
@@ -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);
Quoted 9 lines

Review static ssize_t cca_tsm_guest_req(struct pci_tdi *tdi, · line 527

Quoted 2 lines
(quoted diff) +1 -0
+		}
 	}

This stuff cannot flow through sysfs. The VMM must support running in a sandbox so it cannot easially call out to sysfs while the VM is running. That makes the sandboxing more complex and ugly. The flow we have now relies on fd passing from the launcher into the sandbox to get things like vfio and iommufd into the VMM.

Quoted 4 lines

This does not go through sysfs. It uses the following IOMMUFD ioctl:

IOCTL_OP(IOMMU_VDEVICE_TSM_REQ, iommufd_vdevice_tsm_req_ioctl,
struct iommu_vdevice_tsm_req, tsm_code),

I see, I saw this when I was grepping:

static ssize_t tsm_request_store(struct device *dev,
struct device_attribute *attr, const char *__buf, size_t count)

Which the name and sysfs parts confused me, it looked like core code. Turns out it is the sample driver.

Quoted 11 lines

I need to spend more time considering your suggestion to handle guest requests through viommu_ops rather than as TSM backend operations. The split described below seemed more natural to me.

- Arm SMMU: pSMMU/vSMMU setup, SID/STE management, and Realm stream-table creation.
- PCI TSM: device association, bind lifetime, DSM lookup, and TDI state.
- arm-cca-host: PDEV/VDEV operations, TDISP transitions, reports, measurements, and IDE interaction.
- IOMMUFD: route userspace requests using the vdevice ID.

Quoted 5 lines

It is not yet clear to me whether operations such as MMIO validation (TSM_REQ_VALIDATE_MMIO), setting the TDI lock/unlock/run state (TSM_REQ_SET_TDI_STATE), querying TDISP object details (TSM_REQ_OBJECT_INFO), and reading or regenerating TDISP objects (TSM_REQ_READ_OBJECT and TSM_REQ_REGEN_OBJECT) belong in viommu_ops.

They way I'm looking at it is the "TDI" is the RMM's VDEV.

The RMM VDEV has to be created from an iommufd vdevice and be 1:1 with that. Thus the iommufd vdevice is the TDI.

Therefore, the struct cca_host_tdi should be the driver specific struct of a struct iommufd_vdevice.

So if you want to get the information in the cca_host_tdi you have to come in through the viommu ops with a vdevice object in hand.

This seems like a much cleaner logical seperation than trying to maintain a cca_host_tdi external to the iommufd vdevice object that actually directly controls its lifecycle.

So if you did this then the cca_tsm_guest_req would flow through some generic viommu op similar to what AMD proposed:

struct iommu_viommu_op {
	__u32 size;
	__u32 vdevice_id;
	__u32 viommu_type;  // enum iommu_viommu_type
	__u32 operation; // unique enum per type
	__u32 req_len;
	__u32 resp_len;
	__aligned_u64 req_uptr;
	__aligned_u64 resp_uptr;
};

enum {
IOMMUFD_VIOMMU_OP_CCA_OBJECT_SIZE
IOMMUFD_VIOMMU_OP_CCA_OBJECT_READ
IOMMUFD_VIOMMU_OP_CCA_UPDATE_INTERFACE_REPORT
IOMMUFD_VIOMMU_OP_CCA_UPDATE_MEASUREMENTS
IOMMUFD_VIOMMU_OP_CCA_VDEV_MAP
IOMMUFD_VIOMMU_OP_CCA_SET_TDI_STATE

And none of the TDI information leaks out side the iommufd world.

The iommufd side change to introduce CCA support would then only be adding the IOCTL for iommu_viommu_op and some fiddling with how the viommu is created.

Then everyone can use the same infrastructure for their related problems.

Quoted 2 lines

Meanwhile, I will clean up my changes and post them as a patch series so that we can review them more closely?

Well, OK, but I'm am still very interested in focusing on the viommu. I cc'd you on another thread so you can see the other topics I'm looking at here that all come down to very similar patterns.

I think the TDI related tsm ops were developed well before iommufd was completed and you have raised a good point now to re-evaluate if the we even need them since we now understand that the iommufd vdevice is in fact the concrete TDI object in the uAPI.

Jason

#26 lore raw

Re: [RFC PATCH v4 03/16] iommu/arm-smmu-v3: Add initial pSMMU realm viommu plumbing

Quoted 141 lines · Jason Gunthorpe
Quoted 16 lines · Aneesh Kumar K.V
Quoted 8 lines
Quoted 2 lines
	vdev->destroy = arm_realm_smmu_v3_vdevice_destroy;
	return tsm_bind(dev, kvm, vdev->virt_id);

I think we should drop tsm_bind() as an abstraction. It doesn't make sense to take that round about path when we are calling RMIs directly above. It was intended to be an abstraction, but it isn't working out with this viommu based abstraction.

I was considering using the viommu only for explicit pSMMU/vSMMU setup, SID/STE management, and realm stream-table creation. I expected other operations, such as vdevice creation and lock/run state transitions, to be driven by IOMMUFD ioctls and dispatched to the TSM backend through abstractions such as tsm_bind() and tsm_guest_req(). This results in the following split:

Quoted 6 lines

- Arm SMMU: pSMMU/vSMMU setup, SID/STE management, and Realm stream-table creation.
- PCI TSM: device association, bind lifetime, DSM lookup, and TDI state.
- arm-cca-host: PDEV/VDEV operations, TDISP transitions, reports, measurements, and IDE interaction.
- IOMMUFD: route userspace requests using the vdevice ID.

I don't really like it, I think the viommu code should handle the VDEV and VSMU, TSM should handle SPDM.

"TDI state" is the existance of a iommufd vdevice, it doesn't make sense to have a seperate "TDI state" concept and a parallel set of APIs out side the iommufd object model that concretely defines the lifecylce of the VDEV.

Is there a reason to have this split aside from it matches the tsm prototypes that were sketched?

Quoted 26 lines
Quoted 16 lines
Quoted 16 lines
(quoted diff) +8 -3
@@ -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);
Quoted 9 lines

Review static ssize_t cca_tsm_guest_req(struct pci_tdi *tdi, · line 527

Quoted 2 lines
(quoted diff) +1 -0
+		}
 	}

This stuff cannot flow through sysfs. The VMM must support running in a sandbox so it cannot easially call out to sysfs while the VM is running. That makes the sandboxing more complex and ugly. The flow we have now relies on fd passing from the launcher into the sandbox to get things like vfio and iommufd into the VMM.

Quoted 4 lines

This does not go through sysfs. It uses the following IOMMUFD ioctl:

IOCTL_OP(IOMMU_VDEVICE_TSM_REQ, iommufd_vdevice_tsm_req_ioctl,
struct iommu_vdevice_tsm_req, tsm_code),

I see, I saw this when I was grepping:

static ssize_t tsm_request_store(struct device *dev,
struct device_attribute *attr, const char *__buf, size_t count)

Which the name and sysfs parts confused me, it looked like core code. Turns out it is the sample driver.

Quoted 11 lines

I need to spend more time considering your suggestion to handle guest requests through viommu_ops rather than as TSM backend operations. The split described below seemed more natural to me.

- Arm SMMU: pSMMU/vSMMU setup, SID/STE management, and Realm stream-table creation.
- PCI TSM: device association, bind lifetime, DSM lookup, and TDI state.
- arm-cca-host: PDEV/VDEV operations, TDISP transitions, reports, measurements, and IDE interaction.
- IOMMUFD: route userspace requests using the vdevice ID.

Quoted 5 lines

It is not yet clear to me whether operations such as MMIO validation (TSM_REQ_VALIDATE_MMIO), setting the TDI lock/unlock/run state (TSM_REQ_SET_TDI_STATE), querying TDISP object details (TSM_REQ_OBJECT_INFO), and reading or regenerating TDISP objects (TSM_REQ_READ_OBJECT and TSM_REQ_REGEN_OBJECT) belong in viommu_ops.

They way I'm looking at it is the "TDI" is the RMM's VDEV.

The RMM VDEV has to be created from an iommufd vdevice and be 1:1 with that. Thus the iommufd vdevice is the TDI.

Therefore, the struct cca_host_tdi should be the driver specific struct of a struct iommufd_vdevice.

So if you want to get the information in the cca_host_tdi you have to come in through the viommu ops with a vdevice object in hand.

This seems like a much cleaner logical seperation than trying to maintain a cca_host_tdi external to the iommufd vdevice object that actually directly controls its lifecycle.

So if you did this then the cca_tsm_guest_req would flow through some generic viommu op similar to what AMD proposed:

struct iommu_viommu_op {
	__u32 size;
	__u32 vdevice_id;
	__u32 viommu_type;  // enum iommu_viommu_type
	__u32 operation; // unique enum per type
	__u32 req_len;
	__u32 resp_len;
	__aligned_u64 req_uptr;
	__aligned_u64 resp_uptr;
};

enum {
IOMMUFD_VIOMMU_OP_CCA_OBJECT_SIZE
IOMMUFD_VIOMMU_OP_CCA_OBJECT_READ
IOMMUFD_VIOMMU_OP_CCA_UPDATE_INTERFACE_REPORT
IOMMUFD_VIOMMU_OP_CCA_UPDATE_MEASUREMENTS
IOMMUFD_VIOMMU_OP_CCA_VDEV_MAP
IOMMUFD_VIOMMU_OP_CCA_SET_TDI_STATE

And none of the TDI information leaks out side the iommufd world.

The iommufd side change to introduce CCA support would then only be adding the IOCTL for iommu_viommu_op and some fiddling with how the viommu is created.

Then everyone can use the same infrastructure for their related problems.

Quoted 2 lines

Meanwhile, I will clean up my changes and post them as a patch series so that we can review them more closely?

Quoted 9 lines

Well, OK, but I'm am still very interested in focusing on the viommu. I cc'd you on another thread so you can see the other topics I'm looking at here that all come down to very similar patterns.

I think the TDI related tsm ops were developed well before iommufd was completed and you have raised a good point now to re-evaluate if the we even need them since we now understand that the iommufd vdevice is in fact the concrete TDI object in the uAPI.

I looked into this, and it becomes fairly complicated. We can move all vdev/TDI-related code to arm-smmu-realm-v3.c, but that would result in:

1. Adding more CCA-specific code to the SMMU driver.
2. arm-cca-host continuing to own the TSM link setup (IDE).
3. Adding callbacks from the device communication helpers back into arm-cca-host, since device communication still goes through DOE.
4. Moving the device communication helpers to firmware/arm-rmm and adding something like:

static const struct arm_rmm_pdev_comm_ops cca_dev_comm_ops = {
.send = cca_dev_comm_send,
.get_cache = cca_dev_comm_get_cache, .refresh_stream_keys = cca_dev_comm_refresh_stream_keys,
};

The locking also becomes more complex. Unlocking a vdev can trigger a stream key refresh, which is owned by arm-cca-host. Currently, the locking is simpler, using pci_tsm_rwsem and pci_tsm_pf0::lock. With the SMMU driver owning the vdev/TDI, we would have:

- pci_tsm_rwsem protecting the lifetime and registration state of pdev->tsm.
- pci_tsm::tdi_lock protecting the function's pci_tdi pointer.
- pci_tsm_pf0::lock protecting DSM-wide connect and disconnect operations, including probing and removing functions managed by that DSM.
- The lock associated with arm_rmm_pdev_comm protecting the shared PDEV communication buffers and cache state used for ARM RMM communication.

I have a working prototype, but it requires quite a bit of cleanup and rebasing before it can be posted as a clean patch series. Let me know if you still want to proceed with this approach.

In summary, this requires three subsystems (arm-smmu-realm-v3, arm-cca-host, and firmware/arm_rmm) to interact closely, with clearly defined locking rules. Previously, all of this was contained within arm-cca-host.

-aneesh

#27 lore raw

Re: [RFC PATCH v4 03/16] iommu/arm-smmu-v3: Add initial pSMMU realm viommu plumbing

Quoted 2 lines · Aneesh Kumar K.V

I looked into this, and it becomes fairly complicated. We can move all vdev/TDI-related code to arm-smmu-realm-v3.c, but that would result in:

I was going for the opposite, you'd move everything out of arm-smmu-v3 and into the arm-cca-host and obtain the viommu through tsm_ops not through iommu_ops.

I guess I pointed to that in another email.

The only thing arm-smmu-v3 should provide is a simple function to give the pdev phys and irq parameters. arm-cca-host calls that when it creates an viommu object.

Quoted 6 lines

1. Adding more CCA-specific code to the SMMU driver.
2. arm-cca-host continuing to own the TSM link setup (IDE).
3. Adding callbacks from the device communication helpers back into arm-cca-host, since device communication still goes through DOE.
4. Moving the device communication helpers to firmware/arm-rmm and adding something like:

With the above change you don't need to do any of this.

Quoted 4 lines

The locking also becomes more complex. Unlocking a vdev can trigger a stream key refresh, which is owned by arm-cca-host. Currently, the locking is simpler, using pci_tsm_rwsem and pci_tsm_pf0::lock. With the SMMU driver owning the vdev/TDI, we would have:

You can't really avoid something, the vdev *IS* the TDI and the ops to effect it come in through the iommufd path. The ideal version is the tdi is never used outside the iommufd path so you don't need to mesh the locking quite as carefully.

Quoted 3 lines

- pci_tsm_rwsem protecting the lifetime and registration state of pdev->tsm.
- pci_tsm::tdi_lock protecting the function's pci_tdi pointer.

Like here, why is there a pci_tdi linked to pci_tsm? Anything coming in outside the iommufd locking is very suspect.

And the iommufd locking should spill over to the TSM, you should not be able to remove a tsm while a viommu object exists.

Jason

#28 lore raw

Re: [RFC PATCH v4 03/16] iommu/arm-smmu-v3: Add initial pSMMU realm viommu plumbing

Quoted 9 lines · Jason Gunthorpe
Quoted 2 lines · Aneesh Kumar K.V

I looked into this, and it becomes fairly complicated. We can move all vdev/TDI-related code to arm-smmu-realm-v3.c, but that would result in:

I was going for the opposite, you'd move everything out of arm-smmu-v3 and into the arm-cca-host and obtain the viommu through tsm_ops not through iommu_ops.

Quoted 6 lines

I guess I pointed to that in another email.

The only thing arm-smmu-v3 should provide is a simple function to give the pdev phys and irq parameters. arm-cca-host calls that when it creates an viommu object.

So ended up with

static const struct tsm_viommu_ops cca_tsm_viommu_ops = {
.owner = THIS_MODULE,
.type = IOMMU_VIOMMU_TYPE_ARM_REALM_SMMUV3, .get_size = cca_viommu_get_size,
.init = cca_viommu_init,
};

struct cca_viommu {
	struct cca_psmmu *psmmu;
	struct iommu_viommu_provider *iommu_provider;
	const struct iommufd_viommu_ops *iommu_ops; // backing SMMU ops
};

// backing ops are mostly used for

static struct iommu_domain * cca_viommu_alloc_domain_nested(struct iommufd_viommu *viommu, u32 flags,
const struct iommu_user_data *user_data)

{
	struct cca_viommu *cca = viommu->provider_data;

	return cca->iommu_ops->alloc_domain_nested(viommu, flags, user_data);
}

static int cca_viommu_cache_invalidate(struct iommufd_viommu *viommu,
struct iommu_user_data_array *array)

{
	struct cca_viommu *cca = viommu->provider_data;

	return cca->iommu_ops->cache_invalidate(viommu, array);
}
struct cca_vdevice {
	struct iommufd_vdevice core;
	struct pci_tsm_context *tsm_context;
	struct cca_host_tdi host_tdi;
	u32 l2_sid;
};

static const struct iommufd_viommu_ops cca_viommu_ops = {
.destroy = cca_viommu_destroy,
.alloc_domain_nested = cca_viommu_alloc_domain_nested, .cache_invalidate = cca_viommu_cache_invalidate, .vdevice_size = VDEVICE_STRUCT_SIZE(struct cca_vdevice, core), .vdevice_init = cca_vdevice_init,
.vdevice_tsm_req = cca_vdevice_tsm_req,
};

and on iommu side

struct iommu_viommu_provider {
	size_t size;
	int (*init)(struct iommufd_viommu *viommu, struct device *dev,
		    enum iommu_viommu_type type,
		    struct iommu_domain *parent_domain,
		    const struct iommu_user_data *user_data);
	int (*get_params)(struct iommu_viommu_provider *provider,
			  struct device *dev, enum iommu_viommu_type type,
			  void *params, size_t params_size);
	void (*release)(struct iommu_viommu_provider *provider);
	void *data;
};

The TSM disconnect path will now fail while any vdevice is alive or active. Destroying a vdevice will unlock and destroy the VDEV. I think we can also unmap its MMIO mappings at that point, provided we track the mapping requests in a list alongside the vdevice details.

All CCA operations will use pci_tsm_pf0::lock, though I think the locking can be made more fine-grained.

I will send a cleaned-up series so that we can review the code changes.

-aneesh

#29 lore raw

Re: [RFC PATCH v4 03/16] iommu/arm-smmu-v3: Add initial pSMMU realm viommu plumbing

Quoted 19 lines · Aneesh Kumar K.V
Quoted 9 lines · Jason Gunthorpe
Quoted 2 lines · Aneesh Kumar K.V

I looked into this, and it becomes fairly complicated. We can move all vdev/TDI-related code to arm-smmu-realm-v3.c, but that would result in:

I was going for the opposite, you'd move everything out of arm-smmu-v3 and into the arm-cca-host and obtain the viommu through tsm_ops not through iommu_ops.

Quoted 6 lines

I guess I pointed to that in another email.

The only thing arm-smmu-v3 should provide is a simple function to give the pdev phys and irq parameters. arm-cca-host calls that when it creates an viommu object.

Quoted 8 lines

So ended up with

static const struct tsm_viommu_ops cca_tsm_viommu_ops = {
.owner = THIS_MODULE,
.type = IOMMU_VIOMMU_TYPE_ARM_REALM_SMMUV3, .get_size = cca_viommu_get_size,
.init = cca_viommu_init,
};

OK, may also be fine in the main tsm ops?

Quoted 4 lines
struct cca_viommu {
	struct cca_psmmu *psmmu;
	struct iommu_viommu_provider *iommu_provider;
	const struct iommufd_viommu_ops *iommu_ops; // backing SMMU ops

??

The viommu created for a RMM owned vSMMU should have no connection to the normal SMMU driver? It just needs the psmmu information.

We need to adjust iommufd side to know that this object cannot accept an iommu_domain, only a kvm fd. I don't want to pass in a fake iommu_domain that has nothing to do with how RMM will operate things.

Quoted 9 lines

static struct iommu_domain * cca_viommu_alloc_domain_nested(struct iommufd_viommu *viommu, u32 flags,
const struct iommu_user_data *user_data)

{
	struct cca_viommu *cca = viommu->provider_data;

	return cca->iommu_ops->alloc_domain_nested(viommu, flags, user_data);
}
Quoted 7 lines

static int cca_viommu_cache_invalidate(struct iommufd_viommu *viommu,
struct iommu_user_data_array *array)

{
	struct cca_viommu *cca = viommu->provider_data;

	return cca->iommu_ops->cache_invalidate(viommu, array);
}

These ops should fail (just be NULL) not reflect to a psmmu.

Quoted 4 lines
struct cca_vdevice {
	struct iommufd_vdevice core;
	struct pci_tsm_context *tsm_context;
	struct cca_host_tdi host_tdi;

Is the extra tdi struct still needed? Isn't cca_vdevice the tdi struct?

Quoted 15 lines
	u32 l2_sid;
};

static const struct iommufd_viommu_ops cca_viommu_ops = {
.destroy = cca_viommu_destroy,
.alloc_domain_nested = cca_viommu_alloc_domain_nested, .cache_invalidate = cca_viommu_cache_invalidate, .vdevice_size = VDEVICE_STRUCT_SIZE(struct cca_vdevice, core), .vdevice_init = cca_vdevice_init,
.vdevice_tsm_req = cca_vdevice_tsm_req,
};

and on iommu side

Quoted 12 lines
struct iommu_viommu_provider {
	size_t size;
	int (*init)(struct iommufd_viommu *viommu, struct device *dev,
		    enum iommu_viommu_type type,
		    struct iommu_domain *parent_domain,
		    const struct iommu_user_data *user_data);
	int (*get_params)(struct iommu_viommu_provider *provider,
			  struct device *dev, enum iommu_viommu_type type,
			  void *params, size_t params_size);
	void (*release)(struct iommu_viommu_provider *provider);
	void *data;
};

Not sure what this is?

Quoted 2 lines

The TSM disconnect path will now fail while any vdevice is alive or active.

Yes, that's makes sense.

Quoted 1 line

Destroying a vdevice will unlock and destroy the VDEV.

Yes

Quoted 3 lines

I think we can also unmap its MMIO mappings at that point, provided we track the mapping requests in a list alongside the vdevice details.

The mmio is owned by vfio, there should be a handshake in VFIO to remove mmio when it becomes private, otherwise I don't think we need to do anything more?

Quoted 2 lines

All CCA operations will use pci_tsm_pf0::lock, though I think the locking can be made more fine-grained.

Sure

Quoted 1 line

I will send a cleaned-up series so that we can review the code changes.

Does it seems reasonable to you? Was there any oddness with modeling the vdev/bind through the viommu?

Anyhow I'll look more closely when you are ready. Thanks

Jason

maintainer
#30 lore raw

RE: [RFC PATCH v4 03/16] iommu/arm-smmu-v3: Add initial pSMMU realm viommu plumbing

Quoted 4 lines

From: Jason Gunthorpe <[email protected]>
Sent: Wednesday, September 9, 2026 8:46 PM

On Wed, Sep 09, 2026 at 03:39:31PM +0530, Aneesh Kumar K.V wrote:

[...]

Quoted 9 lines
Quoted 2 lines

The TSM disconnect path will now fail while any vdevice is alive or active.

Yes, that's makes sense.

Quoted 1 line

Destroying a vdevice will unlock and destroy the VDEV.

Yes

Quoted 7 lines
Quoted 3 lines

I think we can also unmap its MMIO mappings at that point, provided we track the mapping requests in a list alongside the vdevice details.

The mmio is owned by vfio, there should be a handshake in VFIO to remove mmio when it becomes private, otherwise I don't think we need to do anything more?

for TDX a callback in VFIO is invoked to revoke the dmabuf.

btw I didn't see any dmabuf related in this series (even in a hackish way as Yilun sent out last year). Is it not required by CCA or just skipped now waiting for the framework to be settled?

Quoted 11 lines
Quoted 3 lines

All CCA operations will use pci_tsm_pf0::lock, though I think the locking can be made more fine-grained.

Sure

Quoted 1 line

I will send a cleaned-up series so that we can review the code changes.

Does it seems reasonable to you? Was there any oddness with modeling the vdev/bind through the viommu?

Try to catch up the implied changes to other arch's...

So viommu will become the abstraction point for tsm operations and directly talks to the tsm driver instead of going through the merged tsm_ops (bind/unbind/guest_req).

currently what tsm_ops provides additionally is more about synchronization with connect/disconnect, otherwise just calls into underlying tsm driver. As long as viommu creation holds a reference to tsm then disconnection is blocked then it's safe.

Is that the rationale behind?

and IOMMU_VDEVICE_TSM_BIND will be removed. Seems originally this series does SMC_RMI_VDEV_CREATE and SMC_RMI_VDEV_LOCK both in tsm bind op. Then the proposal is moving them all to vdevice creation time so no separate bind step is required.

Does it mean that ARM CCA is essentially an early-bind model i.e. the TDI starts in locked state from guest p.o.v.?

but https://lore.kernel.org/all/[email protected]/ seems to indicate that guest_req() still handles the bind request from the guest:

"
- cca_tsm_guest_req() now handles TSM_REQ_SET_TDI_STATE requests for the unlocked, locked, and running states.
"

a bit confusing, but maybe due to stale info cited from different timing...

for TDX we're currently pursuing a late-bind model (i.e. TDI starts in shared state in guest), so a separate bind interface is still necessary. We could look at whether it should be an explicit @bind viommu op or carried by @guest_req (say, if most preparation works can be moved to vdevice creation).

any other suggested changes to think about?

btw per past discussions VFIO should block some operations (reset, etc.) while a TDI is locked. Do we expect a callback into VFIO to notify the locked state, or require VFIO's own interface to place a cdev into a special state which prevents sensitive operations even before binding it to iommufd (and allow creating TDI vdevice only on such idevice)?

Thanks
Kevin

#31 lore raw

Re: [RFC PATCH v4 03/16] iommu/arm-smmu-v3: Add initial pSMMU realm viommu plumbing

Quoted 9 lines · Kevin
Quoted 3 lines

The mmio is owned by vfio, there should be a handshake in VFIO to remove mmio when it becomes private, otherwise I don't think we need to do anything more?

for TDX a callback in VFIO is invoked to revoke the dmabuf.

btw I didn't see any dmabuf related in this series (even in a hackish way as Yilun sent out last year). Is it not required by CCA or just skipped now waiting for the framework to be settled?

I think just one step at a time :\ I think CCA needs it as well, you also cannot safely leave private MMIO floating around the host userspace on CCA.

Quoted 3 lines

So viommu will become the abstraction point for tsm operations and directly talks to the tsm driver instead of going through the merged tsm_ops (bind/unbind/guest_req).

More precisely for what was being called the TDI - ie it is the abstraction point for the host side of a guest virtual PCI device.

iommufd vdevice already represents this, so we use it again.

Quoted 4 lines

currently what tsm_ops provides additionally is more about synchronization with connect/disconnect, otherwise just calls into underlying tsm driver. As long as viommu creation holds a reference to tsm then disconnection is blocked then it's safe.

Yes, this already has to be true or our lifecylce model is nonsense. Once the iommufd creates the vdevice the guest is running and we cannot disconnect it without unplugging it from the guest. Thus the locking can rely on that. Within an iommufd ioctl context the vdev and underlying connection must be stable.

Quoted 1 line

and IOMMU_VDEVICE_TSM_BIND will be removed.

Replaced, the same logical operations should flow through the viommu command ioctl

Quoted 4 lines

Seems originally this series
does SMC_RMI_VDEV_CREATE and SMC_RMI_VDEV_LOCK both in tsm bind op. Then the proposal is moving them all to vdevice creation time so no separate bind step is required.

Hmm, lock still should be guest visible on CCA, and the device has to start up in the guest as TDISP unlocked. I'm not 100% on the RMI side of the flow, but this does not look right.

VDEV create should be done before the realm is started. We need this setup right because Linux VM is going to validate the vdev during boot if it is affiliated to a vSMMU. lock should be done only when requested by the guest. The guest must start with a normal unlocked T=0 PCI device.

Quoted 2 lines

Does it mean that ARM CCA is essentially an early-bind model i.e. the TDI starts in locked state from guest p.o.v.?

No

Quoted 7 lines

but https://lore.kernel.org/all/[email protected]/ seems to indicate that guest_req() still handles the bind request from the guest:

"
- cca_tsm_guest_req() now handles TSM_REQ_SET_TDI_STATE requests for the unlocked, locked, and running states.
"

Right the guest side request to lock should come that way.

Quoted 7 lines

a bit confusing, but maybe due to stale info cited from different timing...

for TDX we're currently pursuing a late-bind model (i.e. TDI starts in shared state in guest), so a separate bind interface is still necessary. We could look at whether it should be an explicit @bind viommu op or carried by @guest_req (say, if most preparation works can be moved to vdevice creation).

It should go over the viommu command ioctl. It looked like there was alway some iommu specific format to the command in the tsm version of this, so that is the appropriate way.

Quoted 5 lines

btw per past discussions VFIO should block some operations (reset, etc.) while a TDI is locked. Do we expect a callback into VFIO to notify the locked state, or require VFIO's own interface to place a cdev into a special state which prevents sensitive operations even before binding it to iommufd (and allow creating TDI vdevice only on such idevice)?

At a minimum I think we have to synchronize with vfio, somehow, that when the device enforces its private MMIO so it can unmap it. Thay may be an argument we need a few standard viommu ops so the core code can capture lock/unlocks and do this prep work. Or maybe a special cdev state is simpler.

This should be figured out before any uapi is settled.

I don't know about reset, that sounds like something the pci core should deal with?

Jason

maintainer
#32 lore raw

RE: [RFC PATCH v4 03/16] iommu/arm-smmu-v3: Add initial pSMMU realm viommu plumbing

Quoted 10 lines

From: Jason Gunthorpe <[email protected]>
Sent: Thursday, September 10, 2026 8:47 PM

Quoted 6 lines · Kevin
Quoted 3 lines

The mmio is owned by vfio, there should be a handshake in VFIO to remove mmio when it becomes private, otherwise I don't think we need to do anything more?

for TDX a callback in VFIO is invoked to revoke the dmabuf.

Quoted 7 lines
Quoted 3 lines

btw I didn't see any dmabuf related in this series (even in a hackish way as Yilun sent out last year). Is it not required by CCA or just skipped now waiting for the framework to be settled?

I think just one step at a time :\ I think CCA needs it as well, you also cannot safely leave private MMIO floating around the host userspace on CCA.

Good to know

Quoted 15 lines
Quoted 4 lines

So viommu will become the abstraction point for tsm operations and directly talks to the tsm driver instead of going through the merged tsm_ops (bind/unbind/guest_req).

More precisely for what was being called the TDI - ie it is the abstraction point for the host side of a guest virtual PCI device.

iommufd vdevice already represents this, so we use it again.

Quoted 4 lines

currently what tsm_ops provides additionally is more about synchronization with connect/disconnect, otherwise just calls into underlying tsm driver. As long as viommu creation holds a reference to tsm then disconnection is blocked then it's safe.

Quoted 5 lines

Yes, this already has to be true or our lifecylce model is nonsense. Once the iommufd creates the vdevice the guest is running and we cannot disconnect it without unplugging it from the guest. Thus the locking can rely on that. Within an iommufd ioctl context the vdev and underlying connection must be stable.

btw looks a related suggestion is having the tsm driver (arm-cca-host here) implement the viommu ops while leaving the original smmu driver largely intact, and "obtain the viommu through tsm_ops not through iommu_ops".

It's not super clear to me about the implication behind after reading related discussions and what the "obtain" part means.

Could you help elaborate this part?

The old way is kind of:

vdevice_tsm_ops -> tsm_ops -> tsm driver -> iommu driver

then it will become:

viommu_ops (tsm related) -> tsm_driver

where tsm driver will fully handle tsm related viommu_ops?

Quoted 15 lines
Quoted 2 lines

and IOMMU_VDEVICE_TSM_BIND will be removed.

Replaced, the same logical operations should flow through the viommu command ioctl

Quoted 4 lines

Seems originally this series
does SMC_RMI_VDEV_CREATE and SMC_RMI_VDEV_LOCK both in tsm bind op. Then the proposal is moving them all to vdevice creation time so no separate bind step is required.

Hmm, lock still should be guest visible on CCA, and the device has to start up in the guest as TDISP unlocked. I'm not 100% on the RMI side of the flow, but this does not look right.

Quoted 5 lines

VDEV create should be done before the realm is started. We need this setup right because Linux VM is going to validate the vdev during boot if it is affiliated to a vSMMU. lock should be done only when requested by the guest. The guest must start with a normal unlocked T=0 PCI device.

Okay then this part is aligned cross all vendors. Thanks for clarification!

Quoted 36 lines
Quoted 3 lines

Does it mean that ARM CCA is essentially an early-bind model i.e. the TDI starts in locked state from guest p.o.v.?

No

Quoted 1 line

but https://lore.kernel.org/all/[email protected]/ seems to

indicate

Quoted 6 lines

that guest_req() still handles the bind request from the guest:

"
- cca_tsm_guest_req() now handles TSM_REQ_SET_TDI_STATE requests for the unlocked, locked, and running states.
"

Right the guest side request to lock should come that way.

Quoted 7 lines

a bit confusing, but maybe due to stale info cited from different timing...

for TDX we're currently pursuing a late-bind model (i.e. TDI starts in shared state in guest), so a separate bind interface is still necessary. We could look at whether it should be an explicit @bind viommu op or carried by @guest_req (say, if most preparation works can be moved to vdevice

creation).

It should go over the viommu command ioctl. It looked like there was alway some iommu specific format to the command in the tsm version of this, so that is the appropriate way.

Quoted 5 lines

btw per past discussions VFIO should block some operations (reset, etc.) while a TDI is locked. Do we expect a callback into VFIO to notify the locked state, or require VFIO's own interface to place a cdev into a special state which prevents sensitive operations even before binding it to iommufd (and allow creating TDI vdevice only on such idevice)?

Quoted 7 lines

At a minimum I think we have to synchronize with vfio, somehow, that when the device enforces its private MMIO so it can unmap it. Thay may be an argument we need a few standard viommu ops so the core code can capture lock/unlocks and do this prep work. Or maybe a special cdev state is simpler.

This should be figured out before any uapi is settled.

Agree

Quoted 4 lines

I don't know about reset, that sounds like something the pci core should deal with?

yes the pci core sounds a better place to handle it.

btw I held the impression of blocked operations from past discussion [1]. Initial attempt tried to proactively unbind the TDI upon any operations which may transit the TDI to the ERROR state. Then the suggestion at the moment was:

" But now the suggestion is never let VFIO do unbind, instead VFIO should block these operations when device is bound. "

[1] https://lore.kernel.org/all/aEFmPaYorqaYCKBY@yilunxu-OptiPlex-7050/

#33 lore raw

Re: [RFC PATCH v4 03/16] iommu/arm-smmu-v3: Add initial pSMMU realm viommu plumbing

Quoted 18 lines · Kevin
Quoted 5 lines

Yes, this already has to be true or our lifecylce model is nonsense. Once the iommufd creates the vdevice the guest is running and we cannot disconnect it without unplugging it from the guest. Thus the locking can rely on that. Within an iommufd ioctl context the vdev and underlying connection must be stable.

btw looks a related suggestion is having the tsm driver (arm-cca-host here) implement the viommu ops while leaving the original smmu driver largely intact, and "obtain the viommu through tsm_ops not through iommu_ops".

It's not super clear to me about the implication behind after reading related discussions and what the "obtain" part means.

Could you help elaborate this part?

The old way is kind of:

Quoted 7 lines

vdevice_tsm_ops -> tsm_ops -> tsm driver -> iommu driver

then it will become:

viommu_ops (tsm related) -> tsm_driver

where tsm driver will fully handle tsm related viommu_ops?

At least for ARM there is effectively no entanglement with the actual host iommu driver. The viommu is entirely provided by software in the RMM world, so it can have its own dedicated driver. In ARM T=1 transactions are alwayus routed to the RMM's iommu and there is no relation to the host.

I am interested how Intel works here, but I thought it was similar.

AMD is different and I suspect AMD will have to continue to use the viommu from the AMD iommu driver, but I am not sure.

Quoted 4 lines

btw I held the impression of blocked operations from past discussion [1]. Initial attempt tried to proactively unbind the TDI upon any operations which may transit the TDI to the ERROR state. Then the suggestion at the moment was:

With this construction the iommufd vdev is created and permanently exist as long as the VM and viommu exist.

How/when the tsm driver links this to a arch specific "bind/unbind" operation is more up to that driver, but I would expect what is thought of as "bind" should be the affiliation of the device's T=1 stream with the viommu and the target VM. It should not be sensitive to the TDISP state.

That is not prohibited, the TSM driver could do some auto "bind/unbind" whatever that means triggered by ops or tdisp state changing under the covers. But this cannot leak out as some kind of asynchronous vdev destruction.

Quoted 4 lines

" But now the suggestion is never let VFIO do unbind, instead VFIO should block these operations when device is bound. "

[1] https://lore.kernel.org/all/aEFmPaYorqaYCKBY@yilunxu-OptiPlex-7050/

IDK, if the host wants to put a device into error I don't see why the kernel should block it? There are many reasons a device can reach error, that needs to be handled.

Jason

maintainer
#34 lore raw

RE: [RFC PATCH v4 03/16] iommu/arm-smmu-v3: Add initial pSMMU realm viommu plumbing

Quoted 30 lines

From: Jason Gunthorpe <[email protected]>
Sent: Tuesday, September 15, 2026 9:43 PM

Quoted 18 lines · Kevin
Quoted 5 lines

Yes, this already has to be true or our lifecylce model is nonsense. Once the iommufd creates the vdevice the guest is running and we cannot disconnect it without unplugging it from the guest. Thus the locking can rely on that. Within an iommufd ioctl context the vdev and underlying connection must be stable.

btw looks a related suggestion is having the tsm driver (arm-cca-host here) implement the viommu ops while leaving the original smmu driver largely intact, and "obtain the viommu through tsm_ops not through iommu_ops".

It's not super clear to me about the implication behind after reading related discussions and what the "obtain" part means.

Could you help elaborate this part?

The old way is kind of:

Quoted 7 lines

vdevice_tsm_ops -> tsm_ops -> tsm driver -> iommu driver

then it will become:

viommu_ops (tsm related) -> tsm_driver

where tsm driver will fully handle tsm related viommu_ops?

Quoted 7 lines

At least for ARM there is effectively no entanglement with the actual host iommu driver. The viommu is entirely provided by software in the RMM world, so it can have its own dedicated driver. In ARM T=1 transactions are alwayus routed to the RMM's iommu and there is no relation to the host.

I am interested how Intel works here, but I thought it was similar.

Largely yes. Main difference at Intel side is that TDX still relies on the host to initiate iotlb invalidation (upon notification from KVM on S-EPT change). Currently we put this logic in intel-iommu driver but it's more about wrapping invalidation info and passing it to the firmware. Moving it into the tsm driver should be straightforward.

Maybe there'll be other subtle connections to host iommu driver but it doesn't sound a hard problem to solve.

Quoted 3 lines

AMD is different and I suspect AMD will have to continue to use the viommu from the AMD iommu driver, but I am not sure.

ARM/Intel may support guest viommu in the future. So AMD's case is a good reference. Maybe for ARM/Intel we'll stay with two or more viommu ops: one from tsm driver (TDI w/o guest viommu), 2nd from the host iommu driver (legacy w/ guest viommu), 3rd from the host iommu driver (TDI w/ guest viommu), etc. We'll see...

Quoted 6 lines
Quoted 5 lines

btw I held the impression of blocked operations from past discussion [1]. Initial attempt tried to proactively unbind the TDI upon any operations which may transit the TDI to the ERROR state. Then the suggestion at the moment was:

Quoted 8 lines

With this construction the iommufd vdev is created and permanently exist as long as the VM and viommu exist.

How/when the tsm driver links this to a arch specific "bind/unbind" operation is more up to that driver, but I would expect what is thought of as "bind" should be the affiliation of the device's T=1 stream with the viommu and the target VM. It should not be sensitive to the TDISP state.

Not sure about this part.

Each arch has its own definition about the binding flow (about 'how'), but sharing a common step by sending TDISP message to transit the TDI into the CONFIG_LOCKED state upon guest request (i.e. 'when').

According to the TDISP spec, memory reads/writes with T bit set is accepted only when the TDI is in RUN state (except MSI/MSI-X writes are allowed with T bit set in LOCKED but I don't think any arch supports it yet).

So your definition of 'bind' essentially affiliate it to the RUN state?

Quoted 5 lines

That is not prohibited, the TSM driver could do some auto "bind/unbind" whatever that means triggered by ops or tdisp state changing under the covers. But this cannot leak out as some kind of asynchronous vdev destruction.

Maybe it'd be clearer using an example e.g. ARM to clarify the suggested split. Or wait for Aneesh's next version...

Quoted 10 lines
Quoted 5 lines

" But now the suggestion is never let VFIO do unbind, instead VFIO should block these operations when device is bound. "

[1] https://lore.kernel.org/all/aEFmPaYorqaYCKBY@yilunxu-OptiPlex-7050/

IDK, if the host wants to put a device into error I don't see why the kernel should block it? There are many reasons a device can reach error, that needs to be handled.

hmm that's a good point!

#35 lore raw

Re: [RFC PATCH v4 03/16] iommu/arm-smmu-v3: Add initial pSMMU realm viommu plumbing

Quoted 8 lines · Kevin
Quoted 7 lines

At least for ARM there is effectively no entanglement with the actual host iommu driver. The viommu is entirely provided by software in the RMM world, so it can have its own dedicated driver. In ARM T=1 transactions are alwayus routed to the RMM's iommu and there is no relation to the host.

I am interested how Intel works here, but I thought it was similar.

Quoted 8 lines

Largely yes. Main difference at Intel side is that TDX still relies on the host to initiate iotlb invalidation (upon notification from KVM on S-EPT change). Currently we put this logic in intel-iommu driver but it's more about wrapping invalidation info and passing it to the firmware. Moving it into the tsm driver should be straightforward.

Maybe there'll be other subtle connections to host iommu driver but it doesn't sound a hard problem to solve.

Okay, so I saw the driver posting for basic iommu support, can we try to rework that to be split out like Aneesh is doing so everything about TDX calls lives in tsm and intel iommu only provides a small API surface to exchange whatever details are needed to bootstrap TDX module?

Quoted 4 lines
Quoted 2 lines

AMD is different and I suspect AMD will have to continue to use the viommu from the AMD iommu driver, but I am not sure.

ARM/Intel may support guest viommu in the future.

ARM supports guest viommu today, it is in the public spec. Secure guest vSMMU is entirely handled inside the RMM and has no connection to the host iommu driver. It is a <100 line ++ on top of Aneesh's work, Nicolin posted a draft at one point in those threads.

I anticipate a future intel guest T=1 viommu should be the same.

Thus I expect Intel/ARM to have two viommus, one that handles the T=1 stream owned by the TSM driver and implemented entirely by calling TDX/RMM.

One that handles the T=0 stream owned by the iommu driver - and it already exists.

Quoted 1 line

So AMD's case is a good reference.

I think, AMD is completely different. I keep forgetting thier thing, but IIRC they have a secure DTE but instead of having the secure word control the translation it controls the RMP and you end up using the host's translation for T=1 traffic. This is fundamentally different from how Intel and ARM are doing it where the actually IOVA translate is under the control of the secure world.

Both Intel and ARM put the S-EPT into the iommu HW directly.

So, I expect Intel to have an API similar to ARM. When you create the TSM viommu you tell it if the TDX module should create a secure guest visible VT-d emulation. TDX module has to perform the entire emulation because it must be trusted. Existing viommu ops should cover the remaining to register pdevices as vdevices, provide the vBDF and so on.

Quoted 11 lines
Quoted 5 lines

How/when the tsm driver links this to a arch specific "bind/unbind" operation is more up to that driver, but I would expect what is thought of as "bind" should be the affiliation of the device's T=1 stream with the viommu and the target VM. It should not be sensitive to the TDISP state.

Not sure about this part.

Each arch has its own definition about the binding flow (about 'how'), but sharing a common step by sending TDISP message to transit the TDI into the CONFIG_LOCKED state upon guest request (i.e. 'when').

Sure, the LOCKED command can be relayed from the guest, but that shouldn't be called BIND. locked/unlock/run/err is taking a iommufd vdev that is already affiliated with the VM to a specific TDISP state

Quoted 4 lines

According to the TDISP spec, memory reads/writes with T bit set is accepted only when the TDI is in RUN state (except MSI/MSI-X writes are allowed with T bit set in LOCKED but I don't think any arch supports it yet).

Sure

Quoted 1 line

So your definition of 'bind' essentially affiliate it to the RUN state?

No, it is informing the secure world that a physical PCI function is now a virtual PCI function, is a TDI, and is in a certain VM.

Outside virtual hotplug this is a permanent action when the VM is created.

Quoted 7 lines
Quoted 4 lines

That is not prohibited, the TSM driver could do some auto "bind/unbind" whatever that means triggered by ops or tdisp state changing under the covers. But this cannot leak out as some kind of asynchronous vdev destruction.

Maybe it'd be clearer using an example e.g. ARM to clarify the suggested split. Or wait for Aneesh's next version...

In ARM:

BIND is RMI_VDEV_CREATE it links a physical device to a virtual device in a realm.

RMI_VSMMU_CREATE can attach a vSMMU to the realm and there is some way to link the VDEV And the VSMMU together

Some sequence of RMI_VDEV_COMMUNICATE, RMI_VDEV_LOCK, RMI_VDEV_UNLOCK and a few others manipulate the UNLOCKED/LOCKED/RUN/ERR TDISP state of the VDEV.

I assume TDX has the same general shape, I don't know how you could implement this in a radically different way?

So iommufd viommu create calls RMI_VSMMU_CREATE
iommufd vdev create calls RMI_VDEV_CREATE iommufd viommu op ioctl calls the COMMUNICATE/LOCK/UNLOCK

Jason

maintainer
#36 lore raw

RE: [RFC PATCH v4 03/16] iommu/arm-smmu-v3: Add initial pSMMU realm viommu plumbing

Quoted 18 lines

From: Jason Gunthorpe <[email protected]>
Sent: Wednesday, September 16, 2026 8:40 PM

Quoted 14 lines · Kevin
Quoted 7 lines

At least for ARM there is effectively no entanglement with the actual host iommu driver. The viommu is entirely provided by software in the RMM world, so it can have its own dedicated driver. In ARM T=1 transactions are alwayus routed to the RMM's iommu and there is no relation to the host.

I am interested how Intel works here, but I thought it was similar.

Largely yes. Main difference at Intel side is that TDX still relies on the host to initiate iotlb invalidation (upon notification from KVM on S-EPT change). Currently we put this logic in intel-iommu driver but it's more about wrapping invalidation info and passing it to the firmware. Moving it into the tsm driver should be straightforward.

Quoted 8 lines
Quoted 2 lines

Maybe there'll be other subtle connections to host iommu driver but it doesn't sound a hard problem to solve.

Okay, so I saw the driver posting for basic iommu support, can we try to rework that to be split out like Aneesh is doing so everything about TDX calls lives in tsm and intel iommu only provides a small API surface to exchange whatever details are needed to bootstrap TDX module?

Yeah that makes sense.

Quoted 7 lines
Quoted 5 lines
Quoted 3 lines

AMD is different and I suspect AMD will have to continue to use the viommu from the AMD iommu driver, but I am not sure.

ARM/Intel may support guest viommu in the future.

ARM supports guest viommu today, it is in the public spec. Secure

sure, I meant the secure part. :)

Quoted 5 lines

guest vSMMU is entirely handled inside the RMM and has no connection to the host iommu driver. It is a <100 line ++ on top of Aneesh's work, Nicolin posted a draft at one point in those threads.

I anticipate a future intel guest T=1 viommu should be the same.

I expect so.

Quoted 4 lines

Thus I expect Intel/ARM to have two viommus, one that handles the T=1 stream owned by the TSM driver and implemented entirely by calling TDX/RMM.

and this one is special, not tying to existing IOAS/HWPT/attach/detach/etc. semantics.

Quoted 3 lines

One that handles the T=0 stream owned by the iommu driver - and it already exists.

sounds a clear split.

Quoted 9 lines
Quoted 2 lines

So AMD's case is a good reference.

I think, AMD is completely different. I keep forgetting thier thing, but IIRC they have a secure DTE but instead of having the secure word control the translation it controls the RMP and you end up using the host's translation for T=1 traffic. This is fundamentally different from how Intel and ARM are doing it where the actually IOVA translate is under the control of the secure world.

that reminds me...

Quoted 9 lines

Both Intel and ARM put the S-EPT into the iommu HW directly.

So, I expect Intel to have an API similar to ARM. When you create the TSM viommu you tell it if the TDX module should create a secure guest visible VT-d emulation. TDX module has to perform the entire emulation because it must be trusted. Existing viommu ops should cover the remaining to register pdevices as vdevices, provide the vBDF and so on.

will think more along that line.

Quoted 37 lines
Quoted 7 lines
Quoted 6 lines

How/when the tsm driver links this to a arch specific "bind/unbind" operation is more up to that driver, but I would expect what is thought of as "bind" should be the affiliation of the device's T=1 stream with the viommu and the target VM. It should not be sensitive to the TDISP state.

Quoted 5 lines

Not sure about this part.

Each arch has its own definition about the binding flow (about 'how'), but sharing a common step by sending TDISP message to transit the TDI into the CONFIG_LOCKED state upon guest request (i.e. 'when').

Sure, the LOCKED command can be relayed from the guest, but that shouldn't be called BIND. locked/unlock/run/err is taking a iommufd vdev that is already affiliated with the VM to a specific TDISP state

Quoted 4 lines

According to the TDISP spec, memory reads/writes with T bit set is accepted only when the TDI is in RUN state (except MSI/MSI-X writes are allowed with T bit set in LOCKED but I don't think any arch supports it yet).

Sure

Quoted 1 line

So your definition of 'bind' essentially affiliate it to the RUN state?

No, it is informing the secure world that a physical PCI function is now a virtual PCI function, is a TDI, and is in a certain VM.

Outside virtual hotplug this is a permanent action when the VM is created.

Quoted 5 lines
Quoted 4 lines

That is not prohibited, the TSM driver could do some auto "bind/unbind" whatever that means triggered by ops or tdisp state changing under the covers. But this cannot leak out as some kind of asynchronous vdev destruction.

Quoted 7 lines
Quoted 2 lines

Maybe it'd be clearer using an example e.g. ARM to clarify the suggested split. Or wait for Aneesh's next version...

In ARM:

BIND is RMI_VDEV_CREATE it links a physical device to a virtual device in a realm.

TDX: TDH.TDI.CREATE

Quoted 3 lines

RMI_VSMMU_CREATE can attach a vSMMU to the realm and there is some way to link the VDEV And the VSMMU together

TDX: TDH.DMAR.ADD

Quoted 5 lines

Some sequence of RMI_VDEV_COMMUNICATE, RMI_VDEV_LOCK, RMI_VDEV_UNLOCK
and a few others manipulate the UNLOCKED/LOCKED/RUN/ERR TDISP state of the VDEV.

to locked: TDH.TDI.BIND
to unlocked: TDH.TDI.UNBIND
to RUN: TDH.TDI.START

plus some other commands required around those points

Quoted 3 lines

I assume TDX has the same general shape, I don't know how you could implement this in a radically different way?

yes it's similar.

Quoted 5 lines

So iommufd viommu create calls RMI_VSMMU_CREATE
iommufd vdev create calls RMI_VDEV_CREATE iommufd viommu op ioctl calls the COMMUNICATE/LOCK/UNLOCK

so in this case BIND is essentially CREATE. Probably link/affiliate is a clearer name in description to avoid confusion with vendor specific bind command...

#37 lore raw

Re: [RFC PATCH v4 03/16] iommu/arm-smmu-v3: Add initial pSMMU realm viommu plumbing

In reply to Jason Gunthorpe
Quoted 14 lines · Jason Gunthorpe
Quoted 13 lines · Kevin
Quoted 7 lines

At least for ARM there is effectively no entanglement with the actual host iommu driver. The viommu is entirely provided by software in the RMM world, so it can have its own dedicated driver. In ARM T=1 transactions are alwayus routed to the RMM's iommu and there is no relation to the host.

I am interested how Intel works here, but I thought it was similar.

Largely yes. Main difference at Intel side is that TDX still relies on the host to initiate iotlb invalidation (upon notification from KVM on S-EPT change). Currently we put this logic in intel-iommu driver but it's more about wrapping invalidation info and passing it to the firmware. Moving it into the tsm driver should be straightforward.

Quoted 7 lines
Quoted 2 lines

Maybe there'll be other subtle connections to host iommu driver but it doesn't sound a hard problem to solve.

Okay, so I saw the driver posting for basic iommu support, can we try to rework that to be split out like Aneesh is doing so everything about TDX calls lives in tsm and intel iommu only provides a small API surface to exchange whatever details are needed to bootstrap TDX module?

Are you referring to this series?

https://lore.kernel.org/linux-iommu/[email protected]/

It initializes the iommu managed by the secure world (the TDX module).

I’m happy to rework it as you suggested by moving most of the TDX- related code into the tsm driver and leaving the host iommu driver to provide only the necessary assistance.

Thanks,
baolu

#38 lore raw

Re: [RFC PATCH v4 03/16] iommu/arm-smmu-v3: Add initial pSMMU realm viommu plumbing

In reply to Aneesh Kumar K.V
Quoted 9 lines · Aneesh Kumar K.V

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.

Quoted 3 lines

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 thinking we'd make sure the KVM is associated with the viommu and/or possibly the S2 domain. That was always sort of broadly the idea in this space. There are several topics unrelated to CC that needed this.
In any case, when you create the iommufd viommu you should also do VSMMU_CREATE which needs the RD. It seems reasonable to assume the RD is available during vdevice create.

[There is an aside here I will mention: several other use cases need this idea of an "external" domain where the HWPT would be created but not controlled by iommufd or the iommu subsystem. Xen and Hyperv for example. There is probably some merit in thinking more about exactly what the nested parent domain should be for this viommu, but it isn't critical.]

Quoted 14 lines

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
Quoted 5 lines
[   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

What was this one for during viommu alloc?

Quoted 8 lines

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
Quoted 5 lines

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

Why? The VMM needs to create the kvm before starting the iommu stuff.

I would expect that KVM knows it is going to a realm very early on? I had assumed the RD would also be created early by KVM?

What triggers RD creation? Why can't the VMM do it earlier?

Your followup said:
- The realm is created during viommu allocation, ensuring that the vSMMU can be created here.

Do you mean the SMMUv3 driver triggers RD creation? That feels wrong. Did you mean the VMM just does it earlier?

The draft in the next message looks promising, did you discover any other gotchas when exploring it?

When you repost this can you take some care to explain the general idea of this modelling in the commit messages so AMD and Intel can confirm they can use it for both their non-viommu and viommu cases?

Thanks,
Jason

#39 lore raw

Re: [RFC PATCH v4 03/16] iommu/arm-smmu-v3: Add initial pSMMU realm viommu plumbing

Quoted 71 lines · Jason Gunthorpe
Quoted 9 lines · Aneesh Kumar K.V

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.

Quoted 3 lines

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 thinking we'd make sure the KVM is associated with the viommu and/or possibly the S2 domain. That was always sort of broadly the idea in this space. There are several topics unrelated to CC that needed this.
In any case, when you create the iommufd viommu you should also do VSMMU_CREATE which needs the RD. It seems reasonable to assume the RD is available during vdevice create.

[There is an aside here I will mention: several other use cases need this idea of an "external" domain where the HWPT would be created but not controlled by iommufd or the iommu subsystem. Xen and Hyperv for example. There is probably some merit in thinking more about exactly what the nested parent domain should be for this viommu, but it isn't critical.]

Quoted 14 lines

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
Quoted 5 lines
[   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

What was this one for during viommu alloc?

Quoted 8 lines

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
Quoted 5 lines

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

Why? The VMM needs to create the kvm before starting the iommu stuff.

I would expect that KVM knows it is going to a realm very early on? I had assumed the RD would also be created early by KVM?

Quoted 9 lines

What triggers RD creation? Why can't the VMM do it earlier?

Your followup said:
- The realm is created during viommu allocation, ensuring that the vSMMU can be created here.

Do you mean the SMMUv3 driver triggers RD creation? That feels wrong. Did you mean the VMM just does it earlier?

Currently, CCA creates the realm lazily as part of another operation. Realm creation is triggered by kvm_arm_rmi_populate() or kvm_arch_vcpu_run_pid_change()

The change looks like this:

modified arch/arm64/include/asm/kvm_rmi.h
@@ -102,6 +102,14 @@ u64 kvm_realm_reset_id_aa64dfr0_el1(const struct kvm_vcpu *vcpu, u64 val);
bool kvm_rmi_supports_sve(void);

int kvm_init_realm(struct kvm *kvm);
+#ifdef CONFIG_KVM
+int kvm_realm_ensure_created(struct kvm *kvm);
+#else
+static inline int kvm_realm_ensure_created(struct kvm *kvm) +{
+ return -EOPNOTSUPP;
+}
+#endif
int kvm_activate_realm(struct kvm *kvm);
void kvm_destroy_realm(struct kvm *kvm);
int kvm_realm_teardown_stage2(struct kvm *kvm);
modified arch/arm64/kvm/rmi.c
@@ -1120,6 +1120,20 @@ static int realm_ensure_created(struct kvm *kvm)
return realm_create_rd(kvm);
}

+int kvm_realm_ensure_created(struct kvm *kvm)
+{
+ int ret;
+
+ if (!kvm_is_realm(kvm))
+ return -EINVAL;
+
+ guard(mutex)(&kvm->arch.config_lock);
+ ret = realm_ensure_created(kvm);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(kvm_realm_ensure_created);
+
int kvm_arm_rmi_init_ripas(struct kvm *kvm,
struct kvm_arm_rmi_init_ripas *args)
{
modified drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-realm.c
@@ -253,8 +253,9 @@ int arm_realm_smmu_v3_init(struct iommufd_viommu *viommu,
return -EINVAL;

kvm = viommu->kvm_file->private_data;
- if (!kvm_is_realm(kvm))
- return -EINVAL;
+ ret = kvm_realm_ensure_created(kvm);
+ if (ret)
+ return ret;

if (!(smmu->features & ARM_SMMU_FEAT_RME))
return -EOPNOTSUPP;

Quoted 4 lines

The draft in the next message looks promising, did you discover any other gotchas when exploring it?

Nothing significant. I still have questions about moving TDI flows such as vdev creation and guest requests into the SMMU driver instead of keeping them in arm-cca-host.ko, but I will follow up in the other thread.

Quoted 7 lines

When you repost this can you take some care to explain the general idea of this modelling in the commit messages so AMD and Intel can confirm they can use it for both their non-viommu and viommu cases?

Thanks,
Jason

-aneesh

#40 lore raw

Re: [RFC PATCH v4 03/16] iommu/arm-smmu-v3: Add initial pSMMU realm viommu plumbing

Quoted 6 lines · Aneesh Kumar K.V
Quoted 2 lines

Do you mean the SMMUv3 driver triggers RD creation? That feels wrong. Did you mean the VMM just does it earlier?

Currently, CCA creates the realm lazily as part of another operation. Realm creation is triggered by kvm_arm_rmi_populate() or kvm_arch_vcpu_run_pid_change()

Okay, that's not what I would have guessed. But critically is there some way for userspace to specify any realm creation parameters (like rpv, ats_plane and so one) prior to the lazy creation?

Why lazy? Why not just have userspace provide the creation parameters and create the RD right then?

Jason

#41 lore raw

Re: [RFC PATCH v4 03/16] iommu/arm-smmu-v3: Add initial pSMMU realm viommu plumbing

Quoted 11 lines · Jason Gunthorpe
Quoted 6 lines · Aneesh Kumar K.V
Quoted 2 lines

Do you mean the SMMUv3 driver triggers RD creation? That feels wrong. Did you mean the VMM just does it earlier?

Currently, CCA creates the realm lazily as part of another operation. Realm creation is triggered by kvm_arm_rmi_populate() or kvm_arch_vcpu_run_pid_change()

Okay, that's not what I would have guessed. But critically is there some way for userspace to specify any realm creation parameters (like rpv, ats_plane and so one) prior to the lazy creation?

There would be, eventually. Based on the feedback from KVM arm64 maintainers, we are trying to keep the ABI as generic as possible as the normal VMs. Thus, for now we have not included the Realm specific parameter configuration (which was present in the earlier versions) in the latest version of the KVM support.

We have some ideas on how to do the configuration step. e.g. for MEC, see the following thread :

https://lore.kernel.org/all/[email protected]/

Quoted 3 lines

Why lazy? Why not just have userspace provide the creation parameters and create the RD right then?

Cheers
Suzuki

Quoted 2 lines

Jason

#42 lore raw

Re: [RFC PATCH v4 03/16] iommu/arm-smmu-v3: Add initial pSMMU realm viommu plumbing

Quoted 19 lines · Suzuki K Poulose
Quoted 11 lines · Jason Gunthorpe
Quoted 6 lines · Aneesh Kumar K.V
Quoted 2 lines

Do you mean the SMMUv3 driver triggers RD creation? That feels wrong. Did you mean the VMM just does it earlier?

Currently, CCA creates the realm lazily as part of another operation. Realm creation is triggered by kvm_arm_rmi_populate() or kvm_arch_vcpu_run_pid_change()

Okay, that's not what I would have guessed. But critically is there some way for userspace to specify any realm creation parameters (like rpv, ats_plane and so one) prior to the lazy creation?

There would be, eventually. Based on the feedback from KVM arm64 maintainers, we are trying to keep the ABI as generic as possible as the normal VMs. Thus, for now we have not included the Realm specific parameter configuration (which was present in the earlier versions) in the latest version of the KVM support.

Quoted 4 lines

We have some ideas on how to do the configuration step. e.g. for MEC, see the following thread :

https://lore.kernel.org/all/[email protected]/

I see, well please convay to the KVM folks that the iommu side needs the RD to be setup prior to creating viommu objects and needs to be able to supply realm parameters like ats_plane to work..

Don't see a reason to twist the API to accomodate KVM being out of order for now. It is up to you if you want to try to do the lazy create or try to get a defined creation momement with parameters (I prefer this from an aesthetic perspective at least)

Jason

#43 lore raw

Re: [RFC PATCH v4 03/16] iommu/arm-smmu-v3: Add initial pSMMU realm viommu plumbing

In reply to Aneesh Kumar K.V
Quoted 27 lines · Aneesh Kumar K.V
Quoted 21 lines · Nicolin Chen

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.

Quoted 4 lines

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?

Quoted 7 lines

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

#44 lore raw

RFC PATCH v4 4/16 iommu/arm-smmu-v3: Track realm pSMMU users with refcount_t

In reply to Aneesh Kumar K.V (Arm)

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.

---
 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(-)
arch/arm64/include/asm/rmi_cmds.h +1 -0
index 659d68ad5f1d..205fc200d1db 100644
@@ -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,
arch/arm64/kernel/rmi.c +12 -0
index cc4050db5a6a..884ab3f99f2f 100644
@@ -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)
 {
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-realm.c +32 -7
index 6f8de7cead9d..dfff493f96d0 100644
@@ -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;
 }
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +2 -1
index 1e3d4d682e32..e458c3818c34 100644
@@ -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);
 
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h +3 -1
index d528b3212d38..b5d0e1341236 100644
@@ -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 {
Signature · 1 line
2.43.0
#45 lore raw

RFC PATCH v4 5/16 coco: host: arm64: Add support for virtual device communication

In reply to Aneesh Kumar K.V (Arm)

Add support for vdev_communicate with RMM.

---
 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(-)
arch/arm64/include/asm/rmi_cmds.h +32 -0
index 205fc200d1db..2925abde3882 100644
@@ -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 */
arch/arm64/include/asm/rmi_smc.h +7 -0
index 5b540d25914e..72e4a53b74b0 100644
@@ -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 */
drivers/virt/coco/arm-cca-host/rmi-da.c +76 -7

138 lines not on this page · raw message

drivers/virt/coco/arm-cca-host/rmi-da.h +20 -0
index 798a8ed7505f..88fa428f788e 100644
@@ -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);
Signature · 1 line
2.43.0
#46 lore raw

RFC PATCH v4 6/16 coco: host: arm64: Add support for RMM vdev objects

In reply to Aneesh Kumar K.V (Arm)

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.

---
 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(-)
arch/arm64/include/asm/rmi_cmds.h +22 -0
index 2925abde3882..242ce2fac14e 100644
@@ -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 */
arch/arm64/include/asm/rmi_smc.h +22 -0
index 72e4a53b74b0..d14d13a9f169 100644
@@ -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 */
drivers/virt/coco/arm-cca-host/arm-cca.c +26 -1
index 8b1182620872..5930a30dd16f 100644
@@ -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)
drivers/virt/coco/arm-cca-host/rmi-da.c +104 -0

107 lines not on this page · raw message

drivers/virt/coco/arm-cca-host/rmi-da.h +2 -0
index 88fa428f788e..cd13cbf650d5 100644
@@ -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
Signature · 1 line
2.43.0
#47 lore raw

RFC PATCH v4 7/16 coco: host: arm64: Add pdev stream key refresh and purge helpers

In reply to Aneesh Kumar K.V (Arm)

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.

---
 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(+)
arch/arm64/include/asm/rmi_cmds.h +24 -0
index 242ce2fac14e..03dffba763e1 100644
@@ -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 */
drivers/virt/coco/arm-cca-host/rmi-da.c +35 -0
index 84f0b2211cd1..128079d5b993 100644
@@ -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);
+}
drivers/virt/coco/arm-cca-host/rmi-da.h +4 -0
index cd13cbf650d5..d6cdbc638d6d 100644
@@ -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
Signature · 1 line
2.43.0
#48 lore raw

RFC PATCH v4 8/16 coco: host: arm64: Add helpers to unlock and destroy RMM vdev

In reply to Aneesh Kumar K.V (Arm)

- 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

---
 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(-)
arch/arm64/include/asm/rmi_cmds.h +20 -0
index 03dffba763e1..aa7ef9f07517 100644
@@ -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 */
arch/arm64/include/asm/rmi_smc.h +2 -0
index d14d13a9f169..6cd5439f56ec 100644
@@ -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
drivers/virt/coco/arm-cca-host/arm-cca.c +25 -0
index 5930a30dd16f..b75fa20513a9 100644
@@ -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)
drivers/virt/coco/arm-cca-host/rmi-da.c +66 -3

84 lines not on this page · raw message

drivers/virt/coco/arm-cca-host/rmi-da.h +3 -0
index d6cdbc638d6d..97f7eaf1f779 100644
@@ -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
Signature · 1 line
2.43.0
#49 lore raw

RFC PATCH v4 9/16 coco: host: arm64: Add support for da object read RHI handling

In reply to Aneesh Kumar K.V (Arm)

Device assignment-related RHI calls result in a REC exit, which is handled by the tsm guest_request callback.

---
 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
arch/arm64/include/uapi/asm/rmi-da.h new file +21 -0
new file mode 100644
index 000000000000..5ec3413dce94
@@ -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
drivers/virt/coco/arm-cca-host/arm-cca.c +74 -0

92 lines not on this page · raw message

drivers/virt/coco/arm-cca-host/rmi-da.c +95 -0

104 lines not on this page · raw message

drivers/virt/coco/arm-cca-host/rmi-da.h +3 -0
index 97f7eaf1f779..4f1a61a5dcfa 100644
@@ -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
Signature · 1 line
2.43.0
#50 lore raw

RFC PATCH v4 10/16 coco: host: arm64: Add helper for cached object fetches

In reply to Aneesh Kumar K.V (Arm)

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.

---
 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(+)
drivers/virt/coco/arm-cca-host/rmi-da.c +26 -0
index 3db42c21dab0..63b20c8aef54 100644
@@ -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;
drivers/virt/coco/arm-cca-host/rmi-da.h +3 -0
index 4f1a61a5dcfa..c1fc7c01943e 100644
@@ -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;
 };
 
Signature · 1 line
2.43.0
#51 lore raw

RFC PATCH v4 11/16 coco: host: arm64: Fetch interface report via RMI

In reply to Aneesh Kumar K.V (Arm)

- 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

---
 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(-)
arch/arm64/include/asm/rmi_cmds.h +12 -0
index aa7ef9f07517..b3c04029bb47 100644
@@ -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 */
arch/arm64/include/uapi/asm/rmi-da.h +3 -0
index 5ec3413dce94..8d36a4c59849 100644
@@ -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
drivers/virt/coco/arm-cca-host/arm-cca.c +4 -0
index 4bf1f1b394af..2955993d29ac 100644
@@ -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;
 		}
drivers/virt/coco/arm-cca-host/rmi-da.c +53 -1

63 lines not on this page · raw message

drivers/virt/coco/arm-cca-host/rmi-da.h +1 -0
index c1fc7c01943e..b114bf4d4202 100644
@@ -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
Signature · 1 line
2.43.0
#52 lore raw

RFC PATCH v4 12/16 coco: host: arm64: Fetch device measurements via RMI

In reply to Aneesh Kumar K.V (Arm)

- 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

---
 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(+)
arch/arm64/include/asm/rmi_cmds.h +12 -0
index b3c04029bb47..350fd9bc93a4 100644
@@ -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 */
arch/arm64/include/asm/rmi_smc.h +13 -0
index 6cd5439f56ec..29dbe4e0dfb0 100644
@@ -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 */
arch/arm64/include/uapi/asm/rmi-da.h +8 -0
index 8d36a4c59849..97648928f763 100644
@@ -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
drivers/virt/coco/arm-cca-host/arm-cca.c +16 -0
index 2955993d29ac..855427935f2d 100644
@@ -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;
 		}
drivers/virt/coco/arm-cca-host/rmi-da.c +68 -0

71 lines not on this page · raw message

drivers/virt/coco/arm-cca-host/rmi-da.h +1 -0
index b114bf4d4202..621e0858f0c6 100644
@@ -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
Signature · 1 line
2.43.0
#53 lore raw

RFC PATCH v4 13/16 coco: host: KVM: arm64: Handle vdev validate-mapping exits

In reply to Aneesh Kumar K.V (Arm)

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.

---
 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(+)
Documentation/virt/kvm/api.rst +20 -0
index 5dfaafae14b6..4df99bb2857f 100644
@@ -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];
arch/arm64/include/asm/kvm_rmi.h +4 -0
index e1f5523c2dfa..f49988fe182e 100644
@@ -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 */
arch/arm64/include/asm/rmi_smc.h +2 -0
index 29dbe4e0dfb0..6bbabcd853bd 100644
@@ -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 */
arch/arm64/include/uapi/asm/rmi-da.h +9 -0
index 97648928f763..572afb4095f2 100644
@@ -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
arch/arm64/kvm/rmi-exit.c +37 -0
index 7eff6967530c..8c7cf716ce3c 100644
@@ -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",
arch/arm64/kvm/rmi.c +189 -0

201 lines not on this page · raw message

drivers/virt/coco/arm-cca-host/arm-cca.c +27 -0
index 855427935f2d..66e0acadf743 100644
@@ -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;
 	}
drivers/virt/coco/arm-cca-host/rmi-da.c +21 -0
index ec7701ff7e03..543c40fb1160 100644
@@ -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);
+}
drivers/virt/coco/arm-cca-host/rmi-da.h +2 -0
index 621e0858f0c6..3dfb6b3cc2ef 100644
@@ -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
include/uapi/linux/kvm.h +11 -0
index 309f058cf2f8..bac41f2b13e4 100644
@@ -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];
 	};
Signature · 1 line
2.43.0
#54 lore raw

RFC PATCH v4 14/16 KVM: arm64: Unmap device mappings when a private granule is destroyed

In reply to Aneesh Kumar K.V (Arm)

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.

---
 arch/arm64/include/asm/rmi_smc.h |  1 +
 arch/arm64/kvm/rmi.c             | 87 ++++++++++++++++++++++++++++++--
 2 files changed, 83 insertions(+), 5 deletions(-)
arch/arm64/include/asm/rmi_smc.h +1 -0
index 6bbabcd853bd..f3ad545d68b7 100644
@@ -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
arch/arm64/kvm/rmi.c +82 -5

141 lines not on this page · raw message

Signature · 1 line
2.43.0
#55 lore raw

RFC PATCH v4 15/16 coco: host: arm64: Transition vdevs to TDISP RUN state

In reply to Aneesh Kumar K.V (Arm)

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.

---
 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(+)
arch/arm64/include/asm/rmi_cmds.h +11 -0
index 350fd9bc93a4..19eba97a6c7b 100644
@@ -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 */
arch/arm64/include/uapi/asm/rmi-da.h +6 -0
index 572afb4095f2..c0cfcadfae47 100644
@@ -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
drivers/virt/coco/arm-cca-host/arm-cca.c +15 -0
index 66e0acadf743..3a682352fb68 100644
@@ -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;
 		}
drivers/virt/coco/arm-cca-host/rmi-da.c +22 -0
index 543c40fb1160..60b750c961ea 100644
@@ -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);
+}
drivers/virt/coco/arm-cca-host/rmi-da.h +1 -0
index 3dfb6b3cc2ef..3082166038c3 100644
@@ -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
Signature · 1 line
2.43.0
#56 lore raw

RFC PATCH v4 16/16 KVM: arm64: CCA: enable DA in realm create parameters

In reply to Aneesh Kumar K.V (Arm)

Now that we have all the required steps for DA in-place, enable DA while creating realm.

---
 arch/arm64/include/asm/rmi_smc.h | 1 +
 arch/arm64/kvm/rmi.c             | 3 +++
 2 files changed, 4 insertions(+)
arch/arm64/include/asm/rmi_smc.h +1 -0
index f3ad545d68b7..c02e2f087b1c 100644
@@ -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 */
arch/arm64/kvm/rmi.c +3 -0
index cc9e045dcae9..e041c4caee79 100644
@@ -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)) {
Signature · 1 line
2.43.0
maintainer
#57 lore raw
In reply to Aneesh Kumar K.V (Arm)
Quoted 5 lines · Aneesh Kumar K.V (Arm)

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?

Quoted 11 lines

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

Quoted 3 lines

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

#58 lore raw
Quoted 10 lines · Jason Gunthorpe
Quoted 5 lines · Aneesh Kumar K.V (Arm)

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)

Quoted 4 lines

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/[email protected]
[2] https://lore.kernel.org/all/[email protected]

Quoted 8 lines

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?

Quoted 4 lines

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.

Quoted 6 lines

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.

Quoted 20 lines

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?

Quoted 11 lines

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

Quoted 7 lines
Quoted 3 lines

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

maintainer
#59 lore raw
Quoted 8 lines · Aneesh Kumar K.V
Quoted 2 lines

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.

Quoted 8 lines
Quoted 8 lines

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?

Quoted 7 lines
Quoted 3 lines

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