aboutsummaryrefslogtreecommitdiff
path: root/hw/vfio/common.c
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2016-07-05 11:14:27 +0100
committerPeter Maydell <peter.maydell@linaro.org>2016-07-05 11:14:27 +0100
commit8662d7db392f906c7808014051b278ad1542db93 (patch)
treeff0bced1fc218f98a01813144f43ce6de905d486 /hw/vfio/common.c
parent11659423113d2fbcf055085b5e8285d590addfaa (diff)
parent2c7ad80443e9747eb85b508be01cded958191bad (diff)
Merge remote-tracking branch 'remotes/dgibson/tags/ppc-for-2.7-20160705' into staging
ppc patch queue for 2016-07-05 Here's the current ppc, sPAPR and related drivers patch queue. * The big addition is dynamic DMA window support (this includes some core VFIO changes) * There are also several fixes to the MMU emulation for bugs introduced with the HV mode patches * Several other bugfixes and cleanups Changes in v2: I messed up and forgot to make a fix in the last patch which BenH pointed out (introduced by my rebasing). That's fixed in this version, and I'm replacing the tag in place with the revised version. # gpg: Signature made Tue 05 Jul 2016 06:28:58 BST # gpg: using RSA key 0x6C38CACA20D9B392 # gpg: Good signature from "David Gibson <david@gibson.dropbear.id.au>" # gpg: aka "David Gibson (Red Hat) <dgibson@redhat.com>" # gpg: aka "David Gibson (ozlabs.org) <dgibson@ozlabs.org>" # gpg: WARNING: This key is not certified with sufficiently trusted signatures! # gpg: It is not certain that the signature belongs to the owner. # Primary key fingerprint: 75F4 6586 AE61 A66C C44E 87DC 6C38 CACA 20D9 B392 * remotes/dgibson/tags/ppc-for-2.7-20160705: ppc/hash64: Fix support for LPCR:ISL ppc/hash64: Add proper real mode translation support target-ppc: Return page shift from PTEG search target-ppc: Simplify HPTE matching target-ppc: Correct page size decoding in ppc_hash64_pteg_search() ppc: simplify ppc_hash64_hpte_page_shift_noslb() spapr_pci/spapr_pci_vfio: Support Dynamic DMA Windows (DDW) vfio/spapr: Create DMA window dynamically (SPAPR IOMMU v2) vfio: Add host side DMA window capabilities vfio: spapr: Add DMA memory preregistering (SPAPR IOMMU v2) spapr_iommu: Realloc guest visible TCE table when starting/stopping listening ppc: simplify max_smt initialization in ppc_cpu_realizefn() spapr: Ensure thread0 of CPU core is always realized first ppc: Fix xsrdpi, xvrdpi and xvrspi rounding Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'hw/vfio/common.c')
-rw-r--r--hw/vfio/common.c170
1 files changed, 140 insertions, 30 deletions
diff --git a/hw/vfio/common.c b/hw/vfio/common.c
index 7be638e0e3..f3c0522e7e 100644
--- a/hw/vfio/common.c
+++ b/hw/vfio/common.c
@@ -28,6 +28,7 @@
#include "exec/memory.h"
#include "hw/hw.h"
#include "qemu/error-report.h"
+#include "qemu/range.h"
#include "sysemu/kvm.h"
#ifdef CONFIG_KVM
#include "linux/kvm.h"
@@ -241,6 +242,44 @@ static int vfio_dma_map(VFIOContainer *container, hwaddr iova,
return -errno;
}
+static void vfio_host_win_add(VFIOContainer *container,
+ hwaddr min_iova, hwaddr max_iova,
+ uint64_t iova_pgsizes)
+{
+ VFIOHostDMAWindow *hostwin;
+
+ QLIST_FOREACH(hostwin, &container->hostwin_list, hostwin_next) {
+ if (ranges_overlap(hostwin->min_iova,
+ hostwin->max_iova - hostwin->min_iova + 1,
+ min_iova,
+ max_iova - min_iova + 1)) {
+ hw_error("%s: Overlapped IOMMU are not enabled", __func__);
+ }
+ }
+
+ hostwin = g_malloc0(sizeof(*hostwin));
+
+ hostwin->min_iova = min_iova;
+ hostwin->max_iova = max_iova;
+ hostwin->iova_pgsizes = iova_pgsizes;
+ QLIST_INSERT_HEAD(&container->hostwin_list, hostwin, hostwin_next);
+}
+
+static int vfio_host_win_del(VFIOContainer *container, hwaddr min_iova,
+ hwaddr max_iova)
+{
+ VFIOHostDMAWindow *hostwin;
+
+ QLIST_FOREACH(hostwin, &container->hostwin_list, hostwin_next) {
+ if (hostwin->min_iova == min_iova && hostwin->max_iova == max_iova) {
+ QLIST_REMOVE(hostwin, hostwin_next);
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
static bool vfio_listener_skipped_section(MemoryRegionSection *section)
{
return (!memory_region_is_ram(section->mr) &&
@@ -329,6 +368,8 @@ static void vfio_listener_region_add(MemoryListener *listener,
Int128 llend, llsize;
void *vaddr;
int ret;
+ VFIOHostDMAWindow *hostwin;
+ bool hostwin_found;
if (vfio_listener_skipped_section(section)) {
trace_vfio_listener_region_add_skip(
@@ -354,7 +395,40 @@ static void vfio_listener_region_add(MemoryListener *listener,
}
end = int128_get64(int128_sub(llend, int128_one()));
- if ((iova < container->min_iova) || (end > container->max_iova)) {
+ if (container->iommu_type == VFIO_SPAPR_TCE_v2_IOMMU) {
+ VFIOHostDMAWindow *hostwin;
+ hwaddr pgsize = 0;
+
+ /* For now intersections are not allowed, we may relax this later */
+ QLIST_FOREACH(hostwin, &container->hostwin_list, hostwin_next) {
+ if (ranges_overlap(hostwin->min_iova,
+ hostwin->max_iova - hostwin->min_iova + 1,
+ section->offset_within_address_space,
+ int128_get64(section->size))) {
+ ret = -1;
+ goto fail;
+ }
+ }
+
+ ret = vfio_spapr_create_window(container, section, &pgsize);
+ if (ret) {
+ goto fail;
+ }
+
+ vfio_host_win_add(container, section->offset_within_address_space,
+ section->offset_within_address_space +
+ int128_get64(section->size) - 1, pgsize);
+ }
+
+ hostwin_found = false;
+ QLIST_FOREACH(hostwin, &container->hostwin_list, hostwin_next) {
+ if (hostwin->min_iova <= iova && end <= hostwin->max_iova) {
+ hostwin_found = true;
+ break;
+ }
+ }
+
+ if (!hostwin_found) {
error_report("vfio: IOMMU container %p can't map guest IOVA region"
" 0x%"HWADDR_PRIx"..0x%"HWADDR_PRIx,
container, iova, end);
@@ -369,10 +443,6 @@ static void vfio_listener_region_add(MemoryListener *listener,
trace_vfio_listener_region_add_iommu(iova, end);
/*
- * FIXME: We should do some checking to see if the
- * capabilities of the host VFIO IOMMU are adequate to model
- * the guest IOMMU
- *
* FIXME: For VFIO iommu types which have KVM acceleration to
* avoid bouncing all map/unmaps through qemu this way, this
* would be the right place to wire that up (tell the KVM
@@ -493,6 +563,18 @@ static void vfio_listener_region_del(MemoryListener *listener,
"0x%"HWADDR_PRIx") = %d (%m)",
container, iova, int128_get64(llsize), ret);
}
+
+ if (container->iommu_type == VFIO_SPAPR_TCE_v2_IOMMU) {
+ vfio_spapr_remove_window(container,
+ section->offset_within_address_space);
+ if (vfio_host_win_del(container,
+ section->offset_within_address_space,
+ section->offset_within_address_space +
+ int128_get64(section->size) - 1) < 0) {
+ hw_error("%s: Cannot delete missing window at %"HWADDR_PRIx,
+ __func__, section->offset_within_address_space);
+ }
+ }
}
static const MemoryListener vfio_memory_listener = {
@@ -503,6 +585,9 @@ static const MemoryListener vfio_memory_listener = {
static void vfio_listener_release(VFIOContainer *container)
{
memory_listener_unregister(&container->listener);
+ if (container->iommu_type == VFIO_SPAPR_TCE_v2_IOMMU) {
+ memory_listener_unregister(&container->prereg_listener);
+ }
}
static struct vfio_info_cap_header *
@@ -861,8 +946,8 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as)
goto free_container_exit;
}
- ret = ioctl(fd, VFIO_SET_IOMMU,
- v2 ? VFIO_TYPE1v2_IOMMU : VFIO_TYPE1_IOMMU);
+ container->iommu_type = v2 ? VFIO_TYPE1v2_IOMMU : VFIO_TYPE1_IOMMU;
+ ret = ioctl(fd, VFIO_SET_IOMMU, container->iommu_type);
if (ret) {
error_report("vfio: failed to set iommu for container: %m");
ret = -errno;
@@ -876,19 +961,18 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as)
* existing Type1 IOMMUs generally support any IOVA we're
* going to actually try in practice.
*/
- container->min_iova = 0;
- container->max_iova = (hwaddr)-1;
-
- /* Assume just 4K IOVA page size */
- container->iova_pgsizes = 0x1000;
info.argsz = sizeof(info);
ret = ioctl(fd, VFIO_IOMMU_GET_INFO, &info);
/* Ignore errors */
- if ((ret == 0) && (info.flags & VFIO_IOMMU_INFO_PGSIZES)) {
- container->iova_pgsizes = info.iova_pgsizes;
+ if (ret || !(info.flags & VFIO_IOMMU_INFO_PGSIZES)) {
+ /* Assume 4k IOVA page size */
+ info.iova_pgsizes = 4096;
}
- } else if (ioctl(fd, VFIO_CHECK_EXTENSION, VFIO_SPAPR_TCE_IOMMU)) {
+ vfio_host_win_add(container, 0, (hwaddr)-1, info.iova_pgsizes);
+ } else if (ioctl(fd, VFIO_CHECK_EXTENSION, VFIO_SPAPR_TCE_IOMMU) ||
+ ioctl(fd, VFIO_CHECK_EXTENSION, VFIO_SPAPR_TCE_v2_IOMMU)) {
struct vfio_iommu_spapr_tce_info info;
+ bool v2 = !!ioctl(fd, VFIO_CHECK_EXTENSION, VFIO_SPAPR_TCE_v2_IOMMU);
ret = ioctl(group->fd, VFIO_GROUP_SET_CONTAINER, &fd);
if (ret) {
@@ -896,7 +980,9 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as)
ret = -errno;
goto free_container_exit;
}
- ret = ioctl(fd, VFIO_SET_IOMMU, VFIO_SPAPR_TCE_IOMMU);
+ container->iommu_type =
+ v2 ? VFIO_SPAPR_TCE_v2_IOMMU : VFIO_SPAPR_TCE_IOMMU;
+ ret = ioctl(fd, VFIO_SET_IOMMU, container->iommu_type);
if (ret) {
error_report("vfio: failed to set iommu for container: %m");
ret = -errno;
@@ -908,30 +994,54 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as)
* when container fd is closed so we do not call it explicitly
* in this file.
*/
- ret = ioctl(fd, VFIO_IOMMU_ENABLE);
- if (ret) {
- error_report("vfio: failed to enable container: %m");
- ret = -errno;
- goto free_container_exit;
+ if (!v2) {
+ ret = ioctl(fd, VFIO_IOMMU_ENABLE);
+ if (ret) {
+ error_report("vfio: failed to enable container: %m");
+ ret = -errno;
+ goto free_container_exit;
+ }
+ } else {
+ container->prereg_listener = vfio_prereg_listener;
+
+ memory_listener_register(&container->prereg_listener,
+ &address_space_memory);
+ if (container->error) {
+ memory_listener_unregister(&container->prereg_listener);
+ error_report("vfio: RAM memory listener initialization failed for container");
+ goto free_container_exit;
+ }
}
- /*
- * This only considers the host IOMMU's 32-bit window. At
- * some point we need to add support for the optional 64-bit
- * window and dynamic windows
- */
info.argsz = sizeof(info);
ret = ioctl(fd, VFIO_IOMMU_SPAPR_TCE_GET_INFO, &info);
if (ret) {
error_report("vfio: VFIO_IOMMU_SPAPR_TCE_GET_INFO failed: %m");
ret = -errno;
+ if (v2) {
+ memory_listener_unregister(&container->prereg_listener);
+ }
goto free_container_exit;
}
- container->min_iova = info.dma32_window_start;
- container->max_iova = container->min_iova + info.dma32_window_size - 1;
- /* Assume just 4K IOVA pages for now */
- container->iova_pgsizes = 0x1000;
+ if (v2) {
+ /*
+ * There is a default window in just created container.
+ * To make region_add/del simpler, we better remove this
+ * window now and let those iommu_listener callbacks
+ * create/remove them when needed.
+ */
+ ret = vfio_spapr_remove_window(container, info.dma32_window_start);
+ if (ret) {
+ goto free_container_exit;
+ }
+ } else {
+ /* The default table uses 4K pages */
+ vfio_host_win_add(container, info.dma32_window_start,
+ info.dma32_window_start +
+ info.dma32_window_size - 1,
+ 0x1000);
+ }
} else {
error_report("vfio: No available IOMMU models");
ret = -EINVAL;