@@ -43,6 +43,9 @@ obj-$(CONFIG_PCI_CMA) += cma.o cma.asn1.o
$(obj)/cma.o: $(obj)/cma.asn1.h
$(obj)/cma.asn1.o: $(obj)/cma.asn1.c $(obj)/cma.asn1.h
+tsm_pci-y := tsm.o
+obj-$(CONFIG_PCI_TSM) += tsm_pci.o
+
# Endpoint library must be initialized before its users
obj-$(CONFIG_PCI_ENDPOINT) += endpoint/
@@ -499,6 +499,7 @@
#define PCI_EXP_DEVCAP_PWR_VAL 0x03fc0000 /* Slot Power Limit Value */
#define PCI_EXP_DEVCAP_PWR_SCL 0x0c000000 /* Slot Power Limit Scale */
#define PCI_EXP_DEVCAP_FLR 0x10000000 /* Function Level Reset */
+#define PCI_EXP_DEVCAP_TEE 0x40000000 /* TEE I/O (TDISP) Support */
#define PCI_EXP_DEVCTL 0x08 /* Device Control */
#define PCI_EXP_DEVCTL_CERE 0x0001 /* Correctable Error Reporting En. */
#define PCI_EXP_DEVCTL_NFERE 0x0002 /* Non-Fatal Error Reporting Enable */
new file mode 100644
@@ -0,0 +1,233 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * TEE Security Manager for the TEE Device Interface Security Protocol
+ * (TDISP, PCIe r6.1 sec 11)
+ *
+ * Copyright(c) 2024 Intel Corporation. All rights reserved.
+ */
+
+#define dev_fmt(fmt) "TSM: " fmt
+
+#include <linux/pci.h>
+#include <linux/pci-doe.h>
+#include <linux/sysfs.h>
+#include <linux/xarray.h>
+#include <linux/module.h>
+#include <linux/pci-ide.h>
+#include <linux/tsm.h>
+
+#define DRIVER_VERSION "0.1"
+#define DRIVER_AUTHOR "aik@amd.com"
+#define DRIVER_DESC "TSM TDISP library"
+
+static bool is_physical_endpoint(struct pci_dev *pdev)
+{
+ if (!pci_is_pcie(pdev))
+ return false;
+
+ if (pdev->is_virtfn)
+ return false;
+
+ if (pci_pcie_type(pdev) != PCI_EXP_TYPE_ENDPOINT)
+ return false;
+
+ return true;
+}
+
+static bool is_endpoint(struct pci_dev *pdev)
+{
+ if (!pci_is_pcie(pdev))
+ return false;
+
+ if (pci_pcie_type(pdev) != PCI_EXP_TYPE_ENDPOINT)
+ return false;
+
+ return true;
+}
+
+struct tsm_pci_dev_data {
+ struct pci_doe_mb *doe_mb;
+ struct pci_doe_mb *doe_mb_sec;
+};
+
+#define tsm_dev_to_pcidata(tdev) ((struct tsm_pci_dev_data *)tsm_dev_to_bdata(tdev))
+
+static int tsm_pci_dev_spdm_forward(struct tsm_spdm *spdm, u8 type)
+{
+ struct tsm_dev *tdev = container_of(spdm, struct tsm_dev, spdm);
+ struct tsm_pci_dev_data *tdata = tsm_dev_to_pcidata(tdev);
+ struct pci_doe_mb *doe_mb;
+ int rc;
+
+ if (type == TSM_PROTO_SECURED_CMA_SPDM)
+ doe_mb = tdata->doe_mb_sec;
+ else if (type == TSM_PROTO_CMA_SPDM)
+ doe_mb = tdata->doe_mb;
+ else
+ return -EINVAL;
+
+ if (!doe_mb)
+ return -EFAULT;
+
+ rc = pci_doe(doe_mb, PCI_VENDOR_ID_PCI_SIG, type,
+ spdm->req, spdm->req_len, spdm->rsp, spdm->rsp_len);
+ if (rc >= 0)
+ spdm->rsp_len = rc;
+
+ return rc;
+}
+
+static struct tsm_bus_ops tsm_pci_ops = {
+ .spdm_forward = tsm_pci_dev_spdm_forward,
+};
+
+static int tsm_pci_dev_init(struct tsm_bus_subsys *tsm_bus,
+ struct pci_dev *pdev,
+ struct tsm_dev **ptdev)
+{
+ struct tsm_pci_dev_data *tdata;
+ int ret = tsm_dev_init(tsm_bus, &pdev->dev, sizeof(*tdata), ptdev);
+
+ if (ret)
+ return ret;
+
+ tdata = tsm_dev_to_bdata(*ptdev);
+
+ tdata->doe_mb = pci_find_doe_mailbox(pdev,
+ PCI_VENDOR_ID_PCI_SIG,
+ PCI_DOE_PROTOCOL_CMA_SPDM);
+ tdata->doe_mb_sec = pci_find_doe_mailbox(pdev,
+ PCI_VENDOR_ID_PCI_SIG,
+ PCI_DOE_PROTOCOL_SECURED_CMA_SPDM);
+
+ if (tdata->doe_mb || tdata->doe_mb_sec)
+ pci_notice(pdev, "DOE SPDM=%s SecuredSPDM=%s\n",
+ tdata->doe_mb ? "yes":"no", tdata->doe_mb_sec ? "yes":"no");
+
+ return ret;
+}
+
+static int tsm_pci_alloc_device(struct tsm_bus_subsys *tsm_bus,
+ struct pci_dev *pdev)
+{
+ int ret = 0;
+
+ /* Set up TDIs for HV (physical functions) and VM (all functions) */
+ if ((pdev->devcap & PCI_EXP_DEVCAP_TEE) &&
+ (((pdev->is_physfn && (PCI_FUNC(pdev->devfn) == 0)) ||
+ (!pdev->is_physfn && !pdev->is_virtfn)))) {
+
+ struct tsm_dev *tdev = NULL;
+
+ if (!is_physical_endpoint(pdev))
+ return 0;
+
+ ret = tsm_pci_dev_init(tsm_bus, pdev, &tdev);
+ if (ret)
+ return ret;
+
+ ret = tsm_tdi_init(tdev, &pdev->dev);
+ tsm_dev_put(tdev);
+ return ret;
+ }
+
+ /* Set up TDIs for HV (virtual functions), should do nothing in VMs */
+ if (pdev->is_virtfn) {
+ struct pci_dev *pf0 = pci_get_slot(pdev->physfn->bus,
+ pdev->physfn->devfn & ~7);
+
+ if (pf0 && (pf0->devcap & PCI_EXP_DEVCAP_TEE)) {
+ struct tsm_dev *tdev = tsm_dev_get(&pf0->dev);
+
+ if (!is_endpoint(pdev))
+ return 0;
+
+ ret = tsm_tdi_init(tdev, &pdev->dev);
+ tsm_dev_put(tdev);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static void tsm_pci_dev_free(struct pci_dev *pdev)
+{
+ struct tsm_tdi *tdi = tsm_tdi_get(&pdev->dev);
+
+ if (tdi) {
+ tsm_tdi_put(tdi);
+ tsm_tdi_free(tdi);
+ }
+
+ struct tsm_dev *tdev = tsm_dev_get(&pdev->dev);
+
+ if (tdev) {
+ tsm_dev_put(tdev);
+ tsm_dev_free(tdev);
+ }
+
+ WARN_ON(!tdi && tdev);
+}
+
+static int tsm_pci_bus_notifier(struct notifier_block *nb, unsigned long action, void *data)
+{
+ struct tsm_bus_subsys *tsm_bus = container_of(nb, struct tsm_bus_subsys, notifier);
+
+ switch (action) {
+ case BUS_NOTIFY_ADD_DEVICE:
+ tsm_pci_alloc_device(tsm_bus, to_pci_dev(data));
+ break;
+ case BUS_NOTIFY_DEL_DEVICE:
+ tsm_pci_dev_free(to_pci_dev(data));
+ break;
+ }
+
+ return NOTIFY_OK;
+}
+
+struct tsm_bus_subsys *pci_tsm_register(struct tsm_subsys *tsm)
+{
+ struct tsm_bus_subsys *tsm_bus = kzalloc(sizeof(*tsm_bus), GFP_KERNEL);
+ struct pci_dev *pdev = NULL;
+
+ pr_info("Scan TSM PCI\n");
+ tsm_bus->ops = &tsm_pci_ops;
+ tsm_bus->tsm = tsm;
+ tsm_bus->notifier.notifier_call = tsm_pci_bus_notifier;
+ for_each_pci_dev(pdev)
+ tsm_pci_alloc_device(tsm_bus, pdev);
+ bus_register_notifier(&pci_bus_type, &tsm_bus->notifier);
+ return tsm_bus;
+}
+EXPORT_SYMBOL_GPL(pci_tsm_register);
+
+void pci_tsm_unregister(struct tsm_bus_subsys *subsys)
+{
+ struct pci_dev *pdev = NULL;
+
+ pr_info("Shut down TSM PCI\n");
+ bus_unregister_notifier(&pci_bus_type, &subsys->notifier);
+ for_each_pci_dev(pdev)
+ tsm_pci_dev_free(pdev);
+}
+EXPORT_SYMBOL_GPL(pci_tsm_unregister);
+
+static int __init tsm_pci_init(void)
+{
+ pr_info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
+ return 0;
+}
+
+static void __exit tsm_pci_cleanup(void)
+{
+ pr_info(DRIVER_DESC " version: " DRIVER_VERSION " unload\n");
+}
+
+module_init(tsm_pci_init);
+module_exit(tsm_pci_cleanup);
+
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
@@ -137,6 +137,21 @@ config PCI_CMA
config PCI_IDE
bool
+config PCI_TSM
+ tristate "TEE Security Manager for PCI Device Security"
+ select PCI_IDE
+ depends on TSM
+ default m
+ help
+ The TEE (Trusted Execution Environment) Device Interface
+ Security Protocol (TDISP) defines a "TSM" as a platform agent
+ that manages device authentication, link encryption, link
+ integrity protection, and assignment of PCI device functions
+ (virtual or physical) to confidential computing VMs that can
+ access (DMA) guest private memory.
+
+ Enable a platform TSM driver to use this capability.
+
config PCI_DOE
bool
The PCI TSM module scans the PCI bus to initialize a TSM context for physical ("TDEV") and virtual ("TDI") functions. It also implements bus operations which at the moment is just an SPDM bouncer which talks to the PF's DOE mailboxes. The purpose of this module is to keep drivers/virt/coco/(guest|host) unaware of PCI as much as possible (which is not always the case). This module does not add new sysfs interfaces. Signed-off-by: Alexey Kardashevskiy <aik@amd.com> --- drivers/pci/Makefile | 3 + include/uapi/linux/pci_regs.h | 1 + drivers/pci/tsm.c | 233 ++++++++++++++++++++ drivers/pci/Kconfig | 15 ++ 4 files changed, 252 insertions(+)