Skip to content

Commit baf8feb

Browse files
committed
PCI/CMA: Grant guests exclusive control of authentication
At any given time, only a single entity in a physical system may have an SPDM connection to a device. That's because the GET_VERSION request (which begins an authentication sequence) resets "the connection and all context associated with that connection" (SPDM 1.3.0 margin no 158). Thus, when a device is passed through to a guest and the guest has authenticated it, a subsequent authentication by the host would reset the device's CMA-SPDM session behind the guest's back. Prevent by letting the guest claim exclusive CMA ownership of the device during passthrough. Refuse CMA reauthentication on the host as long. After passthrough has concluded, reauthenticate the device on the host. Store the flag indicating guest ownership in struct pci_dev's priv_flags to avoid the concurrency issues observed by commit 44bda4b ("PCI: Fix is_added/is_busmaster race condition"). Side note: The Data Object Exchange r1.1 ECN (published Oct 11 2022) retrofits DOE with Connection IDs. In theory these allow simultaneous CMA-SPDM connections by multiple entities to the same device. But the first hardware generation capable of CMA-SPDM only supports DOE r1.0. The specification also neglects to reserve unique Connection IDs for hosts and guests, which further limits its usefulness. In general, forcing the transport to compensate for SPDM's lack of a connection identifier feels like a questionable layering violation. Signed-off-by: Lukas Wunner <[email protected]> Cc: Alex Williamson <[email protected]>
1 parent ccba26c commit baf8feb

File tree

6 files changed

+70
-2
lines changed

6 files changed

+70
-2
lines changed

drivers/pci/cma.c

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,9 +217,50 @@ void pci_cma_reauthenticate(struct pci_dev *pdev)
217217
if (IS_ERR_OR_NULL(pdev->spdm_state))
218218
return;
219219

220+
if (test_bit(PCI_CMA_OWNED_BY_GUEST, &pdev->priv_flags))
221+
return;
222+
220223
spdm_authenticate(pdev->spdm_state);
221224
}
222225

