diff options
Diffstat (limited to 'hw/vfio')
| -rw-r--r-- | hw/vfio/amd-xgbe.c | 2 | ||||
| -rw-r--r-- | hw/vfio/calxeda-xgmac.c | 2 | ||||
| -rw-r--r-- | hw/vfio/common.c | 40 | ||||
| -rw-r--r-- | hw/vfio/container.c | 2 | ||||
| -rw-r--r-- | hw/vfio/helpers.c | 10 | ||||
| -rw-r--r-- | hw/vfio/igd.c | 127 | ||||
| -rw-r--r-- | hw/vfio/iommufd.c | 5 | ||||
| -rw-r--r-- | hw/vfio/pci-quirks.c | 57 | ||||
| -rw-r--r-- | hw/vfio/pci-quirks.h | 72 | ||||
| -rw-r--r-- | hw/vfio/pci.c | 2 | ||||
| -rw-r--r-- | hw/vfio/platform.c | 4 |
11 files changed, 154 insertions, 169 deletions
diff --git a/hw/vfio/amd-xgbe.c b/hw/vfio/amd-xgbe.c index 96bd608b8d..aaa96903db 100644 --- a/hw/vfio/amd-xgbe.c +++ b/hw/vfio/amd-xgbe.c @@ -41,8 +41,6 @@ static void vfio_amd_xgbe_class_init(ObjectClass *klass, void *data) &vcxc->parent_realize); dc->desc = "VFIO AMD XGBE"; dc->vmsd = &vfio_platform_amd_xgbe_vmstate; - /* Supported by TYPE_VIRT_MACHINE */ - dc->user_creatable = true; } static const TypeInfo vfio_amd_xgbe_dev_info = { diff --git a/hw/vfio/calxeda-xgmac.c b/hw/vfio/calxeda-xgmac.c index 87c382e736..b016d42b49 100644 --- a/hw/vfio/calxeda-xgmac.c +++ b/hw/vfio/calxeda-xgmac.c @@ -41,8 +41,6 @@ static void vfio_calxeda_xgmac_class_init(ObjectClass *klass, void *data) &vcxc->parent_realize); dc->desc = "VFIO Calxeda XGMAC"; dc->vmsd = &vfio_platform_calxeda_xgmac_vmstate; - /* Supported by TYPE_VIRT_MACHINE */ - dc->user_creatable = true; } static const TypeInfo vfio_calxeda_xgmac_dev_info = { diff --git a/hw/vfio/common.c b/hw/vfio/common.c index f7499a9b74..abbdc56b6d 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -555,6 +555,18 @@ static bool vfio_get_section_iova_range(VFIOContainerBase *bcontainer, return true; } +static void vfio_device_error_append(VFIODevice *vbasedev, Error **errp) +{ + /* + * MMIO region mapping failures are not fatal but in this case PCI + * peer-to-peer transactions are broken. + */ + if (vbasedev && vbasedev->type == VFIO_DEVICE_TYPE_PCI) { + error_append_hint(errp, "%s: PCI peer-to-peer transactions " + "on BARs are not supported.\n", vbasedev->name); + } +} + static void vfio_listener_region_add(MemoryListener *listener, MemoryRegionSection *section) { @@ -582,8 +594,9 @@ static void vfio_listener_region_add(MemoryListener *listener, return; } + /* PPC64/pseries machine only */ if (!vfio_container_add_section_window(bcontainer, section, &err)) { - goto fail; + goto mmio_dma_error; } memory_region_ref(section->mr); @@ -668,9 +681,13 @@ static void vfio_listener_region_add(MemoryListener *listener, "0x%"HWADDR_PRIx", %p) = %d (%s)", bcontainer, iova, int128_get64(llsize), vaddr, ret, strerror(-ret)); + mmio_dma_error: if (memory_region_is_ram_device(section->mr)) { /* Allow unexpected mappings not to be fatal for RAM devices */ - error_report_err(err); + VFIODevice *vbasedev = + vfio_get_vfio_device(memory_region_owner(section->mr)); + vfio_device_error_append(vbasedev, &err); + warn_report_err_once(err); return; } goto fail; @@ -679,16 +696,12 @@ static void vfio_listener_region_add(MemoryListener *listener, return; fail: - if (memory_region_is_ram_device(section->mr)) { - error_reportf_err(err, "PCI p2p may not work: "); - return; - } - /* - * On the initfn path, store the first error in the container so we - * can gracefully fail. Runtime, there's not much we can do other - * than throw a hardware error. - */ if (!bcontainer->initialized) { + /* + * At machine init time or when the device is attached to the + * VM, store the first error in the container so we can + * gracefully fail the device realize routine. + */ if (!bcontainer->error) { error_propagate_prepend(&bcontainer->error, err, "Region %s: ", @@ -697,6 +710,10 @@ fail: error_free(err); } } else { + /* + * At runtime, there's not much we can do other than throw a + * hardware error. + */ error_report_err(err); hw_error("vfio: DMA mapping failed, unable to continue"); } @@ -786,6 +803,7 @@ static void vfio_listener_region_del(MemoryListener *listener, memory_region_unref(section->mr); + /* PPC64/pseries machine only */ vfio_container_del_section_window(bcontainer, section); } diff --git a/hw/vfio/container.c b/hw/vfio/container.c index 4ebb526808..7c57bdd27b 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -159,7 +159,6 @@ static int vfio_legacy_dma_unmap(const VFIOContainerBase *bcontainer, unmap.size -= 1ULL << ctz64(bcontainer->pgsizes); continue; } - error_report("VFIO_UNMAP_DMA failed: %s", strerror(errno)); return -errno; } @@ -204,7 +203,6 @@ static int vfio_legacy_dma_map(const VFIOContainerBase *bcontainer, hwaddr iova, return 0; } - error_report("VFIO_MAP_DMA failed: %s", strerror(errno)); return -errno; } diff --git a/hw/vfio/helpers.c b/hw/vfio/helpers.c index 913796f437..4b255d4f3a 100644 --- a/hw/vfio/helpers.c +++ b/hw/vfio/helpers.c @@ -23,6 +23,7 @@ #include <sys/ioctl.h> #include "hw/vfio/vfio-common.h" +#include "hw/vfio/pci.h" #include "hw/hw.h" #include "trace.h" #include "qapi/error.h" @@ -728,3 +729,12 @@ bool vfio_device_hiod_realize(VFIODevice *vbasedev, Error **errp) return HOST_IOMMU_DEVICE_GET_CLASS(hiod)->realize(hiod, vbasedev, errp); } + +VFIODevice *vfio_get_vfio_device(Object *obj) +{ + if (object_dynamic_cast(obj, TYPE_VFIO_PCI)) { + return &VFIO_PCI(obj)->vbasedev; + } else { + return NULL; + } +} diff --git a/hw/vfio/igd.c b/hw/vfio/igd.c index 0740a5dd8c..b1a237edd6 100644 --- a/hw/vfio/igd.c +++ b/hw/vfio/igd.c @@ -18,6 +18,7 @@ #include "hw/hw.h" #include "hw/nvram/fw_cfg.h" #include "pci.h" +#include "pci-quirks.h" #include "trace.h" /* @@ -133,7 +134,7 @@ static uint64_t igd_gtt_memory_size(int gen, uint16_t gmch) } else { ggms = (gmch >> IGD_GMCH_GEN8_GGMS_SHIFT) & IGD_GMCH_GEN8_GGMS_MASK; if (ggms != 0) { - ggms = 1 << ggms; + ggms = 1ULL << ggms; } } @@ -422,83 +423,13 @@ static const MemoryRegionOps vfio_igd_index_quirk = { .endianness = DEVICE_LITTLE_ENDIAN, }; -static uint64_t vfio_igd_pci_config_read(VFIOPCIDevice *vdev, uint64_t offset, - unsigned size) -{ - switch (size) { - case 1: - return pci_get_byte(vdev->pdev.config + offset); - case 2: - return pci_get_word(vdev->pdev.config + offset); - case 4: - return pci_get_long(vdev->pdev.config + offset); - case 8: - return pci_get_quad(vdev->pdev.config + offset); - default: - hw_error("igd: unsupported pci config read at %"PRIx64", size %u", - offset, size); - break; - } - - return 0; -} - -static void vfio_igd_pci_config_write(VFIOPCIDevice *vdev, uint64_t offset, - uint64_t data, unsigned size) -{ - switch (size) { - case 1: - pci_set_byte(vdev->pdev.config + offset, data); - break; - case 2: - pci_set_word(vdev->pdev.config + offset, data); - break; - case 4: - pci_set_long(vdev->pdev.config + offset, data); - break; - case 8: - pci_set_quad(vdev->pdev.config + offset, data); - break; - default: - hw_error("igd: unsupported pci config write at %"PRIx64", size %u", - offset, size); - break; - } -} - -#define VFIO_IGD_QUIRK_MIRROR_REG(reg, name) \ -static uint64_t vfio_igd_quirk_read_##name(void *opaque, \ - hwaddr addr, unsigned size) \ -{ \ - VFIOPCIDevice *vdev = opaque; \ - \ - return vfio_igd_pci_config_read(vdev, reg + addr, size); \ -} \ - \ -static void vfio_igd_quirk_write_##name(void *opaque, hwaddr addr, \ - uint64_t data, unsigned size) \ -{ \ - VFIOPCIDevice *vdev = opaque; \ - \ - vfio_igd_pci_config_write(vdev, reg + addr, data, size); \ -} \ - \ -static const MemoryRegionOps vfio_igd_quirk_mirror_##name = { \ - .read = vfio_igd_quirk_read_##name, \ - .write = vfio_igd_quirk_write_##name, \ - .endianness = DEVICE_LITTLE_ENDIAN, \ -}; - -VFIO_IGD_QUIRK_MIRROR_REG(IGD_GMCH, ggc) -VFIO_IGD_QUIRK_MIRROR_REG(IGD_BDSM, bdsm) -VFIO_IGD_QUIRK_MIRROR_REG(IGD_BDSM_GEN11, bdsm64) - #define IGD_GGC_MMIO_OFFSET 0x108040 #define IGD_BDSM_MMIO_OFFSET 0x1080C0 void vfio_probe_igd_bar0_quirk(VFIOPCIDevice *vdev, int nr) { - VFIOQuirk *quirk; + VFIOQuirk *ggc_quirk, *bdsm_quirk; + VFIOConfigMirrorQuirk *ggc_mirror, *bdsm_mirror; int gen; /* @@ -522,33 +453,39 @@ void vfio_probe_igd_bar0_quirk(VFIOPCIDevice *vdev, int nr) return; } - quirk = vfio_quirk_alloc(2); - quirk->data = vdev; + ggc_quirk = vfio_quirk_alloc(1); + ggc_mirror = ggc_quirk->data = g_malloc0(sizeof(*ggc_mirror)); + ggc_mirror->mem = ggc_quirk->mem; + ggc_mirror->vdev = vdev; + ggc_mirror->bar = nr; + ggc_mirror->offset = IGD_GGC_MMIO_OFFSET; + ggc_mirror->config_offset = IGD_GMCH; - memory_region_init_io(&quirk->mem[0], OBJECT(vdev), - &vfio_igd_quirk_mirror_ggc, vdev, + memory_region_init_io(ggc_mirror->mem, OBJECT(vdev), + &vfio_generic_mirror_quirk, ggc_mirror, "vfio-igd-ggc-quirk", 2); - memory_region_add_subregion_overlap(vdev->bars[0].region.mem, - IGD_GGC_MMIO_OFFSET, &quirk->mem[0], + memory_region_add_subregion_overlap(vdev->bars[nr].region.mem, + ggc_mirror->offset, ggc_mirror->mem, 1); - if (gen < 11) { - memory_region_init_io(&quirk->mem[1], OBJECT(vdev), - &vfio_igd_quirk_mirror_bdsm, vdev, - "vfio-igd-bdsm-quirk", 4); - memory_region_add_subregion_overlap(vdev->bars[0].region.mem, - IGD_BDSM_MMIO_OFFSET, - &quirk->mem[1], 1); - } else { - memory_region_init_io(&quirk->mem[1], OBJECT(vdev), - &vfio_igd_quirk_mirror_bdsm64, vdev, - "vfio-igd-bdsm-quirk", 8); - memory_region_add_subregion_overlap(vdev->bars[0].region.mem, - IGD_BDSM_MMIO_OFFSET, - &quirk->mem[1], 1); - } + QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, ggc_quirk, next); - QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next); + bdsm_quirk = vfio_quirk_alloc(1); + bdsm_mirror = bdsm_quirk->data = g_malloc0(sizeof(*bdsm_mirror)); + bdsm_mirror->mem = bdsm_quirk->mem; + bdsm_mirror->vdev = vdev; + bdsm_mirror->bar = nr; + bdsm_mirror->offset = IGD_BDSM_MMIO_OFFSET; + bdsm_mirror->config_offset = (gen < 11) ? IGD_BDSM : IGD_BDSM_GEN11; + + memory_region_init_io(bdsm_mirror->mem, OBJECT(vdev), + &vfio_generic_mirror_quirk, bdsm_mirror, + "vfio-igd-bdsm-quirk", (gen < 11) ? 4 : 8); + memory_region_add_subregion_overlap(vdev->bars[nr].region.mem, + bdsm_mirror->offset, bdsm_mirror->mem, + 1); + + QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, bdsm_quirk, next); } void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr) diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c index 3490a8f1eb..df61edffc0 100644 --- a/hw/vfio/iommufd.c +++ b/hw/vfio/iommufd.c @@ -515,8 +515,8 @@ static bool iommufd_cdev_attach(const char *name, VFIODevice *vbasedev, } else { ret = iommufd_cdev_ram_block_discard_disable(true); if (ret) { - error_setg(errp, - "Cannot set discarding of RAM broken (%d)", ret); + error_setg_errno(errp, -ret, + "Cannot set discarding of RAM broken"); goto err_discard_disable; } goto found_container; @@ -544,6 +544,7 @@ static bool iommufd_cdev_attach(const char *name, VFIODevice *vbasedev, ret = iommufd_cdev_ram_block_discard_disable(true); if (ret) { + error_setg_errno(errp, -ret, "Cannot set discarding of RAM broken"); goto err_discard_disable; } diff --git a/hw/vfio/pci-quirks.c b/hw/vfio/pci-quirks.c index c8e60475d5..fbe43b0a79 100644 --- a/hw/vfio/pci-quirks.c +++ b/hw/vfio/pci-quirks.c @@ -25,6 +25,7 @@ #include "hw/nvram/fw_cfg.h" #include "hw/qdev-properties.h" #include "pci.h" +#include "pci-quirks.h" #include "trace.h" /* @@ -66,40 +67,6 @@ bool vfio_opt_rom_in_denylist(VFIOPCIDevice *vdev) * Device specific region quirks (mostly backdoors to PCI config space) */ -/* - * The generic window quirks operate on an address and data register, - * vfio_generic_window_address_quirk handles the address register and - * vfio_generic_window_data_quirk handles the data register. These ops - * pass reads and writes through to hardware until a value matching the - * stored address match/mask is written. When this occurs, the data - * register access emulated PCI config space for the device rather than - * passing through accesses. This enables devices where PCI config space - * is accessible behind a window register to maintain the virtualization - * provided through vfio. - */ -typedef struct VFIOConfigWindowMatch { - uint32_t match; - uint32_t mask; -} VFIOConfigWindowMatch; - -typedef struct VFIOConfigWindowQuirk { - struct VFIOPCIDevice *vdev; - - uint32_t address_val; - - uint32_t address_offset; - uint32_t data_offset; - - bool window_enabled; - uint8_t bar; - - MemoryRegion *addr_mem; - MemoryRegion *data_mem; - - uint32_t nr_matches; - VFIOConfigWindowMatch matches[]; -} VFIOConfigWindowQuirk; - static uint64_t vfio_generic_window_quirk_address_read(void *opaque, hwaddr addr, unsigned size) @@ -135,7 +102,7 @@ static void vfio_generic_window_quirk_address_write(void *opaque, hwaddr addr, } } -static const MemoryRegionOps vfio_generic_window_address_quirk = { +const MemoryRegionOps vfio_generic_window_address_quirk = { .read = vfio_generic_window_quirk_address_read, .write = vfio_generic_window_quirk_address_write, .endianness = DEVICE_LITTLE_ENDIAN, @@ -178,26 +145,12 @@ static void vfio_generic_window_quirk_data_write(void *opaque, hwaddr addr, addr + window->data_offset, data, size); } -static const MemoryRegionOps vfio_generic_window_data_quirk = { +const MemoryRegionOps vfio_generic_window_data_quirk = { .read = vfio_generic_window_quirk_data_read, .write = vfio_generic_window_quirk_data_write, .endianness = DEVICE_LITTLE_ENDIAN, }; -/* - * The generic mirror quirk handles devices which expose PCI config space - * through a region within a BAR. When enabled, reads and writes are - * redirected through to emulated PCI config space. XXX if PCI config space - * used memory regions, this could just be an alias. - */ -typedef struct VFIOConfigMirrorQuirk { - struct VFIOPCIDevice *vdev; - uint32_t offset; - uint8_t bar; - MemoryRegion *mem; - uint8_t data[]; -} VFIOConfigMirrorQuirk; - static uint64_t vfio_generic_quirk_mirror_read(void *opaque, hwaddr addr, unsigned size) { @@ -209,6 +162,7 @@ static uint64_t vfio_generic_quirk_mirror_read(void *opaque, (void)vfio_region_read(&vdev->bars[mirror->bar].region, addr + mirror->offset, size); + addr += mirror->config_offset; data = vfio_pci_read_config(&vdev->pdev, addr, size); trace_vfio_quirk_generic_mirror_read(vdev->vbasedev.name, memory_region_name(mirror->mem), @@ -222,13 +176,14 @@ static void vfio_generic_quirk_mirror_write(void *opaque, hwaddr addr, VFIOConfigMirrorQuirk *mirror = opaque; VFIOPCIDevice *vdev = mirror->vdev; + addr += mirror->config_offset; vfio_pci_write_config(&vdev->pdev, addr, data, size); trace_vfio_quirk_generic_mirror_write(vdev->vbasedev.name, memory_region_name(mirror->mem), addr, data); } -static const MemoryRegionOps vfio_generic_mirror_quirk = { +const MemoryRegionOps vfio_generic_mirror_quirk = { .read = vfio_generic_quirk_mirror_read, .write = vfio_generic_quirk_mirror_write, .endianness = DEVICE_LITTLE_ENDIAN, diff --git a/hw/vfio/pci-quirks.h b/hw/vfio/pci-quirks.h new file mode 100644 index 0000000000..d1532e379b --- /dev/null +++ b/hw/vfio/pci-quirks.h @@ -0,0 +1,72 @@ +/* + * vfio generic region quirks (mostly backdoors to PCI config space) + * + * Copyright Red Hat, Inc. 2012-2015 + * + * Authors: + * Alex Williamson <alex.williamson@redhat.com> + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + */ +#ifndef HW_VFIO_VFIO_PCI_QUIRKS_H +#define HW_VFIO_VFIO_PCI_QUIRKS_H + +#include "qemu/osdep.h" +#include "exec/memop.h" + +/* + * The generic window quirks operate on an address and data register, + * vfio_generic_window_address_quirk handles the address register and + * vfio_generic_window_data_quirk handles the data register. These ops + * pass reads and writes through to hardware until a value matching the + * stored address match/mask is written. When this occurs, the data + * register access emulated PCI config space for the device rather than + * passing through accesses. This enables devices where PCI config space + * is accessible behind a window register to maintain the virtualization + * provided through vfio. + */ +typedef struct VFIOConfigWindowMatch { + uint32_t match; + uint32_t mask; +} VFIOConfigWindowMatch; + +typedef struct VFIOConfigWindowQuirk { + struct VFIOPCIDevice *vdev; + + uint32_t address_val; + + uint32_t address_offset; + uint32_t data_offset; + + bool window_enabled; + uint8_t bar; + + MemoryRegion *addr_mem; + MemoryRegion *data_mem; + + uint32_t nr_matches; + VFIOConfigWindowMatch matches[]; +} VFIOConfigWindowQuirk; + +extern const MemoryRegionOps vfio_generic_window_address_quirk; +extern const MemoryRegionOps vfio_generic_window_data_quirk; + +/* + * The generic mirror quirk handles devices which expose PCI config space + * through a region within a BAR. When enabled, reads and writes are + * redirected through to emulated PCI config space. XXX if PCI config space + * used memory regions, this could just be an alias. + */ +typedef struct VFIOConfigMirrorQuirk { + struct VFIOPCIDevice *vdev; + uint32_t offset; /* Offset in BAR */ + uint32_t config_offset; /* Offset in PCI config space */ + uint8_t bar; + MemoryRegion *mem; + uint8_t data[]; +} VFIOConfigMirrorQuirk; + +extern const MemoryRegionOps vfio_generic_mirror_quirk; + +#endif /* HW_VFIO_VFIO_PCI_QUIRKS_H */ diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 9a55e7b773..89d900e9cf 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -3116,7 +3116,7 @@ static void vfio_realize(PCIDevice *pdev, Error **errp) if (!vbasedev->mdev && !pci_device_set_iommu_device(pdev, vbasedev->hiod, errp)) { - error_prepend(errp, "Failed to set iommu_device: "); + error_prepend(errp, "Failed to set vIOMMU: "); goto out_teardown; } diff --git a/hw/vfio/platform.c b/hw/vfio/platform.c index 1070a2113a..f491f4dc95 100644 --- a/hw/vfio/platform.c +++ b/hw/vfio/platform.c @@ -672,13 +672,11 @@ static void vfio_platform_class_init(ObjectClass *klass, void *data) dc->desc = "VFIO-based platform device assignment"; sbc->connect_irq_notifier = vfio_start_irqfd_injection; set_bit(DEVICE_CATEGORY_MISC, dc->categories); - /* Supported by TYPE_VIRT_MACHINE */ - dc->user_creatable = true; } static const TypeInfo vfio_platform_dev_info = { .name = TYPE_VFIO_PLATFORM, - .parent = TYPE_SYS_BUS_DEVICE, + .parent = TYPE_DYNAMIC_SYS_BUS_DEVICE, .instance_size = sizeof(VFIOPlatformDevice), .instance_init = vfio_platform_instance_init, .class_init = vfio_platform_class_init, |