Implement read/write virtualization for PCIe TPH Extended Capability: 1. Read path: Mask out extended requester bit if hardware does not support extended ST, to hide unavailable capability from userspace. 2. Write path: Validate TPH mode and requester configuration, mediate TPH enable/disable requests. Sync shadow ST table to hardware after successful TPH enabling. Clear shadow table and reset TPH status on user session transition, to prevent cross-session state leakage. Gate the enable/disable TPH feature by enable_unsafe_tph module parameter. Signed-off-by: Chengwen Feng --- drivers/vfio/pci/vfio_pci_config.c | 78 ++++++++++++++++++++++++++++++ drivers/vfio/pci/vfio_pci_core.c | 8 ++- 2 files changed, 85 insertions(+), 1 deletion(-) diff --git a/drivers/vfio/pci/vfio_pci_config.c b/drivers/vfio/pci/vfio_pci_config.c index a10ed733f0e3..bed3794e6033 100644 --- a/drivers/vfio/pci/vfio_pci_config.c +++ b/drivers/vfio/pci/vfio_pci_config.c @@ -20,8 +20,10 @@ * must be negotiated with the underlying OS. */ +#include #include #include +#include #include #include #include @@ -35,6 +37,8 @@ ((offset >= PCI_BASE_ADDRESS_0 && offset < PCI_BASE_ADDRESS_5 + 4) || \ (offset >= PCI_ROM_ADDRESS && offset < PCI_ROM_ADDRESS + 4)) +extern bool enable_unsafe_tph; + /* * Lengths of PCI Config Capabilities * 0: Removed from the user visible capability list @@ -313,6 +317,78 @@ static int vfio_virt_config_read(struct vfio_pci_core_device *vdev, int pos, return count; } +static int vfio_pci_tph_config_read(struct vfio_pci_core_device *vdev, int pos, + int count, struct perm_bits *perm, + int offset, __le32 *val) +{ + const int target_byte = PCI_TPH_CAP + 1; + struct pci_dev *pdev = vdev->pdev; + const u8 ext_bit = BIT(0); + u8 *data = (u8 *)val; + int byte_off; + int ret; + + if (pdev->tph_ext_requester || offset > target_byte || + offset + count <= target_byte) + return vfio_direct_config_read(vdev, pos, count, perm, + offset, val); + + ret = vfio_direct_config_read(vdev, pos, count, perm, offset, val); + + /* Mask out TPH requester capability */ + byte_off = target_byte - offset; + /* Defensive check: prevent out-of-bounds access */ + if (byte_off < count) + data[byte_off] &= ~ext_bit; + + return ret; +} + +static int vfio_pci_tph_config_write(struct vfio_pci_core_device *vdev, int pos, + int count, struct perm_bits *perm, + int offset, __le32 val) +{ + struct pci_dev *pdev = vdev->pdev; + u32 data = le32_to_cpu(val); + enum tph_req_policy policy; + u8 mode, req; + int i, ret; + + if (!enable_unsafe_tph) + return count; + + /* + * TPH_CTRL (offset 4) register layout: + * Byte 0 (offset 4): Bits [2:0] - ST Mode selection + * Byte 1 (offset 5): Bits [1:0] - TPH Requester enable + * + * Only intercept writes with count >= 2 which cover both control + * fields. Single-byte partial writes do not affect combined + * configuration. + */ + if (offset != PCI_TPH_CTRL || count < 2) + return count; + + guard(mutex)(&vdev->tph_lock); + + mode = FIELD_GET(PCI_TPH_CTRL_MODE_SEL_MASK, data); + req = FIELD_GET(PCI_TPH_CTRL_REQ_EN_MASK, data); + if (req == PCI_TPH_REQ_TPH_ONLY || req == PCI_TPH_REQ_EXT_TPH) { + policy = (req == PCI_TPH_REQ_TPH_ONLY) ? + TPH_REQ_STANDARD : TPH_REQ_EXTENDED; + ret = pcie_enable_tph(pdev, mode, policy); + if (ret == 0 && vdev->tph_st_shadow) { + for (i = 0; i < vdev->tph_st_entries; i++) + pcie_tph_set_st_entry(pdev, i, + vdev->tph_st_shadow[i]); + } + } else if (req == PCI_TPH_REQ_DISABLE) { + pcie_disable_tph(vdev->pdev); + } + + return count; +} + static struct perm_bits direct_ro_perms = { .readfn = vfio_direct_config_read, }; @@ -1121,6 +1197,8 @@ int __init vfio_pci_init_perm_bits(void) ret |= init_pci_ext_cap_err_perm(&ecap_perms[PCI_EXT_CAP_ID_ERR]); ret |= init_pci_ext_cap_pwr_perm(&ecap_perms[PCI_EXT_CAP_ID_PWR]); ecap_perms[PCI_EXT_CAP_ID_VNDR].writefn = vfio_raw_config_write; + ecap_perms[PCI_EXT_CAP_ID_TPH].readfn = vfio_pci_tph_config_read; + ecap_perms[PCI_EXT_CAP_ID_TPH].writefn = vfio_pci_tph_config_write; ecap_perms[PCI_EXT_CAP_ID_DVSEC].writefn = vfio_raw_config_write; if (ret) diff --git a/drivers/vfio/pci/vfio_pci_core.c b/drivers/vfio/pci/vfio_pci_core.c index 851e4d0828f2..2a5cb945275e 100644 --- a/drivers/vfio/pci/vfio_pci_core.c +++ b/drivers/vfio/pci/vfio_pci_core.c @@ -42,7 +42,7 @@ static bool nointxmask; static bool disable_vga; static bool disable_idle_d3; -static bool enable_unsafe_tph; +bool enable_unsafe_tph; static void vfio_pci_eventfd_rcu_free(struct rcu_head *rcu) { @@ -1595,6 +1595,9 @@ static int vfio_pci_tph_init(struct vfio_pci_core_device *vdev) mutex_init(&vdev->tph_lock); + /* Reset TPH status on new user session */ + pcie_disable_tph(vdev->pdev); + return 0; } @@ -1605,6 +1608,9 @@ static void vfio_pci_tph_deinit(struct vfio_pci_core_device *vdev) vdev->tph_st_entries = 0; mutex_destroy(&vdev->tph_lock); + + /* Reset TPH status on session exit */ + pcie_disable_tph(vdev->pdev); } static int vfio_pci_core_feature_tph_st_config( -- 2.17.1