]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
vfio/pci: Disable qword access to the PCI ROM bar
authorKevin Tian <kevin.tian@intel.com>
Tue, 6 Jan 2026 02:44:28 +0000 (02:44 +0000)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 8 Jan 2026 09:15:05 +0000 (10:15 +0100)
[ Upstream commit dc85a46928c41423ad89869baf05a589e2975575 ]

Commit 2b938e3db335 ("vfio/pci: Enable iowrite64 and ioread64 for vfio
pci") enables qword access to the PCI bar resources. However certain
devices (e.g. Intel X710) are observed with problem upon qword accesses
to the rom bar, e.g. triggering PCI aer errors.

This is triggered by Qemu which caches the rom content by simply does a
pread() of the remaining size until it gets the full contents. The other
bars would only perform operations at the same access width as their
guest drivers.

Instead of trying to identify all broken devices, universally disable
qword access to the rom bar i.e. going back to the old way which worked
reliably for years.

Reported-by: Farrah Chen <farrah.chen@intel.com>
Closes: https://bugzilla.kernel.org/show_bug.cgi?id=220740
Fixes: 2b938e3db335 ("vfio/pci: Enable iowrite64 and ioread64 for vfio pci")
Cc: stable@vger.kernel.org
Signed-off-by: Kevin Tian <kevin.tian@intel.com>
Tested-by: Farrah Chen <farrah.chen@intel.com>
Link: https://lore.kernel.org/r/20251218081650.555015-2-kevin.tian@intel.com
Signed-off-by: Alex Williamson <alex@shazbot.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/vfio/pci/nvgrace-gpu/main.c
drivers/vfio/pci/vfio_pci_rdwr.c
include/linux/vfio_pci_core.h

index 9e1c57baab64a2989630fbfe6a7f3c49af1616a7..9728840cbcc5b1af794c568472449e24251bcdc4 100644 (file)
@@ -482,7 +482,7 @@ nvgrace_gpu_map_and_read(struct nvgrace_gpu_pci_core_device *nvdev,
                ret = vfio_pci_core_do_io_rw(&nvdev->core_device, false,
                                             nvdev->resmem.ioaddr,
                                             buf, offset, mem_count,
-                                            0, 0, false);
+                                            0, 0, false, VFIO_PCI_IO_WIDTH_8);
        }
 
        return ret;
@@ -600,7 +600,7 @@ nvgrace_gpu_map_and_write(struct nvgrace_gpu_pci_core_device *nvdev,
                ret = vfio_pci_core_do_io_rw(&nvdev->core_device, false,
                                             nvdev->resmem.ioaddr,
                                             (char __user *)buf, pos, mem_count,
-                                            0, 0, true);
+                                            0, 0, true, VFIO_PCI_IO_WIDTH_8);
        }
 
        return ret;
