xref: /qemu/hw/vfio/cpr-legacy.c (revision 7e9f21411302d823e9ee563ff6a2a40b1ffc1266)
1 /*
2  * Copyright (c) 2021-2025 Oracle and/or its affiliates.
3  *
4  * SPDX-License-Identifier: GPL-2.0-or-later
5  */
6 
7 #include <sys/ioctl.h>
8 #include <linux/vfio.h>
9 #include "qemu/osdep.h"
10 #include "hw/vfio/vfio-container.h"
11 #include "hw/vfio/vfio-device.h"
12 #include "hw/vfio/vfio-listener.h"
13 #include "migration/blocker.h"
14 #include "migration/cpr.h"
15 #include "migration/migration.h"
16 #include "migration/vmstate.h"
17 #include "qapi/error.h"
18 #include "qemu/error-report.h"
19 
20 static bool vfio_dma_unmap_vaddr_all(VFIOContainer *container, Error **errp)
21 {
22     struct vfio_iommu_type1_dma_unmap unmap = {
23         .argsz = sizeof(unmap),
24         .flags = VFIO_DMA_UNMAP_FLAG_VADDR | VFIO_DMA_UNMAP_FLAG_ALL,
25         .iova = 0,
26         .size = 0,
27     };
28     if (ioctl(container->fd, VFIO_IOMMU_UNMAP_DMA, &unmap)) {
29         error_setg_errno(errp, errno, "vfio_dma_unmap_vaddr_all");
30         return false;
31     }
32     return true;
33 }
34 
35 /*
36  * Set the new @vaddr for any mappings registered during cpr load.
37  * The incoming state is cleared thereafter.
38  */
39 static int vfio_legacy_cpr_dma_map(const VFIOContainerBase *bcontainer,
40                                    hwaddr iova, ram_addr_t size, void *vaddr,
41                                    bool readonly, MemoryRegion *mr)
42 {
43     const VFIOContainer *container = container_of(bcontainer, VFIOContainer,
44                                                   bcontainer);
45     struct vfio_iommu_type1_dma_map map = {
46         .argsz = sizeof(map),
47         .flags = VFIO_DMA_MAP_FLAG_VADDR,
48         .vaddr = (__u64)(uintptr_t)vaddr,
49         .iova = iova,
50         .size = size,
51     };
52 
53     g_assert(cpr_is_incoming());
54 
55     if (ioctl(container->fd, VFIO_IOMMU_MAP_DMA, &map)) {
56         return -errno;
57     }
58 
59     return 0;
60 }
61 
62 static bool vfio_cpr_supported(VFIOContainer *container, Error **errp)
63 {
64     if (!ioctl(container->fd, VFIO_CHECK_EXTENSION, VFIO_UPDATE_VADDR)) {
65         error_setg(errp, "VFIO container does not support VFIO_UPDATE_VADDR");
66         return false;
67 
68     } else if (!ioctl(container->fd, VFIO_CHECK_EXTENSION, VFIO_UNMAP_ALL)) {
69         error_setg(errp, "VFIO container does not support VFIO_UNMAP_ALL");
70         return false;
71 
72     } else {
73         return true;
74     }
75 }
76 
77 static int vfio_container_pre_save(void *opaque)
78 {
79     VFIOContainer *container = opaque;
80     Error *local_err = NULL;
81 
82     if (!vfio_dma_unmap_vaddr_all(container, &local_err)) {
83         error_report_err(local_err);
84         return -1;
85     }
86     return 0;
87 }
88 
89 static int vfio_container_post_load(void *opaque, int version_id)
90 {
91     VFIOContainer *container = opaque;
92     VFIOContainerBase *bcontainer = &container->bcontainer;
93     VFIOGroup *group;
94     Error *local_err = NULL;
95 
96     if (!vfio_listener_register(bcontainer, &local_err)) {
97         error_report_err(local_err);
98         return -1;
99     }
100 
101     QLIST_FOREACH(group, &container->group_list, container_next) {
102         VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(bcontainer);
103 
104         /* Restore original dma_map function */
105         vioc->dma_map = container->cpr.saved_dma_map;
106     }
107     return 0;
108 }
109 
110 static const VMStateDescription vfio_container_vmstate = {
111     .name = "vfio-container",
112     .version_id = 0,
113     .minimum_version_id = 0,
114     .priority = MIG_PRI_LOW,  /* Must happen after devices and groups */
115     .pre_save = vfio_container_pre_save,
116     .post_load = vfio_container_post_load,
117     .needed = cpr_incoming_needed,
118     .fields = (VMStateField[]) {
119         VMSTATE_END_OF_LIST()
120     }
121 };
122 
123 bool vfio_legacy_cpr_register_container(VFIOContainer *container, Error **errp)
124 {
125     VFIOContainerBase *bcontainer = &container->bcontainer;
126     Error **cpr_blocker = &container->cpr.blocker;
127 
128     migration_add_notifier_mode(&bcontainer->cpr_reboot_notifier,
129                                 vfio_cpr_reboot_notifier,
130                                 MIG_MODE_CPR_REBOOT);
131 
132     if (!vfio_cpr_supported(container, cpr_blocker)) {
133         return migrate_add_blocker_modes(cpr_blocker, errp,
134                                          MIG_MODE_CPR_TRANSFER, -1) == 0;
135     }
136 
137     vmstate_register(NULL, -1, &vfio_container_vmstate, container);
138 
139     /* During incoming CPR, divert calls to dma_map. */
140     if (cpr_is_incoming()) {
141         VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(bcontainer);
142         container->cpr.saved_dma_map = vioc->dma_map;
143         vioc->dma_map = vfio_legacy_cpr_dma_map;
144     }
145     return true;
146 }
147 
148 void vfio_legacy_cpr_unregister_container(VFIOContainer *container)
149 {
150     VFIOContainerBase *bcontainer = &container->bcontainer;
151 
152     migration_remove_notifier(&bcontainer->cpr_reboot_notifier);
153     migrate_del_blocker(&container->cpr.blocker);
154     vmstate_unregister(NULL, &vfio_container_vmstate, container);
155 }
156 
157 int vfio_cpr_group_get_device_fd(int d, const char *name)
158 {
159     const int id = 0;
160     int fd = cpr_find_fd(name, id);
161 
162     if (fd < 0) {
163         fd = ioctl(d, VFIO_GROUP_GET_DEVICE_FD, name);
164         if (fd >= 0) {
165             cpr_save_fd(name, id, fd);
166         }
167     }
168     return fd;
169 }
170 
171 static bool same_device(int fd1, int fd2)
172 {
173     struct stat st1, st2;
174 
175     return !fstat(fd1, &st1) && !fstat(fd2, &st2) && st1.st_dev == st2.st_dev;
176 }
177 
178 bool vfio_cpr_container_match(VFIOContainer *container, VFIOGroup *group,
179                               int fd)
180 {
181     if (container->fd == fd) {
182         return true;
183     }
184     if (!same_device(container->fd, fd)) {
185         return false;
186     }
187     /*
188      * Same device, different fd.  This occurs when the container fd is
189      * cpr_save'd multiple times, once for each groupid, so SCM_RIGHTS
190      * produces duplicates.  De-dup it.
191      */
192     cpr_delete_fd("vfio_container_for_group", group->groupid);
193     close(fd);
194     cpr_save_fd("vfio_container_for_group", group->groupid, container->fd);
195     return true;
196 }
197