|
|
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 |
|