218e99
From 7bbd16ac0463f162e4ca38e11876bdea519a0461 Mon Sep 17 00:00:00 2001
218e99
From: Alex Williamson <alex.williamson@redhat.com>
218e99
Date: Tue, 5 Nov 2013 15:30:34 +0100
218e99
Subject: [PATCH 11/25] vfio-pci: VGA quirk update
218e99
218e99
RH-Author: Alex Williamson <alex.williamson@redhat.com>
218e99
Message-id: <20131105153034.15749.72694.stgit@bling.home>
218e99
Patchwork-id: 55414
218e99
O-Subject: [RHEL7 qemu-kvm PATCH v2 1/2] vfio-pci: VGA quirk update
218e99
Bugzilla: 1025477
218e99
RH-Acked-by: Bandan Das <bsd@redhat.com>
218e99
RH-Acked-by: Jeffrey Cody <jcody@redhat.com>
218e99
RH-Acked-by: Paolo Bonzini <pbonzini@redhat.com>
218e99
218e99
Bugzilla: 1025477
218e99
Upstream commit: 39360f0b91d38790b5756d621ca9a7dd93ca8816
218e99
218e99
Turns out all the suspicions for AMD devices were correct, everywhere
218e99
we read a BAR address that the address matches the config space offset,
218e99
there's full access to PCI config space.  Attempt to generalize some
218e99
helpers to allow quirks to easily be added for mirrors and windows.
218e99
Also fill in complete config space for AMD.
218e99
218e99
Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
218e99
---
218e99
 hw/misc/vfio.c |  657 +++++++++++++++++++++++++++-----------------------------
218e99
 1 file changed, 321 insertions(+), 336 deletions(-)
218e99
218e99
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
218e99
---
218e99
 hw/misc/vfio.c |  657 +++++++++++++++++++++++++++-----------------------------
218e99
 1 files changed, 321 insertions(+), 336 deletions(-)
218e99
218e99
diff --git a/hw/misc/vfio.c b/hw/misc/vfio.c
218e99
index a966c58..a072fd9 100644
218e99
--- a/hw/misc/vfio.c
218e99
+++ b/hw/misc/vfio.c
218e99
@@ -59,8 +59,23 @@ typedef struct VFIOQuirk {
218e99
     MemoryRegion mem;
218e99
     struct VFIODevice *vdev;
218e99
     QLIST_ENTRY(VFIOQuirk) next;
218e99
-    uint32_t data;
218e99
-    uint32_t data2;
218e99
+    struct {
218e99
+        uint32_t base_offset:TARGET_PAGE_BITS;
218e99
+        uint32_t address_offset:TARGET_PAGE_BITS;
218e99
+        uint32_t address_size:3;
218e99
+        uint32_t bar:3;
218e99
+
218e99
+        uint32_t address_match;
218e99
+        uint32_t address_mask;
218e99
+
218e99
+        uint32_t address_val:TARGET_PAGE_BITS;
218e99
+        uint32_t data_offset:TARGET_PAGE_BITS;
218e99
+        uint32_t data_size:3;
218e99
+
218e99
+        uint8_t flags;
218e99
+        uint8_t read_flags;
218e99
+        uint8_t write_flags;
218e99
+    } data;
218e99
 } VFIOQuirk;
218e99
 
