summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Williams <dan.j.williams@intel.com>2025-11-12 18:14:44 -0800
committerDan Williams <dan.j.williams@intel.com>2025-11-14 15:06:57 -0800
commit50cbec192f5317e29be993e2a634bbbdfcf0230e (patch)
tree04bc891186b774c23d2911314664c9e06842a69e
parent079115370d00c78ef69b31dd15def90adf2aa579 (diff)
PCI/TSM: Add pci_tsm_bind() helper for instantiating TDIs
After a PCIe device has established a secure link and session between a TEE Security Manager (TSM) and its local Device Security Manager (DSM), the device or its subfunctions are candidates to be bound to a private memory context, a TVM. A PCIe device function interface assigned to a TVM is a TEE Device Interface (TDI). The pci_tsm_bind() requests the low-level TSM driver to associate the device with private MMIO and private IOMMU context resources of a given TVM represented by a @kvm argument. A device in the bound state corresponds to the TDISP protocol LOCKED state and awaits validation by the TVM. It is a 'struct pci_tsm_link_ops' operation because, similar to IDE establishment, it involves host side resource establishment and context setup on behalf of the guest. It is also expected to be performed lazily to allow for operation of the device in non-confidential "shared" context for pre-lock configuration. Co-developed-by: Xu Yilun <yilun.xu@linux.intel.com> Signed-off-by: Xu Yilun <yilun.xu@linux.intel.com> Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com> Link: https://patch.msgid.link/20251113021446.436830-7-dan.j.williams@intel.com Signed-off-by: Dan Williams <dan.j.williams@intel.com>
-rw-r--r--drivers/pci/tsm.c109
-rw-r--r--include/linux/pci-tsm.h34
2 files changed, 142 insertions, 1 deletions
diff --git a/drivers/pci/tsm.c b/drivers/pci/tsm.c
index 6a2849f77adc..39de91a47a26 100644
--- a/drivers/pci/tsm.c
+++ b/drivers/pci/tsm.c
@@ -270,6 +270,95 @@ static int remove_fn(struct pci_dev *pdev, void *data)
return 0;
}
+/*
+ * Note, this helper only returns an error code and takes an argument for
+ * compatibility with the pci_walk_bus() callback prototype. pci_tsm_unbind()
+ * always succeeds.
+ */
+static int __pci_tsm_unbind(struct pci_dev *pdev, void *data)
+{
+ struct pci_tdi *tdi;
+ struct pci_tsm_pf0 *tsm_pf0;
+
+ lockdep_assert_held(&pci_tsm_rwsem);
+
+ if (!pdev->tsm)
+ return 0;
+
+ tsm_pf0 = to_pci_tsm_pf0(pdev->tsm);
+ guard(mutex)(&tsm_pf0->lock);
+
+ tdi = pdev->tsm->tdi;
+ if (!tdi)
+ return 0;
+
+ to_pci_tsm_ops(pdev->tsm)->unbind(tdi);
+ pdev->tsm->tdi = NULL;
+
+ return 0;
+}
+
+void pci_tsm_unbind(struct pci_dev *pdev)
+{
+ guard(rwsem_read)(&pci_tsm_rwsem);
+ __pci_tsm_unbind(pdev, NULL);
+}
+EXPORT_SYMBOL_GPL(pci_tsm_unbind);
+
+/**
+ * pci_tsm_bind() - Bind @pdev as a TDI for @kvm
+ * @pdev: PCI device function to bind
+ * @kvm: Private memory attach context
+ * @tdi_id: Identifier (virtual BDF) for the TDI as referenced by the TSM and DSM
+ *
+ * Returns 0 on success, or a negative error code on failure.
+ *
+ * Context: Caller is responsible for constraining the bind lifetime to the
+ * registered state of the device. For example, pci_tsm_bind() /
+ * pci_tsm_unbind() limited to the VFIO driver bound state of the device.
+ */
+int pci_tsm_bind(struct pci_dev *pdev, struct kvm *kvm, u32 tdi_id)
+{
+ struct pci_tsm_pf0 *tsm_pf0;
+ struct pci_tdi *tdi;
+
+ if (!kvm)
+ return -EINVAL;
+
+ guard(rwsem_read)(&pci_tsm_rwsem);
+
+ if (!pdev->tsm)
+ return -EINVAL;
+
+ if (!is_link_tsm(pdev->tsm->tsm_dev))
+ return -ENXIO;
+
+ tsm_pf0 = to_pci_tsm_pf0(pdev->tsm);
+ guard(mutex)(&tsm_pf0->lock);
+
+ /* Resolve races to bind a TDI */
+ if (pdev->tsm->tdi) {
+ if (pdev->tsm->tdi->kvm != kvm)
+ return -EBUSY;
+ return 0;
+ }
+
+ tdi = to_pci_tsm_ops(pdev->tsm)->bind(pdev, kvm, tdi_id);
+ if (IS_ERR(tdi))
+ return PTR_ERR(tdi);
+
+ pdev->tsm->tdi = tdi;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pci_tsm_bind);
+
+static void pci_tsm_unbind_all(struct pci_dev *pdev)
+{
+ pci_tsm_walk_fns_reverse(pdev, __pci_tsm_unbind, NULL);
+ __pci_tsm_unbind(pdev, NULL);
+}
+
static void __pci_tsm_disconnect(struct pci_dev *pdev)
{
struct pci_tsm_pf0 *tsm_pf0 = to_pci_tsm_pf0(pdev->tsm);
@@ -278,6 +367,8 @@ static void __pci_tsm_disconnect(struct pci_dev *pdev)
/* disconnect() mutually exclusive with subfunction pci_tsm_init() */
lockdep_assert_held_write(&pci_tsm_rwsem);
+ pci_tsm_unbind_all(pdev);
+
/*
* disconnect() is uninterruptible as it may be called for device
* teardown
@@ -440,6 +531,22 @@ static struct pci_dev *find_dsm_dev(struct pci_dev *pdev)
}
/**
+ * pci_tsm_tdi_constructor() - base 'struct pci_tdi' initialization for link TSMs
+ * @pdev: PCI device function representing the TDI
+ * @tdi: context to initialize
+ * @kvm: Private memory attach context
+ * @tdi_id: Identifier (virtual BDF) for the TDI as referenced by the TSM and DSM
+ */
+void pci_tsm_tdi_constructor(struct pci_dev *pdev, struct pci_tdi *tdi,
+ struct kvm *kvm, u32 tdi_id)
+{
+ tdi->pdev = pdev;
+ tdi->kvm = kvm;
+ tdi->tdi_id = tdi_id;
+}
+EXPORT_SYMBOL_GPL(pci_tsm_tdi_constructor);
+
+/**
* pci_tsm_link_constructor() - base 'struct pci_tsm' initialization for link TSMs
* @pdev: The PCI device
* @tsm: context to initialize
@@ -532,7 +639,7 @@ int pci_tsm_register(struct tsm_dev *tsm_dev)
static void pci_tsm_fn_exit(struct pci_dev *pdev)
{
- /* TODO: unbind the fn */
+ __pci_tsm_unbind(pdev, NULL);
tsm_remove(pdev->tsm);
}
diff --git a/include/linux/pci-tsm.h b/include/linux/pci-tsm.h
index d7b078d5e272..a5e297677917 100644
--- a/include/linux/pci-tsm.h
+++ b/include/linux/pci-tsm.h
@@ -6,6 +6,8 @@
struct pci_tsm;
struct tsm_dev;
+struct kvm;
+enum pci_tsm_req_scope;
/*
* struct pci_tsm_ops - manage confidential links and security state
@@ -29,12 +31,16 @@ struct pci_tsm_ops {
* @connect: establish / validate a secure connection (e.g. IDE)
* with the device
* @disconnect: teardown the secure link
+ * @bind: bind a TDI in preparation for it to be accepted by a TVM
+ * @unbind: remove a TDI from secure operation with a TVM
*
* Context: @probe, @remove, @connect, and @disconnect run under
* pci_tsm_rwsem held for write to sync with TSM unregistration and
* mutual exclusion of @connect and @disconnect. @connect and
* @disconnect additionally run under the DSM lock (struct
* pci_tsm_pf0::lock) as well as @probe and @remove of the subfunctions.
+ * @bind and @unbind run under pci_tsm_rwsem held for read and the DSM
+ * lock.
*/
struct_group_tagged(pci_tsm_link_ops, link_ops,
struct pci_tsm *(*probe)(struct tsm_dev *tsm_dev,
@@ -42,6 +48,9 @@ struct pci_tsm_ops {
void (*remove)(struct pci_tsm *tsm);
int (*connect)(struct pci_dev *pdev);
void (*disconnect)(struct pci_dev *pdev);
+ struct pci_tdi *(*bind)(struct pci_dev *pdev,
+ struct kvm *kvm, u32 tdi_id);
+ void (*unbind)(struct pci_tdi *tdi);
);
/*
@@ -62,11 +71,24 @@ struct pci_tsm_ops {
};
/**
+ * struct pci_tdi - Core TEE I/O Device Interface (TDI) context
+ * @pdev: host side representation of guest-side TDI
+ * @kvm: TEE VM context of bound TDI
+ * @tdi_id: Identifier (virtual BDF) for the TDI as referenced by the TSM and DSM
+ */
+struct pci_tdi {
+ struct pci_dev *pdev;
+ struct kvm *kvm;
+ u32 tdi_id;
+};
+
+/**
* struct pci_tsm - Core TSM context for a given PCIe endpoint
* @pdev: Back ref to device function, distinguishes type of pci_tsm context
* @dsm_dev: PCI Device Security Manager for link operations on @pdev
* @tsm_dev: PCI TEE Security Manager device for Link Confidentiality or Device
* Function Security operations
+ * @tdi: TDI context established by the @bind link operation
*
* This structure is wrapped by low level TSM driver data and returned by
* probe()/lock(), it is freed by the corresponding remove()/unlock().
@@ -82,6 +104,7 @@ struct pci_tsm {
struct pci_dev *pdev;
struct pci_dev *dsm_dev;
struct tsm_dev *tsm_dev;
+ struct pci_tdi *tdi;
};
/**
@@ -139,6 +162,10 @@ int pci_tsm_pf0_constructor(struct pci_dev *pdev, struct pci_tsm_pf0 *tsm,
void pci_tsm_pf0_destructor(struct pci_tsm_pf0 *tsm);
int pci_tsm_doe_transfer(struct pci_dev *pdev, u8 type, const void *req,
size_t req_sz, void *resp, size_t resp_sz);
+int pci_tsm_bind(struct pci_dev *pdev, struct kvm *kvm, u32 tdi_id);
+void pci_tsm_unbind(struct pci_dev *pdev);
+void pci_tsm_tdi_constructor(struct pci_dev *pdev, struct pci_tdi *tdi,
+ struct kvm *kvm, u32 tdi_id);
#else
static inline int pci_tsm_register(struct tsm_dev *tsm_dev)
{
@@ -147,5 +174,12 @@ static inline int pci_tsm_register(struct tsm_dev *tsm_dev)
static inline void pci_tsm_unregister(struct tsm_dev *tsm_dev)
{
}
+static inline int pci_tsm_bind(struct pci_dev *pdev, struct kvm *kvm, u64 tdi_id)
+{
+ return -ENXIO;
+}
+static inline void pci_tsm_unbind(struct pci_dev *pdev)
+{
+}
#endif
#endif /*__PCI_TSM_H */