[PATCH v1 20/26] coco/tdx-host: Add a helper to exchange SPDM messages through DOE

Xu Yilun
2025-11-17T02:39:07Z

From: Zhenzhong Duan <zhenzhong.duan@intel.com>

TDX host uses this function to exchange TDX Module encrypted data with
devices via SPDM. It is unfortunate that TDX passes raw DOE frames with
headers included and the PCI DOE core wants payloads separated from
headers.

This conversion code is about the same amount of work as teaching the PCI
DOE driver to support raw frames. Unless and until another raw frame use
case shows up, just do this conversion in the TDX TSM driver.

Signed-off-by: Zhenzhong Duan <zhenzhong.duan@intel.com>
Co-developed-by: Xu Yilun <yilun.xu@linux.intel.com>
Signed-off-by: Xu Yilun <yilun.xu@linux.intel.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
---
drivers/virt/coco/tdx-host/tdx-host.c | 61 +++++++++++++++++++++++++++
1 file changed, 61 insertions(+)

diff --git a/drivers/virt/coco/tdx-host/tdx-host.c b/drivers/virt/coco/tdx-host/tdx-host.c
index 3cd19966a61b..f0151561e00e 100644
--- a/drivers/virt/coco/tdx-host/tdx-host.c
+++ b/drivers/virt/coco/tdx-host/tdx-host.c
@@ -5,10 +5,12 @@
* Copyright (C) 2025 Intel Corporation
*/

+#include <linux/bitfield.h>
#include <linux/dmar.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/pci.h>
+#include <linux/pci-doe.h>
#include <linux/pci-tsm.h>
#include <linux/tsm.h>
#include <linux/device/faux.h>
@@ -41,6 +43,65 @@ static struct tdx_link *to_tdx_link(struct pci_tsm *tsm)
return container_of(tsm, struct tdx_link, pci.base_tsm);
}

+#define PCI_DOE_DATA_OBJECT_HEADER_1_OFFSET 0
+#define PCI_DOE_DATA_OBJECT_HEADER_2_OFFSET 4
+#define PCI_DOE_DATA_OBJECT_HEADER_SIZE 8
+#define PCI_DOE_DATA_OBJECT_PAYLOAD_OFFSET PCI_DOE_DATA_OBJECT_HEADER_SIZE
+
+#define PCI_DOE_PROTOCOL_SECURE_SPDM 2
+
+static int __maybe_unused tdx_spdm_msg_exchange(struct tdx_link *tlink,
+ void *request, size_t request_sz,
+ void *response, size_t response_sz)
+{
+ struct pci_dev *pdev = tlink->pci.base_tsm.pdev;
+ void *req_pl_addr, *resp_pl_addr;
+ size_t req_pl_sz, resp_pl_sz;
+ u32 data, len;
+ u16 vendor;
+ u8 type;
+ int ret;
+
+ /*
+ * pci_doe() accept DOE PAYLOAD only but request carries DOE HEADER so
+ * shift the buffers, skip DOE HEADER in request buffer, and fill DOE
+ * HEADER in response buffer manually.
+ */
+
+ data = le32_to_cpu(*(__le32 *)(request + PCI_DOE_DATA_OBJECT_HEADER_1_OFFSET));
+ vendor = FIELD_GET(PCI_DOE_DATA_OBJECT_HEADER_1_VID, data);
+ type = FIELD_GET(PCI_DOE_DATA_OBJECT_HEADER_1_TYPE, data);
+
+ data = le32_to_cpu(*(__le32 *)(request + PCI_DOE_DATA_OBJECT_HEADER_2_OFFSET));
+ len = FIELD_GET(PCI_DOE_DATA_OBJECT_HEADER_2_LENGTH, data);
+
+ req_pl_sz = len * sizeof(__le32) - PCI_DOE_DATA_OBJECT_HEADER_SIZE;
+ resp_pl_sz = response_sz - PCI_DOE_DATA_OBJECT_HEADER_SIZE;
+ req_pl_addr = request + PCI_DOE_DATA_OBJECT_HEADER_SIZE;
+ resp_pl_addr = response + PCI_DOE_DATA_OBJECT_HEADER_SIZE;
+
+ ret = pci_tsm_doe_transfer(pdev, type, req_pl_addr, req_pl_sz,
+ resp_pl_addr, resp_pl_sz);
+ if (ret < 0) {
+ pci_err(pdev, "spdm msg exchange fail %d\n", ret);
+ return ret;
+ }
+
+ data = FIELD_PREP(PCI_DOE_DATA_OBJECT_HEADER_1_VID, vendor) |
+ FIELD_PREP(PCI_DOE_DATA_OBJECT_HEADER_1_TYPE, type);
+ *(__le32 *)(response + PCI_DOE_DATA_OBJECT_HEADER_1_OFFSET) = cpu_to_le32(data);
+
+ len = (ret + PCI_DOE_DATA_OBJECT_HEADER_SIZE) / sizeof(__le32);
+ data = FIELD_PREP(PCI_DOE_DATA_OBJECT_HEADER_2_LENGTH, len);
+ *(__le32 *)(response + PCI_DOE_DATA_OBJECT_HEADER_2_OFFSET) = cpu_to_le32(data);
+
+ ret += PCI_DOE_DATA_OBJECT_HEADER_SIZE;
+
+ pci_dbg(pdev, "%s complete: vendor 0x%x type 0x%x rsp_sz %d\n",
+ __func__, vendor, type, ret);
+ return ret;
+}
+
static int tdx_link_connect(struct pci_dev *pdev)
{
return -ENXIO;
--
2.25.1