diff options
41 files changed, 492 insertions, 227 deletions
diff --git a/backends/hostmem-file.c b/backends/hostmem-file.c index 40e1e5b..733408e 100644 --- a/backends/hostmem-file.c +++ b/backends/hostmem-file.c @@ -29,6 +29,7 @@ struct HostMemoryBackendFile { uint64_t align; bool discard_data; bool is_pmem; + bool readonly; }; static void @@ -56,7 +57,7 @@ file_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) backend->size, fb->align, (backend->share ? RAM_SHARED : 0) | (fb->is_pmem ? RAM_PMEM : 0), - fb->mem_path, errp); + fb->mem_path, fb->readonly, errp); g_free(name); #endif } @@ -151,6 +152,28 @@ static void file_memory_backend_set_pmem(Object *o, bool value, Error **errp) fb->is_pmem = value; } +static bool file_memory_backend_get_readonly(Object *obj, Error **errp) +{ + HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(obj); + + return fb->readonly; +} + +static void file_memory_backend_set_readonly(Object *obj, bool value, + Error **errp) +{ + HostMemoryBackend *backend = MEMORY_BACKEND(obj); + HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(obj); + + if (host_memory_backend_mr_inited(backend)) { + error_setg(errp, "cannot change property 'readonly' of %s.", + object_get_typename(obj)); + return; + } + + fb->readonly = value; +} + static void file_backend_unparent(Object *obj) { HostMemoryBackend *backend = MEMORY_BACKEND(obj); @@ -182,6 +205,9 @@ file_backend_class_init(ObjectClass *oc, void *data) NULL, NULL); object_class_property_add_bool(oc, "pmem", file_memory_backend_get_pmem, file_memory_backend_set_pmem); + object_class_property_add_bool(oc, "readonly", + file_memory_backend_get_readonly, + file_memory_backend_set_readonly); } static void file_backend_instance_finalize(Object *o) diff --git a/block/blkverify.c b/block/blkverify.c index 4aed53a..943e62b 100644 --- a/block/blkverify.c +++ b/block/blkverify.c @@ -31,7 +31,7 @@ typedef struct BlkverifyRequest { uint64_t bytes; int flags; - int (*request_fn)(BdrvChild *, int64_t, unsigned int, QEMUIOVector *, + int (*request_fn)(BdrvChild *, int64_t, int64_t, QEMUIOVector *, BdrvRequestFlags); int ret; /* test image result */ diff --git a/block/file-posix.c b/block/file-posix.c index 11aafa9..05079b4 100644 --- a/block/file-posix.c +++ b/block/file-posix.c @@ -2969,7 +2969,7 @@ raw_do_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int bytes, req->bytes = BDRV_MAX_LENGTH - req->offset; - assert(bdrv_check_request(req->offset, req->bytes) == 0); + bdrv_check_request(req->offset, req->bytes, &error_abort); bdrv_make_request_serialising(req, bs->bl.request_alignment); } @@ -41,7 +41,7 @@ static void bdrv_parent_cb_resize(BlockDriverState *bs); static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs, - int64_t offset, int bytes, BdrvRequestFlags flags); + int64_t offset, int64_t bytes, BdrvRequestFlags flags); static void bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore, bool ignore_bds_parents) @@ -717,10 +717,10 @@ static void tracked_request_end(BdrvTrackedRequest *req) static void tracked_request_begin(BdrvTrackedRequest *req, BlockDriverState *bs, int64_t offset, - uint64_t bytes, + int64_t bytes, enum BdrvTrackedRequestType type) { - assert(bytes <= INT64_MAX && offset <= INT64_MAX - bytes); + bdrv_check_request(offset, bytes, &error_abort); *req = (BdrvTrackedRequest){ .bs = bs, @@ -741,8 +741,10 @@ static void tracked_request_begin(BdrvTrackedRequest *req, } static bool tracked_request_overlaps(BdrvTrackedRequest *req, - int64_t offset, uint64_t bytes) + int64_t offset, int64_t bytes) { + bdrv_check_request(offset, bytes, &error_abort); + /* aaaa bbbb */ if (offset >= req->overlap_offset + req->overlap_bytes) { return false; @@ -810,8 +812,10 @@ static void tracked_request_set_serialising(BdrvTrackedRequest *req, uint64_t align) { int64_t overlap_offset = req->offset & ~(align - 1); - uint64_t overlap_bytes = ROUND_UP(req->offset + req->bytes, align) - - overlap_offset; + int64_t overlap_bytes = + ROUND_UP(req->offset + req->bytes, align) - overlap_offset; + + bdrv_check_request(req->offset, req->bytes, &error_abort); if (!req->serialising) { qatomic_inc(&req->bs->serialising_in_flight); @@ -920,26 +924,75 @@ bool coroutine_fn bdrv_make_request_serialising(BdrvTrackedRequest *req, return waited; } -int bdrv_check_request(int64_t offset, int64_t bytes) +static int bdrv_check_qiov_request(int64_t offset, int64_t bytes, + QEMUIOVector *qiov, size_t qiov_offset, + Error **errp) { - if (offset < 0 || bytes < 0) { + /* + * Check generic offset/bytes correctness + */ + + if (offset < 0) { + error_setg(errp, "offset is negative: %" PRIi64, offset); + return -EIO; + } + + if (bytes < 0) { + error_setg(errp, "bytes is negative: %" PRIi64, bytes); return -EIO; } if (bytes > BDRV_MAX_LENGTH) { + error_setg(errp, "bytes(%" PRIi64 ") exceeds maximum(%" PRIi64 ")", + bytes, BDRV_MAX_LENGTH); + return -EIO; + } + + if (offset > BDRV_MAX_LENGTH) { + error_setg(errp, "offset(%" PRIi64 ") exceeds maximum(%" PRIi64 ")", + offset, BDRV_MAX_LENGTH); return -EIO; } if (offset > BDRV_MAX_LENGTH - bytes) { + error_setg(errp, "sum of offset(%" PRIi64 ") and bytes(%" PRIi64 ") " + "exceeds maximum(%" PRIi64 ")", offset, bytes, + BDRV_MAX_LENGTH); + return -EIO; + } + + if (!qiov) { + return 0; + } + + /* + * Check qiov and qiov_offset + */ + + if (qiov_offset > qiov->size) { + error_setg(errp, "qiov_offset(%zu) overflow io vector size(%zu)", + qiov_offset, qiov->size); + return -EIO; + } + + if (bytes > qiov->size - qiov_offset) { + error_setg(errp, "bytes(%" PRIi64 ") + qiov_offset(%zu) overflow io " + "vector size(%zu)", bytes, qiov_offset, qiov->size); return -EIO; } return 0; } -static int bdrv_check_request32(int64_t offset, int64_t bytes) +int bdrv_check_request(int64_t offset, int64_t bytes, Error **errp) { - int ret = bdrv_check_request(offset, bytes); + return bdrv_check_qiov_request(offset, bytes, NULL, 0, errp); +} + +static int bdrv_check_request32(int64_t offset, int64_t bytes, + QEMUIOVector *qiov, size_t qiov_offset) +{ + int ret = bdrv_check_qiov_request(offset, bytes, qiov, qiov_offset, NULL); if (ret < 0) { return ret; } @@ -952,7 +1005,7 @@ static int bdrv_check_request32(int64_t offset, int64_t bytes) } int bdrv_pwrite_zeroes(BdrvChild *child, int64_t offset, - int bytes, BdrvRequestFlags flags) + int64_t bytes, BdrvRequestFlags flags) { return bdrv_pwritev(child, offset, bytes, NULL, BDRV_REQ_ZERO_WRITE | flags); @@ -1000,7 +1053,7 @@ int bdrv_make_zero(BdrvChild *child, BdrvRequestFlags flags) } /* See bdrv_pwrite() for the return codes */ -int bdrv_pread(BdrvChild *child, int64_t offset, void *buf, int bytes) +int bdrv_pread(BdrvChild *child, int64_t offset, void *buf, int64_t bytes) { int ret; QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes); @@ -1020,7 +1073,8 @@ int bdrv_pread(BdrvChild *child, int64_t offset, void *buf, int bytes) -EINVAL Invalid offset or number of bytes -EACCES Trying to write a read-only device */ -int bdrv_pwrite(BdrvChild *child, int64_t offset, const void *buf, int bytes) +int bdrv_pwrite(BdrvChild *child, int64_t offset, const void *buf, + int64_t bytes) { int ret; QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes); @@ -1041,7 +1095,7 @@ int bdrv_pwrite(BdrvChild *child, int64_t offset, const void *buf, int bytes) * Returns 0 on success, -errno in error cases. */ int bdrv_pwrite_sync(BdrvChild *child, int64_t offset, - const void *buf, int count) + const void *buf, int64_t count) { int ret; @@ -1072,7 +1126,7 @@ static void bdrv_co_io_em_complete(void *opaque, int ret) } static int coroutine_fn bdrv_driver_preadv(BlockDriverState *bs, - uint64_t offset, uint64_t bytes, + int64_t offset, int64_t bytes, QEMUIOVector *qiov, size_t qiov_offset, int flags) { @@ -1082,6 +1136,7 @@ static int coroutine_fn bdrv_driver_preadv(BlockDriverState *bs, QEMUIOVector local_qiov; int ret; + bdrv_check_qiov_request(offset, bytes, qiov, qiov_offset, &error_abort); assert(!(flags & ~BDRV_REQ_MASK)); assert(!(flags & BDRV_REQ_NO_FALLBACK)); @@ -1141,7 +1196,7 @@ out: } static int coroutine_fn bdrv_driver_pwritev(BlockDriverState *bs, - uint64_t offset, uint64_t bytes, + int64_t offset, int64_t bytes, QEMUIOVector *qiov, size_t qiov_offset, int flags) { @@ -1151,6 +1206,7 @@ static int coroutine_fn bdrv_driver_pwritev(BlockDriverState *bs, QEMUIOVector local_qiov; int ret; + bdrv_check_qiov_request(offset, bytes, qiov, qiov_offset, &error_abort); assert(!(flags & ~BDRV_REQ_MASK)); assert(!(flags & BDRV_REQ_NO_FALLBACK)); @@ -1221,14 +1277,16 @@ emulate_flags: } static int coroutine_fn -bdrv_driver_pwritev_compressed(BlockDriverState *bs, uint64_t offset, - uint64_t bytes, QEMUIOVector *qiov, +bdrv_driver_pwritev_compressed(BlockDriverState *bs, int64_t offset, + int64_t bytes, QEMUIOVector *qiov, size_t qiov_offset) { BlockDriver *drv = bs->drv; QEMUIOVector local_qiov; int ret; + bdrv_check_qiov_request(offset, bytes, qiov, qiov_offset, &error_abort); + if (!drv) { return -ENOMEDIUM; } @@ -1254,7 +1312,7 @@ bdrv_driver_pwritev_compressed(BlockDriverState *bs, uint64_t offset, } static int coroutine_fn bdrv_co_do_copy_on_readv(BdrvChild *child, - int64_t offset, unsigned int bytes, QEMUIOVector *qiov, + int64_t offset, int64_t bytes, QEMUIOVector *qiov, size_t qiov_offset, int flags) { BlockDriverState *bs = child->bs; @@ -1269,13 +1327,15 @@ static int coroutine_fn bdrv_co_do_copy_on_readv(BdrvChild *child, BlockDriver *drv = bs->drv; int64_t cluster_offset; int64_t cluster_bytes; - size_t skip_bytes; + int64_t skip_bytes; int ret; int max_transfer = MIN_NON_ZERO(bs->bl.max_transfer, BDRV_REQUEST_MAX_BYTES); - unsigned int progress = 0; + int64_t progress = 0; bool skip_write; + bdrv_check_qiov_request(offset, bytes, qiov, qiov_offset, &error_abort); + if (!drv) { return -ENOMEDIUM; } @@ -1416,15 +1476,16 @@ err: * reads; any other features must be implemented by the caller. */ static int coroutine_fn bdrv_aligned_preadv(BdrvChild *child, - BdrvTrackedRequest *req, int64_t offset, unsigned int bytes, + BdrvTrackedRequest *req, int64_t offset, int64_t bytes, int64_t align, QEMUIOVector *qiov, size_t qiov_offset, int flags) { BlockDriverState *bs = child->bs; int64_t total_bytes, max_bytes; int ret = 0; - uint64_t bytes_remaining = bytes; + int64_t bytes_remaining = bytes; int max_transfer; + bdrv_check_qiov_request(offset, bytes, qiov, qiov_offset, &error_abort); assert(is_power_of_2(align)); assert((offset & (align - 1)) == 0); assert((bytes & (align - 1)) == 0); @@ -1486,7 +1547,7 @@ static int coroutine_fn bdrv_aligned_preadv(BdrvChild *child, } while (bytes_remaining) { - int num; + int64_t num; if (max_bytes) { num = MIN(bytes_remaining, MIN(max_bytes, max_transfer)); @@ -1548,8 +1609,12 @@ static bool bdrv_init_padding(BlockDriverState *bs, int64_t offset, int64_t bytes, BdrvRequestPadding *pad) { - uint64_t align = bs->bl.request_alignment; - size_t sum; + int64_t align = bs->bl.request_alignment; + int64_t sum; + + bdrv_check_request(offset, bytes, &error_abort); + assert(align <= INT_MAX); /* documented in block/block_int.h */ + assert(align <= SIZE_MAX / 2); /* so we can allocate the buffer */ memset(pad, 0, sizeof(*pad)); @@ -1589,7 +1654,7 @@ static int bdrv_padding_rmw_read(BdrvChild *child, assert(req->serialising && pad->buf); if (pad->head || pad->merge_reads) { - uint64_t bytes = pad->merge_reads ? pad->buf_len : align; + int64_t bytes = pad->merge_reads ? pad->buf_len : align; qemu_iovec_init_buf(&local_qiov, pad->buf, bytes); @@ -1644,6 +1709,7 @@ static void bdrv_padding_destroy(BdrvRequestPadding *pad) qemu_vfree(pad->buf); qemu_iovec_destroy(&pad->local_qiov); } + memset(pad, 0, sizeof(*pad)); } /* @@ -1653,40 +1719,55 @@ static void bdrv_padding_destroy(BdrvRequestPadding *pad) * read of padding, bdrv_padding_rmw_read() should be called separately if * needed. * - * All parameters except @bs are in-out: they represent original request at - * function call and padded (if padding needed) at function finish. - * - * Function always succeeds. + * Request parameters (@qiov, &qiov_offset, &offset, &bytes) are in-out: + * - on function start they represent original request + * - on failure or when padding is not needed they are unchanged + * - on success when padding is needed they represent padded request */ -static bool bdrv_pad_request(BlockDriverState *bs, - QEMUIOVector **qiov, size_t *qiov_offset, - int64_t *offset, unsigned int *bytes, - BdrvRequestPadding *pad) +static int bdrv_pad_request(BlockDriverState *bs, + QEMUIOVector **qiov, size_t *qiov_offset, + int64_t *offset, int64_t *bytes, + BdrvRequestPadding *pad, bool *padded) { + int ret; + + bdrv_check_qiov_request(*offset, *bytes, *qiov, *qiov_offset, &error_abort); + if (!bdrv_init_padding(bs, *offset, *bytes, pad)) { - return false; + if (padded) { + *padded = false; + } + return 0; } - qemu_iovec_init_extended(&pad->local_qiov, pad->buf, pad->head, - *qiov, *qiov_offset, *bytes, - pad->buf + pad->buf_len - pad->tail, pad->tail); + ret = qemu_iovec_init_extended(&pad->local_qiov, pad->buf, pad->head, + *qiov, *qiov_offset, *bytes, + pad->buf + pad->buf_len - pad->tail, + pad->tail); + if (ret < 0) { + bdrv_padding_destroy(pad); + return ret; + } *bytes += pad->head + pad->tail; *offset -= pad->head; *qiov = &pad->local_qiov; *qiov_offset = 0; + if (padded) { + *padded = true; + } - return true; + return 0; } int coroutine_fn bdrv_co_preadv(BdrvChild *child, - int64_t offset, unsigned int bytes, QEMUIOVector *qiov, + int64_t offset, int64_t bytes, QEMUIOVector *qiov, BdrvRequestFlags flags) { return bdrv_co_preadv_part(child, offset, bytes, qiov, 0, flags); } int coroutine_fn bdrv_co_preadv_part(BdrvChild *child, - int64_t offset, unsigned int bytes, + int64_t offset, int64_t bytes, QEMUIOVector *qiov, size_t qiov_offset, BdrvRequestFlags flags) { @@ -1695,13 +1776,13 @@ int coroutine_fn bdrv_co_preadv_part(BdrvChild *child, BdrvRequestPadding pad; int ret; - trace_bdrv_co_preadv(bs, offset, bytes, flags); + trace_bdrv_co_preadv_part(bs, offset, bytes, flags); if (!bdrv_is_inserted(bs)) { return -ENOMEDIUM; } - ret = bdrv_check_request32(offset, bytes); + ret = bdrv_check_request32(offset, bytes, qiov, qiov_offset); if (ret < 0) { return ret; } @@ -1725,7 +1806,11 @@ int coroutine_fn bdrv_co_preadv_part(BdrvChild *child, flags |= BDRV_REQ_COPY_ON_READ; } - bdrv_pad_request(bs, &qiov, &qiov_offset, &offset, &bytes, &pad); + ret = bdrv_pad_request(bs, &qiov, &qiov_offset, &offset, &bytes, &pad, + NULL); + if (ret < 0) { + return ret; + } tracked_request_begin(&req, bs, offset, bytes, BDRV_TRACKED_READ); ret = bdrv_aligned_preadv(child, &req, offset, bytes, @@ -1740,7 +1825,7 @@ int coroutine_fn bdrv_co_preadv_part(BdrvChild *child, } static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs, - int64_t offset, int bytes, BdrvRequestFlags flags) + int64_t offset, int64_t bytes, BdrvRequestFlags flags) { BlockDriver *drv = bs->drv; QEMUIOVector qiov; @@ -1755,6 +1840,8 @@ static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs, bs->bl.request_alignment); int max_transfer = MIN_NON_ZERO(bs->bl.max_transfer, MAX_BOUNCE_BUFFER); + bdrv_check_request(offset, bytes, &error_abort); + if (!drv) { return -ENOMEDIUM; } @@ -1770,7 +1857,7 @@ static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs, assert(max_write_zeroes >= bs->bl.request_alignment); while (bytes > 0 && !ret) { - int num = bytes; + int64_t num = bytes; /* Align request. Block drivers can expect the "bulk" of the request * to be aligned, and that unaligned requests do not cross cluster @@ -1851,11 +1938,12 @@ fail: } static inline int coroutine_fn -bdrv_co_write_req_prepare(BdrvChild *child, int64_t offset, uint64_t bytes, +bdrv_co_write_req_prepare(BdrvChild *child, int64_t offset, int64_t bytes, BdrvTrackedRequest *req, int flags) { BlockDriverState *bs = child->bs; - int64_t end_sector = DIV_ROUND_UP(offset + bytes, BDRV_SECTOR_SIZE); + + bdrv_check_request(offset, bytes, &error_abort); if (bs->read_only) { return -EPERM; @@ -1882,7 +1970,8 @@ bdrv_co_write_req_prepare(BdrvChild *child, int64_t offset, uint64_t bytes, assert(req->overlap_offset <= offset); assert(offset + bytes <= req->overlap_offset + req->overlap_bytes); - assert(end_sector <= bs->total_sectors || child->perm & BLK_PERM_RESIZE); + assert(offset + bytes <= bs->total_sectors * BDRV_SECTOR_SIZE || + child->perm & BLK_PERM_RESIZE); switch (req->type) { case BDRV_TRACKED_WRITE: @@ -1903,12 +1992,14 @@ bdrv_co_write_req_prepare(BdrvChild *child, int64_t offset, uint64_t bytes, } static inline void coroutine_fn -bdrv_co_write_req_finish(BdrvChild *child, int64_t offset, uint64_t bytes, +bdrv_co_write_req_finish(BdrvChild *child, int64_t offset, int64_t bytes, BdrvTrackedRequest *req, int ret) { int64_t end_sector = DIV_ROUND_UP(offset + bytes, BDRV_SECTOR_SIZE); BlockDriverState *bs = child->bs; + bdrv_check_request(offset, bytes, &error_abort); + qatomic_inc(&bs->write_gen); /* @@ -1945,16 +2036,18 @@ bdrv_co_write_req_finish(BdrvChild *child, int64_t offset, uint64_t bytes, * after possibly fragmenting it. */ static int coroutine_fn bdrv_aligned_pwritev(BdrvChild *child, - BdrvTrackedRequest *req, int64_t offset, unsigned int bytes, + BdrvTrackedRequest *req, int64_t offset, int64_t bytes, int64_t align, QEMUIOVector *qiov, size_t qiov_offset, int flags) { BlockDriverState *bs = child->bs; BlockDriver *drv = bs->drv; int ret; - uint64_t bytes_remaining = bytes; + int64_t bytes_remaining = bytes; int max_transfer; + bdrv_check_qiov_request(offset, bytes, qiov, qiov_offset, &error_abort); + if (!drv) { return -ENOMEDIUM; } @@ -1966,7 +2059,6 @@ static int coroutine_fn bdrv_aligned_pwritev(BdrvChild *child, assert(is_power_of_2(align)); assert((offset & (align - 1)) == 0); assert((bytes & (align - 1)) == 0); - assert(!qiov || qiov_offset + bytes <= qiov->size); max_transfer = QEMU_ALIGN_DOWN(MIN_NON_ZERO(bs->bl.max_transfer, INT_MAX), align); @@ -2028,7 +2120,7 @@ static int coroutine_fn bdrv_aligned_pwritev(BdrvChild *child, static int coroutine_fn bdrv_co_do_zero_pwritev(BdrvChild *child, int64_t offset, - unsigned int bytes, + int64_t bytes, BdrvRequestFlags flags, BdrvTrackedRequest *req) { @@ -2065,7 +2157,7 @@ static int coroutine_fn bdrv_co_do_zero_pwritev(BdrvChild *child, assert(!bytes || (offset & (align - 1)) == 0); if (bytes >= align) { /* Write the aligned part in the middle. */ - uint64_t aligned_bytes = bytes & ~(align - 1); + int64_t aligned_bytes = bytes & ~(align - 1); ret = bdrv_aligned_pwritev(child, req, offset, aligned_bytes, align, NULL, 0, flags); if (ret < 0) { @@ -2095,14 +2187,14 @@ out: * Handle a write request in coroutine context */ int coroutine_fn bdrv_co_pwritev(BdrvChild *child, - int64_t offset, unsigned int bytes, QEMUIOVector *qiov, + int64_t offset, int64_t bytes, QEMUIOVector *qiov, BdrvRequestFlags flags) { return bdrv_co_pwritev_part(child, offset, bytes, qiov, 0, flags); } int coroutine_fn bdrv_co_pwritev_part(BdrvChild *child, - int64_t offset, unsigned int bytes, QEMUIOVector *qiov, size_t qiov_offset, + int64_t offset, int64_t bytes, QEMUIOVector *qiov, size_t qiov_offset, BdrvRequestFlags flags) { BlockDriverState *bs = child->bs; @@ -2110,14 +2202,15 @@ int coroutine_fn bdrv_co_pwritev_part(BdrvChild *child, uint64_t align = bs->bl.request_alignment; BdrvRequestPadding pad; int ret; + bool padded = false; - trace_bdrv_co_pwritev(child->bs, offset, bytes, flags); + trace_bdrv_co_pwritev_part(child->bs, offset, bytes, flags); if (!bdrv_is_inserted(bs)) { return -ENOMEDIUM; } - ret = bdrv_check_request32(offset, bytes); + ret = bdrv_check_request32(offset, bytes, qiov, qiov_offset); if (ret < 0) { return ret; } @@ -2141,20 +2234,35 @@ int coroutine_fn bdrv_co_pwritev_part(BdrvChild *child, return 0; } + if (!(flags & BDRV_REQ_ZERO_WRITE)) { + /* + * Pad request for following read-modify-write cycle. + * bdrv_co_do_zero_pwritev() does aligning by itself, so, we do + * alignment only if there is no ZERO flag. + */ + ret = bdrv_pad_request(bs, &qiov, &qiov_offset, &offset, &bytes, &pad, + &padded); + if (ret < 0) { + return ret; + } + } + bdrv_inc_in_flight(bs); - /* - * Align write if necessary by performing a read-modify-write cycle. - * Pad qiov with the read parts and be sure to have a tracked request not - * only for bdrv_aligned_pwritev, but also for the reads of the RMW cycle. - */ tracked_request_begin(&req, bs, offset, bytes, BDRV_TRACKED_WRITE); if (flags & BDRV_REQ_ZERO_WRITE) { + assert(!padded); ret = bdrv_co_do_zero_pwritev(child, offset, bytes, flags, &req); goto out; } - if (bdrv_pad_request(bs, &qiov, &qiov_offset, &offset, &bytes, &pad)) { + if (padded) { + /* + * Request was unaligned to request_alignment and therefore + * padded. We are going to do read-modify-write, and must + * serialize the request to prevent interactions of the + * widened region with other transactions. + */ bdrv_make_request_serialising(&req, align); bdrv_padding_rmw_read(child, &req, &pad, false); } @@ -2172,7 +2280,7 @@ out: } int coroutine_fn bdrv_co_pwrite_zeroes(BdrvChild *child, int64_t offset, - int bytes, BdrvRequestFlags flags) + int64_t bytes, BdrvRequestFlags flags) { trace_bdrv_co_pwrite_zeroes(child->bs, offset, bytes, flags); @@ -2847,7 +2955,7 @@ int coroutine_fn bdrv_co_pdiscard(BdrvChild *child, int64_t offset, return -EPERM; } - ret = bdrv_check_request(offset, bytes); + ret = bdrv_check_request(offset, bytes, NULL); if (ret < 0) { return ret; } @@ -3093,8 +3201,8 @@ void bdrv_unregister_buf(BlockDriverState *bs, void *host) } static int coroutine_fn bdrv_co_copy_range_internal( - BdrvChild *src, uint64_t src_offset, BdrvChild *dst, - uint64_t dst_offset, uint64_t bytes, + BdrvChild *src, int64_t src_offset, BdrvChild *dst, + int64_t dst_offset, int64_t bytes, BdrvRequestFlags read_flags, BdrvRequestFlags write_flags, bool recurse_src) { @@ -3108,7 +3216,7 @@ static int coroutine_fn bdrv_co_copy_range_internal( if (!dst || !dst->bs || !bdrv_is_inserted(dst->bs)) { return -ENOMEDIUM; } - ret = bdrv_check_request32(dst_offset, bytes); + ret = bdrv_check_request32(dst_offset, bytes, NULL, 0); if (ret) { return ret; } @@ -3119,7 +3227,7 @@ static int coroutine_fn bdrv_co_copy_range_internal( if (!src || !src->bs || !bdrv_is_inserted(src->bs)) { return -ENOMEDIUM; } - ret = bdrv_check_request32(src_offset, bytes); + ret = bdrv_check_request32(src_offset, bytes, NULL, 0); if (ret) { return ret; } @@ -3172,9 +3280,9 @@ static int coroutine_fn bdrv_co_copy_range_internal( * * See the comment of bdrv_co_copy_range for the parameter and return value * semantics. */ -int coroutine_fn bdrv_co_copy_range_from(BdrvChild *src, uint64_t src_offset, - BdrvChild *dst, uint64_t dst_offset, - uint64_t bytes, +int coroutine_fn bdrv_co_copy_range_from(BdrvChild *src, int64_t src_offset, + BdrvChild *dst, int64_t dst_offset, + int64_t bytes, BdrvRequestFlags read_flags, BdrvRequestFlags write_flags) { @@ -3188,9 +3296,9 @@ int coroutine_fn bdrv_co_copy_range_from(BdrvChild *src, uint64_t src_offset, * * See the comment of bdrv_co_copy_range for the parameter and return value * semantics. */ -int coroutine_fn bdrv_co_copy_range_to(BdrvChild *src, uint64_t src_offset, - BdrvChild *dst, uint64_t dst_offset, - uint64_t bytes, +int coroutine_fn bdrv_co_copy_range_to(BdrvChild *src, int64_t src_offset, + BdrvChild *dst, int64_t dst_offset, + int64_t bytes, BdrvRequestFlags read_flags, BdrvRequestFlags write_flags) { @@ -3200,9 +3308,9 @@ int coroutine_fn bdrv_co_copy_range_to(BdrvChild *src, uint64_t src_offset, bytes, read_flags, write_flags, false); } -int coroutine_fn bdrv_co_copy_range(BdrvChild *src, uint64_t src_offset, - BdrvChild *dst, uint64_t dst_offset, - uint64_t bytes, BdrvRequestFlags read_flags, +int coroutine_fn bdrv_co_copy_range(BdrvChild *src, int64_t src_offset, + BdrvChild *dst, int64_t dst_offset, + int64_t bytes, BdrvRequestFlags read_flags, BdrvRequestFlags write_flags) { return bdrv_co_copy_range_from(src, src_offset, @@ -3249,10 +3357,8 @@ int coroutine_fn bdrv_co_truncate(BdrvChild *child, int64_t offset, bool exact, return -EINVAL; } - ret = bdrv_check_request(offset, 0); + ret = bdrv_check_request(offset, 0, errp); if (ret < 0) { - error_setg(errp, "Required too big image size, it must be not greater " - "than %" PRId64, BDRV_MAX_LENGTH); return ret; } diff --git a/block/nbd.c b/block/nbd.c index 42e10c7..b3cbbeb 100644 --- a/block/nbd.c +++ b/block/nbd.c @@ -235,7 +235,14 @@ static void nbd_client_detach_aio_context(BlockDriverState *bs) /* Timer is deleted in nbd_client_co_drain_begin() */ assert(!s->reconnect_delay_timer); - qio_channel_detach_aio_context(QIO_CHANNEL(s->ioc)); + /* + * If reconnect is in progress we may have no ->ioc. It will be + * re-instantiated in the proper aio context once the connection is + * reestablished. + */ + if (s->ioc) { + qio_channel_detach_aio_context(QIO_CHANNEL(s->ioc)); + } } static void nbd_client_attach_aio_context_bh(void *opaque) @@ -243,13 +250,15 @@ static void nbd_client_attach_aio_context_bh(void *opaque) BlockDriverState *bs = opaque; BDRVNBDState *s = (BDRVNBDState *)bs->opaque; - /* - * The node is still drained, so we know the coroutine has yielded in - * nbd_read_eof(), the only place where bs->in_flight can reach 0, or it is - * entered for the first time. Both places are safe for entering the - * coroutine. - */ - qemu_aio_coroutine_enter(bs->aio_context, s->connection_co); + if (s->connection_co) { + /* + * The node is still drained, so we know the coroutine has yielded in + * nbd_read_eof(), the only place where bs->in_flight can reach 0, or + * it is entered for the first time. Both places are safe for entering + * the coroutine. + */ + qemu_aio_coroutine_enter(bs->aio_context, s->connection_co); + } bdrv_dec_in_flight(bs); } diff --git a/block/throttle-groups.c b/block/throttle-groups.c index abd16ed..fb203c3 100644 --- a/block/throttle-groups.c +++ b/block/throttle-groups.c @@ -358,12 +358,15 @@ static void schedule_next_request(ThrottleGroupMember *tgm, bool is_write) * @is_write: the type of operation (read/write) */ void coroutine_fn throttle_group_co_io_limits_intercept(ThrottleGroupMember *tgm, - unsigned int bytes, + int64_t bytes, bool is_write) { bool must_wait; ThrottleGroupMember *token; ThrottleGroup *tg = container_of(tgm->throttle_state, ThrottleGroup, ts); + + assert(bytes >= 0); + qemu_mutex_lock(&tg->lock); /* First we check if this I/O has to be throttled. */ diff --git a/block/trace-events b/block/trace-events index ecbc32a..1a12d63 100644 --- a/block/trace-events +++ b/block/trace-events @@ -11,12 +11,12 @@ blk_root_attach(void *child, void *blk, void *bs) "child %p blk %p bs %p" blk_root_detach(void *child, void *blk, void *bs) "child %p blk %p bs %p" # io.c -bdrv_co_preadv(void *bs, int64_t offset, int64_t nbytes, unsigned int flags) "bs %p offset %"PRId64" nbytes %"PRId64" flags 0x%x" -bdrv_co_pwritev(void *bs, int64_t offset, int64_t nbytes, unsigned int flags) "bs %p offset %"PRId64" nbytes %"PRId64" flags 0x%x" -bdrv_co_pwrite_zeroes(void *bs, int64_t offset, int count, int flags) "bs %p offset %"PRId64" count %d flags 0x%x" -bdrv_co_do_copy_on_readv(void *bs, int64_t offset, unsigned int bytes, int64_t cluster_offset, int64_t cluster_bytes) "bs %p offset %"PRId64" bytes %u cluster_offset %"PRId64" cluster_bytes %"PRId64 -bdrv_co_copy_range_from(void *src, uint64_t src_offset, void *dst, uint64_t dst_offset, uint64_t bytes, int read_flags, int write_flags) "src %p offset %"PRIu64" dst %p offset %"PRIu64" bytes %"PRIu64" rw flags 0x%x 0x%x" -bdrv_co_copy_range_to(void *src, uint64_t src_offset, void *dst, uint64_t dst_offset, uint64_t bytes, int read_flags, int write_flags) "src %p offset %"PRIu64" dst %p offset %"PRIu64" bytes %"PRIu64" rw flags 0x%x 0x%x" +bdrv_co_preadv_part(void *bs, int64_t offset, int64_t bytes, unsigned int flags) "bs %p offset %" PRId64 " bytes %" PRId64 " flags 0x%x" +bdrv_co_pwritev_part(void *bs, int64_t offset, int64_t bytes, unsigned int flags) "bs %p offset %" PRId64 " bytes %" PRId64 " flags 0x%x" +bdrv_co_pwrite_zeroes(void *bs, int64_t offset, int64_t bytes, int flags) "bs %p offset %" PRId64 " bytes %" PRId64 " flags 0x%x" +bdrv_co_do_copy_on_readv(void *bs, int64_t offset, int64_t bytes, int64_t cluster_offset, int64_t cluster_bytes) "bs %p offset %" PRId64 " bytes %" PRId64 " cluster_offset %" PRId64 " cluster_bytes %" PRId64 +bdrv_co_copy_range_from(void *src, int64_t src_offset, void *dst, int64_t dst_offset, int64_t bytes, int read_flags, int write_flags) "src %p offset %" PRId64 " dst %p offset %" PRId64 " bytes %" PRId64 " rw flags 0x%x 0x%x" +bdrv_co_copy_range_to(void *src, int64_t src_offset, void *dst, int64_t dst_offset, int64_t bytes, int read_flags, int write_flags) "src %p offset %" PRId64 " dst %p offset %" PRId64 " bytes %" PRId64 " rw flags 0x%x 0x%x" # stream.c stream_one_iteration(void *s, int64_t offset, uint64_t bytes, int is_allocated) "s %p offset %" PRId64 " bytes %" PRIu64 " is_allocated %d" diff --git a/docs/nvdimm.txt b/docs/nvdimm.txt index c2c6e44..0aae682 100644 --- a/docs/nvdimm.txt +++ b/docs/nvdimm.txt @@ -17,8 +17,8 @@ following command line options: -machine pc,nvdimm -m $RAM_SIZE,slots=$N,maxmem=$MAX_SIZE - -object memory-backend-file,id=mem1,share=on,mem-path=$PATH,size=$NVDIMM_SIZE - -device nvdimm,id=nvdimm1,memdev=mem1 + -object memory-backend-file,id=mem1,share=on,mem-path=$PATH,size=$NVDIMM_SIZE,readonly=off + -device nvdimm,id=nvdimm1,memdev=mem1,unarmed=off Where, @@ -31,9 +31,10 @@ Where, of normal RAM devices and vNVDIMM devices, e.g. $MAX_SIZE should be >= $RAM_SIZE + $NVDIMM_SIZE here. - - "object memory-backend-file,id=mem1,share=on,mem-path=$PATH,size=$NVDIMM_SIZE" - creates a backend storage of size $NVDIMM_SIZE on a file $PATH. All - accesses to the virtual NVDIMM device go to the file $PATH. + - "object memory-backend-file,id=mem1,share=on,mem-path=$PATH, + size=$NVDIMM_SIZE,readonly=off" creates a backend storage of size + $NVDIMM_SIZE on a file $PATH. All accesses to the virtual NVDIMM device go + to the file $PATH. "share=on/off" controls the visibility of guest writes. If "share=on", then guest writes will be applied to the backend @@ -42,8 +43,17 @@ Where, "share=off", then guest writes won't be applied to the backend file and thus will be invisible to other guests. - - "device nvdimm,id=nvdimm1,memdev=mem1" creates a virtual NVDIMM - device whose storage is provided by above memory backend device. + "readonly=on/off" controls whether the file $PATH is opened read-only or + read/write (default). + + - "device nvdimm,id=nvdimm1,memdev=mem1,unarmed=off" creates a read/write + virtual NVDIMM device whose storage is provided by above memory backend + device. + + "unarmed" controls the ACPI NFIT NVDIMM Region Mapping Structure "NVDIMM + State Flags" Bit 3 indicating that the device is "unarmed" and cannot accept + persistent writes. Linux guest drivers set the device to read-only when this + bit is present. Set unarmed to on when the memdev has readonly=on. Multiple vNVDIMM devices can be created if multiple pairs of "-object" and "-device" are provided. @@ -81,3 +81,4 @@ config XLNX_ZYNQMP bool select REGISTER select CAN_BUS + select PTIMER diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index 13cc42d..be017b9 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -52,6 +52,7 @@ config EXYNOS4 select PTIMER select SDHCI select USB_EHCI_SYSBUS + select OR_IRQ config HIGHBANK bool @@ -336,6 +337,7 @@ config STM32F205_SOC config STM32F405_SOC bool select ARM_V7M + select OR_IRQ select STM32F4XX_SYSCFG select STM32F4XX_EXTI @@ -352,6 +354,7 @@ config XLNX_ZYNQMP_ARM select XILINX_AXI select XILINX_SPIPS select XLNX_ZYNQMP + select XLNX_ZDMA config XLNX_VERSAL bool @@ -360,6 +363,8 @@ config XLNX_VERSAL select CADENCE select VIRTIO_MMIO select UNIMP + select XLNX_ZDMA + select XLNX_ZYNQMP config NPCM7XX bool diff --git a/hw/arm/digic_boards.c b/hw/arm/digic_boards.c index be12873..6cdc1d8 100644 --- a/hw/arm/digic_boards.c +++ b/hw/arm/digic_boards.c @@ -142,7 +142,7 @@ static void canon_a1100_init(MachineState *machine) static void canon_a1100_machine_init(MachineClass *mc) { - mc->desc = "Canon PowerShot A1100 IS"; + mc->desc = "Canon PowerShot A1100 IS (ARM946)"; mc->init = &canon_a1100_init; mc->ignore_memory_transaction_failures = true; mc->default_ram_size = 64 * MiB; diff --git a/hw/arm/microbit.c b/hw/arm/microbit.c index 0947491..e949433 100644 --- a/hw/arm/microbit.c +++ b/hw/arm/microbit.c @@ -64,7 +64,7 @@ static void microbit_machine_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); - mc->desc = "BBC micro:bit"; + mc->desc = "BBC micro:bit (Cortex-M0)"; mc->init = microbit_init; mc->max_cpus = 1; } diff --git a/hw/arm/netduino2.c b/hw/arm/netduino2.c index 8f10334..1733b71 100644 --- a/hw/arm/netduino2.c +++ b/hw/arm/netduino2.c @@ -54,7 +54,7 @@ static void netduino2_init(MachineState *machine) static void netduino2_machine_init(MachineClass *mc) { - mc->desc = "Netduino 2 Machine"; + mc->desc = "Netduino 2 Machine (Cortex-M3)"; mc->init = netduino2_init; mc->ignore_memory_transaction_failures = true; } diff --git a/hw/arm/netduinoplus2.c b/hw/arm/netduinoplus2.c index 68abd3e..d3ad7a2 100644 --- a/hw/arm/netduinoplus2.c +++ b/hw/arm/netduinoplus2.c @@ -55,7 +55,7 @@ static void netduinoplus2_init(MachineState *machine) static void netduinoplus2_machine_init(MachineClass *mc) { - mc->desc = "Netduino Plus 2 Machine"; + mc->desc = "Netduino Plus 2 Machine (Cortex-M4)"; mc->init = netduinoplus2_init; } diff --git a/hw/arm/orangepi.c b/hw/arm/orangepi.c index d6306df..40cdb5c 100644 --- a/hw/arm/orangepi.c +++ b/hw/arm/orangepi.c @@ -113,7 +113,7 @@ static void orangepi_init(MachineState *machine) static void orangepi_machine_init(MachineClass *mc) { - mc->desc = "Orange Pi PC"; + mc->desc = "Orange Pi PC (Cortex-A7)"; mc->init = orangepi_init; mc->block_default_type = IF_SD; mc->units_per_default_bus = 1; diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index bbca0e9..98b99d4 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -801,7 +801,7 @@ static void smmuv3_notify_iova(IOMMUMemoryRegion *mr, { SMMUDevice *sdev = container_of(mr, SMMUDevice, iommu); IOMMUTLBEvent event; - uint8_t granule = tg; + uint8_t granule; if (!tg) { SMMUEventInfo event = {.inval_ste_allowed = true}; @@ -821,6 +821,8 @@ static void smmuv3_notify_iova(IOMMUMemoryRegion *mr, return; } granule = tt->granule_sz; + } else { + granule = tg * 2 + 10; } event.type = IOMMU_NOTIFIER_UNMAP; diff --git a/hw/arm/stellaris.c b/hw/arm/stellaris.c index ad72c09..27292ec 100644 --- a/hw/arm/stellaris.c +++ b/hw/arm/stellaris.c @@ -1538,7 +1538,7 @@ static void lm3s811evb_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); - mc->desc = "Stellaris LM3S811EVB"; + mc->desc = "Stellaris LM3S811EVB (Cortex-M3)"; mc->init = lm3s811evb_init; mc->ignore_memory_transaction_failures = true; mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m3"); @@ -1554,7 +1554,7 @@ static void lm3s6965evb_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); - mc->desc = "Stellaris LM3S6965EVB"; + mc->desc = "Stellaris LM3S6965EVB (Cortex-M3)"; mc->init = lm3s6965evb_init; mc->ignore_memory_transaction_failures = true; mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m3"); diff --git a/hw/char/exynos4210_uart.c b/hw/char/exynos4210_uart.c index 6361df2..80d401a 100644 --- a/hw/char/exynos4210_uart.c +++ b/hw/char/exynos4210_uart.c @@ -519,6 +519,7 @@ static uint64_t exynos4210_uart_read(void *opaque, hwaddr offset, s->reg[I_(UTRSTAT)] &= ~UTRSTAT_Rx_BUFFER_DATA_READY; res = s->reg[I_(URXH)]; } + qemu_chr_fe_accept_input(&s->chr); exynos4210_uart_update_dmabusy(s); trace_exynos_uart_read(s->channel, offset, exynos4210_uart_regname(offset), res); @@ -553,7 +554,11 @@ static int exynos4210_uart_can_receive(void *opaque) { Exynos4210UartState *s = (Exynos4210UartState *)opaque; - return fifo_empty_elements_number(&s->rx); + if (s->reg[I_(UFCON)] & UFCON_FIFO_ENABLE) { + return fifo_empty_elements_number(&s->rx); + } else { + return !(s->reg[I_(UTRSTAT)] & UTRSTAT_Rx_BUFFER_DATA_READY); + } } static void exynos4210_uart_receive(void *opaque, const uint8_t *buf, int size) diff --git a/hw/dma/Kconfig b/hw/dma/Kconfig index d67492d..5d6be1a 100644 --- a/hw/dma/Kconfig +++ b/hw/dma/Kconfig @@ -18,6 +18,9 @@ config ZYNQ_DEVCFG bool select REGISTER +config XLNX_ZDMA + bool + config STP2000 bool diff --git a/hw/dma/meson.build b/hw/dma/meson.build index b991d76..47b4a7c 100644 --- a/hw/dma/meson.build +++ b/hw/dma/meson.build @@ -9,7 +9,7 @@ softmmu_ss.add(when: 'CONFIG_ZYNQ_DEVCFG', if_true: files('xlnx-zynq-devcfg.c')) softmmu_ss.add(when: 'CONFIG_ETRAXFS', if_true: files('etraxfs_dma.c')) softmmu_ss.add(when: 'CONFIG_STP2000', if_true: files('sparc32_dma.c')) softmmu_ss.add(when: 'CONFIG_XLNX_ZYNQMP_ARM', if_true: files('xlnx_dpdma.c')) -softmmu_ss.add(when: 'CONFIG_XLNX_ZYNQMP_ARM', if_true: files('xlnx-zdma.c')) +softmmu_ss.add(when: 'CONFIG_XLNX_ZDMA', if_true: files('xlnx-zdma.c')) softmmu_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_dma.c', 'soc_dma.c')) softmmu_ss.add(when: 'CONFIG_PXA2XX', if_true: files('pxa2xx_dma.c')) softmmu_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_dma.c')) diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c index af41e2f..a994b1f 100644 --- a/hw/intc/arm_gic.c +++ b/hw/intc/arm_gic.c @@ -28,6 +28,7 @@ #include "qemu/module.h" #include "trace.h" #include "sysemu/kvm.h" +#include "sysemu/qtest.h" /* #define DEBUG_GIC */ @@ -57,7 +58,7 @@ static const uint8_t gic_id_gicv2[] = { static inline int gic_get_current_cpu(GICState *s) { - if (s->num_cpu > 1) { + if (!qtest_enabled() && s->num_cpu > 1) { return current_cpu->cpu_index; } return 0; @@ -1476,7 +1477,7 @@ static void gic_dist_writel(void *opaque, hwaddr offset, int target_cpu; cpu = gic_get_current_cpu(s); - irq = value & 0x3ff; + irq = value & 0xf; switch ((value >> 24) & 3) { case 0: mask = (value >> 16) & ALL_CPU_MASK; diff --git a/hw/mem/nvdimm.c b/hw/mem/nvdimm.c index 03c2201..e0a9d60 100644 --- a/hw/mem/nvdimm.c +++ b/hw/mem/nvdimm.c @@ -146,6 +146,15 @@ static void nvdimm_prepare_memory_region(NVDIMMDevice *nvdimm, Error **errp) return; } + if (!nvdimm->unarmed && memory_region_is_rom(mr)) { + HostMemoryBackend *hostmem = dimm->hostmem; + + error_setg(errp, "'unarmed' property must be off since memdev %s " + "is read-only", + object_get_canonical_path_component(OBJECT(hostmem))); + return; + } + nvdimm->nvdimm_mr = g_new(MemoryRegion, 1); memory_region_init_alias(nvdimm->nvdimm_mr, OBJECT(dimm), "nvdimm-memory", mr, 0, pmem_size); diff --git a/hw/ssi/imx_spi.c b/hw/ssi/imx_spi.c index d8885ae..189423b 100644 --- a/hw/ssi/imx_spi.c +++ b/hw/ssi/imx_spi.c @@ -128,7 +128,14 @@ static uint8_t imx_spi_selected_channel(IMXSPIState *s) static uint32_t imx_spi_burst_length(IMXSPIState *s) { - return EXTRACT(s->regs[ECSPI_CONREG], ECSPI_CONREG_BURST_LENGTH) + 1; + uint32_t burst; + + burst = EXTRACT(s->regs[ECSPI_CONREG], ECSPI_CONREG_BURST_LENGTH) + 1; + if (burst % 8) { + burst = ROUND_UP(burst, 8); + } + + return burst; } static bool imx_spi_is_enabled(IMXSPIState *s) @@ -162,7 +169,6 @@ static void imx_spi_flush_txfifo(IMXSPIState *s) while (!fifo32_is_empty(&s->tx_fifo)) { int tx_burst = 0; - int index = 0; if (s->burst_length <= 0) { s->burst_length = imx_spi_burst_length(s); @@ -178,12 +184,12 @@ static void imx_spi_flush_txfifo(IMXSPIState *s) DPRINTF("data tx:0x%08x\n", tx); - tx_burst = MIN(s->burst_length, 32); + tx_burst = (s->burst_length % 32) ? : 32; rx = 0; while (tx_burst > 0) { - uint8_t byte = tx & 0xff; + uint8_t byte = tx >> (tx_burst - 8); DPRINTF("writing 0x%02x\n", (uint32_t)byte); @@ -192,13 +198,11 @@ static void imx_spi_flush_txfifo(IMXSPIState *s) DPRINTF("0x%02x read\n", (uint32_t)byte); - tx = tx >> 8; - rx |= (byte << (index * 8)); + rx = (rx << 8) | byte; /* Remove 8 bits from the actual burst */ tx_burst -= 8; s->burst_length -= 8; - index++; } DPRINTF("data rx:0x%08x\n", rx); @@ -228,22 +232,49 @@ static void imx_spi_flush_txfifo(IMXSPIState *s) fifo32_num_used(&s->tx_fifo), fifo32_num_used(&s->rx_fifo)); } -static void imx_spi_reset(DeviceState *dev) +static void imx_spi_common_reset(IMXSPIState *s) { - IMXSPIState *s = IMX_SPI(dev); - - DPRINTF("\n"); - - memset(s->regs, 0, sizeof(s->regs)); + int i; - s->regs[ECSPI_STATREG] = 0x00000003; + for (i = 0; i < ARRAY_SIZE(s->regs); i++) { + switch (i) { + case ECSPI_CONREG: + /* CONREG is not updated on soft reset */ + break; + case ECSPI_STATREG: + s->regs[i] = 0x00000003; + break; + default: + s->regs[i] = 0; + break; + } + } imx_spi_rxfifo_reset(s); imx_spi_txfifo_reset(s); + s->burst_length = 0; +} + +static void imx_spi_soft_reset(IMXSPIState *s) +{ + int i; + + imx_spi_common_reset(s); + imx_spi_update_irq(s); - s->burst_length = 0; + for (i = 0; i < ECSPI_NUM_CS; i++) { + qemu_set_irq(s->cs_lines[i], 1); + } +} + +static void imx_spi_reset(DeviceState *dev) +{ + IMXSPIState *s = IMX_SPI(dev); + + imx_spi_common_reset(s); + s->regs[ECSPI_CONREG] = 0; } static uint64_t imx_spi_read(void *opaque, hwaddr offset, unsigned size) @@ -258,42 +289,40 @@ static uint64_t imx_spi_read(void *opaque, hwaddr offset, unsigned size) return 0; } - switch (index) { - case ECSPI_RXDATA: - if (!imx_spi_is_enabled(s)) { - value = 0; - } else if (fifo32_is_empty(&s->rx_fifo)) { - /* value is undefined */ - value = 0xdeadbeef; - } else { - /* read from the RX FIFO */ - value = fifo32_pop(&s->rx_fifo); - } - - break; - case ECSPI_TXDATA: - qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Trying to read from TX FIFO\n", - TYPE_IMX_SPI, __func__); - - /* Reading from TXDATA gives 0 */ - - break; - case ECSPI_MSGDATA: - qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Trying to read from MSG FIFO\n", - TYPE_IMX_SPI, __func__); + value = s->regs[index]; + + if (imx_spi_is_enabled(s)) { + switch (index) { + case ECSPI_RXDATA: + if (fifo32_is_empty(&s->rx_fifo)) { + /* value is undefined */ + value = 0xdeadbeef; + } else { + /* read from the RX FIFO */ + value = fifo32_pop(&s->rx_fifo); + } + break; + case ECSPI_TXDATA: + qemu_log_mask(LOG_GUEST_ERROR, + "[%s]%s: Trying to read from TX FIFO\n", + TYPE_IMX_SPI, __func__); - /* Reading from MSGDATA gives 0 */ + /* Reading from TXDATA gives 0 */ + break; + case ECSPI_MSGDATA: + qemu_log_mask(LOG_GUEST_ERROR, + "[%s]%s: Trying to read from MSG FIFO\n", + TYPE_IMX_SPI, __func__); + /* Reading from MSGDATA gives 0 */ + break; + default: + break; + } - break; - default: - value = s->regs[index]; - break; + imx_spi_update_irq(s); } - DPRINTF("reg[%s] => 0x%" PRIx32 "\n", imx_spi_reg_name(index), value); - imx_spi_update_irq(s); - return (uint64_t)value; } @@ -303,6 +332,7 @@ static void imx_spi_write(void *opaque, hwaddr offset, uint64_t value, IMXSPIState *s = opaque; uint32_t index = offset >> 2; uint32_t change_mask; + uint32_t burst; if (index >= ECSPI_MAX) { qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%" @@ -313,6 +343,14 @@ static void imx_spi_write(void *opaque, hwaddr offset, uint64_t value, DPRINTF("reg[%s] <= 0x%" PRIx32 "\n", imx_spi_reg_name(index), (uint32_t)value); + if (!imx_spi_is_enabled(s)) { + /* Block is disabled */ + if (index != ECSPI_CONREG) { + /* Ignore access */ + return; + } + } + change_mask = s->regs[index] ^ value; switch (index) { @@ -321,10 +359,7 @@ static void imx_spi_write(void *opaque, hwaddr offset, uint64_t value, TYPE_IMX_SPI, __func__); break; case ECSPI_TXDATA: - if (!imx_spi_is_enabled(s)) { - /* Ignore writes if device is disabled */ - break; - } else if (fifo32_is_full(&s->tx_fifo)) { + if (fifo32_is_full(&s->tx_fifo)) { /* Ignore writes if queue is full */ break; } @@ -350,9 +385,17 @@ static void imx_spi_write(void *opaque, hwaddr offset, uint64_t value, case ECSPI_CONREG: s->regs[ECSPI_CONREG] = value; + burst = EXTRACT(s->regs[ECSPI_CONREG], ECSPI_CONREG_BURST_LENGTH) + 1; + if (burst % 8) { + qemu_log_mask(LOG_UNIMP, + "[%s]%s: burst length %d not supported: rounding up to next multiple of 8\n", + TYPE_IMX_SPI, __func__, burst); + } + if (!imx_spi_is_enabled(s)) { - /* device is disabled, so this is a reset */ - imx_spi_reset(DEVICE(s)); + /* device is disabled, so this is a soft reset */ + imx_spi_soft_reset(s); + return; } @@ -361,7 +404,7 @@ static void imx_spi_write(void *opaque, hwaddr offset, uint64_t value, /* We are in master mode */ - for (i = 0; i < 4; i++) { + for (i = 0; i < ECSPI_NUM_CS; i++) { qemu_set_irq(s->cs_lines[i], i == imx_spi_selected_channel(s) ? 0 : 1); } @@ -424,12 +467,10 @@ static void imx_spi_realize(DeviceState *dev, Error **errp) sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem); sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq); - for (i = 0; i < 4; ++i) { + for (i = 0; i < ECSPI_NUM_CS; ++i) { sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->cs_lines[i]); } - s->burst_length = 0; - fifo32_create(&s->tx_fifo, ECSPI_FIFO_SIZE); fifo32_create(&s->rx_fifo, ECSPI_FIFO_SIZE); } diff --git a/include/block/block.h b/include/block/block.h index 81fcaad..0a9f2c1 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -392,12 +392,13 @@ int bdrv_reopen_prepare(BDRVReopenState *reopen_state, void bdrv_reopen_commit(BDRVReopenState *reopen_state); void bdrv_reopen_abort(BDRVReopenState *reopen_state); int bdrv_pwrite_zeroes(BdrvChild *child, int64_t offset, - int bytes, BdrvRequestFlags flags); + int64_t bytes, BdrvRequestFlags flags); int bdrv_make_zero(BdrvChild *child, BdrvRequestFlags flags); -int bdrv_pread(BdrvChild *child, int64_t offset, void *buf, int bytes); -int bdrv_pwrite(BdrvChild *child, int64_t offset, const void *buf, int bytes); +int bdrv_pread(BdrvChild *child, int64_t offset, void *buf, int64_t bytes); +int bdrv_pwrite(BdrvChild *child, int64_t offset, const void *buf, + int64_t bytes); int bdrv_pwrite_sync(BdrvChild *child, int64_t offset, - const void *buf, int count); + const void *buf, int64_t bytes); /* * Efficiently zero a region of the disk image. Note that this is a regular * I/O request like read or write and should have a reasonable size. This @@ -405,7 +406,7 @@ int bdrv_pwrite_sync(BdrvChild *child, int64_t offset, * because it may allocate memory for the entire region. */ int coroutine_fn bdrv_co_pwrite_zeroes(BdrvChild *child, int64_t offset, - int bytes, BdrvRequestFlags flags); + int64_t bytes, BdrvRequestFlags flags); BlockDriverState *bdrv_find_backing_image(BlockDriverState *bs, const char *backing_file); void bdrv_refresh_filename(BlockDriverState *bs); @@ -844,8 +845,8 @@ void bdrv_unregister_buf(BlockDriverState *bs, void *host); * * Returns: 0 if succeeded; negative error code if failed. **/ -int coroutine_fn bdrv_co_copy_range(BdrvChild *src, uint64_t src_offset, - BdrvChild *dst, uint64_t dst_offset, - uint64_t bytes, BdrvRequestFlags read_flags, +int coroutine_fn bdrv_co_copy_range(BdrvChild *src, int64_t src_offset, + BdrvChild *dst, int64_t dst_offset, + int64_t bytes, BdrvRequestFlags read_flags, BdrvRequestFlags write_flags); #endif diff --git a/include/block/block_int.h b/include/block/block_int.h index d01fc23..22a2789 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -79,12 +79,12 @@ enum BdrvTrackedRequestType { typedef struct BdrvTrackedRequest { BlockDriverState *bs; int64_t offset; - uint64_t bytes; + int64_t bytes; enum BdrvTrackedRequestType type; bool serialising; int64_t overlap_offset; - uint64_t overlap_bytes; + int64_t overlap_bytes; QLIST_ENTRY(BdrvTrackedRequest) list; Coroutine *co; /* owner, used for deadlock detection */ @@ -93,7 +93,7 @@ typedef struct BdrvTrackedRequest { struct BdrvTrackedRequest *waiting_for; } BdrvTrackedRequest; -int bdrv_check_request(int64_t offset, int64_t bytes); +int bdrv_check_request(int64_t offset, int64_t bytes, Error **errp); struct BlockDriver { const char *format_name; @@ -1032,16 +1032,16 @@ extern BlockDriver bdrv_raw; extern BlockDriver bdrv_qcow2; int coroutine_fn bdrv_co_preadv(BdrvChild *child, - int64_t offset, unsigned int bytes, QEMUIOVector *qiov, + int64_t offset, int64_t bytes, QEMUIOVector *qiov, BdrvRequestFlags flags); int coroutine_fn bdrv_co_preadv_part(BdrvChild *child, - int64_t offset, unsigned int bytes, + int64_t offset, int64_t bytes, QEMUIOVector *qiov, size_t qiov_offset, BdrvRequestFlags flags); int coroutine_fn bdrv_co_pwritev(BdrvChild *child, - int64_t offset, unsigned int bytes, QEMUIOVector *qiov, + int64_t offset, int64_t bytes, QEMUIOVector *qiov, BdrvRequestFlags flags); int coroutine_fn bdrv_co_pwritev_part(BdrvChild *child, - int64_t offset, unsigned int bytes, + int64_t offset, int64_t bytes, QEMUIOVector *qiov, size_t qiov_offset, BdrvRequestFlags flags); static inline int coroutine_fn bdrv_co_pread(BdrvChild *child, @@ -1357,14 +1357,14 @@ void bdrv_dec_in_flight(BlockDriverState *bs); void blockdev_close_all_bdrv_states(void); -int coroutine_fn bdrv_co_copy_range_from(BdrvChild *src, uint64_t src_offset, - BdrvChild *dst, uint64_t dst_offset, - uint64_t bytes, +int coroutine_fn bdrv_co_copy_range_from(BdrvChild *src, int64_t src_offset, + BdrvChild *dst, int64_t dst_offset, + int64_t bytes, BdrvRequestFlags read_flags, BdrvRequestFlags write_flags); -int coroutine_fn bdrv_co_copy_range_to(BdrvChild *src, uint64_t src_offset, - BdrvChild *dst, uint64_t dst_offset, - uint64_t bytes, +int coroutine_fn bdrv_co_copy_range_to(BdrvChild *src, int64_t src_offset, + BdrvChild *dst, int64_t dst_offset, + int64_t bytes, BdrvRequestFlags read_flags, BdrvRequestFlags write_flags); diff --git a/include/block/nbd.h b/include/block/nbd.h index 4a52a43..5f34d23 100644 --- a/include/block/nbd.h +++ b/include/block/nbd.h @@ -364,7 +364,7 @@ static inline int nbd_read(QIOChannel *ioc, void *buffer, size_t size, if (desc) { error_prepend(errp, "Failed to read %s: ", desc); } - return -1; + return ret; } return 0; @@ -375,8 +375,9 @@ static inline int nbd_read##bits(QIOChannel *ioc, \ uint##bits##_t *val, \ const char *desc, Error **errp) \ { \ - if (nbd_read(ioc, val, sizeof(*val), desc, errp) < 0) { \ - return -1; \ + int ret = nbd_read(ioc, val, sizeof(*val), desc, errp); \ + if (ret < 0) { \ + return ret; \ } \ *val = be##bits##_to_cpu(*val); \ return 0; \ diff --git a/include/block/throttle-groups.h b/include/block/throttle-groups.h index 8bf7d23..9541b32 100644 --- a/include/block/throttle-groups.h +++ b/include/block/throttle-groups.h @@ -77,7 +77,7 @@ void throttle_group_unregister_tgm(ThrottleGroupMember *tgm); void throttle_group_restart_tgm(ThrottleGroupMember *tgm); void coroutine_fn throttle_group_co_io_limits_intercept(ThrottleGroupMember *tgm, - unsigned int bytes, + int64_t bytes, bool is_write); void throttle_group_attach_aio_context(ThrottleGroupMember *tgm, AioContext *new_context); diff --git a/include/exec/memory.h b/include/exec/memory.h index 521d990..c6ce74f 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -966,6 +966,7 @@ void memory_region_init_resizeable_ram(MemoryRegion *mr, * - RAM_PMEM: the memory is persistent memory * Other bits are ignored now. * @path: the path in which to allocate the RAM. + * @readonly: true to open @path for reading, false for read/write. * @errp: pointer to Error*, to store an error if it happens. * * Note that this function does not do anything to cause the data in the @@ -978,6 +979,7 @@ void memory_region_init_ram_from_file(MemoryRegion *mr, uint64_t align, uint32_t ram_flags, const char *path, + bool readonly, Error **errp); /** diff --git a/include/exec/ram_addr.h b/include/exec/ram_addr.h index c6d2ef1..40b1660 100644 --- a/include/exec/ram_addr.h +++ b/include/exec/ram_addr.h @@ -110,6 +110,7 @@ long qemu_maxrampagesize(void); * - RAM_PMEM: the backend @mem_path or @fd is persistent memory * Other bits are ignored. * @mem_path or @fd: specify the backing file or device + * @readonly: true to open @path for reading, false for read/write. * @errp: pointer to Error*, to store an error if it happens * * Return: @@ -118,9 +119,9 @@ long qemu_maxrampagesize(void); */ RAMBlock *qemu_ram_alloc_from_file(ram_addr_t size, MemoryRegion *mr, uint32_t ram_flags, const char *mem_path, - Error **errp); + bool readonly, Error **errp); RAMBlock *qemu_ram_alloc_from_fd(ram_addr_t size, MemoryRegion *mr, - uint32_t ram_flags, int fd, + uint32_t ram_flags, int fd, bool readonly, Error **errp); RAMBlock *qemu_ram_alloc_from_ptr(ram_addr_t size, void *host, diff --git a/include/hw/ssi/imx_spi.h b/include/hw/ssi/imx_spi.h index b82b17f..eeaf49b 100644 --- a/include/hw/ssi/imx_spi.h +++ b/include/hw/ssi/imx_spi.h @@ -77,6 +77,9 @@ #define EXTRACT(value, name) extract32(value, name##_SHIFT, name##_LENGTH) +/* number of chip selects supported */ +#define ECSPI_NUM_CS 4 + #define TYPE_IMX_SPI "imx.spi" OBJECT_DECLARE_SIMPLE_TYPE(IMXSPIState, IMX_SPI) @@ -89,7 +92,7 @@ struct IMXSPIState { qemu_irq irq; - qemu_irq cs_lines[4]; + qemu_irq cs_lines[ECSPI_NUM_CS]; SSIBus *bus; diff --git a/include/qemu/iov.h b/include/qemu/iov.h index b6b283a..9330746 100644 --- a/include/qemu/iov.h +++ b/include/qemu/iov.h @@ -222,7 +222,7 @@ static inline void *qemu_iovec_buf(QEMUIOVector *qiov) void qemu_iovec_init(QEMUIOVector *qiov, int alloc_hint); void qemu_iovec_init_external(QEMUIOVector *qiov, struct iovec *iov, int niov); -void qemu_iovec_init_extended( +int qemu_iovec_init_extended( QEMUIOVector *qiov, void *head_buf, size_t head_len, QEMUIOVector *mid_qiov, size_t mid_offset, size_t mid_len, diff --git a/include/qemu/mmap-alloc.h b/include/qemu/mmap-alloc.h index e786266..8b7a5c7 100644 --- a/include/qemu/mmap-alloc.h +++ b/include/qemu/mmap-alloc.h @@ -14,6 +14,7 @@ size_t qemu_mempath_getpagesize(const char *mem_path); * @size: the number of bytes to be mmaped * @align: if not zero, specify the alignment of the starting mapping address; * otherwise, the alignment in use will be determined by QEMU. + * @readonly: true for a read-only mapping, false for read/write. * @shared: map has RAM_SHARED flag. * @is_pmem: map has RAM_PMEM flag. * @@ -24,6 +25,7 @@ size_t qemu_mempath_getpagesize(const char *mem_path); void *qemu_ram_mmap(int fd, size_t size, size_t align, + bool readonly, bool shared, bool is_pmem); diff --git a/qemu-options.hx b/qemu-options.hx index d0410f0..c09c464 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -4426,7 +4426,7 @@ SRST they are specified. Note that the 'id' property must be set. These objects are placed in the '/objects' path. - ``-object memory-backend-file,id=id,size=size,mem-path=dir,share=on|off,discard-data=on|off,merge=on|off,dump=on|off,prealloc=on|off,host-nodes=host-nodes,policy=default|preferred|bind|interleave,align=align`` + ``-object memory-backend-file,id=id,size=size,mem-path=dir,share=on|off,discard-data=on|off,merge=on|off,dump=on|off,prealloc=on|off,host-nodes=host-nodes,policy=default|preferred|bind|interleave,align=align,readonly=on|off`` Creates a memory file backend object, which can be used to back the guest RAM with huge pages. @@ -4509,6 +4509,9 @@ SRST 4.15) and the filesystem of ``mem-path`` mounted with DAX option. + The ``readonly`` option specifies whether the backing file is opened + read-only or read-write (default). + ``-object memory-backend-ram,id=id,merge=on|off,dump=on|off,share=on|off,prealloc=on|off,size=size,host-nodes=host-nodes,policy=default|preferred|bind|interleave`` Creates a memory backend object, which can be used to back the guest RAM. Memory backend objects offer more control than the diff --git a/softmmu/memory.c b/softmmu/memory.c index 333e1ed..676c298 100644 --- a/softmmu/memory.c +++ b/softmmu/memory.c @@ -1587,15 +1587,18 @@ void memory_region_init_ram_from_file(MemoryRegion *mr, uint64_t align, uint32_t ram_flags, const char *path, + bool readonly, Error **errp) { Error *err = NULL; memory_region_init(mr, owner, name, size); mr->ram = true; + mr->readonly = readonly; mr->terminates = true; mr->destructor = memory_region_destructor_ram; mr->align = align; - mr->ram_block = qemu_ram_alloc_from_file(size, mr, ram_flags, path, &err); + mr->ram_block = qemu_ram_alloc_from_file(size, mr, ram_flags, path, + readonly, &err); if (err) { mr->size = int128_zero(); object_unparent(OBJECT(mr)); @@ -1618,7 +1621,7 @@ void memory_region_init_ram_from_fd(MemoryRegion *mr, mr->destructor = memory_region_destructor_ram; mr->ram_block = qemu_ram_alloc_from_fd(size, mr, share ? RAM_SHARED : 0, - fd, &err); + fd, false, &err); if (err) { mr->size = int128_zero(); object_unparent(OBJECT(mr)); diff --git a/softmmu/physmem.c b/softmmu/physmem.c index cdcd197..60760a3 100644 --- a/softmmu/physmem.c +++ b/softmmu/physmem.c @@ -1398,6 +1398,7 @@ static int64_t get_file_align(int fd) static int file_ram_open(const char *path, const char *region_name, + bool readonly, bool *created, Error **errp) { @@ -1408,7 +1409,7 @@ static int file_ram_open(const char *path, *created = false; for (;;) { - fd = open(path, O_RDWR); + fd = open(path, readonly ? O_RDONLY : O_RDWR); if (fd >= 0) { /* @path names an existing file, use it */ break; @@ -1460,6 +1461,7 @@ static int file_ram_open(const char *path, static void *file_ram_alloc(RAMBlock *block, ram_addr_t memory, int fd, + bool readonly, bool truncate, Error **errp) { @@ -1510,7 +1512,7 @@ static void *file_ram_alloc(RAMBlock *block, perror("ftruncate"); } - area = qemu_ram_mmap(fd, memory, block->mr->align, + area = qemu_ram_mmap(fd, memory, block->mr->align, readonly, block->flags & RAM_SHARED, block->flags & RAM_PMEM); if (area == MAP_FAILED) { error_setg_errno(errp, errno, @@ -1942,7 +1944,7 @@ static void ram_block_add(RAMBlock *new_block, Error **errp, bool shared) #ifdef CONFIG_POSIX RAMBlock *qemu_ram_alloc_from_fd(ram_addr_t size, MemoryRegion *mr, - uint32_t ram_flags, int fd, + uint32_t ram_flags, int fd, bool readonly, Error **errp) { RAMBlock *new_block; @@ -1996,7 +1998,8 @@ RAMBlock *qemu_ram_alloc_from_fd(ram_addr_t size, MemoryRegion *mr, new_block->used_length = size; new_block->max_length = size; new_block->flags = ram_flags; - new_block->host = file_ram_alloc(new_block, size, fd, !file_size, errp); + new_block->host = file_ram_alloc(new_block, size, fd, readonly, + !file_size, errp); if (!new_block->host) { g_free(new_block); return NULL; @@ -2015,18 +2018,19 @@ RAMBlock *qemu_ram_alloc_from_fd(ram_addr_t size, MemoryRegion *mr, RAMBlock *qemu_ram_alloc_from_file(ram_addr_t size, MemoryRegion *mr, uint32_t ram_flags, const char *mem_path, - Error **errp) + bool readonly, Error **errp) { int fd; bool created; RAMBlock *block; - fd = file_ram_open(mem_path, memory_region_name(mr), &created, errp); + fd = file_ram_open(mem_path, memory_region_name(mr), readonly, &created, + errp); if (fd < 0) { return NULL; } - block = qemu_ram_alloc_from_fd(size, mr, ram_flags, fd, errp); + block = qemu_ram_alloc_from_fd(size, mr, ram_flags, fd, readonly, errp); if (!block) { if (created) { unlink(mem_path); diff --git a/tests/qemu-iotests/185.out b/tests/qemu-iotests/185.out index 9dedc8e..754a641 100644 --- a/tests/qemu-iotests/185.out +++ b/tests/qemu-iotests/185.out @@ -89,7 +89,7 @@ Formatting 'TEST_DIR/t.qcow2.copy', fmt=qcow2 cluster_size=65536 extended_l2=off 'format': 'IMGFMT', 'sync': 'full', 'speed': 65536, - 'x-perf': { 'max-chunk': 65536 } } } + 'x-perf': {'max-chunk': 65536} } } Formatting 'TEST_DIR/t.qcow2.copy', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=67108864 lazy_refcounts=off refcount_bits=16 {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "disk"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}} diff --git a/tests/qemu-iotests/206.out b/tests/qemu-iotests/206.out index e8a36de..5dd589d 100644 --- a/tests/qemu-iotests/206.out +++ b/tests/qemu-iotests/206.out @@ -180,7 +180,7 @@ Job failed: Could not resize image: Image size cannot be negative {"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "qcow2", "file": "node0", "size": 9223372036854775296}}} {"return": {}} -Job failed: Could not resize image: Required too big image size, it must be not greater than 9223372035781033984 +Job failed: Could not resize image: offset(9223372036854775296) exceeds maximum(9223372035781033984) {"execute": "job-dismiss", "arguments": {"id": "job0"}} {"return": {}} diff --git a/tests/test-write-threshold.c b/tests/test-write-threshold.c index 4cf0326..fc1c45a 100644 --- a/tests/test-write-threshold.c +++ b/tests/test-write-threshold.c @@ -7,6 +7,7 @@ */ #include "qemu/osdep.h" +#include "qapi/error.h" #include "block/block_int.h" #include "block/write-threshold.h" @@ -64,7 +65,7 @@ static void test_threshold_not_trigger(void) req.offset = 1024; req.bytes = 1024; - assert(bdrv_check_request(req.offset, req.bytes) == 0); + bdrv_check_request(req.offset, req.bytes, &error_abort); bdrv_write_threshold_set(&bs, threshold); amount = bdrv_write_threshold_exceeded(&bs, &req); @@ -84,7 +85,7 @@ static void test_threshold_trigger(void) req.offset = (4 * 1024 * 1024) - 1024; req.bytes = 2 * 1024; - assert(bdrv_check_request(req.offset, req.bytes) == 0); + bdrv_check_request(req.offset, req.bytes, &error_abort); bdrv_write_threshold_set(&bs, threshold); amount = bdrv_write_threshold_exceeded(&bs, &req); @@ -415,7 +415,7 @@ int qemu_iovec_subvec_niov(QEMUIOVector *qiov, size_t offset, size_t len) * Compile new iovec, combining @head_buf buffer, sub-qiov of @mid_qiov, * and @tail_buf buffer into new qiov. */ -void qemu_iovec_init_extended( +int qemu_iovec_init_extended( QEMUIOVector *qiov, void *head_buf, size_t head_len, QEMUIOVector *mid_qiov, size_t mid_offset, size_t mid_len, @@ -425,12 +425,24 @@ void qemu_iovec_init_extended( int total_niov, mid_niov = 0; struct iovec *p, *mid_iov = NULL; + assert(mid_qiov->niov <= IOV_MAX); + + if (SIZE_MAX - head_len < mid_len || + SIZE_MAX - head_len - mid_len < tail_len) + { + return -EINVAL; + } + if (mid_len) { mid_iov = qiov_slice(mid_qiov, mid_offset, mid_len, &mid_head, &mid_tail, &mid_niov); } total_niov = !!head_len + mid_niov + !!tail_len; + if (total_niov > IOV_MAX) { + return -EINVAL; + } + if (total_niov == 1) { qemu_iovec_init_buf(qiov, NULL, 0); p = &qiov->local_iov; @@ -459,6 +471,8 @@ void qemu_iovec_init_extended( p->iov_base = tail_buf; p->iov_len = tail_len; } + + return 0; } /* @@ -492,7 +506,14 @@ bool qemu_iovec_is_zero(QEMUIOVector *qiov, size_t offset, size_t bytes) void qemu_iovec_init_slice(QEMUIOVector *qiov, QEMUIOVector *source, size_t offset, size_t len) { - qemu_iovec_init_extended(qiov, NULL, 0, source, offset, len, NULL, 0); + int ret; + + assert(source->size >= len); + assert(source->size - len >= offset); + + /* We shrink the request, so we can't overflow neither size_t nor MAX_IOV */ + ret = qemu_iovec_init_extended(qiov, NULL, 0, source, offset, len, NULL, 0); + assert(ret == 0); } void qemu_iovec_destroy(QEMUIOVector *qiov) diff --git a/util/mmap-alloc.c b/util/mmap-alloc.c index 27dcccd..890fda6 100644 --- a/util/mmap-alloc.c +++ b/util/mmap-alloc.c @@ -85,9 +85,11 @@ size_t qemu_mempath_getpagesize(const char *mem_path) void *qemu_ram_mmap(int fd, size_t size, size_t align, + bool readonly, bool shared, bool is_pmem) { + int prot; int flags; int map_sync_flags = 0; int guardfd; @@ -146,8 +148,9 @@ void *qemu_ram_mmap(int fd, offset = QEMU_ALIGN_UP((uintptr_t)guardptr, align) - (uintptr_t)guardptr; - ptr = mmap(guardptr + offset, size, PROT_READ | PROT_WRITE, - flags | map_sync_flags, fd, 0); + prot = PROT_READ | (readonly ? 0 : PROT_WRITE); + + ptr = mmap(guardptr + offset, size, prot, flags | map_sync_flags, fd, 0); if (ptr == MAP_FAILED && map_sync_flags) { if (errno == ENOTSUP) { @@ -171,8 +174,7 @@ void *qemu_ram_mmap(int fd, * if map failed with MAP_SHARED_VALIDATE | MAP_SYNC, * we will remove these flags to handle compatibility. */ - ptr = mmap(guardptr + offset, size, PROT_READ | PROT_WRITE, - flags, fd, 0); + ptr = mmap(guardptr + offset, size, prot, flags, fd, 0); } if (ptr == MAP_FAILED) { diff --git a/util/oslib-posix.c b/util/oslib-posix.c index 359c52d..bf57d3b 100644 --- a/util/oslib-posix.c +++ b/util/oslib-posix.c @@ -230,7 +230,7 @@ void *qemu_memalign(size_t alignment, size_t size) void *qemu_anon_ram_alloc(size_t size, uint64_t *alignment, bool shared) { size_t align = QEMU_VMALLOC_ALIGN; - void *ptr = qemu_ram_mmap(-1, size, align, shared, false); + void *ptr = qemu_ram_mmap(-1, size, align, false, shared, false); if (ptr == MAP_FAILED) { return NULL; |