diff options
147 files changed, 2574 insertions, 949 deletions
diff --git a/CODING_STYLE b/CODING_STYLE index ec075dedc4..cb8edcbb36 100644 --- a/CODING_STYLE +++ b/CODING_STYLE @@ -29,6 +29,45 @@ Spaces of course are superior to tabs because: Do not leave whitespace dangling off the ends of lines. +1.1 Multiline Indent + +There are several places where indent is necessary: + + - if/else + - while/for + - function definition & call + +When breaking up a long line to fit within line width, we need a proper indent +for the following lines. + +In case of if/else, while/for, align the secondary lines just after the +opening parenthesis of the first. + +For example: + + if (a == 1 && + b == 2) { + + while (a == 1 && + b == 2) { + +In case of function, there are several variants: + + * 4 spaces indent from the beginning + * align the secondary lines just after the opening parenthesis of the + first + +For example: + + do_something(x, y, + z); + + do_something(x, y, + z); + + do_something(x, do_another(y, + z)); + 2. Line width Lines should be 80 characters; try not to make them longer. @@ -108,10 +147,10 @@ block to a separate function altogether. When comparing a variable for (in)equality with a constant, list the constant on the right, as in: -if (a == 1) { - /* Reads like: "If a equals 1" */ - do_something(); -} + if (a == 1) { + /* Reads like: "If a equals 1" */ + do_something(); + } Rationale: Yoda conditions (as in 'if (1 == a)') are awkward to read. Besides, good compilers already warn users when '==' is mis-typed as '=', diff --git a/MAINTAINERS b/MAINTAINERS index 66ddbda9c9..f25729a06d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1391,6 +1391,13 @@ F: include/hw/net/ F: tests/virtio-net-test.c T: git https://github.com/jasowang/qemu.git net +Parallel NOR Flash devices +M: Philippe Mathieu-Daudé <philmd@redhat.com> +T: git https://gitlab.com/philmd/qemu.git pflash-next +S: Maintained +F: hw/block/pflash_cfi*.c +F: include/hw/block/flash.h + SCSI M: Paolo Bonzini <pbonzini@redhat.com> R: Fam Zheng <fam@euphon.net> @@ -2404,12 +2411,13 @@ F: block/ssh.c CURL L: qemu-block@nongnu.org -S: Supported +S: Odd Fixes F: block/curl.c GLUSTER L: qemu-block@nongnu.org -S: Supported +L: integration@gluster.org +S: Odd Fixes F: block/gluster.c Null Block Driver @@ -639,7 +639,7 @@ clean: ! -path ./roms/edk2/BaseTools/Source/Python/UPT/Dll/sqlite3.dll \ -exec rm {} + rm -f $(edk2-decompressed) - rm -f $(filter-out %.tlb,$(TOOLS)) $(HELPERS-y) qemu-ga TAGS cscope.* *.pod *~ */*~ + rm -f $(filter-out %.tlb,$(TOOLS)) $(HELPERS-y) qemu-ga$(EXESUF) TAGS cscope.* *.pod *~ */*~ rm -f fsdev/*.pod scsi/*.pod rm -f qemu-img-cmds.h rm -f ui/shader/*-vert.h ui/shader/*-frag.h @@ -899,11 +899,14 @@ ui/shader.o: $(SRC_PATH)/ui/shader.c \ MAKEINFO=makeinfo MAKEINFOINCLUDES= -I docs -I $(<D) -I $(@D) MAKEINFOFLAGS=--no-split --number-sections $(MAKEINFOINCLUDES) -TEXI2PODFLAGS=$(MAKEINFOINCLUDES) "-DVERSION=$(VERSION)" +TEXI2PODFLAGS=$(MAKEINFOINCLUDES) -DVERSION="$(VERSION)" -DCONFDIR="$(qemu_confdir)" TEXI2PDFFLAGS=$(if $(V),,--quiet) -I $(SRC_PATH) $(MAKEINFOINCLUDES) -docs/version.texi: $(SRC_PATH)/VERSION - $(call quiet-command,echo "@set VERSION $(VERSION)" > $@,"GEN","$@") +docs/version.texi: $(SRC_PATH)/VERSION config-host.mak + $(call quiet-command,(\ + echo "@set VERSION $(VERSION)" && \ + echo "@set CONFDIR $(qemu_confdir)" \ + )> $@,"GEN","$@") %.html: %.texi docs/version.texi $(call quiet-command,LC_ALL=C $(MAKEINFO) $(MAKEINFOFLAGS) --no-headers \ @@ -973,7 +976,7 @@ qemu-doc.html qemu-doc.info qemu-doc.pdf qemu-doc.txt: \ qemu-img.texi qemu-nbd.texi qemu-options.texi qemu-option-trace.texi \ qemu-deprecated.texi qemu-monitor.texi qemu-img-cmds.texi qemu-ga.texi \ qemu-monitor-info.texi docs/qemu-block-drivers.texi \ - docs/qemu-cpu-models.texi + docs/qemu-cpu-models.texi docs/security.texi docs/interop/qemu-ga-ref.dvi docs/interop/qemu-ga-ref.html \ docs/interop/qemu-ga-ref.info docs/interop/qemu-ga-ref.pdf \ @@ -1743,11 +1743,10 @@ static void bdrv_child_perm(BlockDriverState *bs, BlockDriverState *child_bs, uint64_t parent_perm, uint64_t parent_shared, uint64_t *nperm, uint64_t *nshared) { - if (bs->drv && bs->drv->bdrv_child_perm) { - bs->drv->bdrv_child_perm(bs, c, role, reopen_queue, - parent_perm, parent_shared, - nperm, nshared); - } + assert(bs->drv && bs->drv->bdrv_child_perm); + bs->drv->bdrv_child_perm(bs, c, role, reopen_queue, + parent_perm, parent_shared, + nperm, nshared); /* TODO Take force_share from reopen_queue */ if (child_bs && child_bs->force_share) { *nshared = BLK_PERM_ALL; @@ -4083,14 +4082,14 @@ static void bdrv_delete(BlockDriverState *bs) assert(bdrv_op_blocker_is_empty(bs)); assert(!bs->refcnt); - bdrv_close(bs); - /* remove from list, if necessary */ if (bs->node_name[0] != '\0') { QTAILQ_REMOVE(&graph_bdrv_states, bs, node_list); } QTAILQ_REMOVE(&all_bdrv_states, bs, bs_list); + bdrv_close(bs); + g_free(bs); } @@ -4122,7 +4121,7 @@ typedef struct CheckCo { int ret; } CheckCo; -static void bdrv_check_co_entry(void *opaque) +static void coroutine_fn bdrv_check_co_entry(void *opaque) { CheckCo *cco = opaque; cco->ret = bdrv_co_check(cco->bs, cco->res, cco->fix); diff --git a/block/commit.c b/block/commit.c index 27537d995b..14e5bb394c 100644 --- a/block/commit.c +++ b/block/commit.c @@ -303,23 +303,14 @@ void commit_start(const char *job_id, BlockDriverState *bs, commit_top_bs->total_sectors = top->total_sectors; bdrv_set_aio_context(commit_top_bs, bdrv_get_aio_context(top)); - bdrv_set_backing_hd(commit_top_bs, top, &local_err); + bdrv_append(commit_top_bs, top, &local_err); if (local_err) { - bdrv_unref(commit_top_bs); - commit_top_bs = NULL; - error_propagate(errp, local_err); - goto fail; - } - bdrv_replace_node(top, commit_top_bs, &local_err); - if (local_err) { - bdrv_unref(commit_top_bs); commit_top_bs = NULL; error_propagate(errp, local_err); goto fail; } s->commit_top_bs = commit_top_bs; - bdrv_unref(commit_top_bs); /* Block all nodes between top and base, because they will * disappear from the chain after this operation. */ diff --git a/block/io.c b/block/io.c index dfc153b8d8..aeebc9c23c 100644 --- a/block/io.c +++ b/block/io.c @@ -837,42 +837,6 @@ static int bdrv_prwv_co(BdrvChild *child, int64_t offset, return rwco.ret; } -/* - * Process a synchronous request using coroutines - */ -static int bdrv_rw_co(BdrvChild *child, int64_t sector_num, uint8_t *buf, - int nb_sectors, bool is_write, BdrvRequestFlags flags) -{ - QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, - nb_sectors * BDRV_SECTOR_SIZE); - - if (nb_sectors < 0 || nb_sectors > BDRV_REQUEST_MAX_SECTORS) { - return -EINVAL; - } - - return bdrv_prwv_co(child, sector_num << BDRV_SECTOR_BITS, - &qiov, is_write, flags); -} - -/* return < 0 if error. See bdrv_write() for the return codes */ -int bdrv_read(BdrvChild *child, int64_t sector_num, - uint8_t *buf, int nb_sectors) -{ - return bdrv_rw_co(child, sector_num, buf, nb_sectors, false, 0); -} - -/* Return < 0 if error. Important errors are: - -EIO generic I/O error (may happen for all errors) - -ENOMEDIUM No media inserted. - -EINVAL Invalid sector number or nb_sectors - -EACCES Trying to write a read-only device -*/ -int bdrv_write(BdrvChild *child, int64_t sector_num, - const uint8_t *buf, int nb_sectors) -{ - return bdrv_rw_co(child, sector_num, (uint8_t *)buf, nb_sectors, true, 0); -} - int bdrv_pwrite_zeroes(BdrvChild *child, int64_t offset, int bytes, BdrvRequestFlags flags) { @@ -935,6 +899,7 @@ int bdrv_preadv(BdrvChild *child, int64_t offset, QEMUIOVector *qiov) return qiov->size; } +/* See bdrv_pwrite() for the return codes */ int bdrv_pread(BdrvChild *child, int64_t offset, void *buf, int bytes) { QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes); @@ -958,6 +923,12 @@ int bdrv_pwritev(BdrvChild *child, int64_t offset, QEMUIOVector *qiov) return qiov->size; } +/* Return no. of bytes on success or < 0 on error. Important errors are: + -EIO generic I/O error (may happen for all errors) + -ENOMEDIUM No media inserted. + -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) { QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes); @@ -1516,7 +1487,7 @@ static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs, assert(!bs->supported_zero_flags); } - if (ret == -ENOTSUP && !(flags & BDRV_REQ_NO_FALLBACK)) { + if (ret < 0 && !(flags & BDRV_REQ_NO_FALLBACK)) { /* Fall back to bounce buffer if write zeroes is unsupported */ BdrvRequestFlags write_flags = flags & ~BDRV_REQ_ZERO_WRITE; diff --git a/block/qcow2-bitmap.c b/block/qcow2-bitmap.c index e53a1609d7..8a75366c92 100644 --- a/block/qcow2-bitmap.c +++ b/block/qcow2-bitmap.c @@ -202,7 +202,7 @@ static void clear_bitmap_table(BlockDriverState *bs, uint64_t *bitmap_table, continue; } - qcow2_free_clusters(bs, addr, s->cluster_size, QCOW2_DISCARD_OTHER); + qcow2_free_clusters(bs, addr, s->cluster_size, QCOW2_DISCARD_ALWAYS); bitmap_table[i] = 0; } } diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c index e0fe322500..7481903396 100644 --- a/block/qcow2-refcount.c +++ b/block/qcow2-refcount.c @@ -1520,12 +1520,31 @@ int qcow2_inc_refcounts_imrt(BlockDriverState *bs, BdrvCheckResult *res, { BDRVQcow2State *s = bs->opaque; uint64_t start, last, cluster_offset, k, refcount; + int64_t file_len; int ret; if (size <= 0) { return 0; } + file_len = bdrv_getlength(bs->file->bs); + if (file_len < 0) { + return file_len; + } + + /* + * Last cluster of qcow2 image may be semi-allocated, so it may be OK to + * reference some space after file end but it should be less than one + * cluster. + */ + if (offset + size - file_len >= s->cluster_size) { + fprintf(stderr, "ERROR: counting reference for region exceeding the " + "end of the file by one cluster or more: offset 0x%" PRIx64 + " size 0x%" PRIx64 "\n", offset, size); + res->corruptions++; + return 0; + } + start = start_of_cluster(s, offset); last = start_of_cluster(s, offset + size - 1); for(cluster_offset = start; cluster_offset <= last; @@ -1572,7 +1591,7 @@ enum { static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res, void **refcount_table, int64_t *refcount_table_size, int64_t l2_offset, - int flags, BdrvCheckMode fix) + int flags, BdrvCheckMode fix, bool active) { BDRVQcow2State *s = bs->opaque; uint64_t *l2_table, l2_entry; @@ -1641,17 +1660,10 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res, { uint64_t offset = l2_entry & L2E_OFFSET_MASK; - if (flags & CHECK_FRAG_INFO) { - res->bfi.allocated_clusters++; - if (next_contiguous_offset && - offset != next_contiguous_offset) { - res->bfi.fragmented_clusters++; - } - next_contiguous_offset = offset + s->cluster_size; - } - /* Correct offsets are cluster aligned */ if (offset_into_cluster(s, offset)) { + res->corruptions++; + if (qcow2_get_cluster_type(bs, l2_entry) == QCOW2_CLUSTER_ZERO_ALLOC) { @@ -1663,11 +1675,12 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res, if (fix & BDRV_FIX_ERRORS) { uint64_t l2e_offset = l2_offset + (uint64_t)i * sizeof(uint64_t); + int ign = active ? QCOW2_OL_ACTIVE_L2 : + QCOW2_OL_INACTIVE_L2; l2_entry = QCOW_OFLAG_ZERO; l2_table[i] = cpu_to_be64(l2_entry); - ret = qcow2_pre_write_overlap_check(bs, - QCOW2_OL_ACTIVE_L2 | QCOW2_OL_INACTIVE_L2, + ret = qcow2_pre_write_overlap_check(bs, ign, l2e_offset, sizeof(uint64_t), false); if (ret < 0) { fprintf(stderr, "ERROR: Overlap check failed\n"); @@ -1686,21 +1699,28 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res, /* Do not abort, continue checking the rest of this * L2 table's entries */ } else { + res->corruptions--; res->corruptions_fixed++; /* Skip marking the cluster as used * (it is unused now) */ continue; } - } else { - res->corruptions++; } } else { fprintf(stderr, "ERROR offset=%" PRIx64 ": Data cluster is " "not properly aligned; L2 entry corrupted.\n", offset); - res->corruptions++; } } + if (flags & CHECK_FRAG_INFO) { + res->bfi.allocated_clusters++; + if (next_contiguous_offset && + offset != next_contiguous_offset) { + res->bfi.fragmented_clusters++; + } + next_contiguous_offset = offset + s->cluster_size; + } + /* Mark cluster as used */ if (!has_data_file(bs)) { ret = qcow2_inc_refcounts_imrt(bs, res, refcount_table, @@ -1743,7 +1763,7 @@ static int check_refcounts_l1(BlockDriverState *bs, void **refcount_table, int64_t *refcount_table_size, int64_t l1_table_offset, int l1_size, - int flags, BdrvCheckMode fix) + int flags, BdrvCheckMode fix, bool active) { BDRVQcow2State *s = bs->opaque; uint64_t *l1_table = NULL, l2_offset, l1_size2; @@ -1799,7 +1819,7 @@ static int check_refcounts_l1(BlockDriverState *bs, /* Process and check L2 entries */ ret = check_refcounts_l2(bs, res, refcount_table, refcount_table_size, l2_offset, flags, - fix); + fix, active); if (ret < 0) { goto fail; } @@ -1846,7 +1866,7 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res, for (i = 0; i < s->l1_size; i++) { uint64_t l1_entry = s->l1_table[i]; uint64_t l2_offset = l1_entry & L1E_OFFSET_MASK; - bool l2_dirty = false; + int l2_dirty = 0; if (!l2_offset) { continue; @@ -1859,6 +1879,7 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res, continue; } if ((refcount == 1) != ((l1_entry & QCOW_OFLAG_COPIED) != 0)) { + res->corruptions++; fprintf(stderr, "%s OFLAG_COPIED L2 cluster: l1_index=%d " "l1_entry=%" PRIx64 " refcount=%" PRIu64 "\n", repair ? "Repairing" : "ERROR", i, l1_entry, refcount); @@ -1871,9 +1892,8 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res, res->check_errors++; goto fail; } + res->corruptions--; res->corruptions_fixed++; - } else { - res->corruptions++; } } @@ -1905,6 +1925,7 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res, } } if ((refcount == 1) != ((l2_entry & QCOW_OFLAG_COPIED) != 0)) { + res->corruptions++; fprintf(stderr, "%s OFLAG_COPIED data cluster: " "l2_entry=%" PRIx64 " refcount=%" PRIu64 "\n", repair ? "Repairing" : "ERROR", l2_entry, refcount); @@ -1912,16 +1933,13 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res, l2_table[j] = cpu_to_be64(refcount == 1 ? l2_entry | QCOW_OFLAG_COPIED : l2_entry & ~QCOW_OFLAG_COPIED); - l2_dirty = true; - res->corruptions_fixed++; - } else { - res->corruptions++; + l2_dirty++; } } } } - if (l2_dirty) { + if (l2_dirty > 0) { ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_ACTIVE_L2, l2_offset, s->cluster_size, false); @@ -1940,6 +1958,8 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res, res->check_errors++; goto fail; } + res->corruptions -= l2_dirty; + res->corruptions_fixed += l2_dirty; } } @@ -1977,6 +1997,7 @@ static int check_refblocks(BlockDriverState *bs, BdrvCheckResult *res, } if (cluster >= *nb_clusters) { + res->corruptions++; fprintf(stderr, "%s refcount block %" PRId64 " is outside image\n", fix & BDRV_FIX_ERRORS ? "Repairing" : "ERROR", i); @@ -2016,6 +2037,7 @@ static int check_refblocks(BlockDriverState *bs, BdrvCheckResult *res, goto resize_fail; } + res->corruptions--; res->corruptions_fixed++; ret = qcow2_inc_refcounts_imrt(bs, res, refcount_table, nb_clusters, @@ -2029,12 +2051,9 @@ static int check_refblocks(BlockDriverState *bs, BdrvCheckResult *res, continue; resize_fail: - res->corruptions++; *rebuild = true; fprintf(stderr, "ERROR could not resize image: %s\n", strerror(-ret)); - } else { - res->corruptions++; } continue; } @@ -2090,7 +2109,7 @@ static int calculate_refcounts(BlockDriverState *bs, BdrvCheckResult *res, /* current L1 table */ ret = check_refcounts_l1(bs, res, refcount_table, nb_clusters, s->l1_table_offset, s->l1_size, CHECK_FRAG_INFO, - fix); + fix, true); if (ret < 0) { return ret; } @@ -2119,7 +2138,8 @@ static int calculate_refcounts(BlockDriverState *bs, BdrvCheckResult *res, continue; } ret = check_refcounts_l1(bs, res, refcount_table, nb_clusters, - sn->l1_table_offset, sn->l1_size, 0, fix); + sn->l1_table_offset, sn->l1_size, 0, fix, + false); if (ret < 0) { return ret; } @@ -2409,8 +2429,8 @@ write_refblocks: on_disk_refblock = (void *)((char *) *refcount_table + refblock_index * s->cluster_size); - ret = bdrv_write(bs->file, refblock_offset / BDRV_SECTOR_SIZE, - on_disk_refblock, s->cluster_sectors); + ret = bdrv_pwrite(bs->file, refblock_offset, on_disk_refblock, + s->cluster_size); if (ret < 0) { fprintf(stderr, "ERROR writing refblock: %s\n", strerror(-ret)); goto fail; diff --git a/block/qcow2.c b/block/qcow2.c index a520d116ef..8e024007db 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -1259,7 +1259,6 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options, s->cluster_bits = header.cluster_bits; s->cluster_size = 1 << s->cluster_bits; - s->cluster_sectors = 1 << (s->cluster_bits - BDRV_SECTOR_BITS); /* Initialise version 3 header fields */ if (header.version == 2) { diff --git a/block/qcow2.h b/block/qcow2.h index fdee297f33..e62508d1ce 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -266,7 +266,6 @@ typedef struct Qcow2BitmapHeaderExt { typedef struct BDRVQcow2State { int cluster_bits; int cluster_size; - int cluster_sectors; int l2_slice_size; int l2_bits; int l2_size; diff --git a/block/ssh.c b/block/ssh.c index 859249113d..12fd4f39e8 100644 --- a/block/ssh.c +++ b/block/ssh.c @@ -75,6 +75,14 @@ typedef struct BDRVSSHState { /* Used to warn if 'flush' is not supported. */ bool unsafe_flush_warning; + + /* + * Store the user name for ssh_refresh_filename() because the + * default depends on the system you are on -- therefore, when we + * generate a filename, it should always contain the user name we + * are actually using. + */ + char *user; } BDRVSSHState; static void ssh_state_init(BDRVSSHState *s) @@ -87,6 +95,8 @@ static void ssh_state_init(BDRVSSHState *s) static void ssh_state_free(BDRVSSHState *s) { + g_free(s->user); + if (s->sftp_handle) { libssh2_sftp_close(s->sftp_handle); } @@ -628,14 +638,13 @@ static int connect_to_ssh(BDRVSSHState *s, BlockdevOptionsSsh *opts, int ssh_flags, int creat_mode, Error **errp) { int r, ret; - const char *user; long port = 0; if (opts->has_user) { - user = opts->user; + s->user = g_strdup(opts->user); } else { - user = g_get_user_name(); - if (!user) { + s->user = g_strdup(g_get_user_name()); + if (!s->user) { error_setg_errno(errp, errno, "Can't get user name"); ret = -errno; goto err; @@ -685,7 +694,7 @@ static int connect_to_ssh(BDRVSSHState *s, BlockdevOptionsSsh *opts, } /* Authenticate. */ - ret = authenticate(s, user, errp); + ret = authenticate(s, s->user, errp); if (ret < 0) { goto err; } @@ -1242,6 +1251,58 @@ static int coroutine_fn ssh_co_truncate(BlockDriverState *bs, int64_t offset, return ssh_grow_file(s, offset, errp); } +static void ssh_refresh_filename(BlockDriverState *bs) +{ + BDRVSSHState *s = bs->opaque; + const char *path, *host_key_check; + int ret; + + /* + * None of these options can be represented in a plain "host:port" + * format, so if any was given, we have to abort. + */ + if (s->inet->has_ipv4 || s->inet->has_ipv6 || s->inet->has_to || + s->inet->has_numeric) + { + return; + } + + path = qdict_get_try_str(bs->full_open_options, "path"); + assert(path); /* mandatory option */ + + host_key_check = qdict_get_try_str(bs->full_open_options, "host_key_check"); + + ret = snprintf(bs->exact_filename, sizeof(bs->exact_filename), + "ssh://%s@%s:%s%s%s%s", + s->user, s->inet->host, s->inet->port, path, + host_key_check ? "?host_key_check=" : "", + host_key_check ?: ""); + if (ret >= sizeof(bs->exact_filename)) { + /* An overflow makes the filename unusable, so do not report any */ + bs->exact_filename[0] = '\0'; + } +} + +static char *ssh_bdrv_dirname(BlockDriverState *bs, Error **errp) +{ + if (qdict_haskey(bs->full_open_options, "host_key_check")) { + /* + * We cannot generate a simple prefix if we would have to + * append a query string. + */ + error_setg(errp, + "Cannot generate a base directory with host_key_check set"); + return NULL; + } + + if (bs->exact_filename[0] == '\0') { + error_setg(errp, "Cannot generate a base directory for this ssh node"); + return NULL; + } + + return path_combine(bs->exact_filename, ""); +} + static const char *const ssh_strong_runtime_opts[] = { "host", "port", @@ -1268,6 +1329,8 @@ static BlockDriver bdrv_ssh = { .bdrv_getlength = ssh_getlength, .bdrv_co_truncate = ssh_co_truncate, .bdrv_co_flush_to_disk = ssh_co_flush, + .bdrv_refresh_filename = ssh_refresh_filename, + .bdrv_dirname = ssh_bdrv_dirname, .create_opts = &ssh_create_opts, .strong_runtime_opts = ssh_strong_runtime_opts, }; diff --git a/block/vdi.c b/block/vdi.c index e1c42ad732..d7ef6628e7 100644 --- a/block/vdi.c +++ b/block/vdi.c @@ -171,6 +171,8 @@ typedef struct { uint64_t unused2[7]; } QEMU_PACKED VdiHeader; +QEMU_BUILD_BUG_ON(sizeof(VdiHeader) != 512); + typedef struct { /* The block map entries are little endian (even in memory). */ uint32_t *bmap; @@ -384,7 +386,7 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags, logout("\n"); - ret = bdrv_read(bs->file, 0, (uint8_t *)&header, 1); + ret = bdrv_pread(bs->file, 0, &header, sizeof(header)); if (ret < 0) { goto fail; } @@ -484,8 +486,8 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags, goto fail; } - ret = bdrv_read(bs->file, s->bmap_sector, (uint8_t *)s->bmap, - bmap_size); + ret = bdrv_pread(bs->file, header.offset_bmap, s->bmap, + bmap_size * SECTOR_SIZE); if (ret < 0) { goto fail_free_bmap; } @@ -704,7 +706,7 @@ nonallocating_write: assert(VDI_IS_ALLOCATED(bmap_first)); *header = s->header; vdi_header_to_le(header); - ret = bdrv_write(bs->file, 0, block, 1); + ret = bdrv_pwrite(bs->file, 0, block, sizeof(VdiHeader)); g_free(block); block = NULL; @@ -722,10 +724,11 @@ nonallocating_write: base = ((uint8_t *)&s->bmap[0]) + bmap_first * SECTOR_SIZE; logout("will write %u block map sectors starting from entry %u\n", n_sectors, bmap_first); - ret = bdrv_write(bs->file, offset, base, n_sectors); + ret = bdrv_pwrite(bs->file, offset * SECTOR_SIZE, base, + n_sectors * SECTOR_SIZE); } - return ret; + return ret < 0 ? ret : 0; } static int coroutine_fn vdi_co_do_create(BlockdevCreateOptions *create_options, diff --git a/block/vvfat.c b/block/vvfat.c index 5f66787890..253cc716dd 100644 --- a/block/vvfat.c +++ b/block/vvfat.c @@ -1494,8 +1494,8 @@ static int vvfat_read(BlockDriverState *bs, int64_t sector_num, DLOG(fprintf(stderr, "sectors %" PRId64 "+%" PRId64 " allocated\n", sector_num, n >> BDRV_SECTOR_BITS)); - if (bdrv_read(s->qcow, sector_num, buf + i * 0x200, - n >> BDRV_SECTOR_BITS)) { + if (bdrv_pread(s->qcow, sector_num * BDRV_SECTOR_SIZE, + buf + i * 0x200, n) < 0) { return -1; } i += (n >> BDRV_SECTOR_BITS) - 1; @@ -1983,8 +1983,9 @@ static uint32_t get_cluster_count_for_direntry(BDRVVVFATState* s, if (res) { return -1; } - res = bdrv_write(s->qcow, offset, s->cluster_buffer, 1); - if (res) { + res = bdrv_pwrite(s->qcow, offset * BDRV_SECTOR_SIZE, + s->cluster_buffer, BDRV_SECTOR_SIZE); + if (res < 0) { return -2; } } @@ -3050,7 +3051,8 @@ DLOG(checkpoint()); * Use qcow backend. Commit later. */ DLOG(fprintf(stderr, "Write to qcow backend: %d + %d\n", (int)sector_num, nb_sectors)); - ret = bdrv_write(s->qcow, sector_num, buf, nb_sectors); + ret = bdrv_pwrite(s->qcow, sector_num * BDRV_SECTOR_SIZE, buf, + nb_sectors * BDRV_SECTOR_SIZE); if (ret < 0) { fprintf(stderr, "Error writing to qcow backend\n"); return ret; @@ -2937,9 +2937,9 @@ if test "$auth_pam" != "no"; then int main(void) { const char *service_name = "qemu"; const char *user = "frank"; - const struct pam_conv *pam_conv = NULL; + const struct pam_conv pam_conv = { 0 }; pam_handle_t *pamh = NULL; - pam_start(service_name, user, pam_conv, &pamh); + pam_start(service_name, user, &pam_conv, &pamh); return 0; } EOF @@ -7882,7 +7882,6 @@ LINKS="$LINKS python" for bios_file in \ $source_path/pc-bios/*.bin \ $source_path/pc-bios/*.lid \ - $source_path/pc-bios/*.aml \ $source_path/pc-bios/*.rom \ $source_path/pc-bios/*.dtb \ $source_path/pc-bios/*.img \ diff --git a/contrib/libvhost-user/libvhost-user.h b/contrib/libvhost-user/libvhost-user.h index 414ceb0a2f..78b33306e8 100644 --- a/contrib/libvhost-user/libvhost-user.h +++ b/contrib/libvhost-user/libvhost-user.h @@ -148,7 +148,7 @@ typedef struct VhostUserInflight { uint16_t queue_size; } VhostUserInflight; -#if defined(_WIN32) +#if defined(_WIN32) && (defined(__x86_64__) || defined(__i386__)) # define VU_PACKED __attribute__((gcc_struct, packed)) #else # define VU_PACKED __attribute__((packed)) diff --git a/docs/devel/index.rst b/docs/devel/index.rst index ebbab636ce..2a4ddf40ad 100644 --- a/docs/devel/index.rst +++ b/docs/devel/index.rst @@ -20,3 +20,4 @@ Contents: stable-process testing decodetree + secure-coding-practices diff --git a/docs/devel/secure-coding-practices.rst b/docs/devel/secure-coding-practices.rst new file mode 100644 index 0000000000..cbfc8af67e --- /dev/null +++ b/docs/devel/secure-coding-practices.rst @@ -0,0 +1,106 @@ +======================= +Secure Coding Practices +======================= +This document covers topics that both developers and security researchers must +be aware of so that they can develop safe code and audit existing code +properly. + +Reporting Security Bugs +----------------------- +For details on how to report security bugs or ask questions about potential +security bugs, see the `Security Process wiki page +<https://wiki.qemu.org/SecurityProcess>`_. + +General Secure C Coding Practices +--------------------------------- +Most CVEs (security bugs) reported against QEMU are not specific to +virtualization or emulation. They are simply C programming bugs. Therefore +it's critical to be aware of common classes of security bugs. + +There is a wide selection of resources available covering secure C coding. For +example, the `CERT C Coding Standard +<https://wiki.sei.cmu.edu/confluence/display/c/SEI+CERT+C+Coding+Standard>`_ +covers the most important classes of security bugs. + +Instead of describing them in detail here, only the names of the most important +classes of security bugs are mentioned: + +* Buffer overflows +* Use-after-free and double-free +* Integer overflows +* Format string vulnerabilities + +Some of these classes of bugs can be detected by analyzers. Static analysis is +performed regularly by Coverity and the most obvious of these bugs are even +reported by compilers. Dynamic analysis is possible with valgrind, tsan, and +asan. + +Input Validation +---------------- +Inputs from the guest or external sources (e.g. network, files) cannot be +trusted and may be invalid. Inputs must be checked before using them in a way +that could crash the program, expose host memory to the guest, or otherwise be +exploitable by an attacker. + +The most sensitive attack surface is device emulation. All hardware register +accesses and data read from guest memory must be validated. A typical example +is a device that contains multiple units that are selectable by the guest via +an index register:: + + typedef struct { + ProcessingUnit unit[2]; + ... + } MyDeviceState; + + static void mydev_writel(void *opaque, uint32_t addr, uint32_t val) + { + MyDeviceState *mydev = opaque; + ProcessingUnit *unit; + + switch (addr) { + case MYDEV_SELECT_UNIT: + unit = &mydev->unit[val]; <-- this input wasn't validated! + ... + } + } + +If ``val`` is not in range [0, 1] then an out-of-bounds memory access will take +place when ``unit`` is dereferenced. The code must check that ``val`` is 0 or +1 and handle the case where it is invalid. + +Unexpected Device Accesses +-------------------------- +The guest may access device registers in unusual orders or at unexpected +moments. Device emulation code must not assume that the guest follows the +typical "theory of operation" presented in driver writer manuals. The guest +may make nonsense accesses to device registers such as starting operations +before the device has been fully initialized. + +A related issue is that device emulation code must be prepared for unexpected +device register accesses while asynchronous operations are in progress. A +well-behaved guest might wait for a completion interrupt before accessing +certain device registers. Device emulation code must handle the case where the +guest overwrites registers or submits further requests before an ongoing +request completes. Unexpected accesses must not cause memory corruption or +leaks in QEMU. + +Invalid device register accesses can be reported with +``qemu_log_mask(LOG_GUEST_ERROR, ...)``. The ``-d guest_errors`` command-line +option enables these log messages. + +Live Migration +-------------- +Device state can be saved to disk image files and shared with other users. +Live migration code must validate inputs when loading device state so an +attacker cannot gain control by crafting invalid device states. Device state +is therefore considered untrusted even though it is typically generated by QEMU +itself. + +Guest Memory Access Races +------------------------- +Guests with multiple vCPUs may modify guest RAM while device emulation code is +running. Device emulation code must copy in descriptors and other guest RAM +structures and only process the local copy. This prevents +time-of-check-to-time-of-use (TOCTOU) race conditions that could cause QEMU to +crash when a vCPU thread modifies guest RAM while device emulation is +processing it. diff --git a/docs/security.texi b/docs/security.texi new file mode 100644 index 0000000000..927764f1e6 --- /dev/null +++ b/docs/security.texi @@ -0,0 +1,131 @@ +@node Security +@chapter Security + +@section Overview + +This chapter explains the security requirements that QEMU is designed to meet +and principles for securely deploying QEMU. + +@section Security Requirements + +QEMU supports many different use cases, some of which have stricter security +requirements than others. The community has agreed on the overall security +requirements that users may depend on. These requirements define what is +considered supported from a security perspective. + +@subsection Virtualization Use Case + +The virtualization use case covers cloud and virtual private server (VPS) +hosting, as well as traditional data center and desktop virtualization. These +use cases rely on hardware virtualization extensions to execute guest code +safely on the physical CPU at close-to-native speed. + +The following entities are untrusted, meaning that they may be buggy or +malicious: + +@itemize +@item Guest +@item User-facing interfaces (e.g. VNC, SPICE, WebSocket) +@item Network protocols (e.g. NBD, live migration) +@item User-supplied files (e.g. disk images, kernels, device trees) +@item Passthrough devices (e.g. PCI, USB) +@end itemize + +Bugs affecting these entities are evaluated on whether they can cause damage in +real-world use cases and treated as security bugs if this is the case. + +@subsection Non-virtualization Use Case + +The non-virtualization use case covers emulation using the Tiny Code Generator +(TCG). In principle the TCG and device emulation code used in conjunction with +the non-virtualization use case should meet the same security requirements as +the virtualization use case. However, for historical reasons much of the +non-virtualization use case code was not written with these security +requirements in mind. + +Bugs affecting the non-virtualization use case are not considered security +bugs at this time. Users with non-virtualization use cases must not rely on +QEMU to provide guest isolation or any security guarantees. + +@section Architecture + +This section describes the design principles that ensure the security +requirements are met. + +@subsection Guest Isolation + +Guest isolation is the confinement of guest code to the virtual machine. When +guest code gains control of execution on the host this is called escaping the +virtual machine. Isolation also includes resource limits such as throttling of +CPU, memory, disk, or network. Guests must be unable to exceed their resource +limits. + +QEMU presents an attack surface to the guest in the form of emulated devices. +The guest must not be able to gain control of QEMU. Bugs in emulated devices +could allow malicious guests to gain code execution in QEMU. At this point the +guest has escaped the virtual machine and is able to act in the context of the +QEMU process on the host. + +Guests often interact with other guests and share resources with them. A +malicious guest must not gain control of other guests or access their data. +Disk image files and network traffic must be protected from other guests unless +explicitly shared between them by the user. + +@subsection Principle of Least Privilege + +The principle of least privilege states that each component only has access to +the privileges necessary for its function. In the case of QEMU this means that +each process only has access to resources belonging to the guest. + +The QEMU process should not have access to any resources that are inaccessible +to the guest. This way the guest does not gain anything by escaping into the +QEMU process since it already has access to those same resources from within +the guest. + +Following the principle of least privilege immediately fulfills guest isolation +requirements. For example, guest A only has access to its own disk image file +@code{a.img} and not guest B's disk image file @code{b.img}. + +In reality certain resources are inaccessible to the guest but must be +available to QEMU to perform its function. For example, host system calls are +necessary for QEMU but are not exposed to guests. A guest that escapes into +the QEMU process can then begin invoking host system calls. + +New features must be designed to follow the principle of least privilege. +Should this not be possible for technical reasons, the security risk must be +clearly documented so users are aware of the trade-off of enabling the feature. + +@subsection Isolation mechanisms + +Several isolation mechanisms are available to realize this architecture of +guest isolation and the principle of least privilege. With the exception of +Linux seccomp, these mechanisms are all deployed by management tools that +launch QEMU, such as libvirt. They are also platform-specific so they are only +described briefly for Linux here. + +The fundamental isolation mechanism is that QEMU processes must run as +unprivileged users. Sometimes it seems more convenient to launch QEMU as +root to give it access to host devices (e.g. @code{/dev/net/tun}) but this poses a +huge security risk. File descriptor passing can be used to give an otherwise +unprivileged QEMU process access to host devices without running QEMU as root. +It is also possible to launch QEMU as a non-root user and configure UNIX groups +for access to @code{/dev/kvm}, @code{/dev/net/tun}, and other device nodes. +Some Linux distros already ship with UNIX groups for these devices by default. + +@itemize +@item SELinux and AppArmor make it possible to confine processes beyond the +traditional UNIX process and file permissions model. They restrict the QEMU +process from accessing processes and files on the host system that are not +needed by QEMU. + +@item Resource limits and cgroup controllers provide throughput and utilization +limits on key resources such as CPU time, memory, and I/O bandwidth. + +@item Linux namespaces can be used to make process, file system, and other system +resources unavailable to QEMU. A namespaced QEMU process is restricted to only +those resources that were granted to it. + +@item Linux seccomp is available via the QEMU @option{--sandbox} option. It disables +system calls that are not needed by QEMU, thereby reducing the host kernel +attack surface. +@end itemize diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c index 1c23ebd992..29d225ed14 100644 --- a/hw/arm/aspeed.c +++ b/hw/arm/aspeed.c @@ -25,6 +25,7 @@ #include "sysemu/block-backend.h" #include "hw/loader.h" #include "qemu/error-report.h" +#include "qemu/units.h" static struct arm_boot_info aspeed_board_binfo = { .board_id = -1, /* device-tree-only board */ @@ -331,6 +332,9 @@ static void aspeed_machine_class_init(ObjectClass *oc, void *data) mc->no_floppy = 1; mc->no_cdrom = 1; mc->no_parallel = 1; + if (board->ram) { + mc->default_ram_size = board->ram; + } amc->board = board; } @@ -352,6 +356,7 @@ static const AspeedBoardConfig aspeed_boards[] = { .spi_model = "mx25l25635e", .num_cs = 1, .i2c_init = palmetto_bmc_i2c_init, + .ram = 256 * MiB, }, { .name = MACHINE_TYPE_NAME("ast2500-evb"), .desc = "Aspeed AST2500 EVB (ARM1176)", @@ -361,6 +366,7 @@ static const AspeedBoardConfig aspeed_boards[] = { .spi_model = "mx25l25635e", .num_cs = 1, .i2c_init = ast2500_evb_i2c_init, + .ram = 512 * MiB, }, { .name = MACHINE_TYPE_NAME("romulus-bmc"), .desc = "OpenPOWER Romulus BMC (ARM1176)", @@ -370,6 +376,7 @@ static const AspeedBoardConfig aspeed_boards[] = { .spi_model = "mx66l1g45g", .num_cs = 2, .i2c_init = romulus_bmc_i2c_init, + .ram = 512 * MiB, }, { .name = MACHINE_TYPE_NAME("witherspoon-bmc"), .desc = "OpenPOWER Witherspoon BMC (ARM1176)", @@ -379,6 +386,7 @@ static const AspeedBoardConfig aspeed_boards[] = { .spi_model = "mx66l1g45g", .num_cs = 2, .i2c_init = witherspoon_bmc_i2c_init, + .ram = 512 * MiB, }, }; diff --git a/hw/arm/raspi.c b/hw/arm/raspi.c index 66899c28dc..fe2bb511b9 100644 --- a/hw/arm/raspi.c +++ b/hw/arm/raspi.c @@ -12,6 +12,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qapi/error.h" #include "qemu-common.h" #include "cpu.h" @@ -175,6 +176,12 @@ static void raspi_init(MachineState *machine, int version) BusState *bus; DeviceState *carddev; + if (machine->ram_size > 1 * GiB) { + error_report("Requested ram size is too large for this machine: " + "maximum is 1GB"); + exit(1); + } + object_initialize(&s->soc, sizeof(s->soc), version == 3 ? TYPE_BCM2837 : TYPE_BCM2836); object_property_add_child(OBJECT(machine), "soc", OBJECT(&s->soc), diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 16ba67f7a7..5331ab71e2 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -30,6 +30,7 @@ #include "qemu/osdep.h" #include "qemu/units.h" +#include "qemu/option.h" #include "qapi/error.h" #include "hw/sysbus.h" #include "hw/arm/arm.h" @@ -871,25 +872,19 @@ static void create_virtio_devices(const VirtMachineState *vms, qemu_irq *pic) } } -static void create_one_flash(const char *name, hwaddr flashbase, - hwaddr flashsize, const char *file, - MemoryRegion *sysmem) +#define VIRT_FLASH_SECTOR_SIZE (256 * KiB) + +static PFlashCFI01 *virt_flash_create1(VirtMachineState *vms, + const char *name, + const char *alias_prop_name) { - /* Create and map a single flash device. We use the same - * parameters as the flash devices on the Versatile Express board. + /* + * Create a single flash device. We use the same parameters as + * the flash devices on the Versatile Express board. */ - DriveInfo *dinfo = drive_get_next(IF_PFLASH); DeviceState *dev = qdev_create(NULL, TYPE_PFLASH_CFI01); - SysBusDevice *sbd = SYS_BUS_DEVICE(dev); - const uint64_t sectorlength = 256 * 1024; - - if (dinfo) { - qdev_prop_set_drive(dev, "drive", blk_by_legacy_dinfo(dinfo), - &error_abort); - } - qdev_prop_set_uint32(dev, "num-blocks", flashsize / sectorlength); - qdev_prop_set_uint64(dev, "sector-length", sectorlength); + qdev_prop_set_uint64(dev, "sector-length", VIRT_FLASH_SECTOR_SIZE); qdev_prop_set_uint8(dev, "width", 4); qdev_prop_set_uint8(dev, "device-width", 2); qdev_prop_set_bit(dev, "big-endian", false); @@ -898,41 +893,41 @@ static void create_one_flash(const char *name, hwaddr flashbase, qdev_prop_set_uint16(dev, "id2", 0x00); qdev_prop_set_uint16(dev, "id3", 0x00); qdev_prop_set_string(dev, "name", name); - qdev_init_nofail(dev); + object_property_add_child(OBJECT(vms), name, OBJECT(dev), + &error_abort); + object_property_add_alias(OBJECT(vms), alias_prop_name, + OBJECT(dev), "drive", &error_abort); + return PFLASH_CFI01(dev); +} - memory_region_add_subregion(sysmem, flashbase, - sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0)); +static void virt_flash_create(VirtMachineState *vms) +{ + vms->flash[0] = virt_flash_create1(vms, "virt.flash0", "pflash0"); + vms->flash[1] = virt_flash_create1(vms, "virt.flash1", "pflash1"); +} - if (file) { - char *fn; - int image_size; +static void virt_flash_map1(PFlashCFI01 *flash, + hwaddr base, hwaddr size, + MemoryRegion *sysmem) +{ + DeviceState *dev = DEVICE(flash); - if (drive_get(IF_PFLASH, 0, 0)) { - error_report("The contents of the first flash device may be " - "specified with -bios or with -drive if=pflash... " - "but you cannot use both options at once"); - exit(1); - } - fn = qemu_find_file(QEMU_FILE_TYPE_BIOS, file); - if (!fn) { - error_report("Could not find ROM image '%s'", file); - exit(1); - } - image_size = load_image_mr(fn, sysbus_mmio_get_region(sbd, 0)); - g_free(fn); - if (image_size < 0) { - error_report("Could not load ROM image '%s'", file); - exit(1); - } - } + assert(size % VIRT_FLASH_SECTOR_SIZE == 0); + assert(size / VIRT_FLASH_SECTOR_SIZE <= UINT32_MAX); + qdev_prop_set_uint32(dev, "num-blocks", size / VIRT_FLASH_SECTOR_SIZE); + qdev_init_nofail(dev); + + memory_region_add_subregion(sysmem, base, + sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), + 0)); } -static void create_flash(const VirtMachineState *vms, - MemoryRegion *sysmem, - MemoryRegion *secure_sysmem) +static void virt_flash_map(VirtMachineState *vms, + MemoryRegion *sysmem, + MemoryRegion *secure_sysmem) { - /* Create two flash devices to fill the VIRT_FLASH space in the memmap. - * Any file passed via -bios goes in the first of these. + /* + * Map two flash devices to fill the VIRT_FLASH space in the memmap. * sysmem is the system memory space. secure_sysmem is the secure view * of the system, and the first flash device should be made visible only * there. The second flash device is visible to both secure and nonsecure. @@ -941,12 +936,20 @@ static void create_flash(const VirtMachineState *vms, */ hwaddr flashsize = vms->memmap[VIRT_FLASH].size / 2; hwaddr flashbase = vms->memmap[VIRT_FLASH].base; - char *nodename; - create_one_flash("virt.flash0", flashbase, flashsize, - bios_name, secure_sysmem); - create_one_flash("virt.flash1", flashbase + flashsize, flashsize, - NULL, sysmem); + virt_flash_map1(vms->flash[0], flashbase, flashsize, + secure_sysmem); + virt_flash_map1(vms->flash[1], flashbase + flashsize, flashsize, + sysmem); +} + +static void virt_flash_fdt(VirtMachineState *vms, + MemoryRegion *sysmem, + MemoryRegion *secure_sysmem) +{ + hwaddr flashsize = vms->memmap[VIRT_FLASH].size / 2; + hwaddr flashbase = vms->memmap[VIRT_FLASH].base; + char *nodename; if (sysmem == secure_sysmem) { /* Report both flash devices as a single node in the DT */ @@ -959,7 +962,8 @@ static void create_flash(const VirtMachineState *vms, qemu_fdt_setprop_cell(vms->fdt, nodename, "bank-width", 4); g_free(nodename); } else { - /* Report the devices as separate nodes so we can mark one as + /* + * Report the devices as separate nodes so we can mark one as * only visible to the secure world. */ nodename = g_strdup_printf("/secflash@%" PRIx64, flashbase); @@ -982,6 +986,54 @@ static void create_flash(const VirtMachineState *vms, } } +static bool virt_firmware_init(VirtMachineState *vms, + MemoryRegion *sysmem, + MemoryRegion *secure_sysmem) +{ + int i; + BlockBackend *pflash_blk0; + + /* Map legacy -drive if=pflash to machine properties */ + for (i = 0; i < ARRAY_SIZE(vms->flash); i++) { + pflash_cfi01_legacy_drive(vms->flash[i], + drive_get(IF_PFLASH, 0, i)); + } + + virt_flash_map(vms, sysmem, secure_sysmem); + + pflash_blk0 = pflash_cfi01_get_blk(vms->flash[0]); + + if (bios_name) { + char *fname; + MemoryRegion *mr; + int image_size; + + if (pflash_blk0) { + error_report("The contents of the first flash device may be " + "specified with -bios or with -drive if=pflash... " + "but you cannot use both options at once"); + exit(1); + } + + /* Fall back to -bios */ + + fname = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); + if (!fname) { + error_report("Could not find ROM image '%s'", bios_name); + exit(1); + } + mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(vms->flash[0]), 0); + image_size = load_image_mr(fname, mr); + g_free(fname); + if (image_size < 0) { + error_report("Could not load ROM image '%s'", bios_name); + exit(1); + } + } + + return pflash_blk0 || bios_name; +} + static FWCfgState *create_fw_cfg(const VirtMachineState *vms, AddressSpace *as) { hwaddr base = vms->memmap[VIRT_FW_CFG].base; @@ -1421,7 +1473,7 @@ static void machvirt_init(MachineState *machine) MemoryRegion *secure_sysmem = NULL; int n, virt_max_cpus; MemoryRegion *ram = g_new(MemoryRegion, 1); - bool firmware_loaded = bios_name || drive_get(IF_PFLASH, 0, 0); + bool firmware_loaded; bool aarch64 = true; /* @@ -1460,6 +1512,27 @@ static void machvirt_init(MachineState *machine) exit(1); } + if (vms->secure) { + if (kvm_enabled()) { + error_report("mach-virt: KVM does not support Security extensions"); + exit(1); + } + + /* + * The Secure view of the world is the same as the NonSecure, + * but with a few extra devices. Create it as a container region + * containing the system memory at low priority; any secure-only + * devices go in at higher priority and take precedence. + */ + secure_sysmem = g_new(MemoryRegion, 1); + memory_region_init(secure_sysmem, OBJECT(machine), "secure-memory", + UINT64_MAX); + memory_region_add_subregion_overlap(secure_sysmem, 0, sysmem, -1); + } + + firmware_loaded = virt_firmware_init(vms, sysmem, + secure_sysmem ?: sysmem); + /* If we have an EL3 boot ROM then the assumption is that it will * implement PSCI itself, so disable QEMU's internal implementation * so it doesn't get in the way. Instead of starting secondary @@ -1505,23 +1578,6 @@ static void machvirt_init(MachineState *machine) exit(1); } - if (vms->secure) { - if (kvm_enabled()) { - error_report("mach-virt: KVM does not support Security extensions"); - exit(1); - } - - /* The Secure view of the world is the same as the NonSecure, - * but with a few extra devices. Create it as a container region - * containing the system memory at low priority; any secure-only - * devices go in at higher priority and take precedence. - */ - secure_sysmem = g_new(MemoryRegion, 1); - memory_region_init(secure_sysmem, OBJECT(machine), "secure-memory", - UINT64_MAX); - memory_region_add_subregion_overlap(secure_sysmem, 0, sysmem, -1); - } - create_fdt(vms); possible_cpus = mc->possible_cpu_arch_ids(machine); @@ -1610,7 +1666,7 @@ static void machvirt_init(MachineState *machine) &machine->device_memory->mr); } - create_flash(vms, sysmem, secure_sysmem ? secure_sysmem : sysmem); + virt_flash_fdt(vms, sysmem, secure_sysmem); create_gic(vms, pic); @@ -1956,6 +2012,8 @@ static void virt_instance_init(Object *obj) NULL); vms->irqmap = a15irqmap; + + virt_flash_create(vms); } static const TypeInfo virt_machine_info = { diff --git a/hw/block/pflash_cfi01.c b/hw/block/pflash_cfi01.c index 16dfae14b8..333b736277 100644 --- a/hw/block/pflash_cfi01.c +++ b/hw/block/pflash_cfi01.c @@ -44,9 +44,12 @@ #include "qapi/error.h" #include "qemu/timer.h" #include "qemu/bitops.h" +#include "qemu/error-report.h" #include "qemu/host-utils.h" #include "qemu/log.h" +#include "qemu/option.h" #include "hw/sysbus.h" +#include "sysemu/blockdev.h" #include "sysemu/sysemu.h" #include "trace.h" @@ -968,6 +971,31 @@ MemoryRegion *pflash_cfi01_get_memory(PFlashCFI01 *fl) return &fl->mem; } +/* + * Handle -drive if=pflash for machines that use properties. + * If @dinfo is null, do nothing. + * Else if @fl's property "drive" is already set, fatal error. + * Else set it to the BlockBackend with @dinfo. + */ +void pflash_cfi01_legacy_drive(PFlashCFI01 *fl, DriveInfo *dinfo) +{ + Location loc; + + if (!dinfo) { + return; + } + + loc_push_none(&loc); + qemu_opts_loc_restore(dinfo->opts); + if (fl->blk) { + error_report("clashes with -machine"); + exit(1); + } + qdev_prop_set_drive(DEVICE(fl), "drive", + blk_by_legacy_dinfo(dinfo), &error_fatal); + loc_pop(&loc); +} + static void postload_update_cb(void *opaque, int running, RunState state) { PFlashCFI01 *pfl = opaque; diff --git a/hw/display/Kconfig b/hw/display/Kconfig index 72be57a403..c236cd2d0a 100644 --- a/hw/display/Kconfig +++ b/hw/display/Kconfig @@ -1,3 +1,8 @@ +config DDC + bool + depends on I2C + select EDID + config EDID bool diff --git a/hw/display/Makefile.objs b/hw/display/Makefile.objs index dbd453ab1b..650031f725 100644 --- a/hw/display/Makefile.objs +++ b/hw/display/Makefile.objs @@ -1,3 +1,4 @@ +common-obj-$(CONFIG_DDC) += i2c-ddc.o common-obj-$(CONFIG_EDID) += edid-generate.o edid-region.o common-obj-$(CONFIG_FW_CFG_DMA) += ramfb.o diff --git a/hw/display/ati_2d.c b/hw/display/ati_2d.c index bc98ba6eeb..fe3ae14864 100644 --- a/hw/display/ati_2d.c +++ b/hw/display/ati_2d.c @@ -79,10 +79,10 @@ void ati_2d_blt(ATIVGAState *s) s->regs.dst_width, s->regs.dst_height); end = s->vga.vram_ptr + s->vga.vram_size; if (src_bits >= end || dst_bits >= end || - src_bits + (s->regs.src_y + s->regs.dst_height) * src_stride + - s->regs.src_x >= end || - dst_bits + (s->regs.dst_y + s->regs.dst_height) * dst_stride + - s->regs.dst_x >= end) { + src_bits + s->regs.src_x + (s->regs.src_y + s->regs.dst_height) * + src_stride * sizeof(uint32_t) >= end || + dst_bits + s->regs.dst_x + (s->regs.dst_y + s->regs.dst_height) * + dst_stride * sizeof(uint32_t) >= end) { qemu_log_mask(LOG_UNIMP, "blt outside vram not implemented\n"); return; } @@ -140,8 +140,8 @@ void ati_2d_blt(ATIVGAState *s) filler); end = s->vga.vram_ptr + s->vga.vram_size; if (dst_bits >= end || - dst_bits + (s->regs.dst_y + s->regs.dst_height) * dst_stride + - s->regs.dst_x >= end) { + dst_bits + s->regs.dst_x + (s->regs.dst_y + s->regs.dst_height) * + dst_stride * sizeof(uint32_t) >= end) { qemu_log_mask(LOG_UNIMP, "blt outside vram not implemented\n"); return; } diff --git a/hw/display/cirrus_vga.c b/hw/display/cirrus_vga.c index a0e71469f4..76c052c702 100644 --- a/hw/display/cirrus_vga.c +++ b/hw/display/cirrus_vga.c @@ -23,8 +23,13 @@ * THE SOFTWARE. */ /* - * Reference: Finn Thogersons' VGADOC4b - * available at http://home.worldonline.dk/~finth/ + * Reference: Finn Thogersons' VGADOC4b: + * + * http://web.archive.org/web/20021019054927/http://home.worldonline.dk/finth/ + * + * VGADOC4b.ZIP content available at: + * + * https://pdos.csail.mit.edu/6.828/2005/readings/hardware/vgadoc */ #include "qemu/osdep.h" #include "qemu/units.h" @@ -33,7 +38,6 @@ #include "hw/hw.h" #include "hw/pci/pci.h" #include "ui/pixel_ops.h" -#include "hw/loader.h" #include "cirrus_vga_internal.h" /* diff --git a/hw/i2c/i2c-ddc.c b/hw/display/i2c-ddc.c index 7aa8727771..9fe5403a92 100644 --- a/hw/i2c/i2c-ddc.c +++ b/hw/display/i2c-ddc.c @@ -20,7 +20,7 @@ #include "qemu-common.h" #include "qemu/log.h" #include "hw/i2c/i2c.h" -#include "hw/i2c/i2c-ddc.h" +#include "hw/display/i2c-ddc.h" #ifndef DEBUG_I2CDDC #define DEBUG_I2CDDC 0 diff --git a/hw/display/qxl.c b/hw/display/qxl.c index c8ce5781e0..3880a7410b 100644 --- a/hw/display/qxl.c +++ b/hw/display/qxl.c @@ -33,24 +33,6 @@ #include "qxl.h" -/* - * NOTE: SPICE_RING_PROD_ITEM accesses memory on the pci bar and as - * such can be changed by the guest, so to avoid a guest trigerrable - * abort we just qxl_set_guest_bug and set the return to NULL. Still - * it may happen as a result of emulator bug as well. - */ -#undef SPICE_RING_PROD_ITEM -#define SPICE_RING_PROD_ITEM(qxl, r, ret) { \ - uint32_t prod = (r)->prod & SPICE_RING_INDEX_MASK(r); \ - if (prod >= ARRAY_SIZE((r)->items)) { \ - qxl_set_guest_bug(qxl, "SPICE_RING_PROD_ITEM indices mismatch " \ - "%u >= %zu", prod, ARRAY_SIZE((r)->items)); \ - ret = NULL; \ - } else { \ - ret = &(r)->items[prod].el; \ - } \ - } - #undef SPICE_RING_CONS_ITEM #define SPICE_RING_CONS_ITEM(qxl, r, ret) { \ uint32_t cons = (r)->cons & SPICE_RING_INDEX_MASK(r); \ @@ -414,7 +396,8 @@ static void init_qxl_rom(PCIQXLDevice *d) static void init_qxl_ram(PCIQXLDevice *d) { uint8_t *buf; - uint64_t *item; + uint32_t prod; + QXLReleaseRing *ring; buf = d->vga.vram_ptr; d->ram = (QXLRam *)(buf + le32_to_cpu(d->shadow_rom.ram_header_offset)); @@ -426,9 +409,12 @@ static void init_qxl_ram(PCIQXLDevice *d) SPICE_RING_INIT(&d->ram->cmd_ring); SPICE_RING_INIT(&d->ram->cursor_ring); SPICE_RING_INIT(&d->ram->release_ring); - SPICE_RING_PROD_ITEM(d, &d->ram->release_ring, item); - assert(item); - *item = 0; + + ring = &d->ram->release_ring; + prod = ring->prod & SPICE_RING_INDEX_MASK(ring); + assert(prod < ARRAY_SIZE(ring->items)); + ring->items[prod].el = 0; + qxl_ring_set_dirty(d); } @@ -732,7 +718,7 @@ static int interface_req_cmd_notification(QXLInstance *sin) static inline void qxl_push_free_res(PCIQXLDevice *d, int flush) { QXLReleaseRing *ring = &d->ram->release_ring; - uint64_t *item; + uint32_t prod; int notify; #define QXL_FREE_BUNCH_SIZE 32 @@ -759,11 +745,15 @@ static inline void qxl_push_free_res(PCIQXLDevice *d, int flush) if (notify) { qxl_send_events(d, QXL_INTERRUPT_DISPLAY); } - SPICE_RING_PROD_ITEM(d, ring, item); - if (!item) { + + ring = &d->ram->release_ring; + prod = ring->prod & SPICE_RING_INDEX_MASK(ring); + if (prod >= ARRAY_SIZE(ring->items)) { + qxl_set_guest_bug(d, "SPICE_RING_PROD_ITEM indices mismatch " + "%u >= %zu", prod, ARRAY_SIZE(ring->items)); return; } - *item = 0; + ring->items[prod].el = 0; d->num_free_res = 0; d->last_release = NULL; qxl_ring_set_dirty(d); @@ -775,8 +765,12 @@ static void interface_release_resource(QXLInstance *sin, { PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl); QXLReleaseRing *ring; - uint64_t *item, id; + uint32_t prod; + uint64_t id; + if (!ext.info) { + return; + } if (ext.group_id == MEMSLOT_GROUP_HOST) { /* host group -> vga mode update request */ QXLCommandExt *cmdext = (void *)(intptr_t)(ext.info->id); @@ -792,16 +786,18 @@ static void interface_release_resource(QXLInstance *sin, * pci bar 0, $command.release_info */ ring = &qxl->ram->release_ring; - SPICE_RING_PROD_ITEM(qxl, ring, item); - if (!item) { + prod = ring->prod & SPICE_RING_INDEX_MASK(ring); + if (prod >= ARRAY_SIZE(ring->items)) { + qxl_set_guest_bug(qxl, "SPICE_RING_PROD_ITEM indices mismatch " + "%u >= %zu", prod, ARRAY_SIZE(ring->items)); return; } - if (*item == 0) { + if (ring->items[prod].el == 0) { /* stick head into the ring */ id = ext.info->id; ext.info->next = 0; qxl_ram_set_dirty(qxl, &ext.info->next); - *item = id; + ring->items[prod].el = id; qxl_ring_set_dirty(qxl); } else { /* append item to the list */ diff --git a/hw/display/sii9022.c b/hw/display/sii9022.c index 9994385c35..9c36e4c17e 100644 --- a/hw/display/sii9022.c +++ b/hw/display/sii9022.c @@ -16,7 +16,7 @@ #include "qemu/osdep.h" #include "qemu-common.h" #include "hw/i2c/i2c.h" -#include "hw/i2c/i2c-ddc.h" +#include "hw/display/i2c-ddc.h" #include "trace.h" #define SII9022_SYS_CTRL_DATA 0x1a diff --git a/hw/display/sm501.c b/hw/display/sm501.c index 2122291308..1e2709b2d0 100644 --- a/hw/display/sm501.c +++ b/hw/display/sm501.c @@ -35,7 +35,7 @@ #include "hw/sysbus.h" #include "hw/pci/pci.h" #include "hw/i2c/i2c.h" -#include "hw/i2c/i2c-ddc.h" +#include "hw/display/i2c-ddc.h" #include "qemu/range.h" #include "ui/pixel_ops.h" #include "qemu/bswap.h" diff --git a/hw/i2c/Kconfig b/hw/i2c/Kconfig index 820b24de5b..78a2008e3a 100644 --- a/hw/i2c/Kconfig +++ b/hw/i2c/Kconfig @@ -5,11 +5,6 @@ config SMBUS_EEPROM bool depends on I2C -config DDC - bool - depends on I2C - select EDID - config VERSATILE_I2C bool select I2C diff --git a/hw/i2c/Makefile.objs b/hw/i2c/Makefile.objs index 5f76b6a990..d7073a401f 100644 --- a/hw/i2c/Makefile.objs +++ b/hw/i2c/Makefile.objs @@ -1,6 +1,5 @@ common-obj-$(CONFIG_I2C) += core.o smbus_slave.o smbus_master.o common-obj-$(CONFIG_SMBUS_EEPROM) += smbus_eeprom.o -common-obj-$(CONFIG_DDC) += i2c-ddc.o common-obj-$(CONFIG_VERSATILE_I2C) += versatile_i2c.o common-obj-$(CONFIG_ACPI_X86_ICH) += smbus_ich9.o common-obj-$(CONFIG_ACPI_SMBUS) += pm_smbus.o diff --git a/hw/i2c/smbus_ich9.c b/hw/i2c/smbus_ich9.c index 7b24be8256..251d3d142f 100644 --- a/hw/i2c/smbus_ich9.c +++ b/hw/i2c/smbus_ich9.c @@ -6,23 +6,18 @@ * VA Linux Systems Japan K.K. * Copyright (C) 2012 Jason Baron <jbaron@redhat.com> * - * This is based on acpi.c, but heavily rewritten. + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License version 2 as published by the Free Software Foundation. - * - * This library is distributed in the hope that it will be useful, + * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see <http://www.gnu.org/licenses/> - * - * Contributions after 2012-01-13 are licensed under the terms of the - * GNU GPL, version 2 or (at your option) any later version. + * General Public License for more details. * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/> */ #include "qemu/osdep.h" #include "hw/hw.h" diff --git a/hw/i386/pc_sysfw.c b/hw/i386/pc_sysfw.c index c628540774..751fcafa12 100644 --- a/hw/i386/pc_sysfw.c +++ b/hw/i386/pc_sysfw.c @@ -269,9 +269,7 @@ void pc_system_firmware_init(PCMachineState *pcms, { PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); int i; - DriveInfo *pflash_drv; BlockBackend *pflash_blk[ARRAY_SIZE(pcms->flash)]; - Location loc; if (!pcmc->pci_enabled) { old_pc_system_rom_init(rom_memory, true); @@ -280,21 +278,9 @@ void pc_system_firmware_init(PCMachineState *pcms, /* Map legacy -drive if=pflash to machine properties */ for (i = 0; i < ARRAY_SIZE(pcms->flash); i++) { + pflash_cfi01_legacy_drive(pcms->flash[i], + drive_get(IF_PFLASH, 0, i)); pflash_blk[i] = pflash_cfi01_get_blk(pcms->flash[i]); - pflash_drv = drive_get(IF_PFLASH, 0, i); - if (!pflash_drv) { - continue; - } - loc_push_none(&loc); - qemu_opts_loc_restore(pflash_drv->opts); - if (pflash_blk[i]) { - error_report("clashes with -machine"); - exit(1); - } - pflash_blk[i] = blk_by_legacy_dinfo(pflash_drv); - qdev_prop_set_drive(DEVICE(pcms->flash[i]), - "drive", pflash_blk[i], &error_fatal); - loc_pop(&loc); } /* Reject gaps */ diff --git a/hw/intc/armv7m_nvic.c b/hw/intc/armv7m_nvic.c index fff6e694e6..3a346a682a 100644 --- a/hw/intc/armv7m_nvic.c +++ b/hw/intc/armv7m_nvic.c @@ -213,6 +213,7 @@ static void nvic_recompute_state_secure(NVICState *s) int active_prio = NVIC_NOEXC_PRIO; int pend_irq = 0; bool pending_is_s_banked = false; + int pend_subprio = 0; /* R_CQRV: precedence is by: * - lowest group priority; if both the same then @@ -226,7 +227,7 @@ static void nvic_recompute_state_secure(NVICState *s) for (i = 1; i < s->num_irq; i++) { for (bank = M_REG_S; bank >= M_REG_NS; bank--) { VecInfo *vec; - int prio; + int prio, subprio; bool targets_secure; if (bank == M_REG_S) { @@ -241,8 +242,12 @@ static void nvic_recompute_state_secure(NVICState *s) } prio = exc_group_prio(s, vec->prio, targets_secure); - if (vec->enabled && vec->pending && prio < pend_prio) { + subprio = vec->prio & ~nvic_gprio_mask(s, targets_secure); + if (vec->enabled && vec->pending && + ((prio < pend_prio) || + (prio == pend_prio && prio >= 0 && subprio < pend_subprio))) { pend_prio = prio; + pend_subprio = subprio; pend_irq = i; pending_is_s_banked = (bank == M_REG_S); } @@ -1162,6 +1167,10 @@ static uint32_t nvic_readl(NVICState *s, uint32_t offset, MemTxAttrs attrs) if (!arm_feature(&cpu->env, ARM_FEATURE_M_MAIN)) { goto bad_offset; } + if (!attrs.secure && + !(s->cpu->env.v7m.aircr & R_V7M_AIRCR_BFHFNMINS_MASK)) { + return 0; + } return cpu->env.v7m.bfar; case 0xd3c: /* Aux Fault Status. */ /* TODO: Implement fault status registers. */ @@ -1641,6 +1650,10 @@ static void nvic_writel(NVICState *s, uint32_t offset, uint32_t value, if (!arm_feature(&cpu->env, ARM_FEATURE_M_MAIN)) { goto bad_offset; } + if (!attrs.secure && + !(s->cpu->env.v7m.aircr & R_V7M_AIRCR_BFHFNMINS_MASK)) { + return; + } cpu->env.v7m.bfar = value; return; case 0xd3c: /* Aux Fault Status. */ @@ -2125,11 +2138,18 @@ static MemTxResult nvic_sysreg_read(void *opaque, hwaddr addr, val = 0; break; }; - /* The BFSR bits [15:8] are shared between security states - * and we store them in the NS copy + /* + * The BFSR bits [15:8] are shared between security states + * and we store them in the NS copy. They are RAZ/WI for + * NS code if AIRCR.BFHFNMINS is 0. */ val = s->cpu->env.v7m.cfsr[attrs.secure]; - val |= s->cpu->env.v7m.cfsr[M_REG_NS] & R_V7M_CFSR_BFSR_MASK; + if (!attrs.secure && + !(s->cpu->env.v7m.aircr & R_V7M_AIRCR_BFHFNMINS_MASK)) { + val &= ~R_V7M_CFSR_BFSR_MASK; + } else { + val |= s->cpu->env.v7m.cfsr[M_REG_NS] & R_V7M_CFSR_BFSR_MASK; + } val = extract32(val, (offset - 0xd28) * 8, size * 8); break; case 0xfe0 ... 0xfff: /* ID. */ @@ -2244,6 +2264,12 @@ static MemTxResult nvic_sysreg_write(void *opaque, hwaddr addr, */ value <<= ((offset - 0xd28) * 8); + if (!attrs.secure && + !(s->cpu->env.v7m.aircr & R_V7M_AIRCR_BFHFNMINS_MASK)) { + /* BFSR bits are RAZ/WI for NS if BFHFNMINS is set */ + value &= ~R_V7M_CFSR_BFSR_MASK; + } + s->cpu->env.v7m.cfsr[attrs.secure] &= ~value; if (attrs.secure) { /* The BFSR bits [15:8] are shared between security states @@ -2465,10 +2491,12 @@ static void armv7m_nvic_reset(DeviceState *dev) * the System Handler Control register */ s->vectors[ARMV7M_EXCP_SVC].enabled = 1; - s->vectors[ARMV7M_EXCP_DEBUG].enabled = 1; s->vectors[ARMV7M_EXCP_PENDSV].enabled = 1; s->vectors[ARMV7M_EXCP_SYSTICK].enabled = 1; + /* DebugMonitor is enabled via DEMCR.MON_EN */ + s->vectors[ARMV7M_EXCP_DEBUG].enabled = 0; + resetprio = arm_feature(&s->cpu->env, ARM_FEATURE_V8) ? -4 : -3; s->vectors[ARMV7M_EXCP_RESET].prio = resetprio; s->vectors[ARMV7M_EXCP_NMI].prio = -2; diff --git a/hw/net/pcnet.c b/hw/net/pcnet.c index d9ba04bdfc..16683091c9 100644 --- a/hw/net/pcnet.c +++ b/hw/net/pcnet.c @@ -36,6 +36,7 @@ */ #include "qemu/osdep.h" +#include "qemu/log.h" #include "hw/qdev.h" #include "net/net.h" #include "net/eth.h" @@ -1501,7 +1502,8 @@ static void pcnet_bcr_writew(PCNetState *s, uint32_t rap, uint32_t val) val |= 0x0300; break; default: - printf("Bad SWSTYLE=0x%02x\n", val & 0xff); + qemu_log_mask(LOG_GUEST_ERROR, "pcnet: Bad SWSTYLE=0x%02x\n", + val & 0xff); val = 0x0200; break; } diff --git a/hw/openrisc/cputimer.c b/hw/openrisc/cputimer.c index 850f88761c..fe95efc41c 100644 --- a/hw/openrisc/cputimer.c +++ b/hw/openrisc/cputimer.c @@ -7,7 +7,7 @@ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/hw/openrisc/openrisc_sim.c b/hw/openrisc/openrisc_sim.c index 7d3b734d24..0a906d815e 100644 --- a/hw/openrisc/openrisc_sim.c +++ b/hw/openrisc/openrisc_sim.c @@ -7,7 +7,7 @@ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/hw/openrisc/pic_cpu.c b/hw/openrisc/pic_cpu.c index 569b443f59..2f53cfc82e 100644 --- a/hw/openrisc/pic_cpu.c +++ b/hw/openrisc/pic_cpu.c @@ -7,7 +7,7 @@ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/hw/rdma/rdma_backend.c b/hw/rdma/rdma_backend.c index d1660b6474..cf34874e9d 100644 --- a/hw/rdma/rdma_backend.c +++ b/hw/rdma/rdma_backend.c @@ -40,6 +40,7 @@ typedef struct BackendCtx { void *up_ctx; struct ibv_sge sge; /* Used to save MAD recv buffer */ RdmaBackendQP *backend_qp; /* To maintain recv buffers */ + RdmaBackendSRQ *backend_srq; } BackendCtx; struct backend_umad { @@ -99,6 +100,7 @@ static int rdma_poll_cq(RdmaDeviceResources *rdma_dev_res, struct ibv_cq *ibcq) int i, ne, total_ne = 0; BackendCtx *bctx; struct ibv_wc wc[2]; + RdmaProtectedGSList *cqe_ctx_list; qemu_mutex_lock(&rdma_dev_res->lock); do { @@ -116,8 +118,13 @@ static int rdma_poll_cq(RdmaDeviceResources *rdma_dev_res, struct ibv_cq *ibcq) comp_handler(bctx->up_ctx, &wc[i]); - rdma_protected_gslist_remove_int32(&bctx->backend_qp->cqe_ctx_list, - wc[i].wr_id); + if (bctx->backend_qp) { + cqe_ctx_list = &bctx->backend_qp->cqe_ctx_list; + } else { + cqe_ctx_list = &bctx->backend_srq->cqe_ctx_list; + } + + rdma_protected_gslist_remove_int32(cqe_ctx_list, wc[i].wr_id); rdma_rm_dealloc_cqe_ctx(rdma_dev_res, wc[i].wr_id); g_free(bctx); } @@ -662,6 +669,60 @@ err_free_bctx: g_free(bctx); } +void rdma_backend_post_srq_recv(RdmaBackendDev *backend_dev, + RdmaBackendSRQ *srq, struct ibv_sge *sge, + uint32_t num_sge, void *ctx) +{ + BackendCtx *bctx; + struct ibv_sge new_sge[MAX_SGE]; + uint32_t bctx_id; + int rc; + struct ibv_recv_wr wr = {}, *bad_wr; + + bctx = g_malloc0(sizeof(*bctx)); + bctx->up_ctx = ctx; + bctx->backend_srq = srq; + + rc = rdma_rm_alloc_cqe_ctx(backend_dev->rdma_dev_res, &bctx_id, bctx); + if (unlikely(rc)) { + complete_work(IBV_WC_GENERAL_ERR, VENDOR_ERR_NOMEM, ctx); + goto err_free_bctx; + } + + rdma_protected_gslist_append_int32(&srq->cqe_ctx_list, bctx_id); + + rc = build_host_sge_array(backend_dev->rdma_dev_res, new_sge, sge, num_sge, + &backend_dev->rdma_dev_res->stats.rx_bufs_len); + if (rc) { + complete_work(IBV_WC_GENERAL_ERR, rc, ctx); + goto err_dealloc_cqe_ctx; + } + + wr.num_sge = num_sge; + wr.sg_list = new_sge; + wr.wr_id = bctx_id; + rc = ibv_post_srq_recv(srq->ibsrq, &wr, &bad_wr); + if (rc) { + rdma_error_report("ibv_post_srq_recv fail, srqn=0x%x, rc=%d, errno=%d", + srq->ibsrq->handle, rc, errno); + complete_work(IBV_WC_GENERAL_ERR, VENDOR_ERR_FAIL_BACKEND, ctx); + goto err_dealloc_cqe_ctx; + } + + atomic_inc(&backend_dev->rdma_dev_res->stats.missing_cqe); + backend_dev->rdma_dev_res->stats.rx_bufs++; + backend_dev->rdma_dev_res->stats.rx_srq++; + + return; + +err_dealloc_cqe_ctx: + backend_dev->rdma_dev_res->stats.rx_bufs_err++; + rdma_rm_dealloc_cqe_ctx(backend_dev->rdma_dev_res, bctx_id); + +err_free_bctx: + g_free(bctx); +} + int rdma_backend_create_pd(RdmaBackendDev *backend_dev, RdmaBackendPD *pd) { pd->ibpd = ibv_alloc_pd(backend_dev->context); @@ -733,9 +794,9 @@ void rdma_backend_destroy_cq(RdmaBackendCQ *cq) int rdma_backend_create_qp(RdmaBackendQP *qp, uint8_t qp_type, RdmaBackendPD *pd, RdmaBackendCQ *scq, - RdmaBackendCQ *rcq, uint32_t max_send_wr, - uint32_t max_recv_wr, uint32_t max_send_sge, - uint32_t max_recv_sge) + RdmaBackendCQ *rcq, RdmaBackendSRQ *srq, + uint32_t max_send_wr, uint32_t max_recv_wr, + uint32_t max_send_sge, uint32_t max_recv_sge) { struct ibv_qp_init_attr attr = {}; @@ -763,6 +824,9 @@ int rdma_backend_create_qp(RdmaBackendQP *qp, uint8_t qp_type, attr.cap.max_recv_wr = max_recv_wr; attr.cap.max_send_sge = max_send_sge; attr.cap.max_recv_sge = max_recv_sge; + if (srq) { + attr.srq = srq->ibsrq; + } qp->ibqp = ibv_create_qp(pd->ibpd, &attr); if (!qp->ibqp) { @@ -938,6 +1002,55 @@ void rdma_backend_destroy_qp(RdmaBackendQP *qp, RdmaDeviceResources *dev_res) rdma_protected_gslist_destroy(&qp->cqe_ctx_list); } +int rdma_backend_create_srq(RdmaBackendSRQ *srq, RdmaBackendPD *pd, + uint32_t max_wr, uint32_t max_sge, + uint32_t srq_limit) +{ + struct ibv_srq_init_attr srq_init_attr = {}; + + srq_init_attr.attr.max_wr = max_wr; + srq_init_attr.attr.max_sge = max_sge; + srq_init_attr.attr.srq_limit = srq_limit; + + srq->ibsrq = ibv_create_srq(pd->ibpd, &srq_init_attr); + if (!srq->ibsrq) { + rdma_error_report("ibv_create_srq failed, errno=%d", errno); + return -EIO; + } + + rdma_protected_gslist_init(&srq->cqe_ctx_list); + + return 0; +} + +int rdma_backend_query_srq(RdmaBackendSRQ *srq, struct ibv_srq_attr *srq_attr) +{ + if (!srq->ibsrq) { + return -EINVAL; + } + + return ibv_query_srq(srq->ibsrq, srq_attr); +} + +int rdma_backend_modify_srq(RdmaBackendSRQ *srq, struct ibv_srq_attr *srq_attr, + int srq_attr_mask) +{ + if (!srq->ibsrq) { + return -EINVAL; + } + + return ibv_modify_srq(srq->ibsrq, srq_attr, srq_attr_mask); +} + +void rdma_backend_destroy_srq(RdmaBackendSRQ *srq, RdmaDeviceResources *dev_res) +{ + if (srq->ibsrq) { + ibv_destroy_srq(srq->ibsrq); + } + g_slist_foreach(srq->cqe_ctx_list.list, free_cqe_ctx, dev_res); + rdma_protected_gslist_destroy(&srq->cqe_ctx_list); +} + #define CHK_ATTR(req, dev, member, fmt) ({ \ trace_rdma_check_dev_attr(#member, dev.member, req->member); \ if (req->member > dev.member) { \ @@ -960,6 +1073,7 @@ static int init_device_caps(RdmaBackendDev *backend_dev, } dev_attr->max_sge = MAX_SGE; + dev_attr->max_srq_sge = MAX_SGE; CHK_ATTR(dev_attr, bk_dev_attr, max_mr_size, "%" PRId64); CHK_ATTR(dev_attr, bk_dev_attr, max_qp, "%d"); @@ -970,6 +1084,7 @@ static int init_device_caps(RdmaBackendDev *backend_dev, CHK_ATTR(dev_attr, bk_dev_attr, max_qp_rd_atom, "%d"); CHK_ATTR(dev_attr, bk_dev_attr, max_qp_init_rd_atom, "%d"); CHK_ATTR(dev_attr, bk_dev_attr, max_ah, "%d"); + CHK_ATTR(dev_attr, bk_dev_attr, max_srq, "%d"); return 0; } diff --git a/hw/rdma/rdma_backend.h b/hw/rdma/rdma_backend.h index 38056d97c7..7c1a19a2b5 100644 --- a/hw/rdma/rdma_backend.h +++ b/hw/rdma/rdma_backend.h @@ -89,9 +89,9 @@ void rdma_backend_poll_cq(RdmaDeviceResources *rdma_dev_res, RdmaBackendCQ *cq); int rdma_backend_create_qp(RdmaBackendQP *qp, uint8_t qp_type, RdmaBackendPD *pd, RdmaBackendCQ *scq, - RdmaBackendCQ *rcq, uint32_t max_send_wr, - uint32_t max_recv_wr, uint32_t max_send_sge, - uint32_t max_recv_sge); + RdmaBackendCQ *rcq, RdmaBackendSRQ *srq, + uint32_t max_send_wr, uint32_t max_recv_wr, + uint32_t max_send_sge, uint32_t max_recv_sge); int rdma_backend_qp_state_init(RdmaBackendDev *backend_dev, RdmaBackendQP *qp, uint8_t qp_type, uint32_t qkey); int rdma_backend_qp_state_rtr(RdmaBackendDev *backend_dev, RdmaBackendQP *qp, @@ -114,4 +114,16 @@ void rdma_backend_post_recv(RdmaBackendDev *backend_dev, RdmaBackendQP *qp, uint8_t qp_type, struct ibv_sge *sge, uint32_t num_sge, void *ctx); +int rdma_backend_create_srq(RdmaBackendSRQ *srq, RdmaBackendPD *pd, + uint32_t max_wr, uint32_t max_sge, + uint32_t srq_limit); +int rdma_backend_query_srq(RdmaBackendSRQ *srq, struct ibv_srq_attr *srq_attr); +int rdma_backend_modify_srq(RdmaBackendSRQ *srq, struct ibv_srq_attr *srq_attr, + int srq_attr_mask); +void rdma_backend_destroy_srq(RdmaBackendSRQ *srq, + RdmaDeviceResources *dev_res); +void rdma_backend_post_srq_recv(RdmaBackendDev *backend_dev, + RdmaBackendSRQ *srq, struct ibv_sge *sge, + uint32_t num_sge, void *ctx); + #endif diff --git a/hw/rdma/rdma_backend_defs.h b/hw/rdma/rdma_backend_defs.h index 817153dc8c..0b55be3503 100644 --- a/hw/rdma/rdma_backend_defs.h +++ b/hw/rdma/rdma_backend_defs.h @@ -68,4 +68,9 @@ typedef struct RdmaBackendQP { RdmaProtectedGSList cqe_ctx_list; } RdmaBackendQP; +typedef struct RdmaBackendSRQ { + struct ibv_srq *ibsrq; + RdmaProtectedGSList cqe_ctx_list; +} RdmaBackendSRQ; + #endif diff --git a/hw/rdma/rdma_rm.c b/hw/rdma/rdma_rm.c index bac3b2f4a6..1927f85472 100644 --- a/hw/rdma/rdma_rm.c +++ b/hw/rdma/rdma_rm.c @@ -37,6 +37,8 @@ void rdma_dump_device_counters(Monitor *mon, RdmaDeviceResources *dev_res) dev_res->stats.tx_err); monitor_printf(mon, "\trx_bufs : %" PRId64 "\n", dev_res->stats.rx_bufs); + monitor_printf(mon, "\trx_srq : %" PRId64 "\n", + dev_res->stats.rx_srq); monitor_printf(mon, "\trx_bufs_len : %" PRId64 "\n", dev_res->stats.rx_bufs_len); monitor_printf(mon, "\trx_bufs_err : %" PRId64 "\n", @@ -384,12 +386,14 @@ int rdma_rm_alloc_qp(RdmaDeviceResources *dev_res, uint32_t pd_handle, uint8_t qp_type, uint32_t max_send_wr, uint32_t max_send_sge, uint32_t send_cq_handle, uint32_t max_recv_wr, uint32_t max_recv_sge, - uint32_t recv_cq_handle, void *opaque, uint32_t *qpn) + uint32_t recv_cq_handle, void *opaque, uint32_t *qpn, + uint8_t is_srq, uint32_t srq_handle) { int rc; RdmaRmQP *qp; RdmaRmCQ *scq, *rcq; RdmaRmPD *pd; + RdmaRmSRQ *srq = NULL; uint32_t rm_qpn; pd = rdma_rm_get_pd(dev_res, pd_handle); @@ -406,6 +410,16 @@ int rdma_rm_alloc_qp(RdmaDeviceResources *dev_res, uint32_t pd_handle, return -EINVAL; } + if (is_srq) { + srq = rdma_rm_get_srq(dev_res, srq_handle); + if (!srq) { + rdma_error_report("Invalid srqn %d", srq_handle); + return -EINVAL; + } + + srq->recv_cq_handle = recv_cq_handle; + } + if (qp_type == IBV_QPT_GSI) { scq->notify = CNT_SET; rcq->notify = CNT_SET; @@ -422,10 +436,14 @@ int rdma_rm_alloc_qp(RdmaDeviceResources *dev_res, uint32_t pd_handle, qp->send_cq_handle = send_cq_handle; qp->recv_cq_handle = recv_cq_handle; qp->opaque = opaque; + qp->is_srq = is_srq; rc = rdma_backend_create_qp(&qp->backend_qp, qp_type, &pd->backend_pd, - &scq->backend_cq, &rcq->backend_cq, max_send_wr, - max_recv_wr, max_send_sge, max_recv_sge); + &scq->backend_cq, &rcq->backend_cq, + is_srq ? &srq->backend_srq : NULL, + max_send_wr, max_recv_wr, max_send_sge, + max_recv_sge); + if (rc) { rc = -EIO; goto out_dealloc_qp; @@ -542,6 +560,96 @@ void rdma_rm_dealloc_qp(RdmaDeviceResources *dev_res, uint32_t qp_handle) rdma_res_tbl_dealloc(&dev_res->qp_tbl, qp->qpn); } +RdmaRmSRQ *rdma_rm_get_srq(RdmaDeviceResources *dev_res, uint32_t srq_handle) +{ + return rdma_res_tbl_get(&dev_res->srq_tbl, srq_handle); +} + +int rdma_rm_alloc_srq(RdmaDeviceResources *dev_res, uint32_t pd_handle, + uint32_t max_wr, uint32_t max_sge, uint32_t srq_limit, + uint32_t *srq_handle, void *opaque) +{ + RdmaRmSRQ *srq; + RdmaRmPD *pd; + int rc; + + pd = rdma_rm_get_pd(dev_res, pd_handle); + if (!pd) { + return -EINVAL; + } + + srq = rdma_res_tbl_alloc(&dev_res->srq_tbl, srq_handle); + if (!srq) { + return -ENOMEM; + } + + rc = rdma_backend_create_srq(&srq->backend_srq, &pd->backend_pd, + max_wr, max_sge, srq_limit); + if (rc) { + rc = -EIO; + goto out_dealloc_srq; + } + + srq->opaque = opaque; + + return 0; + +out_dealloc_srq: + rdma_res_tbl_dealloc(&dev_res->srq_tbl, *srq_handle); + + return rc; +} + +int rdma_rm_query_srq(RdmaDeviceResources *dev_res, uint32_t srq_handle, + struct ibv_srq_attr *srq_attr) +{ + RdmaRmSRQ *srq; + + srq = rdma_rm_get_srq(dev_res, srq_handle); + if (!srq) { + return -EINVAL; + } + + return rdma_backend_query_srq(&srq->backend_srq, srq_attr); +} + +int rdma_rm_modify_srq(RdmaDeviceResources *dev_res, uint32_t srq_handle, + struct ibv_srq_attr *srq_attr, int srq_attr_mask) +{ + RdmaRmSRQ *srq; + + srq = rdma_rm_get_srq(dev_res, srq_handle); + if (!srq) { + return -EINVAL; + } + + if ((srq_attr_mask & IBV_SRQ_LIMIT) && + (srq_attr->srq_limit == 0)) { + return -EINVAL; + } + + if ((srq_attr_mask & IBV_SRQ_MAX_WR) && + (srq_attr->max_wr == 0)) { + return -EINVAL; + } + + return rdma_backend_modify_srq(&srq->backend_srq, srq_attr, + srq_attr_mask); +} + +void rdma_rm_dealloc_srq(RdmaDeviceResources *dev_res, uint32_t srq_handle) +{ + RdmaRmSRQ *srq; + + srq = rdma_rm_get_srq(dev_res, srq_handle); + if (!srq) { + return; + } + + rdma_backend_destroy_srq(&srq->backend_srq, dev_res); + rdma_res_tbl_dealloc(&dev_res->srq_tbl, srq_handle); +} + void *rdma_rm_get_cqe_ctx(RdmaDeviceResources *dev_res, uint32_t cqe_ctx_id) { void **cqe_ctx; @@ -671,6 +779,8 @@ int rdma_rm_init(RdmaDeviceResources *dev_res, struct ibv_device_attr *dev_attr) res_tbl_init("CQE_CTX", &dev_res->cqe_ctx_tbl, dev_attr->max_qp * dev_attr->max_qp_wr, sizeof(void *)); res_tbl_init("UC", &dev_res->uc_tbl, MAX_UCS, sizeof(RdmaRmUC)); + res_tbl_init("SRQ", &dev_res->srq_tbl, dev_attr->max_srq, + sizeof(RdmaRmSRQ)); init_ports(dev_res); @@ -689,6 +799,7 @@ void rdma_rm_fini(RdmaDeviceResources *dev_res, RdmaBackendDev *backend_dev, fini_ports(dev_res, backend_dev, ifname); + res_tbl_free(&dev_res->srq_tbl); res_tbl_free(&dev_res->uc_tbl); res_tbl_free(&dev_res->cqe_ctx_tbl); res_tbl_free(&dev_res->qp_tbl); diff --git a/hw/rdma/rdma_rm.h b/hw/rdma/rdma_rm.h index 4f03f9b8c5..e8639909cd 100644 --- a/hw/rdma/rdma_rm.h +++ b/hw/rdma/rdma_rm.h @@ -53,7 +53,8 @@ int rdma_rm_alloc_qp(RdmaDeviceResources *dev_res, uint32_t pd_handle, uint8_t qp_type, uint32_t max_send_wr, uint32_t max_send_sge, uint32_t send_cq_handle, uint32_t max_recv_wr, uint32_t max_recv_sge, - uint32_t recv_cq_handle, void *opaque, uint32_t *qpn); + uint32_t recv_cq_handle, void *opaque, uint32_t *qpn, + uint8_t is_srq, uint32_t srq_handle); RdmaRmQP *rdma_rm_get_qp(RdmaDeviceResources *dev_res, uint32_t qpn); int rdma_rm_modify_qp(RdmaDeviceResources *dev_res, RdmaBackendDev *backend_dev, uint32_t qp_handle, uint32_t attr_mask, uint8_t sgid_idx, @@ -65,6 +66,16 @@ int rdma_rm_query_qp(RdmaDeviceResources *dev_res, RdmaBackendDev *backend_dev, int attr_mask, struct ibv_qp_init_attr *init_attr); void rdma_rm_dealloc_qp(RdmaDeviceResources *dev_res, uint32_t qp_handle); +RdmaRmSRQ *rdma_rm_get_srq(RdmaDeviceResources *dev_res, uint32_t srq_handle); +int rdma_rm_alloc_srq(RdmaDeviceResources *dev_res, uint32_t pd_handle, + uint32_t max_wr, uint32_t max_sge, uint32_t srq_limit, + uint32_t *srq_handle, void *opaque); +int rdma_rm_query_srq(RdmaDeviceResources *dev_res, uint32_t srq_handle, + struct ibv_srq_attr *srq_attr); +int rdma_rm_modify_srq(RdmaDeviceResources *dev_res, uint32_t srq_handle, + struct ibv_srq_attr *srq_attr, int srq_attr_mask); +void rdma_rm_dealloc_srq(RdmaDeviceResources *dev_res, uint32_t srq_handle); + int rdma_rm_alloc_cqe_ctx(RdmaDeviceResources *dev_res, uint32_t *cqe_ctx_id, void *ctx); void *rdma_rm_get_cqe_ctx(RdmaDeviceResources *dev_res, uint32_t cqe_ctx_id); diff --git a/hw/rdma/rdma_rm_defs.h b/hw/rdma/rdma_rm_defs.h index c200d311de..534f2f74d3 100644 --- a/hw/rdma/rdma_rm_defs.h +++ b/hw/rdma/rdma_rm_defs.h @@ -33,6 +33,7 @@ #define MAX_QP_RD_ATOM 16 #define MAX_QP_INIT_RD_ATOM 16 #define MAX_AH 64 +#define MAX_SRQ 512 #define MAX_RM_TBL_NAME 16 #define MAX_CONSEQ_EMPTY_POLL_CQ 4096 /* considered as error above this */ @@ -87,8 +88,15 @@ typedef struct RdmaRmQP { uint32_t send_cq_handle; uint32_t recv_cq_handle; enum ibv_qp_state qp_state; + uint8_t is_srq; } RdmaRmQP; +typedef struct RdmaRmSRQ { + RdmaBackendSRQ backend_srq; + uint32_t recv_cq_handle; + void *opaque; +} RdmaRmSRQ; + typedef struct RdmaRmGid { union ibv_gid gid; int backend_gid_index; @@ -106,6 +114,7 @@ typedef struct RdmaRmStats { uint64_t rx_bufs; uint64_t rx_bufs_len; uint64_t rx_bufs_err; + uint64_t rx_srq; uint64_t completions; uint64_t mad_tx; uint64_t mad_tx_err; @@ -128,6 +137,7 @@ struct RdmaDeviceResources { RdmaRmResTbl qp_tbl; RdmaRmResTbl cq_tbl; RdmaRmResTbl cqe_ctx_tbl; + RdmaRmResTbl srq_tbl; GHashTable *qp_hash; /* Keeps mapping between real and emulated */ QemuMutex lock; RdmaRmStats stats; diff --git a/hw/rdma/vmw/pvrdma_cmd.c b/hw/rdma/vmw/pvrdma_cmd.c index 4afcd2037d..8d70c0d23d 100644 --- a/hw/rdma/vmw/pvrdma_cmd.c +++ b/hw/rdma/vmw/pvrdma_cmd.c @@ -357,7 +357,7 @@ static int destroy_cq(PVRDMADev *dev, union pvrdma_cmd_req *req, static int create_qp_rings(PCIDevice *pci_dev, uint64_t pdir_dma, PvrdmaRing **rings, uint32_t scqe, uint32_t smax_sge, uint32_t spages, uint32_t rcqe, uint32_t rmax_sge, - uint32_t rpages) + uint32_t rpages, uint8_t is_srq) { uint64_t *dir = NULL, *tbl = NULL; PvrdmaRing *sr, *rr; @@ -365,9 +365,14 @@ static int create_qp_rings(PCIDevice *pci_dev, uint64_t pdir_dma, char ring_name[MAX_RING_NAME_SZ]; uint32_t wqe_sz; - if (!spages || spages > PVRDMA_MAX_FAST_REG_PAGES - || !rpages || rpages > PVRDMA_MAX_FAST_REG_PAGES) { - rdma_error_report("Got invalid page count for QP ring: %d, %d", spages, + if (!spages || spages > PVRDMA_MAX_FAST_REG_PAGES) { + rdma_error_report("Got invalid send page count for QP ring: %d", + spages); + return rc; + } + + if (!is_srq && (!rpages || rpages > PVRDMA_MAX_FAST_REG_PAGES)) { + rdma_error_report("Got invalid recv page count for QP ring: %d", rpages); return rc; } @@ -384,8 +389,12 @@ static int create_qp_rings(PCIDevice *pci_dev, uint64_t pdir_dma, goto out; } - sr = g_malloc(2 * sizeof(*rr)); - rr = &sr[1]; + if (!is_srq) { + sr = g_malloc(2 * sizeof(*rr)); + rr = &sr[1]; + } else { + sr = g_malloc(sizeof(*sr)); + } *rings = sr; @@ -407,15 +416,18 @@ static int create_qp_rings(PCIDevice *pci_dev, uint64_t pdir_dma, goto out_unmap_ring_state; } - /* Create recv ring */ - rr->ring_state = &sr->ring_state[1]; - wqe_sz = pow2ceil(sizeof(struct pvrdma_rq_wqe_hdr) + - sizeof(struct pvrdma_sge) * rmax_sge - 1); - sprintf(ring_name, "qp_rring_%" PRIx64, pdir_dma); - rc = pvrdma_ring_init(rr, ring_name, pci_dev, rr->ring_state, - rcqe, wqe_sz, (dma_addr_t *)&tbl[1 + spages], rpages); - if (rc) { - goto out_free_sr; + if (!is_srq) { + /* Create recv ring */ + rr->ring_state = &sr->ring_state[1]; + wqe_sz = pow2ceil(sizeof(struct pvrdma_rq_wqe_hdr) + + sizeof(struct pvrdma_sge) * rmax_sge - 1); + sprintf(ring_name, "qp_rring_%" PRIx64, pdir_dma); + rc = pvrdma_ring_init(rr, ring_name, pci_dev, rr->ring_state, + rcqe, wqe_sz, (dma_addr_t *)&tbl[1 + spages], + rpages); + if (rc) { + goto out_free_sr; + } } goto out; @@ -436,10 +448,12 @@ out: return rc; } -static void destroy_qp_rings(PvrdmaRing *ring) +static void destroy_qp_rings(PvrdmaRing *ring, uint8_t is_srq) { pvrdma_ring_free(&ring[0]); - pvrdma_ring_free(&ring[1]); + if (!is_srq) { + pvrdma_ring_free(&ring[1]); + } rdma_pci_dma_unmap(ring->dev, ring->ring_state, TARGET_PAGE_SIZE); g_free(ring); @@ -458,7 +472,7 @@ static int create_qp(PVRDMADev *dev, union pvrdma_cmd_req *req, rc = create_qp_rings(PCI_DEVICE(dev), cmd->pdir_dma, &rings, cmd->max_send_wr, cmd->max_send_sge, cmd->send_chunks, cmd->max_recv_wr, cmd->max_recv_sge, - cmd->total_chunks - cmd->send_chunks - 1); + cmd->total_chunks - cmd->send_chunks - 1, cmd->is_srq); if (rc) { return rc; } @@ -467,9 +481,9 @@ static int create_qp(PVRDMADev *dev, union pvrdma_cmd_req *req, cmd->max_send_wr, cmd->max_send_sge, cmd->send_cq_handle, cmd->max_recv_wr, cmd->max_recv_sge, cmd->recv_cq_handle, rings, - &resp->qpn); + &resp->qpn, cmd->is_srq, cmd->srq_handle); if (rc) { - destroy_qp_rings(rings); + destroy_qp_rings(rings, cmd->is_srq); return rc; } @@ -531,10 +545,9 @@ static int destroy_qp(PVRDMADev *dev, union pvrdma_cmd_req *req, return -EINVAL; } - rdma_rm_dealloc_qp(&dev->rdma_dev_res, cmd->qp_handle); - ring = (PvrdmaRing *)qp->opaque; - destroy_qp_rings(ring); + destroy_qp_rings(ring, qp->is_srq); + rdma_rm_dealloc_qp(&dev->rdma_dev_res, cmd->qp_handle); return 0; } @@ -596,6 +609,149 @@ static int destroy_uc(PVRDMADev *dev, union pvrdma_cmd_req *req, return 0; } +static int create_srq_ring(PCIDevice *pci_dev, PvrdmaRing **ring, + uint64_t pdir_dma, uint32_t max_wr, + uint32_t max_sge, uint32_t nchunks) +{ + uint64_t *dir = NULL, *tbl = NULL; + PvrdmaRing *r; + int rc = -EINVAL; + char ring_name[MAX_RING_NAME_SZ]; + uint32_t wqe_sz; + + if (!nchunks || nchunks > PVRDMA_MAX_FAST_REG_PAGES) { + rdma_error_report("Got invalid page count for SRQ ring: %d", + nchunks); + return rc; + } + + dir = rdma_pci_dma_map(pci_dev, pdir_dma, TARGET_PAGE_SIZE); + if (!dir) { + rdma_error_report("Failed to map to SRQ page directory"); + goto out; + } + + tbl = rdma_pci_dma_map(pci_dev, dir[0], TARGET_PAGE_SIZE); + if (!tbl) { + rdma_error_report("Failed to map to SRQ page table"); + goto out; + } + + r = g_malloc(sizeof(*r)); + *ring = r; + + r->ring_state = (struct pvrdma_ring *) + rdma_pci_dma_map(pci_dev, tbl[0], TARGET_PAGE_SIZE); + if (!r->ring_state) { + rdma_error_report("Failed to map tp SRQ ring state"); + goto out_free_ring_mem; + } + + wqe_sz = pow2ceil(sizeof(struct pvrdma_rq_wqe_hdr) + + sizeof(struct pvrdma_sge) * max_sge - 1); + sprintf(ring_name, "srq_ring_%" PRIx64, pdir_dma); + rc = pvrdma_ring_init(r, ring_name, pci_dev, &r->ring_state[1], max_wr, + wqe_sz, (dma_addr_t *)&tbl[1], nchunks - 1); + if (rc) { + goto out_unmap_ring_state; + } + + goto out; + +out_unmap_ring_state: + rdma_pci_dma_unmap(pci_dev, r->ring_state, TARGET_PAGE_SIZE); + +out_free_ring_mem: + g_free(r); + +out: + rdma_pci_dma_unmap(pci_dev, tbl, TARGET_PAGE_SIZE); + rdma_pci_dma_unmap(pci_dev, dir, TARGET_PAGE_SIZE); + + return rc; +} + +static void destroy_srq_ring(PvrdmaRing *ring) +{ + pvrdma_ring_free(ring); + rdma_pci_dma_unmap(ring->dev, ring->ring_state, TARGET_PAGE_SIZE); + g_free(ring); +} + +static int create_srq(PVRDMADev *dev, union pvrdma_cmd_req *req, + union pvrdma_cmd_resp *rsp) +{ + struct pvrdma_cmd_create_srq *cmd = &req->create_srq; + struct pvrdma_cmd_create_srq_resp *resp = &rsp->create_srq_resp; + PvrdmaRing *ring = NULL; + int rc; + + memset(resp, 0, sizeof(*resp)); + + rc = create_srq_ring(PCI_DEVICE(dev), &ring, cmd->pdir_dma, + cmd->attrs.max_wr, cmd->attrs.max_sge, + cmd->nchunks); + if (rc) { + return rc; + } + + rc = rdma_rm_alloc_srq(&dev->rdma_dev_res, cmd->pd_handle, + cmd->attrs.max_wr, cmd->attrs.max_sge, + cmd->attrs.srq_limit, &resp->srqn, ring); + if (rc) { + destroy_srq_ring(ring); + return rc; + } + + return 0; +} + +static int query_srq(PVRDMADev *dev, union pvrdma_cmd_req *req, + union pvrdma_cmd_resp *rsp) +{ + struct pvrdma_cmd_query_srq *cmd = &req->query_srq; + struct pvrdma_cmd_query_srq_resp *resp = &rsp->query_srq_resp; + + memset(resp, 0, sizeof(*resp)); + + return rdma_rm_query_srq(&dev->rdma_dev_res, cmd->srq_handle, + (struct ibv_srq_attr *)&resp->attrs); +} + +static int modify_srq(PVRDMADev *dev, union pvrdma_cmd_req *req, + union pvrdma_cmd_resp *rsp) +{ + struct pvrdma_cmd_modify_srq *cmd = &req->modify_srq; + + /* Only support SRQ limit */ + if (!(cmd->attr_mask & IBV_SRQ_LIMIT) || + (cmd->attr_mask & IBV_SRQ_MAX_WR)) + return -EINVAL; + + return rdma_rm_modify_srq(&dev->rdma_dev_res, cmd->srq_handle, + (struct ibv_srq_attr *)&cmd->attrs, + cmd->attr_mask); +} + +static int destroy_srq(PVRDMADev *dev, union pvrdma_cmd_req *req, + union pvrdma_cmd_resp *rsp) +{ + struct pvrdma_cmd_destroy_srq *cmd = &req->destroy_srq; + RdmaRmSRQ *srq; + PvrdmaRing *ring; + + srq = rdma_rm_get_srq(&dev->rdma_dev_res, cmd->srq_handle); + if (!srq) { + return -EINVAL; + } + + ring = (PvrdmaRing *)srq->opaque; + destroy_srq_ring(ring); + rdma_rm_dealloc_srq(&dev->rdma_dev_res, cmd->srq_handle); + + return 0; +} + struct cmd_handler { uint32_t cmd; uint32_t ack; @@ -621,6 +777,10 @@ static struct cmd_handler cmd_handlers[] = { {PVRDMA_CMD_DESTROY_UC, PVRDMA_CMD_DESTROY_UC_RESP_NOOP, destroy_uc}, {PVRDMA_CMD_CREATE_BIND, PVRDMA_CMD_CREATE_BIND_RESP_NOOP, create_bind}, {PVRDMA_CMD_DESTROY_BIND, PVRDMA_CMD_DESTROY_BIND_RESP_NOOP, destroy_bind}, + {PVRDMA_CMD_CREATE_SRQ, PVRDMA_CMD_CREATE_SRQ_RESP, create_srq}, + {PVRDMA_CMD_QUERY_SRQ, PVRDMA_CMD_QUERY_SRQ_RESP, query_srq}, + {PVRDMA_CMD_MODIFY_SRQ, PVRDMA_CMD_MODIFY_SRQ_RESP, modify_srq}, + {PVRDMA_CMD_DESTROY_SRQ, PVRDMA_CMD_DESTROY_SRQ_RESP, destroy_srq}, }; int pvrdma_exec_cmd(PVRDMADev *dev) diff --git a/hw/rdma/vmw/pvrdma_main.c b/hw/rdma/vmw/pvrdma_main.c index 0b46561bad..769f7990f8 100644 --- a/hw/rdma/vmw/pvrdma_main.c +++ b/hw/rdma/vmw/pvrdma_main.c @@ -53,6 +53,7 @@ static Property pvrdma_dev_properties[] = { DEFINE_PROP_INT32("dev-caps-max-qp-init-rd-atom", PVRDMADev, dev_attr.max_qp_init_rd_atom, MAX_QP_INIT_RD_ATOM), DEFINE_PROP_INT32("dev-caps-max-ah", PVRDMADev, dev_attr.max_ah, MAX_AH), + DEFINE_PROP_INT32("dev-caps-max-srq", PVRDMADev, dev_attr.max_srq, MAX_SRQ), DEFINE_PROP_CHR("mad-chardev", PVRDMADev, mad_chr), DEFINE_PROP_END_OF_LIST(), }; @@ -261,6 +262,9 @@ static void init_dsr_dev_caps(PVRDMADev *dev) dsr->caps.max_mr = dev->dev_attr.max_mr; dsr->caps.max_pd = dev->dev_attr.max_pd; dsr->caps.max_ah = dev->dev_attr.max_ah; + dsr->caps.max_srq = dev->dev_attr.max_srq; + dsr->caps.max_srq_wr = dev->dev_attr.max_srq_wr; + dsr->caps.max_srq_sge = dev->dev_attr.max_srq_sge; dsr->caps.gid_tbl_len = MAX_GIDS; dsr->caps.sys_image_guid = 0; dsr->caps.node_guid = dev->node_guid; @@ -485,6 +489,13 @@ static void pvrdma_uar_write(void *opaque, hwaddr addr, uint64_t val, pvrdma_cq_poll(&dev->rdma_dev_res, val & PVRDMA_UAR_HANDLE_MASK); } break; + case PVRDMA_UAR_SRQ_OFFSET: + if (val & PVRDMA_UAR_SRQ_RECV) { + trace_pvrdma_uar_write(addr, val, "QP", "SRQ", + val & PVRDMA_UAR_HANDLE_MASK, 0); + pvrdma_srq_recv(dev, val & PVRDMA_UAR_HANDLE_MASK); + } + break; default: rdma_error_report("Unsupported command, addr=0x%"PRIx64", val=0x%"PRIx64, addr, val); @@ -554,6 +565,11 @@ static void init_dev_caps(PVRDMADev *dev) dev->dev_attr.max_cqe = pg_tbl_bytes / sizeof(struct pvrdma_cqe) - TARGET_PAGE_SIZE; /* First page is ring state */ + + dev->dev_attr.max_srq_wr = pg_tbl_bytes / + ((sizeof(struct pvrdma_rq_wqe_hdr) + + sizeof(struct pvrdma_sge)) * + dev->dev_attr.max_sge) - TARGET_PAGE_SIZE; } static int pvrdma_check_ram_shared(Object *obj, void *opaque) diff --git a/hw/rdma/vmw/pvrdma_qp_ops.c b/hw/rdma/vmw/pvrdma_qp_ops.c index 5b9786efbe..bd6db858de 100644 --- a/hw/rdma/vmw/pvrdma_qp_ops.c +++ b/hw/rdma/vmw/pvrdma_qp_ops.c @@ -70,7 +70,7 @@ static int pvrdma_post_cqe(PVRDMADev *dev, uint32_t cq_handle, memset(cqe1, 0, sizeof(*cqe1)); cqe1->wr_id = cqe->wr_id; - cqe1->qp = cqe->qp; + cqe1->qp = cqe->qp ? cqe->qp : wc->qp_num; cqe1->opcode = cqe->opcode; cqe1->status = wc->status; cqe1->byte_len = wc->byte_len; @@ -241,6 +241,50 @@ void pvrdma_qp_recv(PVRDMADev *dev, uint32_t qp_handle) } } +void pvrdma_srq_recv(PVRDMADev *dev, uint32_t srq_handle) +{ + RdmaRmSRQ *srq; + PvrdmaRqWqe *wqe; + PvrdmaRing *ring; + + srq = rdma_rm_get_srq(&dev->rdma_dev_res, srq_handle); + if (unlikely(!srq)) { + return; + } + + ring = (PvrdmaRing *)srq->opaque; + + wqe = (struct PvrdmaRqWqe *)pvrdma_ring_next_elem_read(ring); + while (wqe) { + CompHandlerCtx *comp_ctx; + + /* Prepare CQE */ + comp_ctx = g_malloc(sizeof(CompHandlerCtx)); + comp_ctx->dev = dev; + comp_ctx->cq_handle = srq->recv_cq_handle; + comp_ctx->cqe.wr_id = wqe->hdr.wr_id; + comp_ctx->cqe.qp = 0; + comp_ctx->cqe.opcode = IBV_WC_RECV; + + if (wqe->hdr.num_sge > dev->dev_attr.max_sge) { + rdma_error_report("Invalid num_sge=%d (max %d)", wqe->hdr.num_sge, + dev->dev_attr.max_sge); + complete_with_error(VENDOR_ERR_INV_NUM_SGE, comp_ctx); + continue; + } + + rdma_backend_post_srq_recv(&dev->backend_dev, &srq->backend_srq, + (struct ibv_sge *)&wqe->sge[0], + wqe->hdr.num_sge, + comp_ctx); + + pvrdma_ring_read_inc(ring); + + wqe = pvrdma_ring_next_elem_read(ring); + } + +} + void pvrdma_cq_poll(RdmaDeviceResources *dev_res, uint32_t cq_handle) { RdmaRmCQ *cq; diff --git a/hw/rdma/vmw/pvrdma_qp_ops.h b/hw/rdma/vmw/pvrdma_qp_ops.h index 31cb48ba29..82e720a76f 100644 --- a/hw/rdma/vmw/pvrdma_qp_ops.h +++ b/hw/rdma/vmw/pvrdma_qp_ops.h @@ -22,6 +22,7 @@ int pvrdma_qp_ops_init(void); void pvrdma_qp_ops_fini(void); void pvrdma_qp_send(PVRDMADev *dev, uint32_t qp_handle); void pvrdma_qp_recv(PVRDMADev *dev, uint32_t qp_handle); +void pvrdma_srq_recv(PVRDMADev *dev, uint32_t srq_handle); void pvrdma_cq_poll(RdmaDeviceResources *dev_res, uint32_t cq_handle); #endif diff --git a/hw/sparc/leon3.c b/hw/sparc/leon3.c index 774639af33..0383b17c29 100644 --- a/hw/sparc/leon3.c +++ b/hw/sparc/leon3.c @@ -194,6 +194,10 @@ static void leon3_generic_hw_init(MachineState *machine) &entry, NULL, NULL, 1 /* big endian */, EM_SPARC, 0, 0); if (kernel_size < 0) { + kernel_size = load_uimage(kernel_filename, NULL, &entry, + NULL, NULL, NULL); + } + if (kernel_size < 0) { error_report("could not load kernel '%s'", kernel_filename); exit(1); } diff --git a/include/block/block.h b/include/block/block.h index c7a26199aa..5e2b98b0ee 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -316,10 +316,6 @@ int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue, Error **errp); void bdrv_reopen_commit(BDRVReopenState *reopen_state); void bdrv_reopen_abort(BDRVReopenState *reopen_state); -int bdrv_read(BdrvChild *child, int64_t sector_num, - uint8_t *buf, int nb_sectors); -int bdrv_write(BdrvChild *child, int64_t sector_num, - const uint8_t *buf, int nb_sectors); int bdrv_pwrite_zeroes(BdrvChild *child, int64_t offset, int bytes, BdrvRequestFlags flags); int bdrv_make_zero(BdrvChild *child, BdrvRequestFlags flags); diff --git a/include/block/nbd.h b/include/block/nbd.h index 6d05983a55..bb9f5bc021 100644 --- a/include/block/nbd.h +++ b/include/block/nbd.h @@ -127,18 +127,32 @@ typedef struct NBDExtent { /* Transmission (export) flags: sent from server to client during handshake, but describe what will happen during transmission */ -#define NBD_FLAG_HAS_FLAGS (1 << 0) /* Flags are there */ -#define NBD_FLAG_READ_ONLY (1 << 1) /* Device is read-only */ -#define NBD_FLAG_SEND_FLUSH (1 << 2) /* Send FLUSH */ -#define NBD_FLAG_SEND_FUA (1 << 3) /* Send FUA (Force Unit Access) */ -#define NBD_FLAG_ROTATIONAL (1 << 4) /* Use elevator algorithm - - rotational media */ -#define NBD_FLAG_SEND_TRIM (1 << 5) /* Send TRIM (discard) */ -#define NBD_FLAG_SEND_WRITE_ZEROES (1 << 6) /* Send WRITE_ZEROES */ -#define NBD_FLAG_SEND_DF (1 << 7) /* Send DF (Do not Fragment) */ -#define NBD_FLAG_CAN_MULTI_CONN (1 << 8) /* Multi-client cache consistent */ -#define NBD_FLAG_SEND_RESIZE (1 << 9) /* Send resize */ -#define NBD_FLAG_SEND_CACHE (1 << 10) /* Send CACHE (prefetch) */ +enum { + NBD_FLAG_HAS_FLAGS_BIT = 0, /* Flags are there */ + NBD_FLAG_READ_ONLY_BIT = 1, /* Device is read-only */ + NBD_FLAG_SEND_FLUSH_BIT = 2, /* Send FLUSH */ + NBD_FLAG_SEND_FUA_BIT = 3, /* Send FUA (Force Unit Access) */ + NBD_FLAG_ROTATIONAL_BIT = 4, /* Use elevator algorithm - + rotational media */ + NBD_FLAG_SEND_TRIM_BIT = 5, /* Send TRIM (discard) */ + NBD_FLAG_SEND_WRITE_ZEROES_BIT = 6, /* Send WRITE_ZEROES */ + NBD_FLAG_SEND_DF_BIT = 7, /* Send DF (Do not Fragment) */ + NBD_FLAG_CAN_MULTI_CONN_BIT = 8, /* Multi-client cache consistent */ + NBD_FLAG_SEND_RESIZE_BIT = 9, /* Send resize */ + NBD_FLAG_SEND_CACHE_BIT = 10, /* Send CACHE (prefetch) */ +}; + +#define NBD_FLAG_HAS_FLAGS (1 << NBD_FLAG_HAS_FLAGS_BIT) +#define NBD_FLAG_READ_ONLY (1 << NBD_FLAG_READ_ONLY_BIT) +#define NBD_FLAG_SEND_FLUSH (1 << NBD_FLAG_SEND_FLUSH_BIT) +#define NBD_FLAG_SEND_FUA (1 << NBD_FLAG_SEND_FUA_BIT) +#define NBD_FLAG_ROTATIONAL (1 << NBD_FLAG_ROTATIONAL_BIT) +#define NBD_FLAG_SEND_TRIM (1 << NBD_FLAG_SEND_TRIM_BIT) +#define NBD_FLAG_SEND_WRITE_ZEROES (1 << NBD_FLAG_SEND_WRITE_ZEROES_BIT) +#define NBD_FLAG_SEND_DF (1 << NBD_FLAG_SEND_DF_BIT) +#define NBD_FLAG_CAN_MULTI_CONN (1 << NBD_FLAG_CAN_MULTI_CONN_BIT) +#define NBD_FLAG_SEND_RESIZE (1 << NBD_FLAG_SEND_RESIZE_BIT) +#define NBD_FLAG_SEND_CACHE (1 << NBD_FLAG_SEND_CACHE_BIT) /* New-style handshake (global) flags, sent from server to client, and control what will happen during handshake phase. */ diff --git a/include/exec/cpu-common.h b/include/exec/cpu-common.h index 848a4b94ab..f7dbe75fbc 100644 --- a/include/exec/cpu-common.h +++ b/include/exec/cpu-common.h @@ -7,9 +7,6 @@ #include "exec/hwaddr.h" #endif -#include "qemu/bswap.h" -#include "qemu/queue.h" - /* The CPU list lock nests outside page_(un)lock or mmap_(un)lock */ void qemu_init_cpu_list(void); void cpu_list_lock(void); diff --git a/include/exec/poison.h b/include/exec/poison.h index 1a7a57baae..b862320fa6 100644 --- a/include/exec/poison.h +++ b/include/exec/poison.h @@ -44,6 +44,7 @@ #pragma GCC poison TARGET_LONG_BITS #pragma GCC poison TARGET_FMT_lx #pragma GCC poison TARGET_FMT_ld +#pragma GCC poison TARGET_FMT_lu #pragma GCC poison TARGET_PAGE_SIZE #pragma GCC poison TARGET_PAGE_MASK diff --git a/include/hw/arm/aspeed.h b/include/hw/arm/aspeed.h index 325c091d09..02073a6b4d 100644 --- a/include/hw/arm/aspeed.h +++ b/include/hw/arm/aspeed.h @@ -22,6 +22,7 @@ typedef struct AspeedBoardConfig { const char *spi_model; uint32_t num_cs; void (*i2c_init)(AspeedBoardState *bmc); + uint32_t ram; } AspeedBoardConfig; #define TYPE_ASPEED_MACHINE MACHINE_TYPE_NAME("aspeed") diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h index 507517c603..424070924e 100644 --- a/include/hw/arm/virt.h +++ b/include/hw/arm/virt.h @@ -35,6 +35,7 @@ #include "qemu/notify.h" #include "hw/boards.h" #include "hw/arm/arm.h" +#include "hw/block/flash.h" #include "sysemu/kvm.h" #include "hw/intc/arm_gicv3_common.h" @@ -113,6 +114,7 @@ typedef struct { Notifier machine_done; DeviceState *platform_bus_dev; FWCfgState *fw_cfg; + PFlashCFI01 *flash[2]; bool secure; bool highmem; bool highmem_ecam; diff --git a/include/hw/block/flash.h b/include/hw/block/flash.h index a0f488732a..1acaf7de80 100644 --- a/include/hw/block/flash.h +++ b/include/hw/block/flash.h @@ -24,6 +24,7 @@ PFlashCFI01 *pflash_cfi01_register(hwaddr base, int be); BlockBackend *pflash_cfi01_get_blk(PFlashCFI01 *fl); MemoryRegion *pflash_cfi01_get_memory(PFlashCFI01 *fl); +void pflash_cfi01_legacy_drive(PFlashCFI01 *dev, DriveInfo *dinfo); /* pflash_cfi02.c */ diff --git a/include/hw/i2c/i2c-ddc.h b/include/hw/display/i2c-ddc.h index c29443c5af..c29443c5af 100644 --- a/include/hw/i2c/i2c-ddc.h +++ b/include/hw/display/i2c-ddc.h diff --git a/include/hw/display/xlnx_dp.h b/include/hw/display/xlnx_dp.h index 26b759cd44..45a805033a 100644 --- a/include/hw/display/xlnx_dp.h +++ b/include/hw/display/xlnx_dp.h @@ -27,7 +27,7 @@ #include "hw/misc/auxbus.h" #include "hw/i2c/i2c.h" #include "hw/display/dpcd.h" -#include "hw/i2c/i2c-ddc.h" +#include "hw/display/i2c-ddc.h" #include "qemu/fifo8.h" #include "qemu/units.h" #include "hw/dma/xlnx_dpdma.h" diff --git a/include/qemu/compiler.h b/include/qemu/compiler.h index 296b2fd572..09fc44cca4 100644 --- a/include/qemu/compiler.h +++ b/include/qemu/compiler.h @@ -28,7 +28,7 @@ #define QEMU_SENTINEL __attribute__((sentinel)) -#if defined(_WIN32) +#if defined(_WIN32) && (defined(__x86_64__) || defined(__i386__)) # define QEMU_PACKED __attribute__((gcc_struct, packed)) #else # define QEMU_PACKED __attribute__((packed)) diff --git a/include/qemu/osdep.h b/include/qemu/osdep.h index 303d315c5d..af2b91f0b8 100644 --- a/include/qemu/osdep.h +++ b/include/qemu/osdep.h @@ -85,17 +85,17 @@ extern int daemon(int, int); #endif #endif +/* enable C99/POSIX format strings (needs mingw32-runtime 3.15 or later) */ +#ifdef __MINGW32__ +#define __USE_MINGW_ANSI_STDIO 1 +#endif + #include <stdarg.h> #include <stddef.h> #include <stdbool.h> #include <stdint.h> #include <sys/types.h> #include <stdlib.h> - -/* enable C99/POSIX format strings (needs mingw32-runtime 3.15 or later) */ -#ifdef __MINGW32__ -#define __USE_MINGW_ANSI_STDIO 1 -#endif #include <stdio.h> #include <string.h> @@ -432,7 +432,7 @@ void job_enter_cond(Job *job, bool(*fn)(Job *job)) timer_del(&job->sleep_timer); job->busy = true; job_unlock(); - aio_co_wake(job->co); + aio_co_enter(job->aio_context, job->co); } void job_enter(Job *job) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index c1a26021f8..ef42e02d82 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -2366,11 +2366,19 @@ static void load_elf_image(const char *image_name, int image_fd, vaddr_ps = TARGET_ELF_PAGESTART(vaddr); vaddr_len = TARGET_ELF_PAGELENGTH(eppnt->p_filesz + vaddr_po); - error = target_mmap(vaddr_ps, vaddr_len, - elf_prot, MAP_PRIVATE | MAP_FIXED, - image_fd, eppnt->p_offset - vaddr_po); - if (error == -1) { - goto exit_perror; + /* + * Some segments may be completely empty without any backing file + * segment, in that case just let zero_bss allocate an empty buffer + * for it. + */ + if (eppnt->p_filesz != 0) { + error = target_mmap(vaddr_ps, vaddr_len, elf_prot, + MAP_PRIVATE | MAP_FIXED, + image_fd, eppnt->p_offset - vaddr_po); + + if (error == -1) { + goto exit_perror; + } } vaddr_ef = vaddr + eppnt->p_filesz; @@ -2872,7 +2880,7 @@ struct target_elf_prpsinfo { target_gid_t pr_gid; target_pid_t pr_pid, pr_ppid, pr_pgrp, pr_sid; /* Lots missing */ - char pr_fname[16]; /* filename of executable */ + char pr_fname[16] QEMU_NONSTRING; /* filename of executable */ char pr_psargs[ELF_PRARGSZ]; /* initial part of arg list */ }; diff --git a/linux-user/exit.c b/linux-user/exit.c index 14e94e28fa..bdda720553 100644 --- a/linux-user/exit.c +++ b/linux-user/exit.c @@ -18,6 +18,9 @@ */ #include "qemu/osdep.h" #include "qemu.h" +#ifdef TARGET_GPROF +#include <sys/gmon.h> +#endif #ifdef CONFIG_GCOV extern void __gcov_dump(void); diff --git a/linux-user/ioctls.h b/linux-user/ioctls.h index ae8951625f..37501f575c 100644 --- a/linux-user/ioctls.h +++ b/linux-user/ioctls.h @@ -178,7 +178,7 @@ #endif /* CONFIG_USBFS */ IOCTL(SIOCATMARK, IOC_R, MK_PTR(TYPE_INT)) - IOCTL(SIOCGIFNAME, IOC_RW, MK_PTR(TYPE_INT)) + IOCTL(SIOCGIFNAME, IOC_RW, MK_PTR(MK_STRUCT(STRUCT_int_ifreq))) IOCTL(SIOCGIFFLAGS, IOC_W | IOC_R, MK_PTR(MK_STRUCT(STRUCT_short_ifreq))) IOCTL(SIOCSIFFLAGS, IOC_W, MK_PTR(MK_STRUCT(STRUCT_short_ifreq))) IOCTL(SIOCGIFADDR, IOC_W | IOC_R, MK_PTR(MK_STRUCT(STRUCT_sockaddr_ifreq))) diff --git a/linux-user/openrisc/target_cpu.h b/linux-user/openrisc/target_cpu.h index d1ea4506e2..32ff135089 100644 --- a/linux-user/openrisc/target_cpu.h +++ b/linux-user/openrisc/target_cpu.h @@ -6,7 +6,7 @@ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/linux-user/openrisc/target_structs.h b/linux-user/openrisc/target_structs.h index afbb7ad108..e98e2bc799 100644 --- a/linux-user/openrisc/target_structs.h +++ b/linux-user/openrisc/target_structs.h @@ -6,7 +6,7 @@ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/linux-user/sh4/target_cpu.h b/linux-user/sh4/target_cpu.h index 1a647ddb98..b0be9a2c1b 100644 --- a/linux-user/sh4/target_cpu.h +++ b/linux-user/sh4/target_cpu.h @@ -6,7 +6,7 @@ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/linux-user/sh4/target_structs.h b/linux-user/sh4/target_structs.h index 3e832bf69a..00ac39478b 100644 --- a/linux-user/sh4/target_structs.h +++ b/linux-user/sh4/target_structs.h @@ -6,7 +6,7 @@ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/linux-user/signal.c b/linux-user/signal.c index e2c0b37173..44b2d3b35a 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -508,6 +508,11 @@ void signal_init(void) act.sa_flags = SA_SIGINFO; act.sa_sigaction = host_signal_handler; for(i = 1; i <= TARGET_NSIG; i++) { +#ifdef TARGET_GPROF + if (i == SIGPROF) { + continue; + } +#endif host_sig = target_to_host_signal(i); sigaction(host_sig, NULL, &oact); if (oact.sa_sigaction == (void *)SIG_IGN) { diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 96cd4bf86d..f5ff6f5dc8 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -59,9 +59,6 @@ #ifdef CONFIG_TIMERFD #include <sys/timerfd.h> #endif -#ifdef TARGET_GPROF -#include <sys/gmon.h> -#endif #ifdef CONFIG_EVENTFD #include <sys/eventfd.h> #endif @@ -1864,6 +1861,28 @@ static abi_long do_setsockopt(int sockfd, int level, int optname, case IPV6_RECVHOPLIMIT: case IPV6_2292HOPLIMIT: case IPV6_CHECKSUM: + case IPV6_ADDRFORM: + case IPV6_2292PKTINFO: + case IPV6_RECVTCLASS: + case IPV6_RECVRTHDR: + case IPV6_2292RTHDR: + case IPV6_RECVHOPOPTS: + case IPV6_2292HOPOPTS: + case IPV6_RECVDSTOPTS: + case IPV6_2292DSTOPTS: + case IPV6_TCLASS: +#ifdef IPV6_RECVPATHMTU + case IPV6_RECVPATHMTU: +#endif +#ifdef IPV6_TRANSPARENT + case IPV6_TRANSPARENT: +#endif +#ifdef IPV6_FREEBIND + case IPV6_FREEBIND: +#endif +#ifdef IPV6_RECVORIGDSTADDR + case IPV6_RECVORIGDSTADDR: +#endif val = 0; if (optlen < sizeof(uint32_t)) { return -TARGET_EINVAL; @@ -2358,6 +2377,28 @@ static abi_long do_getsockopt(int sockfd, int level, int optname, case IPV6_RECVHOPLIMIT: case IPV6_2292HOPLIMIT: case IPV6_CHECKSUM: + case IPV6_ADDRFORM: + case IPV6_2292PKTINFO: + case IPV6_RECVTCLASS: + case IPV6_RECVRTHDR: + case IPV6_2292RTHDR: + case IPV6_RECVHOPOPTS: + case IPV6_2292HOPOPTS: + case IPV6_RECVDSTOPTS: + case IPV6_2292DSTOPTS: + case IPV6_TCLASS: +#ifdef IPV6_RECVPATHMTU + case IPV6_RECVPATHMTU: +#endif +#ifdef IPV6_TRANSPARENT + case IPV6_TRANSPARENT: +#endif +#ifdef IPV6_FREEBIND + case IPV6_FREEBIND: +#endif +#ifdef IPV6_RECVORIGDSTADDR + case IPV6_RECVORIGDSTADDR: +#endif if (get_user_u32(len, optlen)) return -TARGET_EFAULT; if (len < 0) diff --git a/linux-user/uname.c b/linux-user/uname.c index 313b79dbad..1c05f95387 100644 --- a/linux-user/uname.c +++ b/linux-user/uname.c @@ -72,9 +72,8 @@ const char *cpu_to_uname_machine(void *cpu_env) #define COPY_UTSNAME_FIELD(dest, src) \ do { \ - /* __NEW_UTS_LEN doesn't include terminating null */ \ - (void) strncpy((dest), (src), __NEW_UTS_LEN); \ - (dest)[__NEW_UTS_LEN] = '\0'; \ + memcpy((dest), (src), MIN(sizeof(src), sizeof(dest))); \ + (dest)[sizeof(dest) - 1] = '\0'; \ } while (0) int sys_uname(struct new_utsname *buf) @@ -837,9 +837,10 @@ int qemu_show_nic_models(const char *arg, const char *const *models) return 0; } - fprintf(stderr, "qemu: Supported NIC models: "); - for (i = 0 ; models[i]; i++) - fprintf(stderr, "%s%c", models[i], models[i+1] ? ',' : '\n'); + printf("Supported NIC models:\n"); + for (i = 0 ; models[i]; i++) { + printf("%s\n", models[i]); + } return 1; } diff --git a/qemu-doc.texi b/qemu-doc.texi index ae3c3f9632..577d1e8376 100644 --- a/qemu-doc.texi +++ b/qemu-doc.texi @@ -38,6 +38,7 @@ * QEMU Guest Agent:: * QEMU User space emulator:: * System requirements:: +* Security:: * Implementation notes:: * Deprecated features:: * Supported build platforms:: @@ -2878,6 +2879,8 @@ added with Linux 4.5 which is supported by the major distros. And even if RHEL7 has kernel 3.10, KVM there has the required functionality there to make it close to a 4.5 or newer kernel. +@include docs/security.texi + @include qemu-tech.texi @include qemu-deprecated.texi diff --git a/qemu-ga.texi b/qemu-ga.texi index 4c7a8fd163..f00ad830f2 100644 --- a/qemu-ga.texi +++ b/qemu-ga.texi @@ -30,7 +30,7 @@ set user's password @end itemize qemu-ga will read a system configuration file on startup (located at -@file{/etc/qemu/qemu-ga.conf} by default), then parse remaining +@file{@value{CONFDIR}/qemu-ga.conf} by default), then parse remaining configuration options on the command line. For the same key, the last option wins, but the lists accumulate (see below for configuration file format). @@ -58,7 +58,7 @@ file format). Enable fsfreeze hook. Accepts an optional argument that specifies script to run on freeze/thaw. Script will be called with 'freeze'/'thaw' arguments accordingly (default is - @samp{/etc/qemu/fsfreeze-hook}). If using -F with an argument, do + @samp{@value{CONFDIR}/fsfreeze-hook}). If using -F with an argument, do not follow -F with a space (for example: @samp{-F/var/run/fsfreezehook.sh}). diff --git a/qemu-img.c b/qemu-img.c index e6ad5978e0..28fba1e7a7 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -37,6 +37,7 @@ #include "qemu/option.h" #include "qemu/error-report.h" #include "qemu/log.h" +#include "qemu/units.h" #include "qom/object_interfaces.h" #include "sysemu/sysemu.h" #include "sysemu/block-backend.h" @@ -1216,7 +1217,7 @@ static int compare_buffers(const uint8_t *buf1, const uint8_t *buf2, return res; } -#define IO_BUF_SIZE (2 * 1024 * 1024) +#define IO_BUF_SIZE (2 * MiB) /* * Check if passed sectors are empty (not allocated or contain only 0 bytes) @@ -2960,7 +2961,7 @@ static int img_map(int argc, char **argv) int64_t n; /* Probe up to 1 GiB at a time. */ - n = MIN(1 << 30, length - offset); + n = MIN(1 * GiB, length - offset); ret = get_block_status(bs, offset, n, &next); if (ret < 0) { @@ -3311,26 +3312,30 @@ static int img_rebase(int argc, char **argv) char backing_name[PATH_MAX]; QDict *options = NULL; - if (bs->backing_format[0] != '\0') { - options = qdict_new(); - qdict_put_str(options, "driver", bs->backing_format); - } - - if (force_share) { - if (!options) { + if (bs->backing) { + if (bs->backing_format[0] != '\0') { options = qdict_new(); + qdict_put_str(options, "driver", bs->backing_format); } - qdict_put_bool(options, BDRV_OPT_FORCE_SHARE, true); - } - bdrv_get_backing_filename(bs, backing_name, sizeof(backing_name)); - blk_old_backing = blk_new_open(backing_name, NULL, - options, src_flags, &local_err); - if (!blk_old_backing) { - error_reportf_err(local_err, - "Could not open old backing file '%s': ", - backing_name); - ret = -1; - goto out; + + if (force_share) { + if (!options) { + options = qdict_new(); + } + qdict_put_bool(options, BDRV_OPT_FORCE_SHARE, true); + } + bdrv_get_backing_filename(bs, backing_name, sizeof(backing_name)); + blk_old_backing = blk_new_open(backing_name, NULL, + options, src_flags, &local_err); + if (!blk_old_backing) { + error_reportf_err(local_err, + "Could not open old backing file '%s': ", + backing_name); + ret = -1; + goto out; + } + } else { + blk_old_backing = NULL; } if (out_baseimg[0]) { @@ -3383,7 +3388,7 @@ static int img_rebase(int argc, char **argv) */ if (!unsafe) { int64_t size; - int64_t old_backing_size; + int64_t old_backing_size = 0; int64_t new_backing_size = 0; uint64_t offset; int64_t n; @@ -3399,15 +3404,18 @@ static int img_rebase(int argc, char **argv) ret = -1; goto out; } - old_backing_size = blk_getlength(blk_old_backing); - if (old_backing_size < 0) { - char backing_name[PATH_MAX]; + if (blk_old_backing) { + old_backing_size = blk_getlength(blk_old_backing); + if (old_backing_size < 0) { + char backing_name[PATH_MAX]; - bdrv_get_backing_filename(bs, backing_name, sizeof(backing_name)); - error_report("Could not get size of '%s': %s", - backing_name, strerror(-old_backing_size)); - ret = -1; - goto out; + bdrv_get_backing_filename(bs, backing_name, + sizeof(backing_name)); + error_report("Could not get size of '%s': %s", + backing_name, strerror(-old_backing_size)); + ret = -1; + goto out; + } } if (blk_new_backing) { new_backing_size = blk_getlength(blk_new_backing); @@ -3424,6 +3432,8 @@ static int img_rebase(int argc, char **argv) } for (offset = 0; offset < size; offset += n) { + bool buf_old_is_zero = false; + /* How many bytes can we handle with the next read? */ n = MIN(IO_BUF_SIZE, size - offset); @@ -3444,6 +3454,7 @@ static int img_rebase(int argc, char **argv) */ if (offset >= old_backing_size) { memset(buf_old, 0, n); + buf_old_is_zero = true; } else { if (offset + n > old_backing_size) { n = old_backing_size - offset; @@ -3479,8 +3490,12 @@ static int img_rebase(int argc, char **argv) if (compare_buffers(buf_old + written, buf_new + written, n - written, &pnum)) { - ret = blk_pwrite(blk, offset + written, - buf_old + written, pnum, 0); + if (buf_old_is_zero) { + ret = blk_pwrite_zeroes(blk, offset + written, pnum, 0); + } else { + ret = blk_pwrite(blk, offset + written, + buf_old + written, pnum, 0); + } if (ret < 0) { error_report("Error while writing to COW image: %s", strerror(-ret)); diff --git a/qemu-nbd.c b/qemu-nbd.c index dca9e72cee..081fcf74d5 100644 --- a/qemu-nbd.c +++ b/qemu-nbd.c @@ -279,37 +279,25 @@ static int qemu_nbd_client_list(SocketAddress *saddr, QCryptoTLSCreds *tls, printf(" description: %s\n", list[i].description); } if (list[i].flags & NBD_FLAG_HAS_FLAGS) { + static const char *const flag_names[] = { + [NBD_FLAG_READ_ONLY_BIT] = "readonly", + [NBD_FLAG_SEND_FLUSH_BIT] = "flush", + [NBD_FLAG_SEND_FUA_BIT] = "fua", + [NBD_FLAG_ROTATIONAL_BIT] = "rotational", + [NBD_FLAG_SEND_TRIM_BIT] = "trim", + [NBD_FLAG_SEND_WRITE_ZEROES_BIT] = "zeroes", + [NBD_FLAG_SEND_DF_BIT] = "df", + [NBD_FLAG_CAN_MULTI_CONN_BIT] = "multi", + [NBD_FLAG_SEND_RESIZE_BIT] = "resize", + [NBD_FLAG_SEND_CACHE_BIT] = "cache", + }; + printf(" size: %" PRIu64 "\n", list[i].size); printf(" flags: 0x%x (", list[i].flags); - if (list[i].flags & NBD_FLAG_READ_ONLY) { - printf(" readonly"); - } - if (list[i].flags & NBD_FLAG_SEND_FLUSH) { - printf(" flush"); - } - if (list[i].flags & NBD_FLAG_SEND_FUA) { - printf(" fua"); - } - if (list[i].flags & NBD_FLAG_ROTATIONAL) { - printf(" rotational"); - } - if (list[i].flags & NBD_FLAG_SEND_TRIM) { - printf(" trim"); - } - if (list[i].flags & NBD_FLAG_SEND_WRITE_ZEROES) { - printf(" zeroes"); - } - if (list[i].flags & NBD_FLAG_SEND_DF) { - printf(" df"); - } - if (list[i].flags & NBD_FLAG_CAN_MULTI_CONN) { - printf(" multi"); - } - if (list[i].flags & NBD_FLAG_SEND_RESIZE) { - printf(" resize"); - } - if (list[i].flags & NBD_FLAG_SEND_CACHE) { - printf(" cache"); + for (size_t bit = 0; bit < ARRAY_SIZE(flag_names); bit++) { + if (flag_names[bit] && (list[i].flags & (1 << bit))) { + printf(" %s", flag_names[bit]); + } } printf(" )\n"); } diff --git a/qga/commands-win32.c b/qga/commands-win32.c index d40d61f605..6b67f16faf 100644 --- a/qga/commands-win32.c +++ b/qga/commands-win32.c @@ -457,7 +457,7 @@ void qmp_guest_file_flush(int64_t handle, Error **errp) #ifdef CONFIG_QGA_NTDDSCSI -static STORAGE_BUS_TYPE win2qemu[] = { +static GuestDiskBusType win2qemu[] = { [BusTypeUnknown] = GUEST_DISK_BUS_TYPE_UNKNOWN, [BusTypeScsi] = GUEST_DISK_BUS_TYPE_SCSI, [BusTypeAtapi] = GUEST_DISK_BUS_TYPE_IDE, diff --git a/qom/object.c b/qom/object.c index e3206d6799..d3412e7fdc 100644 --- a/qom/object.c +++ b/qom/object.c @@ -679,7 +679,7 @@ Object *object_new_with_propv(const char *typename, error_setg(errp, "object type '%s' is abstract", typename); return NULL; } - obj = object_new(typename); + obj = object_new_with_type(klass->type); if (object_set_propv(obj, &local_err, vargs) < 0) { goto error; diff --git a/scripts/cocci-macro-file.h b/scripts/cocci-macro-file.h index e485cdccae..c6bbc05ba3 100644 --- a/scripts/cocci-macro-file.h +++ b/scripts/cocci-macro-file.h @@ -23,7 +23,12 @@ #define QEMU_NORETURN __attribute__ ((__noreturn__)) #define QEMU_WARN_UNUSED_RESULT __attribute__((warn_unused_result)) #define QEMU_SENTINEL __attribute__((sentinel)) -#define QEMU_PACKED __attribute__((gcc_struct, packed)) + +#if defined(_WIN32) && (defined(__x86_64__) || defined(__i386__)) +# define QEMU_PACKED __attribute__((gcc_struct, packed)) +#else +# define QEMU_PACKED __attribute__((packed)) +#endif #define cat(x,y) x ## y #define cat2(x,y) cat(x,y) diff --git a/scripts/decodetree.py b/scripts/decodetree.py index aa790b596a..81874e22cc 100755 --- a/scripts/decodetree.py +++ b/scripts/decodetree.py @@ -27,6 +27,7 @@ import getopt insnwidth = 32 insnmask = 0xffffffff +variablewidth = False fields = {} arguments = {} formats = {} @@ -255,7 +256,7 @@ class FunctionField: return self.func + '(' + str(self.base) + ')' def str_extract(self): - return self.func + '(' + self.base.str_extract() + ')' + return self.func + '(ctx, ' + self.base.str_extract() + ')' def __eq__(self, other): return self.func == other.func and self.base == other.base @@ -289,7 +290,7 @@ class Arguments: class General: """Common code between instruction formats and instruction patterns""" - def __init__(self, name, lineno, base, fixb, fixm, udfm, fldm, flds): + def __init__(self, name, lineno, base, fixb, fixm, udfm, fldm, flds, w): self.name = name self.file = input_file self.lineno = lineno @@ -299,6 +300,7 @@ class General: self.undefmask = udfm self.fieldmask = fldm self.fields = flds + self.width = w def __str__(self): return self.name + ' ' + str_match_bits(self.fixedbits, self.fixedmask) @@ -316,7 +318,7 @@ class Format(General): return decode_function + '_extract_' + self.name def output_extract(self): - output('static void ', self.extract_name(), '(', + output('static void ', self.extract_name(), '(DisasContext *ctx, ', self.base.struct_name(), ' *a, ', insntype, ' insn)\n{\n') for n, f in self.fields.items(): output(' a->', n, ' = ', f.str_extract(), ';\n') @@ -341,7 +343,8 @@ class Pattern(General): arg = self.base.base.name output(ind, '/* ', self.file, ':', str(self.lineno), ' */\n') if not extracted: - output(ind, self.base.extract_name(), '(&u.f_', arg, ', insn);\n') + output(ind, self.base.extract_name(), + '(ctx, &u.f_', arg, ', insn);\n') for n, f in self.fields.items(): output(ind, 'u.f_', arg, '.', n, ' = ', f.str_extract(), ';\n') output(ind, 'if (', translate_prefix, '_', self.name, @@ -352,7 +355,7 @@ class Pattern(General): class MultiPattern(General): """Class representing an overlapping set of instruction patterns""" - def __init__(self, lineno, pats, fixb, fixm, udfm): + def __init__(self, lineno, pats, fixb, fixm, udfm, w): self.file = input_file self.lineno = lineno self.pats = pats @@ -360,6 +363,7 @@ class MultiPattern(General): self.fixedbits = fixb self.fixedmask = fixm self.undefmask = udfm + self.width = w def __str__(self): r = "{" @@ -502,7 +506,7 @@ def infer_argument_set(flds): return arg -def infer_format(arg, fieldmask, flds): +def infer_format(arg, fieldmask, flds, width): global arguments global formats global decode_function @@ -521,6 +525,8 @@ def infer_format(arg, fieldmask, flds): continue if fieldmask != fmt.fieldmask: continue + if width != fmt.width: + continue if not eq_fields_for_fmts(flds, fmt.fields): continue return (fmt, const_flds) @@ -529,7 +535,7 @@ def infer_format(arg, fieldmask, flds): if not arg: arg = infer_argument_set(flds) - fmt = Format(name, 0, arg, 0, 0, 0, fieldmask, var_flds) + fmt = Format(name, 0, arg, 0, 0, 0, fieldmask, var_flds, width) formats[name] = fmt return (fmt, const_flds) @@ -546,6 +552,7 @@ def parse_generic(lineno, is_format, name, toks): global re_ident global insnwidth global insnmask + global variablewidth fixedmask = 0 fixedbits = 0 @@ -633,8 +640,15 @@ def parse_generic(lineno, is_format, name, toks): error(lineno, 'invalid token "{0}"'.format(t)) width += shift + if variablewidth and width < insnwidth and width % 8 == 0: + shift = insnwidth - width + fixedbits <<= shift + fixedmask <<= shift + undefmask <<= shift + undefmask |= (1 << shift) - 1 + # We should have filled in all of the bits of the instruction. - if not (is_format and width == 0) and width != insnwidth: + elif not (is_format and width == 0) and width != insnwidth: error(lineno, 'definition has {0} bits'.format(width)) # Do not check for fields overlaping fields; one valid usage @@ -660,7 +674,7 @@ def parse_generic(lineno, is_format, name, toks): if name in formats: error(lineno, 'duplicate format name', name) fmt = Format(name, lineno, arg, fixedbits, fixedmask, - undefmask, fieldmask, flds) + undefmask, fieldmask, flds, width) formats[name] = fmt else: # Patterns can reference a format ... @@ -670,12 +684,14 @@ def parse_generic(lineno, is_format, name, toks): error(lineno, 'pattern specifies both format and argument set') if fixedmask & fmt.fixedmask: error(lineno, 'pattern fixed bits overlap format fixed bits') + if width != fmt.width: + error(lineno, 'pattern uses format of different width') fieldmask |= fmt.fieldmask fixedbits |= fmt.fixedbits fixedmask |= fmt.fixedmask undefmask |= fmt.undefmask else: - (fmt, flds) = infer_format(arg, fieldmask, flds) + (fmt, flds) = infer_format(arg, fieldmask, flds, width) arg = fmt.base for f in flds.keys(): if f not in arg.fields: @@ -687,7 +703,7 @@ def parse_generic(lineno, is_format, name, toks): if f not in flds.keys() and f not in fmt.fields.keys(): error(lineno, 'field {0} not initialized'.format(f)) pat = Pattern(name, lineno, fmt, fixedbits, fixedmask, - undefmask, fieldmask, flds) + undefmask, fieldmask, flds, width) patterns.append(pat) allpatterns.append(pat) @@ -727,6 +743,13 @@ def build_multi_pattern(lineno, pats): if p.lineno < lineno: lineno = p.lineno + width = None + for p in pats: + if width is None: + width = p.width + elif width != p.width: + error(lineno, 'width mismatch in patterns within braces') + repeat = True while repeat: if fixedmask == 0: @@ -742,7 +765,7 @@ def build_multi_pattern(lineno, pats): else: repeat = False - mp = MultiPattern(lineno, pats, fixedbits, fixedmask, undefmask) + mp = MultiPattern(lineno, pats, fixedbits, fixedmask, undefmask, width) patterns.append(mp) # end build_multi_pattern @@ -872,7 +895,7 @@ class Tree: # extract the fields now. if not extracted and self.base: output(ind, self.base.extract_name(), - '(&u.f_', self.base.base.name, ', insn);\n') + '(ctx, &u.f_', self.base.base.name, ', insn);\n') extracted = True # Attempt to aid the compiler in producing compact switch statements. @@ -943,6 +966,147 @@ def build_tree(pats, outerbits, outermask): # end build_tree +class SizeTree: + """Class representing a node in a size decode tree""" + + def __init__(self, m, w): + self.mask = m + self.subs = [] + self.base = None + self.width = w + + def str1(self, i): + ind = str_indent(i) + r = '{0}{1:08x}'.format(ind, self.mask) + r += ' [\n' + for (b, s) in self.subs: + r += '{0} {1:08x}:\n'.format(ind, b) + r += s.str1(i + 4) + '\n' + r += ind + ']' + return r + + def __str__(self): + return self.str1(0) + + def output_code(self, i, extracted, outerbits, outermask): + ind = str_indent(i) + + # If we need to load more bytes to test, do so now. + if extracted < self.width: + output(ind, 'insn = ', decode_function, + '_load_bytes(ctx, insn, {0}, {1});\n' + .format(extracted / 8, self.width / 8)); + extracted = self.width + + # Attempt to aid the compiler in producing compact switch statements. + # If the bits in the mask are contiguous, extract them. + sh = is_contiguous(self.mask) + if sh > 0: + # Propagate SH down into the local functions. + def str_switch(b, sh=sh): + return '(insn >> {0}) & 0x{1:x}'.format(sh, b >> sh) + + def str_case(b, sh=sh): + return '0x{0:x}'.format(b >> sh) + else: + def str_switch(b): + return 'insn & 0x{0:08x}'.format(b) + + def str_case(b): + return '0x{0:08x}'.format(b) + + output(ind, 'switch (', str_switch(self.mask), ') {\n') + for b, s in sorted(self.subs): + innermask = outermask | self.mask + innerbits = outerbits | b + output(ind, 'case ', str_case(b), ':\n') + output(ind, ' /* ', + str_match_bits(innerbits, innermask), ' */\n') + s.output_code(i + 4, extracted, innerbits, innermask) + output(ind, '}\n') + output(ind, 'return insn;\n') +# end SizeTree + +class SizeLeaf: + """Class representing a leaf node in a size decode tree""" + + def __init__(self, m, w): + self.mask = m + self.width = w + + def str1(self, i): + ind = str_indent(i) + return '{0}{1:08x}'.format(ind, self.mask) + + def __str__(self): + return self.str1(0) + + def output_code(self, i, extracted, outerbits, outermask): + global decode_function + ind = str_indent(i) + + # If we need to load more bytes, do so now. + if extracted < self.width: + output(ind, 'insn = ', decode_function, + '_load_bytes(ctx, insn, {0}, {1});\n' + .format(extracted / 8, self.width / 8)); + extracted = self.width + output(ind, 'return insn;\n') +# end SizeLeaf + + +def build_size_tree(pats, width, outerbits, outermask): + global insnwidth + + # Collect the mask of bits that are fixed in this width + innermask = 0xff << (insnwidth - width) + innermask &= ~outermask + minwidth = None + onewidth = True + for i in pats: + innermask &= i.fixedmask + if minwidth is None: + minwidth = i.width + elif minwidth != i.width: + onewidth = False; + if minwidth < i.width: + minwidth = i.width + + if onewidth: + return SizeLeaf(innermask, minwidth) + + if innermask == 0: + if width < minwidth: + return build_size_tree(pats, width + 8, outerbits, outermask) + + pnames = [] + for p in pats: + pnames.append(p.name + ':' + p.file + ':' + str(p.lineno)) + error_with_file(pats[0].file, pats[0].lineno, + 'overlapping patterns size {0}:'.format(width), pnames) + + bins = {} + for i in pats: + fb = i.fixedbits & innermask + if fb in bins: + bins[fb].append(i) + else: + bins[fb] = [i] + + fullmask = outermask | innermask + lens = sorted(bins.keys()) + if len(lens) == 1: + b = lens[0] + return build_size_tree(bins[b], width + 8, b | outerbits, fullmask) + + r = SizeTree(innermask, width) + for b, l in bins.items(): + s = build_size_tree(l, width, b | outerbits, fullmask) + r.subs.append((b, s)) + return r +# end build_size_tree + + def prop_format(tree): """Propagate Format objects into the decode tree""" @@ -965,6 +1129,23 @@ def prop_format(tree): # end prop_format +def prop_size(tree): + """Propagate minimum widths up the decode size tree""" + + if isinstance(tree, SizeTree): + min = None + for (b, s) in tree.subs: + width = prop_size(s) + if min is None or min > width: + min = width + assert min >= tree.width + tree.width = min + else: + min = tree.width + return min +# end prop_size + + def main(): global arguments global formats @@ -979,13 +1160,14 @@ def main(): global insntype global insnmask global decode_function + global variablewidth decode_scope = 'static ' long_opts = ['decode=', 'translate=', 'output=', 'insnwidth=', - 'static-decode='] + 'static-decode=', 'varinsnwidth='] try: - (opts, args) = getopt.getopt(sys.argv[1:], 'o:w:', long_opts) + (opts, args) = getopt.getopt(sys.argv[1:], 'o:vw:', long_opts) except getopt.GetoptError as err: error(0, err) for o, a in opts: @@ -999,7 +1181,9 @@ def main(): elif o == '--translate': translate_prefix = a translate_scope = '' - elif o in ('-w', '--insnwidth'): + elif o in ('-w', '--insnwidth', '--varinsnwidth'): + if o == '--varinsnwidth': + variablewidth = True insnwidth = int(a) if insnwidth == 16: insntype = 'uint16_t' @@ -1017,8 +1201,12 @@ def main(): parse_file(f) f.close() - t = build_tree(patterns, 0, 0) - prop_format(t) + if variablewidth: + stree = build_size_tree(patterns, 8, 0, 0) + prop_size(stree) + + dtree = build_tree(patterns, 0, 0) + prop_format(dtree) if output_file: output_fd = open(output_file, 'w') @@ -1059,11 +1247,18 @@ def main(): f = arguments[n] output(i4, i4, f.struct_name(), ' f_', f.name, ';\n') output(i4, '} u;\n\n') - t.output_code(4, False, 0, 0) + dtree.output_code(4, False, 0, 0) output(i4, 'return false;\n') output('}\n') + if variablewidth: + output('\n', decode_scope, insntype, ' ', decode_function, + '_load(DisasContext *ctx)\n{\n', + ' ', insntype, ' insn = 0;\n\n') + stree.output_code(4, 0, 0, 0) + output('}\n') + if output_file: output_fd.close() # end main diff --git a/slirp b/slirp -Subproject 59a1b1f165458c2acb7ff0525b543945f741622 +Subproject f0da6726207b740f6101028b2992f918477a4b0 diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 22bc6e00ab..733b840a71 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -1285,6 +1285,7 @@ static inline uint32_t xpsr_read(CPUARMState *env) | (env->CF << 29) | ((env->VF & 0x80000000) >> 3) | (env->QF << 27) | (env->thumb << 24) | ((env->condexec_bits & 3) << 25) | ((env->condexec_bits & 0xfc) << 8) + | (env->GE << 16) | env->v7m.exception; } @@ -1300,6 +1301,9 @@ static inline void xpsr_write(CPUARMState *env, uint32_t val, uint32_t mask) if (mask & XPSR_Q) { env->QF = ((val & XPSR_Q) != 0); } + if (mask & XPSR_GE) { + env->GE = (val & XPSR_GE) >> 16; + } if (mask & XPSR_T) { env->thumb = ((val & XPSR_T) != 0); } @@ -2610,18 +2614,25 @@ bool write_list_to_cpustate(ARMCPU *cpu); /** * write_cpustate_to_list: * @cpu: ARMCPU + * @kvm_sync: true if this is for syncing back to KVM * * For each register listed in the ARMCPU cpreg_indexes list, write * its value from the ARMCPUState structure into the cpreg_values list. * This is used to copy info from TCG's working data structures into * KVM or for outbound migration. * + * @kvm_sync is true if we are doing this in order to sync the + * register state back to KVM. In this case we will only update + * values in the list if the previous list->cpustate sync actually + * successfully wrote the CPU state. Otherwise we will keep the value + * that is in the list. + * * Returns: true if all register values were read correctly, * false if some register was unknown or could not be read. * Note that we do not stop early on failure -- we will attempt * reading all registers in the list. */ -bool write_cpustate_to_list(ARMCPU *cpu); +bool write_cpustate_to_list(ARMCPU *cpu, bool kvm_sync); #define ARM_CPUID_TI915T 0x54029152 #define ARM_CPUID_TI925T 0x54029252 diff --git a/target/arm/helper.c b/target/arm/helper.c index 81a92ab491..1e6eb0d0f3 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -1,4 +1,5 @@ #include "qemu/osdep.h" +#include "qemu/units.h" #include "target/arm/idau.h" #include "trace.h" #include "cpu.h" @@ -266,7 +267,7 @@ static bool raw_accessors_invalid(const ARMCPRegInfo *ri) return true; } -bool write_cpustate_to_list(ARMCPU *cpu) +bool write_cpustate_to_list(ARMCPU *cpu, bool kvm_sync) { /* Write the coprocessor state from cpu->env to the (index,value) list. */ int i; @@ -275,6 +276,7 @@ bool write_cpustate_to_list(ARMCPU *cpu) for (i = 0; i < cpu->cpreg_array_len; i++) { uint32_t regidx = kvm_to_cpreg_id(cpu->cpreg_indexes[i]); const ARMCPRegInfo *ri; + uint64_t newval; ri = get_arm_cp_reginfo(cpu->cp_regs, regidx); if (!ri) { @@ -284,7 +286,29 @@ bool write_cpustate_to_list(ARMCPU *cpu) if (ri->type & ARM_CP_NO_RAW) { continue; } - cpu->cpreg_values[i] = read_raw_cp_reg(&cpu->env, ri); + + newval = read_raw_cp_reg(&cpu->env, ri); + if (kvm_sync) { + /* + * Only sync if the previous list->cpustate sync succeeded. + * Rather than tracking the success/failure state for every + * item in the list, we just recheck "does the raw write we must + * have made in write_list_to_cpustate() read back OK" here. + */ + uint64_t oldval = cpu->cpreg_values[i]; + + if (oldval == newval) { + continue; + } + + write_raw_cp_reg(&cpu->env, ri, oldval); + if (read_raw_cp_reg(&cpu->env, ri) != oldval) { + continue; + } + + write_raw_cp_reg(&cpu->env, ri, newval); + } + cpu->cpreg_values[i] = newval; } return ok; } @@ -8704,7 +8728,7 @@ static void do_v7m_exception_exit(ARMCPU *cpu) { CPUARMState *env = &cpu->env; uint32_t excret; - uint32_t xpsr; + uint32_t xpsr, xpsr_mask; bool ufault = false; bool sfault = false; bool return_to_sp_process; @@ -9156,8 +9180,13 @@ static void do_v7m_exception_exit(ARMCPU *cpu) } *frame_sp_p = frameptr; } + + xpsr_mask = ~(XPSR_SPREALIGN | XPSR_SFPA); + if (!arm_feature(env, ARM_FEATURE_THUMB_DSP)) { + xpsr_mask &= ~XPSR_GE; + } /* This xpsr_write() will invalidate frame_sp_p as it may switch stack */ - xpsr_write(env, xpsr, ~(XPSR_SPREALIGN | XPSR_SFPA)); + xpsr_write(env, xpsr, xpsr_mask); if (env->v7m.secure) { bool sfpa = xpsr & XPSR_SFPA; @@ -12642,6 +12671,9 @@ uint32_t HELPER(v7m_mrs)(CPUARMState *env, uint32_t reg) } if (!(reg & 4)) { mask |= XPSR_NZCV | XPSR_Q; /* APSR */ + if (arm_feature(env, ARM_FEATURE_THUMB_DSP)) { + mask |= XPSR_GE; + } } /* EPSR reads as zero */ return xpsr_read(env) & mask; @@ -13099,14 +13131,17 @@ void HELPER(dc_zva)(CPUARMState *env, uint64_t vaddr_in) * We know that in fact for any v8 CPU the page size is at least 4K * and the block size must be 2K or less, but TARGET_PAGE_SIZE is only * 1K as an artefact of legacy v5 subpage support being present in the - * same QEMU executable. + * same QEMU executable. So in practice the hostaddr[] array has + * two entries, given the current setting of TARGET_PAGE_BITS_MIN. */ int maxidx = DIV_ROUND_UP(blocklen, TARGET_PAGE_SIZE); - void *hostaddr[maxidx]; + void *hostaddr[DIV_ROUND_UP(2 * KiB, 1 << TARGET_PAGE_BITS_MIN)]; int try, i; unsigned mmu_idx = cpu_mmu_index(env, false); TCGMemOpIdx oi = make_memop_idx(MO_UB, mmu_idx); + assert(maxidx <= ARRAY_SIZE(hostaddr)); + for (try = 0; try < 2; try++) { for (i = 0; i < maxidx; i++) { diff --git a/target/arm/kvm.c b/target/arm/kvm.c index 79a79f0190..5995634612 100644 --- a/target/arm/kvm.c +++ b/target/arm/kvm.c @@ -497,6 +497,14 @@ void kvm_arm_reset_vcpu(ARMCPU *cpu) fprintf(stderr, "write_kvmstate_to_list failed\n"); abort(); } + /* + * Sync the reset values also into the CPUState. This is necessary + * because the next thing we do will be a kvm_arch_put_registers() + * which will update the list values from the CPUState before copying + * the list values back to KVM. It's OK to ignore failure returns here + * for the same reason we do so in kvm_arch_get_registers(). + */ + write_list_to_cpustate(cpu); } /* diff --git a/target/arm/kvm32.c b/target/arm/kvm32.c index 50327989dc..327375f625 100644 --- a/target/arm/kvm32.c +++ b/target/arm/kvm32.c @@ -384,24 +384,8 @@ int kvm_arch_put_registers(CPUState *cs, int level) return ret; } - /* Note that we do not call write_cpustate_to_list() - * here, so we are only writing the tuple list back to - * KVM. This is safe because nothing can change the - * CPUARMState cp15 fields (in particular gdb accesses cannot) - * and so there are no changes to sync. In fact syncing would - * be wrong at this point: for a constant register where TCG and - * KVM disagree about its value, the preceding write_list_to_cpustate() - * would not have had any effect on the CPUARMState value (since the - * register is read-only), and a write_cpustate_to_list() here would - * then try to write the TCG value back into KVM -- this would either - * fail or incorrectly change the value the guest sees. - * - * If we ever want to allow the user to modify cp15 registers via - * the gdb stub, we would need to be more clever here (for instance - * tracking the set of registers kvm_arch_get_registers() successfully - * managed to update the CPUARMState with, and only allowing those - * to be written back up into the kernel). - */ + write_cpustate_to_list(cpu, true); + if (!write_list_to_kvmstate(cpu, level)) { return EINVAL; } diff --git a/target/arm/kvm64.c b/target/arm/kvm64.c index 089af9c5f0..e3ba149248 100644 --- a/target/arm/kvm64.c +++ b/target/arm/kvm64.c @@ -838,6 +838,8 @@ int kvm_arch_put_registers(CPUState *cs, int level) return ret; } + write_cpustate_to_list(cpu, true); + if (!write_list_to_kvmstate(cpu, level)) { return EINVAL; } diff --git a/target/arm/machine.c b/target/arm/machine.c index 09567d4fc6..96d032f2a7 100644 --- a/target/arm/machine.c +++ b/target/arm/machine.c @@ -646,7 +646,7 @@ static int cpu_pre_save(void *opaque) abort(); } } else { - if (!write_cpustate_to_list(cpu)) { + if (!write_cpustate_to_list(cpu, false)) { /* This should never fail. */ abort(); } diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index 245cd82621..80645db508 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -54,35 +54,35 @@ typedef void gen_helper_gvec_mem_scatter(TCGv_env, TCGv_ptr, TCGv_ptr, /* See e.g. ASR (immediate, predicated). * Returns -1 for unallocated encoding; diagnose later. */ -static int tszimm_esz(int x) +static int tszimm_esz(DisasContext *s, int x) { x >>= 3; /* discard imm3 */ return 31 - clz32(x); } -static int tszimm_shr(int x) +static int tszimm_shr(DisasContext *s, int x) { - return (16 << tszimm_esz(x)) - x; + return (16 << tszimm_esz(s, x)) - x; } /* See e.g. LSL (immediate, predicated). */ -static int tszimm_shl(int x) +static int tszimm_shl(DisasContext *s, int x) { - return x - (8 << tszimm_esz(x)); + return x - (8 << tszimm_esz(s, x)); } -static inline int plus1(int x) +static inline int plus1(DisasContext *s, int x) { return x + 1; } /* The SH bit is in bit 8. Extract the low 8 and shift. */ -static inline int expand_imm_sh8s(int x) +static inline int expand_imm_sh8s(DisasContext *s, int x) { return (int8_t)x << (x & 0x100 ? 8 : 0); } -static inline int expand_imm_sh8u(int x) +static inline int expand_imm_sh8u(DisasContext *s, int x) { return (uint8_t)x << (x & 0x100 ? 8 : 0); } @@ -90,7 +90,7 @@ static inline int expand_imm_sh8u(int x) /* Convert a 2-bit memory size (msz) to a 4-bit data type (dtype) * with unsigned data. C.f. SVE Memory Contiguous Load Group. */ -static inline int msz_dtype(int msz) +static inline int msz_dtype(DisasContext *s, int msz) { static const uint8_t dtype[4] = { 0, 5, 10, 15 }; return dtype[msz]; @@ -4834,7 +4834,7 @@ static void do_ldrq(DisasContext *s, int zt, int pg, TCGv_i64 addr, int msz) int desc, poff; /* Load the first quadword using the normal predicated load helpers. */ - desc = sve_memopidx(s, msz_dtype(msz)); + desc = sve_memopidx(s, msz_dtype(s, msz)); desc |= zt << MEMOPIDX_SHIFT; desc = simd_desc(16, 16, desc); t_desc = tcg_const_i32(desc); @@ -5016,7 +5016,7 @@ static void do_st_zpa(DisasContext *s, int zt, int pg, TCGv_i64 addr, fn = fn_multiple[be][nreg - 1][msz]; } assert(fn != NULL); - do_mem_zpa(s, zt, pg, addr, msz_dtype(msz), fn); + do_mem_zpa(s, zt, pg, addr, msz_dtype(s, msz), fn); } static bool trans_ST_zprr(DisasContext *s, arg_rprr_store *a) @@ -5065,7 +5065,7 @@ static void do_mem_zpz(DisasContext *s, int zt, int pg, int zm, TCGv_i32 t_desc; int desc; - desc = sve_memopidx(s, msz_dtype(msz)); + desc = sve_memopidx(s, msz_dtype(s, msz)); desc |= scale << MEMOPIDX_SHIFT; desc = simd_desc(vsz, vsz, desc); t_desc = tcg_const_i32(desc); diff --git a/target/hppa/translate.c b/target/hppa/translate.c index e1febdfea1..188fe688cb 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -279,7 +279,7 @@ typedef struct DisasContext { } DisasContext; /* Note that ssm/rsm instructions number PSW_W and PSW_E differently. */ -static int expand_sm_imm(int val) +static int expand_sm_imm(DisasContext *ctx, int val) { if (val & PSW_SM_E) { val = (val & ~PSW_SM_E) | PSW_E; @@ -291,43 +291,43 @@ static int expand_sm_imm(int val) } /* Inverted space register indicates 0 means sr0 not inferred from base. */ -static int expand_sr3x(int val) +static int expand_sr3x(DisasContext *ctx, int val) { return ~val; } /* Convert the M:A bits within a memory insn to the tri-state value we use for the final M. */ -static int ma_to_m(int val) +static int ma_to_m(DisasContext *ctx, int val) { return val & 2 ? (val & 1 ? -1 : 1) : 0; } /* Convert the sign of the displacement to a pre or post-modify. */ -static int pos_to_m(int val) +static int pos_to_m(DisasContext *ctx, int val) { return val ? 1 : -1; } -static int neg_to_m(int val) +static int neg_to_m(DisasContext *ctx, int val) { return val ? -1 : 1; } /* Used for branch targets and fp memory ops. */ -static int expand_shl2(int val) +static int expand_shl2(DisasContext *ctx, int val) { return val << 2; } /* Used for fp memory ops. */ -static int expand_shl3(int val) +static int expand_shl3(DisasContext *ctx, int val) { return val << 3; } /* Used for assemble_21. */ -static int expand_shl11(int val) +static int expand_shl11(DisasContext *ctx, int val) { return val << 11; } diff --git a/target/openrisc/cpu.h b/target/openrisc/cpu.h index a50861955a..88a8c70092 100644 --- a/target/openrisc/cpu.h +++ b/target/openrisc/cpu.h @@ -6,7 +6,7 @@ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/target/openrisc/exception.c b/target/openrisc/exception.c index 49470be051..28c1fce523 100644 --- a/target/openrisc/exception.c +++ b/target/openrisc/exception.c @@ -6,7 +6,7 @@ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/target/openrisc/exception_helper.c b/target/openrisc/exception_helper.c index 6073a5b21c..0797cc9d38 100644 --- a/target/openrisc/exception_helper.c +++ b/target/openrisc/exception_helper.c @@ -6,7 +6,7 @@ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/target/openrisc/fpu_helper.c b/target/openrisc/fpu_helper.c index 265ce13337..b9d2ebbb8c 100644 --- a/target/openrisc/fpu_helper.c +++ b/target/openrisc/fpu_helper.c @@ -7,7 +7,7 @@ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/target/openrisc/insns.decode b/target/openrisc/insns.decode index dad68c8422..7df81c1f22 100644 --- a/target/openrisc/insns.decode +++ b/target/openrisc/insns.decode @@ -6,7 +6,7 @@ # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either -# version 2 of the License, or (at your option) any later version. +# version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/target/openrisc/interrupt.c b/target/openrisc/interrupt.c index bbae956361..ee280df895 100644 --- a/target/openrisc/interrupt.c +++ b/target/openrisc/interrupt.c @@ -6,7 +6,7 @@ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/target/openrisc/machine.c b/target/openrisc/machine.c index 5d822f7ab1..c9e084814c 100644 --- a/target/openrisc/machine.c +++ b/target/openrisc/machine.c @@ -6,7 +6,7 @@ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/target/openrisc/mmu.c b/target/openrisc/mmu.c index e7d5219e11..5dec68dcff 100644 --- a/target/openrisc/mmu.c +++ b/target/openrisc/mmu.c @@ -7,7 +7,7 @@ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/target/riscv/insn_trans/trans_rvc.inc.c b/target/riscv/insn_trans/trans_rvc.inc.c index ebcd977b2f..3e5d6fd5ea 100644 --- a/target/riscv/insn_trans/trans_rvc.inc.c +++ b/target/riscv/insn_trans/trans_rvc.inc.c @@ -48,13 +48,13 @@ static bool trans_c_flw_ld(DisasContext *ctx, arg_c_flw_ld *a) REQUIRE_EXT(ctx, RVF); arg_c_lw tmp; - decode_insn16_extract_cl_w(&tmp, ctx->opcode); + decode_insn16_extract_cl_w(ctx, &tmp, ctx->opcode); arg_flw arg = { .rd = tmp.rd, .rs1 = tmp.rs1, .imm = tmp.uimm }; return trans_flw(ctx, &arg); #else /* C.LD ( RV64C/RV128C-only ) */ arg_c_fld tmp; - decode_insn16_extract_cl_d(&tmp, ctx->opcode); + decode_insn16_extract_cl_d(ctx, &tmp, ctx->opcode); arg_ld arg = { .rd = tmp.rd, .rs1 = tmp.rs1, .imm = tmp.uimm }; return trans_ld(ctx, &arg); #endif @@ -80,13 +80,13 @@ static bool trans_c_fsw_sd(DisasContext *ctx, arg_c_fsw_sd *a) REQUIRE_EXT(ctx, RVF); arg_c_sw tmp; - decode_insn16_extract_cs_w(&tmp, ctx->opcode); + decode_insn16_extract_cs_w(ctx, &tmp, ctx->opcode); arg_fsw arg = { .rs1 = tmp.rs1, .rs2 = tmp.rs2, .imm = tmp.uimm }; return trans_fsw(ctx, &arg); #else /* C.SD ( RV64C/RV128C-only ) */ arg_c_fsd tmp; - decode_insn16_extract_cs_d(&tmp, ctx->opcode); + decode_insn16_extract_cs_d(ctx, &tmp, ctx->opcode); arg_sd arg = { .rs1 = tmp.rs1, .rs2 = tmp.rs2, .imm = tmp.uimm }; return trans_sd(ctx, &arg); #endif @@ -107,7 +107,7 @@ static bool trans_c_jal_addiw(DisasContext *ctx, arg_c_jal_addiw *a) #ifdef TARGET_RISCV32 /* C.JAL */ arg_c_j tmp; - decode_insn16_extract_cj(&tmp, ctx->opcode); + decode_insn16_extract_cj(ctx, &tmp, ctx->opcode); arg_jal arg = { .rd = 1, .imm = tmp.imm }; return trans_jal(ctx, &arg); #else diff --git a/target/riscv/translate.c b/target/riscv/translate.c index 967eac7bc3..2ff6b49487 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -517,7 +517,7 @@ static void decode_RV32_64C(DisasContext *ctx) } #define EX_SH(amount) \ - static int ex_shift_##amount(int imm) \ + static int ex_shift_##amount(DisasContext *ctx, int imm) \ { \ return imm << amount; \ } @@ -533,7 +533,7 @@ EX_SH(12) } \ } while (0) -static int ex_rvc_register(int reg) +static int ex_rvc_register(DisasContext *ctx, int reg) { return 8 + reg; } diff --git a/target/sh4/cpu.h b/target/sh4/cpu.h index 84b08ff640..1be36fe875 100644 --- a/target/sh4/cpu.h +++ b/target/sh4/cpu.h @@ -6,7 +6,7 @@ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/target/sh4/gdbstub.c b/target/sh4/gdbstub.c index 13bea00d7d..54568e96f9 100644 --- a/target/sh4/gdbstub.c +++ b/target/sh4/gdbstub.c @@ -7,7 +7,7 @@ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/target/sh4/helper.c b/target/sh4/helper.c index 2ff0cf4060..fa51269fb1 100644 --- a/target/sh4/helper.c +++ b/target/sh4/helper.c @@ -6,7 +6,7 @@ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/target/sh4/op_helper.c b/target/sh4/op_helper.c index 4f825bae5a..28027f9e0b 100644 --- a/target/sh4/op_helper.c +++ b/target/sh4/op_helper.c @@ -6,7 +6,7 @@ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/target/sh4/translate.c b/target/sh4/translate.c index cdf0888490..5a7d8c4535 100644 --- a/target/sh4/translate.c +++ b/target/sh4/translate.c @@ -6,7 +6,7 @@ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/tests/Makefile.include b/tests/Makefile.include index 7c8b9c84b2..60de085ee1 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -482,25 +482,6 @@ GENERATED_FILES += tests/test-qapi-types.h \ tests/test-qapi-events-sub-sub-module.h \ tests/test-qapi-introspect.h -test-obj-y = tests/check-qnum.o tests/check-qstring.o tests/check-qdict.o \ - tests/check-qlist.o tests/check-qnull.o tests/check-qobject.o \ - tests/check-qjson.o tests/check-qlit.o \ - tests/check-block-qtest.o \ - tests/test-coroutine.o tests/test-string-output-visitor.o \ - tests/test-string-input-visitor.o tests/test-qobject-output-visitor.o \ - tests/test-clone-visitor.o \ - tests/test-qobject-input-visitor.o \ - tests/test-qmp-cmds.o tests/test-visitor-serialization.o \ - tests/test-x86-cpuid.o tests/test-mul64.o tests/test-int128.o \ - tests/test-opts-visitor.o tests/test-qmp-event.o \ - tests/rcutorture.o tests/test-rcu-list.o \ - tests/test-rcu-simpleq.o \ - tests/test-rcu-tailq.o \ - tests/test-qdist.o tests/test-shift128.o \ - tests/test-qht.o tests/qht-bench.o tests/test-qht-par.o \ - tests/atomic_add-bench.o tests/atomic64-bench.o - -$(test-obj-y): QEMU_INCLUDES += -Itests QEMU_CFLAGS += -I$(SRC_PATH)/tests @@ -1103,7 +1084,7 @@ check-tests/qemu-iotests-quick.sh: tests/qemu-iotests-quick.sh qemu-img$(EXESUF) .PHONY: $(patsubst %, check-%, $(check-qapi-schema-y)) $(patsubst %, check-%, $(check-qapi-schema-y)): check-%.json: $(SRC_PATH)/%.json $(call quiet-command, PYTHONPATH=$(SRC_PATH)/scripts \ - $(PYTHON) $(SRC_PATH)/tests/qapi-schema/test-qapi.py \ + PYTHONIOENCODING=utf-8 $(PYTHON) $(SRC_PATH)/tests/qapi-schema/test-qapi.py \ $^ >$*.test.out 2>$*.test.err; \ echo $$? >$*.test.exit, \ "TEST","$*.out") diff --git a/tests/drive_del-test.c b/tests/drive_del-test.c index 2f9474e03c..b56b223fc2 100644 --- a/tests/drive_del-test.c +++ b/tests/drive_del-test.c @@ -16,32 +16,32 @@ #include "qapi/qmp/qdict.h" /* TODO actually test the results and get rid of this */ -#define qmp_discard_response(...) qobject_unref(qmp(__VA_ARGS__)) +#define qmp_discard_response(q, ...) qobject_unref(qtest_qmp(q, __VA_ARGS__)) -static void drive_add(void) +static void drive_add(QTestState *qts) { - char *resp = hmp("drive_add 0 if=none,id=drive0"); + char *resp = qtest_hmp(qts, "drive_add 0 if=none,id=drive0"); g_assert_cmpstr(resp, ==, "OK\r\n"); g_free(resp); } -static void drive_del(void) +static void drive_del(QTestState *qts) { - char *resp = hmp("drive_del drive0"); + char *resp = qtest_hmp(qts, "drive_del drive0"); g_assert_cmpstr(resp, ==, ""); g_free(resp); } -static void device_del(void) +static void device_del(QTestState *qts) { QDict *response; /* Complication: ignore DEVICE_DELETED event */ - qmp_discard_response("{'execute': 'device_del'," + qmp_discard_response(qts, "{'execute': 'device_del'," " 'arguments': { 'id': 'dev0' } }"); - response = qmp_receive(); + response = qtest_qmp_receive(qts); g_assert(response); g_assert(qdict_haskey(response, "return")); qobject_unref(response); @@ -49,18 +49,20 @@ static void device_del(void) static void test_drive_without_dev(void) { + QTestState *qts; + /* Start with an empty drive */ - qtest_start("-drive if=none,id=drive0"); + qts = qtest_init("-drive if=none,id=drive0"); /* Delete the drive */ - drive_del(); + drive_del(qts); /* Ensure re-adding the drive works - there should be no duplicate ID error * because the old drive must be gone. */ - drive_add(); + drive_add(qts); - qtest_end(); + qtest_quit(qts); } /* @@ -85,54 +87,53 @@ static void test_after_failed_device_add(void) { char driver[32]; QDict *response; + QTestState *qts; snprintf(driver, sizeof(driver), "virtio-blk-%s", qvirtio_get_dev_type()); - qtest_start("-drive if=none,id=drive0"); + qts = qtest_init("-drive if=none,id=drive0"); /* Make device_add fail. If this leaks the virtio-blk device then a * reference to drive0 will also be held (via qdev properties). */ - response = qmp("{'execute': 'device_add'," - " 'arguments': {" - " 'driver': %s," - " 'drive': 'drive0'" - "}}", driver); + response = qtest_qmp(qts, "{'execute': 'device_add'," + " 'arguments': {" + " 'driver': %s," + " 'drive': 'drive0'" + "}}", driver); g_assert(response); qmp_assert_error_class(response, "GenericError"); /* Delete the drive */ - drive_del(); + drive_del(qts); /* Try to re-add the drive. This fails with duplicate IDs if a leaked * virtio-blk device exists that holds a reference to the old drive0. */ - drive_add(); + drive_add(qts); - qtest_end(); + qtest_quit(qts); } static void test_drive_del_device_del(void) { - char *args; + QTestState *qts; /* Start with a drive used by a device that unplugs instantaneously */ - args = g_strdup_printf("-drive if=none,id=drive0,file=null-co://,format=raw" - " -device virtio-scsi-%s" - " -device scsi-hd,drive=drive0,id=dev0", - qvirtio_get_dev_type()); - qtest_start(args); + qts = qtest_initf("-drive if=none,id=drive0,file=null-co://,format=raw" + " -device virtio-scsi-%s" + " -device scsi-hd,drive=drive0,id=dev0", + qvirtio_get_dev_type()); /* * Delete the drive, and then the device * Doing it in this order takes notoriously tricky special paths */ - drive_del(); - device_del(); + drive_del(qts); + device_del(qts); - qtest_end(); - g_free(args); + qtest_quit(qts); } int main(int argc, char **argv) diff --git a/tests/e1000e-test.c b/tests/e1000e-test.c index 77ba8095bb..6a946c0484 100644 --- a/tests/e1000e-test.c +++ b/tests/e1000e-test.c @@ -231,8 +231,10 @@ static void test_e1000e_multiple_transfers(void *obj, void *data, static void test_e1000e_hotplug(void *obj, void *data, QGuestAllocator * alloc) { + QTestState *qts = global_qtest; /* TODO: get rid of global_qtest here */ + qtest_qmp_device_add("e1000e", "e1000e_net", "{'addr': '0x06'}"); - qpci_unplug_acpi_device_test("e1000e_net", 0x06); + qpci_unplug_acpi_device_test(qts, "e1000e_net", 0x06); } static void data_test_clear(void *sockets) diff --git a/tests/ide-test.c b/tests/ide-test.c index d863a99f7f..0277e7d5a9 100644 --- a/tests/ide-test.c +++ b/tests/ide-test.c @@ -36,7 +36,7 @@ #include "hw/pci/pci_regs.h" /* TODO actually test the results and get rid of this */ -#define qmp_discard_response(...) qobject_unref(qmp(__VA_ARGS__)) +#define qmp_discard_response(q, ...) qobject_unref(qtest_qmp(q, __VA_ARGS__)) #define TEST_IMAGE_SIZE 64 * 1024 * 1024 @@ -125,38 +125,38 @@ static QGuestAllocator guest_malloc; static char tmp_path[] = "/tmp/qtest.XXXXXX"; static char debug_path[] = "/tmp/qtest-blkdebug.XXXXXX"; -static void ide_test_start(const char *cmdline_fmt, ...) +static QTestState *ide_test_start(const char *cmdline_fmt, ...) { + QTestState *qts; va_list ap; - char *cmdline; va_start(ap, cmdline_fmt); - cmdline = g_strdup_vprintf(cmdline_fmt, ap); + qts = qtest_vinitf(cmdline_fmt, ap); va_end(ap); - qtest_start(cmdline); - pc_alloc_init(&guest_malloc, global_qtest, 0); + pc_alloc_init(&guest_malloc, qts, 0); - g_free(cmdline); + return qts; } -static void ide_test_quit(void) +static void ide_test_quit(QTestState *qts) { if (pcibus) { qpci_free_pc(pcibus); pcibus = NULL; } alloc_destroy(&guest_malloc); - qtest_end(); + qtest_quit(qts); } -static QPCIDevice *get_pci_device(QPCIBar *bmdma_bar, QPCIBar *ide_bar) +static QPCIDevice *get_pci_device(QTestState *qts, QPCIBar *bmdma_bar, + QPCIBar *ide_bar) { QPCIDevice *dev; uint16_t vendor_id, device_id; if (!pcibus) { - pcibus = qpci_new_pc(global_qtest, NULL); + pcibus = qpci_new_pc(qts, NULL); } /* Find PCI device and verify it's the right one */ @@ -198,8 +198,8 @@ static uint64_t trim_range_le(uint64_t sector, uint16_t count) return cpu_to_le64(((uint64_t)count << 48) + sector); } -static int send_dma_request(int cmd, uint64_t sector, int nb_sectors, - PrdtEntry *prdt, int prdt_entries, +static int send_dma_request(QTestState *qts, int cmd, uint64_t sector, + int nb_sectors, PrdtEntry *prdt, int prdt_entries, void(*post_exec)(QPCIDevice *dev, QPCIBar ide_bar, uint64_t sector, int nb_sectors)) { @@ -211,7 +211,7 @@ static int send_dma_request(int cmd, uint64_t sector, int nb_sectors, uint8_t status; int flags; - dev = get_pci_device(&bmdma_bar, &ide_bar); + dev = get_pci_device(qts, &bmdma_bar, &ide_bar); flags = cmd & ~0xff; cmd &= 0xff; @@ -246,7 +246,7 @@ static int send_dma_request(int cmd, uint64_t sector, int nb_sectors, /* Setup PRDT */ len = sizeof(*prdt) * prdt_entries; guest_prdt = guest_alloc(&guest_malloc, len); - memwrite(guest_prdt, prdt, len); + qtest_memwrite(qts, guest_prdt, prdt, len); qpci_io_writel(dev, bmdma_bar, bmreg_prdt, guest_prdt); /* ATA DMA command */ @@ -283,14 +283,15 @@ static int send_dma_request(int cmd, uint64_t sector, int nb_sectors, status = qpci_io_readb(dev, bmdma_bar, bmreg_status); } while ((status & (BM_STS_ACTIVE | BM_STS_INTR)) == BM_STS_ACTIVE); - g_assert_cmpint(get_irq(IDE_PRIMARY_IRQ), ==, !!(status & BM_STS_INTR)); + g_assert_cmpint(qtest_get_irq(qts, IDE_PRIMARY_IRQ), ==, + !!(status & BM_STS_INTR)); /* Check IDE status code */ assert_bit_set(qpci_io_readb(dev, ide_bar, reg_status), DRDY); assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), BSY | DRQ); /* Reading the status register clears the IRQ */ - g_assert(!get_irq(IDE_PRIMARY_IRQ)); + g_assert(!qtest_get_irq(qts, IDE_PRIMARY_IRQ)); /* Stop DMA transfer if still active */ if (status & BM_STS_ACTIVE) { @@ -302,42 +303,61 @@ static int send_dma_request(int cmd, uint64_t sector, int nb_sectors, return status; } +static QTestState *test_bmdma_setup(void) +{ + QTestState *qts; + + qts = ide_test_start( + "-drive file=%s,if=ide,cache=writeback,format=raw " + "-global ide-hd.serial=%s -global ide-hd.ver=%s", + tmp_path, "testdisk", "version"); + qtest_irq_intercept_in(qts, "ioapic"); + + return qts; +} + +static void test_bmdma_teardown(QTestState *qts) +{ + ide_test_quit(qts); +} + static void test_bmdma_simple_rw(void) { + QTestState *qts; QPCIDevice *dev; QPCIBar bmdma_bar, ide_bar; uint8_t status; uint8_t *buf; uint8_t *cmpbuf; size_t len = 512; - uintptr_t guest_buf = guest_alloc(&guest_malloc, len); + uintptr_t guest_buf; + PrdtEntry prdt[1]; - PrdtEntry prdt[] = { - { - .addr = cpu_to_le32(guest_buf), - .size = cpu_to_le32(len | PRDT_EOT), - }, - }; + qts = test_bmdma_setup(); + + guest_buf = guest_alloc(&guest_malloc, len); + prdt[0].addr = cpu_to_le32(guest_buf); + prdt[0].size = cpu_to_le32(len | PRDT_EOT); - dev = get_pci_device(&bmdma_bar, &ide_bar); + dev = get_pci_device(qts, &bmdma_bar, &ide_bar); buf = g_malloc(len); cmpbuf = g_malloc(len); /* Write 0x55 pattern to sector 0 */ memset(buf, 0x55, len); - memwrite(guest_buf, buf, len); + qtest_memwrite(qts, guest_buf, buf, len); - status = send_dma_request(CMD_WRITE_DMA, 0, 1, prdt, + status = send_dma_request(qts, CMD_WRITE_DMA, 0, 1, prdt, ARRAY_SIZE(prdt), NULL); g_assert_cmphex(status, ==, BM_STS_INTR); assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR); /* Write 0xaa pattern to sector 1 */ memset(buf, 0xaa, len); - memwrite(guest_buf, buf, len); + qtest_memwrite(qts, guest_buf, buf, len); - status = send_dma_request(CMD_WRITE_DMA, 1, 1, prdt, + status = send_dma_request(qts, CMD_WRITE_DMA, 1, 1, prdt, ARRAY_SIZE(prdt), NULL); g_assert_cmphex(status, ==, BM_STS_INTR); assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR); @@ -345,31 +365,35 @@ static void test_bmdma_simple_rw(void) /* Read and verify 0x55 pattern in sector 0 */ memset(cmpbuf, 0x55, len); - status = send_dma_request(CMD_READ_DMA, 0, 1, prdt, ARRAY_SIZE(prdt), NULL); + status = send_dma_request(qts, CMD_READ_DMA, 0, 1, prdt, ARRAY_SIZE(prdt), + NULL); g_assert_cmphex(status, ==, BM_STS_INTR); assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR); - memread(guest_buf, buf, len); + qtest_memread(qts, guest_buf, buf, len); g_assert(memcmp(buf, cmpbuf, len) == 0); /* Read and verify 0xaa pattern in sector 1 */ memset(cmpbuf, 0xaa, len); - status = send_dma_request(CMD_READ_DMA, 1, 1, prdt, ARRAY_SIZE(prdt), NULL); + status = send_dma_request(qts, CMD_READ_DMA, 1, 1, prdt, ARRAY_SIZE(prdt), + NULL); g_assert_cmphex(status, ==, BM_STS_INTR); assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR); - memread(guest_buf, buf, len); + qtest_memread(qts, guest_buf, buf, len); g_assert(memcmp(buf, cmpbuf, len) == 0); - free_pci_device(dev); g_free(buf); g_free(cmpbuf); + + test_bmdma_teardown(qts); } static void test_bmdma_trim(void) { + QTestState *qts; QPCIDevice *dev; QPCIBar bmdma_bar, ide_bar; uint8_t status; @@ -380,16 +404,16 @@ static void test_bmdma_trim(void) const uint64_t bad_range = trim_range_le(TEST_IMAGE_SIZE / 512 - 1, 2); size_t len = 512; uint8_t *buf; - uintptr_t guest_buf = guest_alloc(&guest_malloc, len); + uintptr_t guest_buf; + PrdtEntry prdt[1]; - PrdtEntry prdt[] = { - { - .addr = cpu_to_le32(guest_buf), - .size = cpu_to_le32(len | PRDT_EOT), - }, - }; + qts = test_bmdma_setup(); + + guest_buf = guest_alloc(&guest_malloc, len); + prdt[0].addr = cpu_to_le32(guest_buf), + prdt[0].size = cpu_to_le32(len | PRDT_EOT), - dev = get_pci_device(&bmdma_bar, &ide_bar); + dev = get_pci_device(qts, &bmdma_bar, &ide_bar); buf = g_malloc(len); @@ -397,9 +421,9 @@ static void test_bmdma_trim(void) *((uint64_t *)buf) = trim_range[0]; *((uint64_t *)buf + 1) = trim_range[1]; - memwrite(guest_buf, buf, 2 * sizeof(uint64_t)); + qtest_memwrite(qts, guest_buf, buf, 2 * sizeof(uint64_t)); - status = send_dma_request(CMD_DSM, 0, 1, prdt, + status = send_dma_request(qts, CMD_DSM, 0, 1, prdt, ARRAY_SIZE(prdt), NULL); g_assert_cmphex(status, ==, BM_STS_INTR); assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR); @@ -408,9 +432,9 @@ static void test_bmdma_trim(void) *((uint64_t *)buf) = trim_range[2]; *((uint64_t *)buf + 1) = bad_range; - memwrite(guest_buf, buf, 2 * sizeof(uint64_t)); + qtest_memwrite(qts, guest_buf, buf, 2 * sizeof(uint64_t)); - status = send_dma_request(CMD_DSM, 0, 1, prdt, + status = send_dma_request(qts, CMD_DSM, 0, 1, prdt, ARRAY_SIZE(prdt), NULL); g_assert_cmphex(status, ==, BM_STS_INTR); assert_bit_set(qpci_io_readb(dev, ide_bar, reg_status), ERR); @@ -418,10 +442,12 @@ static void test_bmdma_trim(void) free_pci_device(dev); g_free(buf); + test_bmdma_teardown(qts); } static void test_bmdma_short_prdt(void) { + QTestState *qts; QPCIDevice *dev; QPCIBar bmdma_bar, ide_bar; uint8_t status; @@ -433,24 +459,28 @@ static void test_bmdma_short_prdt(void) }, }; - dev = get_pci_device(&bmdma_bar, &ide_bar); + qts = test_bmdma_setup(); + + dev = get_pci_device(qts, &bmdma_bar, &ide_bar); /* Normal request */ - status = send_dma_request(CMD_READ_DMA, 0, 1, + status = send_dma_request(qts, CMD_READ_DMA, 0, 1, prdt, ARRAY_SIZE(prdt), NULL); g_assert_cmphex(status, ==, 0); assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR); /* Abort the request before it completes */ - status = send_dma_request(CMD_READ_DMA | CMDF_ABORT, 0, 1, + status = send_dma_request(qts, CMD_READ_DMA | CMDF_ABORT, 0, 1, prdt, ARRAY_SIZE(prdt), NULL); g_assert_cmphex(status, ==, 0); assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR); free_pci_device(dev); + test_bmdma_teardown(qts); } static void test_bmdma_one_sector_short_prdt(void) { + QTestState *qts; QPCIDevice *dev; QPCIBar bmdma_bar, ide_bar; uint8_t status; @@ -463,24 +493,28 @@ static void test_bmdma_one_sector_short_prdt(void) }, }; - dev = get_pci_device(&bmdma_bar, &ide_bar); + qts = test_bmdma_setup(); + + dev = get_pci_device(qts, &bmdma_bar, &ide_bar); /* Normal request */ - status = send_dma_request(CMD_READ_DMA, 0, 2, + status = send_dma_request(qts, CMD_READ_DMA, 0, 2, prdt, ARRAY_SIZE(prdt), NULL); g_assert_cmphex(status, ==, 0); assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR); /* Abort the request before it completes */ - status = send_dma_request(CMD_READ_DMA | CMDF_ABORT, 0, 2, + status = send_dma_request(qts, CMD_READ_DMA | CMDF_ABORT, 0, 2, prdt, ARRAY_SIZE(prdt), NULL); g_assert_cmphex(status, ==, 0); assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR); free_pci_device(dev); + test_bmdma_teardown(qts); } static void test_bmdma_long_prdt(void) { + QTestState *qts; QPCIDevice *dev; QPCIBar bmdma_bar, ide_bar; uint8_t status; @@ -492,29 +526,35 @@ static void test_bmdma_long_prdt(void) }, }; - dev = get_pci_device(&bmdma_bar, &ide_bar); + qts = test_bmdma_setup(); + + dev = get_pci_device(qts, &bmdma_bar, &ide_bar); /* Normal request */ - status = send_dma_request(CMD_READ_DMA, 0, 1, + status = send_dma_request(qts, CMD_READ_DMA, 0, 1, prdt, ARRAY_SIZE(prdt), NULL); g_assert_cmphex(status, ==, BM_STS_ACTIVE | BM_STS_INTR); assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR); /* Abort the request before it completes */ - status = send_dma_request(CMD_READ_DMA | CMDF_ABORT, 0, 1, + status = send_dma_request(qts, CMD_READ_DMA | CMDF_ABORT, 0, 1, prdt, ARRAY_SIZE(prdt), NULL); g_assert_cmphex(status, ==, BM_STS_INTR); assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR); free_pci_device(dev); + test_bmdma_teardown(qts); } static void test_bmdma_no_busmaster(void) { + QTestState *qts; QPCIDevice *dev; QPCIBar bmdma_bar, ide_bar; uint8_t status; - dev = get_pci_device(&bmdma_bar, &ide_bar); + qts = test_bmdma_setup(); + + dev = get_pci_device(qts, &bmdma_bar, &ide_bar); /* No PRDT_EOT, each entry addr 0/size 64k, and in theory qemu shouldn't be * able to access it anyway because the Bus Master bit in the PCI command @@ -522,7 +562,7 @@ static void test_bmdma_no_busmaster(void) * good at confusing and occasionally crashing qemu. */ PrdtEntry prdt[4096] = { }; - status = send_dma_request(CMD_READ_DMA | CMDF_NO_BM, 0, 512, + status = send_dma_request(qts, CMD_READ_DMA | CMDF_NO_BM, 0, 512, prdt, ARRAY_SIZE(prdt), NULL); /* Not entirely clear what the expected result is, but this is what we get @@ -530,20 +570,7 @@ static void test_bmdma_no_busmaster(void) g_assert_cmphex(status, ==, BM_STS_ACTIVE | BM_STS_INTR); assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR); free_pci_device(dev); -} - -static void test_bmdma_setup(void) -{ - ide_test_start( - "-drive file=%s,if=ide,cache=writeback,format=raw " - "-global ide-hd.serial=%s -global ide-hd.ver=%s", - tmp_path, "testdisk", "version"); - qtest_irq_intercept_in(global_qtest, "ioapic"); -} - -static void test_bmdma_teardown(void) -{ - ide_test_quit(); + test_bmdma_teardown(qts); } static void string_cpu_to_be16(uint16_t *s, size_t bytes) @@ -559,6 +586,7 @@ static void string_cpu_to_be16(uint16_t *s, size_t bytes) static void test_identify(void) { + QTestState *qts; QPCIDevice *dev; QPCIBar bmdma_bar, ide_bar; uint8_t data; @@ -566,12 +594,12 @@ static void test_identify(void) int i; int ret; - ide_test_start( + qts = ide_test_start( "-drive file=%s,if=ide,cache=writeback,format=raw " "-global ide-hd.serial=%s -global ide-hd.ver=%s", tmp_path, "testdisk", "version"); - dev = get_pci_device(&bmdma_bar, &ide_bar); + dev = get_pci_device(qts, &bmdma_bar, &ide_bar); /* IDENTIFY command on device 0*/ qpci_io_writeb(dev, ide_bar, reg_device, 0); @@ -605,7 +633,7 @@ static void test_identify(void) /* Write cache enabled bit */ assert_bit_set(buf[85], 0x20); - ide_test_quit(); + ide_test_quit(qts); free_pci_device(dev); } @@ -613,7 +641,7 @@ static void test_identify(void) * Write sector 1 with random data to make IDE storage dirty * Needed for flush tests so that flushes actually go though the block layer */ -static void make_dirty(uint8_t device) +static void make_dirty(QTestState *qts, uint8_t device) { QPCIDevice *dev; QPCIBar bmdma_bar, ide_bar; @@ -622,7 +650,7 @@ static void make_dirty(uint8_t device) uintptr_t guest_buf; void* buf; - dev = get_pci_device(&bmdma_bar, &ide_bar); + dev = get_pci_device(qts, &bmdma_bar, &ide_bar); guest_buf = guest_alloc(&guest_malloc, len); buf = g_malloc(len); @@ -630,7 +658,7 @@ static void make_dirty(uint8_t device) g_assert(guest_buf); g_assert(buf); - memwrite(guest_buf, buf, len); + qtest_memwrite(qts, guest_buf, buf, len); PrdtEntry prdt[] = { { @@ -639,7 +667,7 @@ static void make_dirty(uint8_t device) }, }; - status = send_dma_request(CMD_WRITE_DMA, 1, 1, prdt, + status = send_dma_request(qts, CMD_WRITE_DMA, 1, 1, prdt, ARRAY_SIZE(prdt), NULL); g_assert_cmphex(status, ==, BM_STS_INTR); assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR); @@ -650,23 +678,24 @@ static void make_dirty(uint8_t device) static void test_flush(void) { + QTestState *qts; QPCIDevice *dev; QPCIBar bmdma_bar, ide_bar; uint8_t data; - ide_test_start( + qts = ide_test_start( "-drive file=blkdebug::%s,if=ide,cache=writeback,format=raw", tmp_path); - dev = get_pci_device(&bmdma_bar, &ide_bar); + dev = get_pci_device(qts, &bmdma_bar, &ide_bar); - qtest_irq_intercept_in(global_qtest, "ioapic"); + qtest_irq_intercept_in(qts, "ioapic"); /* Dirty media so that CMD_FLUSH_CACHE will actually go to disk */ - make_dirty(0); + make_dirty(qts, 0); /* Delay the completion of the flush request until we explicitly do it */ - g_free(hmp("qemu-io ide0-hd0 \"break flush_to_os A\"")); + g_free(qtest_hmp(qts, "qemu-io ide0-hd0 \"break flush_to_os A\"")); /* FLUSH CACHE command on device 0*/ qpci_io_writeb(dev, ide_bar, reg_device, 0); @@ -678,7 +707,7 @@ static void test_flush(void) assert_bit_clear(data, DF | ERR | DRQ); /* Complete the command */ - g_free(hmp("qemu-io ide0-hd0 \"resume A\"")); + g_free(qtest_hmp(qts, "qemu-io ide0-hd0 \"resume A\"")); /* Check registers */ data = qpci_io_readb(dev, ide_bar, reg_device); @@ -691,29 +720,30 @@ static void test_flush(void) assert_bit_set(data, DRDY); assert_bit_clear(data, BSY | DF | ERR | DRQ); - ide_test_quit(); + ide_test_quit(qts); free_pci_device(dev); } static void test_retry_flush(const char *machine) { + QTestState *qts; QPCIDevice *dev; QPCIBar bmdma_bar, ide_bar; uint8_t data; prepare_blkdebug_script(debug_path, "flush_to_disk"); - ide_test_start( + qts = ide_test_start( "-drive file=blkdebug:%s:%s,if=ide,cache=writeback,format=raw," "rerror=stop,werror=stop", debug_path, tmp_path); - dev = get_pci_device(&bmdma_bar, &ide_bar); + dev = get_pci_device(qts, &bmdma_bar, &ide_bar); - qtest_irq_intercept_in(global_qtest, "ioapic"); + qtest_irq_intercept_in(qts, "ioapic"); /* Dirty media so that CMD_FLUSH_CACHE will actually go to disk */ - make_dirty(0); + make_dirty(qts, 0); /* FLUSH CACHE command on device 0*/ qpci_io_writeb(dev, ide_bar, reg_device, 0); @@ -724,10 +754,10 @@ static void test_retry_flush(const char *machine) assert_bit_set(data, BSY | DRDY); assert_bit_clear(data, DF | ERR | DRQ); - qmp_eventwait("STOP"); + qtest_qmp_eventwait(qts, "STOP"); /* Complete the command */ - qmp_discard_response("{'execute':'cont' }"); + qmp_discard_response(qts, "{'execute':'cont' }"); /* Check registers */ data = qpci_io_readb(dev, ide_bar, reg_device); @@ -740,18 +770,19 @@ static void test_retry_flush(const char *machine) assert_bit_set(data, DRDY); assert_bit_clear(data, BSY | DF | ERR | DRQ); - ide_test_quit(); + ide_test_quit(qts); free_pci_device(dev); } static void test_flush_nodev(void) { + QTestState *qts; QPCIDevice *dev; QPCIBar bmdma_bar, ide_bar; - ide_test_start(""); + qts = ide_test_start(""); - dev = get_pci_device(&bmdma_bar, &ide_bar); + dev = get_pci_device(qts, &bmdma_bar, &ide_bar); /* FLUSH CACHE command on device 0*/ qpci_io_writeb(dev, ide_bar, reg_device, 0); @@ -760,16 +791,17 @@ static void test_flush_nodev(void) /* Just testing that qemu doesn't crash... */ free_pci_device(dev); - ide_test_quit(); + ide_test_quit(qts); } static void test_flush_empty_drive(void) { + QTestState *qts; QPCIDevice *dev; QPCIBar bmdma_bar, ide_bar; - ide_test_start("-device ide-cd,bus=ide.0"); - dev = get_pci_device(&bmdma_bar, &ide_bar); + qts = ide_test_start("-device ide-cd,bus=ide.0"); + dev = get_pci_device(qts, &bmdma_bar, &ide_bar); /* FLUSH CACHE command on device 0 */ qpci_io_writeb(dev, ide_bar, reg_device, 0); @@ -778,7 +810,7 @@ static void test_flush_empty_drive(void) /* Just testing that qemu doesn't crash... */ free_pci_device(dev); - ide_test_quit(); + ide_test_quit(qts); } static void test_pci_retry_flush(void) @@ -823,21 +855,21 @@ static void send_scsi_cdb_read10(QPCIDevice *dev, QPCIBar ide_bar, } } -static void nsleep(int64_t nsecs) +static void nsleep(QTestState *qts, int64_t nsecs) { const struct timespec val = { .tv_nsec = nsecs }; nanosleep(&val, NULL); - clock_set(nsecs); + qtest_clock_set(qts, nsecs); } -static uint8_t ide_wait_clear(uint8_t flag) +static uint8_t ide_wait_clear(QTestState *qts, uint8_t flag) { QPCIDevice *dev; QPCIBar bmdma_bar, ide_bar; uint8_t data; time_t st; - dev = get_pci_device(&bmdma_bar, &ide_bar); + dev = get_pci_device(qts, &bmdma_bar, &ide_bar); /* Wait with a 5 second timeout */ time(&st); @@ -850,26 +882,26 @@ static uint8_t ide_wait_clear(uint8_t flag) if (difftime(time(NULL), st) > 5.0) { break; } - nsleep(400); + nsleep(qts, 400); } g_assert_not_reached(); } -static void ide_wait_intr(int irq) +static void ide_wait_intr(QTestState *qts, int irq) { time_t st; bool intr; time(&st); while (true) { - intr = get_irq(irq); + intr = qtest_get_irq(qts, irq); if (intr) { return; } if (difftime(time(NULL), st) > 5.0) { break; } - nsleep(400); + nsleep(qts, 400); } g_assert_not_reached(); @@ -877,6 +909,7 @@ static void ide_wait_intr(int irq) static void cdrom_pio_impl(int nblocks) { + QTestState *qts; QPCIDevice *dev; QPCIBar bmdma_bar, ide_bar; FILE *fh; @@ -897,10 +930,11 @@ static void cdrom_pio_impl(int nblocks) g_assert_cmpint(ret, ==, patt_blocks); fclose(fh); - ide_test_start("-drive if=none,file=%s,media=cdrom,format=raw,id=sr0,index=0 " - "-device ide-cd,drive=sr0,bus=ide.0", tmp_path); - dev = get_pci_device(&bmdma_bar, &ide_bar); - qtest_irq_intercept_in(global_qtest, "ioapic"); + qts = ide_test_start( + "-drive if=none,file=%s,media=cdrom,format=raw,id=sr0,index=0 " + "-device ide-cd,drive=sr0,bus=ide.0", tmp_path); + dev = get_pci_device(qts, &bmdma_bar, &ide_bar); + qtest_irq_intercept_in(qts, "ioapic"); /* PACKET command on device 0 */ qpci_io_writeb(dev, ide_bar, reg_device, 0); @@ -908,8 +942,8 @@ static void cdrom_pio_impl(int nblocks) qpci_io_writeb(dev, ide_bar, reg_lba_high, (BYTE_COUNT_LIMIT >> 8 & 0xFF)); qpci_io_writeb(dev, ide_bar, reg_command, CMD_PACKET); /* HP0: Check_Status_A State */ - nsleep(400); - data = ide_wait_clear(BSY); + nsleep(qts, 400); + data = ide_wait_clear(qts, BSY); /* HP1: Send_Packet State */ assert_bit_set(data, DRQ | DRDY); assert_bit_clear(data, ERR | DF | BSY); @@ -930,10 +964,10 @@ static void cdrom_pio_impl(int nblocks) size_t rem = (rxsize / 2) - offset; /* HP3: INTRQ_Wait */ - ide_wait_intr(IDE_PRIMARY_IRQ); + ide_wait_intr(qts, IDE_PRIMARY_IRQ); /* HP2: Check_Status_B (and clear IRQ) */ - data = ide_wait_clear(BSY); + data = ide_wait_clear(qts, BSY); assert_bit_set(data, DRQ | DRDY); assert_bit_clear(data, ERR | DF | BSY); @@ -945,17 +979,17 @@ static void cdrom_pio_impl(int nblocks) } /* Check for final completion IRQ */ - ide_wait_intr(IDE_PRIMARY_IRQ); + ide_wait_intr(qts, IDE_PRIMARY_IRQ); /* Sanity check final state */ - data = ide_wait_clear(DRQ); + data = ide_wait_clear(qts, DRQ); assert_bit_set(data, DRDY); assert_bit_clear(data, DRQ | ERR | DF | BSY); g_assert_cmpint(memcmp(pattern, rx, rxsize), ==, 0); g_free(pattern); g_free(rx); - test_bmdma_teardown(); + test_bmdma_teardown(qts); free_pci_device(dev); } @@ -973,6 +1007,7 @@ static void test_cdrom_pio_large(void) static void test_cdrom_dma(void) { + QTestState *qts; static const size_t len = ATAPI_BLOCK_SIZE; size_t ret; char *pattern = g_malloc(ATAPI_BLOCK_SIZE * 16); @@ -981,9 +1016,10 @@ static void test_cdrom_dma(void) PrdtEntry prdt[1]; FILE *fh; - ide_test_start("-drive if=none,file=%s,media=cdrom,format=raw,id=sr0,index=0 " - "-device ide-cd,drive=sr0,bus=ide.0", tmp_path); - qtest_irq_intercept_in(global_qtest, "ioapic"); + qts = ide_test_start( + "-drive if=none,file=%s,media=cdrom,format=raw,id=sr0,index=0 " + "-device ide-cd,drive=sr0,bus=ide.0", tmp_path); + qtest_irq_intercept_in(qts, "ioapic"); guest_buf = guest_alloc(&guest_malloc, len); prdt[0].addr = cpu_to_le32(guest_buf); @@ -995,15 +1031,15 @@ static void test_cdrom_dma(void) g_assert_cmpint(ret, ==, 16); fclose(fh); - send_dma_request(CMD_PACKET, 0, 1, prdt, 1, send_scsi_cdb_read10); + send_dma_request(qts, CMD_PACKET, 0, 1, prdt, 1, send_scsi_cdb_read10); /* Read back data from guest memory into local qtest memory */ - memread(guest_buf, rx, len); + qtest_memread(qts, guest_buf, rx, len); g_assert_cmpint(memcmp(pattern, rx, len), ==, 0); g_free(pattern); g_free(rx); - test_bmdma_teardown(); + test_bmdma_teardown(qts); } int main(int argc, char **argv) @@ -1028,7 +1064,6 @@ int main(int argc, char **argv) qtest_add_func("/ide/identify", test_identify); - qtest_add_func("/ide/bmdma/setup", test_bmdma_setup); qtest_add_func("/ide/bmdma/simple_rw", test_bmdma_simple_rw); qtest_add_func("/ide/bmdma/trim", test_bmdma_trim); qtest_add_func("/ide/bmdma/short_prdt", test_bmdma_short_prdt); @@ -1036,7 +1071,6 @@ int main(int argc, char **argv) test_bmdma_one_sector_short_prdt); qtest_add_func("/ide/bmdma/long_prdt", test_bmdma_long_prdt); qtest_add_func("/ide/bmdma/no_busmaster", test_bmdma_no_busmaster); - qtest_add_func("/ide/bmdma/teardown", test_bmdma_teardown); qtest_add_func("/ide/flush", test_flush); qtest_add_func("/ide/flush/nodev", test_flush_nodev); diff --git a/tests/ivshmem-test.c b/tests/ivshmem-test.c index 227561fbca..a467b8c03d 100644 --- a/tests/ivshmem-test.c +++ b/tests/ivshmem-test.c @@ -383,18 +383,21 @@ static void test_ivshmem_server(void) static void test_ivshmem_hotplug(void) { + QTestState *qts; const char *arch = qtest_get_arch(); - qtest_start("-object memory-backend-ram,size=1M,id=mb1"); + qts = qtest_init("-object memory-backend-ram,size=1M,id=mb1"); + global_qtest = qts; /* TODO: Get rid of global_qtest here */ qtest_qmp_device_add("ivshmem-plain", "iv1", "{'addr': %s, 'memdev': 'mb1'}", stringify(PCI_SLOT_HP)); if (strcmp(arch, "ppc64") != 0) { - qpci_unplug_acpi_device_test("iv1", PCI_SLOT_HP); + qpci_unplug_acpi_device_test(qts, "iv1", PCI_SLOT_HP); } - qtest_end(); + qtest_quit(qts); + global_qtest = NULL; } static void test_ivshmem_memdev(void) diff --git a/tests/libqos/pci-pc.c b/tests/libqos/pci-pc.c index 407d8aff78..634fedd049 100644 --- a/tests/libqos/pci-pc.c +++ b/tests/libqos/pci-pc.c @@ -176,19 +176,19 @@ void qpci_free_pc(QPCIBus *bus) g_free(s); } -void qpci_unplug_acpi_device_test(const char *id, uint8_t slot) +void qpci_unplug_acpi_device_test(QTestState *qts, const char *id, uint8_t slot) { QDict *response; - response = qmp("{'execute': 'device_del', 'arguments': {'id': %s}}", - id); + response = qtest_qmp(qts, "{'execute': 'device_del'," + " 'arguments': {'id': %s}}", id); g_assert(response); g_assert(!qdict_haskey(response, "error")); qobject_unref(response); - outb(ACPI_PCIHP_ADDR + PCI_EJ_BASE, 1 << slot); + qtest_outb(qts, ACPI_PCIHP_ADDR + PCI_EJ_BASE, 1 << slot); - qmp_eventwait("DEVICE_DELETED"); + qtest_qmp_eventwait(qts, "DEVICE_DELETED"); } static void qpci_pc_register_nodes(void) diff --git a/tests/libqos/pci.h b/tests/libqos/pci.h index 8e1d292a7d..a5389a5845 100644 --- a/tests/libqos/pci.h +++ b/tests/libqos/pci.h @@ -123,7 +123,7 @@ QPCIBar qpci_iomap(QPCIDevice *dev, int barno, uint64_t *sizeptr); void qpci_iounmap(QPCIDevice *dev, QPCIBar addr); QPCIBar qpci_legacy_iomap(QPCIDevice *dev, uint16_t addr); -void qpci_unplug_acpi_device_test(const char *id, uint8_t slot); +void qpci_unplug_acpi_device_test(QTestState *qs, const char *id, uint8_t slot); void add_qpci_address(QOSGraphEdgeOptions *opts, QPCIAddress *addr); #endif diff --git a/tests/megasas-test.c b/tests/megasas-test.c index 33aa97042c..1111d331d3 100644 --- a/tests/megasas-test.c +++ b/tests/megasas-test.c @@ -66,7 +66,7 @@ static void megasas_pd_get_info_fuzz(void *obj, void *data, QGuestAllocator *all context[7] = cpu_to_le32(0); context_pa = guest_alloc(alloc, sizeof(context)); - memwrite(context_pa, context, sizeof(context)); + qtest_memwrite(dev->bus->qts, context_pa, context, sizeof(context)); qpci_io_writel(dev, bar, 0x40, context_pa); } diff --git a/tests/qemu-iotests/059.out b/tests/qemu-iotests/059.out index 700ad1f290..f51394ae8e 100644 --- a/tests/qemu-iotests/059.out +++ b/tests/qemu-iotests/059.out @@ -2,15 +2,15 @@ QA output created by 059 === Testing invalid granularity === Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 -can't open device TEST_DIR/t.vmdk: Invalid granularity, image may be corrupt +qemu-io: can't open device TEST_DIR/t.vmdk: Invalid granularity, image may be corrupt === Testing too big L2 table size === Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 -can't open device TEST_DIR/t.vmdk: L2 table size too big +qemu-io: can't open device TEST_DIR/t.vmdk: L2 table size too big === Testing too big L1 table size === Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 -can't open device TEST_DIR/t.vmdk: L1 size too big +qemu-io: can't open device TEST_DIR/t.vmdk: L1 size too big === Testing monolithicFlat creation and opening === Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2147483648 subformat=monolithicFlat @@ -2050,7 +2050,7 @@ wrote 512/512 bytes at offset 10240 === Testing monolithicFlat with internally generated JSON file name === Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 subformat=monolithicFlat -can't open: Cannot use relative extent paths with VMDK descriptor file 'json:{"image": {"driver": "file", "filename": "TEST_DIR/t.IMGFMT"}, "driver": "blkdebug", "inject-error.0.event": "read_aio"}' +qemu-io: can't open: Cannot use relative extent paths with VMDK descriptor file 'json:{"image": {"driver": "file", "filename": "TEST_DIR/t.IMGFMT"}, "driver": "blkdebug", "inject-error.0.event": "read_aio"}' === Testing version 3 === image: TEST_DIR/iotest-version3.IMGFMT diff --git a/tests/qemu-iotests/083.out b/tests/qemu-iotests/083.out index 7419722cd7..eee6dd1379 100644 --- a/tests/qemu-iotests/083.out +++ b/tests/qemu-iotests/083.out @@ -1,43 +1,43 @@ QA output created by 083 === Check disconnect before neg1 === -can't open device nbd+tcp://127.0.0.1:PORT/foo +qemu-io: can't open device nbd+tcp://127.0.0.1:PORT/foo === Check disconnect after neg1 === -can't open device nbd+tcp://127.0.0.1:PORT/foo +qemu-io: can't open device nbd+tcp://127.0.0.1:PORT/foo === Check disconnect 8 neg1 === -can't open device nbd+tcp://127.0.0.1:PORT/foo +qemu-io: can't open device nbd+tcp://127.0.0.1:PORT/foo === Check disconnect 16 neg1 === -can't open device nbd+tcp://127.0.0.1:PORT/foo +qemu-io: can't open device nbd+tcp://127.0.0.1:PORT/foo === Check disconnect before export === -can't open device nbd+tcp://127.0.0.1:PORT/foo +qemu-io: can't open device nbd+tcp://127.0.0.1:PORT/foo === Check disconnect after export === -can't open device nbd+tcp://127.0.0.1:PORT/foo +qemu-io: can't open device nbd+tcp://127.0.0.1:PORT/foo === Check disconnect 4 export === -can't open device nbd+tcp://127.0.0.1:PORT/foo +qemu-io: can't open device nbd+tcp://127.0.0.1:PORT/foo === Check disconnect 12 export === -can't open device nbd+tcp://127.0.0.1:PORT/foo +qemu-io: can't open device nbd+tcp://127.0.0.1:PORT/foo === Check disconnect 16 export === -can't open device nbd+tcp://127.0.0.1:PORT/foo +qemu-io: can't open device nbd+tcp://127.0.0.1:PORT/foo === Check disconnect before neg2 === -can't open device nbd+tcp://127.0.0.1:PORT/foo +qemu-io: can't open device nbd+tcp://127.0.0.1:PORT/foo === Check disconnect after neg2 === @@ -45,11 +45,11 @@ read failed: Input/output error === Check disconnect 8 neg2 === -can't open device nbd+tcp://127.0.0.1:PORT/foo +qemu-io: can't open device nbd+tcp://127.0.0.1:PORT/foo === Check disconnect 10 neg2 === -can't open device nbd+tcp://127.0.0.1:PORT/foo +qemu-io: can't open device nbd+tcp://127.0.0.1:PORT/foo === Check disconnect before request === @@ -86,23 +86,23 @@ read 512/512 bytes at offset 0 === Check disconnect before neg-classic === -can't open device nbd+tcp://127.0.0.1:PORT/ +qemu-io: can't open device nbd+tcp://127.0.0.1:PORT/ === Check disconnect 8 neg-classic === -can't open device nbd+tcp://127.0.0.1:PORT/ +qemu-io: can't open device nbd+tcp://127.0.0.1:PORT/ === Check disconnect 16 neg-classic === -can't open device nbd+tcp://127.0.0.1:PORT/ +qemu-io: can't open device nbd+tcp://127.0.0.1:PORT/ === Check disconnect 24 neg-classic === -can't open device nbd+tcp://127.0.0.1:PORT/ +qemu-io: can't open device nbd+tcp://127.0.0.1:PORT/ === Check disconnect 28 neg-classic === -can't open device nbd+tcp://127.0.0.1:PORT/ +qemu-io: can't open device nbd+tcp://127.0.0.1:PORT/ === Check disconnect after neg-classic === @@ -110,43 +110,43 @@ read failed: Input/output error === Check disconnect before neg1 === -can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock +qemu-io: can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock === Check disconnect after neg1 === -can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock +qemu-io: can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock === Check disconnect 8 neg1 === -can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock +qemu-io: can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock === Check disconnect 16 neg1 === -can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock +qemu-io: can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock === Check disconnect before export === -can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock +qemu-io: can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock === Check disconnect after export === -can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock +qemu-io: can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock === Check disconnect 4 export === -can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock +qemu-io: can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock === Check disconnect 12 export === -can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock +qemu-io: can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock === Check disconnect 16 export === -can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock +qemu-io: can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock === Check disconnect before neg2 === -can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock +qemu-io: can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock === Check disconnect after neg2 === @@ -154,11 +154,11 @@ read failed: Input/output error === Check disconnect 8 neg2 === -can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock +qemu-io: can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock === Check disconnect 10 neg2 === -can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock +qemu-io: can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock === Check disconnect before request === @@ -195,23 +195,23 @@ read 512/512 bytes at offset 0 === Check disconnect before neg-classic === -can't open device nbd+unix:///?socket=TEST_DIR/nbd.sock +qemu-io: can't open device nbd+unix:///?socket=TEST_DIR/nbd.sock === Check disconnect 8 neg-classic === -can't open device nbd+unix:///?socket=TEST_DIR/nbd.sock +qemu-io: can't open device nbd+unix:///?socket=TEST_DIR/nbd.sock === Check disconnect 16 neg-classic === -can't open device nbd+unix:///?socket=TEST_DIR/nbd.sock +qemu-io: can't open device nbd+unix:///?socket=TEST_DIR/nbd.sock === Check disconnect 24 neg-classic === -can't open device nbd+unix:///?socket=TEST_DIR/nbd.sock +qemu-io: can't open device nbd+unix:///?socket=TEST_DIR/nbd.sock === Check disconnect 28 neg-classic === -can't open device nbd+unix:///?socket=TEST_DIR/nbd.sock +qemu-io: can't open device nbd+unix:///?socket=TEST_DIR/nbd.sock === Check disconnect after neg-classic === diff --git a/tests/qemu-iotests/092.out b/tests/qemu-iotests/092.out index 6eda321fc6..3e79914873 100644 --- a/tests/qemu-iotests/092.out +++ b/tests/qemu-iotests/092.out @@ -2,25 +2,25 @@ QA output created by 092 == Invalid cluster size == Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 -can't open device TEST_DIR/t.qcow: Cluster size must be between 512 and 64k -can't open device TEST_DIR/t.qcow: Cluster size must be between 512 and 64k -can't open device TEST_DIR/t.qcow: Cluster size must be between 512 and 64k -can't open device TEST_DIR/t.qcow: Cluster size must be between 512 and 64k +qemu-io: can't open device TEST_DIR/t.qcow: Cluster size must be between 512 and 64k +qemu-io: can't open device TEST_DIR/t.qcow: Cluster size must be between 512 and 64k +qemu-io: can't open device TEST_DIR/t.qcow: Cluster size must be between 512 and 64k +qemu-io: can't open device TEST_DIR/t.qcow: Cluster size must be between 512 and 64k == Invalid L2 table size == Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 -can't open device TEST_DIR/t.qcow: L2 table size must be between 512 and 64k -can't open device TEST_DIR/t.qcow: L2 table size must be between 512 and 64k -can't open device TEST_DIR/t.qcow: L2 table size must be between 512 and 64k -can't open device TEST_DIR/t.qcow: L2 table size must be between 512 and 64k +qemu-io: can't open device TEST_DIR/t.qcow: L2 table size must be between 512 and 64k +qemu-io: can't open device TEST_DIR/t.qcow: L2 table size must be between 512 and 64k +qemu-io: can't open device TEST_DIR/t.qcow: L2 table size must be between 512 and 64k +qemu-io: can't open device TEST_DIR/t.qcow: L2 table size must be between 512 and 64k == Invalid size == Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 -can't open device TEST_DIR/t.qcow: Image too large -can't open device TEST_DIR/t.qcow: Image too large +qemu-io: can't open device TEST_DIR/t.qcow: Image too large +qemu-io: can't open device TEST_DIR/t.qcow: Image too large == Invalid backing file length == Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 -can't open device TEST_DIR/t.qcow: Backing file name too long -can't open device TEST_DIR/t.qcow: Backing file name too long +qemu-io: can't open device TEST_DIR/t.qcow: Backing file name too long +qemu-io: can't open device TEST_DIR/t.qcow: Backing file name too long *** done diff --git a/tests/qemu-iotests/110 b/tests/qemu-iotests/110 index fad672c1ae..33b169ffd4 100755 --- a/tests/qemu-iotests/110 +++ b/tests/qemu-iotests/110 @@ -53,8 +53,12 @@ TEST_IMG="$TEST_IMG.base" _make_test_img 64M _make_test_img -b "$TEST_IMG_REL.base" 64M # qemu should be able to reconstruct the filename, so relative backing names # should work +# (We have to filter the backing file format because vmdk always +# reports it (as vmdk), whereas other image formats would do so only +# with the backing_fmt creation option, which neither vmdk nor qcow +# support) TEST_IMG="json:{'driver':'$IMGFMT','file':{'driver':'file','filename':'$TEST_IMG'}}" \ - _img_info | _filter_img_info + _img_info | _filter_img_info | grep -v 'backing file format' echo echo '=== Non-reconstructable filename ===' @@ -78,7 +82,7 @@ TEST_IMG="json:{ } ] } -}" _img_info | _filter_img_info +}" _img_info | _filter_img_info | grep -v 'backing file format' echo echo '=== Backing name is always relative to the backed image ===' @@ -110,7 +114,7 @@ TEST_IMG="json:{ } ] } -}" _img_info | _filter_img_info +}" _img_info | _filter_img_info | grep -v 'backing file format' # success, all done diff --git a/tests/qemu-iotests/126 b/tests/qemu-iotests/126 index 96dc048d59..e3ee65c606 100755 --- a/tests/qemu-iotests/126 +++ b/tests/qemu-iotests/126 @@ -62,8 +62,12 @@ TOP_IMG="$TEST_DIR/image:top.$IMGFMT" TEST_IMG=$BASE_IMG _make_test_img 64M TEST_IMG=$TOP_IMG _make_test_img -b ./image:base.$IMGFMT -# The default cluster size depends on the image format -TEST_IMG=$TOP_IMG _img_info | grep -v 'cluster_size' +# (1) The default cluster size depends on the image format +# (2) vmdk only supports vmdk backing files, so it always reports the +# format of its backing file as such (but neither it nor qcow +# support the backing_fmt creation option, so we cannot use that to +# harmonize the output across all image formats this test supports) +TEST_IMG=$TOP_IMG _img_info | grep -ve 'cluster_size' -e 'backing file format' _rm_test_img "$BASE_IMG" _rm_test_img "$TOP_IMG" @@ -79,7 +83,7 @@ TOP_IMG="file:image:top.$IMGFMT" TEST_IMG=$BASE_IMG _make_test_img 64M TEST_IMG=$TOP_IMG _make_test_img -b "$BASE_IMG" -TEST_IMG=$TOP_IMG _img_info | grep -v 'cluster_size' +TEST_IMG=$TOP_IMG _img_info | grep -ve 'cluster_size' -e 'backing file format' _rm_test_img "$BASE_IMG" _rm_test_img "image:top.$IMGFMT" diff --git a/tests/qemu-iotests/138 b/tests/qemu-iotests/138 index f353ac8219..6a731370db 100755 --- a/tests/qemu-iotests/138 +++ b/tests/qemu-iotests/138 @@ -54,15 +54,13 @@ $QEMU_IO -c 'write 0 512' "$TEST_IMG" | _filter_qemu_io # Put the data cluster at a multiple of 2 TB, resulting in the image apparently # having a multiple of 2^32 clusters # (To be more specific: It is at 32 PB) -poke_file "$TEST_IMG" 2048 "\x80\x80\x00\x00\x00\x00\x00\x00" +poke_file "$TEST_IMG" $((2048 + 8)) "\x00\x80\x00\x00\x00\x00\x00\x00" # An offset of 32 PB results in qemu-img check having to allocate an in-memory -# refcount table of 128 TB (16 bit refcounts, 512 byte clusters). -# This should be generally too much for any system and thus fail. -# What this test is checking is that the qcow2 driver actually tries to allocate -# such a large amount of memory (and is consequently aborting) instead of having -# truncated the cluster count somewhere (which would result in much less memory -# being allocated and then a segfault occurring). +# refcount table of 128 TB (16 bit refcounts, 512 byte clusters), if qemu-img +# don't check that referenced data cluster is far beyond the end of file. +# But starting from 4.0, qemu-img does this check, and instead of "Cannot +# allocate memory", we have an error showing that l2 entry is invalid. _check_test_img # success, all done diff --git a/tests/qemu-iotests/138.out b/tests/qemu-iotests/138.out index 3fe911f85a..aca7d47a80 100644 --- a/tests/qemu-iotests/138.out +++ b/tests/qemu-iotests/138.out @@ -5,5 +5,8 @@ QA output created by 138 Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=512 wrote 512/512 bytes at offset 0 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -qemu-img: Check failed: Cannot allocate memory +ERROR: counting reference for region exceeding the end of the file by one cluster or more: offset 0x80000000000000 size 0x200 + +1 errors were found on the image. +Data may be corrupted, or further writes to the image may corrupt it. *** done diff --git a/tests/qemu-iotests/182 b/tests/qemu-iotests/182 index ff3d7e7ec1..38959bf276 100755 --- a/tests/qemu-iotests/182 +++ b/tests/qemu-iotests/182 @@ -31,6 +31,7 @@ _cleanup() { _cleanup_test_img rm -f "$TEST_IMG.overlay" + rm -f "$TEST_DIR/nbd.socket" } trap "_cleanup; exit \$status" 0 1 2 3 15 @@ -126,15 +127,26 @@ success_or_failure=y _send_qemu_cmd $QEMU_HANDLE \ 'return' \ 'error' -# Now we attach the image to a virtio-blk device. This device does -# require some permissions (at least WRITE and READ_CONSISTENT), so if +# Start an NBD server to which we can attach node1 +success_or_failure=y _send_qemu_cmd $QEMU_HANDLE \ + "{'execute': 'nbd-server-start', + 'arguments': { + 'addr': { + 'type': 'unix', + 'data': { + 'path': '$TEST_DIR/nbd.socket' + } } } }" \ + 'return' \ + 'error' + +# Now we attach the image to the NBD server. This server does require +# some permissions (at least WRITE and READ_CONSISTENT), so if # reopening node0 unshared any (which it should not have), this will # fail (but it should not). success_or_failure=y _send_qemu_cmd $QEMU_HANDLE \ - "{'execute': 'device_add', + "{'execute': 'nbd-server-add', 'arguments': { - 'driver': 'virtio-blk', - 'drive': 'node1' + 'device': 'node1' } }" \ 'return' \ 'error' diff --git a/tests/qemu-iotests/182.out b/tests/qemu-iotests/182.out index af501ca3f3..33d41eea91 100644 --- a/tests/qemu-iotests/182.out +++ b/tests/qemu-iotests/182.out @@ -14,4 +14,5 @@ Formatting 'TEST_DIR/t.qcow2.overlay', fmt=qcow2 size=197120 backing_file=TEST_D {"return": {}} {"return": {}} {"return": {}} +{"return": {}} *** done diff --git a/tests/qemu-iotests/192 b/tests/qemu-iotests/192 index 158086f9d2..61a88ac88d 100755 --- a/tests/qemu-iotests/192 +++ b/tests/qemu-iotests/192 @@ -29,7 +29,9 @@ status=1 # failure is the default! _cleanup() { - _cleanup_test_img + _cleanup_qemu + _cleanup_test_img + rm -f "$TEST_DIR/nbd" } trap "_cleanup; exit \$status" 0 1 2 3 15 diff --git a/tests/qemu-iotests/207 b/tests/qemu-iotests/207 index dfd3c51bd1..b3816136f7 100755 --- a/tests/qemu-iotests/207 +++ b/tests/qemu-iotests/207 @@ -66,7 +66,7 @@ with iotests.FilePath('t.img') as disk_path, \ 'size': 4194304 }) vm.shutdown() - iotests.img_info_log(remote_path, filter_path=disk_path) + iotests.img_info_log(remote_path) iotests.log("") iotests.img_info_log(disk_path) @@ -91,7 +91,7 @@ with iotests.FilePath('t.img') as disk_path, \ 'size': 8388608 }) vm.shutdown() - iotests.img_info_log(remote_path, filter_path=disk_path) + iotests.img_info_log(remote_path) vm.launch() blockdev_create(vm, { 'driver': 'ssh', @@ -108,7 +108,7 @@ with iotests.FilePath('t.img') as disk_path, \ 'size': 4194304 }) vm.shutdown() - iotests.img_info_log(remote_path, filter_path=disk_path) + iotests.img_info_log(remote_path) md5_key = subprocess.check_output( 'ssh-keyscan -t rsa 127.0.0.1 2>/dev/null | grep -v "\\^#" | ' + @@ -146,7 +146,7 @@ with iotests.FilePath('t.img') as disk_path, \ 'size': 8388608 }) vm.shutdown() - iotests.img_info_log(remote_path, filter_path=disk_path) + iotests.img_info_log(remote_path) sha1_key = subprocess.check_output( 'ssh-keyscan -t rsa 127.0.0.1 2>/dev/null | grep -v "\\^#" | ' + @@ -184,7 +184,7 @@ with iotests.FilePath('t.img') as disk_path, \ 'size': 4194304 }) vm.shutdown() - iotests.img_info_log(remote_path, filter_path=disk_path) + iotests.img_info_log(remote_path) # # Invalid path and user diff --git a/tests/qemu-iotests/207.out b/tests/qemu-iotests/207.out index 979d5cf745..ec9823793a 100644 --- a/tests/qemu-iotests/207.out +++ b/tests/qemu-iotests/207.out @@ -5,7 +5,7 @@ {"execute": "job-dismiss", "arguments": {"id": "job0"}} {"return": {}} -image: json:{"driver": "IMGFMT", "file": {"server.host": "127.0.0.1", "server.port": "22", "driver": "ssh", "path": "TEST_IMG"}} +image: TEST_IMG file format: IMGFMT virtual size: 4 MiB (4194304 bytes) @@ -21,7 +21,7 @@ virtual size: 4 MiB (4194304 bytes) {"execute": "job-dismiss", "arguments": {"id": "job0"}} {"return": {}} -image: json:{"driver": "IMGFMT", "file": {"server.host": "127.0.0.1", "server.port": "22", "driver": "ssh", "path": "TEST_IMG"}} +image: TEST_IMG file format: IMGFMT virtual size: 8 MiB (8388608 bytes) @@ -30,7 +30,7 @@ virtual size: 8 MiB (8388608 bytes) {"execute": "job-dismiss", "arguments": {"id": "job0"}} {"return": {}} -image: json:{"driver": "IMGFMT", "file": {"server.host": "127.0.0.1", "server.port": "22", "driver": "ssh", "path": "TEST_IMG"}} +image: TEST_IMG file format: IMGFMT virtual size: 4 MiB (4194304 bytes) @@ -45,7 +45,7 @@ Job failed: remote host key does not match host_key_check 'wrong' {"execute": "job-dismiss", "arguments": {"id": "job0"}} {"return": {}} -image: json:{"driver": "IMGFMT", "file": {"server.host": "127.0.0.1", "server.port": "22", "driver": "ssh", "path": "TEST_IMG"}} +image: TEST_IMG file format: IMGFMT virtual size: 8 MiB (8388608 bytes) @@ -60,7 +60,7 @@ Job failed: remote host key does not match host_key_check 'wrong' {"execute": "job-dismiss", "arguments": {"id": "job0"}} {"return": {}} -image: json:{"driver": "IMGFMT", "file": {"server.host": "127.0.0.1", "server.port": "22", "driver": "ssh", "path": "TEST_IMG"}} +image: TEST_IMG file format: IMGFMT virtual size: 4 MiB (4194304 bytes) diff --git a/tests/qemu-iotests/221 b/tests/qemu-iotests/221 index 808cd9a289..25dd47bcfe 100755 --- a/tests/qemu-iotests/221 +++ b/tests/qemu-iotests/221 @@ -2,7 +2,7 @@ # # Test qemu-img vs. unaligned images # -# Copyright (C) 2018 Red Hat, Inc. +# Copyright (C) 2018-2019 Red Hat, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -41,16 +41,16 @@ echo echo "=== Check mapping of unaligned raw image ===" echo -_make_test_img 43009 # qemu-img create rounds size up +_make_test_img 65537 # qemu-img create rounds size up $QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map -truncate --size=43009 "$TEST_IMG" # so we resize it and check again +truncate --size=65537 "$TEST_IMG" # so we resize it and check again $QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map -$QEMU_IO -c 'w 43008 1' "$TEST_IMG" | _filter_qemu_io # writing also rounds up +$QEMU_IO -c 'w 65536 1' "$TEST_IMG" | _filter_qemu_io # writing also rounds up $QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map -truncate --size=43009 "$TEST_IMG" # so we resize it and check again +truncate --size=65537 "$TEST_IMG" # so we resize it and check again $QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map # success, all done diff --git a/tests/qemu-iotests/221.out b/tests/qemu-iotests/221.out index a9c0190aad..9f9dd52bb0 100644 --- a/tests/qemu-iotests/221.out +++ b/tests/qemu-iotests/221.out @@ -2,15 +2,15 @@ QA output created by 221 === Check mapping of unaligned raw image === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=43009 -[{ "start": 0, "length": 43520, "depth": 0, "zero": true, "data": false, "offset": OFFSET}] -[{ "start": 0, "length": 43520, "depth": 0, "zero": true, "data": false, "offset": OFFSET}] -wrote 1/1 bytes at offset 43008 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=65537 +[{ "start": 0, "length": 66048, "depth": 0, "zero": true, "data": false, "offset": OFFSET}] +[{ "start": 0, "length": 66048, "depth": 0, "zero": true, "data": false, "offset": OFFSET}] +wrote 1/1 bytes at offset 65536 1 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -[{ "start": 0, "length": 40960, "depth": 0, "zero": true, "data": false, "offset": OFFSET}, -{ "start": 40960, "length": 2049, "depth": 0, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 43009, "length": 511, "depth": 0, "zero": true, "data": false, "offset": OFFSET}] -[{ "start": 0, "length": 40960, "depth": 0, "zero": true, "data": false, "offset": OFFSET}, -{ "start": 40960, "length": 2049, "depth": 0, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 43009, "length": 511, "depth": 0, "zero": true, "data": false, "offset": OFFSET}] +[{ "start": 0, "length": 65536, "depth": 0, "zero": true, "data": false, "offset": OFFSET}, +{ "start": 65536, "length": 1, "depth": 0, "zero": false, "data": true, "offset": OFFSET}, +{ "start": 65537, "length": 511, "depth": 0, "zero": true, "data": false, "offset": OFFSET}] +[{ "start": 0, "length": 65536, "depth": 0, "zero": true, "data": false, "offset": OFFSET}, +{ "start": 65536, "length": 1, "depth": 0, "zero": false, "data": true, "offset": OFFSET}, +{ "start": 65537, "length": 511, "depth": 0, "zero": true, "data": false, "offset": OFFSET}] *** done diff --git a/tests/qemu-iotests/233 b/tests/qemu-iotests/233 index b8b6c8cc4c..41b4d46560 100755 --- a/tests/qemu-iotests/233 +++ b/tests/qemu-iotests/233 @@ -139,11 +139,13 @@ nbd_server_start_tcp_socket \ $QEMU_IMG info --image-opts \ --object tls-creds-x509,dir=${tls_dir}/client1,endpoint=client,id=tls0 \ - driver=nbd,host=$nbd_tcp_addr,port=$nbd_tcp_port,tls-creds=tls0 + driver=nbd,host=$nbd_tcp_addr,port=$nbd_tcp_port,tls-creds=tls0 \ + 2>&1 | sed "s/$nbd_tcp_port/PORT/g" $QEMU_IMG info --image-opts \ --object tls-creds-x509,dir=${tls_dir}/client3,endpoint=client,id=tls0 \ - driver=nbd,host=$nbd_tcp_addr,port=$nbd_tcp_port,tls-creds=tls0 + driver=nbd,host=$nbd_tcp_addr,port=$nbd_tcp_port,tls-creds=tls0 \ + 2>&1 | sed "s/$nbd_tcp_port/PORT/g" echo echo "== final server log ==" diff --git a/tests/qemu-iotests/233.out b/tests/qemu-iotests/233.out index 4edc2dd5cf..9b46284ab0 100644 --- a/tests/qemu-iotests/233.out +++ b/tests/qemu-iotests/233.out @@ -57,8 +57,8 @@ read 1048576/1048576 bytes at offset 1048576 1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) == check TLS with authorization == -qemu-img: Could not open 'driver=nbd,host=127.0.0.1,port=10809,tls-creds=tls0': Failed to read option reply: Cannot read from TLS channel: Software caused connection abort -qemu-img: Could not open 'driver=nbd,host=127.0.0.1,port=10809,tls-creds=tls0': Failed to read option reply: Cannot read from TLS channel: Software caused connection abort +qemu-img: Could not open 'driver=nbd,host=127.0.0.1,port=PORT,tls-creds=tls0': Failed to read option reply: Cannot read from TLS channel: Software caused connection abort +qemu-img: Could not open 'driver=nbd,host=127.0.0.1,port=PORT,tls-creds=tls0': Failed to read option reply: Cannot read from TLS channel: Software caused connection abort == final server log == qemu-nbd: option negotiation failed: Verify failed: No certificate was found. diff --git a/tests/qemu-iotests/252 b/tests/qemu-iotests/252 new file mode 100755 index 0000000000..f6c8f71444 --- /dev/null +++ b/tests/qemu-iotests/252 @@ -0,0 +1,124 @@ +#!/usr/bin/env bash +# +# Tests for rebasing COW images that require zero cluster support +# +# Copyright (C) 2019 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +# creator +owner=mreitz@redhat.com + +seq=$(basename $0) +echo "QA output created by $seq" + +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img + rm -f "$TEST_IMG.base_new" +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter +. ./common.pattern + +# Currently only qcow2 and qed support rebasing, and only qcow2 v3 has +# zero cluster support +_supported_fmt qcow2 +_unsupported_imgopts 'compat=0.10' +_supported_proto file +_supported_os Linux + +CLUSTER_SIZE=65536 + +echo +echo "=== Test rebase without input base ===" +echo + +# Cluster allocations to be tested: +# +# Backing (new) 11 -- 11 -- 11 -- +# COW image 22 22 11 11 -- -- +# +# Expected result: +# +# COW image 22 22 11 11 00 -- +# +# (Cluster 2 might be "--" after the rebase, too, but rebase just +# compares the new backing file to the old one and disregards the +# overlay. Therefore, it will never discard overlay clusters.) + +_make_test_img $((6 * CLUSTER_SIZE)) +TEST_IMG="$TEST_IMG.base_new" _make_test_img $((6 * CLUSTER_SIZE)) + +echo + +$QEMU_IO "$TEST_IMG" \ + -c "write -P 0x22 $((0 * CLUSTER_SIZE)) $((2 * CLUSTER_SIZE))" \ + -c "write -P 0x11 $((2 * CLUSTER_SIZE)) $((2 * CLUSTER_SIZE))" \ + | _filter_qemu_io + +$QEMU_IO "$TEST_IMG.base_new" \ + -c "write -P 0x11 $((0 * CLUSTER_SIZE)) $CLUSTER_SIZE" \ + -c "write -P 0x11 $((2 * CLUSTER_SIZE)) $CLUSTER_SIZE" \ + -c "write -P 0x11 $((4 * CLUSTER_SIZE)) $CLUSTER_SIZE" \ + | _filter_qemu_io + +echo + +# This should be a no-op +$QEMU_IMG rebase -b "" "$TEST_IMG" + +# Verify the data is correct +$QEMU_IO "$TEST_IMG" \ + -c "read -P 0x22 $((0 * CLUSTER_SIZE)) $((2 * CLUSTER_SIZE))" \ + -c "read -P 0x11 $((2 * CLUSTER_SIZE)) $((2 * CLUSTER_SIZE))" \ + -c "read -P 0x00 $((4 * CLUSTER_SIZE)) $((2 * CLUSTER_SIZE))" \ + | _filter_qemu_io + +echo + +# Verify the allocation status (first four cluster should be allocated +# in TEST_IMG, clusters 4 and 5 should be unallocated (marked as zero +# clusters here because there is no backing file)) +$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map + +echo + +$QEMU_IMG rebase -b "$TEST_IMG.base_new" "$TEST_IMG" + +# Verify the data is correct +$QEMU_IO "$TEST_IMG" \ + -c "read -P 0x22 $((0 * CLUSTER_SIZE)) $((2 * CLUSTER_SIZE))" \ + -c "read -P 0x11 $((2 * CLUSTER_SIZE)) $((2 * CLUSTER_SIZE))" \ + -c "read -P 0x00 $((4 * CLUSTER_SIZE)) $((2 * CLUSTER_SIZE))" \ + | _filter_qemu_io + +echo + +# Verify the allocation status (first four cluster should be allocated +# in TEST_IMG, cluster 4 should be zero, and cluster 5 should be +# unallocated (signified by '"depth": 1')) +$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map + + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/252.out b/tests/qemu-iotests/252.out new file mode 100644 index 0000000000..12dce889f8 --- /dev/null +++ b/tests/qemu-iotests/252.out @@ -0,0 +1,39 @@ +QA output created by 252 + +=== Test rebase without input base === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=393216 +Formatting 'TEST_DIR/t.IMGFMT.base_new', fmt=IMGFMT size=393216 + +wrote 131072/131072 bytes at offset 0 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 131072/131072 bytes at offset 131072 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 65536/65536 bytes at offset 0 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 65536/65536 bytes at offset 131072 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 65536/65536 bytes at offset 262144 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +read 131072/131072 bytes at offset 0 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 131072/131072 bytes at offset 131072 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 131072/131072 bytes at offset 262144 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +[{ "start": 0, "length": 262144, "depth": 0, "zero": false, "data": true, "offset": OFFSET}, +{ "start": 262144, "length": 131072, "depth": 0, "zero": true, "data": false}] + +read 131072/131072 bytes at offset 0 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 131072/131072 bytes at offset 131072 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 131072/131072 bytes at offset 262144 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +[{ "start": 0, "length": 262144, "depth": 0, "zero": false, "data": true, "offset": OFFSET}, +{ "start": 262144, "length": 65536, "depth": 0, "zero": true, "data": false}, +{ "start": 327680, "length": 65536, "depth": 1, "zero": true, "data": false}] +*** done diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc index a543e546c2..93f87389b6 100644 --- a/tests/qemu-iotests/common.rc +++ b/tests/qemu-iotests/common.rc @@ -158,7 +158,7 @@ else TEST_IMG="nbd:127.0.0.1:10810" elif [ "$IMGPROTO" = "ssh" ]; then TEST_IMG_FILE=$TEST_DIR/t.$IMGFMT - REMOTE_TEST_DIR="ssh://127.0.0.1$TEST_DIR" + REMOTE_TEST_DIR="ssh://\\($USER@\\)\\?127.0.0.1\\(:[0-9]\\+\\)\\?$TEST_DIR" TEST_IMG="ssh://127.0.0.1$TEST_IMG_FILE" elif [ "$IMGPROTO" = "nfs" ]; then TEST_IMG_FILE=$TEST_DIR/t.$IMGFMT diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group index 7ac9a5ea4a..00e474ab0a 100644 --- a/tests/qemu-iotests/group +++ b/tests/qemu-iotests/group @@ -249,3 +249,4 @@ 247 rw auto quick 248 rw auto quick 249 rw auto quick +252 rw auto backing quick diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py index 997dc910cb..f811f69135 100644 --- a/tests/qemu-iotests/iotests.py +++ b/tests/qemu-iotests/iotests.py @@ -411,7 +411,7 @@ def remote_filename(path): if imgproto == 'file': return path elif imgproto == 'ssh': - return "ssh://127.0.0.1%s" % (path) + return "ssh://%s@127.0.0.1:22%s" % (os.environ.get('USER'), path) else: raise Exception("Protocol %s not supported" % (imgproto)) diff --git a/tests/qmp-cmd-test.c b/tests/qmp-cmd-test.c index d12cac539c..9f5228cd99 100644 --- a/tests/qmp-cmd-test.c +++ b/tests/qmp-cmd-test.c @@ -61,10 +61,11 @@ static void test_query(const void *data) int expected_error_class = query_error_class(cmd); QDict *resp, *error; const char *error_class; + QTestState *qts; - qtest_start(common_args); + qts = qtest_init(common_args); - resp = qmp("{ 'execute': %s }", cmd); + resp = qtest_qmp(qts, "{ 'execute': %s }", cmd); error = qdict_get_qdict(resp, "error"); error_class = error ? qdict_get_str(error, "class") : NULL; @@ -78,7 +79,7 @@ static void test_query(const void *data) } qobject_unref(resp); - qtest_end(); + qtest_quit(qts); } static bool query_is_blacklisted(const char *cmd) @@ -118,16 +119,18 @@ static void qmp_schema_init(QmpSchema *schema) QDict *resp; Visitor *qiv; SchemaInfoList *tail; + QTestState *qts; - qtest_start(common_args); - resp = qmp("{ 'execute': 'query-qmp-schema' }"); + qts = qtest_init(common_args); + + resp = qtest_qmp(qts, "{ 'execute': 'query-qmp-schema' }"); qiv = qobject_input_visitor_new(qdict_get(resp, "return")); visit_type_SchemaInfoList(qiv, NULL, &schema->list, &error_abort); visit_free(qiv); qobject_unref(resp); - qtest_end(); + qtest_quit(qts); schema->hash = g_hash_table_new(g_str_hash, g_str_equal); diff --git a/tests/tco-test.c b/tests/tco-test.c index f89a42cdcc..254f735370 100644 --- a/tests/tco-test.c +++ b/tests/tco-test.c @@ -45,13 +45,14 @@ typedef struct { QPCIDevice *dev; QPCIBar tco_io_bar; QPCIBus *bus; + QTestState *qts; } TestData; static void test_end(TestData *d) { g_free(d->dev); qpci_free_pc(d->bus); - qtest_end(); + qtest_quit(d->qts); } static void test_init(TestData *d) @@ -61,7 +62,6 @@ static void test_init(TestData *d) qs = qtest_initf("-machine q35 %s %s", d->noreboot ? "" : "-global ICH9-LPC.noreboot=false", !d->args ? "" : d->args); - global_qtest = qs; qtest_irq_intercept_in(qs, "ioapic"); d->bus = qpci_new_pc(qs, NULL); @@ -78,6 +78,7 @@ static void test_init(TestData *d) qpci_config_writel(d->dev, ICH9_LPC_RCBA, RCBA_BASE_ADDR | 0x1); d->tco_io_bar = qpci_legacy_iomap(d->dev, PM_IO_BASE_ADDR + 0x60); + d->qts = qs; } static void stop_tco(const TestData *d) @@ -115,17 +116,17 @@ static void clear_tco_status(const TestData *d) qpci_io_writew(d->dev, d->tco_io_bar, TCO2_STS, 0x0004); } -static void reset_on_second_timeout(bool enable) +static void reset_on_second_timeout(const TestData *td, bool enable) { uint32_t val; - val = readl(RCBA_BASE_ADDR + ICH9_CC_GCS); + val = qtest_readl(td->qts, RCBA_BASE_ADDR + ICH9_CC_GCS); if (enable) { val &= ~ICH9_CC_GCS_NO_REBOOT; } else { val |= ICH9_CC_GCS_NO_REBOOT; } - writel(RCBA_BASE_ADDR + ICH9_CC_GCS, val); + qtest_writel(td->qts, RCBA_BASE_ADDR + ICH9_CC_GCS, val); } static void test_tco_defaults(void) @@ -171,11 +172,11 @@ static void test_tco_timeout(void) stop_tco(&d); clear_tco_status(&d); - reset_on_second_timeout(false); + reset_on_second_timeout(&d, false); set_tco_timeout(&d, ticks); load_tco(&d); start_tco(&d); - clock_step(ticks * TCO_TICK_NSEC); + qtest_clock_step(d.qts, ticks * TCO_TICK_NSEC); /* test first timeout */ val = qpci_io_readw(d.dev, d.tco_io_bar, TCO1_STS); @@ -190,7 +191,7 @@ static void test_tco_timeout(void) g_assert(ret == 0); /* test second timeout */ - clock_step(ticks * TCO_TICK_NSEC); + qtest_clock_step(d.qts, ticks * TCO_TICK_NSEC); val = qpci_io_readw(d.dev, d.tco_io_bar, TCO1_STS); ret = val & TCO_TIMEOUT ? 1 : 0; g_assert(ret == 1); @@ -215,18 +216,18 @@ static void test_tco_max_timeout(void) stop_tco(&d); clear_tco_status(&d); - reset_on_second_timeout(false); + reset_on_second_timeout(&d, false); set_tco_timeout(&d, ticks); load_tco(&d); start_tco(&d); - clock_step(((ticks & TCO_TMR_MASK) - 1) * TCO_TICK_NSEC); + qtest_clock_step(d.qts, ((ticks & TCO_TMR_MASK) - 1) * TCO_TICK_NSEC); val = qpci_io_readw(d.dev, d.tco_io_bar, TCO_RLD); g_assert_cmpint(val & TCO_RLD_MASK, ==, 1); val = qpci_io_readw(d.dev, d.tco_io_bar, TCO1_STS); ret = val & TCO_TIMEOUT ? 1 : 0; g_assert(ret == 0); - clock_step(TCO_TICK_NSEC); + qtest_clock_step(d.qts, TCO_TICK_NSEC); val = qpci_io_readw(d.dev, d.tco_io_bar, TCO1_STS); ret = val & TCO_TIMEOUT ? 1 : 0; g_assert(ret == 1); @@ -235,9 +236,9 @@ static void test_tco_max_timeout(void) test_end(&d); } -static QDict *get_watchdog_action(void) +static QDict *get_watchdog_action(const TestData *td) { - QDict *ev = qmp_eventwait_ref("WATCHDOG"); + QDict *ev = qtest_qmp_eventwait_ref(td->qts, "WATCHDOG"); QDict *data; data = qdict_get_qdict(ev, "data"); @@ -258,12 +259,12 @@ static void test_tco_second_timeout_pause(void) stop_tco(&td); clear_tco_status(&td); - reset_on_second_timeout(true); + reset_on_second_timeout(&td, true); set_tco_timeout(&td, TCO_SECS_TO_TICKS(16)); load_tco(&td); start_tco(&td); - clock_step(ticks * TCO_TICK_NSEC * 2); - ad = get_watchdog_action(); + qtest_clock_step(td.qts, ticks * TCO_TICK_NSEC * 2); + ad = get_watchdog_action(&td); g_assert(!strcmp(qdict_get_str(ad, "action"), "pause")); qobject_unref(ad); @@ -283,12 +284,12 @@ static void test_tco_second_timeout_reset(void) stop_tco(&td); clear_tco_status(&td); - reset_on_second_timeout(true); + reset_on_second_timeout(&td, true); set_tco_timeout(&td, TCO_SECS_TO_TICKS(16)); load_tco(&td); start_tco(&td); - clock_step(ticks * TCO_TICK_NSEC * 2); - ad = get_watchdog_action(); + qtest_clock_step(td.qts, ticks * TCO_TICK_NSEC * 2); + ad = get_watchdog_action(&td); g_assert(!strcmp(qdict_get_str(ad, "action"), "reset")); qobject_unref(ad); @@ -308,12 +309,12 @@ static void test_tco_second_timeout_shutdown(void) stop_tco(&td); clear_tco_status(&td); - reset_on_second_timeout(true); + reset_on_second_timeout(&td, true); set_tco_timeout(&td, ticks); load_tco(&td); start_tco(&td); - clock_step(ticks * TCO_TICK_NSEC * 2); - ad = get_watchdog_action(); + qtest_clock_step(td.qts, ticks * TCO_TICK_NSEC * 2); + ad = get_watchdog_action(&td); g_assert(!strcmp(qdict_get_str(ad, "action"), "shutdown")); qobject_unref(ad); @@ -333,12 +334,12 @@ static void test_tco_second_timeout_none(void) stop_tco(&td); clear_tco_status(&td); - reset_on_second_timeout(true); + reset_on_second_timeout(&td, true); set_tco_timeout(&td, ticks); load_tco(&td); start_tco(&td); - clock_step(ticks * TCO_TICK_NSEC * 2); - ad = get_watchdog_action(); + qtest_clock_step(td.qts, ticks * TCO_TICK_NSEC * 2); + ad = get_watchdog_action(&td); g_assert(!strcmp(qdict_get_str(ad, "action"), "none")); qobject_unref(ad); @@ -358,7 +359,7 @@ static void test_tco_ticks_counter(void) stop_tco(&d); clear_tco_status(&d); - reset_on_second_timeout(false); + reset_on_second_timeout(&d, false); set_tco_timeout(&d, ticks); load_tco(&d); start_tco(&d); @@ -366,7 +367,7 @@ static void test_tco_ticks_counter(void) do { rld = qpci_io_readw(d.dev, d.tco_io_bar, TCO_RLD) & TCO_RLD_MASK; g_assert_cmpint(rld, ==, ticks); - clock_step(TCO_TICK_NSEC); + qtest_clock_step(d.qts, TCO_TICK_NSEC); ticks--; } while (!(qpci_io_readw(d.dev, d.tco_io_bar, TCO1_STS) & TCO_TIMEOUT)); @@ -405,11 +406,11 @@ static void test_tco1_status_bits(void) stop_tco(&d); clear_tco_status(&d); - reset_on_second_timeout(false); + reset_on_second_timeout(&d, false); set_tco_timeout(&d, ticks); load_tco(&d); start_tco(&d); - clock_step(ticks * TCO_TICK_NSEC); + qtest_clock_step(d.qts, ticks * TCO_TICK_NSEC); qpci_io_writeb(d.dev, d.tco_io_bar, TCO_DAT_IN, 0); qpci_io_writeb(d.dev, d.tco_io_bar, TCO_DAT_OUT, 0); @@ -434,11 +435,11 @@ static void test_tco2_status_bits(void) stop_tco(&d); clear_tco_status(&d); - reset_on_second_timeout(true); + reset_on_second_timeout(&d, true); set_tco_timeout(&d, ticks); load_tco(&d); start_tco(&d); - clock_step(ticks * TCO_TICK_NSEC * 2); + qtest_clock_step(d.qts, ticks * TCO_TICK_NSEC * 2); val = qpci_io_readw(d.dev, d.tco_io_bar, TCO2_STS); ret = val & (TCO_SECOND_TO_STS | TCO_BOOT_STS) ? 1 : 0; diff --git a/tests/test-block-iothread.c b/tests/test-block-iothread.c index 97ac0b159d..036ed9a3b3 100644 --- a/tests/test-block-iothread.c +++ b/tests/test-block-iothread.c @@ -354,6 +354,111 @@ static void test_sync_op(const void *opaque) blk_unref(blk); } +typedef struct TestBlockJob { + BlockJob common; + bool should_complete; + int n; +} TestBlockJob; + +static int test_job_prepare(Job *job) +{ + g_assert(qemu_get_current_aio_context() == qemu_get_aio_context()); + return 0; +} + +static int coroutine_fn test_job_run(Job *job, Error **errp) +{ + TestBlockJob *s = container_of(job, TestBlockJob, common.job); + + job_transition_to_ready(&s->common.job); + while (!s->should_complete) { + s->n++; + g_assert(qemu_get_current_aio_context() == job->aio_context); + + /* Avoid job_sleep_ns() because it marks the job as !busy. We want to + * emulate some actual activity (probably some I/O) here so that the + * drain involved in AioContext switches has to wait for this activity + * to stop. */ + qemu_co_sleep_ns(QEMU_CLOCK_REALTIME, 1000000); + + job_pause_point(&s->common.job); + } + + g_assert(qemu_get_current_aio_context() == job->aio_context); + return 0; +} + +static void test_job_complete(Job *job, Error **errp) +{ + TestBlockJob *s = container_of(job, TestBlockJob, common.job); + s->should_complete = true; +} + +BlockJobDriver test_job_driver = { + .job_driver = { + .instance_size = sizeof(TestBlockJob), + .free = block_job_free, + .user_resume = block_job_user_resume, + .drain = block_job_drain, + .run = test_job_run, + .complete = test_job_complete, + .prepare = test_job_prepare, + }, +}; + +static void test_attach_blockjob(void) +{ + IOThread *iothread = iothread_new(); + AioContext *ctx = iothread_get_aio_context(iothread); + BlockBackend *blk; + BlockDriverState *bs; + TestBlockJob *tjob; + + blk = blk_new(BLK_PERM_ALL, BLK_PERM_ALL); + bs = bdrv_new_open_driver(&bdrv_test, "base", BDRV_O_RDWR, &error_abort); + blk_insert_bs(blk, bs, &error_abort); + + tjob = block_job_create("job0", &test_job_driver, NULL, bs, + 0, BLK_PERM_ALL, + 0, 0, NULL, NULL, &error_abort); + job_start(&tjob->common.job); + + while (tjob->n == 0) { + aio_poll(qemu_get_aio_context(), false); + } + + blk_set_aio_context(blk, ctx); + + tjob->n = 0; + while (tjob->n == 0) { + aio_poll(qemu_get_aio_context(), false); + } + + aio_context_acquire(ctx); + blk_set_aio_context(blk, qemu_get_aio_context()); + aio_context_release(ctx); + + tjob->n = 0; + while (tjob->n == 0) { + aio_poll(qemu_get_aio_context(), false); + } + + blk_set_aio_context(blk, ctx); + + tjob->n = 0; + while (tjob->n == 0) { + aio_poll(qemu_get_aio_context(), false); + } + + aio_context_acquire(ctx); + job_complete_sync(&tjob->common.job, &error_abort); + blk_set_aio_context(blk, qemu_get_aio_context()); + aio_context_release(ctx); + + bdrv_unref(bs); + blk_unref(blk); +} + int main(int argc, char **argv) { int i; @@ -368,5 +473,7 @@ int main(int argc, char **argv) g_test_add_data_func(t->name, t, test_sync_op); } + g_test_add_func("/attach/blockjob", test_attach_blockjob); + return g_test_run(); } diff --git a/tests/test-hmp.c b/tests/test-hmp.c index e344947f7c..5029c4d2c9 100644 --- a/tests/test-hmp.c +++ b/tests/test-hmp.c @@ -73,13 +73,13 @@ static const char *hmp_cmds[] = { }; /* Run through the list of pre-defined commands */ -static void test_commands(void) +static void test_commands(QTestState *qts) { char *response; int i; for (i = 0; hmp_cmds[i] != NULL; i++) { - response = hmp("%s", hmp_cmds[i]); + response = qtest_hmp(qts, "%s", hmp_cmds[i]); if (verbose) { fprintf(stderr, "\texecute HMP command: %s\n" @@ -92,11 +92,11 @@ static void test_commands(void) } /* Run through all info commands and call them blindly (without arguments) */ -static void test_info_commands(void) +static void test_info_commands(QTestState *qts) { char *resp, *info, *info_buf, *endp; - info_buf = info = hmp("help info"); + info_buf = info = qtest_hmp(qts, "help info"); while (*info) { /* Extract the info command, ignore parameters and description */ @@ -108,7 +108,7 @@ static void test_info_commands(void) if (verbose) { fprintf(stderr, "\t%s\n", info); } - resp = hmp("%s", info); + resp = qtest_hmp(qts, "%s", info); g_free(resp); /* And move forward to the next line */ info = strchr(endp + 1, '\n'); @@ -125,14 +125,15 @@ static void test_machine(gconstpointer data) { const char *machine = data; char *args; + QTestState *qts; args = g_strdup_printf("-S -M %s", machine); - qtest_start(args); + qts = qtest_init(args); - test_info_commands(); - test_commands(); + test_info_commands(qts); + test_commands(qts); - qtest_end(); + qtest_quit(qts); g_free(args); g_free((void *)data); } diff --git a/tests/tpm-tests.c b/tests/tpm-tests.c index 582ec0cfd4..e640777aa9 100644 --- a/tests/tpm-tests.c +++ b/tests/tpm-tests.c @@ -22,7 +22,7 @@ static bool tpm_test_swtpm_skip(void) { if (!tpm_util_swtpm_has_tpm2()) { - g_test_message("swtpm not in PATH or missing --tpm2 support"); + g_test_skip("swtpm not in PATH or missing --tpm2 support"); return true; } diff --git a/tests/virtio-blk-test.c b/tests/virtio-blk-test.c index b65365934b..fe1168a90a 100644 --- a/tests/virtio-blk-test.c +++ b/tests/virtio-blk-test.c @@ -679,6 +679,7 @@ static void pci_hotplug(void *obj, void *data, QGuestAllocator *t_alloc) { QVirtioPCIDevice *dev1 = obj; QVirtioPCIDevice *dev; + QTestState *qts = dev1->pdev->bus->qts; /* plug secondary disk */ qtest_qmp_device_add("virtio-blk-pci", "drv1", @@ -693,7 +694,7 @@ static void pci_hotplug(void *obj, void *data, QGuestAllocator *t_alloc) qos_object_destroy((QOSGraphObject *)dev); /* unplug secondary disk */ - qpci_unplug_acpi_device_test("drv1", PCI_SLOT_HP); + qpci_unplug_acpi_device_test(qts, "drv1", PCI_SLOT_HP); } /* diff --git a/tests/virtio-net-test.c b/tests/virtio-net-test.c index 0d956f36fe..163126cf07 100644 --- a/tests/virtio-net-test.c +++ b/tests/virtio-net-test.c @@ -162,13 +162,15 @@ static void stop_cont_test(void *obj, void *data, QGuestAllocator *t_alloc) static void hotplug(void *obj, void *data, QGuestAllocator *t_alloc) { + QVirtioPCIDevice *dev = obj; + QTestState *qts = dev->pdev->bus->qts; const char *arch = qtest_get_arch(); qtest_qmp_device_add("virtio-net-pci", "net1", "{'addr': %s}", stringify(PCI_SLOT_HP)); if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { - qpci_unplug_acpi_device_test("net1", PCI_SLOT_HP); + qpci_unplug_acpi_device_test(qts, "net1", PCI_SLOT_HP); } } diff --git a/tests/virtio-rng-test.c b/tests/virtio-rng-test.c index 5309c7c8ab..fcb22481bd 100644 --- a/tests/virtio-rng-test.c +++ b/tests/virtio-rng-test.c @@ -16,13 +16,16 @@ static void rng_hotplug(void *obj, void *data, QGuestAllocator *alloc) { + QVirtioPCIDevice *dev = obj; + QTestState *qts = dev->pdev->bus->qts; + const char *arch = qtest_get_arch(); qtest_qmp_device_add("virtio-rng-pci", "rng1", "{'addr': %s}", stringify(PCI_SLOT_HP)); if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { - qpci_unplug_acpi_device_test("rng1", PCI_SLOT_HP); + qpci_unplug_acpi_device_test(qts, "rng1", PCI_SLOT_HP); } } diff --git a/util/aio-posix.c b/util/aio-posix.c index 6fbfa7924f..db11021287 100644 --- a/util/aio-posix.c +++ b/util/aio-posix.c @@ -519,6 +519,10 @@ static bool run_poll_handlers_once(AioContext *ctx, int64_t *timeout) if (!node->deleted && node->io_poll && aio_node_check(ctx, node->is_external) && node->io_poll(node->opaque)) { + /* + * Polling was successful, exit try_poll_mode immediately + * to adjust the next polling time. + */ *timeout = 0; if (node->opaque != &ctx->notifier) { progress = true; @@ -558,8 +562,9 @@ static bool run_poll_handlers(AioContext *ctx, int64_t max_ns, int64_t *timeout) do { progress = run_poll_handlers_once(ctx, timeout); elapsed_time = qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - start_time; - } while (!progress && elapsed_time < max_ns - && !atomic_read(&ctx->poll_disable_cnt)); + max_ns = qemu_soonest_timeout(*timeout, max_ns); + assert(!(max_ns && progress)); + } while (elapsed_time < max_ns && !atomic_read(&ctx->poll_disable_cnt)); /* If time has passed with no successful polling, adjust *timeout to * keep the same ending time. @@ -585,8 +590,7 @@ static bool run_poll_handlers(AioContext *ctx, int64_t max_ns, int64_t *timeout) */ static bool try_poll_mode(AioContext *ctx, int64_t *timeout) { - /* See qemu_soonest_timeout() uint64_t hack */ - int64_t max_ns = MIN((uint64_t)*timeout, (uint64_t)ctx->poll_ns); + int64_t max_ns = qemu_soonest_timeout(*timeout, ctx->poll_ns); if (max_ns && !atomic_read(&ctx->poll_disable_cnt)) { poll_set_started(ctx, true); diff --git a/util/cacheinfo.c b/util/cacheinfo.c index 3cd080b83d..eebe1ce9c5 100644 --- a/util/cacheinfo.c +++ b/util/cacheinfo.c @@ -107,7 +107,7 @@ static void sys_cache_info(int *isize, int *dsize) static void arch_cache_info(int *isize, int *dsize) { if (*isize == 0 || *dsize == 0) { - unsigned long ctr; + uint64_t ctr; /* The real cache geometry is in CCSIDR_EL1/CLIDR_EL1/CSSELR_EL1, but (at least under Linux) these are marked protected by the diff --git a/util/qemu-sockets.c b/util/qemu-sockets.c index 9705051690..ba6335e71a 100644 --- a/util/qemu-sockets.c +++ b/util/qemu-sockets.c @@ -830,6 +830,7 @@ static int unix_listen_saddr(UnixSocketAddress *saddr, int sock, fd; char *pathbuf = NULL; const char *path; + size_t pathlen; sock = qemu_socket(PF_UNIX, SOCK_STREAM, 0); if (sock < 0) { @@ -845,7 +846,8 @@ static int unix_listen_saddr(UnixSocketAddress *saddr, path = pathbuf = g_strdup_printf("%s/qemu-socket-XXXXXX", tmpdir); } - if (strlen(path) > sizeof(un.sun_path)) { + pathlen = strlen(path); + if (pathlen > sizeof(un.sun_path)) { error_setg(errp, "UNIX socket path '%s' is too long", path); error_append_hint(errp, "Path must be less than %zu bytes\n", sizeof(un.sun_path)); @@ -877,7 +879,7 @@ static int unix_listen_saddr(UnixSocketAddress *saddr, memset(&un, 0, sizeof(un)); un.sun_family = AF_UNIX; - strncpy(un.sun_path, path, sizeof(un.sun_path)); + memcpy(un.sun_path, path, pathlen); if (bind(sock, (struct sockaddr*) &un, sizeof(un)) < 0) { error_setg_errno(errp, errno, "Failed to bind socket to %s", path); @@ -901,6 +903,7 @@ static int unix_connect_saddr(UnixSocketAddress *saddr, Error **errp) { struct sockaddr_un un; int sock, rc; + size_t pathlen; if (saddr->path == NULL) { error_setg(errp, "unix connect: no path specified"); @@ -913,7 +916,8 @@ static int unix_connect_saddr(UnixSocketAddress *saddr, Error **errp) return -1; } - if (strlen(saddr->path) > sizeof(un.sun_path)) { + pathlen = strlen(saddr->path); + if (pathlen > sizeof(un.sun_path)) { error_setg(errp, "UNIX socket path '%s' is too long", saddr->path); error_append_hint(errp, "Path must be less than %zu bytes\n", sizeof(un.sun_path)); @@ -922,7 +926,7 @@ static int unix_connect_saddr(UnixSocketAddress *saddr, Error **errp) memset(&un, 0, sizeof(un)); un.sun_family = AF_UNIX; - strncpy(un.sun_path, saddr->path, sizeof(un.sun_path)); + memcpy(un.sun_path, saddr->path, pathlen); /* connect to peer */ do { diff --git a/util/readline.c b/util/readline.c index ec91ee0fea..a7672b51c1 100644 --- a/util/readline.c +++ b/util/readline.c @@ -48,14 +48,15 @@ static void readline_update(ReadLineState *rs) if (rs->cmd_buf_size != rs->last_cmd_buf_size || memcmp(rs->cmd_buf, rs->last_cmd_buf, rs->cmd_buf_size) != 0) { - for(i = 0; i < rs->last_cmd_buf_index; i++) { + for (i = 0; i < rs->last_cmd_buf_index; i++) { rs->printf_func(rs->opaque, "\033[D"); } rs->cmd_buf[rs->cmd_buf_size] = '\0'; if (rs->read_password) { len = strlen(rs->cmd_buf); - for(i = 0; i < len; i++) + for (i = 0; i < len; i++) { rs->printf_func(rs->opaque, "*"); + } } else { rs->printf_func(rs->opaque, "%s", rs->cmd_buf); } @@ -67,12 +68,12 @@ static void readline_update(ReadLineState *rs) if (rs->cmd_buf_index != rs->last_cmd_buf_index) { delta = rs->cmd_buf_index - rs->last_cmd_buf_index; if (delta > 0) { - for(i = 0;i < delta; i++) { + for (i = 0; i < delta; i++) { rs->printf_func(rs->opaque, "\033[C"); } } else { delta = -delta; - for(i = 0;i < delta; i++) { + for (i = 0; i < delta; i++) { rs->printf_func(rs->opaque, "\033[D"); } } @@ -178,35 +179,38 @@ static void readline_up_char(ReadLineState *rs) { int idx; - if (rs->hist_entry == 0) - return; + if (rs->hist_entry == 0) { + return; + } if (rs->hist_entry == -1) { - /* Find latest entry */ - for (idx = 0; idx < READLINE_MAX_CMDS; idx++) { - if (rs->history[idx] == NULL) - break; - } - rs->hist_entry = idx; + /* Find latest entry */ + for (idx = 0; idx < READLINE_MAX_CMDS; idx++) { + if (rs->history[idx] == NULL) { + break; + } + } + rs->hist_entry = idx; } rs->hist_entry--; if (rs->hist_entry >= 0) { - pstrcpy(rs->cmd_buf, sizeof(rs->cmd_buf), + pstrcpy(rs->cmd_buf, sizeof(rs->cmd_buf), rs->history[rs->hist_entry]); - rs->cmd_buf_index = rs->cmd_buf_size = strlen(rs->cmd_buf); + rs->cmd_buf_index = rs->cmd_buf_size = strlen(rs->cmd_buf); } } static void readline_down_char(ReadLineState *rs) { - if (rs->hist_entry == -1) + if (rs->hist_entry == -1) { return; + } if (rs->hist_entry < READLINE_MAX_CMDS - 1 && rs->history[++rs->hist_entry] != NULL) { - pstrcpy(rs->cmd_buf, sizeof(rs->cmd_buf), + pstrcpy(rs->cmd_buf, sizeof(rs->cmd_buf), rs->history[rs->hist_entry]); } else { rs->cmd_buf[0] = 0; - rs->hist_entry = -1; + rs->hist_entry = -1; } rs->cmd_buf_index = rs->cmd_buf_size = strlen(rs->cmd_buf); } @@ -216,46 +220,50 @@ static void readline_hist_add(ReadLineState *rs, const char *cmdline) char *hist_entry, *new_entry; int idx; - if (cmdline[0] == '\0') - return; + if (cmdline[0] == '\0') { + return; + } new_entry = NULL; if (rs->hist_entry != -1) { - /* We were editing an existing history entry: replace it */ - hist_entry = rs->history[rs->hist_entry]; - idx = rs->hist_entry; - if (strcmp(hist_entry, cmdline) == 0) { - goto same_entry; - } + /* We were editing an existing history entry: replace it */ + hist_entry = rs->history[rs->hist_entry]; + idx = rs->hist_entry; + if (strcmp(hist_entry, cmdline) == 0) { + goto same_entry; + } } /* Search cmdline in history buffers */ for (idx = 0; idx < READLINE_MAX_CMDS; idx++) { - hist_entry = rs->history[idx]; - if (hist_entry == NULL) - break; - if (strcmp(hist_entry, cmdline) == 0) { - same_entry: - new_entry = hist_entry; - /* Put this entry at the end of history */ - memmove(&rs->history[idx], &rs->history[idx + 1], - (READLINE_MAX_CMDS - (idx + 1)) * sizeof(char *)); - rs->history[READLINE_MAX_CMDS - 1] = NULL; - for (; idx < READLINE_MAX_CMDS; idx++) { - if (rs->history[idx] == NULL) - break; - } - break; - } + hist_entry = rs->history[idx]; + if (hist_entry == NULL) { + break; + } + if (strcmp(hist_entry, cmdline) == 0) { + same_entry: + new_entry = hist_entry; + /* Put this entry at the end of history */ + memmove(&rs->history[idx], &rs->history[idx + 1], + (READLINE_MAX_CMDS - (idx + 1)) * sizeof(char *)); + rs->history[READLINE_MAX_CMDS - 1] = NULL; + for (; idx < READLINE_MAX_CMDS; idx++) { + if (rs->history[idx] == NULL) { + break; + } + } + break; + } } if (idx == READLINE_MAX_CMDS) { - /* Need to get one free slot */ + /* Need to get one free slot */ g_free(rs->history[0]); - memmove(rs->history, &rs->history[1], - (READLINE_MAX_CMDS - 1) * sizeof(char *)); - rs->history[READLINE_MAX_CMDS - 1] = NULL; - idx = READLINE_MAX_CMDS - 1; + memmove(rs->history, &rs->history[1], + (READLINE_MAX_CMDS - 1) * sizeof(char *)); + rs->history[READLINE_MAX_CMDS - 1] = NULL; + idx = READLINE_MAX_CMDS - 1; } - if (new_entry == NULL) + if (new_entry == NULL) { new_entry = g_strdup(cmdline); + } rs->history[idx] = new_entry; rs->hist_entry = -1; } @@ -297,49 +305,55 @@ static void readline_completion(ReadLineState *rs) g_free(cmdline); /* no completion found */ - if (rs->nb_completions <= 0) + if (rs->nb_completions <= 0) { return; + } if (rs->nb_completions == 1) { len = strlen(rs->completions[0]); - for(i = rs->completion_index; i < len; i++) { + for (i = rs->completion_index; i < len; i++) { readline_insert_char(rs, rs->completions[0][i]); } /* extra space for next argument. XXX: make it more generic */ - if (len > 0 && rs->completions[0][len - 1] != '/') + if (len > 0 && rs->completions[0][len - 1] != '/') { readline_insert_char(rs, ' '); + } } else { qsort(rs->completions, rs->nb_completions, sizeof(char *), completion_comp); rs->printf_func(rs->opaque, "\n"); max_width = 0; - max_prefix = 0; - for(i = 0; i < rs->nb_completions; i++) { + max_prefix = 0; + for (i = 0; i < rs->nb_completions; i++) { len = strlen(rs->completions[i]); - if (i==0) { + if (i == 0) { max_prefix = len; } else { - if (len < max_prefix) + if (len < max_prefix) { max_prefix = len; - for(j=0; j<max_prefix; j++) { - if (rs->completions[i][j] != rs->completions[0][j]) + } + for (j = 0; j < max_prefix; j++) { + if (rs->completions[i][j] != rs->completions[0][j]) { max_prefix = j; + } } } - if (len > max_width) + if (len > max_width) { max_width = len; + } } - if (max_prefix > 0) - for(i = rs->completion_index; i < max_prefix; i++) { + if (max_prefix > 0) + for (i = rs->completion_index; i < max_prefix; i++) { readline_insert_char(rs, rs->completions[0][i]); } max_width += 2; - if (max_width < 10) + if (max_width < 10) { max_width = 10; - else if (max_width > 80) + } else if (max_width > 80) { max_width = 80; + } nb_cols = 80 / max_width; j = 0; - for(i = 0; i < rs->nb_completions; i++) { + for (i = 0; i < rs->nb_completions; i++) { rs->printf_func(rs->opaque, "%-*s", max_width, rs->completions[i]); if (++j == nb_cols || i == (rs->nb_completions - 1)) { rs->printf_func(rs->opaque, "\n"); @@ -362,9 +376,9 @@ static void readline_clear_screen(ReadLineState *rs) /* return true if command handled */ void readline_handle_byte(ReadLineState *rs, int ch) { - switch(rs->esc_state) { + switch (rs->esc_state) { case IS_NORM: - switch(ch) { + switch (ch) { case 1: readline_bol(rs); break; @@ -383,8 +397,9 @@ void readline_handle_byte(ReadLineState *rs, int ch) case 10: case 13: rs->cmd_buf[rs->cmd_buf_size] = '\0'; - if (!rs->read_password) + if (!rs->read_password) { readline_hist_add(rs, rs->cmd_buf); + } rs->printf_func(rs->opaque, "\n"); rs->cmd_buf_index = 0; rs->cmd_buf_size = 0; @@ -403,9 +418,9 @@ void readline_handle_byte(ReadLineState *rs, int ch) case 8: readline_backspace(rs); break; - case 155: + case 155: rs->esc_state = IS_CSI; - break; + break; default: if (ch >= 32) { readline_insert_char(rs, ch); @@ -425,15 +440,15 @@ void readline_handle_byte(ReadLineState *rs, int ch) } break; case IS_CSI: - switch(ch) { - case 'A': - case 'F': - readline_up_char(rs); - break; - case 'B': - case 'E': - readline_down_char(rs); - break; + switch (ch) { + case 'A': + case 'F': + readline_up_char(rs); + break; + case 'B': + case 'E': + readline_down_char(rs); + break; case 'D': readline_backward_char(rs); break; @@ -444,7 +459,7 @@ void readline_handle_byte(ReadLineState *rs, int ch) rs->esc_param = rs->esc_param * 10 + (ch - '0'); goto the_end; case '~': - switch(rs->esc_param) { + switch (rs->esc_param) { case 1: readline_bol(rs); break; @@ -463,7 +478,7 @@ void readline_handle_byte(ReadLineState *rs, int ch) the_end: break; case IS_SS3: - switch(ch) { + switch (ch) { case 'F': readline_eol(rs); break; @@ -495,8 +510,9 @@ void readline_restart(ReadLineState *rs) const char *readline_get_history(ReadLineState *rs, unsigned int index) { - if (index >= READLINE_MAX_CMDS) + if (index >= READLINE_MAX_CMDS) { return NULL; + } return rs->history[index]; } @@ -2015,7 +2015,7 @@ typedef struct VGAInterfaceInfo { const char *class_names[2]; } VGAInterfaceInfo; -static VGAInterfaceInfo vga_interfaces[VGA_TYPE_MAX] = { +static const VGAInterfaceInfo vga_interfaces[VGA_TYPE_MAX] = { [VGA_NONE] = { .opt_name = "none", }, @@ -2061,7 +2061,7 @@ static VGAInterfaceInfo vga_interfaces[VGA_TYPE_MAX] = { static bool vga_interface_available(VGAInterfaceType t) { - VGAInterfaceInfo *ti = &vga_interfaces[t]; + const VGAInterfaceInfo *ti = &vga_interfaces[t]; assert(t < VGA_TYPE_MAX); return !ti->class_names[0] || @@ -2069,14 +2069,42 @@ static bool vga_interface_available(VGAInterfaceType t) object_class_by_name(ti->class_names[1]); } -static void select_vgahw(const char *p) +static const char * +get_default_vga_model(const MachineClass *machine_class) +{ + if (machine_class->default_display) { + return machine_class->default_display; + } else if (vga_interface_available(VGA_CIRRUS)) { + return "cirrus"; + } else if (vga_interface_available(VGA_STD)) { + return "std"; + } + + return NULL; +} + +static void select_vgahw(const MachineClass *machine_class, const char *p) { const char *opts; int t; + if (g_str_equal(p, "help")) { + const char *def = get_default_vga_model(machine_class); + + for (t = 0; t < VGA_TYPE_MAX; t++) { + const VGAInterfaceInfo *ti = &vga_interfaces[t]; + + if (vga_interface_available(t) && ti->opt_name) { + printf("%-20s %s%s\n", ti->opt_name, ti->name ?: "", + g_str_equal(ti->opt_name, def) ? " (default)" : ""); + } + } + exit(0); + } + assert(vga_interface_type == VGA_NONE); for (t = 0; t < VGA_TYPE_MAX; t++) { - VGAInterfaceInfo *ti = &vga_interfaces[t]; + const VGAInterfaceInfo *ti = &vga_interfaces[t]; if (ti->opt_name && strstart(p, ti->opt_name, &opts)) { if (!vga_interface_available(t)) { error_report("%s not available", ti->name); @@ -4424,16 +4452,10 @@ int main(int argc, char **argv, char **envp) /* If no default VGA is requested, the default is "none". */ if (default_vga) { - if (machine_class->default_display) { - vga_model = machine_class->default_display; - } else if (vga_interface_available(VGA_CIRRUS)) { - vga_model = "cirrus"; - } else if (vga_interface_available(VGA_STD)) { - vga_model = "std"; - } + vga_model = get_default_vga_model(machine_class); } if (vga_model) { - select_vgahw(vga_model); + select_vgahw(machine_class, vga_model); } if (watchdog) { |