Re: [PATCH v2 1/4] x86/virt/tdx: Add SEAMCALL wrappers for IOMMU setup/clear
From: Xu Yilun
Date: Thu Oct 15 2026 - 02:38:35 EST
On Thu, Oct 15, 2026 at 09:07:02AM +0800, Lu Baolu wrote:
> Add SEAMCALL wrappers for TDH.IOMMU.SETUP and TDH.IOMMU.CLEAR.
We need this patch been reviewed by x86 reviewers. x86 convention
doesn't start with the implementation, so remove it.
We can start with a generic description of what the 2 SEAMCALLs are
doing in general and why. Maybe move some of your description in next
paragraph ahead.
>
> 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.
>
> 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 Xu Yilun for the initial draft.
Feel free to drop it. Not that important.
>
> [1] https://cdrdv2.intel.com/v1/dl/getContent/858625
>
> Signed-off-by: Lu Baolu <baolu.lu@xxxxxxxxxxxxxxx>
> ---
> 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..5d31c1b0890a 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.
Other tdh_*() wrappers don't have the general descriptions. We all know
these are wrappers for some SEAMCALL leafs.
So if there is no special thing to note, remove it.
> + *
> + * 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.
> + */
> +u64 tdh_iommu_setup(u64 reg_base, void *root, u64 *tdx_iommu_id)
IIUC, the void *root is a vague type, no type safty. Is it better we
define an explicit structure that mirrors the in-memory ABI? Something
like:
struct tdx_iommu_metadata {
u64 iq1_pa;
u64 iq2_pa;
DECLARE_FLEX_ARRAY(u64, mt_pa);
};
Or,
struct tdx_iommu_metadata {
u64 iq1_pa;
u64 iq2_pa;
u64 mt_pa[510];
};
static_assert(sizeof(struct tdx_iommu_metadata) == 4096);
> +{
> + 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;
> +}
> +EXPORT_SYMBOL_FOR_MODULES(tdh_iommu_setup, "tdx-host");
> +
> +/*
> + * 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;
> +}
> +EXPORT_SYMBOL_FOR_MODULES(tdh_iommu_clear, "tdx-host");
> --
> 2.43.0
>