]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
vfio/pci: Expose setup ROM at ROM bar when needed
authorYunxiang Li <Yunxiang.Li@amd.com>
Thu, 2 Jan 2025 18:50:13 +0000 (13:50 -0500)
committerAlex Williamson <alex.williamson@redhat.com>
Mon, 6 Jan 2025 15:09:54 +0000 (08:09 -0700)
If ROM bar is missing for any reason, we can fallback to using pdev->rom
to expose the ROM content to the guest. This fixes some passthrough use
cases where the upstream bridge does not have enough address window.

Signed-off-by: Yunxiang Li <Yunxiang.Li@amd.com>
Link: https://lore.kernel.org/r/20250102185013.15082-3-Yunxiang.Li@amd.com
Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
drivers/vfio/pci/vfio_pci_config.c
drivers/vfio/pci/vfio_pci_core.c
drivers/vfio/pci/vfio_pci_rdwr.c

index e41c3a965663e2bfb83d69fad2583a4a5cf906f5..4c673d842fb3552fed890ee467ee520e68d84828 100644 (file)
@@ -511,6 +511,10 @@ static void vfio_bar_fixup(struct vfio_pci_core_device *vdev)
                mask = ~(pci_resource_len(pdev, PCI_ROM_RESOURCE) - 1);
                mask |= PCI_ROM_ADDRESS_ENABLE;
                *vbar &= cpu_to_le32((u32)mask);
+       } else if (pdev->rom && pdev->romlen) {
+               mask = ~(roundup_pow_of_two(pdev->romlen) - 1);
+               mask |= PCI_ROM_ADDRESS_ENABLE;
+               *vbar &= cpu_to_le32((u32)mask);
        } else {
                *vbar = 0;
        }
index b97a3833ffd1462b99b309b7b8e3c36132b12114..586e49efb81be32ccb50ca554a60cec684c37402 100644 (file)
@@ -1054,25 +1054,27 @@ static int vfio_pci_ioctl_get_region_info(struct vfio_pci_core_device *vdev,
 
                info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index);
                info.flags = 0;
+               info.size = 0;
 
-               /* Report the BAR size, not the ROM size */
-               info.size = pci_resource_len(pdev, info.index);
-               if (!info.size)
-                       break;
-
-               /*
-                * Is it really there?  Enable memory decode for implicit access
-                * in pci_map_rom().
-                */
-               cmd = vfio_pci_memory_lock_and_enable(vdev);
-               io = pci_map_rom(pdev, &size);
-               if (io) {
+               if (pci_resource_start(pdev, PCI_ROM_RESOURCE)) {
+                       /*
+                        * Check ROM content is valid. Need to enable memory
+                        * decode for ROM access in pci_map_rom().
+                        */
+                       cmd = vfio_pci_memory_lock_and_enable(vdev);
+                       io = pci_map_rom(pdev, &size);
+                       if (io) {
+                               info.flags = VFIO_REGION_INFO_FLAG_READ;
+                               /* Report the BAR size, not the ROM size. */
+                               info.size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
+                               pci_unmap_rom(pdev, io);
+                       }
+                       vfio_pci_memory_unlock_and_restore(vdev, cmd);
+               } else if (pdev->rom && pdev->romlen) {
                        info.flags = VFIO_REGION_INFO_FLAG_READ;
-                       pci_unmap_rom(pdev, io);
-               } else {
-                       info.size = 0;
+                       /* Report BAR size as power of two. */
+                       info.size = roundup_pow_of_two(pdev->romlen);
                }
-               vfio_pci_memory_unlock_and_restore(vdev, cmd);
 
                break;
        }
index 0f74f58571fb121d85a5b6a14a6e1af8aa2b1caa..6192788c8ba39fe3f7d3ab7f35ac253ad3d33a42 100644 (file)
@@ -237,6 +237,8 @@ ssize_t vfio_pci_bar_rw(struct vfio_pci_core_device *vdev, char __user *buf,
 
        if (pci_resource_start(pdev, bar))
                end = pci_resource_len(pdev, bar);
+       else if (bar == PCI_ROM_RESOURCE && pdev->rom && pdev->romlen)
+               end = roundup_pow_of_two(pdev->romlen);
        else
                return -EINVAL;
 
@@ -251,11 +253,14 @@ ssize_t vfio_pci_bar_rw(struct vfio_pci_core_device *vdev, char __user *buf,
                 * excluded range at the end of the actual ROM.  This makes
                 * filling large ROM BARs much faster.
                 */
-               io = pci_map_rom(pdev, &x_start);
-               if (!io) {
-                       done = -ENOMEM;
-                       goto out;
+               if (pci_resource_start(pdev, bar)) {
+                       io = pci_map_rom(pdev, &x_start);
+               } else {
+                       io = ioremap(pdev->rom, pdev->romlen);
+                       x_start = pdev->romlen;
                }
+               if (!io)
+                       return -ENOMEM;
                x_end = end;
        } else {
                int ret = vfio_pci_core_setup_barmap(vdev, bar);
@@ -278,8 +283,13 @@ ssize_t vfio_pci_bar_rw(struct vfio_pci_core_device *vdev, char __user *buf,
        if (done >= 0)
                *ppos += done;
 
-       if (bar == PCI_ROM_RESOURCE)
-               pci_unmap_rom(pdev, io);
+       if (bar == PCI_ROM_RESOURCE) {
+               if (pci_resource_start(pdev, bar))
+                       pci_unmap_rom(pdev, io);
+               else
+                       iounmap(io);
+       }
+
 out:
        return done;
 }