mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH 0/5] iommu/vt-d: Introduce trusted DMA initialization support
@ 2026-09-15  7:42 Lu Baolu
  2026-09-15  7:42 ` [PATCH 1/5] x86/virt/tdx: Add SEAMCALL wrappers for IOMMU setup/clear Lu Baolu
                   ` (4 more replies)
  0 siblings, 5 replies; 6+ messages in thread
From: Lu Baolu @ 2026-09-15  7:42 UTC (permalink / raw)
  To: iommu, x86, linux-coco, kvm
  Cc: Joerg Roedel, Will Deacon, Robin Murphy, Jason Gunthorpe,
	Kevin Tian, Dave Hansen, Kiryl Shutsemau, Rick Edgecombe,
	yilun.xu, xiaoyao.li, Chao Gao, linux-kernel, Lu Baolu

Hi,

This series adds Intel VT-d trusted extension support to the host Intel
IOMMU driver as groundwork for TEE I/O trusted DMA (e.g. TDX Connect).

The trusted extension introduces a parallel, extension-specific path for
trusted DMA assets used by confidential device assignment. Existing VT-d
functionality remains unchanged for regular host DMA paths.

From the TDX Connect architecture perspective, VT-d is enhanced with:

  1) Trusted DMA translation root table
     - A second root-table address register, restricted to SEAM SAI.
     - Programmed by the TDX module with the root of TDX-managed DMA
       translation tables for TD-assigned devices.

  2) Trusted invalidation queue
     - A second invalidation-queue address register plus dedicated
       head/tail registers, also restricted to SEAM SAI.

  3) IOTLB / DID isolation
     - When TDX Connect is enabled, VT-d restricts VMM DID usage and
       reserves the DID MSB namespace bit for TDX-module use.

This posting focuses on VT-d-side enablement only: it enables those
extension-defined resources and control points while preserving existing
host VT-d behavior for non-trusted flows. Higher-layer integration
(TDX guest/device flows) will be posted separately.

The trusted IOMMU extensions establish an architecture where only
TEE-I/O DMA requests can access TD private memory, and only the TDX
module can configure TEE-I/O access-control permissions. The host
allocates memory for data structures used by the TDX module to 
manage trusted DMA, then requests the TDX module (via SEAMCALL) to
enable the extension in each IOMMU. 

Non-TEE-I/O DMA requests are unaffected and continue to be managed 
by the host through existing IOMMU interfaces.

Patch Breakdown:

- Patch 1: Implements low-level SEAMCALL wrappers (TDH.IOMMU.SETUP/CLEAR)
  to interface with the TDX module.
- Patch 2: Parses platform global metadata fields to calculate required
  IOMMU metadata page requirements.
- Patch 3: Introduces the core driver entry points.
- Patch 4: Implements the metadata page allocation and SEAMCALL
  sequences required to configure and release IOMMU units in trusted
  mode.
- Patch 5: Restricts the domain ID (DID) space allocated to the host by
  reserving the MSB for exclusive use by the TDX module.

ABI History:

TDH.IOMMU.CLEAR previously required the host to provide, as input, the
parameter page used for TDH.IOMMU.SETUP. This ABI has changed to remove
that requirement. Because this ABI change is not yet public, it may
cause confusion when referring to the ABI specification.

Reference:

Implementation details follow the interfaces and behavior defined in the
TDX Connect Application Binary Interface (ABI) Reference Specification
and Intel TDX Connect Architecture Specification:

Link: https://cdrdv2.intel.com/v1/dl/getContent/858625 # [1]
Link: https://www.intel.com/content/www/us/en/content-details/862706/intel-tdx-connect-architecture-specification.html # [2]

This is part of the effort to implement the TDX PCIe Link Encryption
feature, which includes the following components:

[ ] TDX module extensions initialization, posted in [3]
[X] Introduce trusted DMA initialization support
[ ] SEAMCALL helpers for TDX PCIe Link Encryption
[ ] PCIe Link Encryption Establishment via TDX platform services

A full preview of these dependencies and follow-up changes is avaiable
at [4].

Link: https://lore.kernel.org/r/20260618081355.3253581-1-yilun.xu@linux.intel.com # [3]
Link: https://github.com/intel-staging/tdx/tree/tdisp_phase1 # [4]

Please review. Any feedback is highly appreciated.

Thanks,
Baolu

Lu Baolu (5):
  x86/virt/tdx: Add SEAMCALL wrappers for IOMMU setup/clear
  x86/virt/tdx: Read global metadata for trusted IOMMU
  iommu/vt-d: Add interfaces for trusted DMA initialization
  iommu/vt-d: Add helpers to set up and tear down TDX extensions
  iommu/vt-d: Reserve MSB of domain ID space for TDX module

 drivers/iommu/intel/Kconfig                 |  14 ++
 drivers/iommu/intel/Makefile                |   1 +
 arch/x86/include/asm/tdx.h                  |   3 +
 arch/x86/include/asm/tdx_global_metadata.h  |   5 +
 arch/x86/virt/vmx/tdx/tdx.h                 |   2 +
 drivers/iommu/intel/iommu.h                 |  24 ++
 include/linux/dmar.h                        |  12 +
 arch/x86/virt/vmx/tdx/tdx.c                 |  56 +++++
 arch/x86/virt/vmx/tdx/tdx_global_metadata.c |  20 ++
 drivers/iommu/intel/dmar.c                  |   3 +
 drivers/iommu/intel/iommu.c                 |  11 +
 drivers/iommu/intel/tdxc.c                  | 248 ++++++++++++++++++++
 12 files changed, 399 insertions(+)
 create mode 100644 drivers/iommu/intel/tdxc.c

-- 
2.43.0


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 1/5] x86/virt/tdx: Add SEAMCALL wrappers for IOMMU setup/clear
  2026-09-15  7:42 [PATCH 0/5] iommu/vt-d: Introduce trusted DMA initialization support Lu Baolu
