Re: [PATCH v2 4/4] coco/tdx-host: Initialize trusted DMA on all capable IOMMUs

From: Xu Yilun

Date: Thu Oct 15 2026 - 04:43:00 EST


On Thu, Oct 15, 2026 at 09:07:05AM +0800, Lu Baolu wrote:
> Assigning a device to a TD for private memory access requires that DMA
> from that device be translated by structures the TDX module controls.
> An Intel IOMMU that implements the trusted DMA extension provides a
> parallel set of translation and invalidation resources for this purpose.
> These become usable only after the TDX module has placed the IOMMU into
> TDX mode, at which point the trusted resources belong exclusively to the

I think at this point we first need to reason about the connection between
IOMMU TDX mode and the PCIe link encryption. The device private memory
access can be placed in cover-letter as the big picture of TDISP.

I can see we may need to touch a bit about trusted DMA translation at
this phase, because the TDH.IOMMU.SETUP requires the VMM to provide 2
contiguous Invalidation Queue buffers, which leaks the trusted DMA
internals a bit. But I feel this should not be a first topic.

> TDX module, while the host retains control of the regular translation
> path. The transition is performed by TDH.IOMMU.SETUP, which takes the
> register base address of the target IOMMU together with a parameter
> page describing memory the host has allocated for the TDX module to
> manage trusted DMA on that unit.
>
> Perform this initialization from the tdx-host driver by walking every
> IOMMU unit through dmar_tdxcs_iommu_init() and initialize the ones whose
> IOMMU implements the extension.
>
> For each such unit the driver builds the IOMMU_MT parameter layout that
> TDH.IOMMU.SETUP expects: two contiguous buffers for the trusted
> invalidation queue, followed by the number of metadata pages the TDX
> module reports in its global metadata. The pages are allocated on the
> NUMA node of the IOMMU. The root page is passed to the TDX module, which
> returns an identifier naming the initialized IOMMU. That identifier and
> the pages backing it are tracked so the state can be released later.
>
> Releasing the state is the reverse: TDH.IOMMU.CLEAR tears down the
> trusted DMA resources established by TDH.IOMMU.SETUP, after which the
> metadata pages can be freed and the VT-d driver told to lift the domain
> ID restriction applied during initialization. If TDH.IOMMU.CLEAR fails
> the pages are not freed, as the TDX module may still hold references to
> them.
>
> Rename tdx-host.c to main.c and build the module from a tdx-host-y
> object list instead. New functionality can then be added as separate
> source files instead of putting everything in a single file.
>
> The code is built only when its dependencies are present, gated by
> CONFIG_TDX_CONNECT.
>
> Signed-off-by: Lu Baolu <baolu.lu@xxxxxxxxxxxxxxx>
> ---
> drivers/virt/coco/tdx-host/Kconfig | 6 +
> drivers/virt/coco/tdx-host/Makefile | 3 +
> drivers/virt/coco/tdx-host/private.h | 33 +++
> drivers/virt/coco/tdx-host/iommu.c | 255 ++++++++++++++++++
> .../virt/coco/tdx-host/{tdx-host.c => main.c} | 21 +-
> 5 files changed, 317 insertions(+), 1 deletion(-)
> create mode 100644 drivers/virt/coco/tdx-host/private.h
> create mode 100644 drivers/virt/coco/tdx-host/iommu.c
> rename drivers/virt/coco/tdx-host/{tdx-host.c => main.c} (92%)
>
> diff --git a/drivers/virt/coco/tdx-host/Kconfig b/drivers/virt/coco/tdx-host/Kconfig
> index 57d0c01a4357..62d91d4b41fa 100644
> --- a/drivers/virt/coco/tdx-host/Kconfig
> +++ b/drivers/virt/coco/tdx-host/Kconfig
> @@ -4,3 +4,9 @@ config TDX_HOST_SERVICES
> select FW_LOADER
> select FW_UPLOAD
> default m
> +
> +config TDX_CONNECT

Maybe we don't need the configuration. Now we turn on all TDX features
that Linux knows about all the time.

Architecturally, TDX Connect is an default-off feature. But Linux enables
it regardlessly. It is questionable we then have a configuration that
disable the driver that supports the feature.

> + def_bool y
> + depends on TDX_HOST_SERVICES
> + depends on PCI_TSM
> + depends on INTEL_IOMMU

[..]

> +static int tdx_iommu_setup(struct dmar_drhd_unit *drhd)
> +{
> + const struct tdx_sys_info *tdx_sysinfo = tdx_get_sysinfo();
> + unsigned int mt_page_count = tdx_sysinfo->tdx_connect.iommu_mt_page_count;
> + struct tdx_iommu_state *state;
> + u64 r, tdx_iommu_id;
> + int ret;
> +
> + struct tdx_iommu_state *iommu_state __free(kfree) = kzalloc_obj(*iommu_state);
> + if (!iommu_state)
> + return -ENOMEM;
> +
> + struct tdx_iommu_pages *iommu_mt __free(free_mt_pages) =
> + tdx_iommu_alloc_mt_pages(drhd, mt_page_count);
> + if (!iommu_mt)
> + return -ENOMEM;
> +
> + 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 -EOPNOTSUPP;

Enable all IOMMUs regardless brings this problem. We need to try on
every IOMMU, check the TDX error code to see if it is not supported (then
skip) or failed. It's also sad to me that -EOPNOTSUPP is defined as the
API between tdx-host and intel iommu driver.

An alternative could be, bringing up one IOMMU when an associated
device requests link encryption. Then a no-tdx-support iommu fails the
encryption request naturally.

Open to discussion.