diff --git a/include/vfio-user.h b/include/vfio-user.h index dc6fafaa..e1942799 100644 --- a/include/vfio-user.h +++ b/include/vfio-user.h @@ -67,6 +67,7 @@ enum vfio_user_command { VFIO_USER_DMA_WRITE = 12, VFIO_USER_DEVICE_RESET = 13, VFIO_USER_DIRTY_PAGES = 14, + VFIO_USER_REGION_WRITE_MULTI = 15, VFIO_USER_MAX, }; @@ -227,6 +228,21 @@ struct vfio_user_bitmap_range { #endif /* VFIO_REGION_TYPE_MIGRATION */ +#define VFIO_USER_MULTI_DATA 8 +#define VFIO_USER_MULTI_MAX 200 + +struct vfio_user_write_multi_data { + uint64_t offset; + uint32_t region; + uint32_t count; + char data[VFIO_USER_MULTI_DATA]; +} __attribute__((packed)); + +struct vfio_user_write_multi { + uint64_t wr_cnt; + struct vfio_user_write_multi_data wrs[VFIO_USER_MULTI_MAX]; +} __attribute__((packed)); + #ifdef __cplusplus } #endif diff --git a/lib/libvfio-user.c b/lib/libvfio-user.c index 2e2ba8f6..8ebcbb6f 100644 --- a/lib/libvfio-user.c +++ b/lib/libvfio-user.c @@ -264,10 +264,27 @@ is_valid_region_access(vfu_ctx_t *vfu_ctx, size_t size, uint16_t cmd, return false; } - if (cmd == VFIO_USER_REGION_WRITE && size - sizeof(*ra) != ra->count) { - vfu_log(vfu_ctx, LOG_ERR, "region write count too small: " - "expected %lu, got %u", size - sizeof(*ra), ra->count); - return false; + switch (cmd) { + case VFIO_USER_REGION_WRITE: + if (size - sizeof(*ra) != ra->count) { + vfu_log(vfu_ctx, LOG_ERR, "region write count too small: " + "expected %lu, got %u", size - sizeof(*ra), ra->count); + return false; + } + + break; + + case VFIO_USER_REGION_WRITE_MULTI: + if (ra->count > VFIO_USER_MULTI_DATA) { + vfu_log(vfu_ctx, LOG_ERR, "region write count too large: " + "expected %lu, got %u", size - sizeof(*ra), ra->count); + return false; + } + + break; + + default: + break; } index = ra->region; @@ -350,6 +367,67 @@ handle_region_access(vfu_ctx_t *vfu_ctx, vfu_msg_t *msg) return 0; } +static int +handle_region_write_multi(vfu_ctx_t *vfu_ctx, vfu_msg_t *msg) +{ + struct vfio_user_write_multi *wm = msg->in.iov.iov_base; + ssize_t ret; + char *buf; + + assert(vfu_ctx != NULL); + assert(msg != NULL); + + // FIXME: validate properly (?) - need tests + + if (msg->in.iov.iov_len < sizeof(wm->wr_cnt)) { + return ERROR_INT(EINVAL); + } + + if (wm->wr_cnt > VFIO_USER_MULTI_MAX) { + return ERROR_INT(EINVAL); + } + + if (msg->in.iov.iov_len != + (offsetof(struct vfio_user_write_multi, wrs) + + wm->wr_cnt * sizeof(struct vfio_user_write_multi_data))) { + return ERROR_INT(EINVAL); + } + + for (size_t i = 0; i < wm->wr_cnt; i++) { + struct vfio_user_region_access *in_ra; + + /* Re-use the very similar type. */ + in_ra = (struct vfio_user_region_access *)&wm->wrs[i]; + + /* + * We already checked total length so can be sure each entry is at least + * big enough. + */ + if (!is_valid_region_access(vfu_ctx, sizeof(wm->wrs[i]), + msg->hdr.cmd, in_ra)) { + return ERROR_INT(EINVAL); + } + + if (in_ra->count == 0) { + continue; + } + + buf = (char *)(&in_ra->data); + + ret = region_access(vfu_ctx, in_ra->region, buf, in_ra->count, + in_ra->offset, true); + if (ret != in_ra->count) { + /* FIXME we should return whatever has been accessed, not an error */ + if (ret >= 0) { + ret = ERROR_INT(EINVAL); + } + return ret; + } + } + + return 0; +} + static int handle_device_get_info(vfu_ctx_t *vfu_ctx, vfu_msg_t *msg) { @@ -1164,6 +1242,11 @@ handle_request(vfu_ctx_t *vfu_ctx, vfu_msg_t *msg) ret = handle_region_access(vfu_ctx, msg); break; + case VFIO_USER_REGION_WRITE_MULTI: + ret = handle_region_write_multi(vfu_ctx, msg); + break; + + case VFIO_USER_DEVICE_RESET: vfu_log(vfu_ctx, LOG_INFO, "device reset by client"); ret = handle_device_reset(vfu_ctx, VFU_RESET_DEVICE); @@ -1352,6 +1435,10 @@ command_needs_quiesce(vfu_ctx_t *vfu_ctx, const vfu_msg_t *msg) return true; } break; + + case VFIO_USER_REGION_WRITE_MULTI: + /* FIXME */ + return false; } return false; diff --git a/samples/client.c b/samples/client.c index da211b8f..88fbe2fb 100644 --- a/samples/client.c +++ b/samples/client.c @@ -107,6 +107,7 @@ send_version(int sock) slen = snprintf(client_caps, sizeof(client_caps), "{" "\"capabilities\":{" + "\"write_multiple\": true," "\"max_msg_fds\":%u," "\"max_data_xfer_size\":%u," "\"migration\":{" diff --git a/test/py/libvfio_user.py b/test/py/libvfio_user.py index c8525429..a2027696 100644 --- a/test/py/libvfio_user.py +++ b/test/py/libvfio_user.py @@ -158,7 +158,9 @@ VFIO_USER_DMA_WRITE = 12 VFIO_USER_DEVICE_RESET = 13 VFIO_USER_DIRTY_PAGES = 14 -VFIO_USER_MAX = 15 +# FIXME: need tests +VFIO_USER_REGION_WRITE_MULTI = 15 +VFIO_USER_MAX = 16 VFIO_USER_F_TYPE_COMMAND = 0 VFIO_USER_F_TYPE_REPLY = 1