@ 2026-09-15  7:42 ` Lu Baolu
  2026-09-15  7:42 ` [PATCH 2/5] x86/virt/tdx: Read global metadata for trusted IOMMU Lu Baolu
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Lu Baolu @ 2026-09-15  7:42 UTC (permalink / raw)
  To: iommu, x86, linux-coco, kvm
  Cc: Joerg Roedel, Will Deacon, Robin Murphy, Jason Gunthorpe,
	Kevin Tian, Dave Hansen, Kiryl Shutsemau, Rick Edgecombe,
	yilun.xu, xiaoyao.li, Chao Gao, linux-kernel, Lu Baolu

Add SEAMCALL wrappers for TDH.IOMMU.SETUP and TDH.IOMMU.CLEAR.

TDH.IOMMU.SETUP transitions an IOMMU and related I/O stack into Secure
TDX Mode. During this transition, the TDX module validates and protects
relevant I/O resources from further untrusted host access, providing the
trusted foundation required before enabling TEE-IO security protocols
(e.g. SPDM/IDE).

TDH.IOMMU.CLEAR performs the reverse operation and tears down the Secure
TDX Mode state for the target IOMMU.

tdh_iommu_setup() takes the IOMMU register base and a host-allocated
parameter page for TDX-module metadata, and returns a unique
tdx_iommu_id for subsequent operations.

tdh_iommu_clear() takes tdx_iommu_id to tear down the Secure TDX Mode
state and return the IOMMU to normal operation.

The caller is responsible for allocating the required pages and
populating the parameter page in the ABI-defined format.

See Intel TDX Connect ABI Specification [1], Section 3.2
(TDX Connect Host-Side (SEAMCALL) Interface Functions).

Thanks to Yilun for the initial draft.

[1] https://cdrdv2.intel.com/v1/dl/getContent/858625

Signed-off-by: Lu Baolu <baolu.lu@linux.intel.com>
---
 arch/x86/include/asm/tdx.h  |  2 ++
 arch/x86/virt/vmx/tdx/tdx.h |  2 ++
 arch/x86/virt/vmx/tdx/tdx.c | 56 +++++++++++++++++++++++++++++++++++++
 3 files changed, 60 insertions(+)

