* [PATCH v2 2/2] vfio/pci: read ROM via ACPI _ROM when hardware ROM is unreadable
2026-10-21 8:20 [PATCH v2 1/2] vfio/pci: fall back to platform ROM when pci_map_rom() fails Michael S. Tsirkin
@ 2026-10-21 8:20 ` Michael S. Tsirkin
2026-11-10 23:52 ` [PATCH v2 1/2] vfio/pci: fall back to platform ROM when pci_map_rom() fails Alex Williamson
1 sibling, 0 replies; 4+ messages in thread
From: Michael S. Tsirkin @ 2026-10-21 8:20 UTC (permalink / raw)
Cc: Alex Williamson, kvm
Some GPUs (e.g. NVIDIA Quadro P2000 Mobile in an old laptop I found)
have a ROM BAR that exists but is unreadable because the GPU was never
POSTed by the system BIOS. On these systems the VBIOS is available via
the ACPI _ROM method provided by the platform firmware.
Add an ACPI _ROM fallback in the VFIO PCI ROM region handler, tried
after both pci_map_rom() and the boot loader ROM stash (pdev->rom)
fail.
Note: the ACPI spec defines _ROM for display devices and prefers it over
the BAR image when present. This patch does not go as far, I am afraid
to break some setups.
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
---
Notes (changelog):
v1->v2:
probe the _ROM image size once and cache it in the device:
reads no longer rescan the whole ROM per syscall, and the read
bound now follows the probed image size instead of the BAR size
cap the probe at 16 MiB so firmware that keeps returning full
4K chunks cannot loop forever
keep _ROM requests page-aligned, since some firmware only
supports aligned requests
reads past the image now return 0xff like a ROM BAR instead of
depending on firmware end-of-image behaviour
expose the ROM BAR in config space when the ROM is only
available via _ROM, so VMMs that size the BAR through config
space can see the region
drivers/vfio/pci/vfio_pci_config.c | 18 ++-
drivers/vfio/pci/vfio_pci_core.c | 9 ++
drivers/vfio/pci/vfio_pci_priv.h | 9 ++
drivers/vfio/pci/vfio_pci_rdwr.c | 189 ++++++++++++++++++++++++++++-
include/linux/vfio_pci_core.h | 2 +
5 files changed, 219 insertions(+), 8 deletions(-)
diff --git a/drivers/vfio/pci/vfio_pci_config.c b/drivers/vfio/pci/vfio_pci_config.c
index a10ed733f0e3..a775a19bb97b 100644
--- a/drivers/vfio/pci/vfio_pci_config.c
+++ b/drivers/vfio/pci/vfio_pci_config.c
@@ -504,9 +504,11 @@ static void vfio_bar_fixup(struct vfio_pci_core_device *vdev)
vbar = (__le32 *)&vdev->vconfig[PCI_ROM_ADDRESS];
/*
- * NB. REGION_INFO will have reported zero size if we weren't able
- * to read the ROM, but we still return the actual BAR size here if
- * it exists (or the shadow ROM space).
+ * NB. When the ROM is readable, REGION_INFO reports the BAR size;
+ * when it is not, it reports the platform ROM image size rounded
+ * up to a power of two. We still return the actual BAR size here
+ * if it exists (or the shadow ROM space), so the ROM BAR and the
+ * region can differ when the BAR is unreadable.
*/
if (pci_resource_start(pdev, PCI_ROM_RESOURCE)) {
mask = ~(pci_resource_len(pdev, PCI_ROM_RESOURCE) - 1);
@@ -517,7 +519,15 @@ static void vfio_bar_fixup(struct vfio_pci_core_device *vdev)
mask |= PCI_ROM_ADDRESS_ENABLE;
*vbar &= cpu_to_le32((u32)mask);
} else {
- *vbar = 0;
+ size_t rom_size = vfio_pci_rom_acpi_size(vdev);
+
+ if (rom_size) {
+ mask = ~(roundup_pow_of_two(rom_size) - 1);
+ mask |= PCI_ROM_ADDRESS_ENABLE;
+ *vbar &= cpu_to_le32((u32)mask);
+ } else {
+ *vbar = 0;
+ }
}
vdev->bardirty = false;
diff --git a/drivers/vfio/pci/vfio_pci_core.c b/drivers/vfio/pci/vfio_pci_core.c
index 630474d03e3b..2269eee60387 100644
--- a/drivers/vfio/pci/vfio_pci_core.c
+++ b/drivers/vfio/pci/vfio_pci_core.c
@@ -1136,6 +1136,15 @@ int vfio_pci_ioctl_get_region_info(struct vfio_device *core_vdev,
info->size = roundup_pow_of_two(pdev->romlen);
}
+ if (!info->size) {
+ size_t acpi_size = vfio_pci_rom_acpi_size(vdev);
+
+ if (acpi_size) {
+ info->flags = VFIO_REGION_INFO_FLAG_READ;
+ info->size = roundup_pow_of_two(acpi_size);
+ }
+ }
+
break;
}
case VFIO_PCI_VGA_REGION_INDEX:
diff --git a/drivers/vfio/pci/vfio_pci_priv.h b/drivers/vfio/pci/vfio_pci_priv.h
index fca9d0dfac90..8759745661b7 100644
--- a/drivers/vfio/pci/vfio_pci_priv.h
+++ b/drivers/vfio/pci/vfio_pci_priv.h
@@ -44,6 +44,15 @@ ssize_t vfio_pci_config_rw_single(struct vfio_pci_core_device *vdev,
ssize_t vfio_pci_bar_rw(struct vfio_pci_core_device *vdev, char __user *buf,
size_t count, loff_t *ppos, bool iswrite);
+#ifdef CONFIG_ACPI
+size_t vfio_pci_rom_acpi_size(struct vfio_pci_core_device *vdev);
+#else
+static inline size_t vfio_pci_rom_acpi_size(struct vfio_pci_core_device *vdev)
+{
+ return 0;
+}
+#endif
+
#ifdef CONFIG_VFIO_PCI_VGA
ssize_t vfio_pci_vga_rw(struct vfio_pci_core_device *vdev, char __user *buf,
size_t count, loff_t *ppos, bool iswrite);
diff --git a/drivers/vfio/pci/vfio_pci_rdwr.c b/drivers/vfio/pci/vfio_pci_rdwr.c
index 6e47d529cbb9..6d653f1c0388 100644
--- a/drivers/vfio/pci/vfio_pci_rdwr.c
+++ b/drivers/vfio/pci/vfio_pci_rdwr.c
@@ -10,6 +10,7 @@
* Author: Tom Lyon, pugs@cisco.com
*/
+#include <linux/acpi.h>
#include <linux/fs.h>
#include <linux/pci.h>
#include <linux/uaccess.h>
@@ -211,6 +212,163 @@ int vfio_pci_core_setup_barmap(struct vfio_pci_core_device *vdev, int bar)
}
EXPORT_SYMBOL_GPL(vfio_pci_core_setup_barmap);
+#ifdef CONFIG_ACPI
+
+/* No known option ROM comes close; caps the _ROM size probe. */
+#define VFIO_PCI_ROM_MAX_SIZE (16 * 1024 * 1024)
+
+static acpi_handle vfio_pci_acpi_rom_handle(struct pci_dev *pdev)
+{
+ acpi_handle handle, rom_handle;
+
+ handle = ACPI_HANDLE(&pdev->dev);
+ if (!handle)
+ return NULL;
+
+ if (ACPI_FAILURE(acpi_get_handle(handle, "_ROM", &rom_handle)))
+ return NULL;
+
+ return rom_handle;
+}
+
+static int vfio_pci_acpi_rom_read(acpi_handle rom_handle, void *buf,
+ loff_t offset, size_t length)
+{
+ acpi_status status;
+ union acpi_object rom_arg[2];
+ struct acpi_object_list rom_args;
+ struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+ union acpi_object *obj;
+
+ rom_args.count = 2;
+ rom_args.pointer = rom_arg;
+ rom_arg[0].type = ACPI_TYPE_INTEGER;
+ rom_arg[0].integer.value = offset;
+ rom_arg[1].type = ACPI_TYPE_INTEGER;
+ rom_arg[1].integer.value = length;
+
+ status = acpi_evaluate_object(rom_handle, NULL, &rom_args, &buffer);
+ if (ACPI_FAILURE(status))
+ return -EIO;
+
+ obj = buffer.pointer;
+ if (obj->type != ACPI_TYPE_BUFFER) {
+ kfree(buffer.pointer);
+ return -EIO;
+ }
+
+ length = min(length, (size_t)obj->buffer.length);
+ memcpy(buf, obj->buffer.pointer, length);
+ kfree(buffer.pointer);
+ return length;
+}
+
+/*
+ * _ROM returns at most 4K per call; read whole pages until a short
+ * chunk marks the end of the image.
+ */
+static size_t vfio_pci_acpi_rom_probe(struct vfio_pci_core_device *vdev)
+{
+ acpi_handle rom_handle = vfio_pci_acpi_rom_handle(vdev->pdev);
+ void *buf;
+ size_t size = 0;
+ int ret;
+
+ if (!rom_handle)
+ return 0;
+
+ buf = kmalloc(0x1000, GFP_KERNEL);
+ if (!buf)
+ return 0;
+
+ do {
+ ret = vfio_pci_acpi_rom_read(rom_handle, buf, size, 0x1000);
+ if (ret > 0)
+ size += ret;
+ } while (ret == 0x1000 && size < VFIO_PCI_ROM_MAX_SIZE);
+
+ kfree(buf);
+ return size;
+}
+
+/*
+ * The image behind _ROM is static firmware data, so probe it once and
+ * cache the result. Racing callers may probe concurrently, but they
+ * store the same value.
+ */
+size_t vfio_pci_rom_acpi_size(struct vfio_pci_core_device *vdev)
+{
+ /* Read the flag before the size: pairs with the store below. */
+ if (!smp_load_acquire(&vdev->rom_acpi_probed)) {
+ vdev->rom_acpi_size = vfio_pci_acpi_rom_probe(vdev);
+ /* Store the size before the flag: pairs with the load above. */
+ smp_store_release(&vdev->rom_acpi_probed, true);
+ }
+
+ return vdev->rom_acpi_size;
+}
+
+/*
+ * Serve the ROM region from _ROM one page at a time: the method
+ * returns at most 4K, and some implementations only accept
+ * page-aligned requests. Reads past the image return 0xff, like the
+ * unused space of a ROM BAR. The caller has probed the image and
+ * cached its size in vdev.
+ */
+static ssize_t vfio_pci_acpi_rom_rw(struct vfio_pci_core_device *vdev,
+ char __user *buf, size_t count,
+ loff_t pos, bool iswrite)
+{
+ acpi_handle rom_handle = vfio_pci_acpi_rom_handle(vdev->pdev);
+ size_t rom_size = vdev->rom_acpi_size;
+ void *chunk;
+ ssize_t done = 0;
+ int ret;
+
+ if (iswrite)
+ return -EINVAL;
+
+ if (!rom_handle || !rom_size)
+ return -ENODEV;
+
+ chunk = kmalloc(0x1000, GFP_KERNEL);
+ if (!chunk)
+ return -ENOMEM;
+
+ while (count) {
+ size_t off = pos & (0x1000 - 1);
+ size_t n = min_t(size_t, count, 0x1000 - off);
+ size_t image = 0;
+
+ if (pos - off < rom_size) {
+ ret = vfio_pci_acpi_rom_read(rom_handle, chunk,
+ pos - off, 0x1000);
+ if (ret <= 0) {
+ if (ret < 0 && !done)
+ done = ret;
+ break;
+ }
+ image = min_t(size_t, ret, rom_size - (pos - off));
+ }
+
+ memset(chunk + image, 0xff, 0x1000 - image);
+
+ if (copy_to_user(buf, chunk + off, n)) {
+ done = done ? done : -EFAULT;
+ break;
+ }
+
+ buf += n;
+ pos += n;
+ done += n;
+ count -= n;
+ }
+
+ kfree(chunk);
+ return done;
+}
+#endif /* CONFIG_ACPI */
+
ssize_t vfio_pci_bar_rw(struct vfio_pci_core_device *vdev, char __user *buf,
size_t count, loff_t *ppos, bool iswrite)
{
@@ -221,16 +379,23 @@ ssize_t vfio_pci_bar_rw(struct vfio_pci_core_device *vdev, char __user *buf,
resource_size_t end;
void __iomem *io = NULL;
bool rom_is_mapped = false;
+ size_t rom_size = 0;
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))
+ if (pci_resource_start(pdev, bar)) {
end = pci_resource_len(pdev, bar);
- else if (bar == PCI_ROM_RESOURCE && pdev->rom && pdev->romlen)
+ } else if (bar == PCI_ROM_RESOURCE && pdev->rom && pdev->romlen) {
end = roundup_pow_of_two(pdev->romlen);
- else
+ } else if (bar == PCI_ROM_RESOURCE) {
+ rom_size = vfio_pci_rom_acpi_size(vdev);
+ if (!rom_size)
+ return -EINVAL;
+ end = roundup_pow_of_two(rom_size);
+ } else {
return -EINVAL;
+ }
if (bar == PCI_ROM_RESOURCE) {
/*
@@ -247,7 +412,8 @@ ssize_t vfio_pci_bar_rw(struct vfio_pci_core_device *vdev, char __user *buf,
/*
* The BAR can also exist but be unreadable, e.g. on a GPU
* that was never POSTed. Fall back to the ROM image stashed
- * by the boot loader; the region then follows the image size
+ * by the boot loader (pdev->rom) or provided by the platform
+ * firmware (_ROM); the region then follows the image size
* rather than the BAR, so bound the access only after the
* source of the ROM content is known.
*/
@@ -256,6 +422,12 @@ ssize_t vfio_pci_bar_rw(struct vfio_pci_core_device *vdev, char __user *buf,
io = ioremap(pdev->rom, pdev->romlen);
x_start = pdev->romlen;
}
+
+ if (!io && !(pdev->rom && pdev->romlen)) {
+ rom_size = vfio_pci_rom_acpi_size(vdev);
+ if (rom_size)
+ end = roundup_pow_of_two(rom_size);
+ }
}
if (pos >= end)
@@ -263,6 +435,15 @@ ssize_t vfio_pci_bar_rw(struct vfio_pci_core_device *vdev, char __user *buf,
count = min(count, (size_t)(end - pos));
+#ifdef CONFIG_ACPI
+ if (bar == PCI_ROM_RESOURCE && rom_size) {
+ done = vfio_pci_acpi_rom_rw(vdev, buf, count, pos, iswrite);
+ if (done >= 0)
+ *ppos += done;
+ return done;
+ }
+#endif
+
if (bar == PCI_ROM_RESOURCE) {
if (!io)
return -ENOMEM;
diff --git a/include/linux/vfio_pci_core.h b/include/linux/vfio_pci_core.h
index 89165b769e5c..64f86ed8d6b0 100644
--- a/include/linux/vfio_pci_core.h
+++ b/include/linux/vfio_pci_core.h
@@ -115,6 +115,8 @@ struct vfio_pci_core_device {
u16 msix_size;
u32 msix_offset;
u32 rbar[7];
+ size_t rom_acpi_size; /* _ROM image size, if any */
+ bool rom_acpi_probed;
bool has_dyn_msix:1;
bool pci_2_3:1;
bool virq_disabled:1;
--
MST
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH v2 1/2] vfio/pci: fall back to platform ROM when pci_map_rom() fails
@ 2026-10-21 8:20 Michael S. Tsirkin
2026-10-21 8:20 ` [PATCH v2 2/2] vfio/pci: read ROM via ACPI _ROM when hardware ROM is unreadable Michael S. Tsirkin
2026-11-10 23:52 ` [PATCH v2 1/2] vfio/pci: fall back to platform ROM when pci_map_rom() fails Alex Williamson
0 siblings, 2 replies; 4+ messages in thread
From: Michael S. Tsirkin @ 2026-10-21 8:20 UTC (permalink / raw)
Cc: Alex Williamson, kvm
When pci_resource_start() returns non-zero but pci_map_rom() fails
(ROM BAR exists but content is unreadable), the pdev->rom fallback
is unreachable because it's behind an else-if that only triggers
when pci_resource_start() is zero.
Try pdev->rom whenever the ROM BAR didn't produce a mapping, in both
the region info ioctl and the read path.
Fixes: e021e6cbfb5a ("vfio/pci: Expose setup ROM at ROM bar when needed")
Cc: Yunxiang Li <Yunxiang.Li@amd.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
---
Notes (changelog):
v1->v2:
bound reads by the ROM image rather than the BAR once the
pdev->rom fallback is selected, so reads match the reported
region size (a pread spanning the image used to fail with
-EINVAL past the BAR size)
add Fixes: tag
drivers/vfio/pci/vfio_pci_core.c | 4 +++-
drivers/vfio/pci/vfio_pci_rdwr.c | 32 ++++++++++++++++++++++++--------
2 files changed, 27 insertions(+), 9 deletions(-)
diff --git a/drivers/vfio/pci/vfio_pci_core.c b/drivers/vfio/pci/vfio_pci_core.c
index 050e7542952e..630474d03e3b 100644
--- a/drivers/vfio/pci/vfio_pci_core.c
+++ b/drivers/vfio/pci/vfio_pci_core.c
@@ -1128,7 +1128,9 @@ int vfio_pci_ioctl_get_region_info(struct vfio_device *core_vdev,
pci_unmap_rom(pdev, io);
}
vfio_pci_memory_unlock_and_restore(vdev, cmd);
- } else if (pdev->rom && pdev->romlen) {
+ }
+
+ if (!info->size && pdev->rom && pdev->romlen) {
info->flags = VFIO_REGION_INFO_FLAG_READ;
/* Report BAR size as power of two. */
info->size = roundup_pow_of_two(pdev->romlen);
diff --git a/drivers/vfio/pci/vfio_pci_rdwr.c b/drivers/vfio/pci/vfio_pci_rdwr.c
index 3bfbb879a005..6e47d529cbb9 100644
--- a/drivers/vfio/pci/vfio_pci_rdwr.c
+++ b/drivers/vfio/pci/vfio_pci_rdwr.c
@@ -219,7 +219,8 @@ ssize_t vfio_pci_bar_rw(struct vfio_pci_core_device *vdev, char __user *buf,
int bar = VFIO_PCI_OFFSET_TO_INDEX(*ppos);
size_t x_start = 0, x_end = 0;
resource_size_t end;
- void __iomem *io;
+ void __iomem *io = NULL;
+ bool rom_is_mapped = false;
struct resource *res = &vdev->pdev->resource[bar];
ssize_t done;
enum vfio_pci_io_width max_width = VFIO_PCI_IO_WIDTH_8;
@@ -231,11 +232,6 @@ ssize_t vfio_pci_bar_rw(struct vfio_pci_core_device *vdev, char __user *buf,
else
return -EINVAL;
- if (pos >= end)
- return -EINVAL;
-
- count = min(count, (size_t)(end - pos));
-
if (bar == PCI_ROM_RESOURCE) {
/*
* The ROM can fill less space than the BAR, so we start the
@@ -244,10 +240,30 @@ ssize_t vfio_pci_bar_rw(struct vfio_pci_core_device *vdev, char __user *buf,
*/
if (pci_resource_start(pdev, bar)) {
io = pci_map_rom(pdev, &x_start);
- } else {
+ if (io)
+ rom_is_mapped = true;
+ }
+
+ /*
+ * The BAR can also exist but be unreadable, e.g. on a GPU
+ * that was never POSTed. Fall back to the ROM image stashed
+ * by the boot loader; the region then follows the image size
+ * rather than the BAR, so bound the access only after the
+ * source of the ROM content is known.
+ */
+ if (!io && pdev->rom && pdev->romlen) {
+ end = roundup_pow_of_two(pdev->romlen);
io = ioremap(pdev->rom, pdev->romlen);
x_start = pdev->romlen;
}
+ }
+
+ if (pos >= end)
+ return -EINVAL;
+
+ count = min(count, (size_t)(end - pos));
+
+ if (bar == PCI_ROM_RESOURCE) {
if (!io)
return -ENOMEM;
x_end = end;
@@ -283,7 +299,7 @@ ssize_t vfio_pci_bar_rw(struct vfio_pci_core_device *vdev, char __user *buf,
*ppos += done;
if (bar == PCI_ROM_RESOURCE) {
- if (pci_resource_start(pdev, bar))
+ if (rom_is_mapped)
pci_unmap_rom(pdev, io);
else
iounmap(io);
--
MST
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH v2 1/2] vfio/pci: fall back to platform ROM when pci_map_rom() fails
2026-10-21 8:20 [PATCH v2 1/2] vfio/pci: fall back to platform ROM when pci_map_rom() fails Michael S. Tsirkin
2026-10-21 8:20 ` [PATCH v2 2/2] vfio/pci: read ROM via ACPI _ROM when hardware ROM is unreadable Michael S. Tsirkin
@ 2026-11-10 23:52 ` Alex Williamson
2026-11-11 11:02 ` Michael S. Tsirkin
1 sibling, 1 reply; 4+ messages in thread
From: Alex Williamson @ 2026-11-10 23:52 UTC (permalink / raw)
To: Michael S. Tsirkin; +Cc: kvm, alex
On Wed, 21 Oct 2026 04:20:32 -0400
"Michael S. Tsirkin" <mst@redhat.com> wrote:
> When pci_resource_start() returns non-zero but pci_map_rom() fails
> (ROM BAR exists but content is unreadable), the pdev->rom fallback
> is unreachable because it's behind an else-if that only triggers
> when pci_resource_start() is zero.
>
> Try pdev->rom whenever the ROM BAR didn't produce a mapping, in both
> the region info ioctl and the read path.
Here's the crazy thing, users actually update firmware through
vfio-pci. Therefore, what's an invalid ROM image on one check, may be
a valid ROM image next round. If we fall through to a different ROM
provider, we might actually be presenting a changing view of the
device.
I believe this is why we intentionally always report the physical ROM
BAR when present and mapped, regardless of the contents.
QEMU, of course, has the ability to take the ROM as a file and override
whatever is provided through the kernel interface. This is how I had
previously made use of a secondary GPU on a laptop, dumping the ACPI
ROM to a file to provide directly to QEMU.
Does your GPU support a ROM BAR? The v1 patch didn't have this and
would have exposed the physical RAM BAR if present, so I'm guessing not.
Thanks,
Alex
> Fixes: e021e6cbfb5a ("vfio/pci: Expose setup ROM at ROM bar when needed")
> Cc: Yunxiang Li <Yunxiang.Li@amd.com>
> Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
> ---
>
> Notes (changelog):
> v1->v2:
> bound reads by the ROM image rather than the BAR once the
> pdev->rom fallback is selected, so reads match the reported
> region size (a pread spanning the image used to fail with
> -EINVAL past the BAR size)
> add Fixes: tag
>
> drivers/vfio/pci/vfio_pci_core.c | 4 +++-
> drivers/vfio/pci/vfio_pci_rdwr.c | 32 ++++++++++++++++++++++++--------
> 2 files changed, 27 insertions(+), 9 deletions(-)
>
> diff --git a/drivers/vfio/pci/vfio_pci_core.c b/drivers/vfio/pci/vfio_pci_core.c
> index 050e7542952e..630474d03e3b 100644
> --- a/drivers/vfio/pci/vfio_pci_core.c
> +++ b/drivers/vfio/pci/vfio_pci_core.c
> @@ -1128,7 +1128,9 @@ int vfio_pci_ioctl_get_region_info(struct vfio_device *core_vdev,
> pci_unmap_rom(pdev, io);
> }
> vfio_pci_memory_unlock_and_restore(vdev, cmd);
> - } else if (pdev->rom && pdev->romlen) {
> + }
> +
> + if (!info->size && pdev->rom && pdev->romlen) {
> info->flags = VFIO_REGION_INFO_FLAG_READ;
> /* Report BAR size as power of two. */
> info->size = roundup_pow_of_two(pdev->romlen);
> diff --git a/drivers/vfio/pci/vfio_pci_rdwr.c b/drivers/vfio/pci/vfio_pci_rdwr.c
> index 3bfbb879a005..6e47d529cbb9 100644
> --- a/drivers/vfio/pci/vfio_pci_rdwr.c
> +++ b/drivers/vfio/pci/vfio_pci_rdwr.c
> @@ -219,7 +219,8 @@ ssize_t vfio_pci_bar_rw(struct vfio_pci_core_device *vdev, char __user *buf,
> int bar = VFIO_PCI_OFFSET_TO_INDEX(*ppos);
> size_t x_start = 0, x_end = 0;
> resource_size_t end;
> - void __iomem *io;
> + void __iomem *io = NULL;
> + bool rom_is_mapped = false;
> struct resource *res = &vdev->pdev->resource[bar];
> ssize_t done;
> enum vfio_pci_io_width max_width = VFIO_PCI_IO_WIDTH_8;
> @@ -231,11 +232,6 @@ ssize_t vfio_pci_bar_rw(struct vfio_pci_core_device *vdev, char __user *buf,
> else
> return -EINVAL;
>
> - if (pos >= end)
> - return -EINVAL;
> -
> - count = min(count, (size_t)(end - pos));
> -
> if (bar == PCI_ROM_RESOURCE) {
> /*
> * The ROM can fill less space than the BAR, so we start the
> @@ -244,10 +240,30 @@ ssize_t vfio_pci_bar_rw(struct vfio_pci_core_device *vdev, char __user *buf,
> */
> if (pci_resource_start(pdev, bar)) {
> io = pci_map_rom(pdev, &x_start);
> - } else {
> + if (io)
> + rom_is_mapped = true;
> + }
> +
> + /*
> + * The BAR can also exist but be unreadable, e.g. on a GPU
> + * that was never POSTed. Fall back to the ROM image stashed
> + * by the boot loader; the region then follows the image size
> + * rather than the BAR, so bound the access only after the
> + * source of the ROM content is known.
> + */
> + if (!io && pdev->rom && pdev->romlen) {
> + end = roundup_pow_of_two(pdev->romlen);
> io = ioremap(pdev->rom, pdev->romlen);
> x_start = pdev->romlen;
> }
> + }
> +
> + if (pos >= end)
> + return -EINVAL;
> +
> + count = min(count, (size_t)(end - pos));
> +
> + if (bar == PCI_ROM_RESOURCE) {
> if (!io)
> return -ENOMEM;
> x_end = end;
> @@ -283,7 +299,7 @@ ssize_t vfio_pci_bar_rw(struct vfio_pci_core_device *vdev, char __user *buf,
> *ppos += done;
>
> if (bar == PCI_ROM_RESOURCE) {
> - if (pci_resource_start(pdev, bar))
> + if (rom_is_mapped)
> pci_unmap_rom(pdev, io);
> else
> iounmap(io);
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH v2 1/2] vfio/pci: fall back to platform ROM when pci_map_rom() fails
2026-11-10 23:52 ` [PATCH v2 1/2] vfio/pci: fall back to platform ROM when pci_map_rom() fails Alex Williamson
@ 2026-11-11 11:02 ` Michael S. Tsirkin
0 siblings, 0 replies; 4+ messages in thread
From: Michael S. Tsirkin @ 2026-11-11 11:02 UTC (permalink / raw)
To: Alex Williamson; +Cc: kvm
On Tue, Nov 10, 2026 at 04:52:21PM -0700, Alex Williamson wrote:
> On Wed, 21 Oct 2026 04:20:32 -0400
> "Michael S. Tsirkin" <mst@redhat.com> wrote:
>
> > When pci_resource_start() returns non-zero but pci_map_rom() fails
> > (ROM BAR exists but content is unreadable), the pdev->rom fallback
> > is unreachable because it's behind an else-if that only triggers
> > when pci_resource_start() is zero.
> >
> > Try pdev->rom whenever the ROM BAR didn't produce a mapping, in both
> > the region info ioctl and the read path.
>
> Here's the crazy thing, users actually update firmware through
> vfio-pci. Therefore, what's an invalid ROM image on one check, may be
> a valid ROM image next round. If we fall through to a different ROM
> provider, we might actually be presenting a changing view of the
> device.
This is out of spec anyway. The spec is pretty clear that _ROM if
present takes precedence over the ROM BAR. I decided not to change that
if ROM BAR is valid to reduce the chance of breaking a working setup.
From ACPI 6.3, Section A.3.3 "_ROM (Get ROM Data)", page 1123:
If _ROM is present, it is preferred over the image read through the
standard PCI interface, in order to allow platform runtime firmware to
provide re-configured ROM data via the method.
Would you like me to simply implement the spec? Use _ROM if there
even if pci rom bar is valid?
> I believe this is why we intentionally always report the physical ROM
> BAR when present and mapped, regardless of the contents.
No such setups should have _ROM, since rombar would then be ignored.
> QEMU, of course, has the ability to take the ROM as a file and override
> whatever is provided through the kernel interface. This is how I had
> previously made use of a secondary GPU on a laptop, dumping the ACPI
> ROM to a file to provide directly to QEMU.
First we need a way to get the ROM from _ROM.
>
> Does your GPU support a ROM BAR? The v1 patch didn't have this and
> would have exposed the physical RAM BAR if present, so I'm guessing not.
> Thanks,
>
> Alex
It has a bar but it is not accessible. _ROM overrides that, as per spec, and this
appears to be how nvidia drivers use it and expect to use it.
>
> > Fixes: e021e6cbfb5a ("vfio/pci: Expose setup ROM at ROM bar when needed")
> > Cc: Yunxiang Li <Yunxiang.Li@amd.com>
> > Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
> > ---
> >
> > Notes (changelog):
> > v1->v2:
> > bound reads by the ROM image rather than the BAR once the
> > pdev->rom fallback is selected, so reads match the reported
> > region size (a pread spanning the image used to fail with
> > -EINVAL past the BAR size)
> > add Fixes: tag
> >
> > drivers/vfio/pci/vfio_pci_core.c | 4 +++-
> > drivers/vfio/pci/vfio_pci_rdwr.c | 32 ++++++++++++++++++++++++--------
> > 2 files changed, 27 insertions(+), 9 deletions(-)
> >
> > diff --git a/drivers/vfio/pci/vfio_pci_core.c b/drivers/vfio/pci/vfio_pci_core.c
> > index 050e7542952e..630474d03e3b 100644
> > --- a/drivers/vfio/pci/vfio_pci_core.c
> > +++ b/drivers/vfio/pci/vfio_pci_core.c
> > @@ -1128,7 +1128,9 @@ int vfio_pci_ioctl_get_region_info(struct vfio_device *core_vdev,
> > pci_unmap_rom(pdev, io);
> > }
> > vfio_pci_memory_unlock_and_restore(vdev, cmd);
> > - } else if (pdev->rom && pdev->romlen) {
> > + }
> > +
> > + if (!info->size && pdev->rom && pdev->romlen) {
> > info->flags = VFIO_REGION_INFO_FLAG_READ;
> > /* Report BAR size as power of two. */
> > info->size = roundup_pow_of_two(pdev->romlen);
> > diff --git a/drivers/vfio/pci/vfio_pci_rdwr.c b/drivers/vfio/pci/vfio_pci_rdwr.c
> > index 3bfbb879a005..6e47d529cbb9 100644
> > --- a/drivers/vfio/pci/vfio_pci_rdwr.c
> > +++ b/drivers/vfio/pci/vfio_pci_rdwr.c
> > @@ -219,7 +219,8 @@ ssize_t vfio_pci_bar_rw(struct vfio_pci_core_device *vdev, char __user *buf,
> > int bar = VFIO_PCI_OFFSET_TO_INDEX(*ppos);
> > size_t x_start = 0, x_end = 0;
> > resource_size_t end;
> > - void __iomem *io;
> > + void __iomem *io = NULL;
> > + bool rom_is_mapped = false;
> > struct resource *res = &vdev->pdev->resource[bar];
> > ssize_t done;
> > enum vfio_pci_io_width max_width = VFIO_PCI_IO_WIDTH_8;
> > @@ -231,11 +232,6 @@ ssize_t vfio_pci_bar_rw(struct vfio_pci_core_device *vdev, char __user *buf,
> > else
> > return -EINVAL;
> >
> > - if (pos >= end)
> > - return -EINVAL;
> > -
> > - count = min(count, (size_t)(end - pos));
> > -
> > if (bar == PCI_ROM_RESOURCE) {
> > /*
> > * The ROM can fill less space than the BAR, so we start the
> > @@ -244,10 +240,30 @@ ssize_t vfio_pci_bar_rw(struct vfio_pci_core_device *vdev, char __user *buf,
> > */
> > if (pci_resource_start(pdev, bar)) {
> > io = pci_map_rom(pdev, &x_start);
> > - } else {
> > + if (io)
> > + rom_is_mapped = true;
> > + }
> > +
> > + /*
> > + * The BAR can also exist but be unreadable, e.g. on a GPU
> > + * that was never POSTed. Fall back to the ROM image stashed
> > + * by the boot loader; the region then follows the image size
> > + * rather than the BAR, so bound the access only after the
> > + * source of the ROM content is known.
> > + */
> > + if (!io && pdev->rom && pdev->romlen) {
> > + end = roundup_pow_of_two(pdev->romlen);
> > io = ioremap(pdev->rom, pdev->romlen);
> > x_start = pdev->romlen;
> > }
> > + }
> > +
> > + if (pos >= end)
> > + return -EINVAL;
> > +
> > + count = min(count, (size_t)(end - pos));
> > +
> > + if (bar == PCI_ROM_RESOURCE) {
> > if (!io)
> > return -ENOMEM;
> > x_end = end;
> > @@ -283,7 +299,7 @@ ssize_t vfio_pci_bar_rw(struct vfio_pci_core_device *vdev, char __user *buf,
> > *ppos += done;
> >
> > if (bar == PCI_ROM_RESOURCE) {
> > - if (pci_resource_start(pdev, bar))
> > + if (rom_is_mapped)
> > pci_unmap_rom(pdev, io);
> > else
> > iounmap(io);
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-11-11 11:02 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-21 8:20 [PATCH v2 1/2] vfio/pci: fall back to platform ROM when pci_map_rom() fails Michael S. Tsirkin
2026-10-21 8:20 ` [PATCH v2 2/2] vfio/pci: read ROM via ACPI _ROM when hardware ROM is unreadable Michael S. Tsirkin
2026-11-10 23:52 ` [PATCH v2 1/2] vfio/pci: fall back to platform ROM when pci_map_rom() fails Alex Williamson
2026-11-11 11:02 ` Michael S. Tsirkin
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox