[PATCH 6/6] PCI/TSM: Add 'dsm' and 'bound' attributes for dependent functions

Dan Williams
2025-11-05T04:00:58Z

PCI/TSM sysfs for physical function 0 devices, i.e. the "DSM" (Device
Security Manager), contains the 'connect' and 'disconnect' attributes.
After a successful 'connect' operation the DSM, its dependent functions
(SR-IOV virtual functions, non-zero multi-functions, or downstream
endpoints of a switch DSM) are candidates for being transitioned into a
TDISP (TEE Device Interface Security Protocol) operational state, via
pci_tsm_bind(). At present sysfs is blind to which devices are capable of
TDISP operation and it is ambiguous which functions are serviced by which
DSMs.

Add a 'dsm' attribute to identify a function's DSM device, and add a
'bound' attribute to identify when a function has entered a TDISP
operational state.

Cc: Bjorn Helgaas <bhelgaas@google.com>
Cc: Lukas Wunner <lukas@wunner.de>
Cc: Samuel Ortiz <sameo@rivosinc.com>
Cc: Alexey Kardashevskiy <aik@amd.com>
Cc: Xu Yilun <yilun.xu@linux.intel.com>
Cc: Suzuki K Poulose <suzuki.poulose@arm.com>
Cc: "Aneesh Kumar K.V" <aneesh.kumar@kernel.org>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
---
Documentation/ABI/testing/sysfs-bus-pci | 30 ++++++
drivers/pci/tsm.c | 130 ++++++++++++++++++++----
2 files changed, 140 insertions(+), 20 deletions(-)

diff --git a/Documentation/ABI/testing/sysfs-bus-pci b/Documentation/ABI/testing/sysfs-bus-pci
index 6ffe02f854d6..b767db2c52cb 100644
--- a/Documentation/ABI/testing/sysfs-bus-pci
+++ b/Documentation/ABI/testing/sysfs-bus-pci
@@ -655,6 +655,36 @@ Description:
(WO) Write the name of the TSM device that was specified
to 'connect' to teardown the connection.

+What: /sys/bus/pci/devices/.../tsm/dsm
+Contact: linux-coco@lists.linux.dev
+Description: (RO) Return PCI device name of this device's DSM (Device
+ Security Manager). When a device is in the connected state it
+ indicates that the platform TSM (TEE Security Manager) has made
+ a secure-session connection with a device's DSM. A DSM is always
+ physical function 0 and when the device supports TDISP (TEE
+ Device Interface Security Protocol) its managed functions also
+ populate this tsm/dsm attribute. The managed functions of a DSM
+ are SR-IOV (Single Root I/O Virtualization) virtual functions,
+ non-zero functions of a multi-function device, or downstream
+ endpoints depending on whether the DSM is an SR-IOV physical
+ function, function0 of a multi-function device, or an upstream
+ PCIe switch port. This is a "link" TSM attribute, see
+ Documentation/ABI/testing/sysfs-class-tsm.
+
+What: /sys/bus/pci/devices/.../tsm/bound
+Contact: linux-coco@lists.linux.dev
+Description: (RO) Return the device name of the TSM when the device is in a
+ TDISP (TEE Device Interface Security Protocol) operational state
+ (LOCKED, RUN, or ERROR, not UNLOCKED). Bound devices consume
+ platform TSM resources and depend on the device's configuration
+ (e.g. BME (Bus Master Enable) and MSE (Memory Space Enable)
+ among other settings) to remain stable for the duration of the
+ bound state. This attribute is only visible for devices that
+ support TDISP operation, and it is only populated after
+ successful connect and TSM bind. The TSM bind operation is
+ initiated by VFIO/IOMMUFD. This is a "link" TSM attribute, see
+ Documentation/ABI/testing/sysfs-class-tsm.
+
What: /sys/bus/pci/devices/.../authenticated
Contact: linux-pci@vger.kernel.org
Description:
diff --git a/drivers/pci/tsm.c b/drivers/pci/tsm.c
index 4dd518b45eea..9abfdb2b2033 100644
--- a/drivers/pci/tsm.c
+++ b/drivers/pci/tsm.c
@@ -151,6 +151,25 @@ static void pci_tsm_walk_fns_reverse(struct pci_dev *pdev,
}
}