diff --git a/arch/x86/include/asm/tdx.h b/arch/x86/include/asm/tdx.h
index 89e97d5761d8..8b5411d3eb67 100644
--- a/arch/x86/include/asm/tdx.h
+++ b/arch/x86/include/asm/tdx.h
@@ -176,6 +176,8 @@ u64 tdh_mem_page_remove(struct tdx_td *td, u64 gpa, enum pg_level level, u64 *ex
 u64 tdh_phymem_cache_wb(bool resume);
 u64 tdh_phymem_page_wbinvd_tdr(struct tdx_td *td);
 u64 tdh_phymem_page_wbinvd_hkid(u64 hkid, kvm_pfn_t pfn);
+u64 tdh_iommu_setup(u64 reg_base, void *root, u64 *tdx_iommu_id);
+u64 tdh_iommu_clear(u64 tdx_iommu_id);
 #else
 static inline void tdx_init(void) { }
 static inline u32 tdx_get_nr_guest_keyids(void) { return 0; }
diff --git a/arch/x86/virt/vmx/tdx/tdx.h b/arch/x86/virt/vmx/tdx/tdx.h
index bdfd0e1e337a..d7beb42cf972 100644
--- a/arch/x86/virt/vmx/tdx/tdx.h
+++ b/arch/x86/virt/vmx/tdx/tdx.h
@@ -49,6 +49,8 @@
 #define TDH_SYS_SHUTDOWN		52
 #define TDH_SYS_UPDATE			53
 #define TDH_SYS_DISABLE			69
+#define TDH_IOMMU_SETUP			128
+#define TDH_IOMMU_CLEAR			129
 
 /*
  * SEAMCALL leaf:
diff --git a/arch/x86/virt/vmx/tdx/tdx.c b/arch/x86/virt/vmx/tdx/tdx.c
index 1b9ff749dd8e..012825fe1c3c 100644
--- a/arch/x86/virt/vmx/tdx/tdx.c
+++ b/arch/x86/virt/vmx/tdx/tdx.c
@@ -2034,3 +2034,59 @@ void tdx_sys_disable(void)
 	if (ret && (ret & TDX_SW_ERROR) != TDX_SW_ERROR)
 		pr_err("TDH.SYS.DISABLE failed: 0x%016llx\n", ret);
 }
+
+/*
+ * Wrapper for TDH.IOMMU.SETUP leaf, which transitions the IOMMU and related
+ * I/O stack into Secure TDX Mode.
+ */
+u64 tdh_iommu_setup(u64 reg_base, void *root, u64 *tdx_iommu_id)
+{
+	/*
+	 * The @root page format is defined by the TDX Connect ABI spec
+	 * (Table 3.35, Structure of IOMMU_MT Parameter): the first two
+	 * entries describe the size and HPA (Host Physical Address) of
+	 * the two contiguous buffers for the invalidation queue; the
+	 * remaining entries provide HPAs of IOMMU_MT_PAGES_COUNT metadata
+	 * pages. IOMMU_MT_PAGES_COUNT is obtained from trusted IOMMU global
+	 * metadata.
+	 */
+	struct tdx_module_args args = {
+		.rcx = reg_base,
+		.rdx = __pa(root),
+	};
+	u64 ret;
+
+	/*
+	 * Don't loop forever:
+	 *
+	 *  - TDX_INTERRUPTED_RESUMABLE guarantees forward progress between
+	 *    calls.
+	 *  - On TDX_INTERRUPTED_RESUMABLE, all registers except RAX remain
+	 *    unchanged.
+	 */
+	do {
+		ret = seamcall_ret(TDH_IOMMU_SETUP, &args);
+	} while (ret == TDX_INTERRUPTED_RESUMABLE);
+
+	*tdx_iommu_id = args.rcx;
+
+	return ret;
+}
+
+/*
+ * Wrapper for TDH.IOMMU.CLEAR leaf, which terminates Secure TDX Mode for the
+ * target IOMMU and restores control of related hardware resources to the host.
+ */
+u64 tdh_iommu_clear(u64 tdx_iommu_id)
+{
+	struct tdx_module_args args = {
+		.rcx = tdx_iommu_id,
+	};
+	u64 ret;
+
+	do {
+		ret = seamcall(TDH_IOMMU_CLEAR, &args);
+	} while (ret == TDX_INTERRUPTED_RESUMABLE);
+
+	return ret;
+}
-- 
2.43.0


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 2/5] x86/virt/tdx: Read global metadata for trusted IOMMU
  2026-09-15  7:42 [PATCH 0/5] iommu/vt-d: Introduce trusted DMA initialization support Lu Baolu
  2026-09-15  7:42 ` [PATCH 1/5] x86/virt/tdx: Add SEAMCALL wrappers for IOMMU setup/clear Lu Baolu
@ 2026-09-15  7:42 ` Lu Baolu
  2026-09-15  7:42 ` [PATCH 3/5] iommu/vt-d: Add interfaces for trusted DMA initialization Lu Baolu
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Lu Baolu @ 2026-09-15  7:42 UTC (permalink / raw)
  To: iommu, x86, linux-coco, kvm
  Cc: Joerg Roedel, Will Deacon, Robin Murphy, Jason Gunthorpe,
	Kevin Tian, Dave Hansen, Kiryl Shutsemau, Rick Edgecombe,
	yilun.xu, xiaoyao.li, Chao Gao, linux-kernel, Lu Baolu

Add support for reading the global metadata of the trusted IOMMU.

The trusted IOMMU feature is optionally enumerated via TDX_FEATURES0.
Check this feature bit before reading the metadata to avoid causing
the entire TDX initialization to fail on platforms that do not support
it.

The read value represents the number of pages required for the IOMMU
metadata. The host will allocate this count of pages and provide them to
the TDX module when it configures the IOMMU to operate in Secure TDX mode
via the TDH.IOMMU.SETUP SEAMCALL.

Thanks to Yilun for the initial draft.

Signed-off-by: Lu Baolu <baolu.lu@linux.intel.com>
---
 arch/x86/include/asm/tdx.h                  |  1 +
 arch/x86/include/asm/tdx_global_metadata.h  |  5 +++++
 arch/x86/virt/vmx/tdx/tdx_global_metadata.c | 20 ++++++++++++++++++++
 3 files changed, 26 insertions(+)

diff --git a/arch/x86/include/asm/tdx.h b/arch/x86/include/asm/tdx.h
index 8b5411d3eb67..9c9168822274 100644
--- a/arch/x86/include/asm/tdx.h
+++ b/arch/x86/include/asm/tdx.h
@@ -35,6 +35,7 @@
 
 /* Bit definitions of TDX_FEATURES0 metadata field */
 #define TDX_FEATURES0_TD_PRESERVING	BIT_ULL(1)
+#define TDX_FEATURES0_TDXCONNECT	BIT_ULL(6)
 #define TDX_FEATURES0_NO_RBP_MOD	BIT_ULL(18)
 
 #ifndef __ASSEMBLER__
diff --git a/arch/x86/include/asm/tdx_global_metadata.h b/arch/x86/include/asm/tdx_global_metadata.h
index 41150d546589..cf64452fe94f 100644
--- a/arch/x86/include/asm/tdx_global_metadata.h
+++ b/arch/x86/include/asm/tdx_global_metadata.h
@@ -44,12 +44,17 @@ struct tdx_sys_info_handoff {
 	u16 module_hv;
 };
 
+struct tdx_sys_info_connect {
+	u16 iommu_mt_page_count;
+};
+
 struct tdx_sys_info {
 	struct tdx_sys_info_version version;
 	struct tdx_sys_info_features features;
 	struct tdx_sys_info_tdmr tdmr;
 	struct tdx_sys_info_td_ctrl td_ctrl;
 	struct tdx_sys_info_td_conf td_conf;
+	struct tdx_sys_info_connect tdx_connect;
 };
 
 #endif
diff --git a/arch/x86/virt/vmx/tdx/tdx_global_metadata.c b/arch/x86/virt/vmx/tdx/tdx_global_metadata.c
index e49c300f23d4..2ba1c5dcdbe6 100644
--- a/arch/x86/virt/vmx/tdx/tdx_global_metadata.c
+++ b/arch/x86/virt/vmx/tdx/tdx_global_metadata.c
@@ -113,6 +113,20 @@ static int get_tdx_sys_info_handoff(struct tdx_sys_info_handoff *sysinfo_handoff
 	return 0;
 }
 
+static __init int get_tdx_sys_info_connect(struct tdx_sys_info_connect *sysinfo_connect)
+{
+	int ret;
+	u64 val;
+
+	ret = read_sys_metadata_field(0x3000000100000003, &val);
+	if (ret)
+		return ret;
+
+	sysinfo_connect->iommu_mt_page_count = val;
+
+	return 0;
+}
+
 static __init int get_tdx_sys_info(struct tdx_sys_info *sysinfo)
 {
 	int ret = 0;
@@ -129,5 +143,11 @@ static __init int get_tdx_sys_info(struct tdx_sys_info *sysinfo)
 	ret = ret ?: get_tdx_sys_info_td_ctrl(&sysinfo->td_ctrl);
 	ret = ret ?: get_tdx_sys_info_td_conf(&sysinfo->td_conf);
 
+	if (ret)
+		return ret;
+
+	if (sysinfo->features.tdx_features0 & TDX_FEATURES0_TDXCONNECT)
+		ret = get_tdx_sys_info_connect(&sysinfo->tdx_connect);
+
 	return ret;
 }
-- 
2.43.0


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 3/5] iommu/vt-d: Add interfaces for trusted DMA initialization
  2026-09-15  7:42 [PATCH 0/5] iommu/vt-d: Introduce trusted DMA initialization support Lu Baolu
  2026-09-15  7:42 ` [PATCH 1/5] x86/virt/tdx: Add SEAMCALL wrappers for IOMMU setup/clear Lu Baolu
  2026-09-15  7:42 ` [PATCH 2/5] x86/virt/tdx: Read global metadata for trusted IOMMU Lu Baolu
@ 2026-09-15  7:42 ` Lu Baolu
  2026-09-15  7:42 ` [PATCH 4/5] iommu/vt-d: Add helpers to set up and tear down TDX extensions Lu Baolu
  2026-09-15  7:42 ` [PATCH 5/5] iommu/vt-d: Reserve MSB of domain ID space for TDX module Lu Baolu
  4 siblings, 0 replies; 6+ messages in thread
