/* * Container for vfio-user IOMMU type: rather than communicating with the kernel * vfio driver, we communicate over a socket to a server using the vfio-user * protocol. * * SPDX-License-Identifier: GPL-2.0-or-later */ #include #include #include "qemu/osdep.h" #include "hw/vfio-user/container.h" #include "hw/vfio-user/device.h" #include "hw/vfio/vfio-cpr.h" #include "hw/vfio/vfio-device.h" #include "hw/vfio/vfio-listener.h" #include "qapi/error.h" static int vfio_user_dma_unmap(const VFIOContainerBase *bcontainer, hwaddr iova, ram_addr_t size, IOMMUTLBEntry *iotlb, bool unmap_all) { return -ENOTSUP; } static int vfio_user_dma_map(const VFIOContainerBase *bcontainer, hwaddr iova, ram_addr_t size, void *vaddr, bool readonly, MemoryRegion *mrp) { return -ENOTSUP; } static int vfio_user_set_dirty_page_tracking(const VFIOContainerBase *bcontainer, bool start, Error **errp) { error_setg_errno(errp, ENOTSUP, "Not supported"); return -ENOTSUP; } static int vfio_user_query_dirty_bitmap(const VFIOContainerBase *bcontainer, VFIOBitmap *vbmap, hwaddr iova, hwaddr size, Error **errp) { error_setg_errno(errp, ENOTSUP, "Not supported"); return -ENOTSUP; } static bool vfio_user_setup(VFIOContainerBase *bcontainer, Error **errp) { error_setg_errno(errp, ENOTSUP, "Not supported"); return -ENOTSUP; } static VFIOUserContainer *vfio_user_create_container(Error **errp) { VFIOUserContainer *container; container = VFIO_IOMMU_USER(object_new(TYPE_VFIO_IOMMU_USER)); return container; } /* * Try to mirror vfio_container_connect() as much as possible. */ static VFIOUserContainer * vfio_user_container_connect(AddressSpace *as, Error **errp) { VFIOContainerBase *bcontainer; VFIOUserContainer *container; VFIOAddressSpace *space; VFIOIOMMUClass *vioc; space = vfio_address_space_get(as); container = vfio_user_create_container(errp); if (!container) { goto put_space_exit; } bcontainer = &container->bcontainer; if (!vfio_cpr_register_container(bcontainer, errp)) { goto free_container_exit; } vioc = VFIO_IOMMU_GET_CLASS(bcontainer); assert(vioc->setup); if (!vioc->setup(bcontainer, errp)) { goto unregister_container_exit; } vfio_address_space_insert(space, bcontainer); if (!vfio_listener_register(bcontainer, errp)) { goto listener_release_exit; } bcontainer->initialized = true; return container; listener_release_exit: vfio_listener_unregister(bcontainer); if (vioc->release) { vioc->release(bcontainer); } unregister_container_exit: vfio_cpr_unregister_container(bcontainer); free_container_exit: object_unref(container); put_space_exit: vfio_address_space_put(space); return NULL; } static void vfio_user_container_disconnect(VFIOUserContainer *container) { VFIOContainerBase *bcontainer = &container->bcontainer; VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(bcontainer); vfio_listener_unregister(bcontainer); if (vioc->release) { vioc->release(bcontainer); } VFIOAddressSpace *space = bcontainer->space; vfio_cpr_unregister_container(bcontainer); object_unref(container); vfio_address_space_put(space); } static bool vfio_user_device_get(VFIOUserContainer *container, VFIODevice *vbasedev, Error **errp) { struct vfio_device_info info = { .argsz = sizeof(info) }; if (!vfio_user_get_device_info(vbasedev->proxy, &info, errp)) { return false; } vbasedev->fd = -1; vfio_device_prepare(vbasedev, &container->bcontainer, &info); return true; } /* * vfio_user_device_attach: attach a device to a new container. */ static bool vfio_user_device_attach(const char *name, VFIODevice *vbasedev, AddressSpace *as, Error **errp) { VFIOUserContainer *container; container = vfio_user_container_connect(as, errp); if (container == NULL) { error_prepend(errp, "failed to connect proxy"); return false; } return vfio_user_device_get(container, vbasedev, errp); } static void vfio_user_device_detach(VFIODevice *vbasedev) { VFIOUserContainer *container = container_of(vbasedev->bcontainer, VFIOUserContainer, bcontainer); vfio_device_unprepare(vbasedev); vfio_user_container_disconnect(container); } static int vfio_user_pci_hot_reset(VFIODevice *vbasedev, bool single) { /* ->needs_reset is always false for vfio-user. */ return 0; } static void vfio_iommu_user_class_init(ObjectClass *klass, const void *data) { VFIOIOMMUClass *vioc = VFIO_IOMMU_CLASS(klass); vioc->setup = vfio_user_setup; vioc->dma_map = vfio_user_dma_map; vioc->dma_unmap = vfio_user_dma_unmap; vioc->attach_device = vfio_user_device_attach; vioc->detach_device = vfio_user_device_detach; vioc->set_dirty_page_tracking = vfio_user_set_dirty_page_tracking; vioc->query_dirty_bitmap = vfio_user_query_dirty_bitmap; vioc->pci_hot_reset = vfio_user_pci_hot_reset; }; static const TypeInfo types[] = { { .name = TYPE_VFIO_IOMMU_USER, .parent = TYPE_VFIO_IOMMU, .instance_size = sizeof(VFIOUserContainer), .class_init = vfio_iommu_user_class_init, }, }; DEFINE_TYPES(types)