Skip to contentSkip to conversation

iommu/arm-smmu-v3: Support guest-level Realm VSMMU (Part-1)

PATCH Superseded Nicolin Chen 4 participants 26 messages Sep 10 – Oct 29

Subsystems (5)

v1 of 3 Superseded Latest is v3 →

lore
Share & tools
lore
#0 lore raw

cover iommu/arm-smmu-v3: Support guest-level Realm VSMMU (Part-1)

[ PATCH-1 in this series is a provisional cherry-pick from Aneesh's series
that moves the RSI layer out of arch/arm64, per Will's request. I added it here to base the entire series on a public commit ID so that Sashiko can start its job. So, I kept it as-is mostly, with Jason's review tag. ]

This series prepares Realm guest IOMMU and DMA subsystems for devices that are present but not yet inside the Realm trust boundary.

Inside a confidential guest, we expect that all the iommus are going to be T=1 aware; there seems to be no interest in supporting IOMMUs that handle T=0 only. This means they only work on devices that have been put into the run state and issue T=1 transactions.

Today an iommu driver assumes it translates for every device behind it: it probes the device, takes a translating default domain, and puts the DMA API on its page tables. That assumption is wrong for T=0 devices, whose DMA the hypervisor translates instead, so the mappings made by the guest are never consulted and the device silently stops working.

To solve this problem, make TDISP T=0 the default for devices associated with a confidential IOMMU. The tdisp_t1 flag records the future T=1 state.

While T=0, the device remains outside the Realm trust boundary. Its DMA is translated by the host or hypervisor:

    [device: T=0] ===> [hypervisor] ===> [memory]
        |
        +-- (guest association) --> [confidential IOMMU: blocking DMA]

         Legend: ===> DMA path; --> guest topology and control

After reaching T=1, the device is inside the Realm trust boundary. Its DMA may use the guest VSMMU after RMM binds the device to its virtual stream:

    [device] ===> [VSMMU: S1] ===> [RMM/PSMMU: S2 + DPT] ===> [memory]

As part 1 of Realm VSMMU support, implement enough core infrastructure to manage a confidential vIOMMU:
- Detect and mark it as confidential in the core iommu structures
- Support devices in T=0 mode by keeping the DMA API in direct mode. The iommu is up and running, but parks such a device in its blocking domain and translates for nothing, as the core takes over what the device still needs, which today is ATS
- Keep it parked: refuse a translating domain by every route in, whether through the default domain, sysfs, group attach or replace, or PASID

Part 1 only adds the helpers required for T=0 setup and teardown. It does not add iommu_tdisp_enter_t1() until part 2 has a transition to perform.

Then implement SMMUv3 support:
- Let a CPU-integrated IOMMU declare that its DMA can reach private memory, so coherent allocations and streaming mappings avoid the SWIOTLB shared pool
- Identify a T=1 VSMMU using trusted RSI because firmware does not distinguish it from a normal T=0 SMMU
- Validate the firmware MMIO range against RSI before activating the VSMMU and allocating its queues and tables from private memory

The series is on github:
https://github.com/nicolinc/iommufd/commits/smmuv3_realm_guest_p1-v1

Future work
===========

Part 2 will add the T=1 transition after validation through:

- TSM, whose guest-side interface remains under discussion
- RMM, using the Arm-specific RSI VDEV command

For a multi-device IOMMU group, the first device accepted into T=1 claims the group. Part 2 will reject T=1 for every other member.

The current RSI binding validation returns one VSID, so part 2 must reject a firmware specification containing multiple SIDs upon the T=1 transition.

Aneesh Kumar K.V (Arm) (1):
firmware: arm_rmm: Move RSI support out of arch/arm64

Nicolin Chen (7):
firmware: arm_rmm: Add VSMMU commands and fields
dma-mapping: Let a device declare that it reaches private memory
dma-mapping: Keep DMA memory private for capable devices
iommu: Let a driver mark an IOMMU as confidential
iommu: Introduce TDISP T=0 state for confidential IOMMUs
iommu: Park TDISP T=0 devices in the blocking domain iommu/arm-smmu-v3: Probe a guest-level Realm VSMMU via RSI commands

 arch/arm64/Kconfig                            |  1 +
 drivers/firmware/Kconfig                      |  1 +
 drivers/firmware/arm_rmm/Kconfig              | 18 ++++
 drivers/virt/coco/arm-cca-guest/Kconfig       |  2 +-
 arch/arm64/kernel/Makefile                    |  2 +-
 drivers/firmware/Makefile                     |  1 +
 drivers/firmware/arm_rmm/Makefile             |  2 +
 drivers/iommu/Makefile                        |  3 +
 arch/arm64/include/asm/io.h                   |  2 +-
 arch/arm64/include/asm/mem_encrypt.h          |  2 +-
 arch/arm64/include/asm/pgtable-prot.h         |  2 +-
 arch/arm64/include/asm/rsi.h                  | 70 -------------
 drivers/iommu/iommu-priv.h                    | 19 ++++
 .../linux/arm-rsi-cmds.h                      | 99 ++++++++++++++++++-
 .../linux/arm-smccc-rsi.h                     | 26 ++++-
 include/linux/device.h                        |  4 +
 include/linux/dma-direct.h                    |  7 +-
 include/linux/dma-mapping.h                   |  4 +
 include/linux/iommu.h                         | 14 +++
 arch/arm64/kernel/setup.c                     |  2 +-
 arch/arm64/mm/init.c                          |  3 +-
 .../kernel => drivers/firmware/arm_rmm}/rsi.c |  2 +-
 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c   | 55 +++++++++++
 drivers/iommu/iommu-cc.c                      | 57 +++++++++++
 drivers/iommu/iommu.c                         | 86 +++++++++++++++-
 .../virt/coco/arm-cca-guest/arm-cca-guest.c   |  5 +-
 kernel/dma/direct.c                           | 16 +--
 kernel/dma/mapping.c                          | 23 ++++-
 kernel/dma/swiotlb.c                          |  2 +-
 29 files changed, 427 insertions(+), 103 deletions(-)
 create mode 100644 drivers/firmware/arm_rmm/Kconfig
 create mode 100644 drivers/firmware/arm_rmm/Makefile
 delete mode 100644 arch/arm64/include/asm/rsi.h
 rename arch/arm64/include/asm/rsi_cmds.h => include/linux/arm-rsi-cmds.h (64%)
 rename arch/arm64/include/asm/rsi_smc.h => include/linux/arm-smccc-rsi.h (90%)
 rename {arch/arm64/kernel => drivers/firmware/arm_rmm}/rsi.c (99%)
 create mode 100644 drivers/iommu/iommu-cc.c

base-commit: cee9395acd8043be0644b25c34bfa86623f2b935

Signature · 1 line
2.43.0
#1 lore raw

PATCH 1/8 firmware: arm_rmm: Move RSI support out of arch/arm64

From: "Aneesh Kumar K.V (Arm)" <[email protected]>

The RSI SMCCC function IDs describe a firmware ABI and are not arm64 architecture specific definitions. Follow-up changes need to use them from non-arch code, including drivers/firmware/smccc and the Arm CCA guest driver.

Move the complete Realm Service Interface (RSI) implementation from arch/arm64 to drivers/firmware/arm_rmm. The RSI SMCCC definitions and command helpers are also moved to include/linux so they can be shared by architecture code and firmware or driver code. This also keeps the firmware interface outside architecture code, as requested [1].

[1] https://lore.kernel.org/all/agsNO9cc7H-b0H8L@willie-the-truck

---
 arch/arm64/Kconfig                            |  1 +
 drivers/firmware/Kconfig                      |  1 +
 drivers/firmware/arm_rmm/Kconfig              | 18 +++++
 drivers/virt/coco/arm-cca-guest/Kconfig       |  2 +-
 arch/arm64/kernel/Makefile                    |  2 +-
 drivers/firmware/Makefile                     |  1 +
 drivers/firmware/arm_rmm/Makefile             |  2 +
 arch/arm64/include/asm/io.h                   |  2 +-
 arch/arm64/include/asm/mem_encrypt.h          |  2 +-
 arch/arm64/include/asm/pgtable-prot.h         |  2 +-
 arch/arm64/include/asm/rsi.h                  | 70 -----------------
 .../linux/arm-rsi-cmds.h                      | 77 +++++++++++++++++--
 .../linux/arm-smccc-rsi.h                     |  6 +-
 arch/arm64/kernel/setup.c                     |  2 +-
 arch/arm64/mm/init.c                          |  3 +-
 .../kernel => drivers/firmware/arm_rmm}/rsi.c |  2 +-
 .../virt/coco/arm-cca-guest/arm-cca-guest.c   |  5 +-
 17 files changed, 109 insertions(+), 89 deletions(-)
 create mode 100644 drivers/firmware/arm_rmm/Kconfig
 create mode 100644 drivers/firmware/arm_rmm/Makefile
 delete mode 100644 arch/arm64/include/asm/rsi.h
 rename arch/arm64/include/asm/rsi_cmds.h => include/linux/arm-rsi-cmds.h (69%)
 rename arch/arm64/include/asm/rsi_smc.h => include/linux/arm-smccc-rsi.h (98%)
 rename {arch/arm64/kernel => drivers/firmware/arm_rmm}/rsi.c (99%)
arch/arm64/Kconfig +1 -0
index b5a51b0ef9440..ff9565d3ffa59 100644
@@ -38,6 +38,7 @@ config ARM64
 	select ARCH_HAS_MEMBARRIER_SYNC_CORE
 	select ARCH_HAS_MEM_ENCRYPT
 	select ARCH_SUPPORTS_MSEAL_SYSTEM_MAPPINGS
+	select ARCH_SUPPORTS_RMM
 	select ARCH_HAS_NMI_SAFE_THIS_CPU_OPS
 	select ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE
 	select ARCH_HAS_NONLEAF_PMD_YOUNG if ARM64_HAFT
drivers/firmware/Kconfig +1 -0
index b7cc11e4fbfa6..62660bf520a8d 100644
@@ -310,5 +310,6 @@ source "drivers/firmware/samsung/Kconfig"
 source "drivers/firmware/smccc/Kconfig"
 source "drivers/firmware/tegra/Kconfig"
 source "drivers/firmware/xilinx/Kconfig"
+source "drivers/firmware/arm_rmm/Kconfig"
 
 endmenu
drivers/firmware/arm_rmm/Kconfig new file +18 -0
new file mode 100644
index 0000000000000..1322a1447b560
@@ -0,0 +1,18 @@
+
+config ARCH_SUPPORTS_RMM
+	bool
+
+config ARM_RMM
+	bool "Realm Management Monitor (RMM) Support"
+	depends on ARCH_SUPPORTS_RMM
+	default y
+	help
+	  Support the Realm Management Monitor (RMM) on Arm systems that
+	  implement the Realm Management Extension (RME), as defined by the
+	  Arm Confidential Compute Architecture.
+
+	  The RMM provides the Realm Management Interface (RMI) for use by
+	  the Normal World host and the Realm Service Interface (RSI) for
+	  use by Realm guests. The RSI is used to negotiate the RSI version
+	  and Realm configuration with the RMM and manage the protected
+	  state of memory.
drivers/virt/coco/arm-cca-guest/Kconfig +1 -1
index 3f0f013f03f15..b2c225fd445cf 100644
@@ -1,6 +1,6 @@
 config ARM_CCA_GUEST
 	tristate "Arm CCA Guest driver"
-	depends on ARM64
+	depends on ARM_RMM
 	select TSM_REPORTS
 	help
 	  The driver provides userspace interface to request and
arch/arm64/kernel/Makefile +1 -1
index d2690c3ec5288..21fc3a3d25c91 100644
@@ -34,7 +34,7 @@ obj-y			:= debug-monitors.o entry.o irq.o fpsimd.o		\
 			   cpufeature.o alternative.o cacheinfo.o		\
 			   smp.o smp_spin_table.o topology.o smccc-call.o	\
 			   syscall.o proton-pack.o idle.o patching.o pi/	\
-			   rsi.o jump_label.o
+			   jump_label.o
 
 obj-$(CONFIG_COMPAT)			+= sys32.o signal32.o			\
 					   sys_compat.o
drivers/firmware/Makefile +1 -0
index be46f1e1dc77f..196a650ccf025 100644
@@ -39,3 +39,4 @@ obj-y				+= samsung/
 obj-y				+= smccc/
 obj-y				+= tegra/
 obj-y				+= xilinx/
