xref: /qemu/hw/vfio/container-base.c (revision bcfee4938f8d4e8bf5f49981d3c8a78cf267cb4e)
1 /*
2  * VFIO BASE CONTAINER
3  *
4  * Copyright (C) 2023 Intel Corporation.
5  * Copyright Red Hat, Inc. 2023
6  *
7  * Authors: Yi Liu <yi.l.liu@intel.com>
8  *          Eric Auger <eric.auger@redhat.com>
9  *
10  * SPDX-License-Identifier: GPL-2.0-or-later
11  */
12 
13 #include <sys/ioctl.h>
14 #include <linux/vfio.h>
15 
16 #include "qemu/osdep.h"
17 #include "system/tcg.h"
18 #include "system/ram_addr.h"
19 #include "qapi/error.h"
20 #include "qemu/error-report.h"
21 #include "hw/vfio/vfio-container-base.h"
22 #include "hw/vfio/vfio-device.h" /* vfio_device_reset_handler */
23 #include "system/reset.h"
24 #include "vfio-helpers.h"
25 
26 #include "trace.h"
27 
28 static QLIST_HEAD(, VFIOAddressSpace) vfio_address_spaces =
29     QLIST_HEAD_INITIALIZER(vfio_address_spaces);
30 
31 VFIOAddressSpace *vfio_address_space_get(AddressSpace *as)
32 {
33     VFIOAddressSpace *space;
34 
35     QLIST_FOREACH(space, &vfio_address_spaces, list) {
36         if (space->as == as) {
37             return space;
38         }
39     }
40 
41     /* No suitable VFIOAddressSpace, create a new one */
42     space = g_malloc0(sizeof(*space));
43     space->as = as;
44     QLIST_INIT(&space->containers);
45 
46     if (QLIST_EMPTY(&vfio_address_spaces)) {
47         qemu_register_reset(vfio_device_reset_handler, NULL);
48     }
49 
50     QLIST_INSERT_HEAD(&vfio_address_spaces, space, list);
51 
52     return space;
53 }
54 
55 void vfio_address_space_put(VFIOAddressSpace *space)
56 {
57     if (!QLIST_EMPTY(&space->containers)) {
58         return;
59     }
60 
61     QLIST_REMOVE(space, list);
62     g_free(space);
63 
64     if (QLIST_EMPTY(&vfio_address_spaces)) {
65         qemu_unregister_reset(vfio_device_reset_handler, NULL);
66     }
67 }
68 
69 void vfio_address_space_insert(VFIOAddressSpace *space,
70                                VFIOContainerBase *bcontainer)
71 {
72     QLIST_INSERT_HEAD(&space->containers, bcontainer, next);
73     bcontainer->space = space;
74 }
75 
76 int vfio_container_dma_map(VFIOContainerBase *bcontainer,
77                            hwaddr iova, ram_addr_t size,
78                            void *vaddr, bool readonly)
79 {
80     VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(bcontainer);
81 
82     g_assert(vioc->dma_map);
83     return vioc->dma_map(bcontainer, iova, size, vaddr, readonly);
84 }
85 
86 int vfio_container_dma_unmap(VFIOContainerBase *bcontainer,
87                              hwaddr iova, ram_addr_t size,
88                              IOMMUTLBEntry *iotlb)
89 {
90     VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(bcontainer);
91 
92     g_assert(vioc->dma_unmap);
93     return vioc->dma_unmap(bcontainer, iova, size, iotlb);
94 }
95 
96 bool vfio_container_add_section_window(VFIOContainerBase *bcontainer,
97                                        MemoryRegionSection *section,
98                                        Error **errp)
99 {
100     VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(bcontainer);
101 
102     if (!vioc->add_window) {
103         return true;
104     }
105 
106     return vioc->add_window(bcontainer, section, errp);
107 }
108 
109 void vfio_container_del_section_window(VFIOContainerBase *bcontainer,
110                                        MemoryRegionSection *section)
111 {
112     VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(bcontainer);
113 
114     if (!vioc->del_window) {
115         return;
116     }
117 
118     return vioc->del_window(bcontainer, section);
119 }
120 
121 int vfio_container_set_dirty_page_tracking(VFIOContainerBase *bcontainer,
122                                            bool start, Error **errp)
123 {
124     VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(bcontainer);
125     int ret;
126 
127     if (!bcontainer->dirty_pages_supported) {
128         return 0;
129     }
130 
131     g_assert(vioc->set_dirty_page_tracking);
132     if (bcontainer->dirty_pages_started == start) {
133         return 0;
134     }
135 
136     ret = vioc->set_dirty_page_tracking(bcontainer, start, errp);
137     if (!ret) {
138         bcontainer->dirty_pages_started = start;
139     }
140 
141     return ret;
142 }
143 
144 static bool vfio_container_devices_dirty_tracking_is_started(
145     const VFIOContainerBase *bcontainer)
146 {
147     VFIODevice *vbasedev;
148 
149     QLIST_FOREACH(vbasedev, &bcontainer->device_list, container_next) {
150         if (!vbasedev->dirty_tracking) {
151             return false;
152         }
153     }
154 
155     return true;
156 }
157 
158 bool vfio_container_dirty_tracking_is_started(
159     const VFIOContainerBase *bcontainer)
160 {
161     return vfio_container_devices_dirty_tracking_is_started(bcontainer) ||
162            bcontainer->dirty_pages_started;
163 }
164 
165 bool vfio_container_devices_dirty_tracking_is_supported(
166     const VFIOContainerBase *bcontainer)
167 {
168     VFIODevice *vbasedev;
169 
170     QLIST_FOREACH(vbasedev, &bcontainer->device_list, container_next) {
171         if (vbasedev->device_dirty_page_tracking == ON_OFF_AUTO_OFF) {
172             return false;
173         }
174         if (!vbasedev->dirty_pages_supported) {
175             return false;
176         }
177     }
178 
179     return true;
180 }
181 
182 static int vfio_device_dma_logging_report(VFIODevice *vbasedev, hwaddr iova,
183                                           hwaddr size, void *bitmap)
184 {
185     uint64_t buf[DIV_ROUND_UP(sizeof(struct vfio_device_feature) +
186                         sizeof(struct vfio_device_feature_dma_logging_report),
187                         sizeof(uint64_t))] = {};
188     struct vfio_device_feature *feature = (struct vfio_device_feature *)buf;
189     struct vfio_device_feature_dma_logging_report *report =
190         (struct vfio_device_feature_dma_logging_report *)feature->data;
191 
192     report->iova = iova;
193     report->length = size;
194     report->page_size = qemu_real_host_page_size();
195     report->bitmap = (uintptr_t)bitmap;
196 
197     feature->argsz = sizeof(buf);
198     feature->flags = VFIO_DEVICE_FEATURE_GET |
199                      VFIO_DEVICE_FEATURE_DMA_LOGGING_REPORT;
200 
201     if (ioctl(vbasedev->fd, VFIO_DEVICE_FEATURE, feature)) {
202         return -errno;
203     }
204 
205     return 0;
206 }
207 
208 static int vfio_container_iommu_query_dirty_bitmap(const VFIOContainerBase *bcontainer,
209                    VFIOBitmap *vbmap, hwaddr iova, hwaddr size, Error **errp)
210 {
211     VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(bcontainer);
212 
213     g_assert(vioc->query_dirty_bitmap);
214     return vioc->query_dirty_bitmap(bcontainer, vbmap, iova, size,
215                                                errp);
216 }
217 
218 static int vfio_container_devices_query_dirty_bitmap(const VFIOContainerBase *bcontainer,
219                  VFIOBitmap *vbmap, hwaddr iova, hwaddr size, Error **errp)
220 {
221     VFIODevice *vbasedev;
222     int ret;
223 
224     QLIST_FOREACH(vbasedev, &bcontainer->device_list, container_next) {
225         ret = vfio_device_dma_logging_report(vbasedev, iova, size,
226                                              vbmap->bitmap);
227         if (ret) {
228             error_setg_errno(errp, -ret,
229                              "%s: Failed to get DMA logging report, iova: "
230                              "0x%" HWADDR_PRIx ", size: 0x%" HWADDR_PRIx,
231                              vbasedev->name, iova, size);
232 
233             return ret;
234         }
235     }
236 
237     return 0;
238 }
239 
240 int vfio_container_query_dirty_bitmap(const VFIOContainerBase *bcontainer, uint64_t iova,
241                           uint64_t size, ram_addr_t ram_addr, Error **errp)
242 {
243     bool all_device_dirty_tracking =
244         vfio_container_devices_dirty_tracking_is_supported(bcontainer);
245     uint64_t dirty_pages;
246     VFIOBitmap vbmap;
247     int ret;
248 
249     if (!bcontainer->dirty_pages_supported && !all_device_dirty_tracking) {
250         cpu_physical_memory_set_dirty_range(ram_addr, size,
251                                             tcg_enabled() ? DIRTY_CLIENTS_ALL :
252                                             DIRTY_CLIENTS_NOCODE);
253         return 0;
254     }
255 
256     ret = vfio_bitmap_alloc(&vbmap, size);
257     if (ret) {
258         error_setg_errno(errp, -ret,
259                          "Failed to allocate dirty tracking bitmap");
260         return ret;
261     }
262 
263     if (all_device_dirty_tracking) {
264         ret = vfio_container_devices_query_dirty_bitmap(bcontainer, &vbmap, iova, size,
265                                                         errp);
266     } else {
267         ret = vfio_container_iommu_query_dirty_bitmap(bcontainer, &vbmap, iova, size,
268                                                      errp);
269     }
270 
271     if (ret) {
272         goto out;
273     }
274 
275     dirty_pages = cpu_physical_memory_set_dirty_lebitmap(vbmap.bitmap, ram_addr,
276                                                          vbmap.pages);
277 
278     trace_vfio_container_query_dirty_bitmap(iova, size, vbmap.size, ram_addr,
279                                             dirty_pages);
280 out:
281     g_free(vbmap.bitmap);
282 
283     return ret;
284 }
285 
286 static gpointer copy_iova_range(gconstpointer src, gpointer data)
287 {
288      Range *source = (Range *)src;
289      Range *dest = g_new(Range, 1);
290 
291      range_set_bounds(dest, range_lob(source), range_upb(source));
292      return dest;
293 }
294 
295 GList *vfio_container_get_iova_ranges(const VFIOContainerBase *bcontainer)
296 {
297     assert(bcontainer);
298     return g_list_copy_deep(bcontainer->iova_ranges, copy_iova_range, NULL);
299 }
300 
301 static void vfio_container_instance_finalize(Object *obj)
302 {
303     VFIOContainerBase *bcontainer = VFIO_IOMMU(obj);
304     VFIOGuestIOMMU *giommu, *tmp;
305 
306     QLIST_SAFE_REMOVE(bcontainer, next);
307 
308     QLIST_FOREACH_SAFE(giommu, &bcontainer->giommu_list, giommu_next, tmp) {
309         memory_region_unregister_iommu_notifier(
310                 MEMORY_REGION(giommu->iommu_mr), &giommu->n);
311         QLIST_REMOVE(giommu, giommu_next);
312         g_free(giommu);
313     }
314 
315     g_list_free_full(bcontainer->iova_ranges, g_free);
316 }
317 
318 static void vfio_container_instance_init(Object *obj)
319 {
320     VFIOContainerBase *bcontainer = VFIO_IOMMU(obj);
321 
322     bcontainer->error = NULL;
323     bcontainer->dirty_pages_supported = false;
324     bcontainer->dma_max_mappings = 0;
325     bcontainer->iova_ranges = NULL;
326     QLIST_INIT(&bcontainer->giommu_list);
327     QLIST_INIT(&bcontainer->vrdl_list);
328 }
329 
330 static const TypeInfo types[] = {
331     {
332         .name = TYPE_VFIO_IOMMU,
333         .parent = TYPE_OBJECT,
334         .instance_init = vfio_container_instance_init,
335         .instance_finalize = vfio_container_instance_finalize,
336         .instance_size = sizeof(VFIOContainerBase),
337         .class_size = sizeof(VFIOIOMMUClass),
338         .abstract = true,
339     },
340 };
341 
342 DEFINE_TYPES(types)
343