| 1 | /* |
| 2 | * Container for vfio-user IOMMU type: rather than communicating with the kernel |
| 3 | * vfio driver, we communicate over a socket to a server using the vfio-user |
| 4 | * protocol. |
| 5 | * |
| 6 | * SPDX-License-Identifier: GPL-2.0-or-later |
| 7 | */ |
| 8 | |
| 9 | #include "qemu/osdep.h" |
| 10 | #include <sys/ioctl.h> |
| 11 | #include <linux/vfio.h> |
| 12 | |
| 13 | #include "hw/vfio-user/container.h" |
| 14 | #include "hw/vfio-user/device.h" |
| 15 | #include "hw/vfio-user/trace.h" |
| 16 | #include "hw/vfio/vfio-device.h" |
| 17 | #include "hw/vfio/vfio-listener.h" |
| 18 | #include "system/ramblock.h" |
| 19 | #include "qapi/error.h" |
| 20 | |
| 21 | /* |
| 22 | * When DMA space is the physical address space, the region add/del listeners |
| 23 | * will fire during memory update transactions. These depend on BQL being held, |
| 24 | * so do any resulting map/demap ops async while keeping BQL. |
| 25 | */ |
| 26 | static void vfio_user_listener_begin(VFIOContainer *bcontainer) |
| 27 | { |
| 28 | VFIOUserContainer *container = VFIO_IOMMU_USER(bcontainer); |
| 29 | |
| 30 | container->proxy->async_ops = true; |
| 31 | } |
| 32 | |
| 33 | static void vfio_user_listener_commit(VFIOContainer *bcontainer) |
| 34 | { |
| 35 | VFIOUserContainer *container = VFIO_IOMMU_USER(bcontainer); |
| 36 | |
| 37 | /* wait here for any async requests sent during the transaction */ |
| 38 | container->proxy->async_ops = false; |
| 39 | vfio_user_wait_reqs(container->proxy); |
| 40 | } |
| 41 | |
| 42 | static int vfio_user_dma_unmap(const VFIOContainer *bcontainer, |
| 43 | hwaddr iova, uint64_t size, |
| 44 | IOMMUTLBEntry *iotlb, bool unmap_all) |
| 45 | { |
| 46 | VFIOUserContainer *container = VFIO_IOMMU_USER(bcontainer); |
| 47 | |
| 48 | Error *local_err = NULL; |
| 49 | int ret = 0; |
| 50 | |
| 51 | VFIOUserDMAUnmap *msgp = g_malloc(sizeof(*msgp)); |
| 52 | |
| 53 | vfio_user_request_msg(&msgp->hdr, VFIO_USER_DMA_UNMAP, sizeof(*msgp), 0); |
| 54 | msgp->argsz = sizeof(struct vfio_iommu_type1_dma_unmap); |
| 55 | msgp->flags = unmap_all ? VFIO_DMA_UNMAP_FLAG_ALL : 0; |
| 56 | msgp->iova = iova; |
| 57 | msgp->size = size; |
| 58 | trace_vfio_user_dma_unmap(msgp->iova, msgp->size, msgp->flags, |
| 59 | container->proxy->async_ops); |
| 60 | |
| 61 | if (container->proxy->async_ops) { |
| 62 | if (!vfio_user_send_nowait(container->proxy, &msgp->hdr, NULL, |
| 63 | 0, &local_err)) { |
| 64 | error_report_err(local_err); |
| 65 | ret = -EFAULT; |
| 66 | } |
| 67 | } else { |
| 68 | if (!vfio_user_send_wait(container->proxy, &msgp->hdr, NULL, |
| 69 | 0, &local_err)) { |
| 70 | error_report_err(local_err); |
| 71 | ret = -EFAULT; |
| 72 | } |
| 73 | |
| 74 | if (msgp->hdr.flags & VFIO_USER_ERROR) { |
| 75 | ret = -msgp->hdr.error_reply; |
| 76 | } |
| 77 | |
| 78 | g_free(msgp); |
| 79 | } |
| 80 | |
| 81 | return ret; |
| 82 | } |
| 83 | |
| 84 | static int vfio_user_dma_map(const VFIOContainer *bcontainer, hwaddr iova, |
| 85 | uint64_t size, void *vaddr, bool readonly, |
| 86 | MemoryRegion *mrp) |
| 87 | { |
| 88 | VFIOUserContainer *container = VFIO_IOMMU_USER(bcontainer); |
| 89 | |
| 90 | int fd = memory_region_get_fd(mrp); |
| 91 | Error *local_err = NULL; |
| 92 | int ret = 0; |
| 93 | |
| 94 | VFIOUserFDs *fds = NULL; |
| 95 | VFIOUserDMAMap *msgp = g_malloc0(sizeof(*msgp)); |
| 96 | |
| 97 | vfio_user_request_msg(&msgp->hdr, VFIO_USER_DMA_MAP, sizeof(*msgp), 0); |
| 98 | msgp->argsz = sizeof(struct vfio_iommu_type1_dma_map); |
| 99 | msgp->flags = VFIO_DMA_MAP_FLAG_READ; |
| 100 | msgp->offset = 0; |
| 101 | msgp->iova = iova; |
| 102 | msgp->size = size; |
| 103 | |
| 104 | /* |
| 105 | * vaddr enters as a QEMU process address; make it either a file offset |
| 106 | * for mapped areas or leave as 0. |
| 107 | */ |
| 108 | if (fd != -1) { |
| 109 | msgp->offset = qemu_ram_block_host_offset(mrp->ram_block, vaddr); |
| 110 | } |
| 111 | |
| 112 | if (!readonly) { |
| 113 | msgp->flags |= VFIO_DMA_MAP_FLAG_WRITE; |
| 114 | } |
| 115 | |
| 116 | trace_vfio_user_dma_map(msgp->iova, msgp->size, msgp->offset, msgp->flags, |
| 117 | container->proxy->async_ops); |
| 118 | |
| 119 | /* |
| 120 | * The async_ops case sends without blocking. They're later waited for in |
| 121 | * vfio_send_wait_reqs. |
| 122 | */ |
| 123 | if (container->proxy->async_ops) { |
| 124 | /* can't use auto variable since we don't block */ |
| 125 | if (fd != -1) { |
| 126 | fds = vfio_user_getfds(1); |
| 127 | fds->send_fds = 1; |
| 128 | fds->fds[0] = fd; |
| 129 | } |
| 130 | |
| 131 | if (!vfio_user_send_nowait(container->proxy, &msgp->hdr, fds, |
| 132 | 0, &local_err)) { |
| 133 | error_report_err(local_err); |
| 134 | ret = -EFAULT; |
| 135 | } |
| 136 | } else { |
| 137 | VFIOUserFDs local_fds = { 1, 0, &fd }; |
| 138 | |
| 139 | fds = fd != -1 ? &local_fds : NULL; |
| 140 | |
| 141 | if (!vfio_user_send_wait(container->proxy, &msgp->hdr, fds, |
| 142 | 0, &local_err)) { |
| 143 | error_report_err(local_err); |
| 144 | ret = -EFAULT; |
| 145 | } |
| 146 | |
| 147 | if (msgp->hdr.flags & VFIO_USER_ERROR) { |
| 148 | ret = -msgp->hdr.error_reply; |
| 149 | } |
| 150 | |
| 151 | g_free(msgp); |
| 152 | } |
| 153 | |
| 154 | return ret; |
| 155 | } |
| 156 | |
| 157 | static int |
| 158 | vfio_user_set_dirty_page_tracking(const VFIOContainer *bcontainer, |
| 159 | bool start, Error **errp) |
| 160 | { |
| 161 | error_setg_errno(errp, ENOTSUP, "Not supported"); |
| 162 | return -ENOTSUP; |
| 163 | } |
| 164 | |
| 165 | static int vfio_user_query_dirty_bitmap(const VFIOContainer *bcontainer, |
| 166 | VFIOBitmap *vbmap, hwaddr iova, |
| 167 | hwaddr size, uint64_t backend_flag, |
| 168 | Error **errp) |
| 169 | { |
| 170 | error_setg_errno(errp, ENOTSUP, "Not supported"); |
| 171 | return -ENOTSUP; |
| 172 | } |
| 173 | |
| 174 | static bool vfio_user_setup(VFIOContainer *bcontainer, Error **errp) |
| 175 | { |
| 176 | VFIOUserContainer *container = VFIO_IOMMU_USER(bcontainer); |
| 177 | |
| 178 | assert(container->proxy->dma_pgsizes != 0); |
| 179 | bcontainer->pgsizes = container->proxy->dma_pgsizes; |
| 180 | bcontainer->dma_max_mappings = container->proxy->max_dma; |
| 181 | |
| 182 | /* No live migration support yet. */ |
| 183 | bcontainer->dirty_pages_supported = false; |
| 184 | bcontainer->max_dirty_bitmap_size = container->proxy->max_bitmap; |
| 185 | bcontainer->dirty_pgsizes = container->proxy->migr_pgsize; |
| 186 | |
| 187 | return true; |
| 188 | } |
| 189 | |
| 190 | static VFIOUserContainer *vfio_user_create_container(VFIODevice *vbasedev, |
| 191 | Error **errp) |
| 192 | { |
| 193 | VFIOUserContainer *container; |
| 194 | |
| 195 | container = VFIO_IOMMU_USER(object_new(TYPE_VFIO_IOMMU_USER)); |
| 196 | container->proxy = vbasedev->proxy; |
| 197 | return container; |
| 198 | } |
| 199 | |
| 200 | /* |
| 201 | * Try to mirror vfio_container_connect() as much as possible. |
| 202 | */ |
| 203 | static VFIOUserContainer * |
| 204 | vfio_user_container_connect(AddressSpace *as, VFIODevice *vbasedev, |
| 205 | Error **errp) |
| 206 | { |
| 207 | VFIOContainer *bcontainer; |
| 208 | VFIOUserContainer *container; |
| 209 | VFIOAddressSpace *space; |
| 210 | VFIOIOMMUClass *vioc; |
| 211 | int ret; |
| 212 | |
| 213 | space = vfio_address_space_get(as); |
| 214 | |
| 215 | container = vfio_user_create_container(vbasedev, errp); |
| 216 | if (!container) { |
| 217 | goto put_space_exit; |
| 218 | } |
| 219 | |
| 220 | bcontainer = VFIO_IOMMU(container); |
| 221 | |
| 222 | ret = ram_block_uncoordinated_discard_disable(true); |
| 223 | if (ret) { |
| 224 | error_setg_errno(errp, -ret, "Cannot set discarding of RAM broken"); |
| 225 | goto free_container_exit; |
| 226 | } |
| 227 | |
| 228 | vioc = VFIO_IOMMU_GET_CLASS(bcontainer); |
| 229 | assert(vioc->setup); |
| 230 | |
| 231 | if (!vioc->setup(bcontainer, errp)) { |
| 232 | goto enable_discards_exit; |
| 233 | } |
| 234 | |
| 235 | vfio_address_space_insert(space, bcontainer); |
| 236 | |
| 237 | if (!vfio_listener_register(bcontainer, errp)) { |
| 238 | goto listener_release_exit; |
| 239 | } |
| 240 | |
| 241 | bcontainer->initialized = true; |
| 242 | |
| 243 | return container; |
| 244 | |
| 245 | listener_release_exit: |
| 246 | vfio_listener_unregister(bcontainer); |
| 247 | if (vioc->release) { |
| 248 | vioc->release(bcontainer); |
| 249 | } |
| 250 | |
| 251 | enable_discards_exit: |
| 252 | ram_block_uncoordinated_discard_disable(false); |
| 253 | |
| 254 | free_container_exit: |
| 255 | object_unref(container); |
| 256 | |
| 257 | put_space_exit: |
| 258 | vfio_address_space_put(space); |
| 259 | |
| 260 | return NULL; |
| 261 | } |
| 262 | |
| 263 | static void vfio_user_container_disconnect(VFIOUserContainer *container) |
| 264 | { |
| 265 | VFIOContainer *bcontainer = VFIO_IOMMU(container); |
| 266 | VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(bcontainer); |
| 267 | VFIOAddressSpace *space = bcontainer->space; |
| 268 | |
| 269 | ram_block_uncoordinated_discard_disable(false); |
| 270 | |
| 271 | vfio_listener_unregister(bcontainer); |
| 272 | if (vioc->release) { |
| 273 | vioc->release(bcontainer); |
| 274 | } |
| 275 | |
| 276 | object_unref(container); |
| 277 | |
| 278 | vfio_address_space_put(space); |
| 279 | } |
| 280 | |
| 281 | static bool vfio_user_device_get(VFIOUserContainer *container, |
| 282 | VFIODevice *vbasedev, Error **errp) |
| 283 | { |
| 284 | struct vfio_device_info info = { .argsz = sizeof(info) }; |
| 285 | |
| 286 | |
| 287 | if (!vfio_user_get_device_info(vbasedev->proxy, &info, errp)) { |
| 288 | return false; |
| 289 | } |
| 290 | |
| 291 | vbasedev->fd = -1; |
| 292 | |
| 293 | vfio_device_prepare(vbasedev, VFIO_IOMMU(container), &info); |
| 294 | |
| 295 | return true; |
| 296 | } |
| 297 | |
| 298 | /* |
| 299 | * vfio_user_device_attach: attach a device to a new container. |
| 300 | */ |
| 301 | static bool vfio_user_device_attach(const char *name, VFIODevice *vbasedev, |
| 302 | AddressSpace *as, Error **errp) |
| 303 | { |
| 304 | VFIOUserContainer *container; |
| 305 | |
| 306 | container = vfio_user_container_connect(as, vbasedev, errp); |
| 307 | if (container == NULL) { |
| 308 | error_prepend(errp, "failed to connect proxy"); |
| 309 | return false; |
| 310 | } |
| 311 | |
| 312 | if (!vfio_user_device_get(container, vbasedev, errp)) { |
| 313 | vfio_user_container_disconnect(container); |
| 314 | return false; |
| 315 | } |
| 316 | |
| 317 | return true; |
| 318 | } |
| 319 | |
| 320 | static void vfio_user_device_detach(VFIODevice *vbasedev) |
| 321 | { |
| 322 | VFIOUserContainer *container = VFIO_IOMMU_USER(vbasedev->bcontainer); |
| 323 | |
| 324 | vfio_device_unprepare(vbasedev); |
| 325 | |
| 326 | vfio_user_container_disconnect(container); |
| 327 | } |
| 328 | |
| 329 | static int vfio_user_pci_hot_reset(VFIODevice *vbasedev, bool single) |
| 330 | { |
| 331 | /* ->needs_reset is always false for vfio-user. */ |
| 332 | return 0; |
| 333 | } |
| 334 | |
| 335 | static void vfio_iommu_user_class_init(ObjectClass *klass, const void *data) |
| 336 | { |
| 337 | VFIOIOMMUClass *vioc = VFIO_IOMMU_CLASS(klass); |
| 338 | |
| 339 | vioc->setup = vfio_user_setup; |
| 340 | vioc->listener_begin = vfio_user_listener_begin, |
| 341 | vioc->listener_commit = vfio_user_listener_commit, |
| 342 | vioc->dma_map = vfio_user_dma_map; |
| 343 | vioc->dma_unmap = vfio_user_dma_unmap; |
| 344 | vioc->attach_device = vfio_user_device_attach; |
| 345 | vioc->detach_device = vfio_user_device_detach; |
| 346 | vioc->set_dirty_page_tracking = vfio_user_set_dirty_page_tracking; |
| 347 | vioc->query_dirty_bitmap = vfio_user_query_dirty_bitmap; |
| 348 | vioc->pci_hot_reset = vfio_user_pci_hot_reset; |
| 349 | }; |
| 350 | |
| 351 | static const TypeInfo types[] = { |
| 352 | { |
| 353 | .name = TYPE_VFIO_IOMMU_USER, |
| 354 | .parent = TYPE_VFIO_IOMMU, |
| 355 | .instance_size = sizeof(VFIOUserContainer), |
| 356 | .class_init = vfio_iommu_user_class_init, |
| 357 | }, |
| 358 | }; |
| 359 | |
| 360 | DEFINE_TYPES(types) |