[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20250227030952.2319050-7-alistair@alistair23.me>
Date: Thu, 27 Feb 2025 13:09:38 +1000
From: Alistair Francis <alistair@...stair23.me>
To: linux-cxl@...r.kernel.org,
linux-kernel@...r.kernel.org,
lukas@...ner.de,
linux-pci@...r.kernel.org,
bhelgaas@...gle.com,
Jonathan.Cameron@...wei.com,
rust-for-linux@...r.kernel.org,
akpm@...ux-foundation.org
Cc: boqun.feng@...il.com,
bjorn3_gh@...tonmail.com,
wilfred.mallawa@....com,
aliceryhl@...gle.com,
ojeda@...nel.org,
alistair23@...il.com,
a.hindborg@...nel.org,
tmgross@...ch.edu,
gary@...yguo.net,
alex.gaynor@...il.com,
benno.lossin@...ton.me
Subject: [RFC v2 06/20] PCI/CMA: Authenticate devices on enumeration
From: Jonathan Cameron <Jonathan.Cameron@...wei.com>
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, authenticate CMA-capable devices on enumeration.
A subsequent commit will expose the result in sysfs.
When allocating SPDM session state with spdm_create(), the maximum SPDM
message length needs to be passed. Make the PCI_DOE_MAX_LENGTH macro
public and calculate the maximum payload length from it.
Credits: Jonathan wrote a proof-of-concept of this CMA implementation.
Lukas reworked it for upstream. Wilfred contributed fixes for issues
discovered during testing.
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@...wei.com>
Co-developed-by: Wilfred Mallawa <wilfred.mallawa@....com>
Signed-off-by: Wilfred Mallawa <wilfred.mallawa@....com>
Co-developed-by: Lukas Wunner <lukas@...ner.de>
Signed-off-by: Lukas Wunner <lukas@...ner.de>
---
MAINTAINERS | 1 +
drivers/pci/Kconfig | 13 ++++++
drivers/pci/Makefile | 2 +
drivers/pci/cma.c | 101 ++++++++++++++++++++++++++++++++++++++++
drivers/pci/doe.c | 3 --
drivers/pci/pci.h | 8 ++++
drivers/pci/probe.c | 1 +
drivers/pci/remove.c | 1 +
include/linux/pci-doe.h | 4 ++
include/linux/pci.h | 4 ++
10 files changed, 135 insertions(+), 3 deletions(-)
create mode 100644 drivers/pci/cma.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 0f37dbdcfd77..abb3b603299f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -21385,6 +21385,7 @@ L: linux-cxl@...r.kernel.org
L: linux-pci@...r.kernel.org
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 2fbd379923fd..80954adbff7d 100644
--- a/drivers/pci/Kconfig
+++ b/drivers/pci/Kconfig
@@ -121,6 +121,19 @@ config XEN_PCIDEV_FRONTEND
config PCI_ATS
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 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_DOE
bool
diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile
index 67647f1880fb..3cbf2c226a2d 100644
--- a/drivers/pci/Makefile
+++ b/drivers/pci/Makefile
@@ -38,6 +38,8 @@ obj-$(CONFIG_PCI_DYNAMIC_OF_NODES) += of_property.o
obj-$(CONFIG_PCI_NPEM) += npem.o
obj-$(CONFIG_PCIE_TPH) += tph.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..7463cd1179f0
--- /dev/null
+++ b/drivers/pci/cma.c
@@ -0,0 +1,101 @@
+// 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@...wei.com>
+ *
+ * Copyright (C) 2022-24 Intel Corporation
+ */
+
+#define dev_fmt(fmt) "CMA: " fmt
+
+#include <linux/pci.h>
+#include <linux/pci-doe.h>
+#include <linux/pm_runtime.h>
+#include <linux/spdm.h>
+
+#include "pci.h"
+
+/* Keyring that userspace can poke certs into */
+static struct key *pci_cma_keyring;
+
+#define PCI_DOE_FEATURE_CMA 1
+
+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;
+
+ /*
+ * CMA-SPDM operation in non-D0 states is optional (PCIe r6.2
+ * sec 6.31.3). The spec does not define a way to determine
+ * if it's supported, so resume to D0 unconditionally.
+ */
+ rc = pm_runtime_resume_and_get(dev);
+ if (rc)
+ return rc;
+
+ rc = pci_doe(doe, PCI_VENDOR_ID_PCI_SIG, PCI_DOE_FEATURE_CMA,
+ request, request_sz, response, response_sz);
+
+ pm_runtime_put(dev);
+
+ return rc;
+}
+
+void pci_cma_init(struct pci_dev *pdev)
+{
+ struct pci_doe_mb *doe;
+
+ if (IS_ERR(pci_cma_keyring))
+ return;
+
+ if (!pci_is_pcie(pdev))
+ return;
+
+ doe = pci_find_doe_mailbox(pdev, PCI_VENDOR_ID_PCI_SIG,
+ PCI_DOE_FEATURE_CMA);
+ if (!doe)
+ return;
+
+ pdev->spdm_state = spdm_create(&pdev->dev, pci_doe_transport, doe,
+ PCI_DOE_MAX_PAYLOAD, pci_cma_keyring,
+ NULL);
+ if (!pdev->spdm_state)
+ return;
+
+ /*
+ * Keep spdm_state allocated even if initial authentication fails
+ * to allow for provisioning of certificates and reauthentication.
+ */
+ spdm_authenticate(pdev->spdm_state);
+}
+
+void pci_cma_destroy(struct pci_dev *pdev)
+{
+ if (!pdev->spdm_state)
+ return;
+
+ spdm_destroy(pdev->spdm_state);
+}
+
+__init static int pci_cma_keyring_init(void)
+{
+ pci_cma_keyring = keyring_alloc(".cma", KUIDT_INIT(0), KGIDT_INIT(0),
+ current_cred(),
+ (KEY_POS_ALL & ~KEY_POS_SETATTR) |
+ KEY_USR_VIEW | KEY_USR_READ |
+ KEY_USR_WRITE | KEY_USR_SEARCH,
+ KEY_ALLOC_NOT_IN_QUOTA |
+ KEY_ALLOC_SET_KEEP, NULL, NULL);
+ if (IS_ERR(pci_cma_keyring)) {
+ pr_err("PCI: Could not allocate .cma keyring\n");
+ return PTR_ERR(pci_cma_keyring);
+ }
+
+ return 0;
+}
+arch_initcall(pci_cma_keyring_init);
diff --git a/drivers/pci/doe.c b/drivers/pci/doe.c
index 7bd7892c5222..8ea37698082a 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/drivers/pci/pci.h b/drivers/pci/pci.h
index 01e51db8d285..69ba2ae9a0cd 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -448,6 +448,14 @@ static inline void pci_doe_destroy(struct pci_dev *pdev) { }
static inline void pci_doe_disconnected(struct pci_dev *pdev) { }
#endif
+#ifdef CONFIG_PCI_CMA
+void pci_cma_init(struct pci_dev *pdev);
+void pci_cma_destroy(struct pci_dev *pdev);
+#else
+static inline void pci_cma_init(struct pci_dev *pdev) { }
+static inline void pci_cma_destroy(struct pci_dev *pdev) { }
+#endif
+
#ifdef CONFIG_PCI_NPEM
void pci_npem_create(struct pci_dev *dev);
void pci_npem_remove(struct pci_dev *dev);
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index 246744d8d268..edf06055e805 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -2565,6 +2565,7 @@ static void pci_init_capabilities(struct pci_dev *dev)
pci_rcec_init(dev); /* Root Complex Event Collector */
pci_doe_init(dev); /* Data Object Exchange */
pci_tph_init(dev); /* TLP Processing Hints */
+ pci_cma_init(dev); /* Component Measurement & Auth */
pcie_report_downtraining(dev);
pci_init_reset_methods(dev);
diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c
index efc37fcb73e2..e6feab4e3c61 100644
--- a/drivers/pci/remove.c
+++ b/drivers/pci/remove.c
@@ -61,6 +61,7 @@ static void pci_destroy_dev(struct pci_dev *dev)
list_del(&dev->bus_list);
up_write(&pci_bus_sem);
+ pci_cma_destroy(dev);
pci_doe_destroy(dev);
pcie_aspm_exit_link_state(dev);
pci_bridge_d3_update(dev);
diff --git a/include/linux/pci-doe.h b/include/linux/pci-doe.h
index 1f14aed4354b..0d3d7656c456 100644
--- a/include/linux/pci-doe.h
+++ b/include/linux/pci-doe.h
@@ -15,6 +15,10 @@
struct pci_doe_mb;
+/* 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);
diff --git a/include/linux/pci.h b/include/linux/pci.h
index 47b31ad724fa..50c43546a32b 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -39,6 +39,7 @@
#include <linux/io.h>
#include <linux/resource_ext.h>
#include <linux/msi_api.h>
+#include <linux/spdm.h>
#include <uapi/linux/pci.h>
#include <linux/pci_ids.h>
@@ -528,6 +529,9 @@ struct pci_dev {
#ifdef CONFIG_PCI_DOE
struct xarray doe_mbs; /* Data Object Exchange mailboxes */
#endif
+#ifdef CONFIG_PCI_CMA
+ struct spdm_state *spdm_state; /* Security Protocol and Data Model */
+#endif
#ifdef CONFIG_PCI_NPEM
struct npem *npem; /* Native PCIe Enclosure Management */
#endif
--
2.48.1
Powered by blists - more mailing lists