+obj-y				+= arm_rmm/
drivers/firmware/arm_rmm/Makefile new file +2 -0
new file mode 100644
index 0000000000000..3d8181ae94a7b
@@ -0,0 +1,2 @@
+
+obj-$(CONFIG_ARM_RMM)	= rsi.o
arch/arm64/include/asm/io.h +1 -1
index 49a7002661a92..1620537f0332d 100644
@@ -8,6 +8,7 @@
 #ifndef __ASM_IO_H
 #define __ASM_IO_H
 
+#include <linux/arm-rsi-cmds.h>
 #include <linux/types.h>
 #include <linux/pgtable.h>
 
@@ -17,7 +18,6 @@
 #include <asm/early_ioremap.h>
 #include <asm/alternative.h>
 #include <asm/cpufeature.h>
-#include <asm/rsi.h>
 
 /*
  * Generic IO read/write.  These perform native-endian accesses.
arch/arm64/include/asm/mem_encrypt.h +1 -1
index 636f45b4d8afc..f03b9d7b83b43 100644
@@ -2,8 +2,8 @@
 #ifndef __ASM_MEM_ENCRYPT_H
 #define __ASM_MEM_ENCRYPT_H
 
+#include <linux/arm-rsi-cmds.h>
 #include <asm/hypervisor.h>
-#include <asm/rsi.h>
 
 struct device;
 
arch/arm64/include/asm/pgtable-prot.h +1 -1
index 09d7c00cf4054..58e71e592175e 100644
@@ -66,9 +66,9 @@
 
 #ifndef __ASSEMBLER__
 
+#include <linux/arm-rsi-cmds.h>
 #include <asm/cpufeature.h>
 #include <asm/pgtable-types.h>
-#include <asm/rsi.h>
 
 extern bool arm64_use_ng_mappings;
 extern unsigned long prot_ns_shared;
arch/arm64/include/asm/rsi.h deleted +0 -70

70 lines not on this page · raw message

include/linux/arm-rsi-cmds.h renamed from arch/arm64/include/asm/rsi_cmds.h +72 -5

96 lines not on this page · raw message

include/linux/arm-smccc-rsi.h renamed from arch/arm64/include/asm/rsi_smc.h +3 -3
similarity index 98%
rename from arch/arm64/include/asm/rsi_smc.h
rename to include/linux/arm-smccc-rsi.h
index e19253f96c940..fddb77986f706 100644
@@ -3,8 +3,8 @@
  * Copyright (C) 2023 ARM Ltd.
  */
 
-#ifndef __ASM_RSI_SMC_H_
-#define __ASM_RSI_SMC_H_
+#ifndef __LINUX_ARM_SMCCC_RSI_H_
+#define __LINUX_ARM_SMCCC_RSI_H_
 
 #include <linux/arm-smccc.h>
 
