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