xref: /openbmc/qemu/hw/vfio/cpr-legacy.c (revision 1faadd96306b428f2e8e35f71761c333a6777f55)
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 "migration/blocker.h"
13 #include "migration/cpr.h"
14 #include "migration/migration.h"
15 #include "migration/vmstate.h"
16 #include "qapi/error.h"
17 
18 static bool vfio_dma_unmap_vaddr_all(VFIOContainer *container, Error **errp)
19 {
20     struct vfio_iommu_type1_dma_unmap unmap = {
21         .argsz = sizeof(unmap),
22         .flags = VFIO_DMA_UNMAP_FLAG_VADDR | VFIO_DMA_UNMAP_FLAG_ALL,
23         .iova = 0,
24         .size = 0,
25     };
26     if (ioctl(container->fd, VFIO_IOMMU_UNMAP_DMA, &unmap)) {
27         error_setg_errno(errp, errno, "vfio_dma_unmap_vaddr_all");
28         return false;
29     }
30     return true;
31 }
32 
33 
34 static bool vfio_cpr_supported(VFIOContainer *container, Error **errp)
35 {
36     if (!ioctl(container->fd, VFIO_CHECK_EXTENSION, VFIO_UPDATE_VADDR)) {
37         error_setg(errp, "VFIO container does not support VFIO_UPDATE_VADDR");
38         return false;
39 
40     } else if (!ioctl(container->fd, VFIO_CHECK_EXTENSION, VFIO_UNMAP_ALL)) {
41         error_setg(errp, "VFIO container does not support VFIO_UNMAP_ALL");
42         return false;
43 
44     } else {
45         return true;
46     }
47 }
48 
49 static int vfio_container_pre_save(void *opaque)
50 {
51     VFIOContainer *container = opaque;
52     Error *local_err = NULL;
53 
54     if (!vfio_dma_unmap_vaddr_all(container, &local_err)) {
55         error_report_err(local_err);
56         return -1;
57     }
58     return 0;
59 }
60 
61 static const VMStateDescription vfio_container_vmstate = {
62     .name = "vfio-container",
63     .version_id = 0,
64     .minimum_version_id = 0,
65     .pre_save = vfio_container_pre_save,
66     .needed = cpr_incoming_needed,
67     .fields = (VMStateField[]) {
68         VMSTATE_END_OF_LIST()
69     }
70 };
71 
72 bool vfio_legacy_cpr_register_container(VFIOContainer *container, Error **errp)
73 {
74     VFIOContainerBase *bcontainer = &container->bcontainer;
75     Error **cpr_blocker = &container->cpr.blocker;
76 
77     migration_add_notifier_mode(&bcontainer->cpr_reboot_notifier,
78                                 vfio_cpr_reboot_notifier,
79                                 MIG_MODE_CPR_REBOOT);
80 
81     if (!vfio_cpr_supported(container, cpr_blocker)) {
82         return migrate_add_blocker_modes(cpr_blocker, errp,
83                                          MIG_MODE_CPR_TRANSFER, -1) == 0;
84     }
85 
86     vmstate_register(NULL, -1, &vfio_container_vmstate, container);
87 
88     return true;
89 }
90 
91 void vfio_legacy_cpr_unregister_container(VFIOContainer *container)
92 {
93     VFIOContainerBase *bcontainer = &container->bcontainer;
94 
95     migration_remove_notifier(&bcontainer->cpr_reboot_notifier);
96     migrate_del_blocker(&container->cpr.blocker);
97     vmstate_unregister(NULL, &vfio_container_vmstate, container);
98 }
99 
100 int vfio_cpr_group_get_device_fd(int d, const char *name)
101 {
102     const int id = 0;
103     int fd = cpr_find_fd(name, id);
104 
105     if (fd < 0) {
106         fd = ioctl(d, VFIO_GROUP_GET_DEVICE_FD, name);
107         if (fd >= 0) {
108             cpr_save_fd(name, id, fd);
109         }
110     }
111     return fd;
112 }
113 
114 static bool same_device(int fd1, int fd2)
115 {
116     struct stat st1, st2;
117 
118     return !fstat(fd1, &st1) && !fstat(fd2, &st2) && st1.st_dev == st2.st_dev;
119 }
120 
121 bool vfio_cpr_container_match(VFIOContainer *container, VFIOGroup *group,
122                               int fd)
123 {
124     if (container->fd == fd) {
125         return true;
126     }
127     if (!same_device(container->fd, fd)) {
128         return false;
129     }
130     /*
131      * Same device, different fd.  This occurs when the container fd is
132      * cpr_save'd multiple times, once for each groupid, so SCM_RIGHTS
133      * produces duplicates.  De-dup it.
134      */
135     cpr_delete_fd("vfio_container_for_group", group->groupid);
136     close(fd);
137     cpr_save_fd("vfio_container_for_group", group->groupid, container->fd);
138     return true;
139 }
140