From: <dan.j.williams@intel.com>
To: Jonathan Cameron <Jonathan.Cameron@huawei.com>,
Dan Williams <dan.j.williams@intel.com>
Cc: <linux-coco@lists.linux.dev>, <linux-pci@vger.kernel.org>,
<linux-kernel@vger.kernel.org>, <bhelgaas@google.com>,
<aik@amd.com>, <lukas@wunner.de>,
Samuel Ortiz <sameo@rivosinc.com>,
Xu Yilun <yilun.xu@linux.intel.com>
Subject: Re: [PATCH v4 04/10] PCI/TSM: Authenticate devices via platform TSM
Date: Tue, 5 Aug 2025 18:35:46 -0700 [thread overview]
Message-ID: <6892b172976f7_55f0910067@dwillia2-xfh.jf.intel.com.notmuch> (raw)
In-Reply-To: <20250729155650.000017b3@huawei.com>
Jonathan Cameron wrote:
> On Thu, 17 Jul 2025 11:33:52 -0700
> Dan Williams <dan.j.williams@intel.com> wrote:
>
> > The PCIe 6.1 specification, section 11, introduces the Trusted Execution
> > Environment (TEE) Device Interface Security Protocol (TDISP). This
> > protocol 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. Consider that the TSM driver may itself be a PCI driver.
> > Userspace can watch for the arrival of a "TSM" device,
> > /sys/class/tsm/tsm0/uevent KOBJ_CHANGE, to know when the PCI core has
> > initialized TSM services.
> >
> > The operations that can be executed against a PCI device are split into
> > 2 mutually exclusive operation sets, "Link" and "Security" (struct
> > pci_tsm_{link,security}_ops). The "Link" operations manage physical link
> > security properties and communication with the device's Device Security
> > Manager firmware. These are the host side operations in TDISP. The
> > "Security" operations coordinate the security state of the assigned
> > virtual device (TDI). These are the guest side operations in TDISP. Only
> > link management operations are defined at this stage and placeholders
> > provided for the security operations.
> >
> > The locking allows for multiple devices to be executing commands
> > simultaneously, one outstanding command per-device and an rwsem
> > synchronizes the implementation relative to TSM
> > registration/unregistration events.
> >
> > 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>
> Various things inline.
>
> > diff --git a/drivers/pci/tsm.c b/drivers/pci/tsm.c
> > new file mode 100644
> > index 000000000000..0784cc436dd3
> > --- /dev/null
> > +++ b/drivers/pci/tsm.c
> > @@ -0,0 +1,554 @@
> > +// 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.
> > + */
>
> > +static void tsm_remove(struct pci_tsm *tsm)
> > +{
> > + struct pci_dev *pdev;
> > +
> > + if (!tsm)
>
> You protect against this in the DEFINE_FREE() so probably safe
> to assume it is always set if we get here.
It is safe, but I would rather not require reading other code to
understand the expectation that some callers may unconditionally call
this routine.
> > + return;
> > +
> > + pdev = tsm->pdev;
> > + tsm->ops->remove(tsm);
> > + pdev->tsm = NULL;
> > +}
> > +DEFINE_FREE(tsm_remove, struct pci_tsm *, if (_T) tsm_remove(_T))
> > +
> > +static int call_cb_put(struct pci_dev *pdev, void *data,
>
> Is this combination worth while? I don't like the 'and' aspect of it
> and it only saves a few lines...
>
> vs
> if (pdev) {
> rc = cb(pdev, data);
> pci_dev_put(pdev);
> if (rc)
> return;
> }
I think it is worth it, but an even better option is to just let
scope-based cleanup handle it by moving the variable inside the loop
declaration.
>
> > + int (*cb)(struct pci_dev *pdev, void *data))
> > +{
> > + int rc;
> > +
> > + if (!pdev)
> > + return 0;
> > + rc = cb(pdev, data);
> > + pci_dev_put(pdev);
> > + return rc;
> > +}
> > +
> > +static void pci_tsm_walk_fns(struct pci_dev *pdev,
> > + int (*cb)(struct pci_dev *pdev, void *data),
> > + void *data)
> > +{
> > + struct pci_dev *fn;
> > + int i;
> > +
> > + /* walk virtual functions */
> > + for (i = 0; i < pci_num_vf(pdev); i++) {
> > + fn = pci_get_domain_bus_and_slot(pci_domain_nr(pdev->bus),
> > + pci_iov_virtfn_bus(pdev, i),
> > + pci_iov_virtfn_devfn(pdev, i));
> > + if (call_cb_put(fn, data, cb))
> > + return;
> > + }
> > +
> > + /* walk subordinate physical functions */
> > + for (i = 1; i < 8; i++) {
> > + fn = pci_get_slot(pdev->bus,
> > + PCI_DEVFN(PCI_SLOT(pdev->devfn), i));
> > + if (call_cb_put(fn, data, cb))
> > + return;
> > + }
> > +
> > + /* walk downstream devices */
> > + if (pci_pcie_type(pdev) != PCI_EXP_TYPE_UPSTREAM)
>
> spaces rather than tabs...
Fixed.
> > + return;
> > +
> > + if (!is_dsm(pdev))
> > + return;
> > +
> > + pci_walk_bus(pdev->subordinate, cb, data);
> > +}
> > +
> > +static void pci_tsm_walk_fns_reverse(struct pci_dev *pdev,
> > + int (*cb)(struct pci_dev *pdev,
> > + void *data),
> > + void *data)
> > +{
> > + struct pci_dev *fn;
> > + int i;
> > +
> > + /* reverse walk virtual functions */
> > + for (i = pci_num_vf(pdev) - 1; i >= 0; i--) {
> > + fn = pci_get_domain_bus_and_slot(pci_domain_nr(pdev->bus),
> > + pci_iov_virtfn_bus(pdev, i),
> > + pci_iov_virtfn_devfn(pdev, i));
> > + if (call_cb_put(fn, data, cb))
> > + return;
> > + }
> > +
> While it probably doesn't matter can we make this strict reverse by doing
> the physical functions first? I prefer not to think about whether it matters.
Actually, me too, and in that case I also want the downstream devices to
be done in strict reverse order. So, I do not have a use in mind where
this matters, but changed the order to physical->virtual->downstream and
downstream->virtual->physical for the reverse case.
Oh, this is missing the potential case of SR-IOV on multiple physical
functions of the device, so added that too.
>
> > + /* reverse walk subordinate physical functions */
> > + for (i = 7; i >= 1; i--) {
> > + fn = pci_get_slot(pdev->bus,
> > + PCI_DEVFN(PCI_SLOT(pdev->devfn), i));
> > + if (call_cb_put(fn, data, cb))
> > + return;
> > + }
> > +
> > + /* reverse walk downstream devices */
> > + if (pci_pcie_type(pdev) != PCI_EXP_TYPE_UPSTREAM)
> > + return;
> > +
> > + if (!is_dsm(pdev))
> > + return;
> > +
> > + pci_walk_bus_reverse(pdev->subordinate, cb, data);
>
> Likewise, can we do this before the rest.
Fixed.
>
> > +}
>
> > +/*
> > + * Find the PCI Device instance that serves as the Device Security
> > + * Manger (DSM) for @pdev. Note that no additional reference is held for
> > + * the resulting device because @pdev always has a longer registered
> > + * lifetime than its DSM by virtue of being a child of or identical to
> > + * its DSM.
> > + */
> > +static struct pci_dev *find_dsm_dev(struct pci_dev *pdev)
> > +{
> > + struct pci_dev *uport_pf0;
> > +
> > + if (is_pci_tsm_pf0(pdev))
> > + return pdev;
> > +
> > + struct pci_dev *pf0 __free(pci_dev_put) = pf0_dev_get(pdev);
> > + if (!pf0)
> > + return NULL;
> > +
> > + if (is_dsm(pf0))
> > + return pf0;
>
>
> Unusual for a find command to not hold the device reference on the device
> it returns. Maybe just call that out in the comment.
It is, "Note that no additional reference..."
> > +
> > + /*
> > + * For cases where a switch may be hosting TDISP services on
> > + * behalf of downstream devices, check the first usptream port
> > + * relative to this endpoint.
> > + */
> Odd alignment. Space rather than tab.
Hmm, clang-format does not fixup tabs vs spaces in block comments,
fixed.
>
>
> > + if (!pdev->dev.parent || !pdev->dev.parent->parent)
> > + return NULL;
> > +
> > + uport_pf0 = to_pci_dev(pdev->dev.parent->parent);
> > + if (is_dsm(uport_pf0))
> > + return uport_pf0;
> > + return NULL;
> > +}
>
>
> > +/**
> > + * pci_tsm_pf0_constructor() - common 'struct pci_tsm_pf0' initialization
> > + * @pdev: Physical Function 0 PCI device (as indicated by is_pci_tsm_pf0())
> > + * @tsm: context to initialize
>
> ops missing. Run kernel-doc or do W=1 build to catch these.
TIL I do not need need to create a Documentation file to reference this
file to get kdoc build warnings.
Fixed.
>
> > + */
> > +int pci_tsm_pf0_constructor(struct pci_dev *pdev, struct pci_tsm_pf0 *tsm,
> > + const struct pci_tsm_ops *ops)
> > +{
> > + struct tsm_dev *tsm_dev = ops->owner;
> > +
> > + mutex_init(&tsm->lock);
> Might as well do devm_mutex_init()
Hmm, no, this is running out of the driver bind lifetime of @pdev.
> > + tsm->doe_mb = pci_find_doe_mailbox(pdev, PCI_VENDOR_ID_PCI_SIG,
> > + PCI_DOE_PROTO_CMA);
> > + if (!tsm->doe_mb) {
> > + pci_warn(pdev, "TSM init failure, no CMA mailbox\n");
> > + return -ENODEV;
> > + }
> > +
> > + if (tsm_pci_group(tsm_dev))
> > + sysfs_merge_group(&pdev->dev.kobj, tsm_pci_group(tsm_dev));
> > +
> > + return pci_tsm_constructor(pdev, &tsm->tsm, ops);
> > +}
> > +EXPORT_SYMBOL_GPL(pci_tsm_pf0_constructor);
>
> > diff --git a/drivers/virt/coco/tsm-core.c b/drivers/virt/coco/tsm-core.c
> > index 1f53b9049e2d..093824dc68dd 100644
> > --- a/drivers/virt/coco/tsm-core.c
> > +++ b/drivers/virt/coco/tsm-core.c
>
> > +/*
> > + * Caller responsible for ensuring it does not race tsm_dev
> > + * unregistration.
> Wrap is a bit early. unregistration fits on the line above.
True, fixed up editor settings to automate this.
> > + */
> > +struct tsm_dev *find_tsm_dev(int id)
> > +{
> > + guard(rcu)();
> > + return idr_find(&tsm_idr, id);
> > +}
>
> > @@ -44,6 +76,29 @@ static struct tsm_dev *alloc_tsm_dev(struct device *parent,
> > return no_free_ptr(tsm_dev);
> > }
> >
> > +static struct tsm_dev *tsm_register_pci_or_reset(struct tsm_dev *tsm_dev,
> > + struct pci_tsm_ops *pci_ops)
> > +{
> > + int rc;
> > +
> > + if (!pci_ops)
> > + return tsm_dev;
> > +
> > + pci_ops->owner = tsm_dev;
> > + tsm_dev->pci_ops = pci_ops;
> > + rc = pci_tsm_register(tsm_dev);
> > + if (rc) {
> > + dev_err(tsm_dev->dev.parent,
> > + "PCI/TSM registration failure: %d\n", rc);
> > + device_unregister(&tsm_dev->dev);
>
> As below. I'm fairly sure this device_unregister is nothing to do with
> what this function is doing, so having it buried in here is less easy
> to follow than pushing it up a layer.
I prefer a short function with an early exit and no scope based unwind
for this.
> > + return ERR_PTR(rc);
> > + }
> > +
> > + /* Notify TSM userspace that PCI/TSM operations are now possible */
> > + kobject_uevent(&tsm_dev->dev.kobj, KOBJ_CHANGE);
> > + return tsm_dev;
> > +}
> > +
> > static void put_tsm_dev(struct tsm_dev *tsm_dev)
> > {
> > if (!IS_ERR_OR_NULL(tsm_dev))
> > @@ -54,7 +109,8 @@ DEFINE_FREE(put_tsm_dev, struct tsm_dev *,
> > if (!IS_ERR_OR_NULL(_T)) put_tsm_dev(_T))
> >
> > struct tsm_dev *tsm_register(struct device *parent,
> > - const struct attribute_group **groups)
> > + const struct attribute_group **groups,
> > + struct pci_tsm_ops *pci_ops)
> > {
> > struct tsm_dev *tsm_dev __free(put_tsm_dev) =
> > alloc_tsm_dev(parent, groups);
> > @@ -73,12 +129,13 @@ struct tsm_dev *tsm_register(struct device *parent,
> > if (rc)
> > return ERR_PTR(rc);
> >
> > - return no_free_ptr(tsm_dev);
> > + return tsm_register_pci_or_reset(no_free_ptr(tsm_dev), pci_ops);
>
> Having a function call that either succeeds or cleans up something it
> never did on error is odd.
devm_add_action_or_reset() is the same pattern. Do the action, or
otherwise take care of cleaning up. The action in this case is
pci_tsm_register() and the reset is cleaning up the device_add().
> The or_reset hints at that oddity but to me is not enough. If you want
> to use __free magic in here maybe hand off the tsm_dev on succesful
> device registration.
>
> struct tsm_dev *registered_tsm_dev __free(unregister_tsm_dev) =
> no_free_ptr(tsm_dev);
That does not look like an improvement to me.
The moment device_add() succeeds the cleanup shifts from put_device() to
device_unregister(). I considered wrapping device_add(), but I think it
is awkard to redeclare the same variable again vs being able to walk all
instances of @tsm_dev in the function.
[..]
> > + * struct pci_tsm_ops - manage confidential links and security state
> > + * @link_ops: Coordinate PCIe SPDM and IDE establishment via a platform TSM.
> > + * Provide a secure session transport for TDISP state management
> > + * (typically bare metal physical function operations).
> > + * @sec_ops: Lock, unlock, and interrogate the security state of the
> > + * function via the platform TSM (typically virtual function
> > + * operations).
> > + * @owner: Back reference to the TSM device that owns this instance.
> > + *
> > + * This operations are mutually exclusive either a tsm_dev instance
> > + * manages phyiscal link properties or it manages function security
> > + * states like TDISP lock/unlock.
> > + */
> > +struct pci_tsm_ops {
> > + /*
> Likewise though I'm not sure if kernel-doc deals with struct groups.
It does not.
> > +/**
> > + * struct pci_tsm_pf0 - Physical Function 0 TDISP link context
> > + * @tsm: generic core "tsm" context
> > + * @lock: protect @state vs pci_tsm_ops invocation
>
> What is @state referring to?
@state was removed with v4 of the PCI/TSM series.
The kernel-doc for 'struct pci_tsm_pf0' is now:
/**
* struct pci_tsm_pf0 - Physical Function 0 TDISP link context
* @tsm: generic core "tsm" context
* @lock: mutual exclustion for pci_tsm_ops invocation
* @doe_mb: PCIe Data Object Exchange mailbox
*/
struct pci_tsm_pf0 {
struct pci_tsm tsm;
struct mutex lock;
struct pci_doe_mb *doe_mb;
};
next prev parent reply other threads:[~2025-08-06 1:58 UTC|newest]
Thread overview: 70+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-07-17 18:33 [PATCH v4 00/10] PCI/TSM: Core infrastructure for PCI device security (TDISP) Dan Williams
2025-07-17 18:33 ` [PATCH v4 01/10] coco/tsm: Introduce a core device for TEE Security Managers Dan Williams
2025-07-29 11:28 ` Jonathan Cameron
2025-07-17 18:33 ` [PATCH v4 02/10] PCI/IDE: Enumerate Selective Stream IDE capabilities Dan Williams
2025-07-29 12:03 ` Jonathan Cameron
2025-08-05 20:59 ` dan.j.williams
2025-08-07 20:12 ` Bjorn Helgaas
2025-08-07 22:37 ` dan.j.williams
2025-08-07 22:53 ` Bjorn Helgaas
2025-08-08 2:17 ` dan.j.williams
2025-08-08 15:59 ` Bjorn Helgaas
2025-08-07 22:43 ` Bjorn Helgaas
2025-07-17 18:33 ` [PATCH v4 03/10] PCI: Introduce pci_walk_bus_reverse(), for_each_pci_dev_reverse() Dan Williams
2025-07-29 13:06 ` Jonathan Cameron
2025-08-05 23:52 ` dan.j.williams
2025-08-06 10:54 ` Jonathan Cameron
2025-08-07 20:24 ` Bjorn Helgaas
2025-08-07 23:17 ` dan.j.williams
2025-08-07 23:26 ` Bjorn Helgaas
2025-07-17 18:33 ` [PATCH v4 04/10] PCI/TSM: Authenticate devices via platform TSM Dan Williams
2025-07-29 14:56 ` Jonathan Cameron
2025-08-06 1:35 ` dan.j.williams [this message]
2025-08-06 11:10 ` Jonathan Cameron
2025-08-06 23:16 ` dan.j.williams
2025-08-07 10:42 ` Jonathan Cameron
2025-08-07 2:35 ` dan.j.williams
2025-08-05 15:53 ` Xu Yilun
2025-08-06 22:30 ` dan.j.williams
2025-08-07 21:27 ` Bjorn Helgaas
2025-08-08 22:51 ` dan.j.williams
2025-08-13 2:57 ` Alexey Kardashevskiy
2025-08-14 1:40 ` dan.j.williams
2025-08-14 14:52 ` Alexey Kardashevskiy
2025-08-18 21:08 ` dan.j.williams
2025-07-17 18:33 ` [PATCH v4 05/10] samples/devsec: Introduce a PCI device-security bus + endpoint sample Dan Williams
2025-07-29 15:16 ` Jonathan Cameron
2025-08-06 3:20 ` dan.j.williams
2025-08-06 11:16 ` Jonathan Cameron
2025-08-06 18:33 ` dan.j.williams
2025-08-11 13:18 ` Gerd Hoffmann
2025-08-11 20:47 ` dan.j.williams
2025-08-07 21:45 ` Bjorn Helgaas
2025-08-08 23:45 ` dan.j.williams
2025-07-17 18:33 ` [PATCH v4 06/10] PCI: Add PCIe Device 3 Extended Capability enumeration Dan Williams
2025-07-29 15:23 ` Jonathan Cameron
2025-08-06 21:00 ` dan.j.williams
2025-08-06 21:02 ` dan.j.williams
2025-08-07 22:06 ` Bjorn Helgaas
2025-08-09 0:05 ` dan.j.williams
2025-08-07 22:46 ` Bjorn Helgaas
2025-07-17 18:33 ` [PATCH v4 07/10] PCI/IDE: Add IDE establishment helpers Dan Williams
2025-07-29 15:45 ` Jonathan Cameron
2025-08-06 21:40 ` dan.j.williams
2025-08-07 22:38 ` Bjorn Helgaas
2025-08-09 1:52 ` dan.j.williams
2025-08-07 22:47 ` Bjorn Helgaas
2025-08-08 10:21 ` Arto Merilainen
2025-08-08 17:26 ` dan.j.williams
2025-08-11 8:02 ` Arto Merilainen
2025-08-28 8:19 ` Aneesh Kumar K.V
2025-07-17 18:33 ` [PATCH v4 08/10] PCI/IDE: Report available IDE streams Dan Williams
2025-07-29 15:47 ` Jonathan Cameron
2025-08-07 22:48 ` Bjorn Helgaas
2025-07-17 18:33 ` [PATCH v4 09/10] PCI/TSM: Report active " Dan Williams
2025-07-29 15:58 ` Jonathan Cameron
2025-08-06 21:55 ` dan.j.williams
2025-08-07 22:49 ` Bjorn Helgaas
2025-07-17 18:33 ` [PATCH v4 10/10] samples/devsec: Add sample IDE establishment Dan Williams
2025-07-29 16:06 ` Jonathan Cameron
2025-07-18 10:57 ` [PATCH v4 00/10] PCI/TSM: Core infrastructure for PCI device security (TDISP) Aneesh Kumar K.V
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=6892b172976f7_55f0910067@dwillia2-xfh.jf.intel.com.notmuch \
--to=dan.j.williams@intel.com \
--cc=Jonathan.Cameron@huawei.com \
--cc=aik@amd.com \
--cc=bhelgaas@google.com \
--cc=linux-coco@lists.linux.dev \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-pci@vger.kernel.org \
--cc=lukas@wunner.de \
--cc=sameo@rivosinc.com \
--cc=yilun.xu@linux.intel.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).