From: Manish Honap <[email protected]> The MSI-X table is virtualized in vfio_pci_bar_rw() by an open-coded x_start/x_end window that fills reads with -1 and drops writes. Now that a generic excluded-range list expresses the same fill/drop behavior, register the MSI-X table as a read and write excluded range instead of special-casing it in the read/write path.
Add the range when the MSI-X capability is parsed in vfio_pci_core_enable() and clear the list in vfio_pci_core_disable() alongside the config teardown. The read/write path now relies solely on vfio_pci_bar_find_exclusion(), so MSI-X and a provider's (e.g. vfio-cxl) trapped registers share one mechanism. No behavioral change: an access to the MSI-X table still reads -1 and drops writes. Assisted-by: LLM Signed-off-by: Manish Honap <[email protected]> --- drivers/vfio/pci/vfio_pci_core.c | 19 +++++++++++ drivers/vfio/pci/vfio_pci_rdwr.c | 56 ++++++++++++++++++++++++-------- 2 files changed, 62 insertions(+), 13 deletions(-) diff --git a/drivers/vfio/pci/vfio_pci_core.c b/drivers/vfio/pci/vfio_pci_core.c index 9e4fa5d088a4..c5b7a59a4548 100644 --- a/drivers/vfio/pci/vfio_pci_core.c +++ b/drivers/vfio/pci/vfio_pci_core.c @@ -589,6 +589,8 @@ static const struct dev_pm_ops vfio_pci_core_pm_ops = { NULL) }; +static void vfio_pci_free_excluded_ranges(struct vfio_pci_core_device *vdev); + int vfio_pci_core_enable(struct vfio_pci_core_device *vdev) { struct pci_dev *pdev = vdev->pdev; @@ -655,6 +657,22 @@ int vfio_pci_core_enable(struct vfio_pci_core_device *vdev) vdev->msix_offset = table & PCI_MSIX_TABLE_OFFSET; vdev->msix_size = ((flags & PCI_MSIX_FLAGS_QSIZE) + 1) * 16; vdev->has_dyn_msix = pci_msix_can_alloc_dyn(pdev); + + /* + * Virtualize the MSI-X table through the excluded-range list: + * reads fill -1 and writes are dropped so the guest never + * reaches the hardware table directly. + */ + ret = vfio_pci_core_add_excluded_range(vdev, vdev->msix_bar, + vdev->msix_offset, + vdev->msix_size, + VFIO_PCI_EXCLUDE_READ | + VFIO_PCI_EXCLUDE_WRITE); + if (ret) { + vfio_pci_free_excluded_ranges(vdev); + vfio_config_free(vdev); + goto out_free_zdev; + } } else { vdev->msix_bar = 0xFF; vdev->has_dyn_msix = false; @@ -741,6 +759,7 @@ void vfio_pci_core_disable(struct vfio_pci_core_device *vdev) vdev->region = NULL; /* don't krealloc a freed pointer */ vfio_config_free(vdev); + vfio_pci_free_excluded_ranges(vdev); for (i = 0; i < PCI_STD_NUM_BARS; i++) { bar = i + PCI_STD_RESOURCES; diff --git a/drivers/vfio/pci/vfio_pci_rdwr.c b/drivers/vfio/pci/vfio_pci_rdwr.c index 48da1cb08296..f8e5f94a2e8a 100644 --- a/drivers/vfio/pci/vfio_pci_rdwr.c +++ b/drivers/vfio/pci/vfio_pci_rdwr.c @@ -256,21 +256,51 @@ ssize_t vfio_pci_bar_rw(struct vfio_pci_core_device *vdev, char __user *buf, } } - if (bar == vdev->msix_bar) { - x_start = vdev->msix_offset; - x_end = vdev->msix_offset + vdev->msix_size; - } - /* - * A provider-excluded sub-range is filled with -1 on read and dropped on - * write for the same reason: the guest reaches it only through the trap. - * An access spans at most one exclusion window. + * The MSI-X table and any provider-excluded sub-ranges (such as a CXL + * HDM decoder block) are filled with -1 on read and dropped on write: + * the guest reaches them only through the virtualized path, never the + * hardware directly. A BAR can hold several such windows and a single + * access may span more than one, so walk the access one window at a + * time. The ROM BAR uses the single trailing window set above. */ - vfio_pci_bar_find_exclusion(vdev, bar, pos, count, iswrite, - &x_start, &x_end); - - done = vfio_pci_core_do_io_rw(vdev, res->flags & IORESOURCE_MEM, io, buf, pos, - count, x_start, x_end, iswrite, max_width); + if (bar == PCI_ROM_RESOURCE) { + done = vfio_pci_core_do_io_rw(vdev, res->flags & IORESOURCE_MEM, + io, buf, pos, count, x_start, x_end, + iswrite, max_width); + } else { + done = 0; + while (count) { + size_t chunk; + ssize_t ret; + + x_start = 0; + x_end = 0; + if (vfio_pci_bar_find_exclusion(vdev, bar, pos, count, + iswrite, &x_start, + &x_end)) + chunk = min(count, (size_t)(x_end - pos)); + else + chunk = count; + + ret = vfio_pci_core_do_io_rw(vdev, + res->flags & IORESOURCE_MEM, + io, buf, pos, chunk, + x_start, x_end, iswrite, + max_width); + if (ret < 0) { + if (!done) + done = ret; + break; + } + done += ret; + pos += ret; + buf += ret; + count -= ret; + if ((size_t)ret < chunk) + break; + } + } if (done >= 0) *ppos += done; -- 2.25.1