From: Lu Baolu @ 2026-09-15  7:42 UTC (permalink / raw)
  To: iommu, x86, linux-coco, kvm
  Cc: Joerg Roedel, Will Deacon, Robin Murphy, Jason Gunthorpe,
	Kevin Tian, Dave Hansen, Kiryl Shutsemau, Rick Edgecombe,
	yilun.xu, xiaoyao.li, Chao Gao, linux-kernel, Lu Baolu

Add Intel VT-d interfaces to initialize and tear down TDX Connect trusted
DMA support across active IOMMUs.

Trusted DMA depends on trusted-IOMMU hardware extensions, advertised by
the TDXCS bit in the VT-d Extended Capability Register (ECAP). When
present, the TDX module can transition an IOMMU into Secure TDX Mode via
TDH.IOMMU.SETUP, and return it to normal host operation via
TDH.IOMMU.CLEAR.

In this mode, VT-d provides TDX-managed DMA translation and invalidation
resources (trusted translation root and invalidation queue), and splits
domain ID ownership so the TDX module can use its reserved namespace
independently from host-managed DIDs.

Introduce the following interfaces:
- intel_tdxc_init(): bring up TDX Connect support on all active IOMMUs
  that advertise TDXCS and have DMA translation enabled.
- intel_tdxc_exit(): tear down per-IOMMU TDX Connect state.

Initialization is best-effort at system scope: IOMMUs without required
support are skipped, while failures on attempted bring-up are treated as
errors and trigger teardown of previously initialized units.

These entry points are intended to be called by the Intel TDX Connect
platform TSM driver during module init/exit.

Signed-off-by: Lu Baolu <baolu.lu@linux.intel.com>
---
 drivers/iommu/intel/Kconfig  | 14 ++++++
 drivers/iommu/intel/Makefile |  1 +
 drivers/iommu/intel/iommu.h  |  7 +++
 include/linux/dmar.h         | 12 ++++++
 drivers/iommu/intel/iommu.c  | 11 +++++
 drivers/iommu/intel/tdxc.c   | 82 ++++++++++++++++++++++++++++++++++++
 6 files changed, 127 insertions(+)
 create mode 100644 drivers/iommu/intel/tdxc.c

diff --git a/drivers/iommu/intel/Kconfig b/drivers/iommu/intel/Kconfig
index 5471f814e073..e8c0ea79a31d 100644
--- a/drivers/iommu/intel/Kconfig
+++ b/drivers/iommu/intel/Kconfig
@@ -100,4 +100,18 @@ config INTEL_IOMMU_PERF_EVENTS
 	  to aid performance tuning and debug. These are available on modern
 	  processors which support Intel VT-d 4.0 and later.
 
+config INTEL_IOMMU_TDX_CONNECT
+	bool "Intel IOMMU support for TDX Connect"
+	depends on INTEL_TDX_HOST
+	help
+	  Enable Intel VT-d support required by TDX Connect on TDX host systems.
+
+	  Select this if the host will run TDX Connect workloads that require
+	  trusted assignment/sharing of devices with TDX guests. If enabled,
+	  the Intel IOMMU driver integrates with the TDX host module so DMA
+	  translation state used by those workloads is managed in the TDX trust
+	  domain.
+
+	  If unsure, say N.
+
 endif # INTEL_IOMMU
diff --git a/drivers/iommu/intel/Makefile b/drivers/iommu/intel/Makefile
index ada651c4a01b..25bf3b970acb 100644
--- a/drivers/iommu/intel/Makefile
+++ b/drivers/iommu/intel/Makefile
@@ -6,3 +6,4 @@ obj-$(CONFIG_INTEL_IOMMU_DEBUGFS) += debugfs.o
 obj-$(CONFIG_INTEL_IOMMU_SVM) += svm.o
 obj-$(CONFIG_IRQ_REMAP) += irq_remapping.o
 obj-$(CONFIG_INTEL_IOMMU_PERF_EVENTS) += perfmon.o
+obj-$(CONFIG_INTEL_IOMMU_TDX_CONNECT) += tdxc.o
diff --git a/drivers/iommu/intel/iommu.h b/drivers/iommu/intel/iommu.h
index 23dbe6c24439..452a381e6a40 100644
--- a/drivers/iommu/intel/iommu.h
+++ b/drivers/iommu/intel/iommu.h
@@ -187,6 +187,7 @@
  */
 
 #define ecap_pms(e)		(((e) >> 51) & 0x1)
+#define ecap_tdxcs(e)		(((e) >> 50) & 0x1)
 #define ecap_rps(e)		(((e) >> 49) & 0x1)
 #define ecap_smpwc(e)		(((e) >> 48) & 0x1)
 #define ecap_flts(e)		(((e) >> 47) & 0x1)
