xref: /openbmc/qemu/hw/vfio/iommufd.c (revision 5ee3dc7a)
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