xref: /qemu/hw/vfio/container-base.c (revision 7be29f2f1a3f5b037d27eedbd5df9f441e8c8c16)
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 
vfio_address_space_get(AddressSpace * as)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 
vfio_address_space_put(VFIOAddressSpace * space)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 
vfio_address_space_insert(VFIOAddressSpace * space,VFIOContainerBase * bcontainer)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 
vfio_container_dma_map(VFIOContainerBase * bcontainer,hwaddr iova,ram_addr_t size,void * vaddr,bool readonly)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 
vfio_container_dma_unmap(VFIOContainerBase * bcontainer,hwaddr iova,ram_addr_t size,IOMMUTLBEntry * iotlb,bool unmap_all)86 int vfio_container_dma_unmap(VFIOContainerBase *bcontainer,
87                              hwaddr iova, ram_addr_t size,
88                              IOMMUTLBEntry *iotlb, bool unmap_all)
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, unmap_all);
94 }
95 
vfio_container_add_section_window(VFIOContainerBase * bcontainer,MemoryRegionSection * section,Error ** errp)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 
vfio_container_del_section_window(VFIOContainerBase * bcontainer,MemoryRegionSection * section)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 
vfio_container_set_dirty_page_tracking(VFIOContainerBase * bcontainer,bool start,Error ** errp)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 
vfio_container_devices_dirty_tracking_is_started(const VFIOContainerBase * bcontainer)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 
vfio_container_dirty_tracking_is_started(const VFIOContainerBase * bcontainer)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 
vfio_container_devices_dirty_tracking_is_supported(const VFIOContainerBase * bcontainer)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 
vfio_device_dma_logging_report(VFIODevice * vbasedev,hwaddr iova,hwaddr size,void * bitmap)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     return vbasedev->io_ops->device_feature(vbasedev, feature);
202 }
203 
vfio_container_iommu_query_dirty_bitmap(const VFIOContainerBase * bcontainer,VFIOBitmap * vbmap,hwaddr iova,hwaddr size,Error ** errp)204 static int vfio_container_iommu_query_dirty_bitmap(const VFIOContainerBase *bcontainer,
205                    VFIOBitmap *vbmap, hwaddr iova, hwaddr size, Error **errp)
206 {
207     VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(bcontainer);
208 
209     g_assert(vioc->query_dirty_bitmap);
210     return vioc->query_dirty_bitmap(bcontainer, vbmap, iova, size,
211                                                errp);
212 }
213 
vfio_container_devices_query_dirty_bitmap(const VFIOContainerBase * bcontainer,VFIOBitmap * vbmap,hwaddr iova,hwaddr size,Error ** errp)214 static int vfio_container_devices_query_dirty_bitmap(const VFIOContainerBase *bcontainer,
215                  VFIOBitmap *vbmap, hwaddr iova, hwaddr size, Error **errp)
216 {
217     VFIODevice *vbasedev;
218     int ret;
219 
220     QLIST_FOREACH(vbasedev, &bcontainer->device_list, container_next) {
221         ret = vfio_device_dma_logging_report(vbasedev, iova, size,
222                                              vbmap->bitmap);
223         if (ret) {
224             error_setg_errno(errp, -ret,
225                              "%s: Failed to get DMA logging report, iova: "
226                              "0x%" HWADDR_PRIx ", size: 0x%" HWADDR_PRIx,
227                              vbasedev->name, iova, size);
228 
229             return ret;
230         }
231     }
232 
233     return 0;
234 }
235 
vfio_container_query_dirty_bitmap(const VFIOContainerBase * bcontainer,uint64_t iova,uint64_t size,ram_addr_t ram_addr,Error ** errp)236 int vfio_container_query_dirty_bitmap(const VFIOContainerBase *bcontainer, uint64_t iova,
237                           uint64_t size, ram_addr_t ram_addr, Error **errp)
238 {
239     bool all_device_dirty_tracking =
240         vfio_container_devices_dirty_tracking_is_supported(bcontainer);
241     uint64_t dirty_pages;
242     VFIOBitmap vbmap;
243     int ret;
244 
245     if (!bcontainer->dirty_pages_supported && !all_device_dirty_tracking) {
246         cpu_physical_memory_set_dirty_range(ram_addr, size,
247                                             tcg_enabled() ? DIRTY_CLIENTS_ALL :
248                                             DIRTY_CLIENTS_NOCODE);
249         return 0;
250     }
251 
252     ret = vfio_bitmap_alloc(&vbmap, size);
253     if (ret) {
254         error_setg_errno(errp, -ret,
255                          "Failed to allocate dirty tracking bitmap");
256         return ret;
257     }
258 
259     if (all_device_dirty_tracking) {
260         ret = vfio_container_devices_query_dirty_bitmap(bcontainer, &vbmap, iova, size,
261                                                         errp);
262     } else {
263         ret = vfio_container_iommu_query_dirty_bitmap(bcontainer, &vbmap, iova, size,
264                                                      errp);
265     }
266 
267     if (ret) {
268         goto out;
269     }
270 
271     dirty_pages = cpu_physical_memory_set_dirty_lebitmap(vbmap.bitmap, ram_addr,
272                                                          vbmap.pages);
273 
274     trace_vfio_container_query_dirty_bitmap(iova, size, vbmap.size, ram_addr,
275                                             dirty_pages);
276 out:
277     g_free(vbmap.bitmap);
278 
279     return ret;
280 }
281 
copy_iova_range(gconstpointer src,gpointer data)282 static gpointer copy_iova_range(gconstpointer src, gpointer data)
283 {
284      Range *source = (Range *)src;
285      Range *dest = g_new(Range, 1);
286 
287      range_set_bounds(dest, range_lob(source), range_upb(source));
288      return dest;
289 }
290 
vfio_container_get_iova_ranges(const VFIOContainerBase * bcontainer)291 GList *vfio_container_get_iova_ranges(const VFIOContainerBase *bcontainer)
292 {
293     assert(bcontainer);
294     return g_list_copy_deep(bcontainer->iova_ranges, copy_iova_range, NULL);
295 }
296 
vfio_container_instance_finalize(Object * obj)297 static void vfio_container_instance_finalize(Object *obj)
298 {
299     VFIOContainerBase *bcontainer = VFIO_IOMMU(obj);
300     VFIOGuestIOMMU *giommu, *tmp;
301 
302     QLIST_SAFE_REMOVE(bcontainer, next);
303 
304     QLIST_FOREACH_SAFE(giommu, &bcontainer->giommu_list, giommu_next, tmp) {
305         memory_region_unregister_iommu_notifier(
306                 MEMORY_REGION(giommu->iommu_mr), &giommu->n);
307         QLIST_REMOVE(giommu, giommu_next);
308         g_free(giommu);
309     }
310 
311     g_list_free_full(bcontainer->iova_ranges, g_free);
312 }
313 
vfio_container_instance_init(Object * obj)314 static void vfio_container_instance_init(Object *obj)
315 {
316     VFIOContainerBase *bcontainer = VFIO_IOMMU(obj);
317 
318     bcontainer->error = NULL;
319     bcontainer->dirty_pages_supported = false;
320     bcontainer->dma_max_mappings = 0;
321     bcontainer->iova_ranges = NULL;
322     QLIST_INIT(&bcontainer->giommu_list);
323     QLIST_INIT(&bcontainer->vrdl_list);
324 }
325 
326 static const TypeInfo types[] = {
327     {
328         .name = TYPE_VFIO_IOMMU,
329         .parent = TYPE_OBJECT,
330         .instance_init = vfio_container_instance_init,
331         .instance_finalize = vfio_container_instance_finalize,
332         .instance_size = sizeof(VFIOContainerBase),
333         .class_size = sizeof(VFIOIOMMUClass),
334         .abstract = true,
335     },
336 };
337 
338 DEFINE_TYPES(types)
339