218e99
 typedef struct VFIOBAR {
218e99
@@ -72,6 +87,8 @@ typedef struct VFIOBAR {
218e99
     size_t size;
218e99
     uint32_t flags; /* VFIO region flags (rd/wr/mmap) */
218e99
     uint8_t nr; /* cache the BAR number for debug */
218e99
+    bool ioport;
218e99
+    bool mem64;
218e99
     QLIST_HEAD(, VFIOQuirk) quirks;
218e99
 } VFIOBAR;
218e99
 
218e99
@@ -1099,251 +1116,315 @@ static const MemoryRegionOps vfio_vga_ops = {
218e99
  * Device specific quirks
218e99
  */
218e99
 
218e99
-#define PCI_VENDOR_ID_ATI               0x1002
218e99
+/* Is range1 fully contained within range2?  */
218e99
+static bool vfio_range_contained(uint64_t first1, uint64_t len1,
218e99
+                                 uint64_t first2, uint64_t len2) {
218e99
+    return (first1 >= first2 && first1 + len1 <= first2 + len2);
218e99
+}
218e99
 
218e99
-/*
218e99
- * Device 1002:68f9 (Advanced Micro Devices [AMD] nee ATI Cedar PRO [Radeon
218e99
- * HD 5450/6350]) reports the upper byte of the physical address of the
218e99
- * I/O port BAR4 through VGA register 0x3c3.  The BAR is 256 bytes, so the
218e99
- * lower byte is known to be zero.  Probing for this quirk reads 0xff from
218e99
- * port 0x3c3 on some devices so we store the physical address and replace
218e99
- * reads with the virtual address any time it matches.  XXX Research when
218e99
- * to enable quirk.
218e99
- */
218e99
-static uint64_t vfio_ati_3c3_quirk_read(void *opaque,
218e99
-                                        hwaddr addr, unsigned size)
218e99
+static bool vfio_flags_enabled(uint8_t flags, uint8_t mask)
218e99
+{
218e99
+    return (mask && (flags & mask) == mask);
218e99
+}
218e99
+
218e99
+static uint64_t vfio_generic_window_quirk_read(void *opaque,
218e99
+                                               hwaddr addr, unsigned size)
218e99
 {
218e99
     VFIOQuirk *quirk = opaque;
218e99
     VFIODevice *vdev = quirk->vdev;
218e99
-    PCIDevice *pdev = &vdev->pdev;
218e99
-    uint64_t data = vfio_vga_read(&vdev->vga.region[QEMU_PCI_VGA_IO_HI],
218e99
-                                  addr + 0x3, size);
218e99
+    uint64_t data;
218e99
 
218e99
-    if (data == quirk->data) {
218e99
-        data = pci_get_byte(pdev->config + PCI_BASE_ADDRESS_4 + 1);
218e99
-        DPRINTF("%s(0x3c3, 1) = 0x%"PRIx64"\n", __func__, data);
218e99
+    if (vfio_flags_enabled(quirk->data.flags, quirk->data.read_flags) &&
218e99
+        ranges_overlap(addr, size,
218e99
+                       quirk->data.data_offset, quirk->data.data_size)) {
218e99
+        hwaddr offset = addr - quirk->data.data_offset;
218e99
+
218e99
+        if (!vfio_range_contained(addr, size, quirk->data.data_offset,
218e99
+                                  quirk->data.data_size)) {
218e99
+            hw_error("%s: window data read not fully contained: %s\n",
218e99
+                     __func__, memory_region_name(&quirk->mem));
218e99
+        }
218e99
+
218e99
+        data = vfio_pci_read_config(&vdev->pdev,
218e99
+                                    quirk->data.address_val + offset, size);
218e99
+
218e99
+        DPRINTF("%s read(%04x:%02x:%02x.%x:BAR%d+0x%"HWADDR_PRIx", %d) = 0x%"
218e99
+                PRIx64"\n", memory_region_name(&quirk->mem), vdev->host.domain,
218e99
+                vdev->host.bus, vdev->host.slot, vdev->host.function,
218e99
+                quirk->data.bar, addr, size, data);
218e99
+    } else {
218e99
+        data = vfio_bar_read(&vdev->bars[quirk->data.bar],
218e99
+                             addr + quirk->data.base_offset, size);
218e99
     }
218e99
 
218e99
     return data;
218e99
 }
218e99
 
218e99
-static const MemoryRegionOps vfio_ati_3c3_quirk = {
218e99
-    .read = vfio_ati_3c3_quirk_read,
218e99
-    .endianness = DEVICE_LITTLE_ENDIAN,
218e99
-};
218e99
-
218e99
-static void vfio_vga_probe_ati_3c3_quirk(VFIODevice *vdev)
218e99
+static void vfio_generic_window_quirk_write(void *opaque, hwaddr addr,
218e99
+                                            uint64_t data, unsigned size)
218e99
 {
218e99
-    PCIDevice *pdev = &vdev->pdev;
218e99
-    off_t physoffset = vdev->config_offset + PCI_BASE_ADDRESS_4;
218e99
-    uint32_t physbar;
218e99
-    VFIOQuirk *quirk;
218e99
+    VFIOQuirk *quirk = opaque;
218e99
+    VFIODevice *vdev = quirk->vdev;
218e99
 
218e99
-    if (pci_get_word(pdev->config + PCI_VENDOR_ID) != PCI_VENDOR_ID_ATI ||
218e99
-        vdev->bars[4].size < 256) {
218e99
-        return;
218e99
-    }
218e99
+    if (ranges_overlap(addr, size,
218e99
+                       quirk->data.address_offset, quirk->data.address_size)) {
218e99
 
218e99
-    /* Get I/O port BAR physical address */
218e99
-    if (pread(vdev->fd, &physbar, 4, physoffset) != 4) {
218e99
-        error_report("vfio: probe failed for ATI/AMD 0x3c3 quirk on device "
218e99
-                     "%04x:%02x:%02x.%x", vdev->host.domain,
218e99
-                     vdev->host.bus, vdev->host.slot, vdev->host.function);
218e99
-        return;
218e99
+        if (addr != quirk->data.address_offset) {
218e99
+            hw_error("%s: offset write into address window: %s\n",
218e99
+                     __func__, memory_region_name(&quirk->mem));
218e99
+        }
218e99
+
218e99
+        if ((data & ~quirk->data.address_mask) == quirk->data.address_match) {
218e99
+            quirk->data.flags |= quirk->data.write_flags |
218e99
+                                 quirk->data.read_flags;
218e99
+            quirk->data.address_val = data & quirk->data.address_mask;
218e99
+        } else {
218e99
+            quirk->data.flags &= ~(quirk->data.write_flags |
218e99
+                                   quirk->data.read_flags);
218e99
+        }
218e99
     }
218e99
 
218e99
-    quirk = g_malloc0(sizeof(*quirk));
218e99
-    quirk->vdev = vdev;
218e99
-    quirk->data = (physbar >> 8) & 0xff;
218e99
+    if (vfio_flags_enabled(quirk->data.flags, quirk->data.write_flags) &&
218e99
+        ranges_overlap(addr, size,
218e99
+                       quirk->data.data_offset, quirk->data.data_size)) {
218e99
+        hwaddr offset = addr - quirk->data.data_offset;
218e99
 
218e99
-    memory_region_init_io(&quirk->mem, &vfio_ati_3c3_quirk, quirk,
218e99
-                          "vfio-ati-3c3-quirk", 1);
218e99
-    memory_region_add_subregion(&vdev->vga.region[QEMU_PCI_VGA_IO_HI].mem, 3,
218e99
-                                &quirk->mem);
218e99
+        if (!vfio_range_contained(addr, size, quirk->data.data_offset,
218e99
+                                  quirk->data.data_size)) {
218e99
+            hw_error("%s: window data write not fully contained: %s\n",
218e99
+                     __func__, memory_region_name(&quirk->mem));
218e99
+        }
218e99
 
218e99
-    QLIST_INSERT_HEAD(&vdev->vga.region[QEMU_PCI_VGA_IO_HI].quirks,
218e99
-                      quirk, next);
218e99
+        vfio_pci_write_config(&vdev->pdev,
218e99
+                              quirk->data.address_val + offset, data, size);
218e99
+        DPRINTF("%s write(%04x:%02x:%02x.%x:BAR%d+0x%"HWADDR_PRIx", 0x%"
218e99
+                PRIx64", %d)\n", memory_region_name(&quirk->mem),
218e99
+                vdev->host.domain, vdev->host.bus, vdev->host.slot,
218e99
+                vdev->host.function, quirk->data.bar, addr, data, size);
218e99
+        return;
218e99
+    }
218e99
 
218e99
-    DPRINTF("Enabled ATI/AMD quirk 0x3c3 for device %04x:%02x:%02x.%x\n",
218e99
-            vdev->host.domain, vdev->host.bus, vdev->host.slot,
218e99
-            vdev->host.function);
218e99
+    vfio_bar_write(&vdev->bars[quirk->data.bar],
218e99
+                   addr + quirk->data.base_offset, data, size);
218e99
 }
218e99
 
218e99
-/*
218e99
- * Device 1002:68f9 (Advanced Micro Devices [AMD] nee ATI Cedar PRO [Radeon
218e99
- * HD 5450/6350]) reports the physical address of MMIO BAR0 through a
218e99
- * write/read operation on I/O port BAR4.  When uint32_t 0x4010 is written
218e99
- * to offset 0x0, the subsequent read from offset 0x4 returns the contents
218e99
- * of BAR0.  Test for this quirk on all ATI/AMD devices.  XXX - Note that
218e99
- * 0x10 is the offset of BAR0 in config sapce, is this a window to all of
218e99
- * config space?
218e99
- */
218e99
-static uint64_t vfio_ati_4010_quirk_read(void *opaque,
218e99
-                                         hwaddr addr, unsigned size)
218e99
+static const MemoryRegionOps vfio_generic_window_quirk = {
218e99
+    .read = vfio_generic_window_quirk_read,
218e99
+    .write = vfio_generic_window_quirk_write,
218e99
+    .endianness = DEVICE_LITTLE_ENDIAN,
218e99
+};
218e99
+
218e99
+static uint64_t vfio_generic_quirk_read(void *opaque,
218e99
+                                        hwaddr addr, unsigned size)
218e99
 {
218e99
     VFIOQuirk *quirk = opaque;
218e99
     VFIODevice *vdev = quirk->vdev;
218e99
-    PCIDevice *pdev = &vdev->pdev;
218e99
-    uint64_t data = vfio_bar_read(&vdev->bars[4], addr, size);
218e99
+    hwaddr base = quirk->data.address_match & TARGET_PAGE_MASK;
218e99
+    hwaddr offset = quirk->data.address_match & ~TARGET_PAGE_MASK;
218e99
+    uint64_t data;
218e99
 
218e99
-    if (addr == 4 && size == 4 && quirk->data) {
218e99
-        data = pci_get_long(pdev->config + PCI_BASE_ADDRESS_0);
218e99
-        DPRINTF("%s(BAR4+0x4) = 0x%"PRIx64"\n", __func__, data);
218e99
-    }
218e99
+    if (vfio_flags_enabled(quirk->data.flags, quirk->data.read_flags) &&
218e99
+        ranges_overlap(addr, size, offset, quirk->data.address_mask + 1)) {
218e99
+        if (!vfio_range_contained(addr, size, offset,
218e99
+                                  quirk->data.address_mask + 1)) {
218e99
+            hw_error("%s: read not fully contained: %s\n",
218e99
+                     __func__, memory_region_name(&quirk->mem));
218e99
+        }
218e99
 
218e99
-    quirk->data = 0;
218e99
+        data = vfio_pci_read_config(&vdev->pdev, addr - offset, size);
218e99
+
218e99
+        DPRINTF("%s read(%04x:%02x:%02x.%x:BAR%d+0x%"HWADDR_PRIx", %d) = 0x%"
218e99
+                PRIx64"\n", memory_region_name(&quirk->mem), vdev->host.domain,
218e99
+                vdev->host.bus, vdev->host.slot, vdev->host.function,
218e99
+                quirk->data.bar, addr + base, size, data);
218e99
+    } else {
218e99
+        data = vfio_bar_read(&vdev->bars[quirk->data.bar], addr + base, size);
218e99
+    }
218e99
 
218e99
     return data;
218e99
 }
218e99
 
218e99
-static void vfio_ati_4010_quirk_write(void *opaque, hwaddr addr,
218e99
-                                      uint64_t data, unsigned size)
218e99
+static void vfio_generic_quirk_write(void *opaque, hwaddr addr,
218e99
+                                     uint64_t data, unsigned size)
218e99
 {
218e99
     VFIOQuirk *quirk = opaque;
218e99
     VFIODevice *vdev = quirk->vdev;
218e99
+    hwaddr base = quirk->data.address_match & TARGET_PAGE_MASK;
218e99
+    hwaddr offset = quirk->data.address_match & ~TARGET_PAGE_MASK;
218e99
+
218e99
+    if (vfio_flags_enabled(quirk->data.flags, quirk->data.write_flags) &&
218e99
+        ranges_overlap(addr, size, offset, quirk->data.address_mask + 1)) {
218e99
+        if (!vfio_range_contained(addr, size, offset,
218e99
+                                  quirk->data.address_mask + 1)) {
218e99
+            hw_error("%s: write not fully contained: %s\n",
218e99
+                     __func__, memory_region_name(&quirk->mem));
218e99
+        }
218e99
 
218e99
-    vfio_bar_write(&vdev->bars[4], addr, data, size);
218e99
+        vfio_pci_write_config(&vdev->pdev, addr - offset, data, size);
218e99
 
218e99
-    quirk->data = (addr == 0 && size == 4 && data == 0x4010) ? 1 : 0;
218e99
+        DPRINTF("%s write(%04x:%02x:%02x.%x:BAR%d+0x%"HWADDR_PRIx", 0x%"
218e99
+                PRIx64", %d)\n", memory_region_name(&quirk->mem),
218e99
+                vdev->host.domain, vdev->host.bus, vdev->host.slot,
218e99
+                vdev->host.function, quirk->data.bar, addr + base, data, size);
218e99
+    } else {
218e99
+        vfio_bar_write(&vdev->bars[quirk->data.bar], addr + base, data, size);
218e99
+    }
218e99
 }
218e99
 
218e99
-static const MemoryRegionOps vfio_ati_4010_quirk = {
218e99
-    .read = vfio_ati_4010_quirk_read,
218e99
-    .write = vfio_ati_4010_quirk_write,
218e99
+static const MemoryRegionOps vfio_generic_quirk = {
218e99
+    .read = vfio_generic_quirk_read,
218e99
+    .write = vfio_generic_quirk_write,
218e99
     .endianness = DEVICE_LITTLE_ENDIAN,
218e99
 };
218e99
 
218e99
-static void vfio_probe_ati_4010_quirk(VFIODevice *vdev, int nr)
218e99
+#define PCI_VENDOR_ID_ATI               0x1002
218e99
+
218e99
+/*
218e99
+ * Radeon HD cards (HD5450 & HD7850) report the upper byte of the I/O port BAR
218e99
+ * through VGA register 0x3c3.  On newer cards, the I/O port BAR is always
218e99
+ * BAR4 (older cards like the X550 used BAR1, but we don't care to support
218e99
+ * those).  Note that on bare metal, a read of 0x3c3 doesn't always return the
218e99
+ * I/O port BAR address.  Originally this was coded to return the virtual BAR
218e99
+ * address only if the physical register read returns the actual BAR address,
218e99
+ * but users have reported greater success if we return the virtual address
218e99
+ * unconditionally.
218e99
+ */
218e99
+static uint64_t vfio_ati_3c3_quirk_read(void *opaque,
218e99
+                                        hwaddr addr, unsigned size)
218e99
+{
218e99
+    VFIOQuirk *quirk = opaque;
218e99
+    VFIODevice *vdev = quirk->vdev;
218e99
+    uint64_t data = vfio_pci_read_config(&vdev->pdev,
218e99
+                                         PCI_BASE_ADDRESS_0 + (4 * 4) + 1,
218e99
+                                         size);
218e99
+    DPRINTF("%s(0x3c3, 1) = 0x%"PRIx64"\n", __func__, data);
218e99
+
218e99
+    return data;
218e99
+}
218e99
+
218e99
+static const MemoryRegionOps vfio_ati_3c3_quirk = {
218e99
+    .read = vfio_ati_3c3_quirk_read,
218e99
+    .endianness = DEVICE_LITTLE_ENDIAN,
218e99
+};
218e99
+
218e99
+static void vfio_vga_probe_ati_3c3_quirk(VFIODevice *vdev)
218e99
 {
218e99
     PCIDevice *pdev = &vdev->pdev;
218e99
-    off_t physoffset = vdev->config_offset + PCI_BASE_ADDRESS_0;
218e99
-    uint32_t physbar0;
218e99
-    uint64_t data;
218e99
     VFIOQuirk *quirk;
218e99
 
218e99
-    if (!vdev->has_vga || nr != 4 || !vdev->bars[0].size ||
218e99
-        pci_get_word(pdev->config + PCI_VENDOR_ID) != PCI_VENDOR_ID_ATI) {
218e99
-        return;
218e99
-    }
218e99
-
218e99
-    /* Get I/O port BAR physical address */
218e99
-    if (pread(vdev->fd, &physbar0, 4, physoffset) != 4) {
218e99
-        error_report("vfio: probe failed for ATI/AMD 0x4010 quirk on device "
218e99
-                     "%04x:%02x:%02x.%x", vdev->host.domain,
218e99
-                     vdev->host.bus, vdev->host.slot, vdev->host.function);
218e99
+    if (pci_get_word(pdev->config + PCI_VENDOR_ID) != PCI_VENDOR_ID_ATI) {
218e99
         return;
218e99
     }
218e99
 
218e99
-    /* Write 0x4010 to I/O port BAR offset 0 */
218e99
-    vfio_bar_write(&vdev->bars[4], 0, 0x4010, 4);
218e99
-    /* Read back result */
218e99
-    data = vfio_bar_read(&vdev->bars[4], 4, 4);
218e99
-
218e99
-    /* If the register matches the physical address of BAR0, we need a quirk */
218e99
-    if (data != physbar0) {
218e99
+    /*
218e99
+     * As long as the BAR is >= 256 bytes it will be aligned such that the
218e99
+     * lower byte is always zero.  Filter out anything else, if it exists.
218e99
+     */
218e99
+    if (!vdev->bars[4].ioport || vdev->bars[4].size < 256) {
218e99
         return;
218e99
     }
218e99
 
218e99
     quirk = g_malloc0(sizeof(*quirk));
218e99
     quirk->vdev = vdev;
218e99
 
218e99
-    memory_region_init_io(&quirk->mem, &vfio_ati_4010_quirk, quirk,
218e99
-                          "vfio-ati-4010-quirk", 8);
218e99
-    memory_region_add_subregion_overlap(&vdev->bars[nr].mem, 0, &quirk->mem, 1);
218e99
+    memory_region_init_io(&quirk->mem, &vfio_ati_3c3_quirk, quirk,
218e99
+                          "vfio-ati-3c3-quirk", 1);
218e99
+    memory_region_add_subregion(&vdev->vga.region[QEMU_PCI_VGA_IO_HI].mem,
218e99
+                                3 /* offset 3 bytes from 0x3c0 */, &quirk->mem);
218e99
 
218e99
-    QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next);
218e99
+    QLIST_INSERT_HEAD(&vdev->vga.region[QEMU_PCI_VGA_IO_HI].quirks,
218e99
+                      quirk, next);
218e99
 
218e99
-    DPRINTF("Enabled ATI/AMD quirk 0x4010 for device %04x:%02x:%02x.%x\n",
218e99
+    DPRINTF("Enabled ATI/AMD quirk 0x3c3 BAR4for device %04x:%02x:%02x.%x\n",
218e99
             vdev->host.domain, vdev->host.bus, vdev->host.slot,
218e99
             vdev->host.function);
218e99
 }
218e99
 
218e99
 /*
218e99
- * Device 1002:5b63 (Advanced Micro Devices [AMD] nee ATI RV370 [Radeon X550])
218e99
- * retrieves the upper half of the MMIO BAR0 physical address by writing
218e99
- * 0xf10 to I/O port BAR1 offset 0 and reading the result from offset 6.
218e99
- * XXX - 0x10 is the offset of BAR0 in PCI config space, this could provide
218e99
- * full access to config space.  Config space is little endian, so the data
218e99
- * register probably starts at 0x4.
218e99
+ * Newer ATI/AMD devices, including HD5450 and HD7850, have a window to PCI
218e99
+ * config space through MMIO BAR2 at offset 0x4000.  Nothing seems to access
218e99
+ * the MMIO space directly, but a window to this space is provided through
218e99
+ * I/O port BAR4.  Offset 0x0 is the address register and offset 0x4 is the
218e99
+ * data register.  When the address is programmed to a range of 0x4000-0x4fff
218e99
+ * PCI configuration space is available.  Experimentation seems to indicate
218e99
+ * that only read-only access is provided, but we drop writes when the window
218e99
+ * is enabled to config space nonetheless.
218e99
  */
218e99
-static uint64_t vfio_ati_f10_quirk_read(void *opaque,
218e99
-                                        hwaddr addr, unsigned size)
218e99
+static void vfio_probe_ati_bar4_window_quirk(VFIODevice *vdev, int nr)
218e99
 {
218e99
-    VFIOQuirk *quirk = opaque;
218e99
-    VFIODevice *vdev = quirk->vdev;
218e99
     PCIDevice *pdev = &vdev->pdev;
218e99
-    uint64_t data = vfio_bar_read(&vdev->bars[1], addr, size);
218e99
+    VFIOQuirk *quirk;
218e99
 
218e99
-    if (addr == 6 && size == 2 && quirk->data) {
218e99
-        data = pci_get_word(pdev->config + PCI_BASE_ADDRESS_0 + 2);
218e99
-        DPRINTF("%s(BAR1+0x6) = 0x%"PRIx64"\n", __func__, data);
218e99
+    if (!vdev->has_vga || nr != 4 ||
218e99
+        pci_get_word(pdev->config + PCI_VENDOR_ID) != PCI_VENDOR_ID_ATI) {
218e99
+        return;
218e99
     }
218e99
 
218e99
-    quirk->data = 0;
218e99
-
218e99
-    return data;
218e99
-}
218e99
-
218e99
-static void vfio_ati_f10_quirk_write(void *opaque, hwaddr addr,
218e99
-                                     uint64_t data, unsigned size)
218e99
-{
218e99
-    VFIOQuirk *quirk = opaque;
218e99
-    VFIODevice *vdev = quirk->vdev;
218e99
+    quirk = g_malloc0(sizeof(*quirk));
218e99
+    quirk->vdev = vdev;
218e99
+    quirk->data.address_size = 4;
218e99
+    quirk->data.data_offset = 4;
218e99
+    quirk->data.data_size = 4;
218e99
+    quirk->data.address_match = 0x4000;
218e99
+    quirk->data.address_mask = PCIE_CONFIG_SPACE_SIZE - 1;
218e99
+    quirk->data.bar = nr;
218e99
+    quirk->data.read_flags = quirk->data.write_flags = 1;
218e99
+
218e99
+    memory_region_init_io(&quirk->mem,
218e99
+                          &vfio_generic_window_quirk, quirk,
218e99
+                          "vfio-ati-bar4-window-quirk", 8);
218e99
+    memory_region_add_subregion_overlap(&vdev->bars[nr].mem,
218e99
+                          quirk->data.base_offset, &quirk->mem, 1);
218e99
 
218e99
-    vfio_bar_write(&vdev->bars[1], addr, data, size);
218e99
+    QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next);
218e99
 
218e99
-    quirk->data = (addr == 0 && size == 4 && data == 0xf10) ? 1 : 0;
218e99
+    DPRINTF("Enabled ATI/AMD BAR4 window quirk for device %04x:%02x:%02x.%x\n",
218e99
+            vdev->host.domain, vdev->host.bus, vdev->host.slot,
218e99
+            vdev->host.function);
218e99
 }
218e99
 
218e99
-static const MemoryRegionOps vfio_ati_f10_quirk = {
218e99
-    .read = vfio_ati_f10_quirk_read,
218e99
-    .write = vfio_ati_f10_quirk_write,
218e99
-    .endianness = DEVICE_LITTLE_ENDIAN,
218e99
-};
218e99
-
218e99
-static void vfio_probe_ati_f10_quirk(VFIODevice *vdev, int nr)
218e99
+/*
218e99
+ * Trap the BAR2 MMIO window to config space as well.
218e99
+ */
218e99
+static void vfio_probe_ati_bar2_4000_quirk(VFIODevice *vdev, int nr)
218e99
 {
218e99
     PCIDevice *pdev = &vdev->pdev;
218e99
-    off_t physoffset = vdev->config_offset + PCI_BASE_ADDRESS_0;
218e99
-    uint32_t physbar0;
218e99
-    uint64_t data;
218e99
     VFIOQuirk *quirk;
218e99
 
218e99
-    if (!vdev->has_vga || nr != 1 || !vdev->bars[0].size ||
218e99
+    /* Only enable on newer devices where BAR2 is 64bit */
218e99
+    if (!vdev->has_vga || nr != 2 || !vdev->bars[2].mem64 ||
218e99
         pci_get_word(pdev->config + PCI_VENDOR_ID) != PCI_VENDOR_ID_ATI) {
218e99
         return;
218e99
     }
218e99
 
218e99
-    /* Get I/O port BAR physical address */
218e99
-    if (pread(vdev->fd, &physbar0, 4, physoffset) != 4) {
218e99
-        error_report("vfio: probe failed for ATI/AMD 0xf10 quirk on device "
218e99
-                     "%04x:%02x:%02x.%x", vdev->host.domain,
218e99
-                     vdev->host.bus, vdev->host.slot, vdev->host.function);
218e99
-        return;
218e99
-    }
218e99
-
218e99
-    vfio_bar_write(&vdev->bars[1], 0, 0xf10, 4);
218e99
-    data = vfio_bar_read(&vdev->bars[1], 0x6, 2);
218e99
-
218e99
-    /* If the register matches the physical address of BAR0, we need a quirk */
218e99
-    if (data != (le32_to_cpu(physbar0) >> 16)) {
218e99
-        return;
218e99
-    }
218e99
-
218e99
     quirk = g_malloc0(sizeof(*quirk));
218e99
     quirk->vdev = vdev;
218e99
-
218e99
-    memory_region_init_io(&quirk->mem, &vfio_ati_f10_quirk, quirk,
218e99
-                          "vfio-ati-f10-quirk", 8);
218e99
-    memory_region_add_subregion_overlap(&vdev->bars[nr].mem, 0, &quirk->mem, 1);
218e99
+    quirk->data.flags = quirk->data.read_flags = quirk->data.write_flags = 1;
218e99
+    quirk->data.address_match = 0x4000;
218e99
+    quirk->data.address_mask = PCIE_CONFIG_SPACE_SIZE - 1;
218e99
+    quirk->data.bar = nr;
218e99
+
218e99
+    memory_region_init_io(&quirk->mem, &vfio_generic_quirk, quirk,
218e99
+                          "vfio-ati-bar2-4000-quirk",
218e99
+                          TARGET_PAGE_ALIGN(quirk->data.address_mask + 1));
218e99
+    memory_region_add_subregion_overlap(&vdev->bars[nr].mem,
218e99
+                          quirk->data.address_match & TARGET_PAGE_MASK,
218e99
+                          &quirk->mem, 1);
218e99
 
218e99
     QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next);
218e99
 
218e99
-    DPRINTF("Enabled ATI/AMD quirk 0xf10 for device %04x:%02x:%02x.%x\n",
218e99
+    DPRINTF("Enabled ATI/AMD BAR2 0x4000 quirk for device %04x:%02x:%02x.%x\n",
218e99
             vdev->host.domain, vdev->host.bus, vdev->host.slot,
218e99
             vdev->host.function);
218e99
 }
218e99
 
218e99
+/*
218e99
+ * Older ATI/AMD cards like the X550 have a similar window to that above.
218e99
+ * I/O port BAR1 provides a window to a mirror of PCI config space located
218e99
+ * in BAR2 at offset 0xf00.  We don't care to support such older cards, but
218e99
+ * note it for future reference.
218e99
+ */
218e99
+
218e99
 #define PCI_VENDOR_ID_NVIDIA                    0x10de
218e99
 
218e99
 /*
218e99
@@ -1362,7 +1443,7 @@ static void vfio_probe_ati_f10_quirk(VFIODevice *vdev, int nr)
218e99
  * that use the I/O port BAR5 window but it doesn't hurt to leave it.
218e99
  */
218e99
 enum {
218e99
-    NV_3D0_NONE,
218e99
+    NV_3D0_NONE = 0,
218e99
     NV_3D0_SELECT,
218e99
     NV_3D0_WINDOW,
218e99
     NV_3D0_READ,
218e99
@@ -1376,14 +1457,14 @@ static uint64_t vfio_nvidia_3d0_quirk_read(void *opaque,
218e99
     VFIODevice *vdev = quirk->vdev;
218e99
     PCIDevice *pdev = &vdev->pdev;
218e99
     uint64_t data = vfio_vga_read(&vdev->vga.region[QEMU_PCI_VGA_IO_HI],
218e99
-                                  addr + 0x10, size);
218e99
+                                  addr + quirk->data.base_offset, size);
218e99
 
218e99
-    if (quirk->data == NV_3D0_READ && addr == 0) {
218e99
-        data = vfio_pci_read_config(pdev, quirk->data2, size);
218e99
+    if (quirk->data.flags == NV_3D0_READ && addr == quirk->data.data_offset) {
218e99
+        data = vfio_pci_read_config(pdev, quirk->data.address_val, size);
218e99
         DPRINTF("%s(0x3d0, %d) = 0x%"PRIx64"\n", __func__, size, data);
218e99
     }
218e99
 
218e99
-    quirk->data = NV_3D0_NONE;
218e99
+    quirk->data.flags = NV_3D0_NONE;
218e99
 
218e99
     return data;
218e99
 }
218e99
@@ -1395,43 +1476,42 @@ static void vfio_nvidia_3d0_quirk_write(void *opaque, hwaddr addr,
218e99
     VFIODevice *vdev = quirk->vdev;
218e99
     PCIDevice *pdev = &vdev->pdev;
218e99
 
218e99
-    switch (quirk->data) {
218e99
+    switch (quirk->data.flags) {
218e99
     case NV_3D0_NONE:
218e99
-        if (addr == 4 && data == 0x338) {
218e99
-            quirk->data = NV_3D0_SELECT;
218e99
+        if (addr == quirk->data.address_offset && data == 0x338) {
218e99
+            quirk->data.flags = NV_3D0_SELECT;
218e99
         }
218e99
         break;
218e99
     case NV_3D0_SELECT:
218e99
-        quirk->data = NV_3D0_NONE;
218e99
-        if (addr == 0 && (data & ~0xff) == 0x1800) {
218e99
-            quirk->data = NV_3D0_WINDOW;
218e99
-            quirk->data2 = data & 0xff;
218e99
+        quirk->data.flags = NV_3D0_NONE;
218e99
+        if (addr == quirk->data.data_offset &&
218e99
+            (data & ~quirk->data.address_mask) == quirk->data.address_match) {
218e99
+            quirk->data.flags = NV_3D0_WINDOW;
218e99
+            quirk->data.address_val = data & quirk->data.address_mask;
218e99
         }
218e99
         break;
218e99
     case NV_3D0_WINDOW:
218e99
-        quirk->data = NV_3D0_NONE;
218e99
-        if (addr == 4) {
218e99
+        quirk->data.flags = NV_3D0_NONE;
218e99
+        if (addr == quirk->data.address_offset) {
218e99
             if (data == 0x538) {
218e99
-                quirk->data = NV_3D0_READ;
218e99
+                quirk->data.flags = NV_3D0_READ;
218e99
             } else if (data == 0x738) {
218e99
-                quirk->data = NV_3D0_WRITE;
218e99
+                quirk->data.flags = NV_3D0_WRITE;
218e99
             }
218e99
         }
218e99
         break;
218e99
     case NV_3D0_WRITE:
218e99
-        quirk->data = NV_3D0_NONE;
218e99
-        if (addr == 0) {
218e99
-            vfio_pci_write_config(pdev, quirk->data2, data, size);
218e99
+        quirk->data.flags = NV_3D0_NONE;
218e99
+        if (addr == quirk->data.data_offset) {
218e99
+            vfio_pci_write_config(pdev, quirk->data.address_val, data, size);
218e99
             DPRINTF("%s(0x3d0, 0x%"PRIx64", %d)\n", __func__, data, size);
218e99
             return;
218e99
         }
218e99
         break;
218e99
-    default:
218e99
-        quirk->data = NV_3D0_NONE;
218e99
     }
218e99
 
218e99
     vfio_vga_write(&vdev->vga.region[QEMU_PCI_VGA_IO_HI],
218e99
-                   addr + 0x10, data, size);
218e99
+                   addr + quirk->data.base_offset, data, size);
218e99
 }
218e99
 
218e99
 static const MemoryRegionOps vfio_nvidia_3d0_quirk = {
218e99
@@ -1452,11 +1532,18 @@ static void vfio_vga_probe_nvidia_3d0_quirk(VFIODevice *vdev)
218e99
 
218e99
     quirk = g_malloc0(sizeof(*quirk));
218e99
     quirk->vdev = vdev;
218e99
-
218e99
-    memory_region_init_io(&quirk->mem, &vfio_nvidia_3d0_quirk, quirk,
218e99
-                          "vfio-nvidia-3d0-quirk", 6);
218e99
+    quirk->data.base_offset = 0x10;
218e99
+    quirk->data.address_offset = 4;
218e99
+    quirk->data.address_size = 2;
218e99
+    quirk->data.address_match = 0x1800;
218e99
+    quirk->data.address_mask = PCI_CONFIG_SPACE_SIZE - 1;
218e99
+    quirk->data.data_offset = 0;
218e99
+    quirk->data.data_size = 4;
218e99
+
218e99
+    memory_region_init_io(&quirk->mem, &vfio_nvidia_3d0_quirk,
218e99
+                          quirk, "vfio-nvidia-3d0-quirk", 6);
218e99
     memory_region_add_subregion(&vdev->vga.region[QEMU_PCI_VGA_IO_HI].mem,
218e99
-                                0x10, &quirk->mem);
218e99
+                                quirk->data.base_offset, &quirk->mem);
218e99
 
218e99
     QLIST_INSERT_HEAD(&vdev->vga.region[QEMU_PCI_VGA_IO_HI].quirks,
218e99
                       quirk, next);
218e99
@@ -1480,76 +1567,46 @@ enum {
218e99
     NV_BAR5_VALID = 0x7,
218e99
 };
218e99
 
218e99
-static uint64_t vfio_nvidia_bar5_window_quirk_read(void *opaque,
218e99
-                                                   hwaddr addr, unsigned size)
218e99
-{
218e99
-    VFIOQuirk *quirk = opaque;
218e99
-    VFIODevice *vdev = quirk->vdev;
218e99
-    uint64_t data = vfio_bar_read(&vdev->bars[5], addr, size);
218e99
-
218e99
-    if (addr == 0xc && quirk->data == NV_BAR5_VALID) {
218e99
-        data = vfio_pci_read_config(&vdev->pdev, quirk->data2, size);
218e99
-        DPRINTF("%s(%04x:%02x:%02x.%x:BAR5+0x%"HWADDR_PRIx", %d) = 0x%"
218e99
-                PRIx64"\n", __func__, vdev->host.domain, vdev->host.bus,
218e99
-                vdev->host.slot, vdev->host.function, addr, size, data);
218e99
-    }
218e99
-
218e99
-    return data;
218e99
-}
218e99
-
218e99
 static void vfio_nvidia_bar5_window_quirk_write(void *opaque, hwaddr addr,
218e99
                                                 uint64_t data, unsigned size)
218e99
 {
218e99
     VFIOQuirk *quirk = opaque;
218e99
-    VFIODevice *vdev = quirk->vdev;
218e99
 
218e99
-    /*
218e99
-     * Use quirk->data to track enables and quirk->data2 for the offset
218e99
-     */
218e99
     switch (addr) {
218e99
     case 0x0:
218e99
         if (data & 0x1) {
218e99
-            quirk->data |= NV_BAR5_MASTER;
218e99
+            quirk->data.flags |= NV_BAR5_MASTER;
218e99
         } else {
218e99
-            quirk->data &= ~NV_BAR5_MASTER;
218e99
+            quirk->data.flags &= ~NV_BAR5_MASTER;
218e99
         }
218e99
         break;
218e99
     case 0x4:
218e99
         if (data & 0x1) {
218e99
-            quirk->data |= NV_BAR5_ENABLE;
218e99
+            quirk->data.flags |= NV_BAR5_ENABLE;
218e99
         } else {
218e99
-            quirk->data &= ~NV_BAR5_ENABLE;
218e99
+            quirk->data.flags &= ~NV_BAR5_ENABLE;
218e99
         }
218e99
         break;
218e99
     case 0x8:
218e99
-        if (quirk->data & NV_BAR5_MASTER) {
218e99
+        if (quirk->data.flags & NV_BAR5_MASTER) {
218e99
             if ((data & ~0xfff) == 0x88000) {
218e99
-                quirk->data |= NV_BAR5_ADDRESS;
218e99
-                quirk->data2 = data & 0xfff;
218e99
+                quirk->data.flags |= NV_BAR5_ADDRESS;
218e99
+                quirk->data.address_val = data & 0xfff;
218e99
             } else if ((data & ~0xff) == 0x1800) {
218e99
-                quirk->data |= NV_BAR5_ADDRESS;
218e99
-                quirk->data2 = data & 0xff;
218e99
+                quirk->data.flags |= NV_BAR5_ADDRESS;
218e99
+                quirk->data.address_val = data & 0xff;
218e99
             } else {
218e99
-                quirk->data &= ~NV_BAR5_ADDRESS;
218e99
+                quirk->data.flags &= ~NV_BAR5_ADDRESS;
218e99
             }
218e99
         }
218e99
         break;
218e99
-    case 0xc:
218e99
-        if (quirk->data == NV_BAR5_VALID) {
218e99
-            vfio_pci_write_config(&vdev->pdev, quirk->data2, data, size);
218e99
-            DPRINTF("%s(%04x:%02x:%02x.%x:BAR5+0x%"HWADDR_PRIx", 0x%"
218e99
-                    PRIx64", %d)\n", __func__, vdev->host.domain,
218e99
-                    vdev->host.bus, vdev->host.slot, vdev->host.function,
218e99
-                    addr, data, size);
218e99
-            return;
218e99
-        }
218e99
     }
218e99
 
218e99
-    vfio_bar_write(&vdev->bars[5], addr, data, size);
218e99
+    vfio_generic_window_quirk_write(opaque, addr, data, size);
218e99
 }
218e99
 
218e99
 static const MemoryRegionOps vfio_nvidia_bar5_window_quirk = {
218e99
-    .read = vfio_nvidia_bar5_window_quirk_read,
218e99
+    .read = vfio_generic_window_quirk_read,
218e99
     .write = vfio_nvidia_bar5_window_quirk_write,
218e99
     .valid.min_access_size = 4,
218e99
     .endianness = DEVICE_LITTLE_ENDIAN,
218e99
@@ -1567,8 +1624,15 @@ static void vfio_probe_nvidia_bar5_window_quirk(VFIODevice *vdev, int nr)
218e99
 
218e99
     quirk = g_malloc0(sizeof(*quirk));
218e99
     quirk->vdev = vdev;
218e99
-
218e99
-    memory_region_init_io(&quirk->mem, &vfio_nvidia_bar5_window_quirk, quirk,
218e99
+    quirk->data.read_flags = quirk->data.write_flags = NV_BAR5_VALID;
218e99
+    quirk->data.address_offset = 0x8;
218e99
+    quirk->data.address_size = 0; /* actually 4, but avoids generic code */
218e99
+    quirk->data.data_offset = 0xc;
218e99
+    quirk->data.data_size = 4;
218e99
+    quirk->data.bar = nr;
218e99
+
218e99
+    memory_region_init_io(&quirk->mem,
218e99
+                          &vfio_nvidia_bar5_window_quirk, quirk,
218e99
                           "vfio-nvidia-bar5-window-quirk", 16);
218e99
     memory_region_add_subregion_overlap(&vdev->bars[nr].mem, 0, &quirk->mem, 1);
218e99
 
218e99
@@ -1588,51 +1652,6 @@ static void vfio_probe_nvidia_bar5_window_quirk(VFIODevice *vdev, int nr)
218e99
  *
218e99
  * Here's offset 0x88000...
218e99
  */
218e99
-static uint64_t vfio_nvidia_bar0_88000_quirk_read(void *opaque,
218e99
-                                                  hwaddr addr, unsigned size)
218e99
-{
218e99
-    VFIOQuirk *quirk = opaque;
218e99
-    VFIODevice *vdev = quirk->vdev;
218e99
-    hwaddr base = 0x88000 & TARGET_PAGE_MASK;
218e99
-    hwaddr offset = 0x88000 & ~TARGET_PAGE_MASK;
218e99
-    uint64_t data = vfio_bar_read(&vdev->bars[0], addr + base, size);
218e99
-
218e99
-    if (ranges_overlap(addr, size, offset, PCI_CONFIG_SPACE_SIZE)) {
218e99
-        data = vfio_pci_read_config(&vdev->pdev, addr - offset, size);
218e99
-
218e99
-        DPRINTF("%s(%04x:%02x:%02x.%x:BAR0+0x%"HWADDR_PRIx", %d) = 0x%"
218e99
-                PRIx64"\n", __func__, vdev->host.domain, vdev->host.bus,
218e99
-                vdev->host.slot, vdev->host.function, addr + base, size, data);
218e99
-    }
218e99
-
218e99
-    return data;
218e99
-}
218e99
-
218e99
-static void vfio_nvidia_bar0_88000_quirk_write(void *opaque, hwaddr addr,
218e99
-                                               uint64_t data, unsigned size)
218e99
-{
218e99
-    VFIOQuirk *quirk = opaque;
218e99
-    VFIODevice *vdev = quirk->vdev;
218e99
-    hwaddr base = 0x88000 & TARGET_PAGE_MASK;
218e99
-    hwaddr offset = 0x88000 & ~TARGET_PAGE_MASK;
218e99
-
218e99
-    if (ranges_overlap(addr, size, offset, PCI_CONFIG_SPACE_SIZE)) {
218e99
-        vfio_pci_write_config(&vdev->pdev, addr - offset, data, size);
218e99
-
218e99
-        DPRINTF("%s(%04x:%02x:%02x.%x:BAR0+0x%"HWADDR_PRIx", 0x%"
218e99
-                PRIx64", %d)\n", __func__, vdev->host.domain, vdev->host.bus,
218e99
-                vdev->host.slot, vdev->host.function, addr + base, data, size);
218e99
-    } else {
218e99
-        vfio_bar_write(&vdev->bars[0], addr + base, data, size);
218e99
-    }
218e99
-}
218e99
-
218e99
-static const MemoryRegionOps vfio_nvidia_bar0_88000_quirk = {
218e99
-    .read = vfio_nvidia_bar0_88000_quirk_read,
218e99
-    .write = vfio_nvidia_bar0_88000_quirk_write,
218e99
-    .endianness = DEVICE_LITTLE_ENDIAN,
218e99
-};
218e99
-
218e99
 static void vfio_probe_nvidia_bar0_88000_quirk(VFIODevice *vdev, int nr)
218e99
 {
218e99
     PCIDevice *pdev = &vdev->pdev;
218e99
@@ -1645,13 +1664,17 @@ static void vfio_probe_nvidia_bar0_88000_quirk(VFIODevice *vdev, int nr)
218e99
 
218e99
     quirk = g_malloc0(sizeof(*quirk));
218e99
     quirk->vdev = vdev;
218e99
-
218e99
-    memory_region_init_io(&quirk->mem, &vfio_nvidia_bar0_88000_quirk, quirk,
218e99
-                          "vfio-nvidia-bar0-88000-quirk",
218e99
-                          TARGET_PAGE_ALIGN(PCIE_CONFIG_SPACE_SIZE));
218e99
+    quirk->data.flags = quirk->data.read_flags = quirk->data.write_flags = 1;
218e99
+    quirk->data.address_match = 0x88000;
218e99
+    quirk->data.address_mask = PCIE_CONFIG_SPACE_SIZE - 1;
218e99
+    quirk->data.bar = nr;
218e99
+
218e99
+    memory_region_init_io(&quirk->mem, &vfio_generic_quirk,
218e99
+                          quirk, "vfio-nvidia-bar0-88000-quirk",
218e99
+                          TARGET_PAGE_ALIGN(quirk->data.address_mask + 1));
218e99
     memory_region_add_subregion_overlap(&vdev->bars[nr].mem,
218e99
-                                        0x88000 & TARGET_PAGE_MASK,
218e99
-                                        &quirk->mem, 1);
218e99
+                          quirk->data.address_match & TARGET_PAGE_MASK,
218e99
+                          &quirk->mem, 1);
218e99
 
218e99
     QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next);
218e99
 
218e99
@@ -1663,51 +1686,6 @@ static void vfio_probe_nvidia_bar0_88000_quirk(VFIODevice *vdev, int nr)
218e99
 /*
218e99
  * And here's the same for BAR0 offset 0x1800...
218e99
  */
218e99
-static uint64_t vfio_nvidia_bar0_1800_quirk_read(void *opaque,
218e99
-                                                 hwaddr addr, unsigned size)
218e99
-{
218e99
-    VFIOQuirk *quirk = opaque;
218e99
-    VFIODevice *vdev = quirk->vdev;
218e99
-    hwaddr base = 0x1800 & TARGET_PAGE_MASK;
218e99
-    hwaddr offset = 0x1800 & ~TARGET_PAGE_MASK;
218e99
-    uint64_t data = vfio_bar_read(&vdev->bars[0], addr + base, size);
218e99
-
218e99
-    if (ranges_overlap(addr, size, offset, PCI_CONFIG_SPACE_SIZE)) {
218e99
-        data = vfio_pci_read_config(&vdev->pdev, addr - offset, size);
218e99
-
218e99
-        DPRINTF("%s(%04x:%02x:%02x.%x:BAR0+0x%"HWADDR_PRIx", %d) = 0x%"
218e99
-                PRIx64"\n", __func__, vdev->host.domain, vdev->host.bus,
218e99
-                vdev->host.slot, vdev->host.function, addr + base, size, data);
218e99
-    }
218e99
-
218e99
-    return data;
218e99
-}
218e99
-
218e99
-static void vfio_nvidia_bar0_1800_quirk_write(void *opaque, hwaddr addr,
218e99
-                                              uint64_t data, unsigned size)
218e99
-{
218e99
-    VFIOQuirk *quirk = opaque;
218e99
-    VFIODevice *vdev = quirk->vdev;
218e99
-    hwaddr base = 0x1800 & TARGET_PAGE_MASK;
218e99
-    hwaddr offset = 0x1800 & ~TARGET_PAGE_MASK;
218e99
-
218e99
-    if (ranges_overlap(addr, size, offset, PCI_CONFIG_SPACE_SIZE)) {
218e99
-        vfio_pci_write_config(&vdev->pdev, addr - offset, data, size);
218e99
-
218e99
-        DPRINTF("%s(%04x:%02x:%02x.%x:BAR0+0x%"HWADDR_PRIx", 0x%"
218e99
-                PRIx64", %d)\n", __func__, vdev->host.domain, vdev->host.bus,
218e99
-                vdev->host.slot, vdev->host.function, addr + base, data, size);
218e99
-    } else {
218e99
-        vfio_bar_write(&vdev->bars[0], addr + base, data, size);
218e99
-    }
218e99
-}
218e99
-
218e99
-static const MemoryRegionOps vfio_nvidia_bar0_1800_quirk = {
218e99
-    .read = vfio_nvidia_bar0_1800_quirk_read,
218e99
-    .write = vfio_nvidia_bar0_1800_quirk_write,
218e99
-    .endianness = DEVICE_LITTLE_ENDIAN,
218e99
-};
218e99
-
218e99
 static void vfio_probe_nvidia_bar0_1800_quirk(VFIODevice *vdev, int nr)
218e99
 {
218e99
     PCIDevice *pdev = &vdev->pdev;
218e99
@@ -1724,13 +1702,17 @@ static void vfio_probe_nvidia_bar0_1800_quirk(VFIODevice *vdev, int nr)
218e99
 
218e99
     quirk = g_malloc0(sizeof(*quirk));
218e99
     quirk->vdev = vdev;
218e99
+    quirk->data.flags = quirk->data.read_flags = quirk->data.write_flags = 1;
218e99
+    quirk->data.address_match = 0x1800;
218e99
+    quirk->data.address_mask = PCI_CONFIG_SPACE_SIZE - 1;
218e99
+    quirk->data.bar = nr;
218e99
 
218e99
-    memory_region_init_io(&quirk->mem, &vfio_nvidia_bar0_1800_quirk, quirk,
218e99
+    memory_region_init_io(&quirk->mem, &vfio_generic_quirk, quirk,
218e99
                           "vfio-nvidia-bar0-1800-quirk",
218e99
-                          TARGET_PAGE_ALIGN(PCI_CONFIG_SPACE_SIZE));
218e99
+                          TARGET_PAGE_ALIGN(quirk->data.address_mask + 1));
218e99
     memory_region_add_subregion_overlap(&vdev->bars[nr].mem,
218e99
-                                        0x1800 & TARGET_PAGE_MASK,
218e99
-                                        &quirk->mem, 1);
218e99
+                          quirk->data.address_match & TARGET_PAGE_MASK,
218e99
+                          &quirk->mem, 1);
218e99
 
218e99
     QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next);
218e99
 
218e99
@@ -1770,8 +1752,8 @@ static void vfio_vga_quirk_teardown(VFIODevice *vdev)
218e99
 
218e99
 static void vfio_bar_quirk_setup(VFIODevice *vdev, int nr)
218e99
 {
218e99
-    vfio_probe_ati_4010_quirk(vdev, nr);
218e99
-    vfio_probe_ati_f10_quirk(vdev, nr);
218e99
+    vfio_probe_ati_bar4_window_quirk(vdev, nr);
218e99
+    vfio_probe_ati_bar2_4000_quirk(vdev, nr);
218e99
     vfio_probe_nvidia_bar5_window_quirk(vdev, nr);
218e99
     vfio_probe_nvidia_bar0_88000_quirk(vdev, nr);
218e99
     vfio_probe_nvidia_bar0_1800_quirk(vdev, nr);
218e99
@@ -2267,11 +2249,14 @@ static void vfio_map_bar(VFIODevice *vdev, int nr)
218e99
     }
218e99
 
218e99
     pci_bar = le32_to_cpu(pci_bar);
218e99
-    type = pci_bar & (pci_bar & PCI_BASE_ADDRESS_SPACE_IO ?
218e99
-           ~PCI_BASE_ADDRESS_IO_MASK : ~PCI_BASE_ADDRESS_MEM_MASK);
218e99
+    bar->ioport = (pci_bar & PCI_BASE_ADDRESS_SPACE_IO);
218e99
+    bar->mem64 = bar->ioport ? 0 : (pci_bar & PCI_BASE_ADDRESS_MEM_TYPE_64);
218e99
+    type = pci_bar & (bar->ioport ? ~PCI_BASE_ADDRESS_IO_MASK :
218e99
+                                    ~PCI_BASE_ADDRESS_MEM_MASK);
218e99
 
218e99
     /* A "slow" read/write mapping underlies all BARs */
218e99
-    memory_region_init_io(&bar->mem, &vfio_bar_ops, bar, name, size);
218e99
+    memory_region_init_io(&bar->mem, &vfio_bar_ops,
218e99
+                          bar, name, size);
218e99
     pci_register_bar(&vdev->pdev, nr, type, &bar->mem);
218e99
 
218e99
     /*
218e99
-- 
218e99
1.7.1
218e99