index a0595c745732a351243440e7f7841046909059e6..03a84663db9159920160a25589266ed401cd3b48 100644 (file)
@@ -141,7 +141,8 @@ VFIO_IORDWR(64)
 ssize_t vfio_pci_core_do_io_rw(struct vfio_pci_core_device *vdev, bool test_mem,
                               void __iomem *io, char __user *buf,
                               loff_t off, size_t count, size_t x_start,
-                              size_t x_end, bool iswrite)
+                              size_t x_end, bool iswrite,
+                              enum vfio_pci_io_width max_width)
 {
        ssize_t done = 0;
        int ret;
@@ -157,7 +158,7 @@ ssize_t vfio_pci_core_do_io_rw(struct vfio_pci_core_device *vdev, bool test_mem,
                        fillable = 0;
 
 #if defined(ioread64) && defined(iowrite64)
-               if (fillable >= 8 && !(off % 8)) {
+               if (fillable >= 8 && !(off % 8) && max_width >= 8) {
                        ret = vfio_pci_iordwr64(vdev, iswrite, test_mem,
                                                io, buf, off, &filled);
                        if (ret)
@@ -165,13 +166,13 @@ ssize_t vfio_pci_core_do_io_rw(struct vfio_pci_core_device *vdev, bool test_mem,
 
                } else
 #endif
-               if (fillable >= 4 && !(off % 4)) {
+               if (fillable >= 4 && !(off % 4) && max_width >= 4) {
                        ret = vfio_pci_iordwr32(vdev, iswrite, test_mem,
                                                io, buf, off, &filled);
                        if (ret)
                                return ret;
 
-               } else if (fillable >= 2 && !(off % 2)) {
+               } else if (fillable >= 2 && !(off % 2) && max_width >= 2) {
                        ret = vfio_pci_iordwr16(vdev, iswrite, test_mem,
                                                io, buf, off, &filled);
                        if (ret)
@@ -242,6 +243,7 @@ ssize_t vfio_pci_bar_rw(struct vfio_pci_core_device *vdev, char __user *buf,
        void __iomem *io;
        struct resource *res = &vdev->pdev->resource[bar];
        ssize_t done;
+       enum vfio_pci_io_width max_width = VFIO_PCI_IO_WIDTH_8;
 
        if (pci_resource_start(pdev, bar))
                end = pci_resource_len(pdev, bar);
@@ -268,6 +270,16 @@ ssize_t vfio_pci_bar_rw(struct vfio_pci_core_device *vdev, char __user *buf,
                        goto out;
                }
                x_end = end;
+
+               /*
+                * Certain devices (e.g. Intel X710) don't support qword
+                * access to the ROM bar. Otherwise PCI AER errors might be
+                * triggered.
+                *
+                * Disable qword access to the ROM bar universally, which
+                * worked reliably for years before qword access is enabled.
+                */
+               max_width = VFIO_PCI_IO_WIDTH_4;
        } else {
                int ret = vfio_pci_core_setup_barmap(vdev, bar);
                if (ret) {
@@ -284,7 +296,7 @@ ssize_t vfio_pci_bar_rw(struct vfio_pci_core_device *vdev, char __user *buf,
        }
 
        done = vfio_pci_core_do_io_rw(vdev, res->flags & IORESOURCE_MEM, io, buf, pos,
-                                     count, x_start, x_end, iswrite);
+                                     count, x_start, x_end, iswrite, max_width);
 
        if (done >= 0)
                *ppos += done;
@@ -353,7 +365,7 @@ ssize_t vfio_pci_vga_rw(struct vfio_pci_core_device *vdev, char __user *buf,
         * to the memory enable bit in the command register.
         */
        done = vfio_pci_core_do_io_rw(vdev, false, iomem, buf, off, count,
-                                     0, 0, iswrite);
+                                     0, 0, iswrite, VFIO_PCI_IO_WIDTH_8);
 
        vga_put(vdev->pdev, rsrc);
 
index 99da27c032d70f73aaf554f20bc47a133e2df54d..f889be76eaff60d8e6de15fd36012fc16fc70136 100644 (file)
@@ -102,6 +102,13 @@ struct vfio_pci_core_device {
        struct rw_semaphore     memory_lock;
 };
 
+enum vfio_pci_io_width {
+       VFIO_PCI_IO_WIDTH_1 = 1,
+       VFIO_PCI_IO_WIDTH_2 = 2,
+       VFIO_PCI_IO_WIDTH_4 = 4,
+       VFIO_PCI_IO_WIDTH_8 = 8,
+};
+
 /* Will be exported for vfio pci drivers usage */
 int vfio_pci_core_register_dev_region(struct vfio_pci_core_device *vdev,
                                      unsigned int type, unsigned int subtype,
@@ -137,7 +144,8 @@ pci_ers_result_t vfio_pci_core_aer_err_detected(struct pci_dev *pdev,
 ssize_t vfio_pci_core_do_io_rw(struct vfio_pci_core_device *vdev, bool test_mem,
                               void __iomem *io, char __user *buf,
                               loff_t off, size_t count, size_t x_start,
-                              size_t x_end, bool iswrite);
+                              size_t x_end, bool iswrite,
+                              enum vfio_pci_io_width max_width);
 bool vfio_pci_core_range_intersect_range(loff_t buf_start, size_t buf_cnt,
                                         loff_t reg_start, size_t reg_cnt,
                                         loff_t *buf_offset,