diff options
Diffstat (limited to 'lib/libvfio-user.c')
-rw-r--r-- | lib/libvfio-user.c | 107 |
1 files changed, 68 insertions, 39 deletions
diff --git a/lib/libvfio-user.c b/lib/libvfio-user.c index 307f909..99a115d 100644 --- a/lib/libvfio-user.c +++ b/lib/libvfio-user.c @@ -468,30 +468,40 @@ consume_fd(int *fds, size_t nr_fds, size_t index) int handle_dma_map(vfu_ctx_t *vfu_ctx, vfu_msg_t *msg, - struct vfio_user_dma_region *region) + struct vfio_user_dma_map *dma_map) { char rstr[1024]; int fd = -1; int ret; + uint32_t prot = 0; assert(vfu_ctx != NULL); assert(msg != NULL); - assert(region != NULL); + assert(dma_map != NULL); - if (msg->in_size != sizeof(*region)) { - vfu_log(vfu_ctx, LOG_ERR, "bad size of DMA map region %zu", - msg->in_size); + if (msg->in_size < sizeof(*dma_map) || dma_map->argsz < sizeof(*dma_map)) { + vfu_log(vfu_ctx, LOG_ERR, "bad DMA map region size=%zu argsz=%u", + msg->in_size, dma_map->argsz); return ERROR_INT(EINVAL); } - snprintf(rstr, sizeof(rstr), - "[%#lx, %#lx) offset=%#lx prot=%#x flags=%#x", - region->addr, region->addr + region->size, region->offset, - region->prot, region->flags); + snprintf(rstr, sizeof(rstr), "[%#lx, %#lx) offset=%#lx flags=%#x", + dma_map->addr, dma_map->addr + dma_map->size, dma_map->offset, + dma_map->flags); vfu_log(vfu_ctx, LOG_DEBUG, "adding DMA region %s", rstr); - if (region->flags == VFIO_USER_F_DMA_REGION_MAPPABLE) { + if (dma_map->flags & VFIO_USER_F_DMA_REGION_READ) { + prot |= PROT_READ; + dma_map->flags &= ~VFIO_USER_F_DMA_REGION_READ; + } + + if (dma_map->flags & VFIO_USER_F_DMA_REGION_WRITE) { + prot |= PROT_WRITE; + dma_map->flags &= ~VFIO_USER_F_DMA_REGION_WRITE; + } + + if (dma_map->flags == VFIO_USER_F_DMA_REGION_MAPPABLE) { fd = consume_fd(msg->in_fds, msg->nr_in_fds, 0); if (fd < 0) { vfu_log(vfu_ctx, LOG_ERR, @@ -499,11 +509,13 @@ handle_dma_map(vfu_ctx_t *vfu_ctx, vfu_msg_t *msg, rstr); return -1; } + } else if (dma_map->flags != 0) { + vfu_log(vfu_ctx, LOG_ERR, "bad flags=%#x", dma_map->flags); } - ret = dma_controller_add_region(vfu_ctx->dma, (void *)region->addr, - region->size, fd, region->offset, - region->prot); + ret = dma_controller_add_region(vfu_ctx->dma, (void *)dma_map->addr, + dma_map->size, fd, dma_map->offset, + prot); if (ret < 0) { ret = errno; vfu_log(vfu_ctx, LOG_ERR, "failed to add DMA region %s: %m", rstr); @@ -521,31 +533,41 @@ handle_dma_map(vfu_ctx_t *vfu_ctx, vfu_msg_t *msg, int handle_dma_unmap(vfu_ctx_t *vfu_ctx, vfu_msg_t *msg, - struct vfio_user_dma_region *region) + struct vfio_user_dma_unmap *dma_unmap) { int ret; - char *bitmap; + char *bitmap = NULL; char rstr[1024]; assert(vfu_ctx != NULL); assert(msg != NULL); - assert(region != NULL); + assert(dma_unmap != NULL); - if (msg->in_size < sizeof(*region)) { - vfu_log(vfu_ctx, LOG_ERR, "bad size of DMA unmap region %zu", - msg->in_size); + if (msg->in_size < sizeof(*dma_unmap) || dma_unmap->argsz < sizeof(*dma_unmap)) { + vfu_log(vfu_ctx, LOG_ERR, "bad DMA unmap region size=%zu argsz=%u", + msg->in_size, dma_unmap->argsz); return ERROR_INT(EINVAL); } - snprintf(rstr, sizeof(rstr), "[%#lx, %#lx) offset=%#lx prot=%#x flags=%#x", - region->addr, region->addr + region->size, - region->offset, region->prot, region->flags); + snprintf(rstr, sizeof(rstr), "[%#lx, %#lx) flags=%#x", + dma_unmap->addr, dma_unmap->addr + dma_unmap->size, dma_unmap->flags); vfu_log(vfu_ctx, LOG_DEBUG, "removing DMA region %s", rstr); - if (region->flags == VFIO_DMA_UNMAP_FLAG_GET_DIRTY_BITMAP) { - if (msg->in_size < sizeof(*region) + sizeof(struct vfio_user_bitmap)) { - vfu_log(vfu_ctx, LOG_ERR, "bad message size %#lx", msg->in_size); + msg->out_size = sizeof(*dma_unmap); + + if (dma_unmap->flags == VFIO_DMA_UNMAP_FLAG_GET_DIRTY_BITMAP) { + if (msg->in_size < sizeof(*dma_unmap) + sizeof(*dma_unmap->bitmap) + || dma_unmap->argsz < sizeof(*dma_unmap) + sizeof(*dma_unmap->bitmap) + dma_unmap->bitmap->size) { + vfu_log(vfu_ctx, LOG_ERR, "bad message size=%#lx argsz=%#x", + msg->in_size, dma_unmap->argsz); + + /* + * Ideally we should set argsz in the reply and fail the request + * with a struct vfio_user_dma_unmap payload, however this isn't + * currently supported. Instead, we simply fail the request, + * that's what VFIO does anyway. + */ return ERROR_INT(EINVAL); } /* @@ -554,28 +576,35 @@ handle_dma_unmap(vfu_ctx_t *vfu_ctx, vfu_msg_t *msg, * the DMA controller. */ ret = dma_controller_dirty_page_get(vfu_ctx->dma, - (vfu_dma_addr_t)region->addr, - region->size, - region->bitmap->pgsize, - region->bitmap->size, + (vfu_dma_addr_t)dma_unmap->addr, + dma_unmap->size, + dma_unmap->bitmap->pgsize, + dma_unmap->bitmap->size, &bitmap); if (ret < 0) { vfu_log(vfu_ctx, LOG_ERR, "failed to get dirty page bitmap: %m"); return -1; } - msg->out_data = malloc(region->bitmap->size); - if (msg->out_data == NULL) { - return ERROR_INT(ENOMEM); - } - memcpy(msg->out_data, bitmap, region->bitmap->size); - msg->out_size = region->bitmap->size; - } else if (region->flags != 0) { - vfu_log(vfu_ctx, LOG_ERR, "bad flags=%#x", region->flags); + msg->out_size += sizeof(*dma_unmap->bitmap) + dma_unmap->bitmap->size; + } else if (dma_unmap->flags != 0) { + vfu_log(vfu_ctx, LOG_ERR, "bad flags=%#x", dma_unmap->flags); return ERROR_INT(ENOTSUP); } + + msg->out_data = malloc(msg->out_size); + if (msg->out_data == NULL) { + return ERROR_INT(ENOMEM); + } + memcpy(msg->out_data, dma_unmap, sizeof(*dma_unmap)); + + if (dma_unmap->flags & VFIO_DMA_UNMAP_FLAG_GET_DIRTY_BITMAP) { + memcpy(msg->out_data + sizeof(*dma_unmap), dma_unmap->bitmap, sizeof(*dma_unmap->bitmap)); + memcpy(msg->out_data + sizeof(*dma_unmap) + sizeof(*dma_unmap->bitmap), bitmap, dma_unmap->bitmap->size); + } + ret = dma_controller_remove_region(vfu_ctx->dma, - (void *)region->addr, - region->size, + (void *)dma_unmap->addr, + dma_unmap->size, vfu_ctx->dma_unregister, vfu_ctx); if (ret < 0) { |