[PATCH 09/18] PCI/CMA: Add a PCI TSM CMA driver using SPDM

From: alistair23

Date: Thu May 07 2026 - 23:19:39 EST


From: Alistair Francis <alistair.francis@xxxxxxx>

Component Measurement and Authentication (CMA, PCIe r6.2 sec 6.31)
allows for measurement and authentication of PCIe devices. It is
based on the Security Protocol and Data Model specification (SPDM,
https://www.dmtf.org/dsp/DSP0274).

CMA-SPDM in turn forms the basis for Integrity and Data Encryption
(IDE, PCIe r6.2 sec 6.33) because the key material used by IDE is
transmitted over a CMA-SPDM session.

As a first step, add support for authentication via a CMA TSM driver.

This was previously discusd here:
http://lore.kernel.org/69976d7d39c60_2f4a1009@dwillia2-mobl4.notmuch

By utilising a TSM driver we get a lot of the TSM driver probe policies
"for free". Currently there is no mechanism to provide evidence to
userspace, as the TSM system doesn't support that at the moment. That
can be added later when support by TSM.

Credits: Jonathan wrote the original proof-of-concept for a CMA implementation.
Lukas reworked that for upstream. Wilfred contributed fixes for issues
discovered during testing. Alistair reworked it as a TSM driver.

Signed-off-by: Jonathan Cameron <Jonathan.Cameron@xxxxxxxxxx>
Co-developed-by: Wilfred Mallawa <wilfred.mallawa@xxxxxxx>
Signed-off-by: Wilfred Mallawa <wilfred.mallawa@xxxxxxx>
Co-developed-by: Lukas Wunner <lukas@xxxxxxxxx>
Signed-off-by: Lukas Wunner <lukas@xxxxxxxxx>
Signed-off-by: Alistair Francis <alistair.francis@xxxxxxx>
---
MAINTAINERS | 1 +
drivers/pci/Kconfig | 14 ++++
drivers/pci/Makefile | 2 +
drivers/pci/cma.c | 141 ++++++++++++++++++++++++++++++++++++++++
drivers/pci/doe.c | 3 -
include/linux/pci-doe.h | 4 ++
6 files changed, 162 insertions(+), 3 deletions(-)
create mode 100644 drivers/pci/cma.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 2e8ad57fec5d..c99fc6ae2d03 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -24173,6 +24173,7 @@ L: linux-cxl@xxxxxxxxxxxxxxx
L: linux-pci@xxxxxxxxxxxxxxx
S: Maintained
T: git git://git.kernel.org/pub/scm/linux/kernel/git/devsec/spdm.git
+F: drivers/pci/cma.c
F: include/linux/spdm.h
F: lib/rspdm/

diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig
index 33c88432b728..dcf4170381f2 100644
--- a/drivers/pci/Kconfig
+++ b/drivers/pci/Kconfig
@@ -124,6 +124,20 @@ config PCI_ATS
config PCI_IDE
bool

+config PCI_CMA
+ bool "Component Measurement and Authentication (CMA-SPDM)"
+ select CRYPTO_ECDSA
+ select CRYPTO_RSA
+ select CRYPTO_SHA256
+ select CRYPTO_SHA512
+ select PCI_DOE
+ select PCI_TSM
+ select RSPDM
+ help
+ Authenticate devices on enumeration per PCIe r6.2 sec 6.31.
+ A PCI DOE mailbox is used as transport for DMTF SPDM based
+ authentication, measurement and secure channel establishment.
+
config PCI_TSM
bool "PCI TSM: Device security protocol support"
select PCI_IDE
diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile
index 41ebc3b9a518..16abfd0e17e1 100644
--- a/drivers/pci/Makefile
+++ b/drivers/pci/Makefile
@@ -41,6 +41,8 @@ obj-$(CONFIG_PCI_NPEM) += npem.o
obj-$(CONFIG_PCIE_TPH) += tph.o
obj-$(CONFIG_CARDBUS) += setup-cardbus.o

+obj-$(CONFIG_PCI_CMA) += cma.o
+
# Endpoint library must be initialized before its users
obj-$(CONFIG_PCI_ENDPOINT) += endpoint/

diff --git a/drivers/pci/cma.c b/drivers/pci/cma.c
new file mode 100644
index 000000000000..998fde6366fb
--- /dev/null
+++ b/drivers/pci/cma.c
@@ -0,0 +1,141 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Component Measurement and Authentication (CMA-SPDM, PCIe r6.2 sec 6.31)
+ *
+ * Copyright (C) 2021 Huawei
+ * Jonathan Cameron <Jonathan.Cameron@xxxxxxxxxx>
+ * Copyright (C) 2022-24 Intel Corporation
+ * Copyright (C) 2026 Western Digital
+ * Alistair Francis <alistair.francis@xxxxxxx>
+ */
+
+#define dev_fmt(fmt) "CMA: " fmt
+
+#include <linux/pci.h>
+#include <linux/pci-doe.h>
+#include <linux/pci-tsm.h>
+#include <linux/slab.h>
+#include <linux/spdm.h>
+#include <linux/tsm.h>
+
+#include "pci.h"
+
+static ssize_t pci_doe_transport(void *priv, struct device *dev,
+ const void *request, size_t request_sz,
+ void *response, size_t response_sz)
+{
+ struct pci_doe_mb *doe = priv;
+ ssize_t rc;
+
+ rc = pci_doe(doe, PCI_VENDOR_ID_PCI_SIG, PCI_DOE_FEATURE_CMA,
+ request, request_sz, response, response_sz);
+
+ return rc;
+}
+
+/**
+ * struct pci_cma_tsm - CMA SPDM TSM driver context
+ * @pf0: base pci_tsm_pf0 context (must be first)
+ * @spdm: SPDM session for this device
+ */
+struct pci_cma_tsm {
+ struct pci_tsm_pf0 pf0;
+ struct spdm_state *spdm;
+};
+
+static struct pci_cma_tsm *cma_tsm_from_tsm(struct pci_tsm *tsm)
+{
+ struct pci_tsm_pf0 *pf0 = container_of(tsm, struct pci_tsm_pf0, base_tsm);
+
+ return container_of(pf0, struct pci_cma_tsm, pf0);
+}
+
+/**
+ * struct pci_cma_devsec - CMA SPDM devsec TSM context
+ * @spdm: SPDM session for this device
+ */
+struct pci_cma_devsec {
+ struct spdm_state *spdm;
+};
+
+static struct pci_tsm *pci_cma_tsm_probe(struct tsm_dev *tsm_dev,
+ struct pci_dev *pdev)
+{
+ struct pci_doe_mb *doe;
+ struct pci_cma_tsm *cma;
+
+ doe = pci_find_doe_mailbox(pdev, PCI_VENDOR_ID_PCI_SIG,
+ PCI_DOE_FEATURE_CMA);
+ if (!doe)
+ return NULL;
+
+ cma = kzalloc(sizeof(*cma), GFP_KERNEL);
+ if (!cma)
+ return NULL;
+
+ mutex_init(&cma->pf0.lock);
+ cma->pf0.doe_mb = doe;
+ cma->pf0.base_tsm.pdev = pdev;
+ cma->pf0.base_tsm.dsm_dev = pdev;
+ cma->pf0.base_tsm.tsm_dev = tsm_dev;
+
+ cma->spdm = spdm_create(&pdev->dev, pci_doe_transport, doe,
+ PCI_DOE_MAX_PAYLOAD, NULL);
+ if (!cma->spdm) {
+ mutex_destroy(&cma->pf0.lock);
+ kfree(cma);
+ return NULL;
+ }
+
+ return &cma->pf0.base_tsm;
+}
+
+static void pci_cma_tsm_remove(struct pci_tsm *tsm)
+{
+ struct pci_cma_tsm *cma = cma_tsm_from_tsm(tsm);
+
+ spdm_destroy(cma->spdm);
+ mutex_destroy(&cma->pf0.lock);
+ kfree(cma);
+}
+
+static int pci_cma_tsm_connect(struct pci_dev *pdev)
+{
+ struct pci_cma_tsm *cma = cma_tsm_from_tsm(pdev->tsm);
+ int rc;
+
+ rc = spdm_authenticate(cma->spdm);
+ if (rc)
+ return rc;
+
+ return 0;
+}
+
+static void pci_cma_tsm_disconnect(struct pci_dev *pdev)
+{
+ /* SPDM state is freed in pci_cma_tsm_remove() */
+}
+
+static const struct pci_tsm_ops pci_cma_tsm_ops = {
+ .link_ops = {
+ .probe = pci_cma_tsm_probe,
+ .remove = pci_cma_tsm_remove,
+ .connect = pci_cma_tsm_connect,
+ .disconnect = pci_cma_tsm_disconnect,
+ },
+};
+
+static struct tsm_dev *pci_cma_tsm_dev;
+
+static int __init pci_cma_tsm_init(void)
+{
+ struct tsm_dev *tsm_dev;
+
+ tsm_dev = tsm_register(NULL, (struct pci_tsm_ops *)&pci_cma_tsm_ops);
+ if (IS_ERR(tsm_dev))
+ return PTR_ERR(tsm_dev);
+
+ pci_cma_tsm_dev = tsm_dev;
+ return 0;
+}
+late_initcall(pci_cma_tsm_init);
diff --git a/drivers/pci/doe.c b/drivers/pci/doe.c
index 7b41da4ec11a..f236942660a3 100644
--- a/drivers/pci/doe.c
+++ b/drivers/pci/doe.c
@@ -31,9 +31,6 @@
#define PCI_DOE_FLAG_CANCEL 0
#define PCI_DOE_FLAG_DEAD 1

-/* Max data object length is 2^18 dwords */
-#define PCI_DOE_MAX_LENGTH (1 << 18)
-
/**
* struct pci_doe_mb - State for a single DOE mailbox
*
diff --git a/include/linux/pci-doe.h b/include/linux/pci-doe.h
index bd4346a7c4e7..7540396336de 100644
--- a/include/linux/pci-doe.h
+++ b/include/linux/pci-doe.h
@@ -19,6 +19,10 @@ struct pci_doe_mb;
#define PCI_DOE_FEATURE_CMA 1
#define PCI_DOE_FEATURE_SSESSION 2

+/* Max data object length is 2^18 dwords (including 2 dwords for header) */
+#define PCI_DOE_MAX_LENGTH (1 << 18)
+#define PCI_DOE_MAX_PAYLOAD ((PCI_DOE_MAX_LENGTH - 2) * sizeof(u32))
+
struct pci_doe_mb *pci_find_doe_mailbox(struct pci_dev *pdev, u16 vendor,
u8 type);

--
2.52.0