aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStefan Hajnoczi <stefanha@redhat.com>2025-02-12 08:48:07 -0500
committerStefan Hajnoczi <stefanha@redhat.com>2025-02-12 08:48:08 -0500
commit5f1de4d3ce025fcb70b94ebe35241130d990d889 (patch)
tree5d9ef347a322444e61e26de4ed3328bb325d1e80
parentf9edf32ea2e18a56de5d92f57e9d10565c822367 (diff)
parentbe7d8579eb5758c0edf81eb068017a56471a77e0 (diff)
downloadqemu-5f1de4d3ce025fcb70b94ebe35241130d990d889.zip
qemu-5f1de4d3ce025fcb70b94ebe35241130d990d889.tar.gz
qemu-5f1de4d3ce025fcb70b94ebe35241130d990d889.tar.bz2
Merge tag 'pull-vfio-20250211' of https://github.com/legoater/qemu into staging
vfio queue: * Coverity fix * IGD cleanups using VFIOQuirk * SIGSEV fix in IOMMUFD host IOMMU device * Improved error reporting for MMIO region mapping failures # -----BEGIN PGP SIGNATURE----- # # iQIzBAABCAAdFiEEoPZlSPBIlev+awtgUaNDx8/77KEFAmerTzQACgkQUaNDx8/7 # 7KHXLw/+LaONyFor+kuorb5et6rzyrE4keIUDv8zDTM4FnwFKP31wX8feQ63o17U # DQUYN4uM5Ah/PemF/IBCj44x1Eirzl8LW51sMtxg/weCa8xrZOsHjmoKNml4f+zs # ERzO/KSu9PWEWEyX79XGCcu5VQKl60b8Ra5QMBNKZKjVZpfBTxCjHZFIvQxSJFvm # gPKHFDtmtbhtBnq3U/N/PwpnUuH4+p6ofz1eKdOcin11CAks7cAt6bl1CIs7sUbC # ttrrQg6D+UJ5b+ISZjsw5hakfRIdtlZ/lS4jk678gN06108CIMmFPLPaRu27mdX9 # 4wBiMSQM8fFbbHw66FQiPgJeeGAmG/PvdLN4SbRSujkEkKyEyqtH2dRINy9PNXj4 # ZXXugx7xqfPfTEC1lwsyGDdHdHH022V0ScdDpx+K87klRvu30ZjorB7QSCI7x+ZN # yW2ztZQ2hNH6MsgrKTQS6MLArHgU+h0ycaHy+01jj5UKSs3xAf53wNnx2uoBmKGj # gZB/tNFg60qeSuW900ybnBTaH60qLs6xzY7evDRa5cqPYJ+z/lRUYp45fmsgQ1yR # 91PHhC/mQLFjQRc78vF6k7slMm/Fk8JOalZgYPtm+Atdw3ufjOexavoHqN3Sa1Us # keKnR589kHikPd3zZN7sZzT8wMNTAcRbSy72360+PzEZ1Iiiu+M= # =wpSw # -----END PGP SIGNATURE----- # gpg: Signature made Tue 11 Feb 2025 08:23:00 EST # gpg: using RSA key A0F66548F04895EBFE6B0B6051A343C7CFFBECA1 # gpg: Good signature from "Cédric Le Goater <clg@redhat.com>" [full] # gpg: aka "Cédric Le Goater <clg@kaod.org>" [full] # Primary key fingerprint: A0F6 6548 F048 95EB FE6B 0B60 51A3 43C7 CFFB ECA1 * tag 'pull-vfio-20250211' of https://github.com/legoater/qemu: vfio: Remove superfluous error report in vfio_listener_region_add() vfio: Remove reports of DMA mapping errors in backends vfio: Improve error reporting when MMIO region mapping fails vfio: Introduce vfio_get_vfio_device() vfio: Rephrase comment in vfio_listener_region_add() error path vfio/pci: Replace "iommu_device" by "vIOMMU" util/error: Introduce warn_report_err_once() vfio/iommufd: Fix SIGSEV in iommufd_cdev_attach() vfio/igd: use VFIOConfigMirrorQuirk for mirrored registers vfio/pci: introduce config_offset field in VFIOConfigMirrorQuirk vfio/pci: declare generic quirks in a new header file vfio/igd: Fix potential overflow in igd_gtt_memory_size() Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
-rw-r--r--backends/iommufd.c3
-rw-r--r--hw/vfio/common.c40
-rw-r--r--hw/vfio/container.c2
-rw-r--r--hw/vfio/helpers.c10
-rw-r--r--hw/vfio/igd.c127
-rw-r--r--hw/vfio/iommufd.c5
-rw-r--r--hw/vfio/pci-quirks.c57
-rw-r--r--hw/vfio/pci-quirks.h72
-rw-r--r--hw/vfio/pci.c2
-rw-r--r--include/hw/vfio/vfio-common.h1
-rw-r--r--include/qapi/error.h12
-rw-r--r--util/error.c11
12 files changed, 177 insertions, 165 deletions
diff --git a/backends/iommufd.c b/backends/iommufd.c
index 7b4fc8e..d57da44 100644
--- a/backends/iommufd.c
+++ b/backends/iommufd.c
@@ -167,8 +167,6 @@ int iommufd_backend_map_dma(IOMMUFDBackend *be, uint32_t ioas_id, hwaddr iova,
/* TODO: Not support mapping hardware PCI BAR region for now. */
if (errno == EFAULT) {
warn_report("IOMMU_IOAS_MAP failed: %m, PCI BAR?");
- } else {
- error_report("IOMMU_IOAS_MAP failed: %m");
}
}
return ret;
@@ -203,7 +201,6 @@ int iommufd_backend_unmap_dma(IOMMUFDBackend *be, uint32_t ioas_id,
if (ret) {
ret = -errno;
- error_report("IOMMU_IOAS_UNMAP failed: %m");
}
return ret;
}
diff --git a/hw/vfio/common.c b/hw/vfio/common.c
index f7499a9..abbdc56 100644
--- a/hw/vfio/common.c
+++ b/hw/vfio/common.c
@@ -555,6 +555,18 @@ static bool vfio_get_section_iova_range(VFIOContainerBase *bcontainer,
return true;
}
+static void vfio_device_error_append(VFIODevice *vbasedev, Error **errp)
+{
+ /*
+ * MMIO region mapping failures are not fatal but in this case PCI
+ * peer-to-peer transactions are broken.
+ */
+ if (vbasedev && vbasedev->type == VFIO_DEVICE_TYPE_PCI) {
+ error_append_hint(errp, "%s: PCI peer-to-peer transactions "
+ "on BARs are not supported.\n", vbasedev->name);
+ }
+}
+
static void vfio_listener_region_add(MemoryListener *listener,
MemoryRegionSection *section)
{
@@ -582,8 +594,9 @@ static void vfio_listener_region_add(MemoryListener *listener,
return;
}
+ /* PPC64/pseries machine only */
if (!vfio_container_add_section_window(bcontainer, section, &err)) {
- goto fail;
+ goto mmio_dma_error;
}
memory_region_ref(section->mr);
@@ -668,9 +681,13 @@ static void vfio_listener_region_add(MemoryListener *listener,
"0x%"HWADDR_PRIx", %p) = %d (%s)",
bcontainer, iova, int128_get64(llsize), vaddr, ret,
strerror(-ret));
+ mmio_dma_error:
if (memory_region_is_ram_device(section->mr)) {
/* Allow unexpected mappings not to be fatal for RAM devices */
- error_report_err(err);
+ VFIODevice *vbasedev =
+ vfio_get_vfio_device(memory_region_owner(section->mr));
+ vfio_device_error_append(vbasedev, &err);
+ warn_report_err_once(err);
return;
}
goto fail;
@@ -679,16 +696,12 @@ static void vfio_listener_region_add(MemoryListener *listener,
return;
fail:
- if (memory_region_is_ram_device(section->mr)) {
- error_reportf_err(err, "PCI p2p may not work: ");
- return;
- }
- /*
- * On the initfn path, store the first error in the container so we
- * can gracefully fail. Runtime, there's not much we can do other
- * than throw a hardware error.
- */
if (!bcontainer->initialized) {
+ /*
+ * At machine init time or when the device is attached to the
+ * VM, store the first error in the container so we can
+ * gracefully fail the device realize routine.
+ */
if (!bcontainer->error) {
error_propagate_prepend(&bcontainer->error, err,
"Region %s: ",
@@ -697,6 +710,10 @@ fail:
error_free(err);
}
} else {
+ /*
+ * At runtime, there's not much we can do other than throw a
+ * hardware error.
+ */
error_report_err(err);
hw_error("vfio: DMA mapping failed, unable to continue");
}
@@ -786,6 +803,7 @@ static void vfio_listener_region_del(MemoryListener *listener,
memory_region_unref(section->mr);
+ /* PPC64/pseries machine only */
vfio_container_del_section_window(bcontainer, section);
}
diff --git a/hw/vfio/container.c b/hw/vfio/container.c
index 4ebb526..7c57bdd2 100644
--- a/hw/vfio/container.c
+++ b/hw/vfio/container.c
@@ -159,7 +159,6 @@ static int vfio_legacy_dma_unmap(const VFIOContainerBase *bcontainer,
unmap.size -= 1ULL << ctz64(bcontainer->pgsizes);
continue;
}
- error_report("VFIO_UNMAP_DMA failed: %s", strerror(errno));
return -errno;
}
@@ -204,7 +203,6 @@ static int vfio_legacy_dma_map(const VFIOContainerBase *bcontainer, hwaddr iova,
return 0;
}
- error_report("VFIO_MAP_DMA failed: %s", strerror(errno));
return -errno;
}
diff --git a/hw/vfio/helpers.c b/hw/vfio/helpers.c
index 913796f..4b255d4 100644
--- a/hw/vfio/helpers.c
+++ b/hw/vfio/helpers.c
@@ -23,6 +23,7 @@
#include <sys/ioctl.h>
#include "hw/vfio/vfio-common.h"
+#include "hw/vfio/pci.h"
#include "hw/hw.h"
#include "trace.h"
#include "qapi/error.h"
@@ -728,3 +729,12 @@ bool vfio_device_hiod_realize(VFIODevice *vbasedev, Error **errp)
return HOST_IOMMU_DEVICE_GET_CLASS(hiod)->realize(hiod, vbasedev, errp);
}
+
+VFIODevice *vfio_get_vfio_device(Object *obj)
+{
+ if (object_dynamic_cast(obj, TYPE_VFIO_PCI)) {
+ return &VFIO_PCI(obj)->vbasedev;
+ } else {
+ return NULL;
+ }
+}
diff --git a/hw/vfio/igd.c b/hw/vfio/igd.c
index 0740a5d..b1a237e 100644
--- a/hw/vfio/igd.c
+++ b/hw/vfio/igd.c
@@ -18,6 +18,7 @@
#include "hw/hw.h"
#include "hw/nvram/fw_cfg.h"
#include "pci.h"
+#include "pci-quirks.h"
#include "trace.h"
/*
@@ -133,7 +134,7 @@ static uint64_t igd_gtt_memory_size(int gen, uint16_t gmch)
} else {
ggms = (gmch >> IGD_GMCH_GEN8_GGMS_SHIFT) & IGD_GMCH_GEN8_GGMS_MASK;
if (ggms != 0) {
- ggms = 1 << ggms;
+ ggms = 1ULL << ggms;
}
}
@@ -422,83 +423,13 @@ static const MemoryRegionOps vfio_igd_index_quirk = {
.endianness = DEVICE_LITTLE_ENDIAN,
};
-static uint64_t vfio_igd_pci_config_read(VFIOPCIDevice *vdev, uint64_t offset,
- unsigned size)
-{
- switch (size) {
- case 1:
- return pci_get_byte(vdev->pdev.config + offset);
- case 2:
- return pci_get_word(vdev->pdev.config + offset);
- case 4:
- return pci_get_long(vdev->pdev.config + offset);
- case 8:
- return pci_get_quad(vdev->pdev.config + offset);
- default:
- hw_error("igd: unsupported pci config read at %"PRIx64", size %u",
- offset, size);
- break;
- }
-
- return 0;
-}
-
-static void vfio_igd_pci_config_write(VFIOPCIDevice *vdev, uint64_t offset,
- uint64_t data, unsigned size)
-{
- switch (size) {
- case 1:
- pci_set_byte(vdev->pdev.config + offset, data);
- break;
- case 2:
- pci_set_word(vdev->pdev.config + offset, data);
- break;
- case 4:
- pci_set_long(vdev->pdev.config + offset, data);
- break;
- case 8:
- pci_set_quad(vdev->pdev.config + offset, data);
- break;
- default:
- hw_error("igd: unsupported pci config write at %"PRIx64", size %u",
- offset, size);
- break;
- }
-}
-
-#define VFIO_IGD_QUIRK_MIRROR_REG(reg, name) \
-static uint64_t vfio_igd_quirk_read_##name(void *opaque, \
- hwaddr addr, unsigned size) \
-{ \
- VFIOPCIDevice *vdev = opaque; \
- \
- return vfio_igd_pci_config_read(vdev, reg + addr, size); \
-} \
- \
-static void vfio_igd_quirk_write_##name(void *opaque, hwaddr addr, \
- uint64_t data, unsigned size) \
-{ \
- VFIOPCIDevice *vdev = opaque; \
- \
- vfio_igd_pci_config_write(vdev, reg + addr, data, size); \
-} \
- \
-static const MemoryRegionOps vfio_igd_quirk_mirror_##name = { \
- .read = vfio_igd_quirk_read_##name, \
- .write = vfio_igd_quirk_write_##name, \
- .endianness = DEVICE_LITTLE_ENDIAN, \
-};
-
-VFIO_IGD_QUIRK_MIRROR_REG(IGD_GMCH, ggc)
-VFIO_IGD_QUIRK_MIRROR_REG(IGD_BDSM, bdsm)
-VFIO_IGD_QUIRK_MIRROR_REG(IGD_BDSM_GEN11, bdsm64)
-
#define IGD_GGC_MMIO_OFFSET 0x108040
#define IGD_BDSM_MMIO_OFFSET 0x1080C0
void vfio_probe_igd_bar0_quirk(VFIOPCIDevice *vdev, int nr)
{
- VFIOQuirk *quirk;
+ VFIOQuirk *ggc_quirk, *bdsm_quirk;
+ VFIOConfigMirrorQuirk *ggc_mirror, *bdsm_mirror;
int gen;
/*
@@ -522,33 +453,39 @@ void vfio_probe_igd_bar0_quirk(VFIOPCIDevice *vdev, int nr)
return;
}
- quirk = vfio_quirk_alloc(2);
- quirk->data = vdev;
+ ggc_quirk = vfio_quirk_alloc(1);
+ ggc_mirror = ggc_quirk->data = g_malloc0(sizeof(*ggc_mirror));
+ ggc_mirror->mem = ggc_quirk->mem;
+ ggc_mirror->vdev = vdev;
+ ggc_mirror->bar = nr;
+ ggc_mirror->offset = IGD_GGC_MMIO_OFFSET;
+ ggc_mirror->config_offset = IGD_GMCH;
- memory_region_init_io(&quirk->mem[0], OBJECT(vdev),
- &vfio_igd_quirk_mirror_ggc, vdev,
+ memory_region_init_io(ggc_mirror->mem, OBJECT(vdev),
+ &vfio_generic_mirror_quirk, ggc_mirror,
"vfio-igd-ggc-quirk", 2);
- memory_region_add_subregion_overlap(vdev->bars[0].region.mem,
- IGD_GGC_MMIO_OFFSET, &quirk->mem[0],
+ memory_region_add_subregion_overlap(vdev->bars[nr].region.mem,
+ ggc_mirror->offset, ggc_mirror->mem,
1);
- if (gen < 11) {
- memory_region_init_io(&quirk->mem[1], OBJECT(vdev),
- &vfio_igd_quirk_mirror_bdsm, vdev,
- "vfio-igd-bdsm-quirk", 4);
- memory_region_add_subregion_overlap(vdev->bars[0].region.mem,
- IGD_BDSM_MMIO_OFFSET,
- &quirk->mem[1], 1);
- } else {
- memory_region_init_io(&quirk->mem[1], OBJECT(vdev),
- &vfio_igd_quirk_mirror_bdsm64, vdev,
- "vfio-igd-bdsm-quirk", 8);
- memory_region_add_subregion_overlap(vdev->bars[0].region.mem,
- IGD_BDSM_MMIO_OFFSET,
- &quirk->mem[1], 1);
- }
+ QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, ggc_quirk, next);
- QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next);
+ bdsm_quirk = vfio_quirk_alloc(1);
+ bdsm_mirror = bdsm_quirk->data = g_malloc0(sizeof(*bdsm_mirror));
+ bdsm_mirror->mem = bdsm_quirk->mem;
+ bdsm_mirror->vdev = vdev;
+ bdsm_mirror->bar = nr;
+ bdsm_mirror->offset = IGD_BDSM_MMIO_OFFSET;
+ bdsm_mirror->config_offset = (gen < 11) ? IGD_BDSM : IGD_BDSM_GEN11;
+
+ memory_region_init_io(bdsm_mirror->mem, OBJECT(vdev),
+ &vfio_generic_mirror_quirk, bdsm_mirror,
+ "vfio-igd-bdsm-quirk", (gen < 11) ? 4 : 8);
+ memory_region_add_subregion_overlap(vdev->bars[nr].region.mem,
+ bdsm_mirror->offset, bdsm_mirror->mem,
+ 1);
+
+ QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, bdsm_quirk, next);
}
void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr)
diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c
index 3490a8f..df61edf 100644
--- a/hw/vfio/iommufd.c
+++ b/hw/vfio/iommufd.c
@@ -515,8 +515,8 @@ static bool iommufd_cdev_attach(const char *name, VFIODevice *vbasedev,
} else {
ret = iommufd_cdev_ram_block_discard_disable(true);
if (ret) {
- error_setg(errp,
- "Cannot set discarding of RAM broken (%d)", ret);
+ error_setg_errno(errp, -ret,
+ "Cannot set discarding of RAM broken");
goto err_discard_disable;
}
goto found_container;
@@ -544,6 +544,7 @@ static bool iommufd_cdev_attach(const char *name, VFIODevice *vbasedev,
ret = iommufd_cdev_ram_block_discard_disable(true);
if (ret) {
+ error_setg_errno(errp, -ret, "Cannot set discarding of RAM broken");
goto err_discard_disable;
}
diff --git a/hw/vfio/pci-quirks.c b/hw/vfio/pci-quirks.c
index c8e6047..fbe43b0 100644
--- a/hw/vfio/pci-quirks.c
+++ b/hw/vfio/pci-quirks.c
@@ -25,6 +25,7 @@
#include "hw/nvram/fw_cfg.h"
#include "hw/qdev-properties.h"
#include "pci.h"
+#include "pci-quirks.h"
#include "trace.h"
/*
@@ -66,40 +67,6 @@ bool vfio_opt_rom_in_denylist(VFIOPCIDevice *vdev)
* Device specific region quirks (mostly backdoors to PCI config space)
*/
-/*
- * The generic window quirks operate on an address and data register,
- * vfio_generic_window_address_quirk handles the address register and
- * vfio_generic_window_data_quirk handles the data register. These ops
- * pass reads and writes through to hardware until a value matching the
- * stored address match/mask is written. When this occurs, the data
- * register access emulated PCI config space for the device rather than
- * passing through accesses. This enables devices where PCI config space
- * is accessible behind a window register to maintain the virtualization
- * provided through vfio.
- */
-typedef struct VFIOConfigWindowMatch {
- uint32_t match;
- uint32_t mask;
-} VFIOConfigWindowMatch;
-
-typedef struct VFIOConfigWindowQuirk {
- struct VFIOPCIDevice *vdev;
-
- uint32_t address_val;
-
- uint32_t address_offset;
- uint32_t data_offset;
-
- bool window_enabled;
- uint8_t bar;
-
- MemoryRegion *addr_mem;
- MemoryRegion *data_mem;
-
- uint32_t nr_matches;
- VFIOConfigWindowMatch matches[];
-} VFIOConfigWindowQuirk;
-
static uint64_t vfio_generic_window_quirk_address_read(void *opaque,
hwaddr addr,
unsigned size)
@@ -135,7 +102,7 @@ static void vfio_generic_window_quirk_address_write(void *opaque, hwaddr addr,
}
}
-static const MemoryRegionOps vfio_generic_window_address_quirk = {
+const MemoryRegionOps vfio_generic_window_address_quirk = {
.read = vfio_generic_window_quirk_address_read,
.write = vfio_generic_window_quirk_address_write,
.endianness = DEVICE_LITTLE_ENDIAN,
@@ -178,26 +145,12 @@ static void vfio_generic_window_quirk_data_write(void *opaque, hwaddr addr,
addr + window->data_offset, data, size);
}
-static const MemoryRegionOps vfio_generic_window_data_quirk = {
+const MemoryRegionOps vfio_generic_window_data_quirk = {
.read = vfio_generic_window_quirk_data_read,
.write = vfio_generic_window_quirk_data_write,
.endianness = DEVICE_LITTLE_ENDIAN,
};
-/*
- * The generic mirror quirk handles devices which expose PCI config space
- * through a region within a BAR. When enabled, reads and writes are
- * redirected through to emulated PCI config space. XXX if PCI config space
- * used memory regions, this could just be an alias.
- */
-typedef struct VFIOConfigMirrorQuirk {
- struct VFIOPCIDevice *vdev;
- uint32_t offset;
- uint8_t bar;
- MemoryRegion *mem;
- uint8_t data[];
-} VFIOConfigMirrorQuirk;
-
static uint64_t vfio_generic_quirk_mirror_read(void *opaque,
hwaddr addr, unsigned size)
{
@@ -209,6 +162,7 @@ static uint64_t vfio_generic_quirk_mirror_read(void *opaque,
(void)vfio_region_read(&vdev->bars[mirror->bar].region,
addr + mirror->offset, size);
+ addr += mirror->config_offset;
data = vfio_pci_read_config(&vdev->pdev, addr, size);
trace_vfio_quirk_generic_mirror_read(vdev->vbasedev.name,
memory_region_name(mirror->mem),
@@ -222,13 +176,14 @@ static void vfio_generic_quirk_mirror_write(void *opaque, hwaddr addr,
VFIOConfigMirrorQuirk *mirror = opaque;
VFIOPCIDevice *vdev = mirror->vdev;
+ addr += mirror->config_offset;
vfio_pci_write_config(&vdev->pdev, addr, data, size);
trace_vfio_quirk_generic_mirror_write(vdev->vbasedev.name,
memory_region_name(mirror->mem),
addr, data);
}
-static const MemoryRegionOps vfio_generic_mirror_quirk = {
+const MemoryRegionOps vfio_generic_mirror_quirk = {
.read = vfio_generic_quirk_mirror_read,
.write = vfio_generic_quirk_mirror_write,
.endianness = DEVICE_LITTLE_ENDIAN,
diff --git a/hw/vfio/pci-quirks.h b/hw/vfio/pci-quirks.h
new file mode 100644
index 0000000..d1532e3
--- /dev/null
+++ b/hw/vfio/pci-quirks.h
@@ -0,0 +1,72 @@
+/*
+ * vfio generic region quirks (mostly backdoors to PCI config space)
+ *
+ * Copyright Red Hat, Inc. 2012-2015
+ *
+ * Authors:
+ * Alex Williamson <alex.williamson@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ */
+#ifndef HW_VFIO_VFIO_PCI_QUIRKS_H
+#define HW_VFIO_VFIO_PCI_QUIRKS_H
+
+#include "qemu/osdep.h"
+#include "exec/memop.h"
+
+/*
+ * The generic window quirks operate on an address and data register,
+ * vfio_generic_window_address_quirk handles the address register and
+ * vfio_generic_window_data_quirk handles the data register. These ops
+ * pass reads and writes through to hardware until a value matching the
+ * stored address match/mask is written. When this occurs, the data
+ * register access emulated PCI config space for the device rather than
+ * passing through accesses. This enables devices where PCI config space
+ * is accessible behind a window register to maintain the virtualization
+ * provided through vfio.
+ */
+typedef struct VFIOConfigWindowMatch {
+ uint32_t match;
+ uint32_t mask;
+} VFIOConfigWindowMatch;
+
+typedef struct VFIOConfigWindowQuirk {
+ struct VFIOPCIDevice *vdev;
+
+ uint32_t address_val;
+
+ uint32_t address_offset;
+ uint32_t data_offset;
+
+ bool window_enabled;
+ uint8_t bar;
+
+ MemoryRegion *addr_mem;
+ MemoryRegion *data_mem;
+
+ uint32_t nr_matches;
+ VFIOConfigWindowMatch matches[];
+} VFIOConfigWindowQuirk;
+
+extern const MemoryRegionOps vfio_generic_window_address_quirk;
+extern const MemoryRegionOps vfio_generic_window_data_quirk;
+
+/*
+ * The generic mirror quirk handles devices which expose PCI config space
+ * through a region within a BAR. When enabled, reads and writes are
+ * redirected through to emulated PCI config space. XXX if PCI config space
+ * used memory regions, this could just be an alias.
+ */
+typedef struct VFIOConfigMirrorQuirk {
+ struct VFIOPCIDevice *vdev;
+ uint32_t offset; /* Offset in BAR */
+ uint32_t config_offset; /* Offset in PCI config space */
+ uint8_t bar;
+ MemoryRegion *mem;
+ uint8_t data[];
+} VFIOConfigMirrorQuirk;
+
+extern const MemoryRegionOps vfio_generic_mirror_quirk;
+
+#endif /* HW_VFIO_VFIO_PCI_QUIRKS_H */
diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c
index 9a55e7b..89d900e 100644
--- a/hw/vfio/pci.c
+++ b/hw/vfio/pci.c
@@ -3116,7 +3116,7 @@ static void vfio_realize(PCIDevice *pdev, Error **errp)
if (!vbasedev->mdev &&
!pci_device_set_iommu_device(pdev, vbasedev->hiod, errp)) {
- error_prepend(errp, "Failed to set iommu_device: ");
+ error_prepend(errp, "Failed to set vIOMMU: ");
goto out_teardown;
}
diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h
index 0c60be5..ac35136 100644
--- a/include/hw/vfio/vfio-common.h
+++ b/include/hw/vfio/vfio-common.h
@@ -252,6 +252,7 @@ bool vfio_device_hiod_realize(VFIODevice *vbasedev, Error **errp);
bool vfio_attach_device(char *name, VFIODevice *vbasedev,
AddressSpace *as, Error **errp);
void vfio_detach_device(VFIODevice *vbasedev);
+VFIODevice *vfio_get_vfio_device(Object *obj);
int vfio_kvm_device_add_fd(int fd, Error **errp);
int vfio_kvm_device_del_fd(int fd, Error **errp);
diff --git a/include/qapi/error.h b/include/qapi/error.h
index 71f8fb2..f5fe216 100644
--- a/include/qapi/error.h
+++ b/include/qapi/error.h
@@ -467,6 +467,18 @@ void error_reportf_err(Error *err, const char *fmt, ...)
G_GNUC_PRINTF(2, 3);
/*
+ * Similar to warn_report_err(), except it prints the message just once.
+ * Return true when it prints, false otherwise.
+ */
+bool warn_report_err_once_cond(bool *printed, Error *err);
+
+#define warn_report_err_once(err) \
+ ({ \
+ static bool print_once_; \
+ warn_report_err_once_cond(&print_once_, err); \
+ })
+
+/*
* Just like error_setg(), except you get to specify the error class.
* Note: use of error classes other than ERROR_CLASS_GENERIC_ERROR is
* strongly discouraged.
diff --git a/util/error.c b/util/error.c
index e5e2472..673011b 100644
--- a/util/error.c
+++ b/util/error.c
@@ -247,6 +247,17 @@ void warn_report_err(Error *err)
error_free(err);
}
+bool warn_report_err_once_cond(bool *printed, Error *err)
+{
+ if (*printed) {
+ error_free(err);
+ return false;
+ }
+ *printed = true;
+ warn_report_err(err);
+ return true;
+}
+
void error_reportf_err(Error *err, const char *fmt, ...)
{
va_list ap;