@@ -1323,6 +1324,12 @@ static inline void intel_iommu_debugfs_create_dev_pasid(struct dev_pasid_info *d
 static inline void intel_iommu_debugfs_remove_dev_pasid(struct dev_pasid_info *dev_pasid) {}
 #endif /* CONFIG_INTEL_IOMMU_DEBUGFS */
 
+#ifdef CONFIG_INTEL_IOMMU_TDX_CONNECT
+extern bool intel_tdxc_initialized;
+#else
+#define intel_tdxc_initialized (0)
+#endif /* CONFIG_INTEL_IOMMU_TDX_CONNECT */
+
 extern const struct attribute_group *intel_iommu_groups[];
 struct context_entry *iommu_context_addr(struct intel_iommu *iommu, u8 bus,
 					 u8 devfn, int alloc);
diff --git a/include/linux/dmar.h b/include/linux/dmar.h
index 63e35df2cef4..361c0d4a45fb 100644
--- a/include/linux/dmar.h
+++ b/include/linux/dmar.h
@@ -167,6 +167,18 @@ static inline int dmar_iommu_hotplug(struct dmar_drhd_unit *dmaru, bool insert)
 }
 #endif /* CONFIG_INTEL_IOMMU */
 
+#ifdef CONFIG_INTEL_IOMMU_TDX_CONNECT
+int intel_tdxc_init(void);
+void intel_tdxc_exit(void);
+#else
+static inline int intel_tdxc_init(void)
+{
+	return -EOPNOTSUPP;
+}
+
+static inline void intel_tdxc_exit(void) { }
+#endif /* CONFIG_INTEL_IOMMU_TDX_CONNECT */
+
 #ifdef CONFIG_IRQ_REMAP
 extern int dmar_ir_hotplug(struct dmar_drhd_unit *dmaru, bool insert);
 #else  /* CONFIG_IRQ_REMAP */
diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c
index 2e3b3ab216f8..e88457d96b53 100644
--- a/drivers/iommu/intel/iommu.c
+++ b/drivers/iommu/intel/iommu.c
@@ -2168,6 +2168,17 @@ static int intel_iommu_add(struct dmar_drhd_unit *dmaru)
 	iommu_set_root_entry(iommu);
 	iommu_enable_translation(iommu);
 
+	/*
+	 * If an IOMMU is hot-added after intel_tdxc_initialized is set, it is
+	 * not enrolled into TDX secure mode. Ideally this should be integrated
+	 * with dmar_iommu_hotplug() so intel_iommu_bringup_tdxc() can run on
+	 * hotplug. This is currently skipped due to lack of hardware validation.
+	 * Log this limitation to make it visible.
+	 */
+	if (intel_tdxc_initialized && ecap_tdxcs(iommu->ecap))
+		pr_info("Trusted DMA for TEE is not enabled on hot-added IOMMU %s\n",
+			iommu->name);
+
 	iommu_disable_protect_mem_regions(iommu);
 	return 0;
 
diff --git a/drivers/iommu/intel/tdxc.c b/drivers/iommu/intel/tdxc.c
new file mode 100644
index 000000000000..559c752a1527
--- /dev/null
+++ b/drivers/iommu/intel/tdxc.c
@@ -0,0 +1,82 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * tdxc.c - Intel TDX Connect Extensions support
+ *
+ * Copyright (C) 2026 Intel Corporation
+ */
+
+#define pr_fmt(fmt)	"DMAR: " fmt
+
+#include <linux/pci.h>
+#include <asm/vmx.h>
+#include <asm/tdx.h>
+#include "iommu.h"
+
+bool intel_tdxc_initialized;
+
+static int intel_iommu_bringup_tdxc(struct intel_iommu *iommu, unsigned int nr_pages)
+{
+	/*
+	 * Nothing to do if the iommu doesn't support TDX extension or the
+	 * DMA translation has not been enabled.
+	 */
+	if (!ecap_tdxcs(iommu->ecap) || !(iommu->gcmd & DMA_GCMD_TE))
+		return 0;
+
+	/* Bring-up is not complete yet; report as unsupported for now. */
+	return -EOPNOTSUPP;
+}
+
+static void intel_iommu_teardown_tdxc(struct intel_iommu *iommu)
+{
+}
+
+void intel_tdxc_exit(void)
+{
+	struct dmar_drhd_unit *drhd;
+	struct intel_iommu *iommu;
+
+	guard(rwsem_write)(&dmar_global_lock);
+	if (!intel_tdxc_initialized)
+		return;
+
+	for_each_active_iommu(iommu, drhd)
+		intel_iommu_teardown_tdxc(iommu);
+	intel_tdxc_initialized = false;
+}
+EXPORT_SYMBOL_GPL(intel_tdxc_exit);
+
+int intel_tdxc_init(void)
+{
+	const struct tdx_sys_info *tdx_sysinfo = tdx_get_sysinfo();
+	struct dmar_drhd_unit *drhd;
+	unsigned int mt_page_count;
+	struct intel_iommu *iommu;
+	int ret;
+
+	if (!intel_iommu_enabled)
+		return -EOPNOTSUPP;
+
+	if (!tdx_sysinfo ||
+	    !(tdx_sysinfo->features.tdx_features0 & TDX_FEATURES0_TDXCONNECT))
+		return -EOPNOTSUPP;
+
+	mt_page_count = tdx_sysinfo->tdx_connect.iommu_mt_page_count;
+	guard(rwsem_write)(&dmar_global_lock);
+	if (intel_tdxc_initialized)
+		return 0;
+
+	for_each_active_iommu(iommu, drhd) {
+		ret = intel_iommu_bringup_tdxc(iommu, mt_page_count);
+		if (ret) {
+			for_each_active_iommu(iommu, drhd)
+				intel_iommu_teardown_tdxc(iommu);
+
+			return ret;
+		}
+	}
+	intel_tdxc_initialized = true;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(intel_tdxc_init);
-- 
2.43.0


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 4/5] iommu/vt-d: Add helpers to set up and tear down TDX extensions
  2026-09-15  7:42 [PATCH 0/5] iommu/vt-d: Introduce trusted DMA initialization support Lu Baolu
                   ` (2 preceding siblings ...)
  2026-09-15  7:42 ` [PATCH 3/5] iommu/vt-d: Add interfaces for trusted DMA initialization Lu Baolu
@ 2026-09-15  7:42 ` Lu Baolu
  2026-09-15  7:42 ` [PATCH 5/5] iommu/vt-d: Reserve MSB of domain ID space for TDX module Lu Baolu
  4 siblings, 0 replies; 6+ messages in thread
From: Lu Baolu @ 2026-09-15  7:42 UTC (permalink / raw)
  To: iommu, x86, linux-coco, kvm
  Cc: Joerg Roedel, Will Deacon, Robin Murphy, Jason Gunthorpe,
	Kevin Tian, Dave Hansen, Kiryl Shutsemau, Rick Edgecombe,
	yilun.xu, xiaoyao.li, Chao Gao, linux-kernel, Lu Baolu

Implement per-IOMMU TDX Connect bring-up/tear-down helpers.

For each active IOMMU that advertises TDXCS and has DMA translation
enabled, the driver allocates and populates the IOMMU_MT parameter
layout required by TDH.IOMMU.SETUP:

- two contiguous invalidation-queue buffers, and
- IOMMU_MT_PAGES_COUNT metadata pages.

The driver invokes tdh_iommu_setup() to transition the IOMMU into Secure
TDX Mode, then stores the returned tdx_iommu_id and allocation state for
later cleanup.

On teardown, or on partial-init rollback, the driver calls
tdh_iommu_clear() for each initialized IOMMU and frees all associated
IOMMU_MT pages.

Completion handling:

- TDX_SUCCESS: setup completed successfully.
- TDX_OPERAND_INVALID: treated as "not supported on this IOMMU" and
  skipped.
- all other return codes: treated as failures and abort initialization
  with rollback.

No explicit clflush is needed for pages shared with the TDX module in
this flow, as cache coherency is handled architecturally.

Signed-off-by: Lu Baolu <baolu.lu@linux.intel.com>
---
 drivers/iommu/intel/iommu.h |  17 ++++
 drivers/iommu/intel/dmar.c  |   3 +
 drivers/iommu/intel/tdxc.c  | 150 ++++++++++++++++++++++++++++++++++++
 3 files changed, 170 insertions(+)

