blob: f5bfd5431675c3b8abfb3ba44098ac61201fde58 [file] [log] [blame]
/*
* 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 <sys/ioctl.h>
#include <linux/vfio.h>
#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)