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


Reply via email to