@@ -190,4 +190,4 @@ struct realm_config {
  */
 #define SMC_RSI_HOST_CALL			SMC_RSI_FID(0x199)
 
-#endif /* __ASM_RSI_SMC_H_ */
+#endif /* __LINUX_ARM_SMCCC_RSI_H_ */
arch/arm64/kernel/setup.c +1 -1
index 29c6100f0c50b..a707deba6ae70 100644
@@ -7,6 +7,7 @@
  */
 
 #include <linux/acpi.h>
+#include <linux/arm-rsi-cmds.h>
 #include <linux/export.h>
 #include <linux/kernel.h>
 #include <linux/stddef.h>
@@ -43,7 +44,6 @@
 #include <asm/cpu_ops.h>
 #include <asm/kasan.h>
 #include <asm/numa.h>
-#include <asm/rsi.h>
 #include <asm/scs.h>
 #include <asm/sections.h>
 #include <asm/setup.h>
arch/arm64/mm/init.c +2 -1
index fbf215ecc7d06..2a8de20d2b447 100644
@@ -34,6 +34,8 @@
 #include <linux/acpi_iort.h>
 #include <linux/kmemleak.h>
 #include <linux/execmem.h>
+#include <linux/arm-rsi-cmds.h>
+
 
 #include <asm/boot.h>
 #include <asm/fixmap.h>
@@ -43,7 +45,6 @@
 #include <asm/kvm_host.h>
 #include <asm/memory.h>
 #include <asm/numa.h>
-#include <asm/rsi.h>
 #include <asm/sections.h>
 #include <asm/setup.h>
 #include <linux/sizes.h>
drivers/firmware/arm_rmm/rsi.c renamed from arch/arm64/kernel/rsi.c +1 -1
similarity index 99%
rename from arch/arm64/kernel/rsi.c
rename to drivers/firmware/arm_rmm/rsi.c
index 25ca75ce1a4df..8e716f1c1e319 100644
@@ -8,11 +8,11 @@
 #include <linux/psci.h>
 #include <linux/swiotlb.h>
 #include <linux/platform_device.h>
+#include <linux/arm-rsi-cmds.h>
 
 #include <asm/io.h>
 #include <asm/mem_encrypt.h>
 #include <asm/pgtable.h>
-#include <asm/rsi.h>
 
 static struct realm_config config;
 
drivers/virt/coco/arm-cca-guest/arm-cca-guest.c +2 -3
index 0eeddd1ff05b5..b741643d13647 100644
@@ -3,7 +3,8 @@
  * Copyright (C) 2023 ARM Ltd.
  */
 
-#include <linux/arm-smccc.h>
+#include <linux/arm-rsi-cmds.h>
+#include <linux/arm-smccc-rsi.h>
 #include <linux/cc_platform.h>
 #include <linux/kernel.h>
 #include <linux/device-id/platform.h>
@@ -12,8 +13,6 @@
 #include <linux/tsm.h>
 #include <linux/types.h>
 
-#include <asm/rsi.h>
-
 /**
  * struct arm_cca_token_info - a descriptor for the token buffer.
  * @granule:		PA of the granule to which the token will be written
Signature · 1 line
2.43.0
#2 lore raw

PATCH 2/8 firmware: arm_rmm: Add VSMMU commands and fields

In reply to Nicolin Chen

DEN0137 2.0-bet3 adds the RSI_VSMMU_GET_INFO command and replaces the old RSI_VSMMU_ACTIVATE command with RSI_ARCH_DEV_ACTIVATE.

Add the command IDs, return codes, SMMUv3 device type, and helpers for both commands.

Assisted-by Codex:gpt-5.6-sol
---
 include/linux/arm-rsi-cmds.h  | 22 ++++++++++++++++++++++
 include/linux/arm-smccc-rsi.h | 20 ++++++++++++++++++++
 2 files changed, 42 insertions(+)
include/linux/arm-rsi-cmds.h +22 -0
index 13478e14c4296..548ab0bcac722 100644
@@ -237,4 +237,26 @@ static inline unsigned long rsi_attestation_token_continue(phys_addr_t granule,
 	return res.a0;
 }
 
+static inline unsigned long rsi_vsmmu_get_info(phys_addr_t addr,
+					       phys_addr_t *top)
+{
+	struct arm_smccc_res res;
+
+	arm_smccc_1_1_invoke(SMC_RSI_VSMMU_GET_INFO, addr, &res);
+	if (res.a0 == RSI_SUCCESS && top)
+		*top = res.a1;
+
+	return res.a0;
+}
+
+static inline unsigned long rsi_arch_dev_activate(phys_addr_t base,
+						  unsigned long dev_type)
+{
+	struct arm_smccc_res res;
+
+	arm_smccc_1_1_invoke(SMC_RSI_ARCH_DEV_ACTIVATE, base, dev_type, &res);
+
+	return res.a0;
+}
+
 #endif /* __LINUX_ARM_RSI_CMDS_H_ */
include/linux/arm-smccc-rsi.h +20 -0
index fddb77986f706..2c16c23611891 100644
@@ -37,6 +37,7 @@
 #define RSI_ERROR_STATE		UL(2)
 #define RSI_INCOMPLETE		UL(3)
 #define RSI_ERROR_UNKNOWN	UL(4)
+#define RSI_ERROR_DEVICE	UL(5)
 
 #define SMC_RSI_FID(n)		ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL,      \
 						   ARM_SMCCC_SMC_64,         \
@@ -190,4 +191,23 @@ struct realm_config {
  */
 #define SMC_RSI_HOST_CALL			SMC_RSI_FID(0x199)
 
+/*
+ * Get information about a VSMMU.
+ *
+ * arg1 == Base IPA of the VSMMU
+ * ret0 == Status / error
+ * ret1 == Top IPA of the VSMMU
+ */
+#define SMC_RSI_VSMMU_GET_INFO			SMC_RSI_FID(0x19A)
+
+#define RSI_ARCH_DEV_SMMUV3			UL(0)
+
+/*
+ * Activate an architectural device.
+ *
+ * arg1 == Base IPA of the architectural device
+ * arg2 == Architectural device type
+ * ret0 == Status / error
+ */
+#define SMC_RSI_ARCH_DEV_ACTIVATE		SMC_RSI_FID(0x19B)
 #endif /* __LINUX_ARM_SMCCC_RSI_H_ */
Signature · 1 line
2.43.0
#3 lore raw

RE: [PATCH v1 2/8] firmware: arm_rmm: Add VSMMU commands and fields

Quoted 77 lines

-----Original Message-----
From: Nicolin Chen <[email protected]>
Sent: 10 September 2026 04:33
To: Jason Gunthorpe <[email protected]>; Catalin Marinas <[email protected]>; Will Deacon <[email protected]>; [email protected]; Robin Murphy <[email protected]>; [email protected]; Danilo Krummrich <[email protected]>; Marek Szyprowski <[email protected]>; [email protected]
Cc: Mark Rutland <[email protected]>; Greg Kroah-Hartman <[email protected]>; Suzuki K Poulose <[email protected]>; Gavin Shan <[email protected]>; Vikram Sethi <[email protected]>; Anshuman Khandual <[email protected]>; Shanker Donthineni <[email protected]>; Mostafa Saleh <[email protected]>; [email protected]; Thomas Huth <[email protected]>; Marc Zyngier <[email protected]>; Ryan Roberts <[email protected]>; [email protected]; Kohei Enju <[email protected]>; Shaopeng Tan <[email protected]>; Ard Biesheuvel <[email protected]>; James Morse <[email protected]>; Steven Price <[email protected]>; Sang-Heon Jeon <[email protected]>; Omar Sandoval <[email protected]>; Andrew Morton <[email protected]>; Jinjie Ruan <[email protected]>; Sam Edwards <[email protected]>; Douglas Anderson <[email protected]>; Florian Fainelli <[email protected]>; Chen-Yu Tsai <[email protected]>; Huacai Chen <[email protected]>; Thomas Zimmermann <[email protected]>; Pranjal Shrivastava <[email protected]>; Ashish Mhetre <[email protected]>; Shameer Kolothum Thodi <[email protected]>; [email protected]; linux- [email protected]; [email protected]; [email protected]; Sonang Patel <[email protected]>; Ankit Agrawal <[email protected]>
Subject: [PATCH v1 2/8] firmware: arm_rmm: Add VSMMU commands and fields

DEN0137 2.0-bet3 adds the RSI_VSMMU_GET_INFO command and replaces the old
RSI_VSMMU_ACTIVATE command with RSI_ARCH_DEV_ACTIVATE.

Add the command IDs, return codes, SMMUv3 device type, and helpers for both
commands.

Assisted-by Codex:gpt-5.6-sol
---
 include/linux/arm-rsi-cmds.h  | 22 ++++++++++++++++++++++
 include/linux/arm-smccc-rsi.h | 20 ++++++++++++++++++++
 2 files changed, 42 insertions(+)
include/linux/arm-rsi-cmds.h +18 -0
index 13478e14c4296..548ab0bcac722 100644
@@ -237,4 +237,26 @@ static inline unsigned long
 rsi_attestation_token_continue(phys_addr_t granule,
 	return res.a0;
 }
 
+static inline unsigned long rsi_vsmmu_get_info(phys_addr_t addr,
+					       phys_addr_t *top)
+{
+	struct arm_smccc_res res;
+
+	arm_smccc_1_1_invoke(SMC_RSI_VSMMU_GET_INFO, addr, &res);
+	if (res.a0 == RSI_SUCCESS && top)
+		*top = res.a1;
+
+	return res.a0;
+}
+
+static inline unsigned long rsi_arch_dev_activate(phys_addr_t base,
+						  unsigned long dev_type)
+{
+	struct arm_smccc_res res;
+
+	arm_smccc_1_1_invoke(SMC_RSI_ARCH_DEV_ACTIVATE, base,
Quoted 1 line

dev_type, &res);

These calls leave unused input registers unspecified, whereas DEN0137 bet3 B1.2 marks them SBZ. Could we explicitly zero them?

"On calling an RMI or RSI command, any of X1- X16 which are not
specified as input values in the command definition SBZ"

Thanks,
Shameer

#4 lore raw

Re: [PATCH v1 2/8] firmware: arm_rmm: Add VSMMU commands and fields

Quoted 11 lines · Shameer Kolothum Thodi
Quoted 7 lines
include/linux/arm-rsi-cmds.h +6 -0
+static inline unsigned long rsi_arch_dev_activate(phys_addr_t base,
+						  unsigned long dev_type)
+{
+	struct arm_smccc_res res;
+
+	arm_smccc_1_1_invoke(SMC_RSI_ARCH_DEV_ACTIVATE, base,

dev_type, &res);

These calls leave unused input registers unspecified, whereas DEN0137 bet3 B1.2 marks them SBZ. Could we explicitly zero them?

Quoted 2 lines

"On calling an RMI or RSI command, any of X1- X16 which are not
specified as input values in the command definition SBZ"

That seems too broad, Linux doesn't even support the x8-x16 inputs in the macros. I think the spec has errd here.

Requiring zeroing of x0-x7 is sort of reasonable. Any future use of x8-x16 (including outputs) should require a direct opt in of the OS to indicate it supports this bank of registers.

Otherwise you probably need a new macro helper for this protocol..

Jason

#5 lore raw

Re: [PATCH v1 2/8] firmware: arm_rmm: Add VSMMU commands and fields

Quoted 15 lines · Jason Gunthorpe
Quoted 11 lines · Shameer Kolothum Thodi
Quoted 7 lines
include/linux/arm-rsi-cmds.h +6 -0
+static inline unsigned long rsi_arch_dev_activate(phys_addr_t base,
+						  unsigned long dev_type)
+{
+	struct arm_smccc_res res;
+
+	arm_smccc_1_1_invoke(SMC_RSI_ARCH_DEV_ACTIVATE, base,

dev_type, &res);

These calls leave unused input registers unspecified, whereas DEN0137 bet3 B1.2 marks them SBZ. Could we explicitly zero them?

Quoted 2 lines

"On calling an RMI or RSI command, any of X1- X16 which are not
specified as input values in the command definition SBZ"

Quoted 8 lines

That seems too broad, Linux doesn't even support the x8-x16 inputs in the macros. I think the spec has errd here.

Requiring zeroing of x0-x7 is sort of reasonable. Any future use of x8-x16 (including outputs) should require a direct opt in of the OS to indicate it supports this bank of registers.

Otherwise you probably need a new macro helper for this protocol..

We could probably replace it with:

	arm_smccc_smc(SMC_RSI_ARCH_DEV_ACTIVATE, base, dev_type, 0, 0, 0, 0, 0,
		      &res);

This should specify those unused inputs.

Nicolin

#6 lore raw

RE: [PATCH v1 2/8] firmware: arm_rmm: Add VSMMU commands and fields

Quoted 59 lines

-----Original Message-----
From: Nicolin Chen <[email protected]>
Sent: 28 October 2026 18:38
To: Jason Gunthorpe <[email protected]>
Cc: Shameer Kolothum Thodi <[email protected]>; Catalin Marinas <[email protected]>; Will Deacon <[email protected]>; [email protected]; Robin Murphy <[email protected]>; [email protected]; Danilo Krummrich <[email protected]>; Marek Szyprowski <[email protected]>; [email protected]; Mark Rutland <[email protected]>; Greg Kroah-Hartman <[email protected]>; Suzuki K Poulose <[email protected]>; Gavin Shan <[email protected]>; Vikram Sethi <[email protected]>; Anshuman Khandual <[email protected]>; Shanker Donthineni <[email protected]>; Mostafa Saleh <[email protected]>; [email protected]; Thomas Huth <[email protected]>; Marc Zyngier <[email protected]>; Ryan Roberts <[email protected]>; [email protected]; Kohei Enju <[email protected]>; Shaopeng Tan <[email protected]>; Ard Biesheuvel <[email protected]>; James Morse <[email protected]>; Steven Price <[email protected]>; Sang-Heon Jeon <[email protected]>; Omar Sandoval <[email protected]>; Andrew Morton <[email protected]>; Jinjie Ruan <[email protected]>; Sam Edwards <[email protected]>; Douglas Anderson <[email protected]>; Florian Fainelli <[email protected]>; Chen-Yu Tsai <[email protected]>; Huacai Chen <[email protected]>; Thomas Zimmermann <[email protected]>; Pranjal Shrivastava <[email protected]>; Ashish Mhetre <[email protected]>; [email protected]; linux- [email protected]; [email protected]; [email protected]; Sonang Patel <[email protected]>; Ankit Agrawal <[email protected]>
Subject: Re: [PATCH v1 2/8] firmware: arm_rmm: Add VSMMU commands and fields

Quoted 1 line · Jason Gunthorpe

On Wed, Oct 28, 2026 at 09:49:53AM +0000, Shameer Kolothum Thodi

Quoted 21 lines · wrote:
Quoted 11 lines
Quoted 7 lines
include/linux/arm-rsi-cmds.h +6 -0
+static inline unsigned long rsi_arch_dev_activate(phys_addr_t base,
+						  unsigned long dev_type)
+{
+	struct arm_smccc_res res;
+
+	arm_smccc_1_1_invoke(SMC_RSI_ARCH_DEV_ACTIVATE, base,

dev_type, &res);

These calls leave unused input registers unspecified, whereas DEN0137 bet3 B1.2 marks them SBZ. Could we explicitly zero them?

Quoted 2 lines

"On calling an RMI or RSI command, any of X1- X16 which are not
specified as input values in the command definition SBZ"

That seems too broad, Linux doesn't even support the x8-x16 inputs in the macros. I think the spec has errd here.

Requiring zeroing of x0-x7 is sort of reasonable. Any future use of x8-x16 (including outputs) should require a direct opt in of the OS to indicate it supports this bank of registers.

Quoted 8 lines
Quoted 1 line

Otherwise you probably need a new macro helper for this protocol..

We could probably replace it with:
arm_smccc_smc(SMC_RSI_ARCH_DEV_ACTIVATE, base, dev_type, 0,
0, 0, 0, 0,

		      &res);

This should specify those unused inputs.

Or we could use arm_smccc_1_2_smc() with zero initialized registers, as rsi_attestation_token_init() does. I think that would cover X8-X16 too.

But other RSI wrappers don't do that consistently, so it's fine either way, I guess.

Thanks,
Shameer

#7 lore raw

Re: [PATCH v1 2/8] firmware: arm_rmm: Add VSMMU commands and fields

Quoted 31 lines · Shameer Kolothum Thodi
Quoted 31 lines · Jason Gunthorpe
Quoted 1 line

On Wed, Oct 28, 2026 at 09:49:53AM +0000, Shameer Kolothum Thodi

Quoted 14 lines · wrote:
Quoted 11 lines
Quoted 7 lines
include/linux/arm-rsi-cmds.h +6 -0
+static inline unsigned long rsi_arch_dev_activate(phys_addr_t base,
+						  unsigned long dev_type)
+{
+	struct arm_smccc_res res;
+
+	arm_smccc_1_1_invoke(SMC_RSI_ARCH_DEV_ACTIVATE, base,

dev_type, &res);

These calls leave unused input registers unspecified, whereas DEN0137 bet3 B1.2 marks them SBZ. Could we explicitly zero them?

Quoted 2 lines

"On calling an RMI or RSI command, any of X1- X16 which are not
specified as input values in the command definition SBZ"

Quoted 8 lines

That seems too broad, Linux doesn't even support the x8-x16 inputs in the macros. I think the spec has errd here.

Requiring zeroing of x0-x7 is sort of reasonable. Any future use of x8-x16 (including outputs) should require a direct opt in of the OS to indicate it supports this bank of registers.

Otherwise you probably need a new macro helper for this protocol..

We could probably replace it with:
arm_smccc_smc(SMC_RSI_ARCH_DEV_ACTIVATE, base, dev_type, 0,
0, 0, 0, 0,

		      &res);
Quoted 4 lines
Quoted 1 line

This should specify those unused inputs.

Or we could use arm_smccc_1_2_smc() with zero initialized registers, as rsi_attestation_token_init() does. I think that would cover X8-X16 too.

Yeah, that seems to be the right thing to do.

Quoted 2 lines

But other RSI wrappers don't do that consistently, so it's fine either way, I guess.

Seems like a bug, with the spec being specific here it should be fixed.

Jason

#8 lore raw

Re: [PATCH v1 2/8] firmware: arm_rmm: Add VSMMU commands and fields

Quoted 37 lines · Jason Gunthorpe
Quoted 31 lines · Shameer Kolothum Thodi
Quoted 31 lines · Jason Gunthorpe
Quoted 1 line

On Wed, Oct 28, 2026 at 09:49:53AM +0000, Shameer Kolothum Thodi

Quoted 14 lines · wrote:
Quoted 11 lines
Quoted 7 lines
include/linux/arm-rsi-cmds.h +6 -0
+static inline unsigned long rsi_arch_dev_activate(phys_addr_t base,
+						  unsigned long dev_type)
+{
+	struct arm_smccc_res res;
+
+	arm_smccc_1_1_invoke(SMC_RSI_ARCH_DEV_ACTIVATE, base,

dev_type, &res);

These calls leave unused input registers unspecified, whereas DEN0137 bet3 B1.2 marks them SBZ. Could we explicitly zero them?

Quoted 2 lines

"On calling an RMI or RSI command, any of X1- X16 which are not
specified as input values in the command definition SBZ"

Quoted 8 lines

That seems too broad, Linux doesn't even support the x8-x16 inputs in the macros. I think the spec has errd here.

Requiring zeroing of x0-x7 is sort of reasonable. Any future use of x8-x16 (including outputs) should require a direct opt in of the OS to indicate it supports this bank of registers.

Otherwise you probably need a new macro helper for this protocol..

We could probably replace it with:
arm_smccc_smc(SMC_RSI_ARCH_DEV_ACTIVATE, base, dev_type, 0,
0, 0, 0, 0,

		      &res);
Quoted 4 lines
Quoted 1 line

This should specify those unused inputs.

Or we could use arm_smccc_1_2_smc() with zero initialized registers, as rsi_attestation_token_init() does. I think that would cover X8-X16 too.

Quoted 7 lines

Yeah, that seems to be the right thing to do.

Quoted 2 lines

But other RSI wrappers don't do that consistently, so it's fine either way, I guess.

Seems like a bug, with the spec being specific here it should be fixed.

I'll use arm_smccc_1_2_smc() in v2.

Thanks
Nicolin

#9 lore raw

PATCH 3/8 dma-mapping: Let a device declare that it reaches private memory

In reply to Nicolin Chen

DMA devices in a confidential guest can access only shared memory, or both private and shared memory. Some method outside the DMA API will confirm the device is able to access private memory through DMA.

Add DEV_FLAG_DMA_CC_PRIVATE and dma_set_cc_private(dev, bool).

The flag must be stable while any DMA mapping exists.

Assisted-by Claude:claude-opus-5
---
 include/linux/device.h      |  4 ++++
 include/linux/dma-mapping.h |  4 ++++
 kernel/dma/mapping.c        | 21 +++++++++++++++++++++
 3 files changed, 29 insertions(+)
include/linux/device.h +4 -0
index aee79fd6b32b4..54ac6cb7f3762 100644
@@ -599,6 +599,8 @@ struct device_physical_location {
  *		ancestor device.
  * @DEV_FLAG_OFFLINE_DISABLED: If set, the device is permanently online.
  * @DEV_FLAG_OFFLINE: Set after successful invocation of bus type's .offline().
+ * @DEV_FLAG_DMA_CC_PRIVATE: The device is able to access private (encrypted)
+ *		memory, no shared memory bouncing is required from the DMA API.
  * @DEV_FLAG_COUNT: Number of defined struct_device_flags.
  */
 enum struct_device_flags {
@@ -612,6 +614,7 @@ enum struct_device_flags {
 	DEV_FLAG_OF_NODE_REUSED = 7,
 	DEV_FLAG_OFFLINE_DISABLED = 8,
 	DEV_FLAG_OFFLINE = 9,
+	DEV_FLAG_DMA_CC_PRIVATE = 10,
 
 	DEV_FLAG_COUNT
 };
@@ -829,6 +832,7 @@ __create_dev_flag_accessors(dma_coherent, DEV_FLAG_DMA_COHERENT);
 __create_dev_flag_accessors(of_node_reused, DEV_FLAG_OF_NODE_REUSED);
 __create_dev_flag_accessors(offline_disabled, DEV_FLAG_OFFLINE_DISABLED);
 __create_dev_flag_accessors(offline, DEV_FLAG_OFFLINE);
+__create_dev_flag_accessors(dma_cc_private, DEV_FLAG_DMA_CC_PRIVATE);
 
 #undef __create_dev_flag_accessors
 
include/linux/dma-mapping.h +4 -0
index a3e880649fa41..8d23b983092e8 100644
@@ -218,6 +218,7 @@ void *dma_vmap_noncontiguous(struct device *dev, size_t size,
 void dma_vunmap_noncontiguous(struct device *dev, void *vaddr);
 int dma_mmap_noncontiguous(struct device *dev, struct vm_area_struct *vma,
 		size_t size, struct sg_table *sgt);
+void dma_set_cc_private(struct device *dev, bool private);
 #else /* CONFIG_HAS_DMA */
 static inline dma_addr_t dma_map_page_attrs(struct device *dev,
 		struct page *page, size_t offset, size_t size,
@@ -357,6 +358,9 @@ static inline int dma_mmap_noncontiguous(struct device *dev,
 {
 	return -EINVAL;
 }
+static inline void dma_set_cc_private(struct device *dev, bool private)
+{
+}
 #endif /* CONFIG_HAS_DMA */
 
 #ifdef CONFIG_IOMMU_DMA
kernel/dma/mapping.c +21 -0
index bf2651a70b7c2..11c127ad45370 100644
@@ -501,6 +501,27 @@ static void dma_setup_need_sync(struct device *dev)
 static inline void dma_setup_need_sync(struct device *dev) { }
 #endif /* !CONFIG_DMA_NEED_SYNC */
 
+/**
+ * dma_set_cc_private - Set whether @dev can DMA to private memory
+ * @dev: device whose confidential-computing DMA access is being updated
+ * @private: whether the device can DMA to private memory
+ *
+ * Called when a device is known to be able to DMA to private memory.
+ * For architected CPU integrated devices their kernel drivers will
+ * self-accept using a driver specific validation. Other devices will
+ * have a userspace managed process.
+ *
+ * DMA mapping must not be active when this flag is changed. For CPU
+ * integrated devices the driver should self accept early during probe
+ * after validation. Other devices have this flag set automatically
+ * before probing.
+ */
+void dma_set_cc_private(struct device *dev, bool private)
+{
+	dev_assign_dma_cc_private(dev, private);
+}
+EXPORT_SYMBOL_GPL(dma_set_cc_private);
+
 /*
  * The whole dma_get_sgtable() idea is fundamentally unsafe - it seems
  * that the intention is to allow exporting memory allocated via the
Signature · 1 line
2.43.0
#10 lore raw

PATCH 4/8 dma-mapping: Keep DMA memory private for capable devices

In reply to Nicolin Chen

Architecture code can require devices to use unencrypted memory. This makes the DMA API use shared memory for coherent allocations and bounce streaming mappings through SWIOTLB.

A device marked with DEV_FLAG_DMA_CC_PRIVATE can reach private memory directly. Add dma_require_decrypted() to combine force_dma_unencrypted() with the new device flag, and use it throughout the generic DMA code.

Assisted-by Codex:gpt-5.6-sol
---
 include/linux/dma-direct.h |  7 ++++++-
 kernel/dma/direct.c        | 16 ++++++++--------
 kernel/dma/mapping.c       |  2 +-
 kernel/dma/swiotlb.c       |  2 +-
 4 files changed, 16 insertions(+), 11 deletions(-)
include/linux/dma-direct.h +6 -1
index daa31a1adf7b0..fb35ca27941cc 100644
@@ -134,6 +134,11 @@ static inline bool force_dma_unencrypted(struct device *dev)
 }
 #endif /* CONFIG_ARCH_HAS_FORCE_DMA_UNENCRYPTED */
 
+static inline bool dma_require_decrypted(struct device *dev)
+{
+	return !dev_dma_cc_private(dev) && force_dma_unencrypted(dev);
+}
+
 static inline bool dma_capable(struct device *dev, dma_addr_t addr, size_t size,
 		bool is_ram, unsigned long attrs)
 {
@@ -146,7 +151,7 @@ static inline bool dma_capable(struct device *dev, dma_addr_t addr, size_t size,
 	 * requires unencrypted DMA addresses. Treat it as not DMA-capable
 	 * so the caller can fall back to a suitable SWIOTLB pool.
 	 */
-	if (!(attrs & DMA_ATTR_CC_SHARED) && force_dma_unencrypted(dev))
+	if (!(attrs & DMA_ATTR_CC_SHARED) && dma_require_decrypted(dev))
 		return false;
 
 	if (is_ram && !IS_ENABLED(CONFIG_ARCH_DMA_ADDR_T_64BIT) &&
kernel/dma/direct.c +8 -8
index da665ca22d5c0..fbddab9740162 100644
@@ -41,7 +41,7 @@ static inline struct page *dma_direct_to_page(struct device *dev,
 
 u64 dma_direct_get_required_mask(struct device *dev)
 {
-	bool require_decrypted = force_dma_unencrypted(dev);
+	bool require_decrypted = dma_require_decrypted(dev);
 	phys_addr_t phys = ((phys_addr_t)max_pfn << PAGE_SHIFT) - 1;
 	u64 max_dma = phys_to_dma_direct(dev, phys, require_decrypted);
 
@@ -72,7 +72,7 @@ static gfp_t dma_direct_optimal_gfp_mask(struct device *dev, u64 *phys_limit)
 
 bool dma_coherent_ok(struct device *dev, phys_addr_t phys, size_t size)
 {
-	bool require_decrypted = force_dma_unencrypted(dev);
+	bool require_decrypted = dma_require_decrypted(dev);
 	dma_addr_t dma_addr = phys_to_dma_direct(dev, phys, require_decrypted);
 
 	if (dma_addr == DMA_MAPPING_ERROR)
@@ -208,7 +208,7 @@ void *dma_direct_alloc(struct device *dev, size_t size,
 	struct page *page;
 	void *cpu_addr;
 
-	if (force_dma_unencrypted(dev))
+	if (dma_require_decrypted(dev))
 		attrs |= __DMA_ATTR_ALLOC_CC_SHARED;
 
 	if (attrs & __DMA_ATTR_ALLOC_CC_SHARED) {
@@ -368,7 +368,7 @@ void dma_direct_free(struct device *dev, size_t size,
 	 * If the allocation used decrypted/shared backing pages, restore
 	 * the encryption state on free.
 	 */
-	if (force_dma_unencrypted(dev))
+	if (dma_require_decrypted(dev))
 		attrs |= __DMA_ATTR_ALLOC_CC_SHARED;
 
 	if (attrs & __DMA_ATTR_ALLOC_CC_SHARED)
@@ -436,7 +436,7 @@ struct page *dma_direct_alloc_pages(struct device *dev, size_t size,
 	struct page *page;
 	void *cpu_addr;
 
-	if (force_dma_unencrypted(dev))
+	if (dma_require_decrypted(dev))
 		attrs |= __DMA_ATTR_ALLOC_CC_SHARED;
 
 	if ((attrs & __DMA_ATTR_ALLOC_CC_SHARED) && dma_direct_use_pool(dev, gfp))
@@ -480,7 +480,7 @@ void dma_direct_free_pages(struct device *dev, size_t size,
 	 * if the device had requested for an unencrypted buffer,
 	 * convert it to encrypted on free
 	 */
-	bool mark_mem_encrypted = force_dma_unencrypted(dev);
+	bool mark_mem_encrypted = dma_require_decrypted(dev);
 
 	/* If page is not from an atomic pool, dma_free_from_pool_page() fails */
 	if (IS_ENABLED(CONFIG_DMA_COHERENT_POOL) &&
@@ -649,7 +649,7 @@ int dma_direct_mmap(struct device *dev, struct vm_area_struct *vma,
 	const pgoff_t pgoff_end = vma_end_pgoff(vma);
 	int ret = -ENXIO;
 
-	if (force_dma_unencrypted(dev))
+	if (dma_require_decrypted(dev))
 		attrs |= DMA_ATTR_CC_SHARED;
 
 	vma->vm_page_prot = dma_pgprot(dev, vma->vm_page_prot, attrs);
@@ -798,7 +798,7 @@ size_t dma_direct_max_mapping_size(struct device *dev)
 	/* If SWIOTLB is active, use its maximum mapping size */
 	if (is_swiotlb_active(dev) &&
 	    (dma_addressing_limited(dev) || is_swiotlb_force_bounce(dev) ||
-	     force_dma_unencrypted(dev)))
+	     dma_require_decrypted(dev)))
 		return swiotlb_max_mapping_size(dev);
 
 	return SIZE_MAX;
kernel/dma/mapping.c +1 -1
index 11c127ad45370..e7c48a925b7ac 100644
@@ -673,7 +673,7 @@ void *dma_alloc_attrs(struct device *dev, size_t size, dma_addr_t *dma_handle,
 		return NULL;
 	}
 
-	if (force_dma_unencrypted(dev))
+	if (dma_require_decrypted(dev))
 		attrs |= __DMA_ATTR_ALLOC_CC_SHARED;
 
 	if (dma_alloc_from_dev_coherent(dev, size, dma_handle, &cpu_addr)) {
kernel/dma/swiotlb.c +1 -1
index ded7016a46a71..d08a3643ded14 100644
@@ -1552,7 +1552,7 @@ phys_addr_t swiotlb_tbl_map_single(struct device *dev, phys_addr_t orig_addr,
 	if (cc_platform_has(CC_ATTR_GUEST_MEM_ENCRYPT)) {
 
 		/* swiotlb pool is incorrect for this device */
-		if (unlikely(mem->cc_shared != force_dma_unencrypted(dev)))
+		if (unlikely(mem->cc_shared != dma_require_decrypted(dev)))
 			return (phys_addr_t)DMA_MAPPING_ERROR;
 
 	} else if (cc_platform_has(CC_ATTR_HOST_MEM_ENCRYPT)) {
Signature · 1 line
2.43.0
#11 lore raw

RE: [PATCH v1 4/8] dma-mapping: Keep DMA memory private for capable devices

Quoted 189 lines

-----Original Message-----
From: Nicolin Chen <[email protected]>
Sent: 10 September 2026 04:33
To: Jason Gunthorpe <[email protected]>; Catalin Marinas <[email protected]>; Will Deacon <[email protected]>; [email protected]; Robin Murphy <[email protected]>; [email protected]; Danilo Krummrich <[email protected]>; Marek Szyprowski <[email protected]>; [email protected]
Cc: Mark Rutland <[email protected]>; Greg Kroah-Hartman <[email protected]>; Suzuki K Poulose <[email protected]>; Gavin Shan <[email protected]>; Vikram Sethi <[email protected]>; Anshuman Khandual <[email protected]>; Shanker Donthineni <[email protected]>; Mostafa Saleh <[email protected]>; [email protected]; Thomas Huth <[email protected]>; Marc Zyngier <[email protected]>; Ryan Roberts <[email protected]>; [email protected]; Kohei Enju <[email protected]>; Shaopeng Tan <[email protected]>; Ard Biesheuvel <[email protected]>; James Morse <[email protected]>; Steven Price <[email protected]>; Sang-Heon Jeon <[email protected]>; Omar Sandoval <[email protected]>; Andrew Morton <[email protected]>; Jinjie Ruan <[email protected]>; Sam Edwards <[email protected]>; Douglas Anderson <[email protected]>; Florian Fainelli <[email protected]>; Chen-Yu Tsai <[email protected]>; Huacai Chen <[email protected]>; Thomas Zimmermann <[email protected]>; Pranjal Shrivastava <[email protected]>; Ashish Mhetre <[email protected]>; Shameer Kolothum Thodi <[email protected]>; [email protected]; linux- [email protected]; [email protected]; [email protected]; Sonang Patel <[email protected]>; Ankit Agrawal <[email protected]>
Subject: [PATCH v1 4/8] dma-mapping: Keep DMA memory private for capable devices

Architecture code can require devices to use unencrypted memory. This makes the DMA API use shared memory for coherent allocations and bounce streaming
mappings through SWIOTLB.

A device marked with DEV_FLAG_DMA_CC_PRIVATE can reach private memory
directly. Add dma_require_decrypted() to combine force_dma_unencrypted() with the new device flag, and use it throughout the generic DMA code.

Assisted-by Codex:gpt-5.6-sol
---
 include/linux/dma-direct.h |  7 ++++++-
 kernel/dma/direct.c        | 16 ++++++++--------
 kernel/dma/mapping.c       |  2 +-
 kernel/dma/swiotlb.c       |  2 +-
 4 files changed, 16 insertions(+), 11 deletions(-)
include/linux/dma-direct.h +5 -0
index daa31a1adf7b0..fb35ca27941cc 100644
@@ -134,6 +134,11 @@ static inline bool force_dma_unencrypted(struct
 device *dev)
 }
 #endif /* CONFIG_ARCH_HAS_FORCE_DMA_UNENCRYPTED */
 
+static inline bool dma_require_decrypted(struct device *dev)
+{
+	return !dev_dma_cc_private(dev) && force_dma_unencrypted(dev);
+}
+
 static inline bool dma_capable(struct device *dev, dma_addr_t addr, size_t
 size,
include/linux/dma-direct.h +1 -1
 		bool is_ram, unsigned long attrs)
 {
@@ -146,7 +151,7 @@ static inline bool dma_capable(struct device *dev,
 dma_addr_t addr, size_t size,
 	 * requires unencrypted DMA addresses. Treat it as not DMA-capable
 	 * so the caller can fall back to a suitable SWIOTLB pool.
 	 */
-	if (!(attrs & DMA_ATTR_CC_SHARED) &&
 force_dma_unencrypted(dev))
+	if (!(attrs & DMA_ATTR_CC_SHARED) &&
 dma_require_decrypted(dev))
include/linux/dma-direct.h +0 -0
 		return false;
 
 	if (is_ram && !IS_ENABLED(CONFIG_ARCH_DMA_ADDR_T_64BIT) &&
kernel/dma/direct.c +2 -2
index da665ca22d5c0..fbddab9740162 100644
@@ -41,7 +41,7 @@ static inline struct page *dma_direct_to_page(struct
 device *dev,
 
 u64 dma_direct_get_required_mask(struct device *dev)
 {
-	bool require_decrypted = force_dma_unencrypted(dev);
+	bool require_decrypted = dma_require_decrypted(dev);
 	phys_addr_t phys = ((phys_addr_t)max_pfn << PAGE_SHIFT) - 1;
 	u64 max_dma = phys_to_dma_direct(dev, phys, require_decrypted);
@@ -72,7 +72,7 @@ static gfp_t dma_direct_optimal_gfp_mask(struct device
 *dev, u64 *phys_limit)
 
 bool dma_coherent_ok(struct device *dev, phys_addr_t phys, size_t size)
 {
-	bool require_decrypted = force_dma_unencrypted(dev);
+	bool require_decrypted = dma_require_decrypted(dev);
 	dma_addr_t dma_addr = phys_to_dma_direct(dev, phys,
 require_decrypted);
kernel/dma/direct.c +3 -3
 	if (dma_addr == DMA_MAPPING_ERROR)
@@ -208,7 +208,7 @@ void *dma_direct_alloc(struct device *dev, size_t size,
 	struct page *page;
 	void *cpu_addr;
 
-	if (force_dma_unencrypted(dev))
+	if (dma_require_decrypted(dev))
 		attrs |= __DMA_ATTR_ALLOC_CC_SHARED;
 
 	if (attrs & __DMA_ATTR_ALLOC_CC_SHARED) {
@@ -368,7 +368,7 @@ void dma_direct_free(struct device *dev, size_t size,
 	 * If the allocation used decrypted/shared backing pages, restore
 	 * the encryption state on free.
 	 */
-	if (force_dma_unencrypted(dev))
+	if (dma_require_decrypted(dev))
 		attrs |= __DMA_ATTR_ALLOC_CC_SHARED;
 
 	if (attrs & __DMA_ATTR_ALLOC_CC_SHARED)
@@ -436,7 +436,7 @@ struct page *dma_direct_alloc_pages(struct device
 *dev, size_t size,
 	struct page *page;
 	void *cpu_addr;
 
-	if (force_dma_unencrypted(dev))
+	if (dma_require_decrypted(dev))
 		attrs |= __DMA_ATTR_ALLOC_CC_SHARED;
 

if ((attrs & __DMA_ATTR_ALLOC_CC_SHARED) &&
dma_direct_use_pool(dev, gfp))

kernel/dma/direct.c +1 -1
@@ -480,7 +480,7 @@ void dma_direct_free_pages(struct device *dev,
 size_t size,
 	 * if the device had requested for an unencrypted buffer,
 	 * convert it to encrypted on free
 	 */
-	bool mark_mem_encrypted = force_dma_unencrypted(dev);
+	bool mark_mem_encrypted = dma_require_decrypted(dev);
 
 	/* If page is not from an atomic pool, dma_free_from_pool_page()

fails */

kernel/dma/direct.c +1 -1
 	if (IS_ENABLED(CONFIG_DMA_COHERENT_POOL) &&
@@ -649,7 +649,7 @@ int dma_direct_mmap(struct device *dev, struct
 vm_area_struct *vma,
 	const pgoff_t pgoff_end = vma_end_pgoff(vma);
 	int ret = -ENXIO;
 
-	if (force_dma_unencrypted(dev))
+	if (dma_require_decrypted(dev))
 		attrs |= DMA_ATTR_CC_SHARED;
 
kernel/dma/direct.c +1 -1
 	vma->vm_page_prot = dma_pgprot(dev, vma->vm_page_prot, attrs);
@@ -798,7 +798,7 @@ size_t dma_direct_max_mapping_size(struct device
 *dev)
 	/* If SWIOTLB is active, use its maximum mapping size */
 	if (is_swiotlb_active(dev) &&
 	    (dma_addressing_limited(dev) || is_swiotlb_force_bounce(dev) ||
-	     force_dma_unencrypted(dev)))
+	     dma_require_decrypted(dev)))
 		return swiotlb_max_mapping_size(dev);
 
kernel/dma/direct.c +0 -0
 	return SIZE_MAX;
kernel/dma/mapping.c +1 -1
index 11c127ad45370..e7c48a925b7ac 100644
@@ -673,7 +673,7 @@ void *dma_alloc_attrs(struct device *dev, size_t size,
 dma_addr_t *dma_handle,
 		return NULL;
 	}
 
-	if (force_dma_unencrypted(dev))
+	if (dma_require_decrypted(dev))
 		attrs |= __DMA_ATTR_ALLOC_CC_SHARED;
 

if (dma_alloc_from_dev_coherent(dev, size, dma_handle,
&cpu_addr)) {

kernel/dma/swiotlb.c +0 -0
index ded7016a46a71..d08a3643ded14 100644

kernel/dma/swiotlb.c phys_addr_t swiotlb_tbl_map_single(struct device · line 1557

Quoted 7 lines
kernel/dma/swiotlb.c +1 -1
@@ -1552,7 +1552,7 @@ phys_addr_t swiotlb_tbl_map_single(struct device
 *dev, phys_addr_t orig_addr,
 	if (cc_platform_has(CC_ATTR_GUEST_MEM_ENCRYPT)) {
 
 		/* swiotlb pool is incorrect for this device */
-		if (unlikely(mem->cc_shared !=
 force_dma_unencrypted(dev)))
+		if (unlikely(mem->cc_shared != dma_require_decrypted(dev)))

Just wondering, what if a device with CC_PRIVATE set has addressing limitations and needs a private bounce buffer? Or is that not a use case at all?

Thanks,
Shameer

#12 lore raw

Re: [PATCH v1 4/8] dma-mapping: Keep DMA memory private for capable devices

Quoted 4 lines · Shameer Kolothum Thodi
Quoted 4 lines
kernel/dma/swiotlb.c +0 -0
@@ -1552,7 +1552,7 @@ phys_addr_t swiotlb_tbl_map_single(struct device
 *dev, phys_addr_t orig_addr,
 	if (cc_platform_has(CC_ATTR_GUEST_MEM_ENCRYPT)) {
 
Quoted 8 lines

kernel/dma/swiotlb.c phys_addr_t swiotlb_tbl_map_single(struct device *dev, phys_addr_t orig_addr, · line 1555

Quoted 4 lines
kernel/dma/swiotlb.c +1 -1
 		/* swiotlb pool is incorrect for this device */
-		if (unlikely(mem->cc_shared !=
 force_dma_unencrypted(dev)))
+		if (unlikely(mem->cc_shared != dma_require_decrypted(dev)))

Just wondering, what if a device with CC_PRIVATE set has addressing limitations and needs a private bounce buffer? Or is that not a use case at all?

Then it will correctly fail here because it is not implemented. This protects accidently copying what should have been private data into shared memory.

If someone ever needs this they will have to implement a swiotlb pool for private memory along side the shared pool.

But a device that can't reach all of private wont' be able to reach shared either, so it is basically fully broken, don't do that. The VMM Should be setting the address layout of the VM so that the devices it installs 'fit' within their addressing constraints. ie do not create a VM with a 52 bit address space when your device only supports 48.

Jason

#13 lore raw

PATCH 5/8 iommu: Let a driver mark an IOMMU as confidential

In reply to Nicolin Chen

An IOMMU inside a confidential VM needs private memory for its queues and tables. Later changes also need to identify it when handling TDISP devices.

Add a confidential marker and a helper for IOMMU drivers. The helper also marks the IOMMU device as able to access private memory. Drivers must call it before iommu_device_register().

Assisted-by Claude:claude-opus-5
---
 include/linux/iommu.h | 10 ++++++++++
 drivers/iommu/iommu.c | 20 ++++++++++++++++++++
 2 files changed, 30 insertions(+)
include/linux/iommu.h +10 -0
index ac43b8b93f14a..bd68532e7f3be 100644
@@ -822,6 +822,8 @@ struct iommu_domain_ops {
  * @singleton_group: Used internally for drivers that have only one group
  * @max_pasids: number of supported PASIDs
  * @ready: set once iommu_device_register() has completed successfully
+ * @confidential: this instance runs inside a confidential VM. Set by the driver
+ *                before any dma allocation.
  */
 struct iommu_device {
 	struct list_head list;
@@ -831,6 +833,7 @@ struct iommu_device {
 	struct iommu_group *singleton_group;
 	u32 max_pasids;
 	bool ready;
+	bool confidential;
 };
 
 /**
@@ -890,6 +893,8 @@ struct dev_iommu {
 int iommu_device_register(struct iommu_device *iommu,
 			  const struct iommu_ops *ops,
 			  struct device *hwdev);
+void iommu_device_set_confidential(struct iommu_device *iommu,
+				   struct device *hwdev);
 void iommu_device_unregister(struct iommu_device *iommu);
 int  iommu_device_sysfs_add(struct iommu_device *iommu,
 			    struct device *parent,
@@ -1423,6 +1428,11 @@ static inline int iommu_device_register(struct iommu_device *iommu,
 	return -ENODEV;
 }
 
+static inline void iommu_device_set_confidential(struct iommu_device *iommu,
+						 struct device *hwdev)
+{
+}
+
 static inline struct iommu_device *dev_to_iommu_device(struct device *dev)
 {
 	return NULL;
drivers/iommu/iommu.c +20 -0
index cd1bca7ede9af..feff390727d13 100644
@@ -17,6 +17,7 @@
 #include <linux/slab.h>
 #include <linux/errno.h>
 #include <linux/host1x_context_bus.h>
+#include <linux/dma-mapping.h>
 #include <linux/iommu.h>
 #include <linux/iommufd.h>
 #include <linux/idr.h>
@@ -313,6 +314,25 @@ int iommu_device_register(struct iommu_device *iommu,
 }
 EXPORT_SYMBOL_GPL(iommu_device_register);
 
+/**
+ * iommu_device_set_confidential - Mark an IOMMU instance as confidential
+ * @iommu: the IOMMU instance
+ * @hwdev: the struct device of @iommu, whose own DMA reaches private memory
+ *
+ * Declare that @iommu runs inside a confidential VM
+ *
+ * Call this before @iommu makes any DMA allocation of its own, and not merely
+ * before iommu_device_register(). Queues and tables allocated any earlier land
+ * in shared memory that the hypervisor can read.
+ */
+void iommu_device_set_confidential(struct iommu_device *iommu,
+				   struct device *hwdev)
+{
+	iommu->confidential = true;
+	dma_set_cc_private(hwdev, true);
+}
+EXPORT_SYMBOL_GPL(iommu_device_set_confidential);
+
 void iommu_device_unregister(struct iommu_device *iommu)
 {
 	for (int i = 0; i < ARRAY_SIZE(iommu_buses); i++)
Signature · 1 line
2.43.0
#14 lore raw

PATCH 6/8 iommu: Introduce TDISP T=0 state for confidential IOMMUs

In reply to Nicolin Chen

TDISP devices have two independent DMA streams, T=1 and T=0. In several platforms these streams terminate into different IOMMU environments with different translation tables. For such platforms the IOMMU driver in Linux handles exactly one of the streams. Typically we expect the T=1 stream to be linked to the vIOMMU and the T=0 stream to simply be left as a no-iommu identity configuration.

As the HW has these two different DMA translation environments, the iommu subsystem must participate in switching between them. When the device uses T=0, its T=1 vIOMMU path should be left in BLOCKING to disable it, and a simplified "T=0 IDENTITY" mode is setup within the DMA API.

    [device: T=0] ===> [hypervisor] ===> [memory]
          |
          +-- (guest association) --> [confidential IOMMU: blocking DMA]

Setting the vIOMMU to BLOCKING also gives up its control of ATS, which is now managed by the core code as part of simplified T=0 mode.

Assisted-by Claude:claude-opus-5
---
 drivers/iommu/Makefile     |  3 ++
 drivers/iommu/iommu-priv.h | 19 +++++++++++++
 include/linux/iommu.h      |  4 +++
 drivers/iommu/iommu-cc.c   | 57 ++++++++++++++++++++++++++++++++++++++
 drivers/iommu/iommu.c      | 17 ++++++++++--
 5 files changed, 98 insertions(+), 2 deletions(-)
 create mode 100644 drivers/iommu/iommu-cc.c
drivers/iommu/Makefile +3 -0
index 2f05725eaab18..dc91d4cf0ce11 100644
@@ -6,6 +6,9 @@ obj-$(CONFIG_RISCV_IOMMU) += riscv/
 obj-$(CONFIG_GENERIC_PT) += generic_pt/fmt/
 obj-$(CONFIG_HYPERV) += hyperv/
 obj-$(CONFIG_IOMMU_API) += iommu.o
+ifdef CONFIG_ARCH_HAS_CC_PLATFORM
+obj-$(CONFIG_IOMMU_API) += iommu-cc.o
+endif
 obj-$(CONFIG_IOMMU_SUPPORT) += iommu-pages.o
 obj-$(CONFIG_IOMMU_API) += iommu-traces.o
 obj-$(CONFIG_IOMMU_API) += iommu-sysfs.o
drivers/iommu/iommu-priv.h +19 -0
index aaffad5854fc9..06e233ac4662f 100644
@@ -8,6 +8,25 @@
 #include <linux/iommu-debug-pagealloc.h>
 #include <linux/msi.h>
 
+#ifdef CONFIG_ARCH_HAS_CC_PLATFORM
+void iommu_tdisp_enter_t0(struct device *dev);
+void iommu_tdisp_exit_t0(struct device *dev);
+#else
+static inline void iommu_tdisp_enter_t0(struct device *dev)
+{
+}
+
+static inline void iommu_tdisp_exit_t0(struct device *dev)
+{
+}
+#endif
+
+static inline bool iommu_using_t0_stream(struct device *dev)
+{
+	return dev->iommu->iommu_dev->confidential &&
+	       !dev->iommu->tdisp_t1;
+}
+
 static inline const struct iommu_ops *dev_iommu_ops(struct device *dev)
 {
 	/*
include/linux/iommu.h +4 -0
index bd68532e7f3be..e3ed0dc538e0d 100644
@@ -871,6 +871,9 @@ struct iommu_fault_param {
  * @max_pasids:  number of PASIDs this device can consume
  * @attach_deferred: the dma domain attachment is deferred
  * @pci_32bit_workaround: Limit DMA allocations to 32-bit IOVAs
+ * @tdisp_t1: device uses its confidential IOMMU's T=1 stream. A zero value
+ *            means T=0, where the T=1 stream remains blocked while the core
+ *            owns required DMA operations such as ATS.
  * @require_direct: device requires IOMMU_RESV_DIRECT regions
  * @shadow_on_flush: IOTLB flushes are used to sync shadow tables
  *
@@ -886,6 +889,7 @@ struct dev_iommu {
 	u32				max_pasids;
 	u32				attach_deferred:1;
 	u32				pci_32bit_workaround:1;
+	u32				tdisp_t1:1;
 	u32				require_direct:1;
 	u32				shadow_on_flush:1;
 };
drivers/iommu/iommu-cc.c new file +57 -0

57 lines not on this page · raw message

drivers/iommu/iommu.c +15 -2
index feff390727d13..7ac6569af6773 100644
@@ -535,9 +535,17 @@ static int iommu_init_device(struct device *dev)
 	}
 	dev->iommu->iommu_dev = iommu_dev;
 
+	if (iommu_using_t0_stream(dev)) {
+		/*
+		 * After probe the iommu has to leave the T=1 stream in BLOCKING,
+		 * while the T=0 stream will go through the physical path.
+		 */
+		iommu_tdisp_enter_t0(dev);
+	}
+
 	ret = iommu_device_link(iommu_dev, dev);
 	if (ret)
-		goto err_release;
+		goto err_tdisp_t0;
 
 	group = ops->device_group(dev);
 	if (WARN_ON_ONCE(group == NULL))
@@ -555,7 +563,9 @@ static int iommu_init_device(struct device *dev)
 
 err_unlink:
 	iommu_device_unlink(iommu_dev, dev);
-err_release:
+err_tdisp_t0:
+	if (iommu_using_t0_stream(dev))
+		iommu_tdisp_exit_t0(dev);
 	if (ops->release_device)
 		ops->release_device(dev);
 err_module_put:
@@ -573,6 +583,9 @@ static void iommu_deinit_device(struct device *dev)
 
 	lockdep_assert_held(&group->mutex);
 
+	if (iommu_using_t0_stream(dev))
+		iommu_tdisp_exit_t0(dev);
+
 	iommu_device_unlink(dev->iommu->iommu_dev, dev);
 
 	/*
Signature · 1 line
2.43.0
#15 lore raw

Re: [PATCH v1 6/8] iommu: Introduce TDISP T=0 state for confidential IOMMUs

Quoted 3 lines · Nicolin Chen

+ * @tdisp_t1: device uses its confidential IOMMU's T=1 stream. A zero value
+ * means T=0, where the T=1 stream remains blocked while the core
+ * owns required DMA operations such as ATS.

What happens when the TDI transitions to ERROR, and how does this bit signify it?
A TDI can drop to ERROR asynchronously, and at that point the device is outside the trust boundary again.

Thanks,
Sonang

#16 lore raw

Re: [PATCH v1 6/8] iommu: Introduce TDISP T=0 state for confidential IOMMUs

Quoted 9 lines · sonang patel
Quoted 3 lines · Nicolin Chen

+ * @tdisp_t1: device uses its confidential IOMMU's T=1 stream. A zero value
+ * means T=0, where the T=1 stream remains blocked while the core
+ * owns required DMA operations such as ATS.

What happens when the TDI transitions to ERROR, and how does this bit signify it?
A TDI can drop to ERROR asynchronously, and at that point the device is outside the trust boundary again.

The bit reflects how the kernel expects the device to do DMA. It does not change when the TDISP state changes.

A device that falls to error can no longer do T=1 DMA, the device is expected to halt that internally. It must not start doing T=0 DMA in error either or it will corrupt random shared memory since T=0/T=1 are different IOVA spaces on ARM and Intel.

To recover out of error the userpace would have to cycle it through err/unlocked/locked/run and tell the kernel driver to start it up again. During that time the device must not do any T=0 DMA because nothing has been mapped to it in the T=0 regime.

From the driver perspecitve the device simply stops doing DMA for a while and when it comes back in RUN it is able to do DMA again. It is sort of like existing RAS flows where a device crashes.

Jason

#17 lore raw

PATCH 7/8 iommu: Park TDISP T=0 devices in the blocking domain

In reply to Nicolin Chen

A TDISP T=0 device stays associated with its guest T=1 vIOMMU, yet doesn't use that IOMMU for DMA translation. Instead, park its guest-visible stream in the driver's blocking domain.

Linux's model requires a device to either use T=0 or T=1. Aside from MSI, it cannot use T=0 for normal DMA once moved into T=1 mode, since the device will have IOVA's that are not valid in T=0.

Select the blocking domain before normal default-domain policy is applied.

Direct-map reserved regions are still fine, because the T=0 stream always goes to an identity mapping. Skip them here, as recording the request would only refuse the device its blocking domain.

Reject sysfs domain changes, driver-managed group attachment, and any PASID attachments that would install a translating domain. The state is uniform across each group because all devices on a confidential IOMMU start in T=0.

Assisted-by Claude:claude-opus-5
---
 drivers/iommu/iommu.c | 49 ++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 48 insertions(+), 1 deletion(-)
drivers/iommu/iommu.c +48 -1
index 7ac6569af6773..d6b00e0373f5c 100644
@@ -1238,6 +1238,14 @@ static int iommu_create_device_direct_mappings(struct iommu_domain *domain,
 	unsigned long pg_size;
 	int ret = 0;
 
+	/*
+	 * The direct-map reserved regions are still fine because the T=0 stream
+	 * always goes to an identity mapping. Skip them: recording the request
+	 * would only refuse the device its blocking domain.
+	 */
+	if (iommu_using_t0_stream(dev))
+		return 0;
+
 	pg_size = domain->pgsize_bitmap ? 1UL << __ffs(domain->pgsize_bitmap) : 0;
 
 	if (WARN_ON_ONCE(iommu_is_dma_domain(domain) && !pg_size))
@@ -1790,11 +1798,22 @@ __iommu_group_alloc_default_domain(struct iommu_group *group, int req_type)
 static struct iommu_domain *
 iommu_group_alloc_default_domain(struct iommu_group *group, int req_type)
 {
-	const struct iommu_ops *ops = dev_iommu_ops(iommu_group_first_dev(group));
+	struct device *first = iommu_group_first_dev(group);
+	const struct iommu_ops *ops = dev_iommu_ops(first);
 	struct iommu_domain *dom;
 
 	lockdep_assert_held(&group->mutex);
 
+	/*
+	 * When in T=0 mode the T=1 vIOMMU must always be set to BLOCKED to
+	 * release control of ATS.
+	 */
+	if (iommu_using_t0_stream(first)) {
+		if (!ops->blocked_domain)
+			return ERR_PTR(-EINVAL);
+		return ops->blocked_domain;
+	}
+
 	/*
 	 * Allow legacy drivers to specify the domain that will be the default
 	 * domain. This should always be either an IDENTITY/BLOCKED/PLATFORM
@@ -2399,6 +2418,10 @@ static int __iommu_attach_group(struct iommu_domain *domain,
 	    !domain_iommu_ops_compatible(dev_iommu_ops(dev), domain))
 		return -EINVAL;
 
+	/* A domain cannot be attached while using the T=0 stream. */
+	if (iommu_using_t0_stream(dev))
+		return -EINVAL;
+
 	return __iommu_group_set_domain(group, domain);
 }
 
@@ -3325,6 +3348,12 @@ static ssize_t iommu_group_store_type(struct iommu_group *group,
 		goto out_unlock;
 	}
 
+	/* Default domain changes are not allowed while using the T=0 stream. */
+	if (iommu_using_t0_stream(iommu_group_first_dev(group))) {
+		ret = -EINVAL;
+		goto out_unlock;
+	}
+
 	ret = iommu_setup_default_domain(group, req_type);
 	if (ret)
 		goto out_unlock;
@@ -3690,6 +3719,12 @@ int iommu_attach_device_pasid(struct iommu_domain *domain,
 		goto out_unlock;
 	}
 
+	/* PASID cannot be used on the T=0 stream. */
+	if (iommu_using_t0_stream(dev)) {
+		ret = -EINVAL;
+		goto out_unlock;
+	}
+
 	for_each_group_device(group, device) {
 		/*
 		 * Skip PASID validation for devices without PASID support
@@ -3783,6 +3818,12 @@ int iommu_replace_device_pasid(struct iommu_domain *domain,
 		goto out_unlock;
 	}
 
+	/* PASID cannot be used on the T=0 stream. */
+	if (iommu_using_t0_stream(dev)) {
+		ret = -EINVAL;
+		goto out_unlock;
+	}
+
 	entry = iommu_make_pasid_array_entry(domain, handle);
 	curr = xa_cmpxchg(&group->pasid_array, pasid, NULL,
 			  XA_ZERO_ENTRY, GFP_KERNEL);
@@ -4017,6 +4058,12 @@ int iommu_replace_group_handle(struct iommu_group *group,
 		return -EINVAL;
 
 	mutex_lock(&group->mutex);
+	/* Like attach, replacement is not allowed when using the T=0 stream. */
+	if (iommu_using_t0_stream(iommu_group_first_dev(group))) {
+		ret = -EINVAL;
+		goto err_unlock;
+	}
+
 	entry = iommu_make_pasid_array_entry(new_domain, handle);
 	ret = xa_reserve(&group->pasid_array, IOMMU_NO_PASID, GFP_KERNEL);
 	if (ret)
Signature · 1 line
2.43.0
#18 lore raw

RE: [PATCH v1 7/8] iommu: Park TDISP T=0 devices in the blocking domain

Quoted 101 lines

-----Original Message-----
From: Nicolin Chen <[email protected]>
Sent: 10 September 2026 04:33
To: Jason Gunthorpe <[email protected]>; Catalin Marinas <[email protected]>; Will Deacon <[email protected]>; [email protected]; Robin Murphy <[email protected]>; [email protected]; Danilo Krummrich <[email protected]>; Marek Szyprowski <[email protected]>; [email protected]
Cc: Mark Rutland <[email protected]>; Greg Kroah-Hartman <[email protected]>; Suzuki K Poulose <[email protected]>; Gavin Shan <[email protected]>; Vikram Sethi <[email protected]>; Anshuman Khandual <[email protected]>; Shanker Donthineni <[email protected]>; Mostafa Saleh <[email protected]>; [email protected]; Thomas Huth <[email protected]>; Marc Zyngier <[email protected]>; Ryan Roberts <[email protected]>; [email protected]; Kohei Enju <[email protected]>; Shaopeng Tan <[email protected]>; Ard Biesheuvel <[email protected]>; James Morse <[email protected]>; Steven Price <[email protected]>; Sang-Heon Jeon <[email protected]>; Omar Sandoval <[email protected]>; Andrew Morton <[email protected]>; Jinjie Ruan <[email protected]>; Sam Edwards <[email protected]>; Douglas Anderson <[email protected]>; Florian Fainelli <[email protected]>; Chen-Yu Tsai <[email protected]>; Huacai Chen <[email protected]>; Thomas Zimmermann <[email protected]>; Pranjal Shrivastava <[email protected]>; Ashish Mhetre <[email protected]>; Shameer Kolothum Thodi <[email protected]>; [email protected]; linux- [email protected]; [email protected]; [email protected]; Sonang Patel <[email protected]>; Ankit Agrawal <[email protected]>
Subject: [PATCH v1 7/8] iommu: Park TDISP T=0 devices in the blocking domain

A TDISP T=0 device stays associated with its guest T=1 vIOMMU, yet doesn't use that IOMMU for DMA translation. Instead, park its guest-visible stream in the driver's blocking domain.

Linux's model requires a device to either use T=0 or T=1. Aside from MSI, it cannot use T=0 for normal DMA once moved into T=1 mode, since the device will have IOVA's that are not valid in T=0.

Select the blocking domain before normal default-domain policy is applied.

Direct-map reserved regions are still fine, because the T=0 stream always goes to an identity mapping. Skip them here, as recording the request would only refuse the device its blocking domain.

Reject sysfs domain changes, driver-managed group attachment, and any PASID
attachments that would install a translating domain. The state is uniform across each group because all devices on a confidential IOMMU start in T=0.

Assisted-by Claude:claude-opus-5
---
 drivers/iommu/iommu.c | 49
++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 48 insertions(+), 1 deletion(-)
drivers/iommu/iommu.c +3 -0
index 7ac6569af6773..d6b00e0373f5c 100644
@@ -1238,6 +1238,14 @@ static int
 iommu_create_device_direct_mappings(struct iommu_domain *domain,
 	unsigned long pg_size;
 	int ret = 0;
 
+	/*
+	 * The direct-map reserved regions are still fine because the T=0 stream
+	 * always goes to an identity mapping. Skip them: recording the

request

drivers/iommu/iommu.c +5 -0
+	 * would only refuse the device its blocking domain.
+	 */
+	if (iommu_using_t0_stream(dev))
+		return 0;
+
 	pg_size = domain->pgsize_bitmap ? 1UL << __ffs(domain-
Quoted 1 line

pgsize_bitmap) : 0;

drivers/iommu/iommu.c +4 -1
 	if (WARN_ON_ONCE(iommu_is_dma_domain(domain) && !pg_size))
@@ -1790,11 +1798,22 @@ __iommu_group_alloc_default_domain(struct
 iommu_group *group, int req_type)
 static struct iommu_domain *
 iommu_group_alloc_default_domain(struct iommu_group *group, int
 req_type)
 {
-	const struct iommu_ops *ops =
 dev_iommu_ops(iommu_group_first_dev(group));
+	struct device *first = iommu_group_first_dev(group);
+	const struct iommu_ops *ops = dev_iommu_ops(first);
 	struct iommu_domain *dom;
 
 	lockdep_assert_held(&group->mutex);
 
+	/*
+	 * When in T=0 mode the T=1 vIOMMU must always be set to

BLOCKED to

drivers/iommu/iommu.c __iommu_group_alloc_default_domain(struct iommu_group *group, int req_type) · line 1814

Quoted 6 lines
drivers/iommu/iommu.c +6 -0
+	 * release control of ATS.
+	 */
+	if (iommu_using_t0_stream(first)) {
+		if (!ops->blocked_domain)
+			return ERR_PTR(-EINVAL);
+		return ops->blocked_domain;

How is MSI allocation handled for T=0 devices? The ITS calls iommu_dma_prepare_msi(), which appears to return -EOPNOTSUPP for the blocked domain. Is that intentional?

Thanks,
Shameer

#19 lore raw

Re: [PATCH v1 7/8] iommu: Park TDISP T=0 devices in the blocking domain

Quoted 5 lines · Shameer Kolothum Thodi
Quoted 5 lines
(quoted diff) +2 -0
+	/*
+	 * When in T=0 mode the T=1 vIOMMU must always be set to

BLOCKED to

drivers/iommu/iommu.c +2 -0
+	 * release control of ATS.
+	 */
Quoted 8 lines

drivers/iommu/iommu.c __iommu_group_alloc_default_domain(struct iommu_group *group, int req_type) · line 1814

Quoted 4 lines
drivers/iommu/iommu.c +4 -0
+	if (iommu_using_t0_stream(first)) {
+		if (!ops->blocked_domain)
+			return ERR_PTR(-EINVAL);
+		return ops->blocked_domain;

How is MSI allocation handled for T=0 devices? The ITS calls iommu_dma_prepare_msi(), which appears to return -EOPNOTSUPP for the blocked domain. Is that intentional?

For a TDISP capable devince MSI is always issued using T=0, meaning it does not go through the SMMU. It should be wired to defeat iommu_dma_prepare_msi() and use an identity IOVA as though there is no iommu present..

AFAIK this is true for all devices all arches today.

Jason

#20 lore raw

Re: [PATCH v1 7/8] iommu: Park TDISP T=0 devices in the blocking domain

Quoted 15 lines · Jason Gunthorpe
Quoted 5 lines · Shameer Kolothum Thodi
Quoted 5 lines
(quoted diff) +2 -0
+	/*
+	 * When in T=0 mode the T=1 vIOMMU must always be set to

BLOCKED to

(quoted diff) +2 -0
+	 * release control of ATS.
+	 */
Quoted 8 lines

Review

Quoted 4 lines
(quoted diff) +4 -0
+	if (iommu_using_t0_stream(first)) {
+		if (!ops->blocked_domain)
+			return ERR_PTR(-EINVAL);
+		return ops->blocked_domain;

How is MSI allocation handled for T=0 devices? The ITS calls iommu_dma_prepare_msi(), which appears to return -EOPNOTSUPP for the blocked domain. Is that intentional?

Quoted 4 lines

For a TDISP capable devince MSI is always issued using T=0, meaning it does not go through the SMMU. It should be wired to defeat iommu_dma_prepare_msi() and use an identity IOVA as though there is no iommu present..

I'll fold this in:

------------------------------------------------------------

drivers/iommu/iommu.c +3 -2
index d6b00e0373f5c..1474224c2565f 100644
@@ -4324,8 +4324,9 @@ int iommu_dma_prepare_msi(struct msi_desc *desc, phys_addr_t msi_addr)
 		return 0;
 
 	mutex_lock(&group->mutex);
-	/* An IDENTITY domain must pass through */
-	if (group->domain && group->domain->type != IOMMU_DOMAIN_IDENTITY) {
+	/* An IDENTITY domain or a T=0 device must pass through */
+	if (group->domain && group->domain->type != IOMMU_DOMAIN_IDENTITY &&
+	    !iommu_using_t0_stream(dev)) {
 		switch (group->domain->cookie_type) {
 		case IOMMU_COOKIE_DMA_MSI:
 		case IOMMU_COOKIE_DMA_IOVA:

------------------------------------------------------------

Thanks
Nicolin

#21 lore raw

PATCH 8/8 iommu/arm-smmu-v3: Probe a guest-level Realm VSMMU via RSI commands

In reply to Nicolin Chen

In CCA, the ACPI/etc doesn't tell the OS whether the SMMU is a confidential T=1 instance or a normal T=0 one. Instead, it has to be learned by issuing a trusted RSI. Before the kernel can operate the trusted T=1 IOMMU, it also has to validate all the information that it gets from ACPI against the true information that it gets from the RSI call, to ensure the ACPI is correct and prevent substitution attacks.

Use RSI_VSMMU_GET_INFO during probe and require the returned register range to match firmware. Then use RSI_ARCH_DEV_ACTIVATE before mapping registers.

Currently the driver doesn't support a T=0 SMMU inside a realm. For example it doesn't make the page table allocations into shared memory. If we are in a realm reject any SMMU that is not T=1.

Once the SMMU is activated, its DMA will follow the DEV_FLAG_DMA_CC_PRIVATE rules. It must set that flag to ensure its coherent allocations for its own structures are allocated from private memory, not the SWIOTLB shared pool.

Assisted-by Codex:gpt-5.6-sol
---
 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 55 +++++++++++++++++++++
 1 file changed, 55 insertions(+)
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +55 -0
index 5732f3ba0122d..972815c54b11f 100644
@@ -11,9 +11,11 @@
 
 #include <linux/acpi.h>
 #include <linux/acpi_iort.h>
+#include <linux/arm-rsi-cmds.h>
 #include <linux/bitops.h>
 #include <linux/crash_dump.h>
 #include <linux/delay.h>
+#include <linux/dma-mapping.h>
 #include <linux/err.h>
 #include <linux/interrupt.h>
 #include <linux/io-pgtable.h>
@@ -5507,6 +5509,53 @@ static struct arm_smmu_device *arm_smmu_impl_probe(struct arm_smmu_device *smmu)
 	return ERR_PTR(ret);
 }
 
+static void arm_smmu_clear_realm_private(void *dev)
+{
+	dma_set_cc_private(dev, false);
+}
+
+static int arm_smmu_probe_realm_vsmmu(struct arm_smmu_device *smmu,
+				      const struct resource *res)
+{
+	struct device *dev = smmu->dev;
+	unsigned long rsi_ret;
+	phys_addr_t top;
+
+	/*
+	 * Currently the driver does not support a T=0 SMMU inside a realm. For
+	 * instance it does not make the page table allocations into shared
+	 * memory. If we are in a realm reject any SMMU that is not T=1.
+	 */
+	rsi_ret = rsi_vsmmu_get_info(res->start, &top);
+	if (rsi_ret != RSI_SUCCESS) {
+		dev_err(dev, "RSI_VSMMU_GET_INFO failed for %pr: %lu\n", res,
+			rsi_ret);
+		return -ENODEV;
+	}
+
+	if (top != res->end + 1) {
+		dev_err(dev, "VSMMU range %pr ends at %pa\n", res, &top);
+		return -EINVAL;
+	}
+
+	rsi_ret = rsi_arch_dev_activate(res->start, RSI_ARCH_DEV_SMMUV3);
+	if (rsi_ret != RSI_SUCCESS) {
+		dev_err(dev, "RSI_ARCH_DEV_ACTIVATE failed for %pr: %lu\n", res,
+			rsi_ret);
+		return -EIO;
+	}
+
+	/*
+	 * Once activated, the SMMU DMA follows DEV_FLAG_DMA_CC_PRIVATE so its
+	 * queues and tables are allocated from private memory, not the SWIOTLB
+	 * shared pool. It only translates for a device we've requested the RMM
+	 * to put into T=1.
+	 */
+	iommu_device_set_confidential(&smmu->iommu, dev);
+
+	return devm_add_action_or_reset(dev, arm_smmu_clear_realm_private, dev);
+}
+
 static int arm_smmu_device_probe(struct platform_device *pdev)
 {
 	int irq, ret;
@@ -5542,6 +5591,12 @@ static int arm_smmu_device_probe(struct platform_device *pdev)
 	}
 	ioaddr = res->start;
 
+	if (is_realm_world()) {
+		ret = arm_smmu_probe_realm_vsmmu(smmu, res);
+		if (ret)
+			return ret;
+	}
+
 	/*
 	 * Don't map the IMPLEMENTATION DEFINED regions, since they may contain
 	 * the PMCG registers which are reserved by the PMU driver.
Signature · 1 line
2.43.0
#22 lore raw

Re: [PATCH v1 8/8] iommu/arm-smmu-v3: Probe a guest-level Realm VSMMU via RSI commands

drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c static int arm_smmu_device_probe(struct platform_device *pdev) · line 5597

Quoted 8 lines · Nicolin Chen
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +4 -0
@@ -5542,6 +5591,12 @@ static int arm_smmu_device_probe(struct platform_device *pdev)
 	}
 	ioaddr = res->start;
 
+	if (is_realm_world()) {
+		ret = arm_smmu_probe_realm_vsmmu(smmu, res);
+		if (ret)
+			return ret;

Sashiko pointed out a new finding (didn't appear in my local runs) that arm_smmu_impl_probe() would allow an untrusted hypervisor to provide a malicious ACPI/DT table specifying a vendor extension.

I think it's valid, So, we likely need a guard, until impl has RMM level support with additional RSI validation:

- smmu = arm_smmu_impl_probe(smmu);
- if (IS_ERR(smmu))
- return PTR_ERR(smmu);
+ if (!is_realm_world()) {
+ smmu = arm_smmu_impl_probe(smmu);
+ if (IS_ERR(smmu))
+ return PTR_ERR(smmu);
+ }

Nicolin

#23 lore raw

RE: [PATCH v1 8/8] iommu/arm-smmu-v3: Probe a guest-level Realm VSMMU via RSI commands

In reply to Nicolin Chen
Quoted 144 lines

-----Original Message-----
From: Nicolin Chen <[email protected]>
Sent: 10 September 2026 04:33
To: Jason Gunthorpe <[email protected]>; Catalin Marinas <[email protected]>; Will Deacon <[email protected]>; [email protected]; Robin Murphy <[email protected]>; [email protected]; Danilo Krummrich <[email protected]>; Marek Szyprowski <[email protected]>; [email protected]
Cc: Mark Rutland <[email protected]>; Greg Kroah-Hartman <[email protected]>; Suzuki K Poulose <[email protected]>; Gavin Shan <[email protected]>; Vikram Sethi <[email protected]>; Anshuman Khandual <[email protected]>; Shanker Donthineni <[email protected]>; Mostafa Saleh <[email protected]>; [email protected]; Thomas Huth <[email protected]>; Marc Zyngier <[email protected]>; Ryan Roberts <[email protected]>; [email protected]; Kohei Enju <[email protected]>; Shaopeng Tan <[email protected]>; Ard Biesheuvel <[email protected]>; James Morse <[email protected]>; Steven Price <[email protected]>; Sang-Heon Jeon <[email protected]>; Omar Sandoval <[email protected]>; Andrew Morton <[email protected]>; Jinjie Ruan <[email protected]>; Sam Edwards <[email protected]>; Douglas Anderson <[email protected]>; Florian Fainelli <[email protected]>; Chen-Yu Tsai <[email protected]>; Huacai Chen <[email protected]>; Thomas Zimmermann <[email protected]>; Pranjal Shrivastava <[email protected]>; Ashish Mhetre <[email protected]>; Shameer Kolothum Thodi <[email protected]>; [email protected]; linux- [email protected]; [email protected]; [email protected]; Sonang Patel <[email protected]>; Ankit Agrawal <[email protected]>
Subject: [PATCH v1 8/8] iommu/arm-smmu-v3: Probe a guest-level Realm VSMMU via RSI commands

In CCA, the ACPI/etc doesn't tell the OS whether the SMMU is a confidential T=1 instance or a normal T=0 one. Instead, it has to be learned by issuing a trusted RSI. Before the kernel can operate the trusted T=1 IOMMU, it also has to validate all the information that it gets from ACPI against the true information that it gets from the RSI call, to ensure the ACPI is correct and prevent substitution attacks.

Use RSI_VSMMU_GET_INFO during probe and require the returned register range
to match firmware. Then use RSI_ARCH_DEV_ACTIVATE before mapping registers.

Currently the driver doesn't support a T=0 SMMU inside a realm. For example it doesn't make the page table allocations into shared memory. If we are in a realm reject any SMMU that is not T=1.

Once the SMMU is activated, its DMA will follow the DEV_FLAG_DMA_CC_PRIVATE
rules. It must set that flag to ensure its coherent allocations for its own structures are allocated from private memory, not the SWIOTLB shared pool.

Assisted-by Codex:gpt-5.6-sol
---
 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 55
+++++++++++++++++++++
 1 file changed, 55 insertions(+)
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +16 -0
index 5732f3ba0122d..972815c54b11f 100644
@@ -11,9 +11,11 @@
 
 #include <linux/acpi.h>
 #include <linux/acpi_iort.h>
+#include <linux/arm-rsi-cmds.h>
 #include <linux/bitops.h>
 #include <linux/crash_dump.h>
 #include <linux/delay.h>
+#include <linux/dma-mapping.h>
 #include <linux/err.h>
 #include <linux/interrupt.h>
 #include <linux/io-pgtable.h>
@@ -5507,6 +5509,53 @@ static struct arm_smmu_device
 *arm_smmu_impl_probe(struct arm_smmu_device *smmu)
 	return ERR_PTR(ret);
 }
 
+static void arm_smmu_clear_realm_private(void *dev)
+{
+	dma_set_cc_private(dev, false);
+}
+
+static int arm_smmu_probe_realm_vsmmu(struct arm_smmu_device
 *smmu,
+				      const struct resource *res)
+{
+	struct device *dev = smmu->dev;
+	unsigned long rsi_ret;
+	phys_addr_t top;
+
+	/*
+	 * Currently the driver does not support a T=0 SMMU inside a realm.

For

drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +6 -0
+	 * instance it does not make the page table allocations into shared
+	 * memory. If we are in a realm reject any SMMU that is not T=1.
+	 */
+	rsi_ret = rsi_vsmmu_get_info(res->start, &top);
+	if (rsi_ret != RSI_SUCCESS) {
+		dev_err(dev, "RSI_VSMMU_GET_INFO failed for %pr: %lu\n",

res,

drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +12 -0
+			rsi_ret);
+		return -ENODEV;
+	}
+
+	if (top != res->end + 1) {
+		dev_err(dev, "VSMMU range %pr ends at %pa\n", res, &top);
+		return -EINVAL;
+	}
+
+	rsi_ret = rsi_arch_dev_activate(res->start, RSI_ARCH_DEV_SMMUV3);
+	if (rsi_ret != RSI_SUCCESS) {
+		dev_err(dev, "RSI_ARCH_DEV_ACTIVATE failed for %pr:

%lu\n", res,

drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +6 -0
+			rsi_ret);
+		return -EIO;
+	}
+
+	/*
+	 * Once activated, the SMMU DMA follows

DEV_FLAG_DMA_CC_PRIVATE so its

drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +1 -0
+	 * queues and tables are allocated from private memory, not the

SWIOTLB

drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +1 -0
+	 * shared pool. It only translates for a device we've requested the

RMM

drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +5 -0
+	 * to put into T=1.
+	 */
+	iommu_device_set_confidential(&smmu->iommu, dev);
+
+	return devm_add_action_or_reset(dev,

arm_smmu_clear_realm_private, dev);

drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +2 -0
+}
+
 static int arm_smmu_device_probe(struct platform_device *pdev)
 {
 	int irq, ret;

drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c static int arm_smmu_device_probe(struct · line 5599

Quoted 9 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +5 -0
@@ -5542,6 +5591,12 @@ static int arm_smmu_device_probe(struct
 platform_device *pdev)
 	}
 	ioaddr = res->start;
 
+	if (is_realm_world()) {
+		ret = arm_smmu_probe_realm_vsmmu(smmu, res);
+		if (ret)
+			return ret;
+	}

If probe fails after activation succeeds above, wouldn't a module reload hit RSI_ERROR_DEVICE since the VSMMU is still ACTIVE?

B5.4.1.2:

pre: vsmmu.state == VSMMU_ACTIVE
post: result == RSI_ERROR_DEVICE

How is recovery handled?

Thanks,
Shameer

#24 lore raw

Re: [PATCH v1 8/8] iommu/arm-smmu-v3: Probe a guest-level Realm VSMMU via RSI commands

Quoted 14 lines · Shameer Kolothum Thodi

drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c static int arm_smmu_device_probe(struct · line 5599

Quoted 10 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +5 -0
@@ -5542,6 +5591,12 @@ static int arm_smmu_device_probe(struct
 platform_device *pdev)
 	}
 	ioaddr = res->start;
 
+	if (is_realm_world()) {
+		ret = arm_smmu_probe_realm_vsmmu(smmu, res);
+		if (ret)
+			return ret;
+	}

If probe fails after activation succeeds above, wouldn't a module reload hit RSI_ERROR_DEVICE since the VSMMU is still ACTIVE?

Quoted 6 lines

B5.4.1.2:

pre: vsmmu.state == VSMMU_ACTIVE
post: result == RSI_ERROR_DEVICE

How is recovery handled?

There is no way to undo an activation. But what's the practical use case for the SMMU driver to do a module reload?

Nicolin

#25 lore raw

RE: [PATCH v1 8/8] iommu/arm-smmu-v3: Probe a guest-level Realm VSMMU via RSI commands

Quoted 53 lines

-----Original Message-----
From: Nicolin Chen <[email protected]>
Sent: 28 October 2026 18:09
To: Shameer Kolothum Thodi <[email protected]>
Cc: Jason Gunthorpe <[email protected]>; Catalin Marinas <[email protected]>; Will Deacon <[email protected]>; [email protected]; Robin Murphy <[email protected]>; [email protected]; Danilo Krummrich <[email protected]>; Marek Szyprowski <[email protected]>; [email protected]; Mark Rutland <[email protected]>; Greg Kroah-Hartman <[email protected]>; Suzuki K Poulose <[email protected]>; Gavin Shan <[email protected]>; Vikram Sethi <[email protected]>; Anshuman Khandual <[email protected]>; Shanker Donthineni <[email protected]>; Mostafa Saleh <[email protected]>; [email protected]; Thomas Huth <[email protected]>; Marc Zyngier <[email protected]>; Ryan Roberts <[email protected]>; [email protected]; Kohei Enju <[email protected]>; Shaopeng Tan <[email protected]>; Ard Biesheuvel <[email protected]>; James Morse <[email protected]>; Steven Price <[email protected]>; Sang-Heon Jeon <[email protected]>; Omar Sandoval <[email protected]>; Andrew Morton <[email protected]>; Jinjie Ruan <[email protected]>; Sam Edwards <[email protected]>; Douglas Anderson <[email protected]>; Florian Fainelli <[email protected]>; Chen-Yu Tsai <[email protected]>; Huacai Chen <[email protected]>; Thomas Zimmermann <[email protected]>; Pranjal Shrivastava <[email protected]>; Ashish Mhetre <[email protected]>; [email protected]; linux- [email protected]; [email protected]; [email protected]; Sonang Patel <[email protected]>; Ankit Agrawal <[email protected]>
Subject: Re: [PATCH v1 8/8] iommu/arm-smmu-v3: Probe a guest-level Realm VSMMU via RSI commands

Quoted 16 lines · Shameer Kolothum Thodi

drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c static int arm_smmu_device_probe(struct · line 5599

Quoted 10 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +5 -0
@@ -5542,6 +5591,12 @@ static int arm_smmu_device_probe(struct
 platform_device *pdev)
 	}
 	ioaddr = res->start;
 
+	if (is_realm_world()) {
+		ret = arm_smmu_probe_realm_vsmmu(smmu, res);
+		if (ret)
+			return ret;
+	}

If probe fails after activation succeeds above, wouldn't a module reload hit RSI_ERROR_DEVICE since the VSMMU is still ACTIVE?

B5.4.1.2:

Quoted 7 lines
Quoted 4 lines

pre: vsmmu.state == VSMMU_ACTIVE
post: result == RSI_ERROR_DEVICE

How is recovery handled?

There is no way to undo an activation. But what's the practical use case for the SMMU driver to do a module reload?

I was thinking of an allocation failure, then onlining more memory and retrying probe. Not sure how realistic that is in practice.

Any idea why guest side deactivation isn't allowed? I couldn't find the rationale in the spec. Perhaps it needs additional synchronization.

Thanks,
Shameer