|
|
9ae3a8 |
From a33e922436f708fe4881da4b6f363c49db5af581 Mon Sep 17 00:00:00 2001
|
|
|
9ae3a8 |
From: Alex Williamson <alex.williamson@redhat.com>
|
|
|
9ae3a8 |
Date: Fri, 29 Sep 2017 21:46:02 +0200
|
|
|
9ae3a8 |
Subject: [PATCH 14/27] vfio: Generalize region support
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
RH-Author: Alex Williamson <alex.williamson@redhat.com>
|
|
|
9ae3a8 |
Message-id: <20170929214601.16765.68107.stgit@gimli.home>
|
|
|
9ae3a8 |
Patchwork-id: 76772
|
|
|
9ae3a8 |
O-Subject: [RHEL-7.5 qemu-kvm PATCH 14/16] vfio: Generalize region support
|
|
|
9ae3a8 |
Bugzilla: 1494181
|
|
|
9ae3a8 |
RH-Acked-by: Paolo Bonzini <pbonzini@redhat.com>
|
|
|
9ae3a8 |
RH-Acked-by: Auger Eric <eric.auger@redhat.com>
|
|
|
9ae3a8 |
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
Upstream: db0da029a1853d46c90a6c0790ce6ca77fd46ea3
|
|
|
9ae3a8 |
RHEL: MemoryRegions still destroyed from exitfn, so finalize is called
|
|
|
9ae3a8 |
immediately after exit with memory_region_destroy().
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
Both platform and PCI vfio drivers create a "slow", I/O memory region
|
|
|
9ae3a8 |
with one or more mmap memory regions overlayed when supported by the
|
|
|
9ae3a8 |
device. Generalize this to a set of common helpers in the core that
|
|
|
9ae3a8 |
pulls the region info from vfio, fills the region data, configures
|
|
|
9ae3a8 |
slow mapping, and adds helpers for comleting the mmap, enable/disable,
|
|
|
9ae3a8 |
and teardown. This can be immediately used by the PCI MSI-X code,
|
|
|
9ae3a8 |
which needs to mmap around the MSI-X vector table.
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
This also changes VFIORegion.mem to be dynamically allocated because
|
|
|
9ae3a8 |
otherwise we don't know how the caller has allocated VFIORegion and
|
|
|
9ae3a8 |
therefore don't know whether to unreference it to destroy the
|
|
|
9ae3a8 |
MemoryRegion or not.
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
|
|
|
9ae3a8 |
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
|
|
|
9ae3a8 |
---
|
|
|
9ae3a8 |
hw/misc/vfio.c | 360 +++++++++++++++++++++++++++++++++++++++------------------
|
|
|
9ae3a8 |
trace-events | 9 ++
|
|
|
9ae3a8 |
2 files changed, 258 insertions(+), 111 deletions(-)
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
diff --git a/hw/misc/vfio.c b/hw/misc/vfio.c
|
|
|
9ae3a8 |
index 57a0065..d634531 100644
|
|
|
9ae3a8 |
--- a/hw/misc/vfio.c
|
|
|
9ae3a8 |
+++ b/hw/misc/vfio.c
|
|
|
9ae3a8 |
@@ -39,6 +39,7 @@
|
|
|
9ae3a8 |
#include "qemu/range.h"
|
|
|
9ae3a8 |
#include "sysemu/kvm.h"
|
|
|
9ae3a8 |
#include "sysemu/sysemu.h"
|
|
|
9ae3a8 |
+#include "trace.h"
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
/* #define DEBUG_VFIO */
|
|
|
9ae3a8 |
#ifdef DEBUG_VFIO
|
|
|
9ae3a8 |
@@ -84,14 +85,21 @@ typedef struct VFIOQuirk {
|
|
|
9ae3a8 |
} data;
|
|
|
9ae3a8 |
} VFIOQuirk;
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
+typedef struct VFIOMmap {
|
|
|
9ae3a8 |
+ MemoryRegion mem;
|
|
|
9ae3a8 |
+ void *mmap;
|
|
|
9ae3a8 |
+ off_t offset;
|
|
|
9ae3a8 |
+ size_t size;
|
|
|
9ae3a8 |
+} VFIOMmap;
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
typedef struct VFIORegion {
|
|
|
9ae3a8 |
struct VFIODevice *vbasedev;
|
|
|
9ae3a8 |
off_t fd_offset; /* offset of region within device fd */
|
|
|
9ae3a8 |
- MemoryRegion mem; /* slow, read/write access */
|
|
|
9ae3a8 |
- MemoryRegion mmap_mem; /* direct mapped access */
|
|
|
9ae3a8 |
- void *mmap;
|
|
|
9ae3a8 |
+ MemoryRegion *mem; /* slow, read/write access */
|
|
|
9ae3a8 |
size_t size;
|
|
|
9ae3a8 |
uint32_t flags; /* VFIO region flags (rd/wr/mmap) */
|
|
|
9ae3a8 |
+ uint32_t nr_mmaps;
|
|
|
9ae3a8 |
+ VFIOMmap *mmaps;
|
|
|
9ae3a8 |
uint8_t nr; /* cache the region number for debug */
|
|
|
9ae3a8 |
} VFIORegion;
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
@@ -294,6 +302,9 @@ static void vfio_pci_write_config(PCIDevice *pdev, uint32_t addr,
|
|
|
9ae3a8 |
static void vfio_mmap_set_enabled(VFIOPCIDevice *vdev, bool enabled);
|
|
|
9ae3a8 |
static int vfio_get_region_info(VFIODevice *vbasedev, int index,
|
|
|
9ae3a8 |
struct vfio_region_info **info);
|
|
|
9ae3a8 |
+static void vfio_region_mmaps_set_enabled(VFIORegion *region, bool enabled);
|
|
|
9ae3a8 |
+static void vfio_region_exit(VFIORegion *region);
|
|
|
9ae3a8 |
+static void vfio_region_finalize(VFIORegion *region);
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
/*
|
|
|
9ae3a8 |
* Common VFIO interrupt disable
|
|
|
9ae3a8 |
@@ -1681,7 +1692,7 @@ static void vfio_probe_ati_bar4_window_quirk(VFIOPCIDevice *vdev, int nr)
|
|
|
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].region.mem,
|
|
|
9ae3a8 |
+ memory_region_add_subregion_overlap(vdev->bars[nr].region.mem,
|
|
|
9ae3a8 |
quirk->data.base_offset, &quirk->mem, 1);
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next);
|
|
|
9ae3a8 |
@@ -1714,7 +1725,7 @@ static void vfio_probe_ati_bar2_4000_quirk(VFIOPCIDevice *vdev, int nr)
|
|
|
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].region.mem,
|
|
|
9ae3a8 |
+ memory_region_add_subregion_overlap(vdev->bars[nr].region.mem,
|
|
|
9ae3a8 |
quirk->data.address_match & TARGET_PAGE_MASK,
|
|
|
9ae3a8 |
&quirk->mem, 1);
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
@@ -1939,7 +1950,7 @@ static void vfio_probe_nvidia_bar5_window_quirk(VFIOPCIDevice *vdev, int nr)
|
|
|
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].region.mem,
|
|
|
9ae3a8 |
+ memory_region_add_subregion_overlap(vdev->bars[nr].region.mem,
|
|
|
9ae3a8 |
0, &quirk->mem, 1);
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next);
|
|
|
9ae3a8 |
@@ -1977,7 +1988,7 @@ static void vfio_probe_nvidia_bar0_88000_quirk(VFIOPCIDevice *vdev, int nr)
|
|
|
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].region.mem,
|
|
|
9ae3a8 |
+ memory_region_add_subregion_overlap(vdev->bars[nr].region.mem,
|
|
|
9ae3a8 |
quirk->data.address_match & TARGET_PAGE_MASK,
|
|
|
9ae3a8 |
&quirk->mem, 1);
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
@@ -2015,7 +2026,7 @@ static void vfio_probe_nvidia_bar0_1800_quirk(VFIOPCIDevice *vdev, int nr)
|
|
|
9ae3a8 |
memory_region_init_io(&quirk->mem, &vfio_generic_quirk, quirk,
|
|
|
9ae3a8 |
"vfio-nvidia-bar0-1800-quirk",
|
|
|
9ae3a8 |
TARGET_PAGE_ALIGN(quirk->data.address_mask + 1));
|
|
|
9ae3a8 |
- memory_region_add_subregion_overlap(&vdev->bars[nr].region.mem,
|
|
|
9ae3a8 |
+ memory_region_add_subregion_overlap(vdev->bars[nr].region.mem,
|
|
|
9ae3a8 |
quirk->data.address_match & TARGET_PAGE_MASK,
|
|
|
9ae3a8 |
&quirk->mem, 1);
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
@@ -2070,7 +2081,7 @@ static void vfio_bar_quirk_teardown(VFIOPCIDevice *vdev, int nr)
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
while (!QLIST_EMPTY(&bar->quirks)) {
|
|
|
9ae3a8 |
VFIOQuirk *quirk = QLIST_FIRST(&bar->quirks);
|
|
|
9ae3a8 |
- memory_region_del_subregion(&bar->region.mem, &quirk->mem);
|
|
|
9ae3a8 |
+ memory_region_del_subregion(bar->region.mem, &quirk->mem);
|
|
|
9ae3a8 |
memory_region_destroy(&quirk->mem);
|
|
|
9ae3a8 |
QLIST_REMOVE(quirk, next);
|
|
|
9ae3a8 |
g_free(quirk);
|
|
|
9ae3a8 |
@@ -2384,6 +2395,74 @@ static int vfio_setup_msi(VFIOPCIDevice *vdev, int pos)
|
|
|
9ae3a8 |
return 0;
|
|
|
9ae3a8 |
}
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
+static void vfio_pci_fixup_msix_region(VFIOPCIDevice *vdev)
|
|
|
9ae3a8 |
+{
|
|
|
9ae3a8 |
+ off_t start, end;
|
|
|
9ae3a8 |
+ VFIORegion *region = &vdev->bars[vdev->msix->table_bar].region;
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+ /*
|
|
|
9ae3a8 |
+ * We expect to find a single mmap covering the whole BAR, anything else
|
|
|
9ae3a8 |
+ * means it's either unsupported or already setup.
|
|
|
9ae3a8 |
+ */
|
|
|
9ae3a8 |
+ if (region->nr_mmaps != 1 || region->mmaps[0].offset ||
|
|
|
9ae3a8 |
+ region->size != region->mmaps[0].size) {
|
|
|
9ae3a8 |
+ return;
|
|
|
9ae3a8 |
+ }
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+ /* MSI-X table start and end aligned to host page size */
|
|
|
9ae3a8 |
+ start = vdev->msix->table_offset & TARGET_PAGE_MASK;
|
|
|
9ae3a8 |
+ end = TARGET_PAGE_ALIGN((uint64_t)vdev->msix->table_offset +
|
|
|
9ae3a8 |
+ (vdev->msix->entries * PCI_MSIX_ENTRY_SIZE));
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+ /*
|
|
|
9ae3a8 |
+ * Does the MSI-X table cover the beginning of the BAR? The whole BAR?
|
|
|
9ae3a8 |
+ * NB - Host page size is necessarily a power of two and so is the PCI
|
|
|
9ae3a8 |
+ * BAR (not counting EA yet), therefore if we have host page aligned
|
|
|
9ae3a8 |
+ * @start and @end, then any remainder of the BAR before or after those
|
|
|
9ae3a8 |
+ * must be at least host page sized and therefore mmap'able.
|
|
|
9ae3a8 |
+ */
|
|
|
9ae3a8 |
+ if (!start) {
|
|
|
9ae3a8 |
+ if (end >= region->size) {
|
|
|
9ae3a8 |
+ region->nr_mmaps = 0;
|
|
|
9ae3a8 |
+ g_free(region->mmaps);
|
|
|
9ae3a8 |
+ region->mmaps = NULL;
|
|
|
9ae3a8 |
+ trace_vfio_msix_fixup(vdev->vbasedev.name,
|
|
|
9ae3a8 |
+ vdev->msix->table_bar, 0, 0);
|
|
|
9ae3a8 |
+ } else {
|
|
|
9ae3a8 |
+ region->mmaps[0].offset = end;
|
|
|
9ae3a8 |
+ region->mmaps[0].size = region->size - end;
|
|
|
9ae3a8 |
+ trace_vfio_msix_fixup(vdev->vbasedev.name,
|
|
|
9ae3a8 |
+ vdev->msix->table_bar, region->mmaps[0].offset,
|
|
|
9ae3a8 |
+ region->mmaps[0].offset + region->mmaps[0].size);
|
|
|
9ae3a8 |
+ }
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+ /* Maybe it's aligned at the end of the BAR */
|
|
|
9ae3a8 |
+ } else if (end >= region->size) {
|
|
|
9ae3a8 |
+ region->mmaps[0].size = start;
|
|
|
9ae3a8 |
+ trace_vfio_msix_fixup(vdev->vbasedev.name,
|
|
|
9ae3a8 |
+ vdev->msix->table_bar, region->mmaps[0].offset,
|
|
|
9ae3a8 |
+ region->mmaps[0].offset + region->mmaps[0].size);
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+ /* Otherwise it must split the BAR */
|
|
|
9ae3a8 |
+ } else {
|
|
|
9ae3a8 |
+ region->nr_mmaps = 2;
|
|
|
9ae3a8 |
+ region->mmaps = g_renew(VFIOMmap, region->mmaps, 2);
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+ memcpy(®ion->mmaps[1], ®ion->mmaps[0], sizeof(VFIOMmap));
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+ region->mmaps[0].size = start;
|
|
|
9ae3a8 |
+ trace_vfio_msix_fixup(vdev->vbasedev.name,
|
|
|
9ae3a8 |
+ vdev->msix->table_bar, region->mmaps[0].offset,
|
|
|
9ae3a8 |
+ region->mmaps[0].offset + region->mmaps[0].size);
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+ region->mmaps[1].offset = end;
|
|
|
9ae3a8 |
+ region->mmaps[1].size = region->size - end;
|
|
|
9ae3a8 |
+ trace_vfio_msix_fixup(vdev->vbasedev.name,
|
|
|
9ae3a8 |
+ vdev->msix->table_bar, region->mmaps[1].offset,
|
|
|
9ae3a8 |
+ region->mmaps[1].offset + region->mmaps[1].size);
|
|
|
9ae3a8 |
+ }
|
|
|
9ae3a8 |
+}
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
/*
|
|
|
9ae3a8 |
* We don't have any control over how pci_add_capability() inserts
|
|
|
9ae3a8 |
* capabilities into the chain. In order to setup MSI-X we need a
|
|
|
9ae3a8 |
@@ -2461,6 +2540,8 @@ static int vfio_early_setup_msix(VFIOPCIDevice *vdev)
|
|
|
9ae3a8 |
}
|
|
|
9ae3a8 |
}
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
+ vfio_pci_fixup_msix_region(vdev);
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
return 0;
|
|
|
9ae3a8 |
}
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
@@ -2469,9 +2550,9 @@ static int vfio_setup_msix(VFIOPCIDevice *vdev, int pos)
|
|
|
9ae3a8 |
int ret;
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
ret = msix_init(&vdev->pdev, vdev->msix->entries,
|
|
|
9ae3a8 |
- &vdev->bars[vdev->msix->table_bar].region.mem,
|
|
|
9ae3a8 |
+ vdev->bars[vdev->msix->table_bar].region.mem,
|
|
|
9ae3a8 |
vdev->msix->table_bar, vdev->msix->table_offset,
|
|
|
9ae3a8 |
- &vdev->bars[vdev->msix->pba_bar].region.mem,
|
|
|
9ae3a8 |
+ vdev->bars[vdev->msix->pba_bar].region.mem,
|
|
|
9ae3a8 |
vdev->msix->pba_bar, vdev->msix->pba_offset, pos);
|
|
|
9ae3a8 |
if (ret < 0) {
|
|
|
9ae3a8 |
if (ret == -ENOTSUP) {
|
|
|
9ae3a8 |
@@ -2490,8 +2571,8 @@ static void vfio_teardown_msi(VFIOPCIDevice *vdev)
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
if (vdev->msix) {
|
|
|
9ae3a8 |
msix_uninit(&vdev->pdev,
|
|
|
9ae3a8 |
- &vdev->bars[vdev->msix->table_bar].region.mem,
|
|
|
9ae3a8 |
- &vdev->bars[vdev->msix->pba_bar].region.mem);
|
|
|
9ae3a8 |
+ vdev->bars[vdev->msix->table_bar].region.mem,
|
|
|
9ae3a8 |
+ vdev->bars[vdev->msix->pba_bar].region.mem);
|
|
|
9ae3a8 |
}
|
|
|
9ae3a8 |
}
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
@@ -2503,16 +2584,7 @@ static void vfio_mmap_set_enabled(VFIOPCIDevice *vdev, bool enabled)
|
|
|
9ae3a8 |
int i;
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
for (i = 0; i < PCI_ROM_SLOT; i++) {
|
|
|
9ae3a8 |
- VFIOBAR *bar = &vdev->bars[i];
|
|
|
9ae3a8 |
-
|
|
|
9ae3a8 |
- if (!bar->region.size) {
|
|
|
9ae3a8 |
- continue;
|
|
|
9ae3a8 |
- }
|
|
|
9ae3a8 |
-
|
|
|
9ae3a8 |
- memory_region_set_enabled(&bar->region.mmap_mem, enabled);
|
|
|
9ae3a8 |
- if (vdev->msix && vdev->msix->table_bar == i) {
|
|
|
9ae3a8 |
- memory_region_set_enabled(&vdev->msix->mmap_mem, enabled);
|
|
|
9ae3a8 |
- }
|
|
|
9ae3a8 |
+ vfio_region_mmaps_set_enabled(&vdev->bars[i].region, enabled);
|
|
|
9ae3a8 |
}
|
|
|
9ae3a8 |
}
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
@@ -2526,65 +2598,171 @@ static void vfio_unmap_bar(VFIOPCIDevice *vdev, int nr)
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
vfio_bar_quirk_teardown(vdev, nr);
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
- memory_region_del_subregion(&bar->region.mem, &bar->region.mmap_mem);
|
|
|
9ae3a8 |
- munmap(bar->region.mmap, memory_region_size(&bar->region.mmap_mem));
|
|
|
9ae3a8 |
- memory_region_destroy(&bar->region.mmap_mem);
|
|
|
9ae3a8 |
+ vfio_region_exit(&bar->region);
|
|
|
9ae3a8 |
+ vfio_region_finalize(&bar->region);
|
|
|
9ae3a8 |
+}
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+static int vfio_region_setup(Object *obj, VFIODevice *vbasedev,
|
|
|
9ae3a8 |
+ VFIORegion *region, int index, const char *name)
|
|
|
9ae3a8 |
+{
|
|
|
9ae3a8 |
+ struct vfio_region_info *info;
|
|
|
9ae3a8 |
+ int ret;
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+ ret = vfio_get_region_info(vbasedev, index, &info;;
|
|
|
9ae3a8 |
+ if (ret) {
|
|
|
9ae3a8 |
+ return ret;
|
|
|
9ae3a8 |
+ }
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+ region->vbasedev = vbasedev;
|
|
|
9ae3a8 |
+ region->flags = info->flags;
|
|
|
9ae3a8 |
+ region->size = info->size;
|
|
|
9ae3a8 |
+ region->fd_offset = info->offset;
|
|
|
9ae3a8 |
+ region->nr = index;
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
- if (vdev->msix && vdev->msix->table_bar == nr) {
|
|
|
9ae3a8 |
- memory_region_del_subregion(&bar->region.mem, &vdev->msix->mmap_mem);
|
|
|
9ae3a8 |
- munmap(vdev->msix->mmap, memory_region_size(&vdev->msix->mmap_mem));
|
|
|
9ae3a8 |
- memory_region_destroy(&vdev->msix->mmap_mem);
|
|
|
9ae3a8 |
+ if (region->size) {
|
|
|
9ae3a8 |
+ region->mem = g_new0(MemoryRegion, 1);
|
|
|
9ae3a8 |
+ memory_region_init_io(region->mem, &vfio_region_ops,
|
|
|
9ae3a8 |
+ region, name, region->size);
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+ if (VFIO_ALLOW_MMAP &&
|
|
|
9ae3a8 |
+ region->flags & VFIO_REGION_INFO_FLAG_MMAP &&
|
|
|
9ae3a8 |
+ !(region->size & ~TARGET_PAGE_MASK)) {
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+ region->nr_mmaps = 1;
|
|
|
9ae3a8 |
+ region->mmaps = g_new0(VFIOMmap, region->nr_mmaps);
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+ region->mmaps[0].offset = 0;
|
|
|
9ae3a8 |
+ region->mmaps[0].size = region->size;
|
|
|
9ae3a8 |
+ }
|
|
|
9ae3a8 |
}
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
- memory_region_destroy(&bar->region.mem);
|
|
|
9ae3a8 |
+ g_free(info);
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+ trace_vfio_region_setup(vbasedev->name, index, name,
|
|
|
9ae3a8 |
+ region->flags, region->fd_offset, region->size);
|
|
|
9ae3a8 |
+ return 0;
|
|
|
9ae3a8 |
}
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
-static int vfio_mmap_region(Object *obj, VFIORegion *region,
|
|
|
9ae3a8 |
- MemoryRegion *mem, MemoryRegion *submem,
|
|
|
9ae3a8 |
- void **map, size_t size, off_t offset,
|
|
|
9ae3a8 |
- const char *name)
|
|
|
9ae3a8 |
+static int vfio_region_mmap(VFIORegion *region)
|
|
|
9ae3a8 |
{
|
|
|
9ae3a8 |
- int ret = 0;
|
|
|
9ae3a8 |
- VFIODevice *vbasedev = region->vbasedev;
|
|
|
9ae3a8 |
+ int i, prot = 0;
|
|
|
9ae3a8 |
+ char *name;
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+ if (!region->mem) {
|
|
|
9ae3a8 |
+ return 0;
|
|
|
9ae3a8 |
+ }
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+ prot |= region->flags & VFIO_REGION_INFO_FLAG_READ ? PROT_READ : 0;
|
|
|
9ae3a8 |
+ prot |= region->flags & VFIO_REGION_INFO_FLAG_WRITE ? PROT_WRITE : 0;
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+ for (i = 0; i < region->nr_mmaps; i++) {
|
|
|
9ae3a8 |
+ region->mmaps[i].mmap = mmap(NULL, region->mmaps[i].size, prot,
|
|
|
9ae3a8 |
+ MAP_SHARED, region->vbasedev->fd,
|
|
|
9ae3a8 |
+ region->fd_offset +
|
|
|
9ae3a8 |
+ region->mmaps[i].offset);
|
|
|
9ae3a8 |
+ if (region->mmaps[i].mmap == MAP_FAILED) {
|
|
|
9ae3a8 |
+ int ret = -errno;
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
- if (VFIO_ALLOW_MMAP && size && region->flags &
|
|
|
9ae3a8 |
- VFIO_REGION_INFO_FLAG_MMAP) {
|
|
|
9ae3a8 |
- int prot = 0;
|
|
|
9ae3a8 |
+ trace_vfio_region_mmap_fault(memory_region_name(region->mem), i,
|
|
|
9ae3a8 |
+ region->fd_offset +
|
|
|
9ae3a8 |
+ region->mmaps[i].offset,
|
|
|
9ae3a8 |
+ region->fd_offset +
|
|
|
9ae3a8 |
+ region->mmaps[i].offset +
|
|
|
9ae3a8 |
+ region->mmaps[i].size - 1, ret);
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
- if (region->flags & VFIO_REGION_INFO_FLAG_READ) {
|
|
|
9ae3a8 |
- prot |= PROT_READ;
|
|
|
9ae3a8 |
+ region->mmaps[i].mmap = NULL;
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+ for (i--; i >= 0; i--) {
|
|
|
9ae3a8 |
+ memory_region_del_subregion(region->mem, ®ion->mmaps[i].mem);
|
|
|
9ae3a8 |
+ munmap(region->mmaps[i].mmap, region->mmaps[i].size);
|
|
|
9ae3a8 |
+ memory_region_destroy(®ion->mmaps[i].mem);
|
|
|
9ae3a8 |
+ region->mmaps[i].mmap = NULL;
|
|
|
9ae3a8 |
+ }
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+ return ret;
|
|
|
9ae3a8 |
}
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
- if (region->flags & VFIO_REGION_INFO_FLAG_WRITE) {
|
|
|
9ae3a8 |
- prot |= PROT_WRITE;
|
|
|
9ae3a8 |
+ name = g_strdup_printf("%s mmaps[%d]",
|
|
|
9ae3a8 |
+ memory_region_name(region->mem), i);
|
|
|
9ae3a8 |
+ memory_region_init_ram_ptr(®ion->mmaps[i].mem,
|
|
|
9ae3a8 |
+ name, region->mmaps[i].size,
|
|
|
9ae3a8 |
+ region->mmaps[i].mmap);
|
|
|
9ae3a8 |
+ g_free(name);
|
|
|
9ae3a8 |
+ memory_region_set_skip_dump(®ion->mmaps[i].mem);
|
|
|
9ae3a8 |
+ memory_region_add_subregion(region->mem, region->mmaps[i].offset,
|
|
|
9ae3a8 |
+ ®ion->mmaps[i].mem);
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+ trace_vfio_region_mmap(memory_region_name(®ion->mmaps[i].mem),
|
|
|
9ae3a8 |
+ region->mmaps[i].offset,
|
|
|
9ae3a8 |
+ region->mmaps[i].offset +
|
|
|
9ae3a8 |
+ region->mmaps[i].size - 1);
|
|
|
9ae3a8 |
+ }
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+ return 0;
|
|
|
9ae3a8 |
+}
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+static void vfio_region_exit(VFIORegion *region)
|
|
|
9ae3a8 |
+{
|
|
|
9ae3a8 |
+ int i;
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+ if (!region->mem) {
|
|
|
9ae3a8 |
+ return;
|
|
|
9ae3a8 |
+ }
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+ for (i = 0; i < region->nr_mmaps; i++) {
|
|
|
9ae3a8 |
+ if (region->mmaps[i].mmap) {
|
|
|
9ae3a8 |
+ memory_region_del_subregion(region->mem, ®ion->mmaps[i].mem);
|
|
|
9ae3a8 |
}
|
|
|
9ae3a8 |
+ }
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+ trace_vfio_region_exit(region->vbasedev->name, region->nr);
|
|
|
9ae3a8 |
+}
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+static void vfio_region_finalize(VFIORegion *region)
|
|
|
9ae3a8 |
+{
|
|
|
9ae3a8 |
+ int i;
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+ if (!region->mem) {
|
|
|
9ae3a8 |
+ return;
|
|
|
9ae3a8 |
+ }
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
- *map = mmap(NULL, size, prot, MAP_SHARED,
|
|
|
9ae3a8 |
- vbasedev->fd, region->fd_offset + offset);
|
|
|
9ae3a8 |
- if (*map == MAP_FAILED) {
|
|
|
9ae3a8 |
- *map = NULL;
|
|
|
9ae3a8 |
- ret = -errno;
|
|
|
9ae3a8 |
- goto empty_region;
|
|
|
9ae3a8 |
+ for (i = 0; i < region->nr_mmaps; i++) {
|
|
|
9ae3a8 |
+ if (region->mmaps[i].mmap) {
|
|
|
9ae3a8 |
+ munmap(region->mmaps[i].mmap, region->mmaps[i].size);
|
|
|
9ae3a8 |
+ memory_region_destroy(®ion->mmaps[i].mem);
|
|
|
9ae3a8 |
}
|
|
|
9ae3a8 |
+ }
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
- memory_region_init_ram_ptr(submem, name, size, *map);
|
|
|
9ae3a8 |
- memory_region_set_skip_dump(submem);
|
|
|
9ae3a8 |
- } else {
|
|
|
9ae3a8 |
-empty_region:
|
|
|
9ae3a8 |
- /* Create a zero sized sub-region to make cleanup easy. */
|
|
|
9ae3a8 |
- memory_region_init(submem, name, 0);
|
|
|
9ae3a8 |
+ memory_region_destroy(region->mem);
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+ g_free(region->mem);
|
|
|
9ae3a8 |
+ g_free(region->mmaps);
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+ trace_vfio_region_finalize(region->vbasedev->name, region->nr);
|
|
|
9ae3a8 |
+}
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+static void vfio_region_mmaps_set_enabled(VFIORegion *region, bool enabled)
|
|
|
9ae3a8 |
+{
|
|
|
9ae3a8 |
+ int i;
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+ if (!region->mem) {
|
|
|
9ae3a8 |
+ return;
|
|
|
9ae3a8 |
}
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
- memory_region_add_subregion(mem, offset, submem);
|
|
|
9ae3a8 |
+ for (i = 0; i < region->nr_mmaps; i++) {
|
|
|
9ae3a8 |
+ if (region->mmaps[i].mmap) {
|
|
|
9ae3a8 |
+ memory_region_set_enabled(®ion->mmaps[i].mem, enabled);
|
|
|
9ae3a8 |
+ }
|
|
|
9ae3a8 |
+ }
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
- return ret;
|
|
|
9ae3a8 |
+ trace_vfio_region_mmaps_set_enabled(memory_region_name(region->mem),
|
|
|
9ae3a8 |
+ enabled);
|
|
|
9ae3a8 |
}
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
static void vfio_map_bar(VFIOPCIDevice *vdev, int nr)
|
|
|
9ae3a8 |
{
|
|
|
9ae3a8 |
VFIOBAR *bar = &vdev->bars[nr];
|
|
|
9ae3a8 |
uint64_t size = bar->region.size;
|
|
|
9ae3a8 |
- char name[64];
|
|
|
9ae3a8 |
uint32_t pci_bar;
|
|
|
9ae3a8 |
uint8_t type;
|
|
|
9ae3a8 |
int ret;
|
|
|
9ae3a8 |
@@ -2594,8 +2772,6 @@ static void vfio_map_bar(VFIOPCIDevice *vdev, int nr)
|
|
|
9ae3a8 |
return;
|
|
|
9ae3a8 |
}
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
- snprintf(name, sizeof(name), "VFIO %s BAR %d", vdev->vbasedev.name, nr);
|
|
|
9ae3a8 |
-
|
|
|
9ae3a8 |
/* Determine what type of BAR this is for registration */
|
|
|
9ae3a8 |
ret = pread(vdev->vbasedev.fd, &pci_bar, sizeof(pci_bar),
|
|
|
9ae3a8 |
vdev->config_offset + PCI_BASE_ADDRESS_0 + (4 * nr));
|
|
|
9ae3a8 |
@@ -2610,40 +2786,11 @@ static void vfio_map_bar(VFIOPCIDevice *vdev, int nr)
|
|
|
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->region.mem, &vfio_region_ops,
|
|
|
9ae3a8 |
- bar, name, size);
|
|
|
9ae3a8 |
- pci_register_bar(&vdev->pdev, nr, type, &bar->region.mem);
|
|
|
9ae3a8 |
-
|
|
|
9ae3a8 |
- /*
|
|
|
9ae3a8 |
- * We can't mmap areas overlapping the MSIX vector table, so we
|
|
|
9ae3a8 |
- * potentially insert a direct-mapped subregion before and after it.
|
|
|
9ae3a8 |
- */
|
|
|
9ae3a8 |
- if (vdev->msix && vdev->msix->table_bar == nr) {
|
|
|
9ae3a8 |
- size = vdev->msix->table_offset & TARGET_PAGE_MASK;
|
|
|
9ae3a8 |
- }
|
|
|
9ae3a8 |
-
|
|
|
9ae3a8 |
- strncat(name, " mmap", sizeof(name) - strlen(name) - 1);
|
|
|
9ae3a8 |
- if (vfio_mmap_region(OBJECT(vdev), &bar->region, &bar->region.mem,
|
|
|
9ae3a8 |
- &bar->region.mmap_mem, &bar->region.mmap,
|
|
|
9ae3a8 |
- size, 0, name)) {
|
|
|
9ae3a8 |
- error_report("%s unsupported. Performance may be slow", name);
|
|
|
9ae3a8 |
- }
|
|
|
9ae3a8 |
-
|
|
|
9ae3a8 |
- if (vdev->msix && vdev->msix->table_bar == nr) {
|
|
|
9ae3a8 |
- uint64_t start;
|
|
|
9ae3a8 |
+ pci_register_bar(&vdev->pdev, nr, type, bar->region.mem);
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
- start = TARGET_PAGE_ALIGN((uint64_t)vdev->msix->table_offset +
|
|
|
9ae3a8 |
- (vdev->msix->entries * PCI_MSIX_ENTRY_SIZE));
|
|
|
9ae3a8 |
-
|
|
|
9ae3a8 |
- size = start < bar->region.size ? bar->region.size - start : 0;
|
|
|
9ae3a8 |
- strncat(name, " msix-hi", sizeof(name) - strlen(name) - 1);
|
|
|
9ae3a8 |
- /* VFIOMSIXInfo contains another MemoryRegion for this mapping */
|
|
|
9ae3a8 |
- if (vfio_mmap_region(OBJECT(vdev), &bar->region, &bar->region.mem,
|
|
|
9ae3a8 |
- &vdev->msix->mmap_mem,
|
|
|
9ae3a8 |
- &vdev->msix->mmap, size, start, name)) {
|
|
|
9ae3a8 |
- error_report("%s unsupported. Performance may be slow", name);
|
|
|
9ae3a8 |
- }
|
|
|
9ae3a8 |
+ if (vfio_region_mmap(&bar->region)) {
|
|
|
9ae3a8 |
+ error_report("Failed to mmap %s BAR %d. Performance may be slow",
|
|
|
9ae3a8 |
+ vdev->vbasedev.name, nr);
|
|
|
9ae3a8 |
}
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
vfio_bar_quirk_setup(vdev, nr);
|
|
|
9ae3a8 |
@@ -3531,25 +3678,18 @@ static int vfio_get_device(VFIOGroup *group, const char *name,
|
|
|
9ae3a8 |
}
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
for (i = VFIO_PCI_BAR0_REGION_INDEX; i < VFIO_PCI_ROM_REGION_INDEX; i++) {
|
|
|
9ae3a8 |
- ret = vfio_get_region_info(&vdev->vbasedev, i, ®_info);
|
|
|
9ae3a8 |
+ char *name = g_strdup_printf("%s BAR %d", vdev->vbasedev.name, i);
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+ ret = vfio_region_setup(OBJECT(vdev), &vdev->vbasedev,
|
|
|
9ae3a8 |
+ &vdev->bars[i].region, i, name);
|
|
|
9ae3a8 |
+ g_free(name);
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
if (ret) {
|
|
|
9ae3a8 |
error_report("vfio: Error getting region %d info: %m", i);
|
|
|
9ae3a8 |
goto error;
|
|
|
9ae3a8 |
}
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
- DPRINTF("Device %s region %d:\n", name, i);
|
|
|
9ae3a8 |
- DPRINTF(" size: 0x%lx, offset: 0x%lx, flags: 0x%lx\n",
|
|
|
9ae3a8 |
- (unsigned long)reg_info->size, (unsigned long)reg_info->offset,
|
|
|
9ae3a8 |
- (unsigned long)reg_info->flags);
|
|
|
9ae3a8 |
-
|
|
|
9ae3a8 |
- vdev->bars[i].region.vbasedev = &vdev->vbasedev;
|
|
|
9ae3a8 |
- vdev->bars[i].region.flags = reg_info->flags;
|
|
|
9ae3a8 |
- vdev->bars[i].region.size = reg_info->size;
|
|
|
9ae3a8 |
- vdev->bars[i].region.fd_offset = reg_info->offset;
|
|
|
9ae3a8 |
- vdev->bars[i].region.nr = i;
|
|
|
9ae3a8 |
QLIST_INIT(&vdev->bars[i].quirks);
|
|
|
9ae3a8 |
-
|
|
|
9ae3a8 |
- g_free(reg_info);
|
|
|
9ae3a8 |
}
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
ret = vfio_get_region_info(&vdev->vbasedev,
|
|
|
9ae3a8 |
@@ -3644,10 +3784,8 @@ static void vfio_put_device(VFIOPCIDevice *vdev)
|
|
|
9ae3a8 |
DPRINTF("vfio_put_device: close vdev->vbasedev.fd\n");
|
|
|
9ae3a8 |
close(vdev->vbasedev.fd);
|
|
|
9ae3a8 |
g_free(vdev->vbasedev.name);
|
|
|
9ae3a8 |
- if (vdev->msix) {
|
|
|
9ae3a8 |
- g_free(vdev->msix);
|
|
|
9ae3a8 |
- vdev->msix = NULL;
|
|
|
9ae3a8 |
- }
|
|
|
9ae3a8 |
+ g_free(vdev->msix);
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
}
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
static int vfio_get_region_info(VFIODevice *vbasedev, int index,
|
|
|
9ae3a8 |
diff --git a/trace-events b/trace-events
|
|
|
9ae3a8 |
index 6cd46e9..cc62b0b 100644
|
|
|
9ae3a8 |
--- a/trace-events
|
|
|
9ae3a8 |
+++ b/trace-events
|
|
|
9ae3a8 |
@@ -1155,3 +1155,12 @@ kvm_run_exit(int cpu_index, uint32_t reason) "cpu_index %d, reason %d"
|
|
|
9ae3a8 |
# qom/object.c
|
|
|
9ae3a8 |
object_dynamic_cast_assert(const char *type, const char *target, const char *file, int line, const char *func) "%s->%s (%s:%d:%s)"
|
|
|
9ae3a8 |
object_class_dynamic_cast_assert(const char *type, const char *target, const char *file, int line, const char *func) "%s->%s (%s:%d:%s)"
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+# hw/misc/vfio.c
|
|
|
9ae3a8 |
+vfio_msix_fixup(const char *name, int bar, uint64_t start, uint64_t end) " (%s) MSI-X region %d mmap fixup [0x%"PRIx64" - 0x%"PRIx64"]"
|
|
|
9ae3a8 |
+vfio_region_setup(const char *dev, int index, const char *name, unsigned long flags, unsigned long offset, unsigned long size) "Device %s, region %d \"%s\", flags: %lx, offset: %lx, size: %lx"
|
|
|
9ae3a8 |
+vfio_region_mmap_fault(const char *name, int index, unsigned long offset, unsigned long size, int fault) "Region %s mmaps[%d], [%lx - %lx], fault: %d"
|
|
|
9ae3a8 |
+vfio_region_mmap(const char *name, unsigned long offset, unsigned long end) "Region %s [%lx - %lx]"
|
|
|
9ae3a8 |
+vfio_region_exit(const char *name, int index) "Device %s, region %d"
|
|
|
9ae3a8 |
+vfio_region_finalize(const char *name, int index) "Device %s, region %d"
|
|
|
9ae3a8 |
+vfio_region_mmaps_set_enabled(const char *name, bool enabled) "Region %s mmaps enabled: %d"
|
|
|
9ae3a8 |
--
|
|
|
9ae3a8 |
1.8.3.1
|
|
|
9ae3a8 |
|