diff --git a/drivers/iommu/intel/iommu.h b/drivers/iommu/intel/iommu.h
index 452a381e6a40..7bf70113c4c8 100644
--- a/drivers/iommu/intel/iommu.h
+++ b/drivers/iommu/intel/iommu.h
@@ -680,6 +680,17 @@ struct iommu_pmu {
 #define IOMMU_IRQ_ID_OFFSET_PRQ		(DMAR_UNITS_SUPPORTED)
 #define IOMMU_IRQ_ID_OFFSET_PERF	(2 * DMAR_UNITS_SUPPORTED)
 
+/*
+ * Represents a list of pages for TDX Module defined IOMMU_MT object.
+ * Typically it uses a "root page" as the medium to exchange a list of
+ * data pages between host and TDX Module.
+ */
+struct tdxc_pages {
+	u64 *root;
+	void **pages;
+	unsigned int nr_entries;
+};
+
 struct intel_iommu {
 	void __iomem	*reg; /* Pointer to hardware regs, virtual addr */
 	u64 		reg_phys; /* physical address of hw register set */
@@ -736,6 +747,12 @@ struct intel_iommu {
 	void *perf_statistic;
 
 	struct iommu_pmu *pmu;
+#ifdef CONFIG_INTEL_IOMMU_TDX_CONNECT
+	/* mutex to protect below tdx state data */
+	struct mutex tdx_lock;
+	u64 tdx_iommu_id;
+	struct tdxc_pages *mt_pages;
+#endif
 };
 
 /* PCI domain-device relationship */
diff --git a/drivers/iommu/intel/dmar.c b/drivers/iommu/intel/dmar.c
index ba675b08cd20..310b8d0c4132 100644
--- a/drivers/iommu/intel/dmar.c
+++ b/drivers/iommu/intel/dmar.c
@@ -1175,6 +1175,9 @@ static int alloc_iommu(struct dmar_drhd_unit *drhd)
 	ida_init(&iommu->domain_ida);
 	mutex_init(&iommu->did_lock);
 	iommu->max_domain_id = cap_ndoms(iommu->cap);
+#ifdef CONFIG_INTEL_IOMMU_TDX_CONNECT
+	mutex_init(&iommu->tdx_lock);
+#endif
 
 	ver = readl(iommu->reg + DMAR_VER_REG);
 	pr_info("%s: reg_base_addr %llx ver %d:%d cap %llx ecap %llx\n",
diff --git a/drivers/iommu/intel/tdxc.c b/drivers/iommu/intel/tdxc.c
index 559c752a1527..b5dfdeeb23db 100644
--- a/drivers/iommu/intel/tdxc.c
+++ b/drivers/iommu/intel/tdxc.c
@@ -10,12 +10,128 @@
 #include <linux/pci.h>
 #include <asm/vmx.h>
 #include <asm/tdx.h>
+
+#include "../iommu-pages.h"
 #include "iommu.h"
 
+#define IQ_BUFFERS_NUM		2
+#define IQ_BUFFER_PAGES		2
+#define IQ_BUFFER_SIZE		SZ_8K
+
 bool intel_tdxc_initialized;
 
+static void free_mt_pages(struct tdxc_pages *array)
+{
+	if (!array)
+		return;
+
+	for (int i = 0; i < array->nr_entries; i++)
+		iommu_free_pages(array->pages[i]);
+
+	iommu_free_pages(array->root);
+	kfree(array->pages);
+	kfree(array);
+}
+
+DEFINE_FREE(free_mt_pages, struct tdxc_pages *, free_mt_pages(_T))
+
+static void **alloc_mt_pages(unsigned int nr_entries, int node)
+{
+	void **pages;
+	void *vaddr;
+	int i;
+
+	pages = kzalloc_objs(*pages, nr_entries);
+	if (!pages)
+		return NULL;
+
+	/* Allocate two contiguous buffers for the invalidation queue. */
+	pages[0] = iommu_alloc_pages_node_sz(node, GFP_KERNEL, IQ_BUFFER_SIZE);
+	if (!pages[0])
+		goto free_pages;
+
+	pages[1] = iommu_alloc_pages_node_sz(node, GFP_KERNEL, IQ_BUFFER_SIZE);
+	if (!pages[1])
+		goto free_pages;
+
+	/* Allocate the required number of pages for the IOMMU metadata. */
+	for (i = IQ_BUFFERS_NUM; i < nr_entries; i++) {
+		vaddr = iommu_alloc_pages_node_sz(node, GFP_KERNEL, SZ_4K);
+		if (!vaddr)
+			goto free_pages;
+		pages[i] = vaddr;
+	}
+
+	return pages;
+free_pages:
+	for (i = 0; i < nr_entries; i++) {
+		if (!pages[i])
+			break;
+
+		iommu_free_pages(pages[i]);
+	}
+	kfree(pages);
+
+	return NULL;
+}
+
+static void populate_mt_pages(struct tdxc_pages *array)
+{
+	unsigned int nr_entries = array->nr_entries;
+	void **pages = array->pages;
+	u64 *entries = array->root;
+	int i;
+
+	/*
+	 * Populate the parameter for the TDH_IOMMU_SETUP SEAMCALL according to
+	 * the format defined in "Table 3.35: Structure of IOMMU_MT Parameter"
+	 * of the ABI reference specification.
+	 */
+	for (i = 0; i < nr_entries; i++) {
+		entries[i] = __pa(pages[i]);
+		if (i < IQ_BUFFERS_NUM)
+			entries[i] |= IQ_BUFFER_PAGES;
+	}
+}
+
+static struct tdxc_pages *tdxc_alloc_mt_pages(struct intel_iommu *iommu,
+					      unsigned int nr_mt_pages)
+{
+	unsigned int nr_entries = nr_mt_pages + IQ_BUFFERS_NUM;
+	struct tdxc_pages *array;
+
+	if (!nr_mt_pages || nr_mt_pages > (PAGE_SIZE / sizeof(u64) - IQ_BUFFERS_NUM))
+		return NULL;
+
+	array = kzalloc_obj(*array);
+	if (!array)
+		return NULL;
+
+	array->root = iommu_alloc_pages_node_sz(iommu->node, GFP_KERNEL, SZ_4K);
+	if (!array->root)
+		goto free_array;
+
+	array->nr_entries = nr_entries;
+	array->pages = alloc_mt_pages(nr_entries, iommu->node);
+	if (!array->pages)
+		goto free_root;
+
+	populate_mt_pages(array);
+
+	return array;
+
+free_root:
+	iommu_free_pages(array->root);
+free_array:
+	kfree(array);
+	return NULL;
+}
+
 static int intel_iommu_bringup_tdxc(struct intel_iommu *iommu, unsigned int nr_pages)
 {
+	struct dmar_drhd_unit *drhd = iommu->drhd;
+	u64 r, tdx_iommu_id;
+
 	/*
 	 * Nothing to do if the iommu doesn't support TDX extension or the
 	 * DMA translation has not been enabled.
@@ -23,12 +139,46 @@ static int intel_iommu_bringup_tdxc(struct intel_iommu *iommu, unsigned int nr_p
 	if (!ecap_tdxcs(iommu->ecap) || !(iommu->gcmd & DMA_GCMD_TE))
 		return 0;
 
+	struct tdxc_pages *iommu_mt __free(free_mt_pages) =
+			tdxc_alloc_mt_pages(iommu, nr_pages);
+	if (!iommu_mt)
+		return -ENOMEM;
+
+	guard(mutex)(&iommu->tdx_lock);
+	r = tdh_iommu_setup(drhd->reg_base_addr, iommu_mt->root, &tdx_iommu_id);
+	/* TDX Extension is not supported on this iommu. Nothing to do. */
+	if ((r & TDX_SEAMCALL_STATUS_MASK) == TDX_OPERAND_INVALID)
+		return 0;
+	if (r) {
+		pr_err("%s: TDH.IOMMU.SETUP failed, status 0x%llx\n", iommu->name, r);
+		return -EFAULT;
+	}
+
+	iommu->tdx_iommu_id = tdx_iommu_id;
+	iommu->mt_pages = no_free_ptr(iommu_mt);
+
 	/* Bring-up is not complete yet; report as unsupported for now. */
 	return -EOPNOTSUPP;
 }
 
 static void intel_iommu_teardown_tdxc(struct intel_iommu *iommu)
 {
+	u64 r;
+
+	guard(mutex)(&iommu->tdx_lock);
+
+	if (!iommu->mt_pages)
+		return;
+
+	r = tdh_iommu_clear(iommu->tdx_iommu_id);
+	if (r) {
+		pr_err("%s: TDH.IOMMU.CLEAR failed, status 0x%llx\n", iommu->name, r);
+		return;
+	}
+
+	free_mt_pages(iommu->mt_pages);
+	iommu->mt_pages = NULL;
+	iommu->tdx_iommu_id = 0;
 }
 
 void intel_tdxc_exit(void)
-- 
2.43.0


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 5/5] iommu/vt-d: Reserve MSB of domain ID space for TDX module
  2026-09-15  7:42 [PATCH 0/5] iommu/vt-d: Introduce trusted DMA initialization support Lu Baolu
                   ` (3 preceding siblings ...)
  2026-09-15  7:42 ` [PATCH 4/5] iommu/vt-d: Add helpers to set up and tear down TDX extensions Lu Baolu
@ 2026-09-15  7:42 ` Lu Baolu
  4 siblings, 0 replies; 6+ messages in thread
From: Lu Baolu @ 2026-09-15  7:42 UTC (permalink / raw)
  To: iommu, x86, linux-coco, kvm
  Cc: Joerg Roedel, Will Deacon, Robin Murphy, Jason Gunthorpe,
	Kevin Tian, Dave Hansen, Kiryl Shutsemau, Rick Edgecombe,
	yilun.xu, xiaoyao.li, Chao Gao, linux-kernel, Lu Baolu

When an Intel IOMMU is enabled for TDX Connect, the VT-d DID namespace
must be split so the TDX module can use the MSB-tagged half for trusted
DMA translations, while the host/VMM uses the lower half.

After TDH.IOMMU.SETUP succeeds, restrict host domain ID allocation to the
lower half of the DID space by capping max_domain_id to ndoms / 2. Before
applying the cap, verify that no allocated domain IDs already exist in
the upper half; if they do, abort bring-up and roll back the per-IOMMU
TDX setup.

On teardown, restore max_domain_id to the full DID range.

This matches Intel TDX Connect architecture requirements for IOTLB/DID
isolation between TEE and non-TEE translations.

Signed-off-by: Lu Baolu <baolu.lu@linux.intel.com>
---
 drivers/iommu/intel/tdxc.c | 20 ++++++++++++++++++--
 1 file changed, 18 insertions(+), 2 deletions(-)

diff --git a/drivers/iommu/intel/tdxc.c b/drivers/iommu/intel/tdxc.c
index b5dfdeeb23db..1ca03257d456 100644
--- a/drivers/iommu/intel/tdxc.c
+++ b/drivers/iommu/intel/tdxc.c
@@ -129,6 +129,7 @@ static struct tdxc_pages *tdxc_alloc_mt_pages(struct intel_iommu *iommu,
 
 static int intel_iommu_bringup_tdxc(struct intel_iommu *iommu, unsigned int nr_pages)
 {
+	unsigned long ndoms = cap_ndoms(iommu->cap);
 	struct dmar_drhd_unit *drhd = iommu->drhd;
 	u64 r, tdx_iommu_id;
 
@@ -144,6 +145,10 @@ static int intel_iommu_bringup_tdxc(struct intel_iommu *iommu, unsigned int nr_p
 	if (!iommu_mt)
 		return -ENOMEM;
 
+	guard(mutex)(&iommu->did_lock);
+	if (ida_find_first_range(&iommu->domain_ida, ndoms >> 1, ndoms - 1) > 0)
+		return -EBUSY;
+
 	guard(mutex)(&iommu->tdx_lock);
 	r = tdh_iommu_setup(drhd->reg_base_addr, iommu_mt->root, &tdx_iommu_id);
 	/* TDX Extension is not supported on this iommu. Nothing to do. */
@@ -154,17 +159,27 @@ static int intel_iommu_bringup_tdxc(struct intel_iommu *iommu, unsigned int nr_p
 		return -EFAULT;
 	}
 
+	/*
+	 * Intel TDX Connect Architecture Specification, Section 2.2 Trusted DMA
+	 *
+	 * When IOMMU is enabled to support TDX Connect, the IOMMU restricts
+	 * the VMM’s DID setting, reserving the MSB bit for the TDX module. The
+	 * TDX module always sets this reserved bit on the trusted DMA table.
+	 */
+	iommu->max_domain_id = ndoms >> 1;
 	iommu->tdx_iommu_id = tdx_iommu_id;
 	iommu->mt_pages = no_free_ptr(iommu_mt);
 
-	/* Bring-up is not complete yet; report as unsupported for now. */
-	return -EOPNOTSUPP;
+	pr_info("%s: trusted DMA for TEE/IO initialized\n", iommu->name);
+
+	return 0;
 }
 
 static void intel_iommu_teardown_tdxc(struct intel_iommu *iommu)
 {
 	u64 r;
 
+	guard(mutex)(&iommu->did_lock);
 	guard(mutex)(&iommu->tdx_lock);
 
 	if (!iommu->mt_pages)
@@ -179,6 +194,7 @@ static void intel_iommu_teardown_tdxc(struct intel_iommu *iommu)
 	free_mt_pages(iommu->mt_pages);
 	iommu->mt_pages = NULL;
 	iommu->tdx_iommu_id = 0;
+	iommu->max_domain_id = cap_ndoms(iommu->cap);
 }
 
 void intel_tdxc_exit(void)
-- 
2.43.0


^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2026-09-15  7:55 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-15  7:42 [PATCH 0/5] iommu/vt-d: Introduce trusted DMA initialization support Lu Baolu
2026-09-15  7:42 ` [PATCH 1/5] x86/virt/tdx: Add SEAMCALL wrappers for IOMMU setup/clear Lu Baolu
2026-09-15  7:42 ` [PATCH 2/5] x86/virt/tdx: Read global metadata for trusted IOMMU Lu Baolu
2026-09-15  7:42 ` [PATCH 3/5] iommu/vt-d: Add interfaces for trusted DMA initialization Lu Baolu
2026-09-15  7:42 ` [PATCH 4/5] iommu/vt-d: Add helpers to set up and tear down TDX extensions Lu Baolu
2026-09-15  7:42 ` [PATCH 5/5] iommu/vt-d: Reserve MSB of domain ID space for TDX module Lu Baolu

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®