diff --git a/hw/vfio/common.c b/hw/vfio/common.c index 7cdb969fd3..6d15b36e0b 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -622,14 +622,6 @@ static void vfio_listener_region_add(MemoryListener *listener, int128_get64(llend), iommu_idx); - ret = memory_region_iommu_set_page_size_mask(giommu->iommu_mr, - bcontainer->pgsizes, - &err); - if (ret) { - g_free(giommu); - goto fail; - } - ret = memory_region_register_iommu_notifier(section->mr, &giommu->n, &err); if (ret) { diff --git a/hw/vfio/container-base.c b/hw/vfio/container-base.c index 50b1664f89..809b157674 100644 --- a/hw/vfio/container-base.c +++ b/hw/vfio/container-base.c @@ -83,6 +83,21 @@ int vfio_container_query_dirty_bitmap(const VFIOContainerBase *bcontainer, errp); } +static gpointer copy_iova_range(gconstpointer src, gpointer data) +{ + Range *source = (Range *)src; + Range *dest = g_new(Range, 1); + + range_set_bounds(dest, range_lob(source), range_upb(source)); + return dest; +} + +GList *vfio_container_get_iova_ranges(const VFIOContainerBase *bcontainer) +{ + assert(bcontainer); + return g_list_copy_deep(bcontainer->iova_ranges, copy_iova_range, NULL); +} + static void vfio_container_instance_finalize(Object *obj) { VFIOContainerBase *bcontainer = VFIO_IOMMU(obj); diff --git a/hw/vfio/container.c b/hw/vfio/container.c index 88ede913d6..425db1a14c 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -1165,18 +1165,21 @@ static int hiod_legacy_vfio_get_cap(HostIOMMUDevice *hiod, int cap, } static GList * -hiod_legacy_vfio_get_iova_ranges(HostIOMMUDevice *hiod, Error **errp) +hiod_legacy_vfio_get_iova_ranges(HostIOMMUDevice *hiod) { VFIODevice *vdev = hiod->agent; - GList *l = NULL; g_assert(vdev); + return vfio_container_get_iova_ranges(vdev->bcontainer); +} - if (vdev->bcontainer) { - l = g_list_copy(vdev->bcontainer->iova_ranges); - } +static uint64_t +hiod_legacy_vfio_get_page_size_mask(HostIOMMUDevice *hiod) +{ + VFIODevice *vdev = hiod->agent; - return l; + g_assert(vdev); + return vfio_container_get_page_size_mask(vdev->bcontainer); } static void vfio_iommu_legacy_instance_init(Object *obj) @@ -1193,6 +1196,7 @@ static void hiod_legacy_vfio_class_init(ObjectClass *oc, void *data) hioc->realize = hiod_legacy_vfio_realize; hioc->get_cap = hiod_legacy_vfio_get_cap; hioc->get_iova_ranges = hiod_legacy_vfio_get_iova_ranges; + hioc->get_page_size_mask = hiod_legacy_vfio_get_page_size_mask; }; static const TypeInfo types[] = { diff --git a/hw/vfio/display.c b/hw/vfio/display.c index 661e921616..ea87830fe0 100644 --- a/hw/vfio/display.c +++ b/hw/vfio/display.c @@ -124,7 +124,7 @@ static void vfio_display_edid_ui_info(void *opaque, uint32_t idx, } } -static void vfio_display_edid_init(VFIOPCIDevice *vdev) +static bool vfio_display_edid_init(VFIOPCIDevice *vdev, Error **errp) { VFIODisplay *dpy = vdev->dpy; int fd = vdev->vbasedev.fd; @@ -135,7 +135,8 @@ static void vfio_display_edid_init(VFIOPCIDevice *vdev) VFIO_REGION_SUBTYPE_GFX_EDID, &dpy->edid_info); if (ret) { - return; + /* Failed to get GFX edid info, allow to go through without edid. */ + return true; } trace_vfio_display_edid_available(); @@ -167,13 +168,16 @@ static void vfio_display_edid_init(VFIOPCIDevice *vdev) vfio_display_edid_link_up, vdev); vfio_display_edid_update(vdev, true, 0, 0); - return; + return true; err: + error_setg(errp, "vfio: failed to read GFX edid field"); trace_vfio_display_edid_write_error(); + g_free(dpy->edid_info); g_free(dpy->edid_regs); + dpy->edid_info = NULL; dpy->edid_regs = NULL; - return; + return false; } static void vfio_display_edid_exit(VFIODisplay *dpy) @@ -182,6 +186,7 @@ static void vfio_display_edid_exit(VFIODisplay *dpy) return; } + g_free(dpy->edid_info); g_free(dpy->edid_regs); g_free(dpy->edid_blob); timer_free(dpy->edid_link_timer); @@ -365,8 +370,7 @@ static bool vfio_display_dmabuf_init(VFIOPCIDevice *vdev, Error **errp) return false; } } - vfio_display_edid_init(vdev); - return true; + return vfio_display_edid_init(vdev, errp); } static void vfio_display_dmabuf_exit(VFIODisplay *dpy) diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c index c2f158e603..7b5f87a148 100644 --- a/hw/vfio/iommufd.c +++ b/hw/vfio/iommufd.c @@ -644,26 +644,31 @@ static bool hiod_iommufd_vfio_realize(HostIOMMUDevice *hiod, void *opaque, } static GList * -hiod_iommufd_vfio_get_iova_ranges(HostIOMMUDevice *hiod, Error **errp) +hiod_iommufd_vfio_get_iova_ranges(HostIOMMUDevice *hiod) { VFIODevice *vdev = hiod->agent; - GList *l = NULL; g_assert(vdev); - - if (vdev->bcontainer) { - l = g_list_copy(vdev->bcontainer->iova_ranges); - } - - return l; + return vfio_container_get_iova_ranges(vdev->bcontainer); } +static uint64_t +hiod_iommufd_vfio_get_page_size_mask(HostIOMMUDevice *hiod) +{ + VFIODevice *vdev = hiod->agent; + + g_assert(vdev); + return vfio_container_get_page_size_mask(vdev->bcontainer); +} + + static void hiod_iommufd_vfio_class_init(ObjectClass *oc, void *data) { HostIOMMUDeviceClass *hiodc = HOST_IOMMU_DEVICE_CLASS(oc); hiodc->realize = hiod_iommufd_vfio_realize; hiodc->get_iova_ranges = hiod_iommufd_vfio_get_iova_ranges; + hiodc->get_page_size_mask = hiod_iommufd_vfio_get_page_size_mask; }; static const TypeInfo types[] = { diff --git a/hw/virtio/trace-events b/hw/virtio/trace-events index 3cf84e04a7..b7c04f0856 100644 --- a/hw/virtio/trace-events +++ b/hw/virtio/trace-events @@ -131,7 +131,7 @@ virtio_iommu_fill_resv_property(uint32_t devid, uint8_t subtype, uint64_t start, virtio_iommu_notify_map(const char *name, uint64_t virt_start, uint64_t virt_end, uint64_t phys_start, uint32_t flags) "mr=%s virt_start=0x%"PRIx64" virt_end=0x%"PRIx64" phys_start=0x%"PRIx64" flags=%d" virtio_iommu_notify_unmap(const char *name, uint64_t virt_start, uint64_t virt_end) "mr=%s virt_start=0x%"PRIx64" virt_end=0x%"PRIx64 virtio_iommu_remap(const char *name, uint64_t virt_start, uint64_t virt_end, uint64_t phys_start) "mr=%s virt_start=0x%"PRIx64" virt_end=0x%"PRIx64" phys_start=0x%"PRIx64 -virtio_iommu_set_page_size_mask(const char *name, uint64_t old, uint64_t new) "mr=%s old_mask=0x%"PRIx64" new_mask=0x%"PRIx64 +virtio_iommu_update_page_size_mask(const char *name, uint64_t old, uint64_t new) "host iommu device=%s old_mask=0x%"PRIx64" new_mask=0x%"PRIx64 virtio_iommu_notify_flag_add(const char *name) "add notifier to mr %s" virtio_iommu_notify_flag_del(const char *name) "del notifier from mr %s" virtio_iommu_switch_address_space(uint8_t bus, uint8_t slot, uint8_t fn, bool on) "Device %02x:%02x.%x switching address space (iommu enabled=%d)" diff --git a/hw/virtio/virtio-iommu.c b/hw/virtio/virtio-iommu.c index 7c54c6b5e2..33ae61c4a6 100644 --- a/hw/virtio/virtio-iommu.c +++ b/hw/virtio/virtio-iommu.c @@ -563,10 +563,15 @@ static int virtio_iommu_set_host_iova_ranges(VirtIOIOMMU *s, PCIBus *bus, int ret = -EINVAL; if (!sbus) { - error_report("%s no sbus", __func__); + error_setg(errp, "%s: no IOMMUPciBus found!", __func__); + return ret; } sdev = sbus->pbdev[devfn]; + if (!sdev) { + error_setg(errp, "%s: no IOMMUDevice found!", __func__); + return ret; + } current_ranges = sdev->host_resv_ranges; @@ -613,9 +618,39 @@ out: return ret; } +static bool check_page_size_mask(VirtIOIOMMU *viommu, uint64_t new_mask, + Error **errp) +{ + uint64_t cur_mask = viommu->config.page_size_mask; + + if ((cur_mask & new_mask) == 0) { + error_setg(errp, "virtio-iommu reports a page size mask 0x%"PRIx64 + " incompatible with currently supported mask 0x%"PRIx64, + new_mask, cur_mask); + return false; + } + /* + * Once the granule is frozen we can't change the mask anymore. If by + * chance the hotplugged device supports the same granule, we can still + * accept it. + */ + if (viommu->granule_frozen) { + int cur_granule = ctz64(cur_mask); + + if (!(BIT_ULL(cur_granule) & new_mask)) { + error_setg(errp, + "virtio-iommu does not support frozen granule 0x%llx", + BIT_ULL(cur_granule)); + return false; + } + } + return true; +} + static bool virtio_iommu_set_iommu_device(PCIBus *bus, void *opaque, int devfn, HostIOMMUDevice *hiod, Error **errp) { + ERRP_GUARD(); VirtIOIOMMU *viommu = opaque; HostIOMMUDeviceClass *hiodc = HOST_IOMMU_DEVICE_GET_CLASS(hiod); struct hiod_key *new_key; @@ -630,7 +665,7 @@ static bool virtio_iommu_set_iommu_device(PCIBus *bus, void *opaque, int devfn, if (hiodc->get_iova_ranges) { int ret; - host_iova_ranges = hiodc->get_iova_ranges(hiod, errp); + host_iova_ranges = hiodc->get_iova_ranges(hiod); if (!host_iova_ranges) { return true; /* some old kernels may not support that capability */ } @@ -638,8 +673,28 @@ static bool virtio_iommu_set_iommu_device(PCIBus *bus, void *opaque, int devfn, hiod->aliased_devfn, host_iova_ranges, errp); if (ret) { - g_list_free_full(host_iova_ranges, g_free); - return false; + goto error; + } + } + if (hiodc->get_page_size_mask) { + uint64_t new_mask = hiodc->get_page_size_mask(hiod); + + if (check_page_size_mask(viommu, new_mask, errp)) { + /* + * The default mask depends on the "granule" property. For example, + * with 4k granule, it is -(4 * KiB). When an assigned device has + * page size restrictions due to the hardware IOMMU configuration, + * apply this restriction to the mask. + */ + trace_virtio_iommu_update_page_size_mask(hiod->name, + viommu->config.page_size_mask, + new_mask); + if (!viommu->granule_frozen) { + viommu->config.page_size_mask &= new_mask; + } + } else { + error_prepend(errp, "%s: ", hiod->name); + goto error; } } @@ -652,6 +707,9 @@ static bool virtio_iommu_set_iommu_device(PCIBus *bus, void *opaque, int devfn, g_list_free_full(host_iova_ranges, g_free); return true; +error: + g_list_free_full(host_iova_ranges, g_free); + return false; } static void @@ -1350,50 +1408,6 @@ static int virtio_iommu_notify_flag_changed(IOMMUMemoryRegion *iommu_mr, return 0; } -/* - * The default mask depends on the "granule" property. For example, with - * 4k granule, it is -(4 * KiB). When an assigned device has page size - * restrictions due to the hardware IOMMU configuration, apply this restriction - * to the mask. - */ -static int virtio_iommu_set_page_size_mask(IOMMUMemoryRegion *mr, - uint64_t new_mask, - Error **errp) -{ - IOMMUDevice *sdev = container_of(mr, IOMMUDevice, iommu_mr); - VirtIOIOMMU *s = sdev->viommu; - uint64_t cur_mask = s->config.page_size_mask; - - trace_virtio_iommu_set_page_size_mask(mr->parent_obj.name, cur_mask, - new_mask); - - if ((cur_mask & new_mask) == 0) { - error_setg(errp, "virtio-iommu %s reports a page size mask 0x%"PRIx64 - " incompatible with currently supported mask 0x%"PRIx64, - mr->parent_obj.name, new_mask, cur_mask); - return -1; - } - - /* - * Once the granule is frozen we can't change the mask anymore. If by - * chance the hotplugged device supports the same granule, we can still - * accept it. - */ - if (s->granule_frozen) { - int cur_granule = ctz64(cur_mask); - - if (!(BIT_ULL(cur_granule) & new_mask)) { - error_setg(errp, "virtio-iommu %s does not support frozen granule 0x%llx", - mr->parent_obj.name, BIT_ULL(cur_granule)); - return -1; - } - return 0; - } - - s->config.page_size_mask &= new_mask; - return 0; -} - static void virtio_iommu_system_reset(void *opaque) { VirtIOIOMMU *s = opaque; @@ -1416,18 +1430,6 @@ static void virtio_iommu_freeze_granule(Notifier *notifier, void *data) VirtIOIOMMU *s = container_of(notifier, VirtIOIOMMU, machine_done); int granule; - if (likely(s->config.bypass)) { - /* - * Transient IOMMU MR enable to collect page_size_mask requirements - * through memory_region_iommu_set_page_size_mask() called by - * VFIO region_add() callback - */ - s->config.bypass = false; - virtio_iommu_switch_address_space_all(s); - /* restore default */ - s->config.bypass = true; - virtio_iommu_switch_address_space_all(s); - } s->granule_frozen = true; granule = ctz64(s->config.page_size_mask); trace_virtio_iommu_freeze_granule(BIT_ULL(granule)); @@ -1718,7 +1720,6 @@ static void virtio_iommu_memory_region_class_init(ObjectClass *klass, imrc->translate = virtio_iommu_translate; imrc->replay = virtio_iommu_replay; imrc->notify_flag_changed = virtio_iommu_notify_flag_changed; - imrc->iommu_set_page_size_mask = virtio_iommu_set_page_size_mask; } static const TypeInfo virtio_iommu_info = { diff --git a/include/exec/memory.h b/include/exec/memory.h index c26ede33d2..02f7528ec0 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -504,32 +504,6 @@ struct IOMMUMemoryRegionClass { * @iommu: the IOMMUMemoryRegion */ int (*num_indexes)(IOMMUMemoryRegion *iommu); - - /** - * @iommu_set_page_size_mask: - * - * Restrict the page size mask that can be supported with a given IOMMU - * memory region. Used for example to propagate host physical IOMMU page - * size mask limitations to the virtual IOMMU. - * - * Optional method: if this method is not provided, then the default global - * page mask is used. - * - * @iommu: the IOMMUMemoryRegion - * - * @page_size_mask: a bitmask of supported page sizes. At least one bit, - * representing the smallest page size, must be set. Additional set bits - * represent supported block sizes. For example a host physical IOMMU that - * uses page tables with a page size of 4kB, and supports 2MB and 4GB - * blocks, will set mask 0x40201000. A granule of 4kB with indiscriminate - * block sizes is specified with mask 0xfffffffffffff000. - * - * Returns 0 on success, or a negative error. In case of failure, the error - * object must be created. - */ - int (*iommu_set_page_size_mask)(IOMMUMemoryRegion *iommu, - uint64_t page_size_mask, - Error **errp); }; typedef struct RamDiscardListener RamDiscardListener; @@ -1919,18 +1893,6 @@ int memory_region_iommu_attrs_to_index(IOMMUMemoryRegion *iommu_mr, */ int memory_region_iommu_num_indexes(IOMMUMemoryRegion *iommu_mr); -/** - * memory_region_iommu_set_page_size_mask: set the supported page - * sizes for a given IOMMU memory region - * - * @iommu_mr: IOMMU memory region - * @page_size_mask: supported page size mask - * @errp: pointer to Error*, to store an error if it happens. - */ -int memory_region_iommu_set_page_size_mask(IOMMUMemoryRegion *iommu_mr, - uint64_t page_size_mask, - Error **errp); - /** * memory_region_name: get a memory region's name * diff --git a/include/hw/vfio/vfio-container-base.h b/include/hw/vfio/vfio-container-base.h index 419e45ee7a..62a8b60d87 100644 --- a/include/hw/vfio/vfio-container-base.h +++ b/include/hw/vfio/vfio-container-base.h @@ -86,6 +86,15 @@ int vfio_container_set_dirty_page_tracking(VFIOContainerBase *bcontainer, int vfio_container_query_dirty_bitmap(const VFIOContainerBase *bcontainer, VFIOBitmap *vbmap, hwaddr iova, hwaddr size, Error **errp); +GList *vfio_container_get_iova_ranges(const VFIOContainerBase *bcontainer); + +static inline uint64_t +vfio_container_get_page_size_mask(const VFIOContainerBase *bcontainer) +{ + assert(bcontainer); + return bcontainer->pgsizes; +} + #define TYPE_VFIO_IOMMU "vfio-iommu" #define TYPE_VFIO_IOMMU_LEGACY TYPE_VFIO_IOMMU "-legacy" #define TYPE_VFIO_IOMMU_SPAPR TYPE_VFIO_IOMMU "-spapr" diff --git a/include/sysemu/host_iommu_device.h b/include/sysemu/host_iommu_device.h index ee6c813c8b..c1bf74ae2c 100644 --- a/include/sysemu/host_iommu_device.h +++ b/include/sysemu/host_iommu_device.h @@ -87,9 +87,16 @@ struct HostIOMMUDeviceClass { * @hiod Host IOMMU device * * @hiod: handle to the host IOMMU device - * @errp: error handle */ - GList* (*get_iova_ranges)(HostIOMMUDevice *hiod, Error **errp); + GList* (*get_iova_ranges)(HostIOMMUDevice *hiod); + /** + * + * @get_page_size_mask: Return the page size mask supported along this + * @hiod Host IOMMU device + * + * @hiod: handle to the host IOMMU device + */ + uint64_t (*get_page_size_mask)(HostIOMMUDevice *hiod); }; /* diff --git a/system/memory.c b/system/memory.c index 2d69521360..5e6eb459d5 100644 --- a/system/memory.c +++ b/system/memory.c @@ -1901,19 +1901,6 @@ static int memory_region_update_iommu_notify_flags(IOMMUMemoryRegion *iommu_mr, return ret; } -int memory_region_iommu_set_page_size_mask(IOMMUMemoryRegion *iommu_mr, - uint64_t page_size_mask, - Error **errp) -{ - IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_GET_CLASS(iommu_mr); - int ret = 0; - - if (imrc->iommu_set_page_size_mask) { - ret = imrc->iommu_set_page_size_mask(iommu_mr, page_size_mask, errp); - } - return ret; -} - int memory_region_register_iommu_notifier(MemoryRegion *mr, IOMMUNotifier *n, Error **errp) {