Message ID | 173343742510.1074769.16552514658771224955.stgit@dwillia2-xfh.jf.intel.com (mailing list archive) |
---|---|
State | New |
Headers | show |
Series | PCI/TSM: Core infrastructure for PCI device security (TDISP) | expand |
On 6/12/24 09:23, Dan Williams wrote: > The PCIe 6.1 specification, section 11, introduces the Trusted Execution > Environment (TEE) Device Interface Security Protocol (TDISP). This > interface definition builds upon Component Measurement and > Authentication (CMA), and link Integrity and Data Encryption (IDE). It > adds support for assigning devices (PCI physical or virtual function) to > a confidential VM such that the assigned device is enabled to access > guest private memory protected by technologies like Intel TDX, AMD > SEV-SNP, RISCV COVE, or ARM CCA. > > The "TSM" (TEE Security Manager) is a concept in the TDISP specification > of an agent that mediates between a "DSM" (Device Security Manager) and > system software in both a VMM and a confidential VM. A VMM uses TSM ABIs > to setup link security and assign devices. A confidential VM uses TSM > ABIs to transition an assigned device into the TDISP "RUN" state and > validate its configuration. From a Linux perspective the TSM abstracts > many of the details of TDISP, IDE, and CMA. Some of those details leak > through at times, but for the most part TDISP is an internal > implementation detail of the TSM. > > CONFIG_PCI_TSM adds an "authenticated" attribute and "tsm/" subdirectory > to pci-sysfs. The work in progress CONFIG_PCI_CMA (software > kernel-native PCI authentication) that can depend on a local to the PCI > core implementation, CONFIG_PCI_TSM needs to be prepared for late > loading of the platform TSM driver. Consider that the TSM driver may > itself be a PCI driver. Userspace can watch /sys/class/tsm/tsm0/uevent > to know when the PCI core has TSM services enabled. > > The common verbs that the low-level TSM drivers implement are defined by > 'struct pci_tsm_ops'. For now only 'connect' and 'disconnect' are > defined for secure session and IDE establishment. The 'probe' and > 'remove' operations setup per-device context representing the device's > security manager (DSM). Note that there is only one DSM expected per > physical PCI function, and that coordinates a variable number of > assignable interfaces to CVMs. > > The locking allows for multiple devices to be executing commands > simultaneously, one outstanding command per-device and an rwsem flushes > all in-flight commands when a TSM low-level driver/device is removed. > > Thanks to Wu Hao for his work on an early draft of this support. > > Cc: Lukas Wunner <lukas@wunner.de> > Cc: Samuel Ortiz <sameo@rivosinc.com> > Cc: Alexey Kardashevskiy <aik@amd.com> > Acked-by: Bjorn Helgaas <bhelgaas@google.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> > --- > Documentation/ABI/testing/sysfs-bus-pci | 42 ++++ > MAINTAINERS | 2 > drivers/pci/Kconfig | 13 + > drivers/pci/Makefile | 1 > drivers/pci/pci-sysfs.c | 4 > drivers/pci/pci.h | 10 + > drivers/pci/probe.c | 1 > drivers/pci/remove.c | 3 > drivers/pci/tsm.c | 293 +++++++++++++++++++++++++++++++ > drivers/virt/coco/host/tsm-core.c | 19 ++ It is sooo small, make me wonder why we need it at all... > include/linux/pci-tsm.h | 83 +++++++++ > include/linux/pci.h | 3 > include/linux/tsm.h | 4 > include/uapi/linux/pci_regs.h | 1 > 14 files changed, 476 insertions(+), 3 deletions(-) > create mode 100644 drivers/pci/tsm.c > create mode 100644 include/linux/pci-tsm.h > > diff --git a/Documentation/ABI/testing/sysfs-bus-pci b/Documentation/ABI/testing/sysfs-bus-pci > index 5da6a14dc326..0d742ef41aa7 100644 > --- a/Documentation/ABI/testing/sysfs-bus-pci > +++ b/Documentation/ABI/testing/sysfs-bus-pci > @@ -583,3 +583,45 @@ Description: > enclosure-specific indications "specific0" to "specific7", > hence the corresponding led class devices are unavailable if > the DSM interface is used. > + > +What: /sys/bus/pci/devices/.../tsm/ > +Date: July 2024 > +Contact: linux-coco@lists.linux.dev > +Description: > + This directory only appears if a physical device function supports > + authentication (PCIe CMA-SPDM), interface security (PCIe TDISP), and is > + accepted for secure operation by the platform TSM driver. This attribute > + directory appears dynamically after the platform TSM driver loads. So, > + only after the /sys/class/tsm/tsm0 device arrives can tools assume that > + devices without a tsm/ attribute directory will never have one, before > + that, the security capabilities of the device relative to the platform > + TSM are unknown. See Documentation/ABI/testing/sysfs-class-tsm. > + > +What: /sys/bus/pci/devices/.../tsm/connect > +Date: July 2024 > +Contact: linux-coco@lists.linux.dev > +Description: > + (RW) Writing "1" to this file triggers the platform TSM (TEE Security > + Manager) to establish a connection with the device. This typically > + includes an SPDM (DMTF Security Protocols and Data Models) session over > + PCIe DOE (Data Object Exchange) and may also include PCIe IDE (Integrity > + and Data Encryption) establishment. > + > +What: /sys/bus/pci/devices/.../authenticated > +Date: July 2024 > +Contact: linux-pci@vger.kernel.org > +Description: > + When the device's tsm/ directory is present device > + authentication (PCIe CMA-SPDM) and link encryption (PCIe IDE) > + are handled by the platform TSM (TEE Security Manager). When the > + tsm/ directory is not present this attribute reflects only the > + native CMA-SPDM authentication state with the kernel's > + certificate store. > + > + If the attribute is not present, it indicates that > + authentication is unsupported by the device, or the TSM has no > + available authentication methods for the device. > + > + When present and the tsm/ attribute directory is present, the > + authenticated attribute is an alias for the device 'connect' > + state. See the 'tsm/connect' attribute for more details. > diff --git a/MAINTAINERS b/MAINTAINERS > index abaabbc39134..8f28a2d9bbc6 100644 > --- a/MAINTAINERS > +++ b/MAINTAINERS > @@ -23843,8 +23843,10 @@ M: Dan Williams <dan.j.williams@intel.com> > L: linux-coco@lists.linux.dev > S: Maintained > F: Documentation/ABI/testing/configfs-tsm-report > +F: drivers/pci/tsm.c > F: drivers/virt/coco/guest/ > F: drivers/virt/coco/host/ > +F: include/linux/pci-tsm.h > F: include/linux/tsm.h > > TRUSTED SERVICES TEE DRIVER > diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig > index 4e5236c456f5..8dab60dadb7d 100644 > --- a/drivers/pci/Kconfig > +++ b/drivers/pci/Kconfig > @@ -124,6 +124,19 @@ config PCI_ATS > config PCI_IDE > bool > > +config PCI_TSM > + bool "TEE Security Manager for PCI Device Security" > + select PCI_IDE > + 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 > > diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile > index 6612256fd37d..2c545f877062 100644 > --- a/drivers/pci/Makefile > +++ b/drivers/pci/Makefile > @@ -35,6 +35,7 @@ obj-$(CONFIG_XEN_PCIDEV_FRONTEND) += xen-pcifront.o > obj-$(CONFIG_VGA_ARB) += vgaarb.o > obj-$(CONFIG_PCI_DOE) += doe.o > obj-$(CONFIG_PCI_IDE) += ide.o > +obj-$(CONFIG_PCI_TSM) += tsm.o > obj-$(CONFIG_PCI_DYNAMIC_OF_NODES) += of_property.o > obj-$(CONFIG_PCI_NPEM) += npem.o > obj-$(CONFIG_PCIE_TPH) += tph.o > diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c > index 7679d75d71e5..7e1ed3440a50 100644 > --- a/drivers/pci/pci-sysfs.c > +++ b/drivers/pci/pci-sysfs.c > @@ -1696,6 +1696,10 @@ const struct attribute_group *pci_dev_attr_groups[] = { > #endif > #ifdef CONFIG_PCIEASPM > &aspm_ctrl_attr_group, > +#endif > +#ifdef CONFIG_PCI_TSM > + &pci_tsm_auth_attr_group, > + &pci_tsm_attr_group, > #endif > NULL, > }; > diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h > index 0305f497b28a..0537fc72d5be 100644 > --- a/drivers/pci/pci.h > +++ b/drivers/pci/pci.h > @@ -458,6 +458,16 @@ void pci_ide_init(struct pci_dev *dev); > static inline void pci_ide_init(struct pci_dev *dev) { } > #endif > > +#ifdef CONFIG_PCI_TSM > +void pci_tsm_init(struct pci_dev *pdev); > +void pci_tsm_destroy(struct pci_dev *pdev); > +extern const struct attribute_group pci_tsm_attr_group; > +extern const struct attribute_group pci_tsm_auth_attr_group; > +#else > +static inline void pci_tsm_init(struct pci_dev *pdev) { } > +static inline void pci_tsm_destroy(struct pci_dev *pdev) { } > +#endif > + > /** > * pci_dev_set_io_state - Set the new error state if possible. > * > diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c > index e22f515a8da9..7cddde3cb0ed 100644 > --- a/drivers/pci/probe.c > +++ b/drivers/pci/probe.c > @@ -2518,6 +2518,7 @@ static void pci_init_capabilities(struct pci_dev *dev) > pci_doe_init(dev); /* Data Object Exchange */ > pci_tph_init(dev); /* TLP Processing Hints */ > pci_ide_init(dev); /* Link Integrity and Data Encryption */ > + pci_tsm_init(dev); /* TEE Security Manager connection */ > > pcie_report_downtraining(dev); > pci_init_reset_methods(dev); > diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c > index efc37fcb73e2..fd4ccafed067 100644 > --- a/drivers/pci/remove.c > +++ b/drivers/pci/remove.c > @@ -55,6 +55,9 @@ static void pci_destroy_dev(struct pci_dev *dev) > > pci_npem_remove(dev); > > + /* before device_del() to keep config cycle access */ > + pci_tsm_destroy(dev); > + > device_del(&dev->dev); > > down_write(&pci_bus_sem); > diff --git a/drivers/pci/tsm.c b/drivers/pci/tsm.c > new file mode 100644 > index 000000000000..04e9257a6e41 > --- /dev/null > +++ b/drivers/pci/tsm.c > @@ -0,0 +1,293 @@ > +// 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/pci-tsm.h> > +#include <linux/bitfield.h> > +#include "pci.h" > + > +/* > + * Provide a read/write lock against the init / exit of pdev tsm > + * capabilities and arrival/departure of a tsm instance > + */ > +static DECLARE_RWSEM(pci_tsm_rwsem); > +static const struct pci_tsm_ops *tsm_ops; > + > +/* supplemental attributes to surface when pci_tsm_attr_group is active */ > +static const struct attribute_group *pci_tsm_owner_attr_group; > + > +static int pci_tsm_disconnect(struct pci_dev *pdev) > +{ > + struct pci_tsm *pci_tsm = pdev->tsm; > + > + lockdep_assert_held(&pci_tsm_rwsem); > + if_not_guard(mutex_intr, &pci_tsm->lock) > + return -EINTR; > + > + if (pci_tsm->state < PCI_TSM_CONNECT) > + return 0; > + if (pci_tsm->state < PCI_TSM_INIT) > + return -ENXIO; > + > + tsm_ops->disconnect(pdev); > + pci_tsm->state = PCI_TSM_INIT; > + > + return 0; > +} > + > +static int pci_tsm_connect(struct pci_dev *pdev) > +{ > + struct pci_tsm *pci_tsm = pdev->tsm; > + int rc; > + > + lockdep_assert_held(&pci_tsm_rwsem); > + if_not_guard(mutex_intr, &pci_tsm->lock) > + return -EINTR; > + > + if (pci_tsm->state >= PCI_TSM_CONNECT) > + return 0; > + if (pci_tsm->state < PCI_TSM_INIT) > + return -ENXIO; > + > + rc = tsm_ops->connect(pdev); I thought ages ago it was suggested that DOE/SPDM loop happens in a common place and not in the platform driver implementing tsm_ops->connect() (but I may have missed the point then). > + if (rc) > + return rc; > + pci_tsm->state = PCI_TSM_CONNECT; > + return 0; > +} > + > +static ssize_t connect_store(struct device *dev, struct device_attribute *attr, > + const char *buf, size_t len) > +{ > + int rc; > + bool connect; > + struct pci_dev *pdev = to_pci_dev(dev); > + > + rc = kstrtobool(buf, &connect); > + if (rc) > + return rc; > + > + if_not_guard(rwsem_read_intr, &pci_tsm_rwsem) > + return -EINTR; > + > + if (connect) > + rc = pci_tsm_connect(pdev); > + else > + rc = pci_tsm_disconnect(pdev); > + if (rc) > + return rc; > + return len; > +} > + > +static ssize_t connect_show(struct device *dev, struct device_attribute *attr, > + char *buf) > +{ > + struct pci_dev *pdev = to_pci_dev(dev); > + > + if_not_guard(rwsem_read_intr, &pci_tsm_rwsem) > + return -EINTR; > + if (!pdev->tsm) > + return -ENXIO; > + return sysfs_emit(buf, "%d\n", pdev->tsm->state >= PCI_TSM_CONNECT); > +} > +static DEVICE_ATTR_RW(connect); > + > +static bool pci_tsm_group_visible(struct kobject *kobj) > +{ > + struct device *dev = kobj_to_dev(kobj); > + struct pci_dev *pdev = to_pci_dev(dev); > + > + if (pdev->tsm) > + return true; > + return false; > +} > +DEFINE_SIMPLE_SYSFS_GROUP_VISIBLE(pci_tsm); > + > +static struct attribute *pci_tsm_attrs[] = { > + &dev_attr_connect.attr, > + NULL, > +}; > + > +const struct attribute_group pci_tsm_attr_group = { > + .name = "tsm", > + .attrs = pci_tsm_attrs, > + .is_visible = SYSFS_GROUP_VISIBLE(pci_tsm), > +}; > + > +static ssize_t authenticated_show(struct device *dev, > + struct device_attribute *attr, char *buf) > +{ > + /* > + * When device authentication is TSM owned, 'authenticated' is > + * identical to the connect state. > + */ > + return connect_show(dev, attr, buf); > +} > +static DEVICE_ATTR_RO(authenticated); > + > +static struct attribute *pci_tsm_auth_attrs[] = { > + &dev_attr_authenticated.attr, > + NULL, > +}; > + > +const struct attribute_group pci_tsm_auth_attr_group = { > + .attrs = pci_tsm_auth_attrs, > + .is_visible = SYSFS_GROUP_VISIBLE(pci_tsm), > +}; > + > +static void dsm_remove(struct pci_dsm *dsm) > +{ > + if (!dsm) > + return; > + tsm_ops->remove(dsm); > +} > +DEFINE_FREE(dsm_remove, struct pci_dsm *, if (_T) dsm_remove(_T)) > + > +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 void __pci_tsm_init(struct pci_dev *pdev) > +{ > + bool tee_cap; > + > + if (!is_physical_endpoint(pdev)) > + return; > + > + tee_cap = pdev->devcap & PCI_EXP_DEVCAP_TEE; > + > + if (!(pdev->ide_cap || tee_cap)) > + return; > + > + lockdep_assert_held_write(&pci_tsm_rwsem); > + if (!tsm_ops) > + return; > + > + struct pci_tsm *pci_tsm __free(kfree) = kzalloc(sizeof(*pci_tsm), GFP_KERNEL); > + if (!pci_tsm) > + return; > + > + /* > + * If a physical device has any security capabilities it may be > + * a candidate to connect with the platform TSM > + */ > + struct pci_dsm *dsm __free(dsm_remove) = tsm_ops->probe(pdev); > + > + pci_dbg(pdev, "Device security capabilities detected (%s%s ), TSM %s\n", > + pdev->ide_cap ? " ide" : "", tee_cap ? " tee" : "", > + dsm ? "attach" : "skip"); > + > + if (!dsm) > + return; > + > + mutex_init(&pci_tsm->lock); > + pci_tsm->doe_mb = pci_find_doe_mailbox(pdev, PCI_VENDOR_ID_PCI_SIG, > + PCI_DOE_PROTO_CMA); > + if (!pci_tsm->doe_mb) { > + pci_warn(pdev, "TSM init failure, no CMA mailbox\n"); > + return; > + } > + > + pci_tsm->state = PCI_TSM_INIT; > + pci_tsm->dsm = no_free_ptr(dsm); > + pdev->tsm = no_free_ptr(pci_tsm); > + sysfs_update_group(&pdev->dev.kobj, &pci_tsm_auth_attr_group); > + sysfs_update_group(&pdev->dev.kobj, &pci_tsm_attr_group); > + if (pci_tsm_owner_attr_group) > + sysfs_merge_group(&pdev->dev.kobj, pci_tsm_owner_attr_group); > +} > + > +void pci_tsm_init(struct pci_dev *pdev) > +{ > + guard(rwsem_write)(&pci_tsm_rwsem); > + __pci_tsm_init(pdev); > +} > + > +int pci_tsm_register(const struct pci_tsm_ops *ops, const struct attribute_group *grp) > +{ > + struct pci_dev *pdev = NULL; > + > + if (!ops) > + return 0; > + guard(rwsem_write)(&pci_tsm_rwsem); > + if (tsm_ops) > + return -EBUSY; > + tsm_ops = ops; > + pci_tsm_owner_attr_group = grp; > + for_each_pci_dev(pdev) > + __pci_tsm_init(pdev); > + return 0; > +} > +EXPORT_SYMBOL_GPL(pci_tsm_register); > + > +static void __pci_tsm_destroy(struct pci_dev *pdev) > +{ > + struct pci_tsm *pci_tsm = pdev->tsm; > + > + if (!pci_tsm) > + return; > + > + lockdep_assert_held_write(&pci_tsm_rwsem); > + if (pci_tsm->state > PCI_TSM_INIT) > + pci_tsm_disconnect(pdev); > + tsm_ops->remove(pci_tsm->dsm); > + pdev->tsm = NULL; > + if (pci_tsm_owner_attr_group) > + sysfs_unmerge_group(&pdev->dev.kobj, pci_tsm_owner_attr_group); > + sysfs_update_group(&pdev->dev.kobj, &pci_tsm_attr_group); > + sysfs_update_group(&pdev->dev.kobj, &pci_tsm_auth_attr_group); > + kfree(pci_tsm); > +} > + > +void pci_tsm_destroy(struct pci_dev *pdev) > +{ > + guard(rwsem_write)(&pci_tsm_rwsem); > + __pci_tsm_destroy(pdev); > +} > + > +void pci_tsm_unregister(const struct pci_tsm_ops *ops) > +{ > + struct pci_dev *pdev = NULL; > + > + if (!ops) > + return; > + guard(rwsem_write)(&pci_tsm_rwsem); > + if (ops != tsm_ops) > + return; > + for_each_pci_dev(pdev) > + __pci_tsm_destroy(pdev); > + tsm_ops = NULL; > +} > +EXPORT_SYMBOL_GPL(pci_tsm_unregister); > + > +int pci_tsm_doe_transfer(struct pci_dev *pdev, enum pci_doe_proto type, > + const void *req, size_t req_sz, void *resp, > + size_t resp_sz) > +{ > + if (!pdev->tsm || !pdev->tsm->doe_mb) > + return -ENXIO; > + > + return pci_doe(pdev->tsm->doe_mb, PCI_VENDOR_ID_PCI_SIG, type, req, > + req_sz, resp, resp_sz); > +} > +EXPORT_SYMBOL_GPL(pci_tsm_doe_transfer); > diff --git a/drivers/virt/coco/host/tsm-core.c b/drivers/virt/coco/host/tsm-core.c > index 0ee738fc40ed..21270210b03f 100644 > --- a/drivers/virt/coco/host/tsm-core.c > +++ b/drivers/virt/coco/host/tsm-core.c > @@ -8,11 +8,13 @@ > #include <linux/device.h> > #include <linux/module.h> > #include <linux/cleanup.h> > +#include <linux/pci-tsm.h> > > static DECLARE_RWSEM(tsm_core_rwsem); > static struct class *tsm_class; > static struct tsm_subsys { > struct device dev; > + const struct pci_tsm_ops *pci_ops; > } *tsm_subsys; > > static struct tsm_subsys * > @@ -40,7 +42,8 @@ static void put_tsm_subsys(struct tsm_subsys *subsys) > DEFINE_FREE(put_tsm_subsys, struct tsm_subsys *, > if (!IS_ERR_OR_NULL(_T)) put_tsm_subsys(_T)) > struct tsm_subsys *tsm_register(struct device *parent, > - const struct attribute_group **groups) > + const struct attribute_group **groups, > + const struct pci_tsm_ops *pci_ops) > { > struct device *dev; > int rc; > @@ -62,10 +65,20 @@ struct tsm_subsys *tsm_register(struct device *parent, > if (rc) > return ERR_PTR(rc); > > + rc = pci_tsm_register(pci_ops, NULL); > + if (rc) { > + dev_err(parent, "PCI initialization failure: %pe\n", > + ERR_PTR(rc)); > + return ERR_PTR(rc); > + } > + > rc = device_add(dev); > - if (rc) > + if (rc) { > + pci_tsm_unregister(pci_ops); > return ERR_PTR(rc); > + } > > + subsys->pci_ops = pci_ops; > tsm_subsys = no_free_ptr(subsys); > > return tsm_subsys; > @@ -80,7 +93,9 @@ void tsm_unregister(struct tsm_subsys *subsys) > return; > } > > + pci_tsm_unregister(subsys->pci_ops); > device_unregister(&subsys->dev); > + > tsm_subsys = NULL; > } > EXPORT_SYMBOL_GPL(tsm_unregister); > diff --git a/include/linux/pci-tsm.h b/include/linux/pci-tsm.h > new file mode 100644 > index 000000000000..beb0d68129bc > --- /dev/null > +++ b/include/linux/pci-tsm.h > @@ -0,0 +1,83 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +#ifndef __PCI_TSM_H > +#define __PCI_TSM_H > +#include <linux/mutex.h> > + > +struct pci_dev; > + > +/** > + * struct pci_dsm - Device Security Manager context > + * @pdev: physical device back pointer > + */ > +struct pci_dsm { > + struct pci_dev *pdev; > +}; > + > +enum pci_tsm_state { > + PCI_TSM_ERR = -1, > + PCI_TSM_INIT, > + PCI_TSM_CONNECT, > +}; > + > +/** > + * struct pci_tsm - Platform TSM transport context > + * @state: reflect device initialized, connected, or bound > + * @lock: protect @state vs pci_tsm_ops invocation > + * @doe_mb: PCIe Data Object Exchange mailbox > + * @dsm: TSM driver device context established by pci_tsm_ops.probe > + */ > +struct pci_tsm { > + enum pci_tsm_state state; > + struct mutex lock; > + struct pci_doe_mb *doe_mb; > + struct pci_dsm *dsm; > +}; doe_mb and state look are device's attribures so will look more appropriate in pci_dsm ("d" from "dsm" is "device"), and pci_tsm would be some intimate knowledge of the ccp.ko (==PSP) about PCI PFs ("t" == "TEE" == TCB == PSP). Or I got it all wrong? > + > +/** > + * struct pci_tsm_ops - Low-level TSM-exported interface to the PCI core > + * @probe: probe/accept device for tsm operation, setup DSM context > + * @remove: destroy DSM context > + * @connect: establish / validate a secure connection (e.g. IDE) with the device > + * @disconnect: teardown the secure connection > + * > + * @probe and @remove run in pci_tsm_rwsem held for write context. All > + * other ops run under the @pdev->tsm->lock mutex and pci_tsm_rwsem held > + * for read. > + */ > +struct pci_tsm_ops { > + struct pci_dsm *(*probe)(struct pci_dev *pdev); > + void (*remove)(struct pci_dsm *dsm); > + int (*connect)(struct pci_dev *pdev); > + void (*disconnect)(struct pci_dev *pdev); > +}; > + > +enum pci_doe_proto { > + PCI_DOE_PROTO_CMA = 1, > + PCI_DOE_PROTO_SSESSION = 2, > +}; > + > +#ifdef CONFIG_PCI_TSM > +int pci_tsm_register(const struct pci_tsm_ops *ops, > + const struct attribute_group *grp); > +void pci_tsm_unregister(const struct pci_tsm_ops *ops); > +int pci_tsm_doe_transfer(struct pci_dev *pdev, enum pci_doe_proto type, > + const void *req, size_t req_sz, void *resp, > + size_t resp_sz); > +#else > +static inline int pci_tsm_register(const struct pci_tsm_ops *ops, > + const struct attribute_group *grp) > +{ > + return 0; > +} > +static inline void pci_tsm_unregister(const struct pci_tsm_ops *ops) > +{ > +} > +static inline int pci_tsm_doe_transfer(struct pci_dev *pdev, > + enum pci_doe_proto type, const void *req, > + size_t req_sz, void *resp, > + size_t resp_sz) > +{ > + return -ENOENT; > +} > +#endif > +#endif /*__PCI_TSM_H */ > diff --git a/include/linux/pci.h b/include/linux/pci.h > index 50811b7655dd..a0900e7d2012 100644 > --- a/include/linux/pci.h > +++ b/include/linux/pci.h > @@ -535,6 +535,9 @@ struct pci_dev { > u16 ide_cap; /* Link Integrity & Data Encryption */ > u16 sel_ide_cap; /* - Selective Stream register block */ > int nr_ide_mem; /* - Address range limits for streams */ > +#endif > +#ifdef CONFIG_PCI_TSM > + struct pci_tsm *tsm; /* TSM operation state */ > #endif > u16 acs_cap; /* ACS Capability offset */ > u8 supported_speeds; /* Supported Link Speeds Vector */ > diff --git a/include/linux/tsm.h b/include/linux/tsm.h > index 1a97459fc23e..46b9a0c6ea4e 100644 > --- a/include/linux/tsm.h > +++ b/include/linux/tsm.h > @@ -111,7 +111,9 @@ struct tsm_report_ops { > int tsm_report_register(const struct tsm_report_ops *ops, void *priv); > int tsm_report_unregister(const struct tsm_report_ops *ops); > struct tsm_subsys; > +struct pci_tsm_ops; > struct tsm_subsys *tsm_register(struct device *parent, > - const struct attribute_group **groups); > + const struct attribute_group **groups, > + const struct pci_tsm_ops *ops); > void tsm_unregister(struct tsm_subsys *subsys); > #endif /* __TSM_H */ > diff --git a/include/uapi/linux/pci_regs.h b/include/uapi/linux/pci_regs.h > index 9635b27d2485..19bba65a262c 100644 > --- a/include/uapi/linux/pci_regs.h > +++ b/include/uapi/linux/pci_regs.h > @@ -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 */ > I am trying to wrap my head around your tsm. here is what I got in my tree: https://github.com/aik/linux/blob/tsm/include/linux/tsm.h Shortly: drivers/virt/coco/tsm.ko does sysfs (including "connect" and "bind" to control and "certs"/"report" to attest) and implements tsm_dev/tsm_tdi, it does not know pci_dev; drivers/pci/tsm-pci.ko creates/destroys tsm_dev/tsm_dev using tsm.ko; drivers/crypto/ccp/ccp.ko (the PSP guy) registers: - tsm_subsys in tsm.ko (which does "connect" and "bind" and - tsm_bus_subsys in tsm-pci.ko (which does "spdm_forward") ccp.ko knows about pci_dev and whatever else comes in the future, and ccp.ko's "connect" implementation calls the IDE library (I am adopting yours now, with some tweaks). tsm-dev and tsm-tdi embed struct dev each and are added as children to PCI devices: no hide/show attrs, no additional TSM pointer in struct device or pci_dev, looks like: aik@sc ~> ls /sys/bus/pci/devices/0000:e1:04.0/tsm-tdi/tdi:0000:e1:04.0/ device power subsystem tsm_report tsm_report_user tsm_tdi_bind tsm_tdi_status tsm_tdi_status_user uevent aik@sc ~> ls /sys/bus/pci/devices/0000:e1:04.0/tsm_dev/ device power subsystem tsm_certs tsm_cert_slot tsm_certs_user tsm_dev_connect tsm_dev_status tsm_meas tsm_meas_user uevent aik@sc ~> ls /sys/class/tsm/tsm0/ device power stream0:0000:e1:00.0 subsystem uevent aik@sc ~> ls /sys/class/tsm-dev/ tdev:0000:c0:01.1 tdev:0000:e0:01.1 tdev:0000:e1:00.0 aik@sc ~> ls /sys/class/tsm-tdi/ tdi:0000:c0:01.1 tdi:0000:e0:01.1 tdi:0000:e1:00.0 tdi:0000:e1:04.0 tdi:0000:e1:04.1 tdi:0000:e1:04.2 tdi:0000:e1:04.3 SPDM forwarding seems a bus-agnostic concept, "connect" is a PCI thing but pci_dev is only needed for DOE/IDE. Or is separating struct pci_dev from struct device not worth it and most of it should go to tsm-pci.ko? Then what is left for tsm.ko? Thanks,
On Thu, Dec 05, 2024 at 02:23:45PM -0800, Dan Williams wrote: > The PCIe 6.1 specification, section 11, introduces the Trusted Execution > Environment (TEE) Device Interface Security Protocol (TDISP). This > interface definition builds upon Component Measurement and > Authentication (CMA), and link Integrity and Data Encryption (IDE). It > adds support for assigning devices (PCI physical or virtual function) to > a confidential VM such that the assigned device is enabled to access > guest private memory protected by technologies like Intel TDX, AMD > SEV-SNP, RISCV COVE, or ARM CCA. > +++ b/Documentation/ABI/testing/sysfs-bus-pci > @@ -583,3 +583,45 @@ Description: > enclosure-specific indications "specific0" to "specific7", > hence the corresponding led class devices are unavailable if > the DSM interface is used. > + > +What: /sys/bus/pci/devices/.../tsm/ > +Date: July 2024 > +Contact: linux-coco@lists.linux.dev > +Description: > + This directory only appears if a physical device function supports > + authentication (PCIe CMA-SPDM), interface security (PCIe TDISP), and is > + accepted for secure operation by the platform TSM driver. This attribute > + directory appears dynamically after the platform TSM driver loads. So, > + only after the /sys/class/tsm/tsm0 device arrives can tools assume that > + devices without a tsm/ attribute directory will never have one, before > + that, the security capabilities of the device relative to the platform > + TSM are unknown. See Documentation/ABI/testing/sysfs-class-tsm. Wrap to fit in 80 columns like the rest of the file. > + > +What: /sys/bus/pci/devices/.../tsm/connect > +Date: July 2024 > +Contact: linux-coco@lists.linux.dev > +Description: > + (RW) Writing "1" to this file triggers the platform TSM (TEE Security > + Manager) to establish a connection with the device. This typically > + includes an SPDM (DMTF Security Protocols and Data Models) session over > + PCIe DOE (Data Object Exchange) and may also include PCIe IDE (Integrity > + and Data Encryption) establishment.
> +static int pci_tsm_disconnect(struct pci_dev *pdev) > +{ > + struct pci_tsm *pci_tsm = pdev->tsm; > + > + lockdep_assert_held(&pci_tsm_rwsem); > + if_not_guard(mutex_intr, &pci_tsm->lock) > + return -EINTR; > + > + if (pci_tsm->state < PCI_TSM_CONNECT) > + return 0; > + if (pci_tsm->state < PCI_TSM_INIT) > + return -ENXIO; Check PCI_TSM_INIT first, or this condition will never hit. if (pci_tsm->state < PCI_TSM_INIT) return -ENXIO; if (pci_tsm->state < PCI_TSM_CONNECT) return 0; I suggest the same sequence for pci_tsm_connect(). > + > + tsm_ops->disconnect(pdev); > + pci_tsm->state = PCI_TSM_INIT; > + > + return 0; > +} > + > +static int pci_tsm_connect(struct pci_dev *pdev) > +{ > + struct pci_tsm *pci_tsm = pdev->tsm; > + int rc; > + > + lockdep_assert_held(&pci_tsm_rwsem); > + if_not_guard(mutex_intr, &pci_tsm->lock) > + return -EINTR; > + > + if (pci_tsm->state >= PCI_TSM_CONNECT) > + return 0; > + if (pci_tsm->state < PCI_TSM_INIT) > + return -ENXIO; > + > + rc = tsm_ops->connect(pdev); > + if (rc) > + return rc; > + pci_tsm->state = PCI_TSM_CONNECT; > + return 0; > +} > + [...] > + > +static void __pci_tsm_init(struct pci_dev *pdev) > +{ > + bool tee_cap; > + > + if (!is_physical_endpoint(pdev)) > + return; This Filters out virtual functions, just because not ready for support, is it? > + > + tee_cap = pdev->devcap & PCI_EXP_DEVCAP_TEE; > + > + if (!(pdev->ide_cap || tee_cap)) > + return; > + > + lockdep_assert_held_write(&pci_tsm_rwsem); > + if (!tsm_ops) > + return; > + > + struct pci_tsm *pci_tsm __free(kfree) = kzalloc(sizeof(*pci_tsm), GFP_KERNEL); > + if (!pci_tsm) > + return; > + > + /* > + * If a physical device has any security capabilities it may be > + * a candidate to connect with the platform TSM > + */ > + struct pci_dsm *dsm __free(dsm_remove) = tsm_ops->probe(pdev); IIUC, pdev->tsm should be for every pci function (physical or virtual), pdev->tsm->dsm should be only for physical functions, is it? > + > + pci_dbg(pdev, "Device security capabilities detected (%s%s ), TSM %s\n", > + pdev->ide_cap ? " ide" : "", tee_cap ? " tee" : "", > + dsm ? "attach" : "skip"); > + > + if (!dsm) > + return; > + > + mutex_init(&pci_tsm->lock); > + pci_tsm->doe_mb = pci_find_doe_mailbox(pdev, PCI_VENDOR_ID_PCI_SIG, > + PCI_DOE_PROTO_CMA); > + if (!pci_tsm->doe_mb) { > + pci_warn(pdev, "TSM init failure, no CMA mailbox\n"); > + return; > + } > + > + pci_tsm->state = PCI_TSM_INIT; > + pci_tsm->dsm = no_free_ptr(dsm); > + pdev->tsm = no_free_ptr(pci_tsm); > + sysfs_update_group(&pdev->dev.kobj, &pci_tsm_auth_attr_group); > + sysfs_update_group(&pdev->dev.kobj, &pci_tsm_attr_group); > + if (pci_tsm_owner_attr_group) > + sysfs_merge_group(&pdev->dev.kobj, pci_tsm_owner_attr_group); > +}
diff --git a/Documentation/ABI/testing/sysfs-bus-pci b/Documentation/ABI/testing/sysfs-bus-pci index 5da6a14dc326..0d742ef41aa7 100644 --- a/Documentation/ABI/testing/sysfs-bus-pci +++ b/Documentation/ABI/testing/sysfs-bus-pci @@ -583,3 +583,45 @@ Description: enclosure-specific indications "specific0" to "specific7", hence the corresponding led class devices are unavailable if the DSM interface is used. + +What: /sys/bus/pci/devices/.../tsm/ +Date: July 2024 +Contact: linux-coco@lists.linux.dev +Description: + This directory only appears if a physical device function supports + authentication (PCIe CMA-SPDM), interface security (PCIe TDISP), and is + accepted for secure operation by the platform TSM driver. This attribute + directory appears dynamically after the platform TSM driver loads. So, + only after the /sys/class/tsm/tsm0 device arrives can tools assume that + devices without a tsm/ attribute directory will never have one, before + that, the security capabilities of the device relative to the platform + TSM are unknown. See Documentation/ABI/testing/sysfs-class-tsm. + +What: /sys/bus/pci/devices/.../tsm/connect +Date: July 2024 +Contact: linux-coco@lists.linux.dev +Description: + (RW) Writing "1" to this file triggers the platform TSM (TEE Security + Manager) to establish a connection with the device. This typically + includes an SPDM (DMTF Security Protocols and Data Models) session over + PCIe DOE (Data Object Exchange) and may also include PCIe IDE (Integrity + and Data Encryption) establishment. + +What: /sys/bus/pci/devices/.../authenticated +Date: July 2024 +Contact: linux-pci@vger.kernel.org +Description: + When the device's tsm/ directory is present device + authentication (PCIe CMA-SPDM) and link encryption (PCIe IDE) + are handled by the platform TSM (TEE Security Manager). When the + tsm/ directory is not present this attribute reflects only the + native CMA-SPDM authentication state with the kernel's + certificate store. + + If the attribute is not present, it indicates that + authentication is unsupported by the device, or the TSM has no + available authentication methods for the device. + + When present and the tsm/ attribute directory is present, the + authenticated attribute is an alias for the device 'connect' + state. See the 'tsm/connect' attribute for more details. diff --git a/MAINTAINERS b/MAINTAINERS index abaabbc39134..8f28a2d9bbc6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -23843,8 +23843,10 @@ M: Dan Williams <dan.j.williams@intel.com> L: linux-coco@lists.linux.dev S: Maintained F: Documentation/ABI/testing/configfs-tsm-report +F: drivers/pci/tsm.c F: drivers/virt/coco/guest/ F: drivers/virt/coco/host/ +F: include/linux/pci-tsm.h F: include/linux/tsm.h TRUSTED SERVICES TEE DRIVER diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index 4e5236c456f5..8dab60dadb7d 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -124,6 +124,19 @@ config PCI_ATS config PCI_IDE bool +config PCI_TSM + bool "TEE Security Manager for PCI Device Security" + select PCI_IDE + 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 diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index 6612256fd37d..2c545f877062 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -35,6 +35,7 @@ obj-$(CONFIG_XEN_PCIDEV_FRONTEND) += xen-pcifront.o obj-$(CONFIG_VGA_ARB) += vgaarb.o obj-$(CONFIG_PCI_DOE) += doe.o obj-$(CONFIG_PCI_IDE) += ide.o +obj-$(CONFIG_PCI_TSM) += tsm.o obj-$(CONFIG_PCI_DYNAMIC_OF_NODES) += of_property.o obj-$(CONFIG_PCI_NPEM) += npem.o obj-$(CONFIG_PCIE_TPH) += tph.o diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 7679d75d71e5..7e1ed3440a50 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -1696,6 +1696,10 @@ const struct attribute_group *pci_dev_attr_groups[] = { #endif #ifdef CONFIG_PCIEASPM &aspm_ctrl_attr_group, +#endif +#ifdef CONFIG_PCI_TSM + &pci_tsm_auth_attr_group, + &pci_tsm_attr_group, #endif NULL, }; diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 0305f497b28a..0537fc72d5be 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -458,6 +458,16 @@ void pci_ide_init(struct pci_dev *dev); static inline void pci_ide_init(struct pci_dev *dev) { } #endif +#ifdef CONFIG_PCI_TSM +void pci_tsm_init(struct pci_dev *pdev); +void pci_tsm_destroy(struct pci_dev *pdev); +extern const struct attribute_group pci_tsm_attr_group; +extern const struct attribute_group pci_tsm_auth_attr_group; +#else +static inline void pci_tsm_init(struct pci_dev *pdev) { } +static inline void pci_tsm_destroy(struct pci_dev *pdev) { } +#endif + /** * pci_dev_set_io_state - Set the new error state if possible. * diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index e22f515a8da9..7cddde3cb0ed 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -2518,6 +2518,7 @@ static void pci_init_capabilities(struct pci_dev *dev) pci_doe_init(dev); /* Data Object Exchange */ pci_tph_init(dev); /* TLP Processing Hints */ pci_ide_init(dev); /* Link Integrity and Data Encryption */ + pci_tsm_init(dev); /* TEE Security Manager connection */ pcie_report_downtraining(dev); pci_init_reset_methods(dev); diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c index efc37fcb73e2..fd4ccafed067 100644 --- a/drivers/pci/remove.c +++ b/drivers/pci/remove.c @@ -55,6 +55,9 @@ static void pci_destroy_dev(struct pci_dev *dev) pci_npem_remove(dev); + /* before device_del() to keep config cycle access */ + pci_tsm_destroy(dev); + device_del(&dev->dev); down_write(&pci_bus_sem); diff --git a/drivers/pci/tsm.c b/drivers/pci/tsm.c new file mode 100644 index 000000000000..04e9257a6e41 --- /dev/null +++ b/drivers/pci/tsm.c @@ -0,0 +1,293 @@ +// 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/pci-tsm.h> +#include <linux/bitfield.h> +#include "pci.h" + +/* + * Provide a read/write lock against the init / exit of pdev tsm + * capabilities and arrival/departure of a tsm instance + */ +static DECLARE_RWSEM(pci_tsm_rwsem); +static const struct pci_tsm_ops *tsm_ops; + +/* supplemental attributes to surface when pci_tsm_attr_group is active */ +static const struct attribute_group *pci_tsm_owner_attr_group; + +static int pci_tsm_disconnect(struct pci_dev *pdev) +{ + struct pci_tsm *pci_tsm = pdev->tsm; + + lockdep_assert_held(&pci_tsm_rwsem); + if_not_guard(mutex_intr, &pci_tsm->lock) + return -EINTR; + + if (pci_tsm->state < PCI_TSM_CONNECT) + return 0; + if (pci_tsm->state < PCI_TSM_INIT) + return -ENXIO; + + tsm_ops->disconnect(pdev); + pci_tsm->state = PCI_TSM_INIT; + + return 0; +} + +static int pci_tsm_connect(struct pci_dev *pdev) +{ + struct pci_tsm *pci_tsm = pdev->tsm; + int rc; + + lockdep_assert_held(&pci_tsm_rwsem); + if_not_guard(mutex_intr, &pci_tsm->lock) + return -EINTR; + + if (pci_tsm->state >= PCI_TSM_CONNECT) + return 0; + if (pci_tsm->state < PCI_TSM_INIT) + return -ENXIO; + + rc = tsm_ops->connect(pdev); + if (rc) + return rc; + pci_tsm->state = PCI_TSM_CONNECT; + return 0; +} + +static ssize_t connect_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t len) +{ + int rc; + bool connect; + struct pci_dev *pdev = to_pci_dev(dev); + + rc = kstrtobool(buf, &connect); + if (rc) + return rc; + + if_not_guard(rwsem_read_intr, &pci_tsm_rwsem) + return -EINTR; + + if (connect) + rc = pci_tsm_connect(pdev); + else + rc = pci_tsm_disconnect(pdev); + if (rc) + return rc; + return len; +} + +static ssize_t connect_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct pci_dev *pdev = to_pci_dev(dev); + + if_not_guard(rwsem_read_intr, &pci_tsm_rwsem) + return -EINTR; + if (!pdev->tsm) + return -ENXIO; + return sysfs_emit(buf, "%d\n", pdev->tsm->state >= PCI_TSM_CONNECT); +} +static DEVICE_ATTR_RW(connect); + +static bool pci_tsm_group_visible(struct kobject *kobj) +{ + struct device *dev = kobj_to_dev(kobj); + struct pci_dev *pdev = to_pci_dev(dev); + + if (pdev->tsm) + return true; + return false; +} +DEFINE_SIMPLE_SYSFS_GROUP_VISIBLE(pci_tsm); + +static struct attribute *pci_tsm_attrs[] = { + &dev_attr_connect.attr, + NULL, +}; + +const struct attribute_group pci_tsm_attr_group = { + .name = "tsm", + .attrs = pci_tsm_attrs, + .is_visible = SYSFS_GROUP_VISIBLE(pci_tsm), +}; + +static ssize_t authenticated_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + /* + * When device authentication is TSM owned, 'authenticated' is + * identical to the connect state. + */ + return connect_show(dev, attr, buf); +} +static DEVICE_ATTR_RO(authenticated); + +static struct attribute *pci_tsm_auth_attrs[] = { + &dev_attr_authenticated.attr, + NULL, +}; + +const struct attribute_group pci_tsm_auth_attr_group = { + .attrs = pci_tsm_auth_attrs, + .is_visible = SYSFS_GROUP_VISIBLE(pci_tsm), +}; + +static void dsm_remove(struct pci_dsm *dsm) +{ + if (!dsm) + return; + tsm_ops->remove(dsm); +} +DEFINE_FREE(dsm_remove, struct pci_dsm *, if (_T) dsm_remove(_T)) + +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 void __pci_tsm_init(struct pci_dev *pdev) +{ + bool tee_cap; + + if (!is_physical_endpoint(pdev)) + return; + + tee_cap = pdev->devcap & PCI_EXP_DEVCAP_TEE; + + if (!(pdev->ide_cap || tee_cap)) + return; + + lockdep_assert_held_write(&pci_tsm_rwsem); + if (!tsm_ops) + return; + + struct pci_tsm *pci_tsm __free(kfree) = kzalloc(sizeof(*pci_tsm), GFP_KERNEL); + if (!pci_tsm) + return; + + /* + * If a physical device has any security capabilities it may be + * a candidate to connect with the platform TSM + */ + struct pci_dsm *dsm __free(dsm_remove) = tsm_ops->probe(pdev); + + pci_dbg(pdev, "Device security capabilities detected (%s%s ), TSM %s\n", + pdev->ide_cap ? " ide" : "", tee_cap ? " tee" : "", + dsm ? "attach" : "skip"); + + if (!dsm) + return; + + mutex_init(&pci_tsm->lock); + pci_tsm->doe_mb = pci_find_doe_mailbox(pdev, PCI_VENDOR_ID_PCI_SIG, + PCI_DOE_PROTO_CMA); + if (!pci_tsm->doe_mb) { + pci_warn(pdev, "TSM init failure, no CMA mailbox\n"); + return; + } + + pci_tsm->state = PCI_TSM_INIT; + pci_tsm->dsm = no_free_ptr(dsm); + pdev->tsm = no_free_ptr(pci_tsm); + sysfs_update_group(&pdev->dev.kobj, &pci_tsm_auth_attr_group); + sysfs_update_group(&pdev->dev.kobj, &pci_tsm_attr_group); + if (pci_tsm_owner_attr_group) + sysfs_merge_group(&pdev->dev.kobj, pci_tsm_owner_attr_group); +} + +void pci_tsm_init(struct pci_dev *pdev) +{ + guard(rwsem_write)(&pci_tsm_rwsem); + __pci_tsm_init(pdev); +} + +int pci_tsm_register(const struct pci_tsm_ops *ops, const struct attribute_group *grp) +{ + struct pci_dev *pdev = NULL; + + if (!ops) + return 0; + guard(rwsem_write)(&pci_tsm_rwsem); + if (tsm_ops) + return -EBUSY; + tsm_ops = ops; + pci_tsm_owner_attr_group = grp; + for_each_pci_dev(pdev) + __pci_tsm_init(pdev); + return 0; +} +EXPORT_SYMBOL_GPL(pci_tsm_register); + +static void __pci_tsm_destroy(struct pci_dev *pdev) +{ + struct pci_tsm *pci_tsm = pdev->tsm; + + if (!pci_tsm) + return; + + lockdep_assert_held_write(&pci_tsm_rwsem); + if (pci_tsm->state > PCI_TSM_INIT) + pci_tsm_disconnect(pdev); + tsm_ops->remove(pci_tsm->dsm); + pdev->tsm = NULL; + if (pci_tsm_owner_attr_group) + sysfs_unmerge_group(&pdev->dev.kobj, pci_tsm_owner_attr_group); + sysfs_update_group(&pdev->dev.kobj, &pci_tsm_attr_group); + sysfs_update_group(&pdev->dev.kobj, &pci_tsm_auth_attr_group); + kfree(pci_tsm); +} + +void pci_tsm_destroy(struct pci_dev *pdev) +{ + guard(rwsem_write)(&pci_tsm_rwsem); + __pci_tsm_destroy(pdev); +} + +void pci_tsm_unregister(const struct pci_tsm_ops *ops) +{ + struct pci_dev *pdev = NULL; + + if (!ops) + return; + guard(rwsem_write)(&pci_tsm_rwsem); + if (ops != tsm_ops) + return; + for_each_pci_dev(pdev) + __pci_tsm_destroy(pdev); + tsm_ops = NULL; +} +EXPORT_SYMBOL_GPL(pci_tsm_unregister); + +int pci_tsm_doe_transfer(struct pci_dev *pdev, enum pci_doe_proto type, + const void *req, size_t req_sz, void *resp, + size_t resp_sz) +{ + if (!pdev->tsm || !pdev->tsm->doe_mb) + return -ENXIO; + + return pci_doe(pdev->tsm->doe_mb, PCI_VENDOR_ID_PCI_SIG, type, req, + req_sz, resp, resp_sz); +} +EXPORT_SYMBOL_GPL(pci_tsm_doe_transfer); diff --git a/drivers/virt/coco/host/tsm-core.c b/drivers/virt/coco/host/tsm-core.c index 0ee738fc40ed..21270210b03f 100644 --- a/drivers/virt/coco/host/tsm-core.c +++ b/drivers/virt/coco/host/tsm-core.c @@ -8,11 +8,13 @@ #include <linux/device.h> #include <linux/module.h> #include <linux/cleanup.h> +#include <linux/pci-tsm.h> static DECLARE_RWSEM(tsm_core_rwsem); static struct class *tsm_class; static struct tsm_subsys { struct device dev; + const struct pci_tsm_ops *pci_ops; } *tsm_subsys; static struct tsm_subsys * @@ -40,7 +42,8 @@ static void put_tsm_subsys(struct tsm_subsys *subsys) DEFINE_FREE(put_tsm_subsys, struct tsm_subsys *, if (!IS_ERR_OR_NULL(_T)) put_tsm_subsys(_T)) struct tsm_subsys *tsm_register(struct device *parent, - const struct attribute_group **groups) + const struct attribute_group **groups, + const struct pci_tsm_ops *pci_ops) { struct device *dev; int rc; @@ -62,10 +65,20 @@ struct tsm_subsys *tsm_register(struct device *parent, if (rc) return ERR_PTR(rc); + rc = pci_tsm_register(pci_ops, NULL); + if (rc) { + dev_err(parent, "PCI initialization failure: %pe\n", + ERR_PTR(rc)); + return ERR_PTR(rc); + } + rc = device_add(dev); - if (rc) + if (rc) { + pci_tsm_unregister(pci_ops); return ERR_PTR(rc); + } + subsys->pci_ops = pci_ops; tsm_subsys = no_free_ptr(subsys); return tsm_subsys; @@ -80,7 +93,9 @@ void tsm_unregister(struct tsm_subsys *subsys) return; } + pci_tsm_unregister(subsys->pci_ops); device_unregister(&subsys->dev); + tsm_subsys = NULL; } EXPORT_SYMBOL_GPL(tsm_unregister); diff --git a/include/linux/pci-tsm.h b/include/linux/pci-tsm.h new file mode 100644 index 000000000000..beb0d68129bc --- /dev/null +++ b/include/linux/pci-tsm.h @@ -0,0 +1,83 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __PCI_TSM_H +#define __PCI_TSM_H +#include <linux/mutex.h> + +struct pci_dev; + +/** + * struct pci_dsm - Device Security Manager context + * @pdev: physical device back pointer + */ +struct pci_dsm { + struct pci_dev *pdev; +}; + +enum pci_tsm_state { + PCI_TSM_ERR = -1, + PCI_TSM_INIT, + PCI_TSM_CONNECT, +}; + +/** + * struct pci_tsm - Platform TSM transport context + * @state: reflect device initialized, connected, or bound + * @lock: protect @state vs pci_tsm_ops invocation + * @doe_mb: PCIe Data Object Exchange mailbox + * @dsm: TSM driver device context established by pci_tsm_ops.probe + */ +struct pci_tsm { + enum pci_tsm_state state; + struct mutex lock; + struct pci_doe_mb *doe_mb; + struct pci_dsm *dsm; +}; + +/** + * struct pci_tsm_ops - Low-level TSM-exported interface to the PCI core + * @probe: probe/accept device for tsm operation, setup DSM context + * @remove: destroy DSM context + * @connect: establish / validate a secure connection (e.g. IDE) with the device + * @disconnect: teardown the secure connection + * + * @probe and @remove run in pci_tsm_rwsem held for write context. All + * other ops run under the @pdev->tsm->lock mutex and pci_tsm_rwsem held + * for read. + */ +struct pci_tsm_ops { + struct pci_dsm *(*probe)(struct pci_dev *pdev); + void (*remove)(struct pci_dsm *dsm); + int (*connect)(struct pci_dev *pdev); + void (*disconnect)(struct pci_dev *pdev); +}; + +enum pci_doe_proto { + PCI_DOE_PROTO_CMA = 1, + PCI_DOE_PROTO_SSESSION = 2, +}; + +#ifdef CONFIG_PCI_TSM +int pci_tsm_register(const struct pci_tsm_ops *ops, + const struct attribute_group *grp); +void pci_tsm_unregister(const struct pci_tsm_ops *ops); +int pci_tsm_doe_transfer(struct pci_dev *pdev, enum pci_doe_proto type, + const void *req, size_t req_sz, void *resp, + size_t resp_sz); +#else +static inline int pci_tsm_register(const struct pci_tsm_ops *ops, + const struct attribute_group *grp) +{ + return 0; +} +static inline void pci_tsm_unregister(const struct pci_tsm_ops *ops) +{ +} +static inline int pci_tsm_doe_transfer(struct pci_dev *pdev, + enum pci_doe_proto type, const void *req, + size_t req_sz, void *resp, + size_t resp_sz) +{ + return -ENOENT; +} +#endif +#endif /*__PCI_TSM_H */ diff --git a/include/linux/pci.h b/include/linux/pci.h index 50811b7655dd..a0900e7d2012 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -535,6 +535,9 @@ struct pci_dev { u16 ide_cap; /* Link Integrity & Data Encryption */ u16 sel_ide_cap; /* - Selective Stream register block */ int nr_ide_mem; /* - Address range limits for streams */ +#endif +#ifdef CONFIG_PCI_TSM + struct pci_tsm *tsm; /* TSM operation state */ #endif u16 acs_cap; /* ACS Capability offset */ u8 supported_speeds; /* Supported Link Speeds Vector */ diff --git a/include/linux/tsm.h b/include/linux/tsm.h index 1a97459fc23e..46b9a0c6ea4e 100644 --- a/include/linux/tsm.h +++ b/include/linux/tsm.h @@ -111,7 +111,9 @@ struct tsm_report_ops { int tsm_report_register(const struct tsm_report_ops *ops, void *priv); int tsm_report_unregister(const struct tsm_report_ops *ops); struct tsm_subsys; +struct pci_tsm_ops; struct tsm_subsys *tsm_register(struct device *parent, - const struct attribute_group **groups); + const struct attribute_group **groups, + const struct pci_tsm_ops *ops); void tsm_unregister(struct tsm_subsys *subsys); #endif /* __TSM_H */ diff --git a/include/uapi/linux/pci_regs.h b/include/uapi/linux/pci_regs.h index 9635b27d2485..19bba65a262c 100644 --- a/include/uapi/linux/pci_regs.h +++ b/include/uapi/linux/pci_regs.h @@ -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 */