+static void link_sysfs_disable(struct pci_dev *pdev)
+{
+ sysfs_update_group(&pdev->dev.kobj, &pci_tsm_auth_attr_group);
+ sysfs_update_group(&pdev->dev.kobj, &pci_tsm_attr_group);
+}
+
+static void link_sysfs_enable(struct pci_dev *pdev)
+{
+ bool tee = has_tee(pdev);
+
+ pci_dbg(pdev, "%s Security Manager detected (%s%s%s)\n",
+ pdev->tsm ? "Device" : "Platform TEE",
+ pdev->ide_cap ? "IDE" : "", pdev->ide_cap && tee ? " " : "",
+ tee ? "TEE" : "");
+
+ sysfs_update_group(&pdev->dev.kobj, &pci_tsm_auth_attr_group);
+ sysfs_update_group(&pdev->dev.kobj, &pci_tsm_attr_group);
+}
+
static int probe_fn(struct pci_dev *pdev, void *dsm)
{
struct pci_dev *dsm_dev = dsm;
@@ -159,6 +178,8 @@ static int probe_fn(struct pci_dev *pdev, void *dsm)
pdev->tsm = ops->probe(dsm_dev->tsm->tsm_dev, pdev);
pci_dbg(pdev, "setup TSM context: DSM: %s status: %s\n",
pci_name(dsm_dev), pdev->tsm ? "success" : "failed");
+ if (pdev->tsm)
+ link_sysfs_enable(pdev);
return 0;
}

@@ -267,6 +288,7 @@ static DEVICE_ATTR_RW(connect);
static int remove_fn(struct pci_dev *pdev, void *data)
{
tsm_remove(pdev->tsm);
+ link_sysfs_disable(pdev);
return 0;
}

@@ -472,12 +494,74 @@ static ssize_t disconnect_store(struct device *dev,
}
static DEVICE_ATTR_WO(disconnect);

+static ssize_t bound_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct pci_tsm_pf0 *tsm_pf0;
+ struct pci_tsm *tsm;
+ int rc;
+
+ ACQUIRE(rwsem_read_intr, lock)(&pci_tsm_rwsem);
+ if ((rc = ACQUIRE_ERR(rwsem_read_intr, &lock)))
+ return rc;
+
+ tsm = pdev->tsm;
+ if (!tsm)
+ return sysfs_emit(buf, "\n");
+ tsm_pf0 = to_pci_tsm_pf0(tsm);
+
+ ACQUIRE(mutex_intr, ops_lock)(&tsm_pf0->lock);
+ if ((rc = ACQUIRE_ERR(mutex_intr, &ops_lock)))
+ return rc;
+
+ if (!tsm->tdi)
+ return sysfs_emit(buf, "\n");
+ return sysfs_emit(buf, "%s\n", dev_name(&tsm->tsm_dev->dev));
+}
+static DEVICE_ATTR_RO(bound);
+
+static ssize_t dsm_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct pci_tsm *tsm;
+ int rc;
+
+ ACQUIRE(rwsem_read_intr, lock)(&pci_tsm_rwsem);
+ if ((rc = ACQUIRE_ERR(rwsem_read_intr, &lock)))
+ return rc;
+
+ tsm = pdev->tsm;
+ if (!tsm)
+ return sysfs_emit(buf, "\n");
+
+ return sysfs_emit(buf, "%s\n", pci_name(tsm->dsm_dev));
+}
+static DEVICE_ATTR_RO(dsm);
+
/* The 'authenticated' attribute is exclusive to the presence of a 'link' TSM */
static bool pci_tsm_link_group_visible(struct kobject *kobj)
{
struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj));

