1*5ee3dc7aSYi Liu /* 2*5ee3dc7aSYi Liu * iommufd container backend 3*5ee3dc7aSYi Liu * 4*5ee3dc7aSYi Liu * Copyright (C) 2023 Intel Corporation. 5*5ee3dc7aSYi Liu * Copyright Red Hat, Inc. 2023 6*5ee3dc7aSYi Liu * 7*5ee3dc7aSYi Liu * Authors: Yi Liu <yi.l.liu@intel.com> 8*5ee3dc7aSYi Liu * Eric Auger <eric.auger@redhat.com> 9*5ee3dc7aSYi Liu * 10*5ee3dc7aSYi Liu * SPDX-License-Identifier: GPL-2.0-or-later 11*5ee3dc7aSYi Liu */ 12*5ee3dc7aSYi Liu 13*5ee3dc7aSYi Liu #include "qemu/osdep.h" 14*5ee3dc7aSYi Liu #include <sys/ioctl.h> 15*5ee3dc7aSYi Liu #include <linux/vfio.h> 16*5ee3dc7aSYi Liu #include <linux/iommufd.h> 17*5ee3dc7aSYi Liu 18*5ee3dc7aSYi Liu #include "hw/vfio/vfio-common.h" 19*5ee3dc7aSYi Liu #include "qemu/error-report.h" 20*5ee3dc7aSYi Liu #include "trace.h" 21*5ee3dc7aSYi Liu #include "qapi/error.h" 22*5ee3dc7aSYi Liu #include "sysemu/iommufd.h" 23*5ee3dc7aSYi Liu #include "hw/qdev-core.h" 24*5ee3dc7aSYi Liu #include "sysemu/reset.h" 25*5ee3dc7aSYi Liu #include "qemu/cutils.h" 26*5ee3dc7aSYi Liu #include "qemu/chardev_open.h" 27*5ee3dc7aSYi Liu 28*5ee3dc7aSYi Liu static int iommufd_cdev_map(VFIOContainerBase *bcontainer, hwaddr iova, 29*5ee3dc7aSYi Liu ram_addr_t size, void *vaddr, bool readonly) 30*5ee3dc7aSYi Liu { 31*5ee3dc7aSYi Liu VFIOIOMMUFDContainer *container = 32*5ee3dc7aSYi Liu container_of(bcontainer, VFIOIOMMUFDContainer, bcontainer); 33*5ee3dc7aSYi Liu 34*5ee3dc7aSYi Liu return iommufd_backend_map_dma(container->be, 35*5ee3dc7aSYi Liu container->ioas_id, 36*5ee3dc7aSYi Liu iova, size, vaddr, readonly); 37*5ee3dc7aSYi Liu } 38*5ee3dc7aSYi Liu 39*5ee3dc7aSYi Liu static int iommufd_cdev_unmap(VFIOContainerBase *bcontainer, 40*5ee3dc7aSYi Liu hwaddr iova, ram_addr_t size, 41*5ee3dc7aSYi Liu IOMMUTLBEntry *iotlb) 42*5ee3dc7aSYi Liu { 43*5ee3dc7aSYi Liu VFIOIOMMUFDContainer *container = 44*5ee3dc7aSYi Liu container_of(bcontainer, VFIOIOMMUFDContainer, bcontainer); 45*5ee3dc7aSYi Liu 46*5ee3dc7aSYi Liu /* TODO: Handle dma_unmap_bitmap with iotlb args (migration) */ 47*5ee3dc7aSYi Liu return iommufd_backend_unmap_dma(container->be, 48*5ee3dc7aSYi Liu container->ioas_id, iova, size); 49*5ee3dc7aSYi Liu } 50*5ee3dc7aSYi Liu 51*5ee3dc7aSYi Liu static int iommufd_cdev_kvm_device_add(VFIODevice *vbasedev, Error **errp) 52*5ee3dc7aSYi Liu { 53*5ee3dc7aSYi Liu return vfio_kvm_device_add_fd(vbasedev->fd, errp); 54*5ee3dc7aSYi Liu } 55*5ee3dc7aSYi Liu 56*5ee3dc7aSYi Liu static void iommufd_cdev_kvm_device_del(VFIODevice *vbasedev) 57*5ee3dc7aSYi Liu { 58*5ee3dc7aSYi Liu Error *err = NULL; 59*5ee3dc7aSYi Liu 60*5ee3dc7aSYi Liu if (vfio_kvm_device_del_fd(vbasedev->fd, &err)) { 61*5ee3dc7aSYi Liu error_report_err(err); 62*5ee3dc7aSYi Liu } 63*5ee3dc7aSYi Liu } 64*5ee3dc7aSYi Liu 65*5ee3dc7aSYi Liu static int iommufd_cdev_connect_and_bind(VFIODevice *vbasedev, Error **errp) 66*5ee3dc7aSYi Liu { 67*5ee3dc7aSYi Liu IOMMUFDBackend *iommufd = vbasedev->iommufd; 68*5ee3dc7aSYi Liu struct vfio_device_bind_iommufd bind = { 69*5ee3dc7aSYi Liu .argsz = sizeof(bind), 70*5ee3dc7aSYi Liu .flags = 0, 71*5ee3dc7aSYi Liu }; 72*5ee3dc7aSYi Liu int ret; 73*5ee3dc7aSYi Liu 74*5ee3dc7aSYi Liu ret = iommufd_backend_connect(iommufd, errp); 75*5ee3dc7aSYi Liu if (ret) { 76*5ee3dc7aSYi Liu return ret; 77*5ee3dc7aSYi Liu } 78*5ee3dc7aSYi Liu 79*5ee3dc7aSYi Liu /* 80*5ee3dc7aSYi Liu * Add device to kvm-vfio to be prepared for the tracking 81*5ee3dc7aSYi Liu * in KVM. Especially for some emulated devices, it requires 82*5ee3dc7aSYi Liu * to have kvm information in the device open. 83*5ee3dc7aSYi Liu */ 84*5ee3dc7aSYi Liu ret = iommufd_cdev_kvm_device_add(vbasedev, errp); 85*5ee3dc7aSYi Liu if (ret) { 86*5ee3dc7aSYi Liu goto err_kvm_device_add; 87*5ee3dc7aSYi Liu } 88*5ee3dc7aSYi Liu 89*5ee3dc7aSYi Liu /* Bind device to iommufd */ 90*5ee3dc7aSYi Liu bind.iommufd = iommufd->fd; 91*5ee3dc7aSYi Liu ret = ioctl(vbasedev->fd, VFIO_DEVICE_BIND_IOMMUFD, &bind); 92*5ee3dc7aSYi Liu if (ret) { 93*5ee3dc7aSYi Liu error_setg_errno(errp, errno, "error bind device fd=%d to iommufd=%d", 94*5ee3dc7aSYi Liu vbasedev->fd, bind.iommufd); 95*5ee3dc7aSYi Liu goto err_bind; 96*5ee3dc7aSYi Liu } 97*5ee3dc7aSYi Liu 98*5ee3dc7aSYi Liu vbasedev->devid = bind.out_devid; 99*5ee3dc7aSYi Liu trace_iommufd_cdev_connect_and_bind(bind.iommufd, vbasedev->name, 100*5ee3dc7aSYi Liu vbasedev->fd, vbasedev->devid); 101*5ee3dc7aSYi Liu return ret; 102*5ee3dc7aSYi Liu err_bind: 103*5ee3dc7aSYi Liu iommufd_cdev_kvm_device_del(vbasedev); 104*5ee3dc7aSYi Liu err_kvm_device_add: 105*5ee3dc7aSYi Liu iommufd_backend_disconnect(iommufd); 106*5ee3dc7aSYi Liu return ret; 107*5ee3dc7aSYi Liu } 108*5ee3dc7aSYi Liu 109*5ee3dc7aSYi Liu static void iommufd_cdev_unbind_and_disconnect(VFIODevice *vbasedev) 110*5ee3dc7aSYi Liu { 111*5ee3dc7aSYi Liu /* Unbind is automatically conducted when device fd is closed */ 112*5ee3dc7aSYi Liu iommufd_cdev_kvm_device_del(vbasedev); 113*5ee3dc7aSYi Liu iommufd_backend_disconnect(vbasedev->iommufd); 114*5ee3dc7aSYi Liu } 115*5ee3dc7aSYi Liu 116*5ee3dc7aSYi Liu static int iommufd_cdev_getfd(const char *sysfs_path, Error **errp) 117*5ee3dc7aSYi Liu { 118*5ee3dc7aSYi Liu long int ret = -ENOTTY; 119*5ee3dc7aSYi Liu char *path, *vfio_dev_path = NULL, *vfio_path = NULL; 120*5ee3dc7aSYi Liu DIR *dir = NULL; 121*5ee3dc7aSYi Liu struct dirent *dent; 122*5ee3dc7aSYi Liu gchar *contents; 123*5ee3dc7aSYi Liu struct stat st; 124*5ee3dc7aSYi Liu gsize length; 125*5ee3dc7aSYi Liu int major, minor; 126*5ee3dc7aSYi Liu dev_t vfio_devt; 127*5ee3dc7aSYi Liu 128*5ee3dc7aSYi Liu path = g_strdup_printf("%s/vfio-dev", sysfs_path); 129*5ee3dc7aSYi Liu if (stat(path, &st) < 0) { 130*5ee3dc7aSYi Liu error_setg_errno(errp, errno, "no such host device"); 131*5ee3dc7aSYi Liu goto out_free_path; 132*5ee3dc7aSYi Liu } 133*5ee3dc7aSYi Liu 134*5ee3dc7aSYi Liu dir = opendir(path); 135*5ee3dc7aSYi Liu if (!dir) { 136*5ee3dc7aSYi Liu error_setg_errno(errp, errno, "couldn't open directory %s", path); 137*5ee3dc7aSYi Liu goto out_free_path; 138*5ee3dc7aSYi Liu } 139*5ee3dc7aSYi Liu 140*5ee3dc7aSYi Liu while ((dent = readdir(dir))) { 141*5ee3dc7aSYi Liu if (!strncmp(dent->d_name, "vfio", 4)) { 142*5ee3dc7aSYi Liu vfio_dev_path = g_strdup_printf("%s/%s/dev", path, dent->d_name); 143*5ee3dc7aSYi Liu break; 144*5ee3dc7aSYi Liu } 145*5ee3dc7aSYi Liu } 146*5ee3dc7aSYi Liu 147*5ee3dc7aSYi Liu if (!vfio_dev_path) { 148*5ee3dc7aSYi Liu error_setg(errp, "failed to find vfio-dev/vfioX/dev"); 149*5ee3dc7aSYi Liu goto out_close_dir; 150*5ee3dc7aSYi Liu } 151*5ee3dc7aSYi Liu 152*5ee3dc7aSYi Liu if (!g_file_get_contents(vfio_dev_path, &contents, &length, NULL)) { 153*5ee3dc7aSYi Liu error_setg(errp, "failed to load \"%s\"", vfio_dev_path); 154*5ee3dc7aSYi Liu goto out_free_dev_path; 155*5ee3dc7aSYi Liu } 156*5ee3dc7aSYi Liu 157*5ee3dc7aSYi Liu if (sscanf(contents, "%d:%d", &major, &minor) != 2) { 158*5ee3dc7aSYi Liu error_setg(errp, "failed to get major:minor for \"%s\"", vfio_dev_path); 159*5ee3dc7aSYi Liu goto out_free_dev_path; 160*5ee3dc7aSYi Liu } 161*5ee3dc7aSYi Liu g_free(contents); 162*5ee3dc7aSYi Liu vfio_devt = makedev(major, minor); 163*5ee3dc7aSYi Liu 164*5ee3dc7aSYi Liu vfio_path = g_strdup_printf("/dev/vfio/devices/%s", dent->d_name); 165*5ee3dc7aSYi Liu ret = open_cdev(vfio_path, vfio_devt); 166*5ee3dc7aSYi Liu if (ret < 0) { 167*5ee3dc7aSYi Liu error_setg(errp, "Failed to open %s", vfio_path); 168*5ee3dc7aSYi Liu } 169*5ee3dc7aSYi Liu 170*5ee3dc7aSYi Liu trace_iommufd_cdev_getfd(vfio_path, ret); 171*5ee3dc7aSYi Liu g_free(vfio_path); 172*5ee3dc7aSYi Liu 173*5ee3dc7aSYi Liu out_free_dev_path: 174*5ee3dc7aSYi Liu g_free(vfio_dev_path); 175*5ee3dc7aSYi Liu out_close_dir: 176*5ee3dc7aSYi Liu closedir(dir); 177*5ee3dc7aSYi Liu out_free_path: 178*5ee3dc7aSYi Liu if (*errp) { 179*5ee3dc7aSYi Liu error_prepend(errp, VFIO_MSG_PREFIX, path); 180*5ee3dc7aSYi Liu } 181*5ee3dc7aSYi Liu g_free(path); 182*5ee3dc7aSYi Liu 183*5ee3dc7aSYi Liu return ret; 184*5ee3dc7aSYi Liu } 185*5ee3dc7aSYi Liu 186*5ee3dc7aSYi Liu static int iommufd_cdev_attach_ioas_hwpt(VFIODevice *vbasedev, uint32_t id, 187*5ee3dc7aSYi Liu Error **errp) 188*5ee3dc7aSYi Liu { 189*5ee3dc7aSYi Liu int ret, iommufd = vbasedev->iommufd->fd; 190*5ee3dc7aSYi Liu struct vfio_device_attach_iommufd_pt attach_data = { 191*5ee3dc7aSYi Liu .argsz = sizeof(attach_data), 192*5ee3dc7aSYi Liu .flags = 0, 193*5ee3dc7aSYi Liu .pt_id = id, 194*5ee3dc7aSYi Liu }; 195*5ee3dc7aSYi Liu 196*5ee3dc7aSYi Liu /* Attach device to an IOAS or hwpt within iommufd */ 197*5ee3dc7aSYi Liu ret = ioctl(vbasedev->fd, VFIO_DEVICE_ATTACH_IOMMUFD_PT, &attach_data); 198*5ee3dc7aSYi Liu if (ret) { 199*5ee3dc7aSYi Liu error_setg_errno(errp, errno, 200*5ee3dc7aSYi Liu "[iommufd=%d] error attach %s (%d) to id=%d", 201*5ee3dc7aSYi Liu iommufd, vbasedev->name, vbasedev->fd, id); 202*5ee3dc7aSYi Liu } else { 203*5ee3dc7aSYi Liu trace_iommufd_cdev_attach_ioas_hwpt(iommufd, vbasedev->name, 204*5ee3dc7aSYi Liu vbasedev->fd, id); 205*5ee3dc7aSYi Liu } 206*5ee3dc7aSYi Liu return ret; 207*5ee3dc7aSYi Liu } 208*5ee3dc7aSYi Liu 209*5ee3dc7aSYi Liu static int iommufd_cdev_detach_ioas_hwpt(VFIODevice *vbasedev, Error **errp) 210*5ee3dc7aSYi Liu { 211*5ee3dc7aSYi Liu int ret, iommufd = vbasedev->iommufd->fd; 212*5ee3dc7aSYi Liu struct vfio_device_detach_iommufd_pt detach_data = { 213*5ee3dc7aSYi Liu .argsz = sizeof(detach_data), 214*5ee3dc7aSYi Liu .flags = 0, 215*5ee3dc7aSYi Liu }; 216*5ee3dc7aSYi Liu 217*5ee3dc7aSYi Liu ret = ioctl(vbasedev->fd, VFIO_DEVICE_DETACH_IOMMUFD_PT, &detach_data); 218*5ee3dc7aSYi Liu if (ret) { 219*5ee3dc7aSYi Liu error_setg_errno(errp, errno, "detach %s failed", vbasedev->name); 220*5ee3dc7aSYi Liu } else { 221*5ee3dc7aSYi Liu trace_iommufd_cdev_detach_ioas_hwpt(iommufd, vbasedev->name); 222*5ee3dc7aSYi Liu } 223*5ee3dc7aSYi Liu return ret; 224*5ee3dc7aSYi Liu } 225*5ee3dc7aSYi Liu 226*5ee3dc7aSYi Liu static int iommufd_cdev_attach_container(VFIODevice *vbasedev, 227*5ee3dc7aSYi Liu VFIOIOMMUFDContainer *container, 228*5ee3dc7aSYi Liu Error **errp) 229*5ee3dc7aSYi Liu { 230*5ee3dc7aSYi Liu return iommufd_cdev_attach_ioas_hwpt(vbasedev, container->ioas_id, errp); 231*5ee3dc7aSYi Liu } 232*5ee3dc7aSYi Liu 233*5ee3dc7aSYi Liu static void iommufd_cdev_detach_container(VFIODevice *vbasedev, 234*5ee3dc7aSYi Liu VFIOIOMMUFDContainer *container) 235*5ee3dc7aSYi Liu { 236*5ee3dc7aSYi Liu Error *err = NULL; 237*5ee3dc7aSYi Liu 238*5ee3dc7aSYi Liu if (iommufd_cdev_detach_ioas_hwpt(vbasedev, &err)) { 239*5ee3dc7aSYi Liu error_report_err(err); 240*5ee3dc7aSYi Liu } 241*5ee3dc7aSYi Liu } 242*5ee3dc7aSYi Liu 243*5ee3dc7aSYi Liu static void iommufd_cdev_container_destroy(VFIOIOMMUFDContainer *container) 244*5ee3dc7aSYi Liu { 245*5ee3dc7aSYi Liu VFIOContainerBase *bcontainer = &container->bcontainer; 246*5ee3dc7aSYi Liu 247*5ee3dc7aSYi Liu if (!QLIST_EMPTY(&bcontainer->device_list)) { 248*5ee3dc7aSYi Liu return; 249*5ee3dc7aSYi Liu } 250*5ee3dc7aSYi Liu memory_listener_unregister(&bcontainer->listener); 251*5ee3dc7aSYi Liu vfio_container_destroy(bcontainer); 252*5ee3dc7aSYi Liu iommufd_backend_free_id(container->be, container->ioas_id); 253*5ee3dc7aSYi Liu g_free(container); 254*5ee3dc7aSYi Liu } 255*5ee3dc7aSYi Liu 256*5ee3dc7aSYi Liu static int iommufd_cdev_ram_block_discard_disable(bool state) 257*5ee3dc7aSYi Liu { 258*5ee3dc7aSYi Liu /* 259*5ee3dc7aSYi Liu * We support coordinated discarding of RAM via the RamDiscardManager. 260*5ee3dc7aSYi Liu */ 261*5ee3dc7aSYi Liu return ram_block_uncoordinated_discard_disable(state); 262*5ee3dc7aSYi Liu } 263*5ee3dc7aSYi Liu 264*5ee3dc7aSYi Liu static int iommufd_cdev_attach(const char *name, VFIODevice *vbasedev, 265*5ee3dc7aSYi Liu AddressSpace *as, Error **errp) 266*5ee3dc7aSYi Liu { 267*5ee3dc7aSYi Liu VFIOContainerBase *bcontainer; 268*5ee3dc7aSYi Liu VFIOIOMMUFDContainer *container; 269*5ee3dc7aSYi Liu VFIOAddressSpace *space; 270*5ee3dc7aSYi Liu struct vfio_device_info dev_info = { .argsz = sizeof(dev_info) }; 271*5ee3dc7aSYi Liu int ret, devfd; 272*5ee3dc7aSYi Liu uint32_t ioas_id; 273*5ee3dc7aSYi Liu Error *err = NULL; 274*5ee3dc7aSYi Liu 275*5ee3dc7aSYi Liu devfd = iommufd_cdev_getfd(vbasedev->sysfsdev, errp); 276*5ee3dc7aSYi Liu if (devfd < 0) { 277*5ee3dc7aSYi Liu return devfd; 278*5ee3dc7aSYi Liu } 279*5ee3dc7aSYi Liu vbasedev->fd = devfd; 280*5ee3dc7aSYi Liu 281*5ee3dc7aSYi Liu ret = iommufd_cdev_connect_and_bind(vbasedev, errp); 282*5ee3dc7aSYi Liu if (ret) { 283*5ee3dc7aSYi Liu goto err_connect_bind; 284*5ee3dc7aSYi Liu } 285*5ee3dc7aSYi Liu 286*5ee3dc7aSYi Liu space = vfio_get_address_space(as); 287*5ee3dc7aSYi Liu 288*5ee3dc7aSYi Liu /* try to attach to an existing container in this space */ 289*5ee3dc7aSYi Liu QLIST_FOREACH(bcontainer, &space->containers, next) { 290*5ee3dc7aSYi Liu container = container_of(bcontainer, VFIOIOMMUFDContainer, bcontainer); 291*5ee3dc7aSYi Liu if (bcontainer->ops != &vfio_iommufd_ops || 292*5ee3dc7aSYi Liu vbasedev->iommufd != container->be) { 293*5ee3dc7aSYi Liu continue; 294*5ee3dc7aSYi Liu } 295*5ee3dc7aSYi Liu if (iommufd_cdev_attach_container(vbasedev, container, &err)) { 296*5ee3dc7aSYi Liu const char *msg = error_get_pretty(err); 297*5ee3dc7aSYi Liu 298*5ee3dc7aSYi Liu trace_iommufd_cdev_fail_attach_existing_container(msg); 299*5ee3dc7aSYi Liu error_free(err); 300*5ee3dc7aSYi Liu err = NULL; 301*5ee3dc7aSYi Liu } else { 302*5ee3dc7aSYi Liu ret = iommufd_cdev_ram_block_discard_disable(true); 303*5ee3dc7aSYi Liu if (ret) { 304*5ee3dc7aSYi Liu error_setg(errp, 305*5ee3dc7aSYi Liu "Cannot set discarding of RAM broken (%d)", ret); 306*5ee3dc7aSYi Liu goto err_discard_disable; 307*5ee3dc7aSYi Liu } 308*5ee3dc7aSYi Liu goto found_container; 309*5ee3dc7aSYi Liu } 310*5ee3dc7aSYi Liu } 311*5ee3dc7aSYi Liu 312*5ee3dc7aSYi Liu /* Need to allocate a new dedicated container */ 313*5ee3dc7aSYi Liu ret = iommufd_backend_alloc_ioas(vbasedev->iommufd, &ioas_id, errp); 314*5ee3dc7aSYi Liu if (ret < 0) { 315*5ee3dc7aSYi Liu goto err_alloc_ioas; 316*5ee3dc7aSYi Liu } 317*5ee3dc7aSYi Liu 318*5ee3dc7aSYi Liu trace_iommufd_cdev_alloc_ioas(vbasedev->iommufd->fd, ioas_id); 319*5ee3dc7aSYi Liu 320*5ee3dc7aSYi Liu container = g_malloc0(sizeof(*container)); 321*5ee3dc7aSYi Liu container->be = vbasedev->iommufd; 322*5ee3dc7aSYi Liu container->ioas_id = ioas_id; 323*5ee3dc7aSYi Liu 324*5ee3dc7aSYi Liu bcontainer = &container->bcontainer; 325*5ee3dc7aSYi Liu vfio_container_init(bcontainer, space, &vfio_iommufd_ops); 326*5ee3dc7aSYi Liu QLIST_INSERT_HEAD(&space->containers, bcontainer, next); 327*5ee3dc7aSYi Liu 328*5ee3dc7aSYi Liu ret = iommufd_cdev_attach_container(vbasedev, container, errp); 329*5ee3dc7aSYi Liu if (ret) { 330*5ee3dc7aSYi Liu goto err_attach_container; 331*5ee3dc7aSYi Liu } 332*5ee3dc7aSYi Liu 333*5ee3dc7aSYi Liu ret = iommufd_cdev_ram_block_discard_disable(true); 334*5ee3dc7aSYi Liu if (ret) { 335*5ee3dc7aSYi Liu goto err_discard_disable; 336*5ee3dc7aSYi Liu } 337*5ee3dc7aSYi Liu 338*5ee3dc7aSYi Liu bcontainer->pgsizes = qemu_real_host_page_size(); 339*5ee3dc7aSYi Liu 340*5ee3dc7aSYi Liu bcontainer->listener = vfio_memory_listener; 341*5ee3dc7aSYi Liu memory_listener_register(&bcontainer->listener, bcontainer->space->as); 342*5ee3dc7aSYi Liu 343*5ee3dc7aSYi Liu if (bcontainer->error) { 344*5ee3dc7aSYi Liu ret = -1; 345*5ee3dc7aSYi Liu error_propagate_prepend(errp, bcontainer->error, 346*5ee3dc7aSYi Liu "memory listener initialization failed: "); 347*5ee3dc7aSYi Liu goto err_listener_register; 348*5ee3dc7aSYi Liu } 349*5ee3dc7aSYi Liu 350*5ee3dc7aSYi Liu bcontainer->initialized = true; 351*5ee3dc7aSYi Liu 352*5ee3dc7aSYi Liu found_container: 353*5ee3dc7aSYi Liu ret = ioctl(devfd, VFIO_DEVICE_GET_INFO, &dev_info); 354*5ee3dc7aSYi Liu if (ret) { 355*5ee3dc7aSYi Liu error_setg_errno(errp, errno, "error getting device info"); 356*5ee3dc7aSYi Liu goto err_listener_register; 357*5ee3dc7aSYi Liu } 358*5ee3dc7aSYi Liu 359*5ee3dc7aSYi Liu /* 360*5ee3dc7aSYi Liu * TODO: examine RAM_BLOCK_DISCARD stuff, should we do group level 361*5ee3dc7aSYi Liu * for discarding incompatibility check as well? 362*5ee3dc7aSYi Liu */ 363*5ee3dc7aSYi Liu if (vbasedev->ram_block_discard_allowed) { 364*5ee3dc7aSYi Liu iommufd_cdev_ram_block_discard_disable(false); 365*5ee3dc7aSYi Liu } 366*5ee3dc7aSYi Liu 367*5ee3dc7aSYi Liu vbasedev->group = 0; 368*5ee3dc7aSYi Liu vbasedev->num_irqs = dev_info.num_irqs; 369*5ee3dc7aSYi Liu vbasedev->num_regions = dev_info.num_regions; 370*5ee3dc7aSYi Liu vbasedev->flags = dev_info.flags; 371*5ee3dc7aSYi Liu vbasedev->reset_works = !!(dev_info.flags & VFIO_DEVICE_FLAGS_RESET); 372*5ee3dc7aSYi Liu vbasedev->bcontainer = bcontainer; 373*5ee3dc7aSYi Liu QLIST_INSERT_HEAD(&bcontainer->device_list, vbasedev, container_next); 374*5ee3dc7aSYi Liu QLIST_INSERT_HEAD(&vfio_device_list, vbasedev, global_next); 375*5ee3dc7aSYi Liu 376*5ee3dc7aSYi Liu trace_iommufd_cdev_device_info(vbasedev->name, devfd, vbasedev->num_irqs, 377*5ee3dc7aSYi Liu vbasedev->num_regions, vbasedev->flags); 378*5ee3dc7aSYi Liu return 0; 379*5ee3dc7aSYi Liu 380*5ee3dc7aSYi Liu err_listener_register: 381*5ee3dc7aSYi Liu iommufd_cdev_ram_block_discard_disable(false); 382*5ee3dc7aSYi Liu err_discard_disable: 383*5ee3dc7aSYi Liu iommufd_cdev_detach_container(vbasedev, container); 384*5ee3dc7aSYi Liu err_attach_container: 385*5ee3dc7aSYi Liu iommufd_cdev_container_destroy(container); 386*5ee3dc7aSYi Liu err_alloc_ioas: 387*5ee3dc7aSYi Liu vfio_put_address_space(space); 388*5ee3dc7aSYi Liu iommufd_cdev_unbind_and_disconnect(vbasedev); 389*5ee3dc7aSYi Liu err_connect_bind: 390*5ee3dc7aSYi Liu close(vbasedev->fd); 391*5ee3dc7aSYi Liu return ret; 392*5ee3dc7aSYi Liu } 393*5ee3dc7aSYi Liu 394*5ee3dc7aSYi Liu static void iommufd_cdev_detach(VFIODevice *vbasedev) 395*5ee3dc7aSYi Liu { 396*5ee3dc7aSYi Liu VFIOContainerBase *bcontainer = vbasedev->bcontainer; 397*5ee3dc7aSYi Liu VFIOAddressSpace *space = bcontainer->space; 398*5ee3dc7aSYi Liu VFIOIOMMUFDContainer *container = container_of(bcontainer, 399*5ee3dc7aSYi Liu VFIOIOMMUFDContainer, 400*5ee3dc7aSYi Liu bcontainer); 401*5ee3dc7aSYi Liu QLIST_REMOVE(vbasedev, global_next); 402*5ee3dc7aSYi Liu QLIST_REMOVE(vbasedev, container_next); 403*5ee3dc7aSYi Liu vbasedev->bcontainer = NULL; 404*5ee3dc7aSYi Liu 405*5ee3dc7aSYi Liu if (!vbasedev->ram_block_discard_allowed) { 406*5ee3dc7aSYi Liu iommufd_cdev_ram_block_discard_disable(false); 407*5ee3dc7aSYi Liu } 408*5ee3dc7aSYi Liu 409*5ee3dc7aSYi Liu iommufd_cdev_detach_container(vbasedev, container); 410*5ee3dc7aSYi Liu iommufd_cdev_container_destroy(container); 411*5ee3dc7aSYi Liu vfio_put_address_space(space); 412*5ee3dc7aSYi Liu 413*5ee3dc7aSYi Liu iommufd_cdev_unbind_and_disconnect(vbasedev); 414*5ee3dc7aSYi Liu close(vbasedev->fd); 415*5ee3dc7aSYi Liu } 416*5ee3dc7aSYi Liu 417*5ee3dc7aSYi Liu const VFIOIOMMUOps vfio_iommufd_ops = { 418*5ee3dc7aSYi Liu .dma_map = iommufd_cdev_map, 419*5ee3dc7aSYi Liu .dma_unmap = iommufd_cdev_unmap, 420*5ee3dc7aSYi Liu .attach_device = iommufd_cdev_attach, 421*5ee3dc7aSYi Liu .detach_device = iommufd_cdev_detach, 422*5ee3dc7aSYi Liu }; 423