226+
#if IS_ENABLED(CONFIG_VFIO_PCI_CORE)
227+
/**
228+
* pci_cma_claim_ownership() - Claim exclusive CMA-SPDM control for guest VM
229+
* @pdev: PCI device
230+
*
231+
* Claim exclusive CMA-SPDM control for a guest virtual machine before
232+
* passthrough of @pdev. The host refrains from performing CMA-SPDM
233+
* authentication of the device until passthrough has concluded.
234+
*
235+
* Necessary because the GET_VERSION request resets the SPDM connection
236+
* and DOE r1.0 allows only a single SPDM connection for the entire system.
237+
* So the host could reset the guest's SPDM connection behind the guest's back.
238+
*/
239+
void pci_cma_claim_ownership(struct pci_dev *pdev)
240+
{
241+
set_bit(PCI_CMA_OWNED_BY_GUEST, &pdev->priv_flags);
242+
243+
if (!IS_ERR_OR_NULL(pdev->spdm_state))
244+
spdm_await(pdev->spdm_state);
245+
}
246+
EXPORT_SYMBOL(pci_cma_claim_ownership);
247+
248+
/**
249+
* pci_cma_return_ownership() - Relinquish CMA-SPDM control to the host
250+
* @pdev: PCI device
251+
*
252+
* Relinquish CMA-SPDM control to the host after passthrough of @pdev to a
253+
* guest virtual machine has concluded.
254+
*/
255+
void pci_cma_return_ownership(struct pci_dev *pdev)
256+
{
257+
clear_bit(PCI_CMA_OWNED_BY_GUEST, &pdev->priv_flags);
258+
259+
pci_cma_reauthenticate(pdev);
260+
}
261+
EXPORT_SYMBOL(pci_cma_return_ownership);
262+
#endif
263+
223264
void pci_cma_destroy(struct pci_dev *pdev)
224265
{
225266
if (IS_ERR_OR_NULL(pdev->spdm_state))

drivers/pci/pci.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -519,6 +519,7 @@ static inline int pci_dev_set_disconnected(struct pci_dev *dev, void *unused)
519519
#define PCI_DPC_RECOVERED 1
520520
#define PCI_DPC_RECOVERING 2
521521
#define PCI_DEV_REMOVED 3
522+
#define PCI_CMA_OWNED_BY_GUEST 4
522523

523524
static inline void pci_dev_assign_added(struct pci_dev *dev)
524525
{

drivers/vfio/pci/vfio_pci_core.c

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -483,10 +483,12 @@ int vfio_pci_core_enable(struct vfio_pci_core_device *vdev)
483483
if (ret)
484484
goto out_power;
485485

486+
pci_cma_claim_ownership(pdev);
487+
486488
/* If reset fails because of the device lock, fail this path entirely */
487489
ret = pci_try_reset_function(pdev);
488490
if (ret == -EAGAIN)
489-
goto out_disable_device;
491+
goto out_cma_return;
490492

491493
vdev->reset_works = !ret;
492494
pci_save_state(pdev);
@@ -545,7 +547,8 @@ int vfio_pci_core_enable(struct vfio_pci_core_device *vdev)
545547
out_free_state:
546548
kfree(vdev->pci_saved_state);
547549
vdev->pci_saved_state = NULL;
548-
out_disable_device:
550+
out_cma_return:
551+
pci_cma_return_ownership(pdev);
549552
pci_disable_device(pdev);
550553
out_power:
551554
if (!disable_idle_d3)
@@ -674,6 +677,8 @@ void vfio_pci_core_disable(struct vfio_pci_core_device *vdev)
674677

675678
vfio_pci_dev_set_try_reset(vdev->vdev.dev_set);
676679

680+
pci_cma_return_ownership(pdev);
681+
677682
/* Put the pm-runtime usage counter acquired during enable */
678683
if (!disable_idle_d3)
679684
pm_runtime_put(&pdev->dev);

include/linux/pci.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2451,6 +2451,14 @@ static inline resource_size_t pci_iov_resource_size(struct pci_dev *dev, int res
24512451
static inline void pci_vf_drivers_autoprobe(struct pci_dev *dev, bool probe) { }
24522452
#endif
24532453

2454+
#ifdef CONFIG_PCI_CMA
2455+
void pci_cma_claim_ownership(struct pci_dev *pdev);
2456+
void pci_cma_return_ownership(struct pci_dev *pdev);
2457+
#else
2458+
static inline void pci_cma_claim_ownership(struct pci_dev *pdev) { }
2459+
static inline void pci_cma_return_ownership(struct pci_dev *pdev) { }
2460+
#endif
2461+
24542462
#if defined(CONFIG_HOTPLUG_PCI) || defined(CONFIG_HOTPLUG_PCI_MODULE)
24552463
void pci_hp_create_module_link(struct pci_slot *pci_slot);
24562464
void pci_hp_remove_module_link(struct pci_slot *pci_slot);

include/linux/spdm.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ struct spdm_state *spdm_create(struct device *dev, spdm_transport *transport,
3232

3333
int spdm_authenticate(struct spdm_state *spdm_state);
3434

35+
void spdm_await(struct spdm_state *spdm_state);
36+
3537
void spdm_destroy(struct spdm_state *spdm_state);
3638

3739
#ifdef CONFIG_SYSFS

lib/spdm/core.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,17 @@ struct spdm_state *spdm_create(struct device *dev, spdm_transport *transport,
414414
}
415415
EXPORT_SYMBOL_GPL(spdm_create);
416416

417+
/**
418+
* spdm_await() - Wait for ongoing spdm_authenticate() to finish
419+
*
420+
* @spdm_state: SPDM session state
421+
*/
422+
void spdm_await(struct spdm_state *spdm_state)
423+
{
424+
mutex_lock(&spdm_state->lock);
425+
mutex_unlock(&spdm_state->lock);
426+
}
427+
417428
/**
418429
* spdm_destroy() - Destroy SPDM session
419430
*

0 commit comments

Comments
 (0)