- return pci_tsm_link_count && is_pci_tsm_pf0(pdev);
+ if (!pci_tsm_link_count)
+ return false;
+
+ if (!pci_is_pcie(pdev))
+ return false;
+
+ if (is_pci_tsm_pf0(pdev))
+ return true;
+
+ /*
+ * Show 'authenticated' and other attributes for the managed
+ * sub-functions of a DSM.
+ */
+ if (pdev->tsm)
+ return true;
+
+ return false;
}
DEFINE_SIMPLE_SYSFS_GROUP_VISIBLE(pci_tsm_link);

@@ -489,9 +573,27 @@ static umode_t pci_tsm_attr_visible(struct kobject *kobj,
struct attribute *attr, int n)
{
if (pci_tsm_link_group_visible(kobj)) {
+ struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj));
+
+ if (attr == &dev_attr_bound.attr) {
+ if (is_pci_tsm_pf0(pdev) && has_tee(pdev))
+ return attr->mode;
+ if (pdev->tsm && has_tee(pdev->tsm->dsm_dev))
+ return attr->mode;
+ }
+
+ if (attr == &dev_attr_dsm.attr) {
+ if (is_pci_tsm_pf0(pdev))
+ return attr->mode;
+ if (pdev->tsm && has_tee(pdev->tsm->dsm_dev))
+ return attr->mode;
+ }
+
if (attr == &dev_attr_connect.attr ||
- attr == &dev_attr_disconnect.attr)
- return attr->mode;
+ attr == &dev_attr_disconnect.attr) {
+ if (is_pci_tsm_pf0(pdev))
+ return attr->mode;
+ }
}

return 0;
@@ -506,6 +608,8 @@ DEFINE_SYSFS_GROUP_VISIBLE(pci_tsm);
static struct attribute *pci_tsm_attrs[] = {
&dev_attr_connect.attr,
&dev_attr_disconnect.attr,
+ &dev_attr_bound.attr,
+ &dev_attr_dsm.attr,
NULL
};

@@ -661,18 +765,6 @@ void pci_tsm_pf0_destructor(struct pci_tsm_pf0 *pf0_tsm)
}
EXPORT_SYMBOL_GPL(pci_tsm_pf0_destructor);

-static void pf0_sysfs_enable(struct pci_dev *pdev)
-{
- bool tee = has_tee(pdev);
-
- pci_dbg(pdev, "Device Security Manager detected (%s%s%s)\n",
- pdev->ide_cap ? "IDE" : "", pdev->ide_cap && tee ? " " : "",
- tee ? "TEE" : "");
-
- sysfs_update_group(&pdev->dev.kobj, &pci_tsm_auth_attr_group);
- sysfs_update_group(&pdev->dev.kobj, &pci_tsm_attr_group);
-}
-
int pci_tsm_register(struct tsm_dev *tsm_dev)
{
struct pci_dev *pdev = NULL;
@@ -693,7 +785,7 @@ int pci_tsm_register(struct tsm_dev *tsm_dev)
if (is_link_tsm(tsm_dev) && pci_tsm_link_count++ == 0) {
for_each_pci_dev(pdev)
if (is_pci_tsm_pf0(pdev))
- pf0_sysfs_enable(pdev);
+ link_sysfs_enable(pdev);
} else if (is_devsec_tsm(tsm_dev)) {
pci_tsm_devsec_count++;
}
@@ -727,10 +819,8 @@ static void __pci_tsm_destroy(struct pci_dev *pdev, struct tsm_dev *tsm_dev)
* skipped if the device itself is being removed since sysfs goes away
* naturally at that point
*/
- if (is_link_tsm(tsm_dev) && is_pci_tsm_pf0(pdev) && !pci_tsm_link_count) {
- sysfs_update_group(&pdev->dev.kobj, &pci_tsm_auth_attr_group);
- sysfs_update_group(&pdev->dev.kobj, &pci_tsm_attr_group);
- }
+ if (is_link_tsm(tsm_dev) && is_pci_tsm_pf0(pdev) && !pci_tsm_link_count)
+ link_sysfs_disable(pdev);

/* Nothing else to do if this device never attached to the departing TSM */
if (!tsm)
--
2.51.0