Setup all trusted IOMMUs on TDX Connect initialization and clear all on TDX Connect removal. Trusted IOMMU setup is the pre-condition for all following TDX Connect operations such as SPDM/IDE setup. It is more of a platform configuration than a standalone IOMMU configuration, so put the implementation in tdx-host driver. There is no dedicated way to enumerate which IOMMU devices support trusted operations. The host has to call TDH.IOMMU.SETUP on all IOMMU devices and tell their trusted capability by the return value. Suggested-by: Lu Baolu Signed-off-by: Xu Yilun Signed-off-by: Dan Williams --- drivers/virt/coco/tdx-host/tdx-host.c | 85 +++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/drivers/virt/coco/tdx-host/tdx-host.c b/drivers/virt/coco/tdx-host/tdx-host.c index 982c928fae86..3cd19966a61b 100644 --- a/drivers/virt/coco/tdx-host/tdx-host.c +++ b/drivers/virt/coco/tdx-host/tdx-host.c @@ -5,6 +5,7 @@ * Copyright (C) 2025 Intel Corporation */ +#include #include #include #include @@ -122,6 +123,82 @@ static void unregister_link_tsm(void *link) tsm_unregister(link); } +static DEFINE_XARRAY(tlink_iommu_xa); + +static void tdx_iommu_clear(u64 iommu_id, struct tdx_page_array *iommu_mt) +{ + u64 r; + + r = tdh_iommu_clear(iommu_id, iommu_mt); + if (r) { + pr_err("%s fail to clear tdx iommu\n", __func__); + goto leak; + } + + if (tdx_page_array_ctrl_release(iommu_mt, iommu_mt->nr_pages, + page_to_phys(iommu_mt->root))) { + pr_err("%s fail to release metadata pages\n", __func__); + goto leak; + } + + return; + +leak: + tdx_page_array_ctrl_leak(iommu_mt); +} + +static int tdx_iommu_enable_one(struct dmar_drhd_unit *drhd) +{ + unsigned int nr_pages = tdx_sysinfo->connect.iommu_mt_page_count; + u64 r, iommu_id; + int ret; + + struct tdx_page_array *iommu_mt __free(tdx_page_array_free) = + tdx_page_array_create_iommu_mt(1, nr_pages); + if (!iommu_mt) + return -ENOMEM; + + r = tdh_iommu_setup(drhd->reg_base_addr, iommu_mt, &iommu_id); + /* This drhd doesn't support tdx mode, skip. */ + if ((r & TDX_SEAMCALL_STATUS_MASK) == TDX_OPERAND_INVALID) + return 0; + + if (r) { + pr_err("fail to enable tdx mode for DRHD[0x%llx]\n", + drhd->reg_base_addr); + return -EFAULT; + } + + ret = xa_insert(&tlink_iommu_xa, (unsigned long)iommu_id, + no_free_ptr(iommu_mt), GFP_KERNEL); + if (ret) { + tdx_iommu_clear(iommu_id, iommu_mt); + return ret; + } + + return 0; +} + +static void tdx_iommu_disable_all(void *data) +{ + struct tdx_page_array *iommu_mt; + unsigned long iommu_id; + + xa_for_each(&tlink_iommu_xa, iommu_id, iommu_mt) + tdx_iommu_clear(iommu_id, iommu_mt); +} + +static int tdx_iommu_enable_all(void) +{ + int ret; + + ret = do_for_each_drhd_unit(tdx_iommu_enable_one); + if (ret) + tdx_iommu_disable_all(NULL); + + return ret; +} + static int __maybe_unused tdx_connect_init(struct device *dev) { struct tsm_dev *link; @@ -151,6 +228,14 @@ static int __maybe_unused tdx_connect_init(struct device *dev) if (ret) return dev_err_probe(dev, ret, "Enable extension failed\n"); + ret = tdx_iommu_enable_all(); + if (ret) + return dev_err_probe(dev, ret, "Enable tdx iommu failed\n"); + + ret = devm_add_action_or_reset(dev, tdx_iommu_disable_all, NULL); + if (ret) + return ret; + link = tsm_register(dev, &tdx_link_ops); if (IS_ERR(link)) return dev_err_probe(dev, PTR_ERR(link), -- 2.25.1