diff options
122 files changed, 3981 insertions, 1022 deletions
@@ -66,6 +66,9 @@ libuser *.cp *.dvi *.exe +*.dll +*.so +*.mo *.fn *.ky *.log @@ -127,13 +127,29 @@ defconfig: ifneq ($(wildcard config-host.mak),) include $(SRC_PATH)/Makefile.objs +endif + +dummy := $(call unnest-vars,, \ + stub-obj-y \ + util-obj-y \ + qga-obj-y \ + block-obj-y \ + block-obj-m \ + common-obj-y \ + common-obj-m) + +ifneq ($(wildcard config-host.mak),) include $(SRC_PATH)/tests/Makefile endif ifeq ($(CONFIG_SMARTCARD_NSS),y) include $(SRC_PATH)/libcacard/Makefile endif -all: $(DOCS) $(TOOLS) $(HELPERS-y) recurse-all +all: $(DOCS) $(TOOLS) $(HELPERS-y) recurse-all modules + +vl.o: QEMU_CFLAGS+=$(GPROF_CFLAGS) + +vl.o: QEMU_CFLAGS+=$(SDL_CFLAGS) config-host.h: config-host.h-timestamp config-host.h-timestamp: config-host.mak @@ -192,6 +208,9 @@ Makefile: $(version-obj-y) $(version-lobj-y) libqemustub.a: $(stub-obj-y) libqemuutil.a: $(util-obj-y) qapi-types.o qapi-visit.o +block-modules = $(foreach o,$(block-obj-m),"$(basename $(subst /,-,$o))",) NULL +util/module.o-cflags = -D'CONFIG_BLOCK_MODULES=$(block-modules)' + ###################################################################### qemu-img.o: qemu-img-cmds.h @@ -247,6 +266,8 @@ clean: rm -f qemu-options.def find . -name '*.[oda]' -type f -exec rm -f {} + find . -name '*.l[oa]' -type f -exec rm -f {} + + find . -name '*$(DSOSUF)' -type f -exec rm -f {} + + find . -name '*.mo' -type f -exec rm -f {} + rm -f $(filter-out %.tlb,$(TOOLS)) $(HELPERS-y) qemu-ga TAGS cscope.* *.pod *~ */*~ rm -f fsdev/*.pod rm -rf .libs */.libs @@ -354,6 +375,12 @@ install-datadir install-localstatedir ifneq ($(TOOLS),) $(INSTALL_PROG) $(STRIP_OPT) $(TOOLS) "$(DESTDIR)$(bindir)" endif +ifneq ($(CONFIG_MODULES),) + $(INSTALL_DIR) "$(DESTDIR)$(qemu_moddir)" + for s in $(patsubst %.mo,%$(DSOSUF),$(modules-m)); do \ + $(INSTALL_PROG) $(STRIP_OPT) $$s "$(DESTDIR)$(qemu_moddir)/$${s//\//-}"; \ + done +endif ifneq ($(HELPERS-y),) $(INSTALL_DIR) "$(DESTDIR)$(libexecdir)" $(INSTALL_PROG) $(STRIP_OPT) $(HELPERS-y) "$(DESTDIR)$(libexecdir)" diff --git a/Makefile.objs b/Makefile.objs index ac1d0e1..4a62913 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -19,6 +19,8 @@ block-obj-y += qemu-coroutine.o qemu-coroutine-lock.o qemu-coroutine-io.o block-obj-y += qemu-coroutine-sleep.o block-obj-y += coroutine-$(CONFIG_COROUTINE_BACKEND).o +block-obj-m = block/ + ifeq ($(CONFIG_VIRTIO)$(CONFIG_VIRTFS)$(CONFIG_PCI),yyy) # Lots of the fsdev/9pcode is pulled in by vl.c via qemu_fsdev_add. # only pull in the actual virtio-9p device if we also enabled virtio. @@ -41,7 +43,7 @@ libcacard-y += libcacard/vcardt.o # single QEMU executable should support all CPUs and machines. ifeq ($(CONFIG_SOFTMMU),y) -common-obj-y = $(block-obj-y) blockdev.o blockdev-nbd.o block/ +common-obj-y = blockdev.o blockdev-nbd.o block/ common-obj-y += net/ common-obj-y += qdev-monitor.o device-hotplug.o common-obj-$(CONFIG_WIN32) += os-win32.o @@ -111,18 +113,3 @@ version-lobj-$(CONFIG_WIN32) += $(BUILD_DIR)/version.lo # by libqemuutil.a. These should be moved to a separate .json schema. qga-obj-y = qga/ qapi-types.o qapi-visit.o qga-vss-dll-obj-y = qga/ - -vl.o: QEMU_CFLAGS+=$(GPROF_CFLAGS) - -vl.o: QEMU_CFLAGS+=$(SDL_CFLAGS) - -QEMU_CFLAGS+=$(GLIB_CFLAGS) - -nested-vars += \ - stub-obj-y \ - util-obj-y \ - qga-obj-y \ - qga-vss-dll-obj-y \ - block-obj-y \ - common-obj-y -dummy := $(call unnest-vars) diff --git a/Makefile.target b/Makefile.target index af6ac7e..ba12340 100644 --- a/Makefile.target +++ b/Makefile.target @@ -130,8 +130,6 @@ else obj-y += hw/$(TARGET_BASE_ARCH)/ endif -main.o: QEMU_CFLAGS+=$(GPROF_CFLAGS) - GENERATED_HEADERS += hmp-commands.h qmp-commands-old.h endif # CONFIG_SOFTMMU @@ -139,13 +137,26 @@ endif # CONFIG_SOFTMMU # Workaround for http://gcc.gnu.org/PR55489, see configure. %/translate.o: QEMU_CFLAGS += $(TRANSLATE_OPT_CFLAGS) -nested-vars += obj-y +dummy := $(call unnest-vars,,obj-y) + +# we are making another call to unnest-vars with different vars, protect obj-y, +# it can be overriden in subdir Makefile.objs +obj-y-save := $(obj-y) -# This resolves all nested paths, so it must come last +block-obj-y := +common-obj-y := include $(SRC_PATH)/Makefile.objs +dummy := $(call unnest-vars,.., \ + block-obj-y \ + block-obj-m \ + common-obj-y \ + common-obj-m) + +# Now restore obj-y +obj-y := $(obj-y-save) -all-obj-y = $(obj-y) -all-obj-y += $(addprefix ../, $(common-obj-y)) +all-obj-y = $(obj-y) $(common-obj-y) +all-obj-$(CONFIG_SOFTMMU) += $(block-obj-y) ifndef CONFIG_HAIKU LIBS+=-lm @@ -955,53 +955,27 @@ free_and_fail: /* * Opens a file using a protocol (file, host_device, nbd, ...) * - * options is a QDict of options to pass to the block drivers, or NULL for an - * empty set of options. The reference to the QDict belongs to the block layer - * after the call (even on failure), so if the caller intends to reuse the - * dictionary, it needs to use QINCREF() before calling bdrv_file_open. + * options is an indirect pointer to a QDict of options to pass to the block + * drivers, or pointer to NULL for an empty set of options. If this function + * takes ownership of the QDict reference, it will set *options to NULL; + * otherwise, it will contain unused/unrecognized options after this function + * returns. Then, the caller is responsible for freeing it. If it intends to + * reuse the QDict, QINCREF() should be called beforehand. */ -int bdrv_file_open(BlockDriverState **pbs, const char *filename, - const char *reference, QDict *options, int flags, - Error **errp) +static int bdrv_file_open(BlockDriverState *bs, const char *filename, + QDict **options, int flags, Error **errp) { - BlockDriverState *bs = NULL; BlockDriver *drv; const char *drvname; bool allow_protocol_prefix = false; Error *local_err = NULL; int ret; - /* NULL means an empty set of options */ - if (options == NULL) { - options = qdict_new(); - } - - if (reference) { - if (filename || qdict_size(options)) { - error_setg(errp, "Cannot reference an existing block device with " - "additional options or a new filename"); - return -EINVAL; - } - QDECREF(options); - - bs = bdrv_lookup_bs(reference, reference, errp); - if (!bs) { - return -ENODEV; - } - bdrv_ref(bs); - *pbs = bs; - return 0; - } - - bs = bdrv_new(""); - bs->options = options; - options = qdict_clone_shallow(options); - /* Fetch the file name from the options QDict if necessary */ if (!filename) { - filename = qdict_get_try_str(options, "filename"); - } else if (filename && !qdict_haskey(options, "filename")) { - qdict_put(options, "filename", qstring_from_str(filename)); + filename = qdict_get_try_str(*options, "filename"); + } else if (filename && !qdict_haskey(*options, "filename")) { + qdict_put(*options, "filename", qstring_from_str(filename)); allow_protocol_prefix = true; } else { error_setg(errp, "Can't specify 'file' and 'filename' options at the " @@ -1011,13 +985,13 @@ int bdrv_file_open(BlockDriverState **pbs, const char *filename, } /* Find the right block driver */ - drvname = qdict_get_try_str(options, "driver"); + drvname = qdict_get_try_str(*options, "driver"); if (drvname) { drv = bdrv_find_format(drvname); if (!drv) { error_setg(errp, "Unknown driver '%s'", drvname); } - qdict_del(options, "driver"); + qdict_del(*options, "driver"); } else if (filename) { drv = bdrv_find_protocol(filename, allow_protocol_prefix); if (!drv) { @@ -1036,46 +1010,30 @@ int bdrv_file_open(BlockDriverState **pbs, const char *filename, /* Parse the filename and open it */ if (drv->bdrv_parse_filename && filename) { - drv->bdrv_parse_filename(filename, options, &local_err); + drv->bdrv_parse_filename(filename, *options, &local_err); if (local_err) { error_propagate(errp, local_err); ret = -EINVAL; goto fail; } - qdict_del(options, "filename"); + qdict_del(*options, "filename"); } if (!drv->bdrv_file_open) { - ret = bdrv_open(bs, filename, options, flags, drv, &local_err); - options = NULL; + ret = bdrv_open(&bs, filename, NULL, *options, flags, drv, &local_err); + *options = NULL; } else { - ret = bdrv_open_common(bs, NULL, options, flags, drv, &local_err); + ret = bdrv_open_common(bs, NULL, *options, flags, drv, &local_err); } if (ret < 0) { error_propagate(errp, local_err); goto fail; } - /* Check if any unknown options were used */ - if (options && (qdict_size(options) != 0)) { - const QDictEntry *entry = qdict_first(options); - error_setg(errp, "Block protocol '%s' doesn't support the option '%s'", - drv->format_name, entry->key); - ret = -EINVAL; - goto fail; - } - QDECREF(options); - bs->growable = 1; - *pbs = bs; return 0; fail: - QDECREF(options); - if (!bs->drv) { - QDECREF(bs->options); - } - bdrv_unref(bs); return ret; } @@ -1115,8 +1073,6 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *options, Error **errp) sizeof(backing_filename)); } - bs->backing_hd = bdrv_new(""); - if (bs->backing_format[0] != '\0') { back_drv = bdrv_find_format(bs->backing_format); } @@ -1125,11 +1081,11 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *options, Error **errp) back_flags = bs->open_flags & ~(BDRV_O_RDWR | BDRV_O_SNAPSHOT | BDRV_O_COPY_ON_READ); - ret = bdrv_open(bs->backing_hd, - *backing_filename ? backing_filename : NULL, options, + assert(bs->backing_hd == NULL); + ret = bdrv_open(&bs->backing_hd, + *backing_filename ? backing_filename : NULL, NULL, options, back_flags, back_drv, &local_err); if (ret < 0) { - bdrv_unref(bs->backing_hd); bs->backing_hd = NULL; bs->open_flags |= BDRV_O_NO_BACKING; error_setg(errp, "Could not open backing file: %s", @@ -1153,10 +1109,6 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *options, Error **errp) * Opens a disk image whose options are given as BlockdevRef in another block * device's options. * - * If force_raw is true, bdrv_file_open() will be used, thereby preventing any - * image format auto-detection. If it is false and a filename is given, - * bdrv_open() will be used for auto-detection. - * * If allow_none is true, no image will be opened if filename is false and no * BlockdevRef is given. *pbs will remain unchanged and 0 will be returned. * @@ -1166,16 +1118,21 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *options, Error **errp) * BlockdevRef. * * The BlockdevRef will be removed from the options QDict. + * + * To conform with the behavior of bdrv_open(), *pbs has to be NULL. */ int bdrv_open_image(BlockDriverState **pbs, const char *filename, QDict *options, const char *bdref_key, int flags, - bool force_raw, bool allow_none, Error **errp) + bool allow_none, Error **errp) { QDict *image_options; int ret; char *bdref_key_dot; const char *reference; + assert(pbs); + assert(*pbs == NULL); + bdref_key_dot = g_strdup_printf("%s.", bdref_key); qdict_extract_subqdict(options, &image_options, bdref_key_dot); g_free(bdref_key_dot); @@ -1192,30 +1149,7 @@ int bdrv_open_image(BlockDriverState **pbs, const char *filename, goto done; } - if (filename && !force_raw) { - /* If a filename is given and the block driver should be detected - automatically (instead of using none), use bdrv_open() in order to do - that auto-detection. */ - BlockDriverState *bs; - - if (reference) { - error_setg(errp, "Cannot reference an existing block device while " - "giving a filename"); - ret = -EINVAL; - goto done; - } - - bs = bdrv_new(""); - ret = bdrv_open(bs, filename, image_options, flags, NULL, errp); - if (ret < 0) { - bdrv_unref(bs); - } else { - *pbs = bs; - } - } else { - ret = bdrv_file_open(pbs, filename, reference, image_options, flags, - errp); - } + ret = bdrv_open(pbs, filename, reference, image_options, flags, NULL, errp); done: qdict_del(options, bdref_key); @@ -1229,17 +1163,58 @@ done: * empty set of options. The reference to the QDict belongs to the block layer * after the call (even on failure), so if the caller intends to reuse the * dictionary, it needs to use QINCREF() before calling bdrv_open. + * + * If *pbs is NULL, a new BDS will be created with a pointer to it stored there. + * If it is not NULL, the referenced BDS will be reused. + * + * The reference parameter may be used to specify an existing block device which + * should be opened. If specified, neither options nor a filename may be given, + * nor can an existing BDS be reused (that is, *pbs has to be NULL). */ -int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options, - int flags, BlockDriver *drv, Error **errp) +int bdrv_open(BlockDriverState **pbs, const char *filename, + const char *reference, QDict *options, int flags, + BlockDriver *drv, Error **errp) { int ret; /* TODO: extra byte is a hack to ensure MAX_PATH space on Windows. */ char tmp_filename[PATH_MAX + 1]; - BlockDriverState *file = NULL; + BlockDriverState *file = NULL, *bs; const char *drvname; Error *local_err = NULL; + assert(pbs); + + if (reference) { + bool options_non_empty = options ? qdict_size(options) : false; + QDECREF(options); + + if (*pbs) { + error_setg(errp, "Cannot reuse an existing BDS when referencing " + "another block device"); + return -EINVAL; + } + + if (filename || options_non_empty) { + error_setg(errp, "Cannot reference an existing block device with " + "additional options or a new filename"); + return -EINVAL; + } + + bs = bdrv_lookup_bs(reference, reference, errp); + if (!bs) { + return -ENODEV; + } + bdrv_ref(bs); + *pbs = bs; + return 0; + } + + if (*pbs) { + bs = *pbs; + } else { + bs = bdrv_new(""); + } + /* NULL means an empty set of options */ if (options == NULL) { options = qdict_new(); @@ -1248,6 +1223,19 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options, bs->options = options; options = qdict_clone_shallow(options); + if (flags & BDRV_O_PROTOCOL) { + assert(!drv); + ret = bdrv_file_open(bs, filename, &options, flags & ~BDRV_O_PROTOCOL, + &local_err); + if (!ret) { + goto done; + } else if (bs->drv) { + goto close_and_fail; + } else { + goto fail; + } + } + /* For snapshot=on, create a temporary qcow2 overlay */ if (flags & BDRV_O_SNAPSHOT) { BlockDriverState *bs1; @@ -1260,12 +1248,11 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options, instead of opening 'filename' directly */ /* Get the required size from the image */ - bs1 = bdrv_new(""); QINCREF(options); - ret = bdrv_open(bs1, filename, options, BDRV_O_NO_BACKING, + bs1 = NULL; + ret = bdrv_open(&bs1, filename, NULL, options, BDRV_O_NO_BACKING, drv, &local_err); if (ret < 0) { - bdrv_unref(bs1); goto fail; } total_size = bdrv_getlength(bs1) & BDRV_SECTOR_MASK; @@ -1322,9 +1309,10 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options, flags |= BDRV_O_ALLOW_RDWR; } + assert(file == NULL); ret = bdrv_open_image(&file, filename, options, "file", - bdrv_open_flags(bs, flags | BDRV_O_UNMAP), true, true, - &local_err); + bdrv_open_flags(bs, flags | BDRV_O_UNMAP) | + BDRV_O_PROTOCOL, true, &local_err); if (ret < 0) { goto fail; } @@ -1377,12 +1365,18 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options, } } +done: /* Check if any unknown options were used */ - if (qdict_size(options) != 0) { + if (options && (qdict_size(options) != 0)) { const QDictEntry *entry = qdict_first(options); - error_setg(errp, "Block format '%s' used by device '%s' doesn't " - "support the option '%s'", drv->format_name, bs->device_name, - entry->key); + if (flags & BDRV_O_PROTOCOL) { + error_setg(errp, "Block protocol '%s' doesn't support the option " + "'%s'", drv->format_name, entry->key); + } else { + error_setg(errp, "Block format '%s' used by device '%s' doesn't " + "support the option '%s'", drv->format_name, + bs->device_name, entry->key); + } ret = -EINVAL; goto close_and_fail; @@ -1393,6 +1387,7 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options, bdrv_dev_change_media_cb(bs, true); } + *pbs = bs; return 0; unlink_and_fail: @@ -1406,13 +1401,24 @@ fail: QDECREF(bs->options); QDECREF(options); bs->options = NULL; + if (!*pbs) { + /* If *pbs is NULL, a new BDS has been created in this function and + needs to be freed now. Otherwise, it does not need to be closed, + since it has not really been opened yet. */ + bdrv_unref(bs); + } if (local_err) { error_propagate(errp, local_err); } return ret; close_and_fail: - bdrv_close(bs); + /* See fail path, but now the BDS has to be always closed */ + if (*pbs) { + bdrv_close(bs); + } else { + bdrv_unref(bs); + } QDECREF(options); if (local_err) { error_propagate(errp, local_err); @@ -5290,9 +5296,8 @@ void bdrv_img_create(const char *filename, const char *fmt, back_flags = flags & ~(BDRV_O_RDWR | BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING); - bs = bdrv_new(""); - - ret = bdrv_open(bs, backing_file->value.s, NULL, back_flags, + bs = NULL; + ret = bdrv_open(&bs, backing_file->value.s, NULL, NULL, back_flags, backing_drv, &local_err); if (ret < 0) { error_setg_errno(errp, -ret, "Could not open '%s': %s", @@ -5300,7 +5305,6 @@ void bdrv_img_create(const char *filename, const char *fmt, error_get_pretty(local_err)); error_free(local_err); local_err = NULL; - bdrv_unref(bs); goto out; } bdrv_get_geometry(bs, &size); @@ -5416,11 +5420,7 @@ bool bdrv_is_first_non_filter(BlockDriverState *candidate) QTAILQ_FOREACH(bs, &bdrv_states, device_list) { bool perm; - if (!bs->file) { - continue; - } - - perm = bdrv_recurse_is_first_non_filter(bs->file, candidate); + perm = bdrv_recurse_is_first_non_filter(bs, candidate); /* candidate is the first non filter */ if (perm) { diff --git a/block/Makefile.objs b/block/Makefile.objs index e254a21..fd88c03 100644 --- a/block/Makefile.objs +++ b/block/Makefile.objs @@ -3,6 +3,7 @@ block-obj-y += qcow2.o qcow2-refcount.o qcow2-cluster.o qcow2-snapshot.o qcow2-c block-obj-y += qed.o qed-gencb.o qed-l2-cache.o qed-table.o qed-cluster.o block-obj-y += qed-check.o block-obj-$(CONFIG_VHDX) += vhdx.o vhdx-endian.o vhdx-log.o +block-obj-$(CONFIG_QUORUM) += quorum.o block-obj-y += parallels.o blkdebug.o blkverify.o block-obj-y += snapshot.o qapi.o block-obj-$(CONFIG_WIN32) += raw-win32.o win32-aio.o @@ -24,4 +25,15 @@ common-obj-y += commit.o common-obj-y += mirror.o common-obj-y += backup.o -$(obj)/curl.o: QEMU_CFLAGS+=$(CURL_CFLAGS) +iscsi.o-cflags := $(LIBISCSI_CFLAGS) +iscsi.o-libs := $(LIBISCSI_LIBS) +curl.o-cflags := $(CURL_CFLAGS) +curl.o-libs := $(CURL_LIBS) +rbd.o-cflags := $(RBD_CFLAGS) +rbd.o-libs := $(RBD_LIBS) +gluster.o-cflags := $(GLUSTERFS_CFLAGS) +gluster.o-libs := $(GLUSTERFS_LIBS) +ssh.o-cflags := $(LIBSSH2_CFLAGS) +ssh.o-libs := $(LIBSSH2_LIBS) +qcow.o-libs := -lz +linux-aio.o-libs := -laio diff --git a/block/blkdebug.c b/block/blkdebug.c index ee10013..380c736 100644 --- a/block/blkdebug.c +++ b/block/blkdebug.c @@ -410,8 +410,9 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags, s->state = 1; /* Open the backing file */ + assert(bs->file == NULL); ret = bdrv_open_image(&bs->file, qemu_opt_get(opts, "x-image"), options, "image", - flags, true, false, &local_err); + flags | BDRV_O_PROTOCOL, false, &local_err); if (ret < 0) { error_propagate(errp, local_err); goto out; diff --git a/block/blkverify.c b/block/blkverify.c index 1563c88..b98b08b 100644 --- a/block/blkverify.c +++ b/block/blkverify.c @@ -135,16 +135,18 @@ static int blkverify_open(BlockDriverState *bs, QDict *options, int flags, } /* Open the raw file */ + assert(bs->file == NULL); ret = bdrv_open_image(&bs->file, qemu_opt_get(opts, "x-raw"), options, - "raw", flags, true, false, &local_err); + "raw", flags | BDRV_O_PROTOCOL, false, &local_err); if (ret < 0) { error_propagate(errp, local_err); goto fail; } /* Open the test file */ + assert(s->test_file == NULL); ret = bdrv_open_image(&s->test_file, qemu_opt_get(opts, "x-image"), options, - "test", flags, false, false, &local_err); + "test", flags, false, &local_err); if (ret < 0) { error_propagate(errp, local_err); s->test_file = NULL; @@ -171,110 +173,6 @@ static int64_t blkverify_getlength(BlockDriverState *bs) return bdrv_getlength(s->test_file); } -/** - * Check that I/O vector contents are identical - * - * @a: I/O vector - * @b: I/O vector - * @ret: Offset to first mismatching byte or -1 if match - */ -static ssize_t blkverify_iovec_compare(QEMUIOVector *a, QEMUIOVector *b) -{ - int i; - ssize_t offset = 0; - - assert(a->niov == b->niov); - for (i = 0; i < a->niov; i++) { - size_t len = 0; - uint8_t *p = (uint8_t *)a->iov[i].iov_base; - uint8_t *q = (uint8_t *)b->iov[i].iov_base; - - assert(a->iov[i].iov_len == b->iov[i].iov_len); - while (len < a->iov[i].iov_len && *p++ == *q++) { - len++; - } - - offset += len; - - if (len != a->iov[i].iov_len) { - return offset; - } - } - return -1; -} - -typedef struct { - int src_index; - struct iovec *src_iov; - void *dest_base; -} IOVectorSortElem; - -static int sortelem_cmp_src_base(const void *a, const void *b) -{ - const IOVectorSortElem *elem_a = a; - const IOVectorSortElem *elem_b = b; - - /* Don't overflow */ - if (elem_a->src_iov->iov_base < elem_b->src_iov->iov_base) { - return -1; - } else if (elem_a->src_iov->iov_base > elem_b->src_iov->iov_base) { - return 1; - } else { - return 0; - } -} - -static int sortelem_cmp_src_index(const void *a, const void *b) -{ - const IOVectorSortElem *elem_a = a; - const IOVectorSortElem *elem_b = b; - - return elem_a->src_index - elem_b->src_index; -} - -/** - * Copy contents of I/O vector - * - * The relative relationships of overlapping iovecs are preserved. This is - * necessary to ensure identical semantics in the cloned I/O vector. - */ -static void blkverify_iovec_clone(QEMUIOVector *dest, const QEMUIOVector *src, - void *buf) -{ - IOVectorSortElem sortelems[src->niov]; - void *last_end; - int i; - - /* Sort by source iovecs by base address */ - for (i = 0; i < src->niov; i++) { - sortelems[i].src_index = i; - sortelems[i].src_iov = &src->iov[i]; - } - qsort(sortelems, src->niov, sizeof(sortelems[0]), sortelem_cmp_src_base); - - /* Allocate buffer space taking into account overlapping iovecs */ - last_end = NULL; - for (i = 0; i < src->niov; i++) { - struct iovec *cur = sortelems[i].src_iov; - ptrdiff_t rewind = 0; - - /* Detect overlap */ - if (last_end && last_end > cur->iov_base) { - rewind = last_end - cur->iov_base; - } - - sortelems[i].dest_base = buf - rewind; - buf += cur->iov_len - MIN(rewind, cur->iov_len); - last_end = MAX(cur->iov_base + cur->iov_len, last_end); - } - - /* Sort by source iovec index and build destination iovec */ - qsort(sortelems, src->niov, sizeof(sortelems[0]), sortelem_cmp_src_index); - for (i = 0; i < src->niov; i++) { - qemu_iovec_add(dest, sortelems[i].dest_base, src->iov[i].iov_len); - } -} - static BlkverifyAIOCB *blkverify_aio_get(BlockDriverState *bs, bool is_write, int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, @@ -338,7 +236,7 @@ static void blkverify_aio_cb(void *opaque, int ret) static void blkverify_verify_readv(BlkverifyAIOCB *acb) { - ssize_t offset = blkverify_iovec_compare(acb->qiov, &acb->raw_qiov); + ssize_t offset = qemu_iovec_compare(acb->qiov, &acb->raw_qiov); if (offset != -1) { blkverify_err(acb, "contents mismatch in sector %" PRId64, acb->sector_num + (int64_t)(offset / BDRV_SECTOR_SIZE)); @@ -356,7 +254,7 @@ static BlockDriverAIOCB *blkverify_aio_readv(BlockDriverState *bs, acb->verify = blkverify_verify_readv; acb->buf = qemu_blockalign(bs->file, qiov->size); qemu_iovec_init(&acb->raw_qiov, acb->qiov->niov); - blkverify_iovec_clone(&acb->raw_qiov, qiov, acb->buf); + qemu_iovec_clone(&acb->raw_qiov, qiov, acb->buf); bdrv_aio_readv(s->test_file, sector_num, qiov, nb_sectors, blkverify_aio_cb, acb); diff --git a/block/bochs.c b/block/bochs.c index 51d9a90..4d6403f 100644 --- a/block/bochs.c +++ b/block/bochs.c @@ -129,7 +129,8 @@ static int bochs_open(BlockDriverState *bs, QDict *options, int flags, strcmp(bochs.subtype, GROWING_TYPE) || ((le32_to_cpu(bochs.version) != HEADER_VERSION) && (le32_to_cpu(bochs.version) != HEADER_V1))) { - return -EMEDIUMTYPE; + error_setg(errp, "Image not in Bochs format"); + return -EINVAL; } if (le32_to_cpu(bochs.version) == HEADER_V1) { diff --git a/block/cow.c b/block/cow.c index 7fc0b12..30deb88 100644 --- a/block/cow.c +++ b/block/cow.c @@ -74,7 +74,8 @@ static int cow_open(BlockDriverState *bs, QDict *options, int flags, } if (be32_to_cpu(cow_header.magic) != COW_MAGIC) { - ret = -EMEDIUMTYPE; + error_setg(errp, "Image not in COW format"); + ret = -EINVAL; goto fail; } @@ -82,7 +83,7 @@ static int cow_open(BlockDriverState *bs, QDict *options, int flags, char version[64]; snprintf(version, sizeof(version), "COW version %d", cow_header.version); - qerror_report(QERR_UNKNOWN_BLOCK_FORMAT_FEATURE, + error_set(errp, QERR_UNKNOWN_BLOCK_FORMAT_FEATURE, bs->device_name, "cow", version); ret = -ENOTSUP; goto fail; @@ -346,16 +347,15 @@ static int cow_create(const char *filename, QEMUOptionParameter *options, ret = bdrv_create_file(filename, options, &local_err); if (ret < 0) { - qerror_report_err(local_err); - error_free(local_err); + error_propagate(errp, local_err); return ret; } - ret = bdrv_file_open(&cow_bs, filename, NULL, NULL, BDRV_O_RDWR, - &local_err); + cow_bs = NULL; + ret = bdrv_open(&cow_bs, filename, NULL, NULL, + BDRV_O_RDWR | BDRV_O_PROTOCOL, NULL, &local_err); if (ret < 0) { - qerror_report_err(local_err); - error_free(local_err); + error_propagate(errp, local_err); return ret; } diff --git a/block/curl.c b/block/curl.c index bb1fc4a..3494c6d 100644 --- a/block/curl.c +++ b/block/curl.c @@ -456,30 +456,27 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags, static int inited = 0; if (flags & BDRV_O_RDWR) { - qerror_report(ERROR_CLASS_GENERIC_ERROR, - "curl block device does not support writes"); + error_setg(errp, "curl block device does not support writes"); return -EROFS; } opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort); qemu_opts_absorb_qdict(opts, options, &local_err); if (local_err) { - qerror_report_err(local_err); - error_free(local_err); + error_propagate(errp, local_err); goto out_noclean; } s->readahead_size = qemu_opt_get_size(opts, "readahead", READ_AHEAD_SIZE); if ((s->readahead_size & 0x1ff) != 0) { - fprintf(stderr, "HTTP_READAHEAD_SIZE %zd is not a multiple of 512\n", - s->readahead_size); + error_setg(errp, "HTTP_READAHEAD_SIZE %zd is not a multiple of 512", + s->readahead_size); goto out_noclean; } file = qemu_opt_get(opts, "url"); if (file == NULL) { - qerror_report(ERROR_CLASS_GENERIC_ERROR, "curl block driver requires " - "an 'url' option"); + error_setg(errp, "curl block driver requires an 'url' option"); goto out_noclean; } diff --git a/block/gluster.c b/block/gluster.c index 58eab07..14d390b 100644 --- a/block/gluster.c +++ b/block/gluster.c @@ -127,7 +127,7 @@ static int qemu_gluster_parseuri(GlusterConf *gconf, const char *filename) } /* transport */ - if (!strcmp(uri->scheme, "gluster")) { + if (!uri->scheme || !strcmp(uri->scheme, "gluster")) { gconf->transport = g_strdup("tcp"); } else if (!strcmp(uri->scheme, "gluster+tcp")) { gconf->transport = g_strdup("tcp"); @@ -163,7 +163,7 @@ static int qemu_gluster_parseuri(GlusterConf *gconf, const char *filename) } gconf->server = g_strdup(qp->p[0].value); } else { - gconf->server = g_strdup(uri->server); + gconf->server = g_strdup(uri->server ? uri->server : "localhost"); gconf->port = uri->port; } @@ -175,7 +175,8 @@ out: return ret; } -static struct glfs *qemu_gluster_init(GlusterConf *gconf, const char *filename) +static struct glfs *qemu_gluster_init(GlusterConf *gconf, const char *filename, + Error **errp) { struct glfs *glfs = NULL; int ret; @@ -183,8 +184,8 @@ static struct glfs *qemu_gluster_init(GlusterConf *gconf, const char *filename) ret = qemu_gluster_parseuri(gconf, filename); if (ret < 0) { - error_report("Usage: file=gluster[+transport]://[server[:port]]/" - "volname/image[?socket=...]"); + error_setg(errp, "Usage: file=gluster[+transport]://[server[:port]]/" + "volname/image[?socket=...]"); errno = -ret; goto out; } @@ -211,9 +212,11 @@ static struct glfs *qemu_gluster_init(GlusterConf *gconf, const char *filename) ret = glfs_init(glfs); if (ret) { - error_report("Gluster connection failed for server=%s port=%d " - "volume=%s image=%s transport=%s", gconf->server, gconf->port, - gconf->volname, gconf->image, gconf->transport); + error_setg_errno(errp, errno, + "Gluster connection failed for server=%s port=%d " + "volume=%s image=%s transport=%s", gconf->server, + gconf->port, gconf->volname, gconf->image, + gconf->transport); goto out; } return glfs; @@ -283,15 +286,14 @@ static int qemu_gluster_open(BlockDriverState *bs, QDict *options, opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort); qemu_opts_absorb_qdict(opts, options, &local_err); if (local_err) { - qerror_report_err(local_err); - error_free(local_err); + error_propagate(errp, local_err); ret = -EINVAL; goto out; } filename = qemu_opt_get(opts, "filename"); - s->glfs = qemu_gluster_init(gconf, filename); + s->glfs = qemu_gluster_init(gconf, filename, errp); if (!s->glfs) { ret = -errno; goto out; @@ -389,9 +391,9 @@ static int qemu_gluster_create(const char *filename, int64_t total_size = 0; GlusterConf *gconf = g_malloc0(sizeof(GlusterConf)); - glfs = qemu_gluster_init(gconf, filename); + glfs = qemu_gluster_init(gconf, filename, errp); if (!glfs) { - ret = -errno; + ret = -EINVAL; goto out; } diff --git a/block/iscsi.c b/block/iscsi.c index f8e496f..41ec097 100644 --- a/block/iscsi.c +++ b/block/iscsi.c @@ -856,7 +856,8 @@ retry: #endif /* SCSI_SENSE_ASCQ_CAPACITY_DATA_HAS_CHANGED */ -static int parse_chap(struct iscsi_context *iscsi, const char *target) +static void parse_chap(struct iscsi_context *iscsi, const char *target, + Error **errp) { QemuOptsList *list; QemuOpts *opts; @@ -865,37 +866,35 @@ static int parse_chap(struct iscsi_context *iscsi, const char *target) list = qemu_find_opts("iscsi"); if (!list) { - return 0; + return; } opts = qemu_opts_find(list, target); if (opts == NULL) { opts = QTAILQ_FIRST(&list->head); if (!opts) { - return 0; + return; } } user = qemu_opt_get(opts, "user"); if (!user) { - return 0; + return; } password = qemu_opt_get(opts, "password"); if (!password) { - error_report("CHAP username specified but no password was given"); - return -1; + error_setg(errp, "CHAP username specified but no password was given"); + return; } if (iscsi_set_initiator_username_pwd(iscsi, user, password)) { - error_report("Failed to set initiator username and password"); - return -1; + error_setg(errp, "Failed to set initiator username and password"); } - - return 0; } -static void parse_header_digest(struct iscsi_context *iscsi, const char *target) +static void parse_header_digest(struct iscsi_context *iscsi, const char *target, + Error **errp) { QemuOptsList *list; QemuOpts *opts; @@ -928,7 +927,7 @@ static void parse_header_digest(struct iscsi_context *iscsi, const char *target) } else if (!strcmp(digest, "NONE-CRC32C")) { iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_NONE_CRC32C); } else { - error_report("Invalid header-digest setting : %s", digest); + error_setg(errp, "Invalid header-digest setting : %s", digest); } } @@ -986,12 +985,11 @@ static void iscsi_nop_timed_event(void *opaque) } #endif -static int iscsi_readcapacity_sync(IscsiLun *iscsilun) +static void iscsi_readcapacity_sync(IscsiLun *iscsilun, Error **errp) { struct scsi_task *task = NULL; struct scsi_readcapacity10 *rc10 = NULL; struct scsi_readcapacity16 *rc16 = NULL; - int ret = 0; int retries = ISCSI_CMD_RETRIES; do { @@ -1006,8 +1004,7 @@ static int iscsi_readcapacity_sync(IscsiLun *iscsilun) if (task != NULL && task->status == SCSI_STATUS_GOOD) { rc16 = scsi_datain_unmarshall(task); if (rc16 == NULL) { - error_report("iSCSI: Failed to unmarshall readcapacity16 data."); - ret = -EINVAL; + error_setg(errp, "iSCSI: Failed to unmarshall readcapacity16 data."); } else { iscsilun->block_size = rc16->block_length; iscsilun->num_blocks = rc16->returned_lba + 1; @@ -1021,8 +1018,7 @@ static int iscsi_readcapacity_sync(IscsiLun *iscsilun) if (task != NULL && task->status == SCSI_STATUS_GOOD) { rc10 = scsi_datain_unmarshall(task); if (rc10 == NULL) { - error_report("iSCSI: Failed to unmarshall readcapacity10 data."); - ret = -EINVAL; + error_setg(errp, "iSCSI: Failed to unmarshall readcapacity10 data."); } else { iscsilun->block_size = rc10->block_size; if (rc10->lba == 0) { @@ -1035,20 +1031,18 @@ static int iscsi_readcapacity_sync(IscsiLun *iscsilun) } break; default: - return 0; + return; } } while (task != NULL && task->status == SCSI_STATUS_CHECK_CONDITION && task->sense.key == SCSI_SENSE_UNIT_ATTENTION && retries-- > 0); if (task == NULL || task->status != SCSI_STATUS_GOOD) { - error_report("iSCSI: failed to send readcapacity10 command."); - ret = -EINVAL; + error_setg(errp, "iSCSI: failed to send readcapacity10 command."); } if (task) { scsi_free_scsi_task(task); } - return ret; } /* TODO Convert to fine grained options */ @@ -1065,35 +1059,36 @@ static QemuOptsList runtime_opts = { }, }; -static struct scsi_task *iscsi_do_inquiry(struct iscsi_context *iscsi, - int lun, int evpd, int pc) { - int full_size; - struct scsi_task *task = NULL; - task = iscsi_inquiry_sync(iscsi, lun, evpd, pc, 64); +static struct scsi_task *iscsi_do_inquiry(struct iscsi_context *iscsi, int lun, + int evpd, int pc, Error **errp) +{ + int full_size; + struct scsi_task *task = NULL; + task = iscsi_inquiry_sync(iscsi, lun, evpd, pc, 64); + if (task == NULL || task->status != SCSI_STATUS_GOOD) { + goto fail; + } + full_size = scsi_datain_getfullsize(task); + if (full_size > task->datain.size) { + scsi_free_scsi_task(task); + + /* we need more data for the full list */ + task = iscsi_inquiry_sync(iscsi, lun, evpd, pc, full_size); if (task == NULL || task->status != SCSI_STATUS_GOOD) { goto fail; } - full_size = scsi_datain_getfullsize(task); - if (full_size > task->datain.size) { - scsi_free_scsi_task(task); - - /* we need more data for the full list */ - task = iscsi_inquiry_sync(iscsi, lun, evpd, pc, full_size); - if (task == NULL || task->status != SCSI_STATUS_GOOD) { - goto fail; - } - } + } - return task; + return task; fail: - error_report("iSCSI: Inquiry command failed : %s", - iscsi_get_error(iscsi)); - if (task) { - scsi_free_scsi_task(task); - return NULL; - } + error_setg(errp, "iSCSI: Inquiry command failed : %s", + iscsi_get_error(iscsi)); + if (task) { + scsi_free_scsi_task(task); return NULL; + } + return NULL; } /* @@ -1119,27 +1114,25 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, int ret; if ((BDRV_SECTOR_SIZE % 512) != 0) { - error_report("iSCSI: Invalid BDRV_SECTOR_SIZE. " - "BDRV_SECTOR_SIZE(%lld) is not a multiple " - "of 512", BDRV_SECTOR_SIZE); + error_setg(errp, "iSCSI: Invalid BDRV_SECTOR_SIZE. " + "BDRV_SECTOR_SIZE(%lld) is not a multiple " + "of 512", BDRV_SECTOR_SIZE); return -EINVAL; } opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort); qemu_opts_absorb_qdict(opts, options, &local_err); if (local_err) { - qerror_report_err(local_err); - error_free(local_err); + error_propagate(errp, local_err); ret = -EINVAL; goto out; } filename = qemu_opt_get(opts, "filename"); - iscsi_url = iscsi_parse_full_url(iscsi, filename); if (iscsi_url == NULL) { - error_report("Failed to parse URL : %s", filename); + error_setg(errp, "Failed to parse URL : %s", filename); ret = -EINVAL; goto out; } @@ -1150,13 +1143,13 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, iscsi = iscsi_create_context(initiator_name); if (iscsi == NULL) { - error_report("iSCSI: Failed to create iSCSI context."); + error_setg(errp, "iSCSI: Failed to create iSCSI context."); ret = -ENOMEM; goto out; } if (iscsi_set_targetname(iscsi, iscsi_url->target)) { - error_report("iSCSI: Failed to set target name."); + error_setg(errp, "iSCSI: Failed to set target name."); ret = -EINVAL; goto out; } @@ -1165,21 +1158,22 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, ret = iscsi_set_initiator_username_pwd(iscsi, iscsi_url->user, iscsi_url->passwd); if (ret != 0) { - error_report("Failed to set initiator username and password"); + error_setg(errp, "Failed to set initiator username and password"); ret = -EINVAL; goto out; } } /* check if we got CHAP username/password via the options */ - if (parse_chap(iscsi, iscsi_url->target) != 0) { - error_report("iSCSI: Failed to set CHAP user/password"); + parse_chap(iscsi, iscsi_url->target, &local_err); + if (local_err != NULL) { + error_propagate(errp, local_err); ret = -EINVAL; goto out; } if (iscsi_set_session_type(iscsi, ISCSI_SESSION_NORMAL) != 0) { - error_report("iSCSI: Failed to set session type to normal."); + error_setg(errp, "iSCSI: Failed to set session type to normal."); ret = -EINVAL; goto out; } @@ -1187,10 +1181,15 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_NONE_CRC32C); /* check if we got HEADER_DIGEST via the options */ - parse_header_digest(iscsi, iscsi_url->target); + parse_header_digest(iscsi, iscsi_url->target, &local_err); + if (local_err != NULL) { + error_propagate(errp, local_err); + ret = -EINVAL; + goto out; + } if (iscsi_full_connect_sync(iscsi, iscsi_url->portal, iscsi_url->lun) != 0) { - error_report("iSCSI: Failed to connect to LUN : %s", + error_setg(errp, "iSCSI: Failed to connect to LUN : %s", iscsi_get_error(iscsi)); ret = -EINVAL; goto out; @@ -1202,14 +1201,14 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, task = iscsi_inquiry_sync(iscsi, iscsilun->lun, 0, 0, 36); if (task == NULL || task->status != SCSI_STATUS_GOOD) { - error_report("iSCSI: failed to send inquiry command."); + error_setg(errp, "iSCSI: failed to send inquiry command."); ret = -EINVAL; goto out; } inq = scsi_datain_unmarshall(task); if (inq == NULL) { - error_report("iSCSI: Failed to unmarshall inquiry data."); + error_setg(errp, "iSCSI: Failed to unmarshall inquiry data."); ret = -EINVAL; goto out; } @@ -1217,7 +1216,9 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, iscsilun->type = inq->periperal_device_type; iscsilun->has_write_same = true; - if ((ret = iscsi_readcapacity_sync(iscsilun)) != 0) { + iscsi_readcapacity_sync(iscsilun, &local_err); + if (local_err != NULL) { + error_propagate(errp, local_err); goto out; } bs->total_sectors = sector_lun2qemu(iscsilun->num_blocks, iscsilun); @@ -1235,14 +1236,15 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, if (iscsilun->lbpme) { struct scsi_inquiry_logical_block_provisioning *inq_lbp; task = iscsi_do_inquiry(iscsilun->iscsi, iscsilun->lun, 1, - SCSI_INQUIRY_PAGECODE_LOGICAL_BLOCK_PROVISIONING); + SCSI_INQUIRY_PAGECODE_LOGICAL_BLOCK_PROVISIONING, + errp); if (task == NULL) { ret = -EINVAL; goto out; } inq_lbp = scsi_datain_unmarshall(task); if (inq_lbp == NULL) { - error_report("iSCSI: failed to unmarshall inquiry datain blob"); + error_setg(errp, "iSCSI: failed to unmarshall inquiry datain blob"); ret = -EINVAL; goto out; } @@ -1255,14 +1257,14 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, if (iscsilun->lbp.lbpu || iscsilun->lbp.lbpws) { struct scsi_inquiry_block_limits *inq_bl; task = iscsi_do_inquiry(iscsilun->iscsi, iscsilun->lun, 1, - SCSI_INQUIRY_PAGECODE_BLOCK_LIMITS); + SCSI_INQUIRY_PAGECODE_BLOCK_LIMITS, errp); if (task == NULL) { ret = -EINVAL; goto out; } inq_bl = scsi_datain_unmarshall(task); if (inq_bl == NULL) { - error_report("iSCSI: failed to unmarshall inquiry datain blob"); + error_setg(errp, "iSCSI: failed to unmarshall inquiry datain blob"); ret = -EINVAL; goto out; } @@ -1353,14 +1355,16 @@ static int iscsi_reopen_prepare(BDRVReopenState *state, static int iscsi_truncate(BlockDriverState *bs, int64_t offset) { IscsiLun *iscsilun = bs->opaque; - int ret = 0; + Error *local_err = NULL; if (iscsilun->type != TYPE_DISK) { return -ENOTSUP; } - if ((ret = iscsi_readcapacity_sync(iscsilun)) != 0) { - return ret; + iscsi_readcapacity_sync(iscsilun, &local_err); + if (local_err != NULL) { + error_free(local_err); + return -EIO; } if (offset > iscsi_getlength(bs)) { diff --git a/block/nbd.c b/block/nbd.c index abae506..5512423 100644 --- a/block/nbd.c +++ b/block/nbd.c @@ -188,31 +188,28 @@ out: g_free(file); } -static int nbd_config(BDRVNBDState *s, QDict *options, char **export) +static void nbd_config(BDRVNBDState *s, QDict *options, char **export, + Error **errp) { Error *local_err = NULL; - if (qdict_haskey(options, "path")) { - if (qdict_haskey(options, "host")) { - qerror_report(ERROR_CLASS_GENERIC_ERROR, "path and host may not " - "be used at the same time."); - return -EINVAL; + if (qdict_haskey(options, "path") == qdict_haskey(options, "host")) { + if (qdict_haskey(options, "path")) { + error_setg(errp, "path and host may not be used at the same time."); + } else { + error_setg(errp, "one of path and host must be specified."); } - s->client.is_unix = true; - } else if (qdict_haskey(options, "host")) { - s->client.is_unix = false; - } else { - return -EINVAL; + return; } + s->client.is_unix = qdict_haskey(options, "path"); s->socket_opts = qemu_opts_create(&socket_optslist, NULL, 0, &error_abort); qemu_opts_absorb_qdict(s->socket_opts, options, &local_err); if (local_err) { - qerror_report_err(local_err); - error_free(local_err); - return -EINVAL; + error_propagate(errp, local_err); + return; } if (!qemu_opt_get(s->socket_opts, "port")) { @@ -223,19 +220,17 @@ static int nbd_config(BDRVNBDState *s, QDict *options, char **export) if (*export) { qdict_del(options, "export"); } - - return 0; } -static int nbd_establish_connection(BlockDriverState *bs) +static int nbd_establish_connection(BlockDriverState *bs, Error **errp) { BDRVNBDState *s = bs->opaque; int sock; if (s->client.is_unix) { - sock = unix_socket_outgoing(qemu_opt_get(s->socket_opts, "path")); + sock = unix_connect_opts(s->socket_opts, errp, NULL, NULL); } else { - sock = tcp_socket_outgoing_opts(s->socket_opts); + sock = inet_connect_opts(s->socket_opts, errp, NULL, NULL); if (sock >= 0) { socket_set_nodelay(sock); } @@ -256,17 +251,19 @@ static int nbd_open(BlockDriverState *bs, QDict *options, int flags, BDRVNBDState *s = bs->opaque; char *export = NULL; int result, sock; + Error *local_err = NULL; /* Pop the config into our state object. Exit if invalid. */ - result = nbd_config(s, options, &export); - if (result != 0) { - return result; + nbd_config(s, options, &export, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return -EINVAL; } /* establish TCP connection, return error if it fails * TODO: Configurable retry-until-timeout behaviour. */ - sock = nbd_establish_connection(bs); + sock = nbd_establish_connection(bs, errp); if (sock < 0) { return sock; } diff --git a/block/parallels.c b/block/parallels.c index 2121e43..3f588f5 100644 --- a/block/parallels.c +++ b/block/parallels.c @@ -85,7 +85,8 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags, if (memcmp(ph.magic, HEADER_MAGIC, 16) || (le32_to_cpu(ph.version) != HEADER_VERSION)) { - ret = -EMEDIUMTYPE; + error_setg(errp, "Image not in Parallels format"); + ret = -EINVAL; goto fail; } diff --git a/block/qcow.c b/block/qcow.c index 948b0c5..1e128be 100644 --- a/block/qcow.c +++ b/block/qcow.c @@ -113,23 +113,26 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, be64_to_cpus(&header.l1_table_offset); if (header.magic != QCOW_MAGIC) { - ret = -EMEDIUMTYPE; + error_setg(errp, "Image not in qcow format"); + ret = -EINVAL; goto fail; } if (header.version != QCOW_VERSION) { char version[64]; snprintf(version, sizeof(version), "QCOW version %d", header.version); - qerror_report(QERR_UNKNOWN_BLOCK_FORMAT_FEATURE, - bs->device_name, "qcow", version); + error_set(errp, QERR_UNKNOWN_BLOCK_FORMAT_FEATURE, + bs->device_name, "qcow", version); ret = -ENOTSUP; goto fail; } if (header.size <= 1 || header.cluster_bits < 9) { + error_setg(errp, "invalid value in qcow header"); ret = -EINVAL; goto fail; } if (header.crypt_method > QCOW_CRYPT_AES) { + error_setg(errp, "invalid encryption method in qcow header"); ret = -EINVAL; goto fail; } @@ -686,16 +689,15 @@ static int qcow_create(const char *filename, QEMUOptionParameter *options, ret = bdrv_create_file(filename, options, &local_err); if (ret < 0) { - qerror_report_err(local_err); - error_free(local_err); + error_propagate(errp, local_err); return ret; } - ret = bdrv_file_open(&qcow_bs, filename, NULL, NULL, BDRV_O_RDWR, - &local_err); + qcow_bs = NULL; + ret = bdrv_open(&qcow_bs, filename, NULL, NULL, + BDRV_O_RDWR | BDRV_O_PROTOCOL, NULL, &local_err); if (ret < 0) { - qerror_report_err(local_err); - error_free(local_err); + error_propagate(errp, local_err); return ret; } diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c index c57f39d..36c1bed 100644 --- a/block/qcow2-cluster.c +++ b/block/qcow2-cluster.c @@ -1367,13 +1367,31 @@ static int discard_single_l2(BlockDriverState *bs, uint64_t offset, uint64_t old_offset; old_offset = be64_to_cpu(l2_table[l2_index + i]); - if ((old_offset & L2E_OFFSET_MASK) == 0) { + + /* + * Make sure that a discarded area reads back as zeroes for v3 images + * (we cannot do it for v2 without actually writing a zero-filled + * buffer). We can skip the operation if the cluster is already marked + * as zero, or if it's unallocated and we don't have a backing file. + * + * TODO We might want to use bdrv_get_block_status(bs) here, but we're + * holding s->lock, so that doesn't work today. + */ + if (old_offset & QCOW_OFLAG_ZERO) { + continue; + } + + if ((old_offset & L2E_OFFSET_MASK) == 0 && !bs->backing_hd) { continue; } /* First remove L2 entries */ qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table); - l2_table[l2_index + i] = cpu_to_be64(0); + if (s->qcow_version >= 3) { + l2_table[l2_index + i] = cpu_to_be64(QCOW_OFLAG_ZERO); + } else { + l2_table[l2_index + i] = cpu_to_be64(0); + } /* Then decrease the refcount */ qcow2_free_any_clusters(bs, old_offset, 1, type); diff --git a/block/qcow2.c b/block/qcow2.c index b1dbdb1..cfe80be 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -449,7 +449,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, if (header.magic != QCOW_MAGIC) { error_setg(errp, "Image is not in qcow2 format"); - ret = -EMEDIUMTYPE; + ret = -EINVAL; goto fail; } if (header.version < 2 || header.version > 3) { @@ -1493,7 +1493,9 @@ static int qcow2_create2(const char *filename, int64_t total_size, return ret; } - ret = bdrv_file_open(&bs, filename, NULL, NULL, BDRV_O_RDWR, &local_err); + bs = NULL; + ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL, + NULL, &local_err); if (ret < 0) { error_propagate(errp, local_err); return ret; @@ -1543,7 +1545,8 @@ static int qcow2_create2(const char *filename, int64_t total_size, goto out; } - bdrv_close(bs); + bdrv_unref(bs); + bs = NULL; /* * And now open the image and make it consistent first (i.e. increase the @@ -1552,7 +1555,7 @@ static int qcow2_create2(const char *filename, int64_t total_size, */ BlockDriver* drv = bdrv_find_format("qcow2"); assert(drv != NULL); - ret = bdrv_open(bs, filename, NULL, + ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH, drv, &local_err); if (ret < 0) { error_propagate(errp, local_err); @@ -1599,10 +1602,11 @@ static int qcow2_create2(const char *filename, int64_t total_size, } } - bdrv_close(bs); + bdrv_unref(bs); + bs = NULL; /* Reopen the image without BDRV_O_NO_FLUSH to flush it before returning */ - ret = bdrv_open(bs, filename, NULL, + ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_BACKING, drv, &local_err); if (local_err) { @@ -1612,7 +1616,9 @@ static int qcow2_create2(const char *filename, int64_t total_size, ret = 0; out: - bdrv_unref(bs); + if (bs) { + bdrv_unref(bs); + } return ret; } diff --git a/block/qed.c b/block/qed.c index b9ca7ac..8802ad3 100644 --- a/block/qed.c +++ b/block/qed.c @@ -391,14 +391,15 @@ static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags, qed_header_le_to_cpu(&le_header, &s->header); if (s->header.magic != QED_MAGIC) { - return -EMEDIUMTYPE; + error_setg(errp, "Image not in QED format"); + return -EINVAL; } if (s->header.features & ~QED_FEATURE_MASK) { /* image uses unsupported feature bits */ char buf[64]; snprintf(buf, sizeof(buf), "%" PRIx64, s->header.features & ~QED_FEATURE_MASK); - qerror_report(QERR_UNKNOWN_BLOCK_FORMAT_FEATURE, + error_set(errp, QERR_UNKNOWN_BLOCK_FORMAT_FEATURE, bs->device_name, "QED", buf); return -ENOTSUP; } @@ -545,7 +546,8 @@ static void bdrv_qed_close(BlockDriverState *bs) static int qed_create(const char *filename, uint32_t cluster_size, uint64_t image_size, uint32_t table_size, - const char *backing_file, const char *backing_fmt) + const char *backing_file, const char *backing_fmt, + Error **errp) { QEDHeader header = { .magic = QED_MAGIC, @@ -562,20 +564,20 @@ static int qed_create(const char *filename, uint32_t cluster_size, size_t l1_size = header.cluster_size * header.table_size; Error *local_err = NULL; int ret = 0; - BlockDriverState *bs = NULL; + BlockDriverState *bs; ret = bdrv_create_file(filename, NULL, &local_err); if (ret < 0) { - qerror_report_err(local_err); - error_free(local_err); + error_propagate(errp, local_err); return ret; } - ret = bdrv_file_open(&bs, filename, NULL, NULL, - BDRV_O_RDWR | BDRV_O_CACHE_WB, &local_err); + bs = NULL; + ret = bdrv_open(&bs, filename, NULL, NULL, + BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_PROTOCOL, NULL, + &local_err); if (ret < 0) { - qerror_report_err(local_err); - error_free(local_err); + error_propagate(errp, local_err); return ret; } @@ -665,7 +667,7 @@ static int bdrv_qed_create(const char *filename, QEMUOptionParameter *options, } return qed_create(filename, cluster_size, image_size, table_size, - backing_file, backing_fmt); + backing_file, backing_fmt, errp); } typedef struct { diff --git a/block/quorum.c b/block/quorum.c new file mode 100644 index 0000000..6c28239 --- /dev/null +++ b/block/quorum.c @@ -0,0 +1,870 @@ +/* + * Quorum Block filter + * + * Copyright (C) 2012-2014 Nodalink, EURL. + * + * Author: + * Benoît Canet <benoit.canet@irqsave.net> + * + * Based on the design and code of blkverify.c (Copyright (C) 2010 IBM, Corp) + * and blkmirror.c (Copyright (C) 2011 Red Hat, Inc). + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include <gnutls/gnutls.h> +#include <gnutls/crypto.h> +#include "block/block_int.h" +#include "qapi/qmp/qjson.h" + +#define HASH_LENGTH 32 + +#define QUORUM_OPT_VOTE_THRESHOLD "vote-threshold" +#define QUORUM_OPT_BLKVERIFY "blkverify" + +/* This union holds a vote hash value */ +typedef union QuorumVoteValue { + char h[HASH_LENGTH]; /* SHA-256 hash */ + int64_t l; /* simpler 64 bits hash */ +} QuorumVoteValue; + +/* A vote item */ +typedef struct QuorumVoteItem { + int index; + QLIST_ENTRY(QuorumVoteItem) next; +} QuorumVoteItem; + +/* this structure is a vote version. A version is the set of votes sharing the + * same vote value. + * The set of votes will be tracked with the items field and its cardinality is + * vote_count. + */ +typedef struct QuorumVoteVersion { + QuorumVoteValue value; + int index; + int vote_count; + QLIST_HEAD(, QuorumVoteItem) items; + QLIST_ENTRY(QuorumVoteVersion) next; +} QuorumVoteVersion; + +/* this structure holds a group of vote versions together */ +typedef struct QuorumVotes { + QLIST_HEAD(, QuorumVoteVersion) vote_list; + bool (*compare)(QuorumVoteValue *a, QuorumVoteValue *b); +} QuorumVotes; + +/* the following structure holds the state of one quorum instance */ +typedef struct BDRVQuorumState { + BlockDriverState **bs; /* children BlockDriverStates */ + int num_children; /* children count */ + int threshold; /* if less than threshold children reads gave the + * same result a quorum error occurs. + */ + bool is_blkverify; /* true if the driver is in blkverify mode + * Writes are mirrored on two children devices. + * On reads the two children devices' contents are + * compared and if a difference is spotted its + * location is printed and the code aborts. + * It is useful to debug other block drivers by + * comparing them with a reference one. + */ +} BDRVQuorumState; + +typedef struct QuorumAIOCB QuorumAIOCB; + +/* Quorum will create one instance of the following structure per operation it + * performs on its children. + * So for each read/write operation coming from the upper layer there will be + * $children_count QuorumChildRequest. + */ +typedef struct QuorumChildRequest { + BlockDriverAIOCB *aiocb; + QEMUIOVector qiov; + uint8_t *buf; + int ret; + QuorumAIOCB *parent; +} QuorumChildRequest; + +/* Quorum will use the following structure to track progress of each read/write + * operation received by the upper layer. + * This structure hold pointers to the QuorumChildRequest structures instances + * used to do operations on each children and track overall progress. + */ +struct QuorumAIOCB { + BlockDriverAIOCB common; + + /* Request metadata */ + uint64_t sector_num; + int nb_sectors; + + QEMUIOVector *qiov; /* calling IOV */ + + QuorumChildRequest *qcrs; /* individual child requests */ + int count; /* number of completed AIOCB */ + int success_count; /* number of successfully completed AIOCB */ + + QuorumVotes votes; + + bool is_read; + int vote_ret; +}; + +static void quorum_vote(QuorumAIOCB *acb); + +static void quorum_aio_cancel(BlockDriverAIOCB *blockacb) +{ + QuorumAIOCB *acb = container_of(blockacb, QuorumAIOCB, common); + BDRVQuorumState *s = acb->common.bs->opaque; + int i; + + /* cancel all callbacks */ + for (i = 0; i < s->num_children; i++) { + bdrv_aio_cancel(acb->qcrs[i].aiocb); + } + + g_free(acb->qcrs); + qemu_aio_release(acb); +} + +static AIOCBInfo quorum_aiocb_info = { + .aiocb_size = sizeof(QuorumAIOCB), + .cancel = quorum_aio_cancel, +}; + +static void quorum_aio_finalize(QuorumAIOCB *acb) +{ + BDRVQuorumState *s = acb->common.bs->opaque; + int i, ret = 0; + + if (acb->vote_ret) { + ret = acb->vote_ret; + } + + acb->common.cb(acb->common.opaque, ret); + + if (acb->is_read) { + for (i = 0; i < s->num_children; i++) { + qemu_vfree(acb->qcrs[i].buf); + qemu_iovec_destroy(&acb->qcrs[i].qiov); + } + } + + g_free(acb->qcrs); + qemu_aio_release(acb); +} + +static bool quorum_sha256_compare(QuorumVoteValue *a, QuorumVoteValue *b) +{ + return !memcmp(a->h, b->h, HASH_LENGTH); +} + +static bool quorum_64bits_compare(QuorumVoteValue *a, QuorumVoteValue *b) +{ + return a->l == b->l; +} + +static QuorumAIOCB *quorum_aio_get(BDRVQuorumState *s, + BlockDriverState *bs, + QEMUIOVector *qiov, + uint64_t sector_num, + int nb_sectors, + BlockDriverCompletionFunc *cb, + void *opaque) +{ + QuorumAIOCB *acb = qemu_aio_get(&quorum_aiocb_info, bs, cb, opaque); + int i; + + acb->common.bs->opaque = s; + acb->sector_num = sector_num; + acb->nb_sectors = nb_sectors; + acb->qiov = qiov; + acb->qcrs = g_new0(QuorumChildRequest, s->num_children); + acb->count = 0; + acb->success_count = 0; + acb->votes.compare = quorum_sha256_compare; + QLIST_INIT(&acb->votes.vote_list); + acb->is_read = false; + acb->vote_ret = 0; + + for (i = 0; i < s->num_children; i++) { + acb->qcrs[i].buf = NULL; + acb->qcrs[i].ret = 0; + acb->qcrs[i].parent = acb; + } + + return acb; +} + +static void quorum_report_bad(QuorumAIOCB *acb, char *node_name, int ret) +{ + QObject *data; + assert(node_name); + data = qobject_from_jsonf("{ 'ret': %d" + ", 'node-name': %s" + ", 'sector-num': %" PRId64 + ", 'sectors-count': %d }", + ret, node_name, acb->sector_num, acb->nb_sectors); + monitor_protocol_event(QEVENT_QUORUM_REPORT_BAD, data); + qobject_decref(data); +} + +static void quorum_report_failure(QuorumAIOCB *acb) +{ + QObject *data; + const char *reference = acb->common.bs->device_name[0] ? + acb->common.bs->device_name : + acb->common.bs->node_name; + data = qobject_from_jsonf("{ 'reference': %s" + ", 'sector-num': %" PRId64 + ", 'sectors-count': %d }", + reference, acb->sector_num, acb->nb_sectors); + monitor_protocol_event(QEVENT_QUORUM_FAILURE, data); + qobject_decref(data); +} + +static int quorum_vote_error(QuorumAIOCB *acb); + +static bool quorum_has_too_much_io_failed(QuorumAIOCB *acb) +{ + BDRVQuorumState *s = acb->common.bs->opaque; + + if (acb->success_count < s->threshold) { + acb->vote_ret = quorum_vote_error(acb); + quorum_report_failure(acb); + return true; + } + + return false; +} + +static void quorum_aio_cb(void *opaque, int ret) +{ + QuorumChildRequest *sacb = opaque; + QuorumAIOCB *acb = sacb->parent; + BDRVQuorumState *s = acb->common.bs->opaque; + + sacb->ret = ret; + acb->count++; + if (ret == 0) { + acb->success_count++; + } else { + quorum_report_bad(acb, sacb->aiocb->bs->node_name, ret); + } + assert(acb->count <= s->num_children); + assert(acb->success_count <= s->num_children); + if (acb->count < s->num_children) { + return; + } + + /* Do the vote on read */ + if (acb->is_read) { + quorum_vote(acb); + } else { + quorum_has_too_much_io_failed(acb); + } + + quorum_aio_finalize(acb); +} + +static void quorum_report_bad_versions(BDRVQuorumState *s, + QuorumAIOCB *acb, + QuorumVoteValue *value) +{ + QuorumVoteVersion *version; + QuorumVoteItem *item; + + QLIST_FOREACH(version, &acb->votes.vote_list, next) { + if (acb->votes.compare(&version->value, value)) { + continue; + } + QLIST_FOREACH(item, &version->items, next) { + quorum_report_bad(acb, s->bs[item->index]->node_name, 0); + } + } +} + +static void quorum_copy_qiov(QEMUIOVector *dest, QEMUIOVector *source) +{ + int i; + assert(dest->niov == source->niov); + assert(dest->size == source->size); + for (i = 0; i < source->niov; i++) { + assert(dest->iov[i].iov_len == source->iov[i].iov_len); + memcpy(dest->iov[i].iov_base, + source->iov[i].iov_base, + source->iov[i].iov_len); + } +} + +static void quorum_count_vote(QuorumVotes *votes, + QuorumVoteValue *value, + int index) +{ + QuorumVoteVersion *v = NULL, *version = NULL; + QuorumVoteItem *item; + + /* look if we have something with this hash */ + QLIST_FOREACH(v, &votes->vote_list, next) { + if (votes->compare(&v->value, value)) { + version = v; + break; + } + } + + /* It's a version not yet in the list add it */ + if (!version) { + version = g_new0(QuorumVoteVersion, 1); + QLIST_INIT(&version->items); + memcpy(&version->value, value, sizeof(version->value)); + version->index = index; + version->vote_count = 0; + QLIST_INSERT_HEAD(&votes->vote_list, version, next); + } + + version->vote_count++; + + item = g_new0(QuorumVoteItem, 1); + item->index = index; + QLIST_INSERT_HEAD(&version->items, item, next); +} + +static void quorum_free_vote_list(QuorumVotes *votes) +{ + QuorumVoteVersion *version, *next_version; + QuorumVoteItem *item, *next_item; + + QLIST_FOREACH_SAFE(version, &votes->vote_list, next, next_version) { + QLIST_REMOVE(version, next); + QLIST_FOREACH_SAFE(item, &version->items, next, next_item) { + QLIST_REMOVE(item, next); + g_free(item); + } + g_free(version); + } +} + +static int quorum_compute_hash(QuorumAIOCB *acb, int i, QuorumVoteValue *hash) +{ + int j, ret; + gnutls_hash_hd_t dig; + QEMUIOVector *qiov = &acb->qcrs[i].qiov; + + ret = gnutls_hash_init(&dig, GNUTLS_DIG_SHA256); + + if (ret < 0) { + return ret; + } + + for (j = 0; j < qiov->niov; j++) { + ret = gnutls_hash(dig, qiov->iov[j].iov_base, qiov->iov[j].iov_len); + if (ret < 0) { + break; + } + } + + gnutls_hash_deinit(dig, (void *) hash); + return ret; +} + +static QuorumVoteVersion *quorum_get_vote_winner(QuorumVotes *votes) +{ + int max = 0; + QuorumVoteVersion *candidate, *winner = NULL; + + QLIST_FOREACH(candidate, &votes->vote_list, next) { + if (candidate->vote_count > max) { + max = candidate->vote_count; + winner = candidate; + } + } + + return winner; +} + +/* qemu_iovec_compare is handy for blkverify mode because it returns the first + * differing byte location. Yet it is handcoded to compare vectors one byte + * after another so it does not benefit from the libc SIMD optimizations. + * quorum_iovec_compare is written for speed and should be used in the non + * blkverify mode of quorum. + */ +static bool quorum_iovec_compare(QEMUIOVector *a, QEMUIOVector *b) +{ + int i; + int result; + + assert(a->niov == b->niov); + for (i = 0; i < a->niov; i++) { + assert(a->iov[i].iov_len == b->iov[i].iov_len); + result = memcmp(a->iov[i].iov_base, + b->iov[i].iov_base, + a->iov[i].iov_len); + if (result) { + return false; + } + } + + return true; +} + +static void GCC_FMT_ATTR(2, 3) quorum_err(QuorumAIOCB *acb, + const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + fprintf(stderr, "quorum: sector_num=%" PRId64 " nb_sectors=%d ", + acb->sector_num, acb->nb_sectors); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + va_end(ap); + exit(1); +} + +static bool quorum_compare(QuorumAIOCB *acb, + QEMUIOVector *a, + QEMUIOVector *b) +{ + BDRVQuorumState *s = acb->common.bs->opaque; + ssize_t offset; + + /* This driver will replace blkverify in this particular case */ + if (s->is_blkverify) { + offset = qemu_iovec_compare(a, b); + if (offset != -1) { + quorum_err(acb, "contents mismatch in sector %" PRId64, + acb->sector_num + + (uint64_t)(offset / BDRV_SECTOR_SIZE)); + } + return true; + } + + return quorum_iovec_compare(a, b); +} + +/* Do a vote to get the error code */ +static int quorum_vote_error(QuorumAIOCB *acb) +{ + BDRVQuorumState *s = acb->common.bs->opaque; + QuorumVoteVersion *winner = NULL; + QuorumVotes error_votes; + QuorumVoteValue result_value; + int i, ret = 0; + bool error = false; + + QLIST_INIT(&error_votes.vote_list); + error_votes.compare = quorum_64bits_compare; + + for (i = 0; i < s->num_children; i++) { + ret = acb->qcrs[i].ret; + if (ret) { + error = true; + result_value.l = ret; + quorum_count_vote(&error_votes, &result_value, i); + } + } + + if (error) { + winner = quorum_get_vote_winner(&error_votes); + ret = winner->value.l; + } + + quorum_free_vote_list(&error_votes); + + return ret; +} + +static void quorum_vote(QuorumAIOCB *acb) +{ + bool quorum = true; + int i, j, ret; + QuorumVoteValue hash; + BDRVQuorumState *s = acb->common.bs->opaque; + QuorumVoteVersion *winner; + + if (quorum_has_too_much_io_failed(acb)) { + return; + } + + /* get the index of the first successful read */ + for (i = 0; i < s->num_children; i++) { + if (!acb->qcrs[i].ret) { + break; + } + } + + assert(i < s->num_children); + + /* compare this read with all other successful reads stopping at quorum + * failure + */ + for (j = i + 1; j < s->num_children; j++) { + if (acb->qcrs[j].ret) { + continue; + } + quorum = quorum_compare(acb, &acb->qcrs[i].qiov, &acb->qcrs[j].qiov); + if (!quorum) { + break; + } + } + + /* Every successful read agrees */ + if (quorum) { + quorum_copy_qiov(acb->qiov, &acb->qcrs[i].qiov); + return; + } + + /* compute hashes for each successful read, also store indexes */ + for (i = 0; i < s->num_children; i++) { + if (acb->qcrs[i].ret) { + continue; + } + ret = quorum_compute_hash(acb, i, &hash); + /* if ever the hash computation failed */ + if (ret < 0) { + acb->vote_ret = ret; + goto free_exit; + } + quorum_count_vote(&acb->votes, &hash, i); + } + + /* vote to select the most represented version */ + winner = quorum_get_vote_winner(&acb->votes); + + /* if the winner count is smaller than threshold the read fails */ + if (winner->vote_count < s->threshold) { + quorum_report_failure(acb); + acb->vote_ret = -EIO; + goto free_exit; + } + + /* we have a winner: copy it */ + quorum_copy_qiov(acb->qiov, &acb->qcrs[winner->index].qiov); + + /* some versions are bad print them */ + quorum_report_bad_versions(s, acb, &winner->value); + +free_exit: + /* free lists */ + quorum_free_vote_list(&acb->votes); +} + +static BlockDriverAIOCB *quorum_aio_readv(BlockDriverState *bs, + int64_t sector_num, + QEMUIOVector *qiov, + int nb_sectors, + BlockDriverCompletionFunc *cb, + void *opaque) +{ + BDRVQuorumState *s = bs->opaque; + QuorumAIOCB *acb = quorum_aio_get(s, bs, qiov, sector_num, + nb_sectors, cb, opaque); + int i; + + acb->is_read = true; + + for (i = 0; i < s->num_children; i++) { + acb->qcrs[i].buf = qemu_blockalign(s->bs[i], qiov->size); + qemu_iovec_init(&acb->qcrs[i].qiov, qiov->niov); + qemu_iovec_clone(&acb->qcrs[i].qiov, qiov, acb->qcrs[i].buf); + } + + for (i = 0; i < s->num_children; i++) { + bdrv_aio_readv(s->bs[i], sector_num, &acb->qcrs[i].qiov, nb_sectors, + quorum_aio_cb, &acb->qcrs[i]); + } + + return &acb->common; +} + +static BlockDriverAIOCB *quorum_aio_writev(BlockDriverState *bs, + int64_t sector_num, + QEMUIOVector *qiov, + int nb_sectors, + BlockDriverCompletionFunc *cb, + void *opaque) +{ + BDRVQuorumState *s = bs->opaque; + QuorumAIOCB *acb = quorum_aio_get(s, bs, qiov, sector_num, nb_sectors, + cb, opaque); + int i; + + for (i = 0; i < s->num_children; i++) { + acb->qcrs[i].aiocb = bdrv_aio_writev(s->bs[i], sector_num, qiov, + nb_sectors, &quorum_aio_cb, + &acb->qcrs[i]); + } + + return &acb->common; +} + +static int64_t quorum_getlength(BlockDriverState *bs) +{ + BDRVQuorumState *s = bs->opaque; + int64_t result; + int i; + + /* check that all file have the same length */ + result = bdrv_getlength(s->bs[0]); + if (result < 0) { + return result; + } + for (i = 1; i < s->num_children; i++) { + int64_t value = bdrv_getlength(s->bs[i]); + if (value < 0) { + return value; + } + if (value != result) { + return -EIO; + } + } + + return result; +} + +static void quorum_invalidate_cache(BlockDriverState *bs) +{ + BDRVQuorumState *s = bs->opaque; + int i; + + for (i = 0; i < s->num_children; i++) { + bdrv_invalidate_cache(s->bs[i]); + } +} + +static coroutine_fn int quorum_co_flush(BlockDriverState *bs) +{ + BDRVQuorumState *s = bs->opaque; + QuorumVoteVersion *winner = NULL; + QuorumVotes error_votes; + QuorumVoteValue result_value; + int i; + int result = 0; + + QLIST_INIT(&error_votes.vote_list); + error_votes.compare = quorum_64bits_compare; + + for (i = 0; i < s->num_children; i++) { + result = bdrv_co_flush(s->bs[i]); + result_value.l = result; + quorum_count_vote(&error_votes, &result_value, i); + } + + winner = quorum_get_vote_winner(&error_votes); + result = winner->value.l; + + quorum_free_vote_list(&error_votes); + + return result; +} + +static bool quorum_recurse_is_first_non_filter(BlockDriverState *bs, + BlockDriverState *candidate) +{ + BDRVQuorumState *s = bs->opaque; + int i; + + for (i = 0; i < s->num_children; i++) { + bool perm = bdrv_recurse_is_first_non_filter(s->bs[i], + candidate); + if (perm) { + return true; + } + } + + return false; +} + +static int quorum_valid_threshold(int threshold, int num_children, Error **errp) +{ + + if (threshold < 1) { + error_set(errp, QERR_INVALID_PARAMETER_VALUE, + "vote-threshold", "value >= 1"); + return -ERANGE; + } + + if (threshold > num_children) { + error_setg(errp, "threshold may not exceed children count"); + return -ERANGE; + } + + return 0; +} + +static QemuOptsList quorum_runtime_opts = { + .name = "quorum", + .head = QTAILQ_HEAD_INITIALIZER(quorum_runtime_opts.head), + .desc = { + { + .name = QUORUM_OPT_VOTE_THRESHOLD, + .type = QEMU_OPT_NUMBER, + .help = "The number of vote needed for reaching quorum", + }, + { + .name = QUORUM_OPT_BLKVERIFY, + .type = QEMU_OPT_BOOL, + .help = "Trigger block verify mode if set", + }, + { /* end of list */ } + }, +}; + +static int quorum_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) +{ + BDRVQuorumState *s = bs->opaque; + Error *local_err = NULL; + QemuOpts *opts; + bool *opened; + QDict *sub = NULL; + QList *list = NULL; + const QListEntry *lentry; + int i; + int ret = 0; + + qdict_flatten(options); + qdict_extract_subqdict(options, &sub, "children."); + qdict_array_split(sub, &list); + + if (qdict_size(sub)) { + error_setg(&local_err, "Invalid option children.%s", + qdict_first(sub)->key); + ret = -EINVAL; + goto exit; + } + + /* count how many different children are present */ + s->num_children = qlist_size(list); + if (s->num_children < 2) { + error_setg(&local_err, + "Number of provided children must be greater than 1"); + ret = -EINVAL; + goto exit; + } + + opts = qemu_opts_create(&quorum_runtime_opts, NULL, 0, &error_abort); + qemu_opts_absorb_qdict(opts, options, &local_err); + if (error_is_set(&local_err)) { + ret = -EINVAL; + goto exit; + } + + s->threshold = qemu_opt_get_number(opts, QUORUM_OPT_VOTE_THRESHOLD, 0); + + /* and validate it against s->num_children */ + ret = quorum_valid_threshold(s->threshold, s->num_children, &local_err); + if (ret < 0) { + goto exit; + } + + /* is the driver in blkverify mode */ + if (qemu_opt_get_bool(opts, QUORUM_OPT_BLKVERIFY, false) && + s->num_children == 2 && s->threshold == 2) { + s->is_blkverify = true; + } else if (qemu_opt_get_bool(opts, QUORUM_OPT_BLKVERIFY, false)) { + fprintf(stderr, "blkverify mode is set by setting blkverify=on " + "and using two files with vote_threshold=2\n"); + } + + /* allocate the children BlockDriverState array */ + s->bs = g_new0(BlockDriverState *, s->num_children); + opened = g_new0(bool, s->num_children); + + for (i = 0, lentry = qlist_first(list); lentry; + lentry = qlist_next(lentry), i++) { + QDict *d; + QString *string; + + switch (qobject_type(lentry->value)) + { + /* List of options */ + case QTYPE_QDICT: + d = qobject_to_qdict(lentry->value); + QINCREF(d); + ret = bdrv_open(&s->bs[i], NULL, NULL, d, flags, NULL, + &local_err); + break; + + /* QMP reference */ + case QTYPE_QSTRING: + string = qobject_to_qstring(lentry->value); + ret = bdrv_open(&s->bs[i], NULL, qstring_get_str(string), NULL, + flags, NULL, &local_err); + break; + + default: + error_setg(&local_err, "Specification of child block device %i " + "is invalid", i); + ret = -EINVAL; + } + + if (ret < 0) { + goto close_exit; + } + opened[i] = true; + } + + g_free(opened); + goto exit; + +close_exit: + /* cleanup on error */ + for (i = 0; i < s->num_children; i++) { + if (!opened[i]) { + continue; + } + bdrv_unref(s->bs[i]); + } + g_free(s->bs); + g_free(opened); +exit: + /* propagate error */ + if (error_is_set(&local_err)) { + error_propagate(errp, local_err); + } + QDECREF(list); + QDECREF(sub); + return ret; +} + +static void quorum_close(BlockDriverState *bs) +{ + BDRVQuorumState *s = bs->opaque; + int i; + + for (i = 0; i < s->num_children; i++) { + bdrv_unref(s->bs[i]); + } + + g_free(s->bs); +} + +static BlockDriver bdrv_quorum = { + .format_name = "quorum", + .protocol_name = "quorum", + + .instance_size = sizeof(BDRVQuorumState), + + .bdrv_file_open = quorum_open, + .bdrv_close = quorum_close, + + .authorizations = { true, true }, + + .bdrv_co_flush_to_disk = quorum_co_flush, + + .bdrv_getlength = quorum_getlength, + + .bdrv_aio_readv = quorum_aio_readv, + .bdrv_aio_writev = quorum_aio_writev, + .bdrv_invalidate_cache = quorum_invalidate_cache, + + .bdrv_recurse_is_first_non_filter = quorum_recurse_is_first_non_filter, +}; + +static void bdrv_quorum_init(void) +{ + bdrv_register(&bdrv_quorum); +} + +block_init(bdrv_quorum_init); diff --git a/block/sheepdog.c b/block/sheepdog.c index e6c0376..f7bd024 100644 --- a/block/sheepdog.c +++ b/block/sheepdog.c @@ -1534,7 +1534,8 @@ static int sd_prealloc(const char *filename) Error *local_err = NULL; int ret; - ret = bdrv_file_open(&bs, filename, NULL, NULL, BDRV_O_RDWR, &local_err); + ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL, + NULL, &local_err); if (ret < 0) { qerror_report_err(local_err); error_free(local_err); @@ -1695,7 +1696,9 @@ static int sd_create(const char *filename, QEMUOptionParameter *options, goto out; } - ret = bdrv_file_open(&bs, backing_file, NULL, NULL, 0, &local_err); + bs = NULL; + ret = bdrv_open(&bs, backing_file, NULL, NULL, BDRV_O_PROTOCOL, NULL, + &local_err); if (ret < 0) { qerror_report_err(local_err); error_free(local_err); diff --git a/block/vdi.c b/block/vdi.c index 2d7490f..ae49cd8 100644 --- a/block/vdi.c +++ b/block/vdi.c @@ -395,43 +395,50 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags, } if (header.signature != VDI_SIGNATURE) { - logout("bad vdi signature %08x\n", header.signature); - ret = -EMEDIUMTYPE; + error_setg(errp, "Image not in VDI format (bad signature %08x)", header.signature); + ret = -EINVAL; goto fail; } else if (header.version != VDI_VERSION_1_1) { - logout("unsupported version %u.%u\n", - header.version >> 16, header.version & 0xffff); + error_setg(errp, "unsupported VDI image (version %u.%u)", + header.version >> 16, header.version & 0xffff); ret = -ENOTSUP; goto fail; } else if (header.offset_bmap % SECTOR_SIZE != 0) { /* We only support block maps which start on a sector boundary. */ - logout("unsupported block map offset 0x%x B\n", header.offset_bmap); + error_setg(errp, "unsupported VDI image (unaligned block map offset " + "0x%x)", header.offset_bmap); ret = -ENOTSUP; goto fail; } else if (header.offset_data % SECTOR_SIZE != 0) { /* We only support data blocks which start on a sector boundary. */ - logout("unsupported data offset 0x%x B\n", header.offset_data); + error_setg(errp, "unsupported VDI image (unaligned data offset 0x%x)", + header.offset_data); ret = -ENOTSUP; goto fail; } else if (header.sector_size != SECTOR_SIZE) { - logout("unsupported sector size %u B\n", header.sector_size); + error_setg(errp, "unsupported VDI image (sector size %u is not %u)", + header.sector_size, SECTOR_SIZE); ret = -ENOTSUP; goto fail; } else if (header.block_size != 1 * MiB) { - logout("unsupported block size %u B\n", header.block_size); + error_setg(errp, "unsupported VDI image (sector size %u is not %u)", + header.block_size, 1 * MiB); ret = -ENOTSUP; goto fail; } else if (header.disk_size > (uint64_t)header.blocks_in_image * header.block_size) { - logout("unsupported disk size %" PRIu64 " B\n", header.disk_size); + error_setg(errp, "unsupported VDI image (disk size %" PRIu64 ", " + "image bitmap has room for %" PRIu64 ")", + header.disk_size, + (uint64_t)header.blocks_in_image * header.block_size); ret = -ENOTSUP; goto fail; } else if (!uuid_is_null(header.uuid_link)) { - logout("link uuid != 0, unsupported\n"); + error_setg(errp, "unsupported VDI image (non-NULL link UUID)"); ret = -ENOTSUP; goto fail; } else if (!uuid_is_null(header.uuid_parent)) { - logout("parent uuid != 0, unsupported\n"); + error_setg(errp, "unsupported VDI image (non-NULL parent UUID)"); ret = -ENOTSUP; goto fail; } diff --git a/block/vhdx.c b/block/vhdx.c index 55689cf..5390ba6 100644 --- a/block/vhdx.c +++ b/block/vhdx.c @@ -402,9 +402,10 @@ int vhdx_update_headers(BlockDriverState *bs, BDRVVHDXState *s, } /* opens the specified header block from the VHDX file header section */ -static int vhdx_parse_header(BlockDriverState *bs, BDRVVHDXState *s) +static void vhdx_parse_header(BlockDriverState *bs, BDRVVHDXState *s, + Error **errp) { - int ret = 0; + int ret; VHDXHeader *header1; VHDXHeader *header2; bool h1_valid = false; @@ -462,7 +463,6 @@ static int vhdx_parse_header(BlockDriverState *bs, BDRVVHDXState *s) } else if (!h1_valid && h2_valid) { s->curr_header = 1; } else if (!h1_valid && !h2_valid) { - ret = -EINVAL; goto fail; } else { /* If both headers are valid, then we choose the active one by the @@ -473,27 +473,22 @@ static int vhdx_parse_header(BlockDriverState *bs, BDRVVHDXState *s) } else if (h2_seq > h1_seq) { s->curr_header = 1; } else { - ret = -EINVAL; goto fail; } } vhdx_region_register(s, s->headers[s->curr_header]->log_offset, s->headers[s->curr_header]->log_length); - - ret = 0; - goto exit; fail: - qerror_report(ERROR_CLASS_GENERIC_ERROR, "No valid VHDX header found"); + error_setg_errno(errp, -ret, "No valid VHDX header found"); qemu_vfree(header1); qemu_vfree(header2); s->headers[0] = NULL; s->headers[1] = NULL; exit: qemu_vfree(buffer); - return ret; } @@ -878,7 +873,7 @@ static int vhdx_open(BlockDriverState *bs, QDict *options, int flags, int ret = 0; uint32_t i; uint64_t signature; - + Error *local_err = NULL; s->bat = NULL; s->first_visible_write = true; @@ -901,8 +896,10 @@ static int vhdx_open(BlockDriverState *bs, QDict *options, int flags, * header update */ vhdx_guid_generate(&s->session_guid); - ret = vhdx_parse_header(bs, s); - if (ret < 0) { + vhdx_parse_header(bs, s, &local_err); + if (local_err != NULL) { + error_propagate(errp, local_err); + ret = -EINVAL; goto fail; } @@ -1797,7 +1794,9 @@ static int vhdx_create(const char *filename, QEMUOptionParameter *options, goto exit; } - ret = bdrv_file_open(&bs, filename, NULL, NULL, BDRV_O_RDWR, &local_err); + bs = NULL; + ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL, + NULL, &local_err); if (ret < 0) { error_propagate(errp, local_err); goto exit; diff --git a/block/vmdk.c b/block/vmdk.c index ff6f5ee..83839f9 100644 --- a/block/vmdk.c +++ b/block/vmdk.c @@ -526,8 +526,34 @@ static int vmdk_open_vmfs_sparse(BlockDriverState *bs, return ret; } -static int vmdk_open_desc_file(BlockDriverState *bs, int flags, - uint64_t desc_offset, Error **errp); +static int vmdk_open_desc_file(BlockDriverState *bs, int flags, char *buf, + Error **errp); + +static char *vmdk_read_desc(BlockDriverState *file, uint64_t desc_offset, + Error **errp) +{ + int64_t size; + char *buf; + int ret; + + size = bdrv_getlength(file); + if (size < 0) { + error_setg_errno(errp, -size, "Could not access file"); + return NULL; + } + + size = MIN(size, 1 << 20); /* avoid unbounded allocation */ + buf = g_malloc0(size + 1); + + ret = bdrv_pread(file, desc_offset, buf, size); + if (ret < 0) { + error_setg_errno(errp, -ret, "Could not read from file"); + g_free(buf); + return NULL; + } + + return buf; +} static int vmdk_open_vmdk4(BlockDriverState *bs, BlockDriverState *file, @@ -546,11 +572,18 @@ static int vmdk_open_vmdk4(BlockDriverState *bs, error_setg_errno(errp, -ret, "Could not read header from file '%s'", file->filename); + return -EINVAL; } if (header.capacity == 0) { uint64_t desc_offset = le64_to_cpu(header.desc_offset); if (desc_offset) { - return vmdk_open_desc_file(bs, flags, desc_offset << 9, errp); + char *buf = vmdk_read_desc(file, desc_offset << 9, errp); + if (!buf) { + return -EINVAL; + } + ret = vmdk_open_desc_file(bs, flags, buf, errp); + g_free(buf); + return ret; } } @@ -609,8 +642,8 @@ static int vmdk_open_vmdk4(BlockDriverState *bs, char buf[64]; snprintf(buf, sizeof(buf), "VMDK version %d", le32_to_cpu(header.version)); - qerror_report(QERR_UNKNOWN_BLOCK_FORMAT_FEATURE, - bs->device_name, "vmdk", buf); + error_set(errp, QERR_UNKNOWN_BLOCK_FORMAT_FEATURE, + bs->device_name, "vmdk", buf); return -ENOTSUP; } else if (le32_to_cpu(header.version) == 3 && (flags & BDRV_O_RDWR)) { /* VMware KB 2064959 explains that version 3 added support for @@ -622,7 +655,7 @@ static int vmdk_open_vmdk4(BlockDriverState *bs, } if (le32_to_cpu(header.num_gtes_per_gt) > 512) { - error_report("L2 table size too big"); + error_setg(errp, "L2 table size too big"); return -EINVAL; } @@ -638,8 +671,8 @@ static int vmdk_open_vmdk4(BlockDriverState *bs, } if (bdrv_getlength(file) < le64_to_cpu(header.grain_offset) * BDRV_SECTOR_SIZE) { - error_report("File truncated, expecting at least %lld bytes", - le64_to_cpu(header.grain_offset) * BDRV_SECTOR_SIZE); + error_setg(errp, "File truncated, expecting at least %lld bytes", + le64_to_cpu(header.grain_offset) * BDRV_SECTOR_SIZE); return -EINVAL; } @@ -701,16 +734,12 @@ static int vmdk_parse_description(const char *desc, const char *opt_name, /* Open an extent file and append to bs array */ static int vmdk_open_sparse(BlockDriverState *bs, - BlockDriverState *file, - int flags, Error **errp) + BlockDriverState *file, int flags, + char *buf, Error **errp) { uint32_t magic; - if (bdrv_pread(file, 0, &magic, sizeof(magic)) != sizeof(magic)) { - return -EIO; - } - - magic = be32_to_cpu(magic); + magic = ldl_be_p(buf); switch (magic) { case VMDK3_MAGIC: return vmdk_open_vmfs_sparse(bs, file, flags, errp); @@ -719,7 +748,8 @@ static int vmdk_open_sparse(BlockDriverState *bs, return vmdk_open_vmdk4(bs, file, flags, errp); break; default: - return -EMEDIUMTYPE; + error_setg(errp, "Image not in VMDK format"); + return -EINVAL; break; } } @@ -776,8 +806,9 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs, path_combine(extent_path, sizeof(extent_path), desc_file_path, fname); - ret = bdrv_file_open(&extent_file, extent_path, NULL, NULL, - bs->open_flags, errp); + extent_file = NULL; + ret = bdrv_open(&extent_file, extent_path, NULL, NULL, + bs->open_flags | BDRV_O_PROTOCOL, NULL, errp); if (ret) { return ret; } @@ -794,8 +825,14 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs, extent->flat_start_offset = flat_offset << 9; } else if (!strcmp(type, "SPARSE") || !strcmp(type, "VMFSSPARSE")) { /* SPARSE extent and VMFSSPARSE extent are both "COWD" sparse file*/ - ret = vmdk_open_sparse(bs, extent_file, bs->open_flags, errp); + char *buf = vmdk_read_desc(extent_file, 0, errp); + if (!buf) { + ret = -EINVAL; + } else { + ret = vmdk_open_sparse(bs, extent_file, bs->open_flags, buf, errp); + } if (ret) { + g_free(buf); bdrv_unref(extent_file); return ret; } @@ -818,29 +855,16 @@ next_line: return 0; } -static int vmdk_open_desc_file(BlockDriverState *bs, int flags, - uint64_t desc_offset, Error **errp) +static int vmdk_open_desc_file(BlockDriverState *bs, int flags, char *buf, + Error **errp) { int ret; - char *buf = NULL; char ct[128]; BDRVVmdkState *s = bs->opaque; - int64_t size; - - size = bdrv_getlength(bs->file); - if (size < 0) { - return -EINVAL; - } - - size = MIN(size, 1 << 20); /* avoid unbounded allocation */ - buf = g_malloc0(size + 1); - ret = bdrv_pread(bs->file, desc_offset, buf, size); - if (ret < 0) { - goto exit; - } if (vmdk_parse_description(buf, "createType", ct, sizeof(ct))) { - ret = -EMEDIUMTYPE; + error_setg(errp, "invalid VMDK image descriptor"); + ret = -EINVAL; goto exit; } if (strcmp(ct, "monolithicFlat") && @@ -856,24 +880,37 @@ static int vmdk_open_desc_file(BlockDriverState *bs, int flags, s->desc_offset = 0; ret = vmdk_parse_extents(buf, bs, bs->file->filename, errp); exit: - g_free(buf); return ret; } static int vmdk_open(BlockDriverState *bs, QDict *options, int flags, Error **errp) { + char *buf = NULL; int ret; BDRVVmdkState *s = bs->opaque; + uint32_t magic; - if (vmdk_open_sparse(bs, bs->file, flags, errp) == 0) { - s->desc_offset = 0x200; - } else { - ret = vmdk_open_desc_file(bs, flags, 0, errp); - if (ret) { - goto fail; - } + buf = vmdk_read_desc(bs->file, 0, errp); + if (!buf) { + return -EINVAL; } + + magic = ldl_be_p(buf); + switch (magic) { + case VMDK3_MAGIC: + case VMDK4_MAGIC: + ret = vmdk_open_sparse(bs, bs->file, flags, buf, errp); + s->desc_offset = 0x200; + break; + default: + ret = vmdk_open_desc_file(bs, flags, buf, errp); + break; + } + if (ret) { + goto fail; + } + /* try to open parent images, if exist */ ret = vmdk_parent_open(bs); if (ret) { @@ -888,10 +925,11 @@ static int vmdk_open(BlockDriverState *bs, QDict *options, int flags, QERR_BLOCK_FORMAT_FEATURE_NOT_SUPPORTED, "vmdk", bs->device_name, "live migration"); migrate_add_blocker(s->migration_blocker); - + g_free(buf); return 0; fail: + g_free(buf); g_free(s->create_type); s->create_type = NULL; vmdk_free_extents(bs); @@ -1493,7 +1531,9 @@ static int vmdk_create_extent(const char *filename, int64_t filesize, goto exit; } - ret = bdrv_file_open(&bs, filename, NULL, NULL, BDRV_O_RDWR, &local_err); + assert(bs == NULL); + ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL, + NULL, &local_err); if (ret < 0) { error_propagate(errp, local_err); goto exit; @@ -1755,10 +1795,10 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options, goto exit; } if (backing_file) { - BlockDriverState *bs = bdrv_new(""); - ret = bdrv_open(bs, backing_file, NULL, BDRV_O_NO_BACKING, NULL, errp); + BlockDriverState *bs = NULL; + ret = bdrv_open(&bs, backing_file, NULL, NULL, BDRV_O_NO_BACKING, NULL, + errp); if (ret != 0) { - bdrv_unref(bs); goto exit; } if (strcmp(bs->drv->format_name, "vmdk")) { @@ -1831,7 +1871,9 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options, goto exit; } } - ret = bdrv_file_open(&new_bs, filename, NULL, NULL, BDRV_O_RDWR, &local_err); + assert(new_bs == NULL); + ret = bdrv_open(&new_bs, filename, NULL, NULL, + BDRV_O_RDWR | BDRV_O_PROTOCOL, NULL, &local_err); if (ret < 0) { error_setg_errno(errp, -ret, "Could not write description"); goto exit; diff --git a/block/vpc.c b/block/vpc.c index 1d326cb..82bf248 100644 --- a/block/vpc.c +++ b/block/vpc.c @@ -190,7 +190,8 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags, goto fail; } if (strncmp(footer->creator, "conectix", 8)) { - ret = -EMEDIUMTYPE; + error_setg(errp, "invalid VPC image"); + ret = -EINVAL; goto fail; } disk_type = VHD_FIXED; diff --git a/block/vvfat.c b/block/vvfat.c index a19e4ca..f966ea5 100644 --- a/block/vvfat.c +++ b/block/vvfat.c @@ -1086,16 +1086,14 @@ DLOG(if (stderr == NULL) { opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort); qemu_opts_absorb_qdict(opts, options, &local_err); if (local_err) { - qerror_report_err(local_err); - error_free(local_err); + error_propagate(errp, local_err); ret = -EINVAL; goto fail; } dirname = qemu_opt_get(opts, "dir"); if (!dirname) { - qerror_report(ERROR_CLASS_GENERIC_ERROR, "vvfat block driver requires " - "a 'dir' option"); + error_setg(errp, "vvfat block driver requires a 'dir' option"); ret = -EINVAL; goto fail; } @@ -1135,8 +1133,7 @@ DLOG(if (stderr == NULL) { case 12: break; default: - qerror_report(ERROR_CLASS_GENERIC_ERROR, "Valid FAT types are only " - "12, 16 and 32"); + error_setg(errp, "Valid FAT types are only 12, 16 and 32"); ret = -EINVAL; goto fail; } @@ -2936,15 +2933,13 @@ static int enable_write_target(BDRVVVFATState *s) goto err; } - s->qcow = bdrv_new(""); - - ret = bdrv_open(s->qcow, s->qcow_filename, NULL, + s->qcow = NULL; + ret = bdrv_open(&s->qcow, s->qcow_filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH, bdrv_qcow, &local_err); if (ret < 0) { qerror_report_err(local_err); error_free(local_err); - bdrv_unref(s->qcow); goto err; } @@ -504,7 +504,7 @@ static DriveInfo *blockdev_init(const char *file, QDict *bs_opts, bdrv_flags |= ro ? 0 : BDRV_O_RDWR; QINCREF(bs_opts); - ret = bdrv_open(dinfo->bdrv, file, bs_opts, bdrv_flags, drv, &error); + ret = bdrv_open(&dinfo->bdrv, file, NULL, bs_opts, bdrv_flags, drv, &error); if (ret < 0) { error_setg(errp, "could not open disk image %s: %s", @@ -1330,12 +1330,12 @@ static void external_snapshot_prepare(BlkTransactionState *common, qstring_from_str(snapshot_node_name)); } - /* We will manually add the backing_hd field to the bs later */ - state->new_bs = bdrv_new(""); /* TODO Inherit bs->options or only take explicit options with an * extended QMP command? */ - ret = bdrv_open(state->new_bs, new_image_file, options, + assert(state->new_bs == NULL); + ret = bdrv_open(&state->new_bs, new_image_file, NULL, options, flags | BDRV_O_NO_BACKING, drv, &local_err); + /* We will manually add the backing_hd field to the bs later */ if (ret != 0) { error_propagate(errp, local_err); } @@ -1582,7 +1582,7 @@ static void qmp_bdrv_open_encrypted(BlockDriverState *bs, const char *filename, Error *local_err = NULL; int ret; - ret = bdrv_open(bs, filename, NULL, bdrv_flags, drv, &local_err); + ret = bdrv_open(&bs, filename, NULL, NULL, bdrv_flags, drv, &local_err); if (ret < 0) { error_propagate(errp, local_err); return; @@ -2018,10 +2018,9 @@ void qmp_drive_backup(const char *device, const char *target, return; } - target_bs = bdrv_new(""); - ret = bdrv_open(target_bs, target, NULL, flags, drv, &local_err); + target_bs = NULL; + ret = bdrv_open(&target_bs, target, NULL, NULL, flags, drv, &local_err); if (ret < 0) { - bdrv_unref(target_bs); error_propagate(errp, local_err); return; } @@ -2162,11 +2161,10 @@ void qmp_drive_mirror(const char *device, const char *target, /* Mirroring takes care of copy-on-write using the source's backing * file. */ - target_bs = bdrv_new(""); - ret = bdrv_open(target_bs, target, NULL, flags | BDRV_O_NO_BACKING, drv, - &local_err); + target_bs = NULL; + ret = bdrv_open(&target_bs, target, NULL, NULL, flags | BDRV_O_NO_BACKING, + drv, &local_err); if (ret < 0) { - bdrv_unref(target_bs); error_propagate(errp, local_err); return; } @@ -12,7 +12,10 @@ else fi TMPC="${TMPDIR1}/qemu-conf-${RANDOM}-$$-${RANDOM}.c" -TMPO="${TMPDIR1}/qemu-conf-${RANDOM}-$$-${RANDOM}.o" +TMPB="qemu-conf-${RANDOM}-$$-${RANDOM}" +TMPO="${TMPDIR1}/${TMPB}.o" +TMPL="${TMPDIR1}/${TMPB}.lo" +TMPA="${TMPDIR1}/lib${TMPB}.la" TMPE="${TMPDIR1}/qemu-conf-${RANDOM}-$$-${RANDOM}.exe" # NB: do not call "exit" in the trap handler; this is buggy with some shells; @@ -86,6 +89,38 @@ compile_prog() { do_cc $QEMU_CFLAGS $local_cflags -o $TMPE $TMPC $LDFLAGS $local_ldflags } +do_libtool() { + local mode=$1 + shift + # Run the compiler, capturing its output to the log. + echo $libtool $mode --tag=CC $cc "$@" >> config.log + $libtool $mode --tag=CC $cc "$@" >> config.log 2>&1 || return $? + # Test passed. If this is an --enable-werror build, rerun + # the test with -Werror and bail out if it fails. This + # makes warning-generating-errors in configure test code + # obvious to developers. + if test "$werror" != "yes"; then + return 0 + fi + # Don't bother rerunning the compile if we were already using -Werror + case "$*" in + *-Werror*) + return 0 + ;; + esac + echo $libtool $mode --tag=CC $cc -Werror "$@" >> config.log + $libtool $mode --tag=CC $cc -Werror "$@" >> config.log 2>&1 && return $? + error_exit "configure test passed without -Werror but failed with -Werror." \ + "This is probably a bug in the configure script. The failing command" \ + "will be at the bottom of config.log." \ + "You can run configure with --disable-werror to bypass this check." +} + +libtool_prog() { + do_libtool --mode=compile $QEMU_CFLAGS -c -fPIE -DPIE -o $TMPO $TMPC || return $? + do_libtool --mode=link $LDFLAGS -o $TMPA $TMPL -rpath /usr/local/lib +} + # symbolically link $1 to $2. Portable version of "ln -sf". symlink() { rm -rf "$2" @@ -205,6 +240,9 @@ mingw32="no" gcov="no" gcov_tool="gcov" EXESUF="" +DSOSUF=".so" +LDFLAGS_SHARED="-shared" +modules="no" prefix="/usr/local" mandir="\${prefix}/share/man" datadir="\${prefix}/share" @@ -264,6 +302,7 @@ gtkabi="2.0" tpm="no" libssh2="" vhdx="" +quorum="no" # parse CC options first for opt do @@ -515,11 +554,10 @@ OpenBSD) Darwin) bsd="yes" darwin="yes" + LDFLAGS_SHARED="-bundle -undefined dynamic_lookup" if [ "$cpu" = "x86_64" ] ; then QEMU_CFLAGS="-arch x86_64 $QEMU_CFLAGS" LDFLAGS="-arch x86_64 $LDFLAGS" - else - QEMU_CFLAGS="-mdynamic-no-pic $QEMU_CFLAGS" fi cocoa="yes" audio_drv_list="coreaudio" @@ -610,6 +648,7 @@ fi if test "$mingw32" = "yes" ; then EXESUF=".exe" + DSOSUF=".dll" QEMU_CFLAGS="-DWIN32_LEAN_AND_MEAN -DWINVER=0x501 $QEMU_CFLAGS" # enable C99/POSIX format strings (needs mingw32-runtime 3.15 or later) QEMU_CFLAGS="-D__USE_MINGW_ANSI_STDIO=1 $QEMU_CFLAGS" @@ -676,6 +715,9 @@ for opt do ;; --disable-debug-info) ;; + --enable-modules) + modules="yes" + ;; --cpu=*) ;; --target-list=*) target_list="$optarg" @@ -1005,6 +1047,10 @@ for opt do ;; --disable-vhdx) vhdx="no" ;; + --disable-quorum) quorum="no" + ;; + --enable-quorum) quorum="yes" + ;; *) echo "ERROR: unknown option $opt"; show_help="yes" ;; esac @@ -1131,7 +1177,8 @@ Advanced options (experts only): --libdir=PATH install libraries in PATH --sysconfdir=PATH install config in PATH$confsuffix --localstatedir=PATH install local state in PATH (set at runtime on win32) - --with-confsuffix=SUFFIX suffix for QEMU data inside datadir and sysconfdir [$confsuffix] + --with-confsuffix=SUFFIX suffix for QEMU data inside datadir/libdir/sysconfdir [$confsuffix] + --enable-modules enable modules support --enable-debug-tcg enable TCG debugging --disable-debug-tcg disable TCG debugging (default) --enable-debug-info enable debugging information (default) @@ -1261,6 +1308,8 @@ Advanced options (experts only): --enable-libssh2 enable ssh block device support --disable-vhdx disables support for the Microsoft VHDX image format --enable-vhdx enable support for the Microsoft VHDX image format + --disable-quorum disable quorum block filter support + --enable-quorum enable quorum block filter support NOTE: The object files are built at the place where configure is launched EOF @@ -1289,6 +1338,35 @@ else error_exit "\"$cc\" either does not exist or does not work" fi +# Check that the C++ compiler exists and works with the C compiler +if has $cxx; then + cat > $TMPC <<EOF +int c_function(void); +int main(void) { return c_function(); } +EOF + + compile_object + + cat > $TMPC <<EOF +extern "C" { + int c_function(void); +} +int c_function(void) { return 42; } +EOF + + if (cc=$cxx do_cc $QEMU_CFLAGS -o $TMPE $TMPC $TMPO $LDFLAGS); then + # C++ compiler $cxx works ok with C compiler $cc + : + else + echo "C++ compiler $cxx does not work with C compiler $cc" + echo "Disabling C++ specific optional code" + cxx= + fi +else + echo "No C++ compiler available; disabling C++ specific optional code" + cxx= +fi + # Consult white-list to determine whether to enable werror # by default. Only enable by default for git builds z_version=`cut -f3 -d. $source_path/VERSION` @@ -1346,6 +1424,9 @@ if compile_prog "-Werror -fno-gcse" "" ; then fi if test "$static" = "yes" ; then + if test "$modules" = "yes" ; then + error_exit "static and modules are mutually incompatible" + fi if test "$pie" = "yes" ; then error_exit "static and pie are mutually incompatible" else @@ -1399,6 +1480,32 @@ EOF fi fi +# check for broken gcc and libtool in RHEL5 +if test -n "$libtool" -a "$pie" != "no" ; then + cat > $TMPC <<EOF + +void *f(unsigned char *buf, int len); +void *g(unsigned char *buf, int len); + +void * +f(unsigned char *buf, int len) +{ + return (void*)0L; +} + +void * +g(unsigned char *buf, int len) +{ + return f(buf, len); +} + +EOF + if ! libtool_prog; then + echo "Disabling libtool due to broken toolchain support" + libtool= + fi +fi + ########################################## # __sync_fetch_and_and requires at least -march=i486. Many toolchains # use i686 as default anyway, but for those that don't, an explicit @@ -1937,6 +2044,30 @@ EOF fi ########################################## +# Quorum probe (check for gnutls) +if test "$quorum" != "no" ; then +cat > $TMPC <<EOF +#include <gnutls/gnutls.h> +#include <gnutls/crypto.h> +int main(void) {char data[4096], digest[32]; +gnutls_hash_fast(GNUTLS_DIG_SHA256, data, 4096, digest); +return 0; +} +EOF +quorum_tls_cflags=`$pkg_config --cflags gnutls 2> /dev/null` +quorum_tls_libs=`$pkg_config --libs gnutls 2> /dev/null` +if compile_prog "$quorum_tls_cflags" "$quorum_tls_libs" ; then + qcow_tls=yes + libs_softmmu="$quorum_tls_libs $libs_softmmu" + libs_tools="$quorum_tls_libs $libs_softmmu" + QEMU_CFLAGS="$QEMU_CFLAGS $quorum_tls_cflags" +else + echo "gnutls > 2.10.0 required to compile Quorum" + exit 1 +fi +fi + +########################################## # VNC SASL detection if test "$vnc" = "yes" -a "$vnc_sasl" != "no" ; then cat > $TMPC <<EOF @@ -2318,8 +2449,6 @@ EOF curl_libs=`$curlconfig --libs 2>/dev/null` if compile_prog "$curl_cflags" "$curl_libs" ; then curl=yes - libs_tools="$curl_libs $libs_tools" - libs_softmmu="$curl_libs $libs_softmmu" else if test "$curl" = "yes" ; then feature_not_found "curl" "Install libcurl devel" @@ -2357,13 +2486,36 @@ if test "$mingw32" = yes; then else glib_req_ver=2.12 fi -if $pkg_config --atleast-version=$glib_req_ver gthread-2.0; then - glib_cflags=`$pkg_config --cflags gthread-2.0` - glib_libs=`$pkg_config --libs gthread-2.0` - LIBS="$glib_libs $LIBS" - libs_qga="$glib_libs $libs_qga" -else - error_exit "glib-$glib_req_ver required to compile QEMU" +glib_modules=gthread-2.0 +if test "$modules" = yes; then + glib_modules="$glib_modules gmodule-2.0" +fi + +for i in $glib_modules; do + if $pkg_config --atleast-version=$glib_req_ver $i; then + glib_cflags=`$pkg_config --cflags $i` + glib_libs=`$pkg_config --libs $i` + CFLAGS="$glib_cflags $CFLAGS" + LIBS="$glib_libs $LIBS" + libs_qga="$glib_libs $libs_qga" + else + error_exit "glib-$glib_req_ver $i is required to compile QEMU" + fi +done + +########################################## +# SHA command probe for modules +if test "$modules" = yes; then + shacmd_probe="sha1sum sha1 shasum" + for c in $shacmd_probe; do + if which $c &>/dev/null; then + shacmd="$c" + break + fi + done + if test "$shacmd" = ""; then + error_exit "one of the checksum commands is required to enable modules: $shacmd_probe" + fi fi ########################################## @@ -2474,8 +2626,6 @@ EOF rbd_libs="-lrbd -lrados" if compile_prog "" "$rbd_libs" ; then rbd=yes - libs_tools="$rbd_libs $libs_tools" - libs_softmmu="$rbd_libs $libs_softmmu" else if test "$rbd" = "yes" ; then feature_not_found "rados block device" "Install librbd/ceph devel" @@ -2492,9 +2642,6 @@ if test "$libssh2" != "no" ; then libssh2_cflags=`$pkg_config libssh2 --cflags` libssh2_libs=`$pkg_config libssh2 --libs` libssh2=yes - libs_tools="$libssh2_libs $libs_tools" - libs_softmmu="$libssh2_libs $libs_softmmu" - QEMU_CFLAGS="$QEMU_CFLAGS $libssh2_cflags" else if test "$libssh2" = "yes" ; then error_exit "libssh2 >= $min_libssh2_version required for --enable-libssh2" @@ -2540,8 +2687,6 @@ int main(void) { io_setup(0, NULL); io_set_eventfd(NULL, 0); eventfd(0, 0); retu EOF if compile_prog "" "-laio" ; then linux_aio=yes - libs_softmmu="$libs_softmmu -laio" - libs_tools="$libs_tools -laio" else if test "$linux_aio" = "yes" ; then feature_not_found "linux AIO" "Install libaio devel" @@ -2710,9 +2855,6 @@ if test "$glusterfs" != "no" ; then glusterfs="yes" glusterfs_cflags=`$pkg_config --cflags glusterfs-api` glusterfs_libs=`$pkg_config --libs glusterfs-api` - CFLAGS="$CFLAGS $glusterfs_cflags" - libs_tools="$glusterfs_libs $libs_tools" - libs_softmmu="$glusterfs_libs $libs_softmmu" if $pkg_config --atleast-version=5 glusterfs-api; then glusterfs_discard="yes" fi @@ -3083,11 +3225,9 @@ EOF libiscsi="yes" libiscsi_cflags=$($pkg_config --cflags libiscsi) libiscsi_libs=$($pkg_config --libs libiscsi) - CFLAGS="$CFLAGS $libiscsi_cflags" - LIBS="$LIBS $libiscsi_libs" elif compile_prog "" "-liscsi" ; then libiscsi="yes" - LIBS="$LIBS -liscsi" + libiscsi_libs="-liscsi" else if test "$libiscsi" = "yes" ; then feature_not_found "libiscsi" "Install libiscsi devel" @@ -3688,6 +3828,7 @@ if test "$mingw32" = "yes" ; then fi qemu_confdir=$sysconfdir$confsuffix +qemu_moddir=$libdir$confsuffix qemu_datadir=$datadir$confsuffix qemu_localedir="$datadir/locale" @@ -3778,6 +3919,7 @@ echo "Install prefix $prefix" echo "BIOS directory `eval echo $qemu_datadir`" echo "binary directory `eval echo $bindir`" echo "library directory `eval echo $libdir`" +echo "module directory `eval echo $qemu_moddir`" echo "libexec directory `eval echo $libexecdir`" echo "include directory `eval echo $includedir`" echo "config directory `eval echo $sysconfdir`" @@ -3804,6 +3946,7 @@ echo "python $python" if test "$slirp" = "yes" ; then echo "smbd $smbd" fi +echo "module support $modules" echo "host CPU $cpu" echo "host big endian $bigendian" echo "target list $target_list" @@ -3893,6 +4036,7 @@ echo "libssh2 support $libssh2" echo "TPM passthrough $tpm_passthrough" echo "QOM debugging $qom_cast_debug" echo "vhdx $vhdx" +echo "Quorum $quorum" if test "$sdl_too_old" = "yes"; then echo "-> Your SDL version is too old - please upgrade to have SDL support" @@ -3916,6 +4060,7 @@ echo "sysconfdir=$sysconfdir" >> $config_host_mak echo "qemu_confdir=$qemu_confdir" >> $config_host_mak echo "qemu_datadir=$qemu_datadir" >> $config_host_mak echo "qemu_docdir=$qemu_docdir" >> $config_host_mak +echo "qemu_moddir=$qemu_moddir" >> $config_host_mak if test "$mingw32" = "no" ; then echo "qemu_localstatedir=$local_statedir" >> $config_host_mak fi @@ -4048,6 +4193,12 @@ echo "TARGET_DIRS=$target_list" >> $config_host_mak if [ "$docs" = "yes" ] ; then echo "BUILD_DOCS=yes" >> $config_host_mak fi +if test "$modules" = "yes"; then + # $shacmd can generate a hash started with digit, which the compiler doesn't + # like as an symbol. So prefix it with an underscore + echo "CONFIG_STAMP=_`(echo $qemu_version; echo $pkgversion; cat $0) | $shacmd - | cut -f1 -d\ `" >> $config_host_mak + echo "CONFIG_MODULES=y" >> $config_host_mak +fi if test "$sdl" = "yes" ; then echo "CONFIG_SDL=y" >> $config_host_mak echo "SDL_CFLAGS=$sdl_cflags" >> $config_host_mak @@ -4119,8 +4270,9 @@ if test "$bswap_h" = "yes" ; then echo "CONFIG_MACHINE_BSWAP_H=y" >> $config_host_mak fi if test "$curl" = "yes" ; then - echo "CONFIG_CURL=y" >> $config_host_mak + echo "CONFIG_CURL=m" >> $config_host_mak echo "CURL_CFLAGS=$curl_cflags" >> $config_host_mak + echo "CURL_LIBS=$curl_libs" >> $config_host_mak fi if test "$brlapi" = "yes" ; then echo "CONFIG_BRLAPI=y" >> $config_host_mak @@ -4209,10 +4361,12 @@ if test "$glx" = "yes" ; then fi if test "$libiscsi" = "yes" ; then - echo "CONFIG_LIBISCSI=y" >> $config_host_mak + echo "CONFIG_LIBISCSI=m" >> $config_host_mak if test "$libiscsi_version" = "1.4.0"; then echo "CONFIG_LIBISCSI_1_4=y" >> $config_host_mak fi + echo "LIBISCSI_CFLAGS=$libiscsi_cflags" >> $config_host_mak + echo "LIBISCSI_LIBS=$libiscsi_libs" >> $config_host_mak fi if test "$libnfs" = "yes" ; then @@ -4237,7 +4391,9 @@ if test "$qom_cast_debug" = "yes" ; then echo "CONFIG_QOM_CAST_DEBUG=y" >> $config_host_mak fi if test "$rbd" = "yes" ; then - echo "CONFIG_RBD=y" >> $config_host_mak + echo "CONFIG_RBD=m" >> $config_host_mak + echo "RBD_CFLAGS=$rbd_cflags" >> $config_host_mak + echo "RBD_LIBS=$rbd_libs" >> $config_host_mak fi echo "CONFIG_COROUTINE_BACKEND=$coroutine" >> $config_host_mak @@ -4280,7 +4436,9 @@ if test "$getauxval" = "yes" ; then fi if test "$glusterfs" = "yes" ; then - echo "CONFIG_GLUSTERFS=y" >> $config_host_mak + echo "CONFIG_GLUSTERFS=m" >> $config_host_mak + echo "GLUSTERFS_CFLAGS=$glusterfs_cflags" >> $config_host_mak + echo "GLUSTERFS_LIBS=$glusterfs_libs" >> $config_host_mak fi if test "$glusterfs_discard" = "yes" ; then @@ -4292,7 +4450,13 @@ if test "$glusterfs_zerofill" = "yes" ; then fi if test "$libssh2" = "yes" ; then - echo "CONFIG_LIBSSH2=y" >> $config_host_mak + echo "CONFIG_LIBSSH2=m" >> $config_host_mak + echo "LIBSSH2_CFLAGS=$libssh2_cflags" >> $config_host_mak + echo "LIBSSH2_LIBS=$libssh2_libs" >> $config_host_mak +fi + +if test "$quorum" = "yes" ; then + echo "CONFIG_QUORUM=y" >> $config_host_mak fi if test "$virtio_blk_data_plane" = "yes" ; then @@ -4423,6 +4587,8 @@ echo "LIBTOOLFLAGS=$LIBTOOLFLAGS" >> $config_host_mak echo "LIBS+=$LIBS" >> $config_host_mak echo "LIBS_TOOLS+=$libs_tools" >> $config_host_mak echo "EXESUF=$EXESUF" >> $config_host_mak +echo "DSOSUF=$DSOSUF" >> $config_host_mak +echo "LDFLAGS_SHARED=$LDFLAGS_SHARED" >> $config_host_mak echo "LIBS_QGA+=$libs_qga" >> $config_host_mak echo "POD2MAN=$POD2MAN" >> $config_host_mak echo "TRANSLATE_OPT_CFLAGS=$TRANSLATE_OPT_CFLAGS" >> $config_host_mak diff --git a/docs/qmp/qmp-events.txt b/docs/qmp/qmp-events.txt index a378c87..00f9515 100644 --- a/docs/qmp/qmp-events.txt +++ b/docs/qmp/qmp-events.txt @@ -500,3 +500,39 @@ Example: Note: If action is "reset", "shutdown", or "pause" the WATCHDOG event is followed respectively by the RESET, SHUTDOWN, or STOP events. + +QUORUM_FAILURE +-------------- + +Emitted by the Quorum block driver if it fails to establish a quorum. + +Data: + +- "reference": device name if defined else node name. +- "sector-num": Number of the first sector of the failed read operation. +- "sector-count": Failed read operation sector count. + +Example: + +{ "event": "QUORUM_FAILURE", + "data": { "reference": "usr1", "sector-num": 345435, "sector-count": 5 }, + "timestamp": { "seconds": 1344522075, "microseconds": 745528 } } + +QUORUM_REPORT_BAD +----------------- + +Emitted to report a corruption of a Quorum file. + +Data: + +- "ret": The IO return code. +- "node-name": The graph node name of the block driver state. +- "sector-num": Number of the first sector of the failed read operation. +- "sector-count": Failed read operation sector count. + +Example: + +{ "event": "QUORUM_REPORT_BAD", + "data": { "ret": 0, "node-name": "1.raw", "sector-num": 345435, + "sector-count": 5 }, + "timestamp": { "seconds": 1344522075, "microseconds": 745528 } } diff --git a/hw/block/xen_disk.c b/hw/block/xen_disk.c index 098f6c6..bc061e6 100644 --- a/hw/block/xen_disk.c +++ b/hw/block/xen_disk.c @@ -483,7 +483,18 @@ static void qemu_aio_complete(void *opaque, int ret) ioreq->status = ioreq->aio_errors ? BLKIF_RSP_ERROR : BLKIF_RSP_OKAY; ioreq_unmap(ioreq); ioreq_finish(ioreq); - bdrv_acct_done(ioreq->blkdev->bs, &ioreq->acct); + switch (ioreq->req.operation) { + case BLKIF_OP_WRITE: + case BLKIF_OP_FLUSH_DISKCACHE: + if (!ioreq->req.nr_segments) { + break; + } + case BLKIF_OP_READ: + bdrv_acct_done(ioreq->blkdev->bs, &ioreq->acct); + break; + default: + break; + } qemu_bh_schedule(ioreq->blkdev->bh); } @@ -813,8 +824,8 @@ static int blk_connect(struct XenDevice *xendev) Error *local_err = NULL; BlockDriver *drv = bdrv_find_whitelisted_format(blkdev->fileproto, readonly); - if (bdrv_open(blkdev->bs, - blkdev->filename, NULL, qflags, drv, &local_err) != 0) + if (bdrv_open(&blkdev->bs, blkdev->filename, NULL, NULL, qflags, + drv, &local_err) != 0) { xen_be_printf(&blkdev->xendev, 0, "error: %s\n", error_get_pretty(local_err)); diff --git a/hw/display/qxl.c b/hw/display/qxl.c index 1471cc0..2a559eb 100644 --- a/hw/display/qxl.c +++ b/hw/display/qxl.c @@ -1429,7 +1429,7 @@ static int qxl_destroy_primary(PCIQXLDevice *d, qxl_async_io async) return 1; } -static void qxl_set_mode(PCIQXLDevice *d, int modenr, int loadvm) +static void qxl_set_mode(PCIQXLDevice *d, unsigned int modenr, int loadvm) { pcibus_t start = d->pci.io_regions[QXL_RAM_RANGE_INDEX].addr; pcibus_t end = d->pci.io_regions[QXL_RAM_RANGE_INDEX].size + start; @@ -1439,6 +1439,12 @@ static void qxl_set_mode(PCIQXLDevice *d, int modenr, int loadvm) .mem_start = start, .mem_end = end }; + + if (modenr >= d->modes->n_modes) { + qxl_set_guest_bug(d, "mode number out of range"); + return; + } + QXLSurfaceCreate surface = { .width = mode->x_res, .height = mode->y_res, diff --git a/hw/ide/piix.c b/hw/ide/piix.c index 0eda301..40757eb 100644 --- a/hw/ide/piix.c +++ b/hw/ide/piix.c @@ -167,7 +167,7 @@ static int pci_piix_ide_initfn(PCIDevice *dev) return 0; } -static int pci_piix3_xen_ide_unplug(DeviceState *dev) +int pci_piix3_xen_ide_unplug(DeviceState *dev) { PCIIDEState *pci_ide; DriveInfo *di; @@ -266,7 +266,6 @@ static void piix3_ide_xen_class_init(ObjectClass *klass, void *data) k->device_id = PCI_DEVICE_ID_INTEL_82371SB_1; k->class_id = PCI_CLASS_STORAGE_IDE; set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); - dc->unplug = pci_piix3_xen_ide_unplug; } static const TypeInfo piix3_ide_xen_info = { diff --git a/hw/microblaze/petalogix_ml605_mmu.c b/hw/microblaze/petalogix_ml605_mmu.c index 37cbbfd..40a9f5c 100644 --- a/hw/microblaze/petalogix_ml605_mmu.c +++ b/hw/microblaze/petalogix_ml605_mmu.c @@ -32,7 +32,6 @@ #include "sysemu/sysemu.h" #include "hw/devices.h" #include "hw/boards.h" -#include "hw/xilinx.h" #include "sysemu/blockdev.h" #include "hw/char/serial.h" #include "exec/address-spaces.h" @@ -49,6 +48,7 @@ #define NUM_SPI_FLASHES 4 +#define SPI_BASEADDR 0x40a00000 #define MEMORY_BASEADDR 0x50000000 #define FLASH_BASEADDR 0x86000000 #define INTC_BASEADDR 0x81800000 @@ -57,6 +57,13 @@ #define AXIENET_BASEADDR 0x82780000 #define AXIDMA_BASEADDR 0x84600000 +#define AXIDMA_IRQ1 0 +#define AXIDMA_IRQ0 1 +#define TIMER_IRQ 2 +#define AXIENET_IRQ 3 +#define SPI_IRQ 4 +#define UART16550_IRQ 5 + static void machine_cpu_reset(MicroBlazeCPU *cpu) { CPUMBState *env = &cpu->env; @@ -111,17 +118,27 @@ petalogix_ml605_init(QEMUMachineInitArgs *args) 2, 0x89, 0x18, 0x0000, 0x0, 0); - dev = xilinx_intc_create(INTC_BASEADDR, qdev_get_gpio_in(DEVICE(cpu), - MB_CPU_IRQ), 4); + dev = qdev_create(NULL, "xlnx.xps-intc"); + qdev_prop_set_uint32(dev, "kind-of-intr", 1 << TIMER_IRQ); + qdev_init_nofail(dev); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, INTC_BASEADDR); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, + qdev_get_gpio_in(DEVICE(cpu), MB_CPU_IRQ)); for (i = 0; i < 32; i++) { irq[i] = qdev_get_gpio_in(dev, i); } serial_mm_init(address_space_mem, UART16550_BASEADDR + 0x1000, 2, - irq[5], 115200, serial_hds[0], DEVICE_LITTLE_ENDIAN); + irq[UART16550_IRQ], 115200, serial_hds[0], + DEVICE_LITTLE_ENDIAN); /* 2 timers at irq 2 @ 100 Mhz. */ - xilinx_timer_create(TIMER_BASEADDR, irq[2], 0, 100 * 1000000); + dev = qdev_create(NULL, "xlnx.xps-timer"); + qdev_prop_set_uint32(dev, "one-timer-only", 0); + qdev_prop_set_uint32(dev, "clock-frequency", 100 * 1000000); + qdev_init_nofail(dev); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, TIMER_BASEADDR); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq[TIMER_IRQ]); /* axi ethernet and dma initialization. */ qemu_check_nic_model(&nd_table[0], "xlnx.axi-ethernet"); @@ -138,16 +155,30 @@ petalogix_ml605_init(QEMUMachineInitArgs *args) "axistream-connected-target", NULL); cs = object_property_get_link(OBJECT(dma), "axistream-control-connected-target", NULL); - xilinx_axiethernet_init(eth0, &nd_table[0], STREAM_SLAVE(ds), - STREAM_SLAVE(cs), 0x82780000, irq[3], 0x1000, - 0x1000); + qdev_set_nic_properties(eth0, &nd_table[0]); + qdev_prop_set_uint32(eth0, "rxmem", 0x1000); + qdev_prop_set_uint32(eth0, "txmem", 0x1000); + object_property_set_link(OBJECT(eth0), OBJECT(ds), + "axistream-connected", &error_abort); + object_property_set_link(OBJECT(eth0), OBJECT(cs), + "axistream-control-connected", &error_abort); + qdev_init_nofail(eth0); + sysbus_mmio_map(SYS_BUS_DEVICE(eth0), 0, AXIENET_BASEADDR); + sysbus_connect_irq(SYS_BUS_DEVICE(eth0), 0, irq[AXIENET_IRQ]); ds = object_property_get_link(OBJECT(eth0), "axistream-connected-target", NULL); cs = object_property_get_link(OBJECT(eth0), "axistream-control-connected-target", NULL); - xilinx_axidma_init(dma, STREAM_SLAVE(ds), STREAM_SLAVE(cs), 0x84600000, - irq[1], irq[0], 100 * 1000000); + qdev_prop_set_uint32(dma, "freqhz", 100 * 1000000); + object_property_set_link(OBJECT(dma), OBJECT(ds), + "axistream-connected", &error_abort); + object_property_set_link(OBJECT(dma), OBJECT(cs), + "axistream-control-connected", &error_abort); + qdev_init_nofail(dma); + sysbus_mmio_map(SYS_BUS_DEVICE(dma), 0, AXIDMA_BASEADDR); + sysbus_connect_irq(SYS_BUS_DEVICE(dma), 0, irq[AXIDMA_IRQ0]); + sysbus_connect_irq(SYS_BUS_DEVICE(dma), 1, irq[AXIDMA_IRQ1]); { SSIBus *spi; @@ -156,8 +187,8 @@ petalogix_ml605_init(QEMUMachineInitArgs *args) qdev_prop_set_uint8(dev, "num-ss-bits", NUM_SPI_FLASHES); qdev_init_nofail(dev); busdev = SYS_BUS_DEVICE(dev); - sysbus_mmio_map(busdev, 0, 0x40a00000); - sysbus_connect_irq(busdev, 0, irq[4]); + sysbus_mmio_map(busdev, 0, SPI_BASEADDR); + sysbus_connect_irq(busdev, 0, irq[SPI_IRQ]); spi = (SSIBus *)qdev_get_child_bus(dev, "spi"); diff --git a/hw/microblaze/petalogix_s3adsp1800_mmu.c b/hw/microblaze/petalogix_s3adsp1800_mmu.c index f500215..6c45e20 100644 --- a/hw/microblaze/petalogix_s3adsp1800_mmu.c +++ b/hw/microblaze/petalogix_s3adsp1800_mmu.c @@ -30,7 +30,6 @@ #include "sysemu/sysemu.h" #include "hw/devices.h" #include "hw/boards.h" -#include "hw/xilinx.h" #include "sysemu/blockdev.h" #include "exec/address-spaces.h" @@ -48,6 +47,10 @@ #define UARTLITE_BASEADDR 0x84000000 #define ETHLITE_BASEADDR 0x81000000 +#define TIMER_IRQ 0 +#define ETHLITE_IRQ 1 +#define UARTLITE_IRQ 3 + static void machine_cpu_reset(MicroBlazeCPU *cpu) { CPUMBState *env = &cpu->env; @@ -93,16 +96,36 @@ petalogix_s3adsp1800_init(QEMUMachineInitArgs *args) FLASH_SIZE >> 16, 1, 0x89, 0x18, 0x0000, 0x0, 1); - dev = xilinx_intc_create(INTC_BASEADDR, qdev_get_gpio_in(DEVICE(cpu), - MB_CPU_IRQ), 0xA); + dev = qdev_create(NULL, "xlnx.xps-intc"); + qdev_prop_set_uint32(dev, "kind-of-intr", + 1 << ETHLITE_IRQ | 1 << UARTLITE_IRQ); + qdev_init_nofail(dev); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, INTC_BASEADDR); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, + qdev_get_gpio_in(DEVICE(cpu), MB_CPU_IRQ)); for (i = 0; i < 32; i++) { irq[i] = qdev_get_gpio_in(dev, i); } - sysbus_create_simple("xlnx.xps-uartlite", UARTLITE_BASEADDR, irq[3]); + sysbus_create_simple("xlnx.xps-uartlite", UARTLITE_BASEADDR, + irq[UARTLITE_IRQ]); + /* 2 timers at irq 2 @ 62 Mhz. */ - xilinx_timer_create(TIMER_BASEADDR, irq[0], 0, 62 * 1000000); - xilinx_ethlite_create(&nd_table[0], ETHLITE_BASEADDR, irq[1], 0, 0); + dev = qdev_create(NULL, "xlnx.xps-timer"); + qdev_prop_set_uint32(dev, "one-timer-only", 0); + qdev_prop_set_uint32(dev, "clock-frequency", 62 * 1000000); + qdev_init_nofail(dev); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, TIMER_BASEADDR); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq[TIMER_IRQ]); + + qemu_check_nic_model(&nd_table[0], "xlnx.xps-ethernetlite"); + dev = qdev_create(NULL, "xlnx.xps-ethernetlite"); + qdev_set_nic_properties(dev, &nd_table[0]); + qdev_prop_set_uint32(dev, "tx-ping-pong", 0); + qdev_prop_set_uint32(dev, "rx-ping-pong", 0); + qdev_init_nofail(dev); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, ETHLITE_BASEADDR); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq[ETHLITE_IRQ]); microblaze_load_kernel(cpu, ddr_base, ram_size, args->initrd_filename, diff --git a/hw/net/opencores_eth.c b/hw/net/opencores_eth.c index 4118d54..4a44304 100644 --- a/hw/net/opencores_eth.c +++ b/hw/net/opencores_eth.c @@ -169,6 +169,7 @@ enum { }; enum { + INT_SOURCE_BUSY = 0x10, INT_SOURCE_RXB = 0x4, INT_SOURCE_TXB = 0x1, }; @@ -351,8 +352,7 @@ static int open_eth_can_receive(NetClientState *nc) OpenEthState *s = qemu_get_nic_opaque(nc); return GET_REGBIT(s, MODER, RXEN) && - (s->regs[TX_BD_NUM] < 0x80) && - (rx_desc(s)->len_flags & RXD_E); + (s->regs[TX_BD_NUM] < 0x80); } static ssize_t open_eth_receive(NetClientState *nc, @@ -402,6 +402,12 @@ static ssize_t open_eth_receive(NetClientState *nc, desc *desc = rx_desc(s); size_t copy_size = GET_REGBIT(s, MODER, HUGEN) ? 65536 : maxfl; + if (!(desc->len_flags & RXD_E)) { + open_eth_int_source_write(s, + s->regs[INT_SOURCE] | INT_SOURCE_BUSY); + return size; + } + desc->len_flags &= ~(RXD_CF | RXD_M | RXD_OR | RXD_IS | RXD_DN | RXD_TL | RXD_SF | RXD_CRC | RXD_LC); @@ -551,6 +557,15 @@ static uint64_t open_eth_reg_read(void *opaque, return v; } +static void open_eth_notify_can_receive(OpenEthState *s) +{ + NetClientState *nc = qemu_get_queue(s->nic); + + if (open_eth_can_receive(nc)) { + qemu_flush_queued_packets(nc); + } +} + static void open_eth_ro(OpenEthState *s, uint32_t val) { } @@ -567,6 +582,7 @@ static void open_eth_moder_host_write(OpenEthState *s, uint32_t val) if (set & MODER_RXEN) { s->rx_desc = s->regs[TX_BD_NUM]; + open_eth_notify_can_receive(s); } if (set & MODER_TXEN) { s->tx_desc = 0; @@ -592,6 +608,18 @@ static void open_eth_int_mask_host_write(OpenEthState *s, uint32_t val) s->regs[INT_SOURCE] & s->regs[INT_MASK]); } +static void open_eth_tx_bd_num_host_write(OpenEthState *s, uint32_t val) +{ + if (val < 0x80) { + bool enable = s->regs[TX_BD_NUM] == 0x80; + + s->regs[TX_BD_NUM] = val; + if (enable) { + open_eth_notify_can_receive(s); + } + } +} + static void open_eth_mii_command_host_write(OpenEthState *s, uint32_t val) { unsigned fiad = GET_REGFIELD(s, MIIADDRESS, FIAD); @@ -630,6 +658,7 @@ static void open_eth_reg_write(void *opaque, [MODER] = open_eth_moder_host_write, [INT_SOURCE] = open_eth_int_source_host_write, [INT_MASK] = open_eth_int_mask_host_write, + [TX_BD_NUM] = open_eth_tx_bd_num_host_write, [MIICOMMAND] = open_eth_mii_command_host_write, [MIITX_DATA] = open_eth_mii_tx_host_write, [MIISTATUS] = open_eth_ro, diff --git a/hw/ppc/virtex_ml507.c b/hw/ppc/virtex_ml507.c index bdb057e..85a0e53 100644 --- a/hw/ppc/virtex_ml507.c +++ b/hw/ppc/virtex_ml507.c @@ -40,11 +40,19 @@ #include "ppc405.h" #include "sysemu/blockdev.h" -#include "hw/xilinx.h" +#include "qapi/qmp/qerror.h" #define EPAPR_MAGIC (0x45504150) #define FLASH_SIZE (16 * 1024 * 1024) +#define INTC_BASEADDR 0x81800000 +#define UART16550_BASEADDR 0x83e01003 +#define TIMER_BASEADDR 0x83c00000 +#define PFLASH_BASEADDR 0xfc000000 + +#define TIMER_IRQ 3 +#define UART16550_IRQ 9 + static struct boot_info { uint32_t bootstrap_pc; @@ -204,22 +212,31 @@ static void virtex_init(QEMUMachineInitArgs *args) memory_region_add_subregion(address_space_mem, ram_base, phys_ram); dinfo = drive_get(IF_PFLASH, 0, 0); - pflash_cfi01_register(0xfc000000, NULL, "virtex.flash", FLASH_SIZE, + pflash_cfi01_register(PFLASH_BASEADDR, NULL, "virtex.flash", FLASH_SIZE, dinfo ? dinfo->bdrv : NULL, (64 * 1024), FLASH_SIZE >> 16, 1, 0x89, 0x18, 0x0000, 0x0, 1); cpu_irq = (qemu_irq *) &env->irq_inputs[PPC40x_INPUT_INT]; - dev = xilinx_intc_create(0x81800000, cpu_irq[0], 0); + dev = qdev_create(NULL, "xlnx.xps-intc"); + qdev_prop_set_uint32(dev, "kind-of-intr", 0); + qdev_init_nofail(dev); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, INTC_BASEADDR); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, cpu_irq[0]); for (i = 0; i < 32; i++) { irq[i] = qdev_get_gpio_in(dev, i); } - serial_mm_init(address_space_mem, 0x83e01003ULL, 2, irq[9], 115200, - serial_hds[0], DEVICE_LITTLE_ENDIAN); + serial_mm_init(address_space_mem, UART16550_BASEADDR, 2, irq[UART16550_IRQ], + 115200, serial_hds[0], DEVICE_LITTLE_ENDIAN); /* 2 timers at irq 2 @ 62 Mhz. */ - xilinx_timer_create(0x83c00000, irq[3], 0, 62 * 1000000); + dev = qdev_create(NULL, "xlnx.xps-timer"); + qdev_prop_set_uint32(dev, "one-timer-only", 0); + qdev_prop_set_uint32(dev, "clock-frequency", 62 * 1000000); + qdev_init_nofail(dev); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, TIMER_BASEADDR); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq[TIMER_IRQ]); if (kernel_filename) { uint64_t entry, low, high; diff --git a/hw/xen/xen_platform.c b/hw/xen/xen_platform.c index 70875e4..1d9d0e9 100644 --- a/hw/xen/xen_platform.c +++ b/hw/xen/xen_platform.c @@ -27,6 +27,7 @@ #include "hw/hw.h" #include "hw/i386/pc.h" +#include "hw/ide.h" #include "hw/pci/pci.h" #include "hw/irq.h" #include "hw/xen/xen_common.h" @@ -110,7 +111,7 @@ static void unplug_disks(PCIBus *b, PCIDevice *d, void *o) if (pci_get_word(d->config + PCI_CLASS_DEVICE) == PCI_CLASS_STORAGE_IDE && strcmp(d->name, "xen-pci-passthrough") != 0) { - qdev_unplug(DEVICE(d), NULL); + pci_piix3_xen_ide_unplug(DEVICE(d)); } } diff --git a/hw/xtensa/xtensa_lx60.c b/hw/xtensa/xtensa_lx60.c index 22e124d..49c58d1 100644 --- a/hw/xtensa/xtensa_lx60.c +++ b/hw/xtensa/xtensa_lx60.c @@ -40,6 +40,7 @@ #include "xtensa_bootparam.h" typedef struct LxBoardDesc { + hwaddr flash_base; size_t flash_size; size_t flash_sector_size; size_t sram_size; @@ -219,7 +220,7 @@ static void lx_init(const LxBoardDesc *board, QEMUMachineInitArgs *args) dinfo = drive_get(IF_PFLASH, 0, 0); if (dinfo) { - flash = pflash_cfi01_register(0xf8000000, + flash = pflash_cfi01_register(board->flash_base, NULL, "lx60.io.flash", board->flash_size, dinfo->bdrv, board->flash_sector_size, board->flash_size / board->flash_sector_size, @@ -265,7 +266,9 @@ static void lx_init(const LxBoardDesc *board, QEMUMachineInitArgs *args) MemoryRegion *flash_io = g_malloc(sizeof(*flash_io)); memory_region_init_alias(flash_io, NULL, "lx60.flash", - flash_mr, 0, board->flash_size); + flash_mr, 0, + board->flash_size < 0x02000000 ? + board->flash_size : 0x02000000); memory_region_add_subregion(system_memory, 0xfe000000, flash_io); } @@ -275,7 +278,8 @@ static void lx_init(const LxBoardDesc *board, QEMUMachineInitArgs *args) static void xtensa_lx60_init(QEMUMachineInitArgs *args) { static const LxBoardDesc lx60_board = { - .flash_size = 0x400000, + .flash_base = 0xf8000000, + .flash_size = 0x00400000, .flash_sector_size = 0x10000, .sram_size = 0x20000, }; @@ -285,13 +289,36 @@ static void xtensa_lx60_init(QEMUMachineInitArgs *args) static void xtensa_lx200_init(QEMUMachineInitArgs *args) { static const LxBoardDesc lx200_board = { - .flash_size = 0x1000000, + .flash_base = 0xf8000000, + .flash_size = 0x01000000, .flash_sector_size = 0x20000, .sram_size = 0x2000000, }; lx_init(&lx200_board, args); } +static void xtensa_ml605_init(QEMUMachineInitArgs *args) +{ + static const LxBoardDesc ml605_board = { + .flash_base = 0xf8000000, + .flash_size = 0x02000000, + .flash_sector_size = 0x20000, + .sram_size = 0x2000000, + }; + lx_init(&ml605_board, args); +} + +static void xtensa_kc705_init(QEMUMachineInitArgs *args) +{ + static const LxBoardDesc kc705_board = { + .flash_base = 0xf0000000, + .flash_size = 0x08000000, + .flash_sector_size = 0x20000, + .sram_size = 0x2000000, + }; + lx_init(&kc705_board, args); +} + static QEMUMachine xtensa_lx60_machine = { .name = "lx60", .desc = "lx60 EVB (" XTENSA_DEFAULT_CPU_MODEL ")", @@ -306,10 +333,26 @@ static QEMUMachine xtensa_lx200_machine = { .max_cpus = 4, }; +static QEMUMachine xtensa_ml605_machine = { + .name = "ml605", + .desc = "ml605 EVB (" XTENSA_DEFAULT_CPU_MODEL ")", + .init = xtensa_ml605_init, + .max_cpus = 4, +}; + +static QEMUMachine xtensa_kc705_machine = { + .name = "kc705", + .desc = "kc705 EVB (" XTENSA_DEFAULT_CPU_MODEL ")", + .init = xtensa_kc705_init, + .max_cpus = 4, +}; + static void xtensa_lx_machines_init(void) { qemu_register_machine(&xtensa_lx60_machine); qemu_register_machine(&xtensa_lx200_machine); + qemu_register_machine(&xtensa_ml605_machine); + qemu_register_machine(&xtensa_kc705_machine); } machine_init(xtensa_lx_machines_init); diff --git a/include/block/block.h b/include/block/block.h index 963a61f..780f48b 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -102,6 +102,9 @@ typedef enum { #define BDRV_O_CHECK 0x1000 /* open solely for consistency check */ #define BDRV_O_ALLOW_RDWR 0x2000 /* allow reopen to change from r/o to r/w */ #define BDRV_O_UNMAP 0x4000 /* execute guest UNMAP/TRIM operations */ +#define BDRV_O_PROTOCOL 0x8000 /* if no block driver is explicitly given: + select an appropriate protocol driver, + ignoring the format layer */ #define BDRV_O_CACHE_MASK (BDRV_O_NOCACHE | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH) @@ -183,15 +186,13 @@ void bdrv_swap(BlockDriverState *bs_new, BlockDriverState *bs_old); void bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top); int bdrv_parse_cache_flags(const char *mode, int *flags); int bdrv_parse_discard_flags(const char *mode, int *flags); -int bdrv_file_open(BlockDriverState **pbs, const char *filename, - const char *reference, QDict *options, int flags, - Error **errp); int bdrv_open_image(BlockDriverState **pbs, const char *filename, QDict *options, const char *bdref_key, int flags, - bool force_raw, bool allow_none, Error **errp); + bool allow_none, Error **errp); int bdrv_open_backing_file(BlockDriverState *bs, QDict *options, Error **errp); -int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options, - int flags, BlockDriver *drv, Error **errp); +int bdrv_open(BlockDriverState **pbs, const char *filename, + const char *reference, QDict *options, int flags, + BlockDriver *drv, Error **errp); BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue, BlockDriverState *bs, int flags); int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp); diff --git a/include/block/nbd.h b/include/block/nbd.h index c90f5e4..79502a0 100644 --- a/include/block/nbd.h +++ b/include/block/nbd.h @@ -62,12 +62,6 @@ enum { #define NBD_MAX_BUFFER_SIZE (32 * 1024 * 1024) ssize_t nbd_wr_sync(int fd, void *buffer, size_t size, bool do_read); -int tcp_socket_incoming(const char *address, uint16_t port); -int tcp_socket_incoming_spec(const char *address_and_port); -int tcp_socket_outgoing_opts(QemuOpts *opts); -int unix_socket_outgoing(const char *path); -int unix_socket_incoming(const char *path); - int nbd_receive_negotiate(int csock, const char *name, uint32_t *flags, off_t *size, size_t *blocksize); int nbd_init(int fd, int csock, uint32_t flags, off_t size, size_t blocksize); diff --git a/include/hw/ide.h b/include/hw/ide.h index 507e6d3..bc8bd32 100644 --- a/include/hw/ide.h +++ b/include/hw/ide.h @@ -17,6 +17,7 @@ void pci_cmd646_ide_init(PCIBus *bus, DriveInfo **hd_table, PCIDevice *pci_piix3_xen_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn); PCIDevice *pci_piix3_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn); PCIDevice *pci_piix4_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn); +int pci_piix3_xen_ide_unplug(DeviceState *dev); void vt82c686b_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn); /* ide-mmio.c */ diff --git a/include/hw/xilinx.h b/include/hw/xilinx.h deleted file mode 100644 index 9d6debe..0000000 --- a/include/hw/xilinx.h +++ /dev/null @@ -1,90 +0,0 @@ -#ifndef HW_XILINX_H -#define HW_XILINX_H 1 - - -#include "qemu-common.h" -#include "qapi/qmp/qerror.h" -#include "hw/stream.h" -#include "net/net.h" - -static inline DeviceState * -xilinx_intc_create(hwaddr base, qemu_irq irq, int kind_of_intr) -{ - DeviceState *dev; - - dev = qdev_create(NULL, "xlnx.xps-intc"); - qdev_prop_set_uint32(dev, "kind-of-intr", kind_of_intr); - qdev_init_nofail(dev); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq); - return dev; -} - -/* OPB Timer/Counter. */ -static inline DeviceState * -xilinx_timer_create(hwaddr base, qemu_irq irq, int oto, int freq) -{ - DeviceState *dev; - - dev = qdev_create(NULL, "xlnx.xps-timer"); - qdev_prop_set_uint32(dev, "one-timer-only", oto); - qdev_prop_set_uint32(dev, "clock-frequency", freq); - qdev_init_nofail(dev); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq); - return dev; -} - -/* XPS Ethernet Lite MAC. */ -static inline DeviceState * -xilinx_ethlite_create(NICInfo *nd, hwaddr base, qemu_irq irq, - int txpingpong, int rxpingpong) -{ - DeviceState *dev; - - qemu_check_nic_model(nd, "xlnx.xps-ethernetlite"); - - dev = qdev_create(NULL, "xlnx.xps-ethernetlite"); - qdev_set_nic_properties(dev, nd); - qdev_prop_set_uint32(dev, "tx-ping-pong", txpingpong); - qdev_prop_set_uint32(dev, "rx-ping-pong", rxpingpong); - qdev_init_nofail(dev); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq); - return dev; -} - -static inline void -xilinx_axiethernet_init(DeviceState *dev, NICInfo *nd, StreamSlave *ds, - StreamSlave *cs, hwaddr base, qemu_irq irq, int txmem, - int rxmem) -{ - qdev_set_nic_properties(dev, nd); - qdev_prop_set_uint32(dev, "rxmem", rxmem); - qdev_prop_set_uint32(dev, "txmem", txmem); - object_property_set_link(OBJECT(dev), OBJECT(ds), - "axistream-connected", &error_abort); - object_property_set_link(OBJECT(dev), OBJECT(cs), - "axistream-control-connected", &error_abort); - qdev_init_nofail(dev); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq); -} - -static inline void -xilinx_axidma_init(DeviceState *dev, StreamSlave *ds, StreamSlave *cs, - hwaddr base, qemu_irq irq, qemu_irq irq2, int freqhz) -{ - qdev_prop_set_uint32(dev, "freqhz", freqhz); - object_property_set_link(OBJECT(dev), OBJECT(ds), - "axistream-connected", &error_abort); - object_property_set_link(OBJECT(dev), OBJECT(cs), - "axistream-control-connected", &error_abort); - qdev_init_nofail(dev); - - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 1, irq2); -} - -#endif diff --git a/include/monitor/monitor.h b/include/monitor/monitor.h index 7e5f752..a49ea11 100644 --- a/include/monitor/monitor.h +++ b/include/monitor/monitor.h @@ -49,6 +49,8 @@ typedef enum MonitorEvent { QEVENT_SPICE_MIGRATE_COMPLETED, QEVENT_GUEST_PANICKED, QEVENT_BLOCK_IMAGE_CORRUPTED, + QEVENT_QUORUM_FAILURE, + QEVENT_QUORUM_REPORT_BAD, /* Add to 'monitor_event_names' array in monitor.c when * defining new events here */ diff --git a/include/qemu-common.h b/include/qemu-common.h index b0e34b2..c8a58a8 100644 --- a/include/qemu-common.h +++ b/include/qemu-common.h @@ -338,6 +338,8 @@ size_t qemu_iovec_from_buf(QEMUIOVector *qiov, size_t offset, const void *buf, size_t bytes); size_t qemu_iovec_memset(QEMUIOVector *qiov, size_t offset, int fillc, size_t bytes); +ssize_t qemu_iovec_compare(QEMUIOVector *a, QEMUIOVector *b); +void qemu_iovec_clone(QEMUIOVector *dest, const QEMUIOVector *src, void *buf); bool buffer_is_zero(const void *buf, size_t len); @@ -352,7 +354,7 @@ char *qemu_find_file(int type, const char *name); /* OS specific functions */ void os_setup_early_signal_handling(void); -char *os_find_datadir(const char *argv0); +char *os_find_datadir(void); void os_parse_cmd_args(int index, const char *optarg); void os_pidfile_error(void); diff --git a/include/qemu/module.h b/include/qemu/module.h index c4ccd57..72d9498 100644 --- a/include/qemu/module.h +++ b/include/qemu/module.h @@ -14,11 +14,31 @@ #ifndef QEMU_MODULE_H #define QEMU_MODULE_H +#include "qemu/osdep.h" + +#define DSO_STAMP_FUN glue(qemu_stamp, CONFIG_STAMP) +#define DSO_STAMP_FUN_STR stringify(DSO_STAMP_FUN) + +#ifdef BUILD_DSO +void DSO_STAMP_FUN(void); +/* This is a dummy symbol to identify a loaded DSO as a QEMU module, so we can + * distinguish "version mismatch" from "not a QEMU module", when the stamp + * check fails during module loading */ +void qemu_module_dummy(void); + +#define module_init(function, type) \ +static void __attribute__((constructor)) do_qemu_init_ ## function(void) \ +{ \ + register_dso_module_init(function, type); \ +} +#else /* This should not be used directly. Use block_init etc. instead. */ #define module_init(function, type) \ -static void __attribute__((constructor)) do_qemu_init_ ## function(void) { \ +static void __attribute__((constructor)) do_qemu_init_ ## function(void) \ +{ \ register_module_init(function, type); \ } +#endif typedef enum { MODULE_INIT_BLOCK, @@ -34,6 +54,7 @@ typedef enum { #define type_init(function) module_init(function, MODULE_INIT_QOM) void register_module_init(void (*fn)(void), module_init_type type); +void register_dso_module_init(void (*fn)(void), module_init_type type); void module_call_init(module_init_type type); diff --git a/include/qemu/option.h b/include/qemu/option.h index 3ea871a..8c0ac34 100644 --- a/include/qemu/option.h +++ b/include/qemu/option.h @@ -79,6 +79,8 @@ void parse_option_size(const char *name, const char *value, void free_option_parameters(QEMUOptionParameter *list); void print_option_parameters(QEMUOptionParameter *list); void print_option_help(QEMUOptionParameter *list); +bool has_help_option(const char *param); +bool is_valid_option_list(const char *param); /* ------------------------------------------------------------------ */ diff --git a/include/qemu/osdep.h b/include/qemu/osdep.h index eac7172..ffb2966 100644 --- a/include/qemu/osdep.h +++ b/include/qemu/osdep.h @@ -215,6 +215,15 @@ bool fips_get_state(void); */ char *qemu_get_local_state_pathname(const char *relative_pathname); +/* Find program directory, and save it for later usage with + * qemu_get_exec_dir(). + * Try OS specific API first, if not working, parse from argv0. */ +void qemu_init_exec_dir(const char *argv0); + +/* Get the saved exec dir. + * Caller needs to release the returned string by g_free() */ +char *qemu_get_exec_dir(void); + /** * qemu_getauxval: * @type: the auxiliary vector key to lookup diff --git a/module-common.c b/module-common.c new file mode 100644 index 0000000..50c6750 --- /dev/null +++ b/module-common.c @@ -0,0 +1,10 @@ +#include "config-host.h" +#include "qemu/module.h" + +void qemu_module_dummy(void) +{ +} + +void DSO_STAMP_FUN(void) +{ +} @@ -508,6 +508,8 @@ static const char *monitor_event_names[] = { [QEVENT_SPICE_MIGRATE_COMPLETED] = "SPICE_MIGRATE_COMPLETED", [QEVENT_GUEST_PANICKED] = "GUEST_PANICKED", [QEVENT_BLOCK_IMAGE_CORRUPTED] = "BLOCK_IMAGE_CORRUPTED", + [QEVENT_QUORUM_FAILURE] = "QUORUM_FAILURE", + [QEVENT_QUORUM_REPORT_BAD] = "QUORUM_REPORT_BAD", }; QEMU_BUILD_BUG_ON(ARRAY_SIZE(monitor_event_names) != QEVENT_MAX) @@ -638,6 +640,9 @@ static void monitor_protocol_event_init(void) monitor_protocol_event_throttle(QEVENT_RTC_CHANGE, 1000); monitor_protocol_event_throttle(QEVENT_BALLOON_CHANGE, 1000); monitor_protocol_event_throttle(QEVENT_WATCHDOG, 1000); + /* limit the rate of quorum events to avoid hammering the management */ + monitor_protocol_event_throttle(QEVENT_QUORUM_REPORT_BAD, 1000); + monitor_protocol_event_throttle(QEVENT_QUORUM_FAILURE, 1000); } /** @@ -188,72 +188,6 @@ static ssize_t write_sync(int fd, void *buffer, size_t size) return ret; } -static void combine_addr(char *buf, size_t len, const char* address, - uint16_t port) -{ - /* If the address-part contains a colon, it's an IPv6 IP so needs [] */ - if (strstr(address, ":")) { - snprintf(buf, len, "[%s]:%u", address, port); - } else { - snprintf(buf, len, "%s:%u", address, port); - } -} - -int tcp_socket_outgoing_opts(QemuOpts *opts) -{ - Error *local_err = NULL; - int fd = inet_connect_opts(opts, &local_err, NULL, NULL); - if (local_err != NULL) { - qerror_report_err(local_err); - error_free(local_err); - } - - return fd; -} - -int tcp_socket_incoming(const char *address, uint16_t port) -{ - char address_and_port[128]; - combine_addr(address_and_port, 128, address, port); - return tcp_socket_incoming_spec(address_and_port); -} - -int tcp_socket_incoming_spec(const char *address_and_port) -{ - Error *local_err = NULL; - int fd = inet_listen(address_and_port, NULL, 0, SOCK_STREAM, 0, &local_err); - - if (local_err != NULL) { - qerror_report_err(local_err); - error_free(local_err); - } - return fd; -} - -int unix_socket_incoming(const char *path) -{ - Error *local_err = NULL; - int fd = unix_listen(path, NULL, 0, &local_err); - - if (local_err != NULL) { - qerror_report_err(local_err); - error_free(local_err); - } - return fd; -} - -int unix_socket_outgoing(const char *path) -{ - Error *local_err = NULL; - int fd = unix_connect(path, &local_err); - - if (local_err != NULL) { - qerror_report_err(local_err); - error_free(local_err); - } - return fd; -} - /* Basic flow for negotiation Server Client @@ -84,46 +84,17 @@ void os_setup_signal_handling(void) running from the build tree this will be "$bindir/../pc-bios". */ #define SHARE_SUFFIX "/share/qemu" #define BUILD_SUFFIX "/pc-bios" -char *os_find_datadir(const char *argv0) +char *os_find_datadir(void) { - char *dir; - char *p = NULL; + char *dir, *exec_dir; char *res; - char buf[PATH_MAX]; size_t max_len; -#if defined(__linux__) - { - int len; - len = readlink("/proc/self/exe", buf, sizeof(buf) - 1); - if (len > 0) { - buf[len] = 0; - p = buf; - } - } -#elif defined(__FreeBSD__) - { - static int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1}; - size_t len = sizeof(buf) - 1; - - *buf = '\0'; - if (!sysctl(mib, ARRAY_SIZE(mib), buf, &len, NULL, 0) && - *buf) { - buf[sizeof(buf) - 1] = '\0'; - p = buf; - } - } -#endif - /* If we don't have any way of figuring out the actual executable - location then try argv[0]. */ - if (!p) { - p = realpath(argv0, buf); - if (!p) { - return NULL; - } + exec_dir = qemu_get_exec_dir(); + if (exec_dir == NULL) { + return NULL; } - dir = dirname(p); - dir = dirname(dir); + dir = dirname(exec_dir); max_len = strlen(dir) + MAX(strlen(SHARE_SUFFIX), strlen(BUILD_SUFFIX)) + 1; @@ -137,6 +108,7 @@ char *os_find_datadir(const char *argv0) } } + g_free(exec_dir); return res; } #undef SHARE_SUFFIX @@ -84,26 +84,9 @@ void os_setup_early_signal_handling(void) } /* Look for support files in the same directory as the executable. */ -char *os_find_datadir(const char *argv0) +char *os_find_datadir(void) { - char *p; - char buf[MAX_PATH]; - DWORD len; - - len = GetModuleFileName(NULL, buf, sizeof(buf) - 1); - if (len == 0) { - return NULL; - } - - buf[len] = 0; - p = buf + len - 1; - while (p != buf && *p != '\\') - p--; - *p = 0; - if (access(buf, R_OK) == 0) { - return g_strdup(buf); - } - return NULL; + return qemu_get_exec_dir(); } void os_set_line_buffering(void) diff --git a/qapi-schema.json b/qapi-schema.json index 473c096..fcb22800 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -4432,6 +4432,24 @@ 'raw': 'BlockdevRef' } } ## +# @BlockdevOptionsQuorum +# +# Driver specific block device options for Quorum +# +# @blkverify: #optional true if the driver must print content mismatch +# +# @children: the children block device to use +# +# @vote_threshold: the vote limit under which a read will fail +# +# Since: 2.0 +## +{ 'type': 'BlockdevOptionsQuorum', + 'data': { '*blkverify': 'bool', + 'children': [ 'BlockdevRef' ], + 'vote-threshold': 'int' } } + +## # @BlockdevOptions # # Options for creating a block device. @@ -4470,7 +4488,8 @@ 'vdi': 'BlockdevOptionsGenericFormat', 'vhdx': 'BlockdevOptionsGenericFormat', 'vmdk': 'BlockdevOptionsGenericCOWFormat', - 'vpc': 'BlockdevOptionsGenericFormat' + 'vpc': 'BlockdevOptionsGenericFormat', + 'quorum': 'BlockdevOptionsQuorum' } } ## @@ -250,16 +250,19 @@ static int print_block_option_help(const char *filename, const char *fmt) return 1; } - proto_drv = bdrv_find_protocol(filename, true); - if (!proto_drv) { - error_report("Unknown protocol '%s'", filename); - return 1; - } - create_options = append_option_parameters(create_options, drv->create_options); - create_options = append_option_parameters(create_options, - proto_drv->create_options); + + if (filename) { + proto_drv = bdrv_find_protocol(filename, true); + if (!proto_drv) { + error_report("Unknown protocol '%s'", filename); + return 1; + } + create_options = append_option_parameters(create_options, + proto_drv->create_options); + } + print_option_help(create_options); free_option_parameters(create_options); return 0; @@ -289,7 +292,7 @@ static BlockDriverState *bdrv_new_open(const char *filename, drv = NULL; } - ret = bdrv_open(bs, filename, NULL, flags, drv, &local_err); + ret = bdrv_open(&bs, filename, NULL, NULL, flags, drv, &local_err); if (ret < 0) { error_report("Could not open '%s': %s", filename, error_get_pretty(local_err)); @@ -310,9 +313,7 @@ static BlockDriverState *bdrv_new_open(const char *filename, } return bs; fail: - if (bs) { - bdrv_unref(bs); - } + bdrv_unref(bs); return NULL; } @@ -371,13 +372,23 @@ static int img_create(int argc, char **argv) case 'e': error_report("option -e is deprecated, please use \'-o " "encryption\' instead!"); - return 1; + goto fail; case '6': error_report("option -6 is deprecated, please use \'-o " "compat6\' instead!"); - return 1; + goto fail; case 'o': - options = optarg; + if (!is_valid_option_list(optarg)) { + error_report("Invalid option list: %s", optarg); + goto fail; + } + if (!options) { + options = g_strdup(optarg); + } else { + char *old_options = options; + options = g_strdup_printf("%s,%s", options, optarg); + g_free(old_options); + } break; case 'q': quiet = true; @@ -386,10 +397,16 @@ static int img_create(int argc, char **argv) } /* Get the filename */ + filename = (optind < argc) ? argv[optind] : NULL; + if (options && has_help_option(options)) { + g_free(options); + return print_block_option_help(filename, fmt); + } + if (optind >= argc) { help(); } - filename = argv[optind++]; + optind++; /* Get image size, if specified */ if (optind < argc) { @@ -405,7 +422,7 @@ static int img_create(int argc, char **argv) error_report("kilobytes, megabytes, gigabytes, terabytes, " "petabytes and exabytes."); } - return 1; + goto fail; } img_size = (uint64_t)sval; } @@ -413,19 +430,20 @@ static int img_create(int argc, char **argv) help(); } - if (options && is_help_option(options)) { - return print_block_option_help(filename, fmt); - } - bdrv_img_create(filename, fmt, base_filename, base_fmt, options, img_size, BDRV_O_FLAGS, &local_err, quiet); if (local_err) { error_report("%s: %s", filename, error_get_pretty(local_err)); error_free(local_err); - return 1; + goto fail; } + g_free(options); return 0; + +fail: + g_free(options); + return 1; } static void dump_json_image_check(ImageCheck *check, bool quiet) @@ -1150,6 +1168,9 @@ static int img_convert(int argc, char **argv) Error *local_err = NULL; QemuOpts *sn_opts = NULL; + /* Initialize before goto out */ + qemu_progress_init(progress, 1.0); + fmt = NULL; out_fmt = "raw"; cache = "unsafe"; @@ -1181,13 +1202,26 @@ static int img_convert(int argc, char **argv) case 'e': error_report("option -e is deprecated, please use \'-o " "encryption\' instead!"); - return 1; + ret = -1; + goto out; case '6': error_report("option -6 is deprecated, please use \'-o " "compat6\' instead!"); - return 1; + ret = -1; + goto out; case 'o': - options = optarg; + if (!is_valid_option_list(optarg)) { + error_report("Invalid option list: %s", optarg); + ret = -1; + goto out; + } + if (!options) { + options = g_strdup(optarg); + } else { + char *old_options = options; + options = g_strdup_printf("%s,%s", options, optarg); + g_free(old_options); + } break; case 's': snapshot_name = optarg; @@ -1198,7 +1232,8 @@ static int img_convert(int argc, char **argv) if (!sn_opts) { error_report("Failed in parsing snapshot param '%s'", optarg); - return 1; + ret = -1; + goto out; } } else { snapshot_name = optarg; @@ -1211,7 +1246,8 @@ static int img_convert(int argc, char **argv) sval = strtosz_suffix(optarg, &end, STRTOSZ_DEFSUFFIX_B); if (sval < 0 || *end) { error_report("Invalid minimum zero buffer size for sparse output specified"); - return 1; + ret = -1; + goto out; } min_sparse = sval / BDRV_SECTOR_SIZE; @@ -1237,20 +1273,18 @@ static int img_convert(int argc, char **argv) } bs_n = argc - optind - 1; - if (bs_n < 1) { - help(); - } - - out_filename = argv[argc - 1]; + out_filename = bs_n >= 1 ? argv[argc - 1] : NULL; - /* Initialize before goto out */ - qemu_progress_init(progress, 1.0); - - if (options && is_help_option(options)) { + if (options && has_help_option(options)) { ret = print_block_option_help(out_filename, out_fmt); goto out; } + if (bs_n < 1) { + help(); + } + + if (bs_n > 1 && out_baseimg) { error_report("-B makes no sense when concatenating multiple input " "images"); @@ -1639,6 +1673,7 @@ out: free_option_parameters(create_options); free_option_parameters(param); qemu_vfree(buf); + g_free(options); if (sn_opts) { qemu_opts_del(sn_opts); } @@ -2314,7 +2349,7 @@ static int img_rebase(int argc, char **argv) bs_old_backing = bdrv_new("old_backing"); bdrv_get_backing_filename(bs, backing_name, sizeof(backing_name)); - ret = bdrv_open(bs_old_backing, backing_name, NULL, BDRV_O_FLAGS, + ret = bdrv_open(&bs_old_backing, backing_name, NULL, NULL, BDRV_O_FLAGS, old_backing_drv, &local_err); if (ret) { error_report("Could not open old backing file '%s': %s", @@ -2324,8 +2359,8 @@ static int img_rebase(int argc, char **argv) } if (out_baseimg[0]) { bs_new_backing = bdrv_new("new_backing"); - ret = bdrv_open(bs_new_backing, out_baseimg, NULL, BDRV_O_FLAGS, - new_backing_drv, &local_err); + ret = bdrv_open(&bs_new_backing, out_baseimg, NULL, NULL, + BDRV_O_FLAGS, new_backing_drv, &local_err); if (ret) { error_report("Could not open new backing file '%s': %s", out_baseimg, error_get_pretty(local_err)); @@ -2637,7 +2672,18 @@ static int img_amend(int argc, char **argv) help(); break; case 'o': - options = optarg; + if (!is_valid_option_list(optarg)) { + error_report("Invalid option list: %s", optarg); + ret = -1; + goto out; + } + if (!options) { + options = g_strdup(optarg); + } else { + char *old_options = options; + options = g_strdup_printf("%s,%s", options, optarg); + g_free(old_options); + } break; case 'f': fmt = optarg; @@ -2648,15 +2694,21 @@ static int img_amend(int argc, char **argv) } } - if (optind != argc - 1) { + if (!options) { help(); } - if (!options) { - help(); + filename = (optind == argc - 1) ? argv[argc - 1] : NULL; + if (fmt && has_help_option(options)) { + /* If a format is explicitly specified (and possibly no filename is + * given), print option help here */ + ret = print_block_option_help(filename, fmt); + goto out; } - filename = argv[argc - 1]; + if (optind != argc - 1) { + help(); + } bs = bdrv_new_open(filename, fmt, BDRV_O_FLAGS | BDRV_O_RDWR, true, quiet); if (!bs) { @@ -2667,7 +2719,8 @@ static int img_amend(int argc, char **argv) fmt = bs->drv->format_name; - if (is_help_option(options)) { + if (has_help_option(options)) { + /* If the format was auto-detected, print option help here */ ret = print_block_option_help(filename, fmt); goto out; } @@ -2694,6 +2747,8 @@ out: } free_option_parameters(create_options); free_option_parameters(options_param); + g_free(options); + if (ret) { return 1; } @@ -2719,6 +2774,7 @@ int main(int argc, char **argv) #endif error_set_progname(argv[0]); + qemu_init_exec_dir(argv[0]); qemu_init_main_loop(); bdrv_init(); @@ -59,7 +59,9 @@ static int openfile(char *name, int flags, int growable, QDict *opts) } if (growable) { - if (bdrv_file_open(&qemuio_bs, name, NULL, opts, flags, &local_err)) { + if (bdrv_open(&qemuio_bs, name, NULL, opts, flags | BDRV_O_PROTOCOL, + NULL, &local_err)) + { fprintf(stderr, "%s: can't open device %s: %s\n", progname, name, error_get_pretty(local_err)); error_free(local_err); @@ -68,7 +70,9 @@ static int openfile(char *name, int flags, int growable, QDict *opts) } else { qemuio_bs = bdrv_new("hda"); - if (bdrv_open(qemuio_bs, name, opts, flags, NULL, &local_err) < 0) { + if (bdrv_open(&qemuio_bs, name, NULL, opts, flags, NULL, &local_err) + < 0) + { fprintf(stderr, "%s: can't open device %s: %s\n", progname, name, error_get_pretty(local_err)); error_free(local_err); @@ -381,6 +385,7 @@ int main(int argc, char **argv) #endif progname = basename(argv[0]); + qemu_init_exec_dir(argv[0]); while ((c = getopt_long(argc, argv, sopt, lopt, &opt_index)) != -1) { switch (c) { @@ -20,6 +20,8 @@ #include "block/block.h" #include "block/nbd.h" #include "qemu/main-loop.h" +#include "qemu/sockets.h" +#include "qemu/error-report.h" #include "block/snapshot.h" #include <stdarg.h> @@ -201,6 +203,56 @@ static void termsig_handler(int signum) qemu_notify_event(); } +static void combine_addr(char *buf, size_t len, const char* address, + uint16_t port) +{ + /* If the address-part contains a colon, it's an IPv6 IP so needs [] */ + if (strstr(address, ":")) { + snprintf(buf, len, "[%s]:%u", address, port); + } else { + snprintf(buf, len, "%s:%u", address, port); + } +} + +static int tcp_socket_incoming(const char *address, uint16_t port) +{ + char address_and_port[128]; + Error *local_err = NULL; + + combine_addr(address_and_port, 128, address, port); + int fd = inet_listen(address_and_port, NULL, 0, SOCK_STREAM, 0, &local_err); + + if (local_err != NULL) { + qerror_report_err(local_err); + error_free(local_err); + } + return fd; +} + +static int unix_socket_incoming(const char *path) +{ + Error *local_err = NULL; + int fd = unix_listen(path, NULL, 0, &local_err); + + if (local_err != NULL) { + qerror_report_err(local_err); + error_free(local_err); + } + return fd; +} + +static int unix_socket_outgoing(const char *path) +{ + Error *local_err = NULL; + int fd = unix_connect(path, &local_err); + + if (local_err != NULL) { + qerror_report_err(local_err); + error_free(local_err); + } + return fd; +} + static void *show_parts(void *arg) { char *device = arg; @@ -376,6 +428,7 @@ int main(int argc, char **argv) memset(&sa_sigterm, 0, sizeof(sa_sigterm)); sa_sigterm.sa_handler = termsig_handler; sigaction(SIGTERM, &sa_sigterm, NULL); + qemu_init_exec_dir(argv[0]); while ((ch = getopt_long(argc, argv, sopt, lopt, &opt_ind)) != -1) { switch (ch) { @@ -597,7 +650,7 @@ int main(int argc, char **argv) bs = bdrv_new("hda"); srcpath = argv[optind]; - ret = bdrv_open(bs, srcpath, NULL, flags, drv, &local_err); + ret = bdrv_open(&bs, srcpath, NULL, NULL, flags, drv, &local_err); if (ret < 0) { errno = -ret; err(EXIT_FAILURE, "Failed to bdrv_open '%s': %s", argv[optind], diff --git a/qga/channel-win32.c b/qga/channel-win32.c index 8a303f3..0d5e5f5 100644 --- a/qga/channel-win32.c +++ b/qga/channel-win32.c @@ -287,12 +287,22 @@ GIOStatus ga_channel_write_all(GAChannel *c, const char *buf, size_t size) static gboolean ga_channel_open(GAChannel *c, GAChannelMethod method, const gchar *path) { - if (method != GA_CHANNEL_VIRTIO_SERIAL) { + COMMTIMEOUTS comTimeOut = {0}; + gchar newpath[MAXPATHLEN] = {0}; + comTimeOut.ReadIntervalTimeout = 1; + + if (method != GA_CHANNEL_VIRTIO_SERIAL && method != GA_CHANNEL_ISA_SERIAL) { g_critical("unsupported communication method"); return false; } - c->handle = CreateFile(path, GENERIC_READ | GENERIC_WRITE, 0, NULL, + if (method == GA_CHANNEL_ISA_SERIAL){ + snprintf(newpath, sizeof(newpath), "\\\\.\\%s", path); + }else { + g_strlcpy(newpath, path, sizeof(newpath)); + } + + c->handle = CreateFile(newpath, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED, NULL); if (c->handle == INVALID_HANDLE_VALUE) { @@ -300,6 +310,12 @@ static gboolean ga_channel_open(GAChannel *c, GAChannelMethod method, return false; } + if (method == GA_CHANNEL_ISA_SERIAL && !SetCommTimeouts(c->handle,&comTimeOut)) { + g_critical("error setting timeout for com port: %lu",GetLastError()); + CloseHandle(c->handle); + return false; + } + return true; } diff --git a/qga/commands-posix.c b/qga/commands-posix.c index cae4171..6b5f11f 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -142,7 +142,7 @@ int64_t qmp_guest_get_time(Error **errp) return time_ns; } -void qmp_guest_set_time(int64_t time_ns, Error **errp) +void qmp_guest_set_time(bool has_time, int64_t time_ns, Error **errp) { int ret; int status; @@ -150,22 +150,28 @@ void qmp_guest_set_time(int64_t time_ns, Error **errp) Error *local_err = NULL; struct timeval tv; - /* year-2038 will overflow in case time_t is 32bit */ - if (time_ns / 1000000000 != (time_t)(time_ns / 1000000000)) { - error_setg(errp, "Time %" PRId64 " is too large", time_ns); - return; - } + /* If user has passed a time, validate and set it. */ + if (has_time) { + /* year-2038 will overflow in case time_t is 32bit */ + if (time_ns / 1000000000 != (time_t)(time_ns / 1000000000)) { + error_setg(errp, "Time %" PRId64 " is too large", time_ns); + return; + } - tv.tv_sec = time_ns / 1000000000; - tv.tv_usec = (time_ns % 1000000000) / 1000; + tv.tv_sec = time_ns / 1000000000; + tv.tv_usec = (time_ns % 1000000000) / 1000; - ret = settimeofday(&tv, NULL); - if (ret < 0) { - error_setg_errno(errp, errno, "Failed to set time to guest"); - return; + ret = settimeofday(&tv, NULL); + if (ret < 0) { + error_setg_errno(errp, errno, "Failed to set time to guest"); + return; + } } - /* Set the Hardware Clock to the current System Time. */ + /* Now, if user has passed a time to set and the system time is set, we + * just need to synchronize the hardware clock. However, if no time was + * passed, user is requesting the opposite: set the system time from the + * hardware clock. */ pid = fork(); if (pid == 0) { setsid(); @@ -173,7 +179,10 @@ void qmp_guest_set_time(int64_t time_ns, Error **errp) reopen_fd_to_null(1); reopen_fd_to_null(2); - execle("/sbin/hwclock", "hwclock", "-w", NULL, environ); + /* Use '/sbin/hwclock -w' to set RTC from the system time, + * or '/sbin/hwclock -s' to set the system time from RTC. */ + execle("/sbin/hwclock", "hwclock", has_time ? "-w" : "-s", + NULL, environ); _exit(EXIT_FAILURE); } else if (pid < 0) { error_setg_errno(errp, errno, "failed to create child process"); @@ -525,7 +534,7 @@ struct GuestFileSeek *qmp_guest_file_seek(int64_t handle, int64_t offset, if (ret == -1) { error_setg_errno(err, errno, "failed to seek file"); } else { - seek_data = g_malloc0(sizeof(GuestFileRead)); + seek_data = g_new0(GuestFileSeek, 1); seek_data->position = ftell(fh); seek_data->eof = feof(fh); } diff --git a/qga/commands-win32.c b/qga/commands-win32.c index 50094dd..0ee07b6 100644 --- a/qga/commands-win32.c +++ b/qga/commands-win32.c @@ -370,25 +370,37 @@ int64_t qmp_guest_get_time(Error **errp) return time_ns; } -void qmp_guest_set_time(int64_t time_ns, Error **errp) +void qmp_guest_set_time(bool has_time, int64_t time_ns, Error **errp) { SYSTEMTIME ts; FILETIME tf; LONGLONG time; - if (time_ns < 0 || time_ns / 100 > INT64_MAX - W32_FT_OFFSET) { - error_setg(errp, "Time %" PRId64 "is invalid", time_ns); - return; - } + if (has_time) { + /* Okay, user passed a time to set. Validate it. */ + if (time_ns < 0 || time_ns / 100 > INT64_MAX - W32_FT_OFFSET) { + error_setg(errp, "Time %" PRId64 "is invalid", time_ns); + return; + } - time = time_ns / 100 + W32_FT_OFFSET; + time = time_ns / 100 + W32_FT_OFFSET; - tf.dwLowDateTime = (DWORD) time; - tf.dwHighDateTime = (DWORD) (time >> 32); + tf.dwLowDateTime = (DWORD) time; + tf.dwHighDateTime = (DWORD) (time >> 32); - if (!FileTimeToSystemTime(&tf, &ts)) { - error_setg(errp, "Failed to convert system time %d", (int)GetLastError()); - return; + if (!FileTimeToSystemTime(&tf, &ts)) { + error_setg(errp, "Failed to convert system time %d", + (int)GetLastError()); + return; + } + } else { + /* Otherwise read the time from RTC which contains the correct value. + * Hopefully. */ + GetSystemTime(&ts); + if (ts.wYear < 1601 || ts.wYear > 30827) { + error_setg(errp, "Failed to get time"); + return; + } } acquire_privilege(SE_SYSTEMTIME_NAME, errp); @@ -47,9 +47,11 @@ #ifndef _WIN32 #define QGA_VIRTIO_PATH_DEFAULT "/dev/virtio-ports/org.qemu.guest_agent.0" #define QGA_STATE_RELATIVE_DIR "run" +#define QGA_SERIAL_PATH_DEFAULT "/dev/ttyS0" #else #define QGA_VIRTIO_PATH_DEFAULT "\\\\.\\Global\\org.qemu.guest_agent.0" #define QGA_STATE_RELATIVE_DIR "qemu-ga" +#define QGA_SERIAL_PATH_DEFAULT "COM1" #endif #ifdef CONFIG_FSFREEZE #define QGA_FSFREEZE_HOOK_DEFAULT CONFIG_QEMU_CONFDIR "/fsfreeze-hook" @@ -189,6 +191,8 @@ static void usage(const char *cmd) " -m, --method transport method: one of unix-listen, virtio-serial, or\n" " isa-serial (virtio-serial is the default)\n" " -p, --path device/socket path (the default for virtio-serial is:\n" +" %s,\n" +" the default for isa-serial is:\n" " %s)\n" " -l, --logfile set logfile path, logs to stderr by default\n" " -f, --pidfile specify pidfile (default is %s)\n" @@ -215,7 +219,8 @@ static void usage(const char *cmd) " -h, --help display this help and exit\n" "\n" "Report bugs to <mdroth@linux.vnet.ibm.com>\n" - , cmd, QEMU_VERSION, QGA_VIRTIO_PATH_DEFAULT, dfl_pathnames.pidfile, + , cmd, QEMU_VERSION, QGA_VIRTIO_PATH_DEFAULT, QGA_SERIAL_PATH_DEFAULT, + dfl_pathnames.pidfile, #ifdef CONFIG_FSFREEZE QGA_FSFREEZE_HOOK_DEFAULT, #endif @@ -659,12 +664,16 @@ static gboolean channel_init(GAState *s, const gchar *method, const gchar *path) } if (path == NULL) { - if (strcmp(method, "virtio-serial") != 0) { + if (strcmp(method, "virtio-serial") == 0 ) { + /* try the default path for the virtio-serial port */ + path = QGA_VIRTIO_PATH_DEFAULT; + } else if (strcmp(method, "isa-serial") == 0){ + /* try the default path for the serial port - COM1 */ + path = QGA_SERIAL_PATH_DEFAULT; + } else { g_critical("must specify a path for this channel"); return false; } - /* try the default path for the virtio-serial port */ - path = QGA_VIRTIO_PATH_DEFAULT; } if (strcmp(method, "virtio-serial") == 0) { diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json index 245f968..80edca1 100644 --- a/qga/qapi-schema.json +++ b/qga/qapi-schema.json @@ -120,17 +120,18 @@ # This command tries to set guest time to the given value, # then sets the Hardware Clock to the current System Time. # This will make it easier for a guest to resynchronize -# without waiting for NTP. +# without waiting for NTP. If no @time is specified, then +# the time to set is read from RTC. # -# @time: time of nanoseconds, relative to the Epoch of -# 1970-01-01 in UTC. +# @time: #optional time of nanoseconds, relative to the Epoch +# of 1970-01-01 in UTC. # # Returns: Nothing on success. # # Since: 1.5 ## { 'command': 'guest-set-time', - 'data': { 'time': 'int' } } + 'data': { '*time': 'int' } } ## # @GuestAgentCommandInfo: diff --git a/qga/vss-win32/provider.cpp b/qga/vss-win32/provider.cpp index bf42b5e..d5129f8 100644 --- a/qga/vss-win32/provider.cpp +++ b/qga/vss-win32/provider.cpp @@ -278,7 +278,9 @@ STDMETHODIMP CQGAVssProvider::DeleteSnapshots( VSS_ID SourceObjectId, VSS_OBJECT_TYPE eSourceObjectType, BOOL bForceDelete, LONG *plDeletedSnapshots, VSS_ID *pNondeletedSnapshotID) { - return E_NOTIMPL; + *plDeletedSnapshots = 0; + *pNondeletedSnapshotID = SourceObjectId; + return S_OK; } STDMETHODIMP CQGAVssProvider::BeginPrepareSnapshot( @@ -291,8 +293,17 @@ STDMETHODIMP CQGAVssProvider::BeginPrepareSnapshot( STDMETHODIMP CQGAVssProvider::IsVolumeSupported( VSS_PWSZ pwszVolumeName, BOOL *pbSupportedByThisProvider) { - *pbSupportedByThisProvider = TRUE; + HANDLE hEventFrozen; + /* Check if a requester is qemu-ga by whether an event is created */ + hEventFrozen = OpenEvent(EVENT_ALL_ACCESS, FALSE, EVENT_NAME_FROZEN); + if (!hEventFrozen) { + *pbSupportedByThisProvider = FALSE; + return S_OK; + } + CloseHandle(hEventFrozen); + + *pbSupportedByThisProvider = TRUE; return S_OK; } @@ -342,18 +353,18 @@ STDMETHODIMP CQGAVssProvider::CommitSnapshots(VSS_ID SnapshotSetId) HANDLE hEventFrozen, hEventThaw, hEventTimeout; hEventFrozen = OpenEvent(EVENT_ALL_ACCESS, FALSE, EVENT_NAME_FROZEN); - if (hEventFrozen == INVALID_HANDLE_VALUE) { + if (!hEventFrozen) { return E_FAIL; } hEventThaw = OpenEvent(EVENT_ALL_ACCESS, FALSE, EVENT_NAME_THAW); - if (hEventThaw == INVALID_HANDLE_VALUE) { + if (!hEventThaw) { CloseHandle(hEventFrozen); return E_FAIL; } hEventTimeout = OpenEvent(EVENT_ALL_ACCESS, FALSE, EVENT_NAME_TIMEOUT); - if (hEventTimeout == INVALID_HANDLE_VALUE) { + if (!hEventTimeout) { CloseHandle(hEventFrozen); CloseHandle(hEventThaw); return E_FAIL; diff --git a/qga/vss-win32/requester.cpp b/qga/vss-win32/requester.cpp index 1e8dd3d..922e74d 100644 --- a/qga/vss-win32/requester.cpp +++ b/qga/vss-win32/requester.cpp @@ -50,10 +50,6 @@ static struct QGAVSSContext { STDAPI requester_init(void) { - vss_ctx.hEventFrozen = INVALID_HANDLE_VALUE; - vss_ctx.hEventThaw = INVALID_HANDLE_VALUE; - vss_ctx.hEventTimeout = INVALID_HANDLE_VALUE; - COMInitializer initializer; /* to call CoInitializeSecurity */ HRESULT hr = CoInitializeSecurity( NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_PKT_PRIVACY, @@ -94,17 +90,17 @@ STDAPI requester_init(void) static void requester_cleanup(void) { - if (vss_ctx.hEventFrozen != INVALID_HANDLE_VALUE) { + if (vss_ctx.hEventFrozen) { CloseHandle(vss_ctx.hEventFrozen); - vss_ctx.hEventFrozen = INVALID_HANDLE_VALUE; + vss_ctx.hEventFrozen = NULL; } - if (vss_ctx.hEventThaw != INVALID_HANDLE_VALUE) { + if (vss_ctx.hEventThaw) { CloseHandle(vss_ctx.hEventThaw); - vss_ctx.hEventThaw = INVALID_HANDLE_VALUE; + vss_ctx.hEventThaw = NULL; } - if (vss_ctx.hEventTimeout != INVALID_HANDLE_VALUE) { + if (vss_ctx.hEventTimeout) { CloseHandle(vss_ctx.hEventTimeout); - vss_ctx.hEventTimeout = INVALID_HANDLE_VALUE; + vss_ctx.hEventTimeout = NULL; } if (vss_ctx.pAsyncSnapshot) { vss_ctx.pAsyncSnapshot->Release(); @@ -256,6 +252,32 @@ void requester_freeze(int *num_vols, ErrorSet *errset) CoInitialize(NULL); + /* Allow unrestricted access to events */ + InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION); + SetSecurityDescriptorDacl(&sd, TRUE, NULL, FALSE); + sa.nLength = sizeof(sa); + sa.lpSecurityDescriptor = &sd; + sa.bInheritHandle = FALSE; + + vss_ctx.hEventFrozen = CreateEvent(&sa, TRUE, FALSE, EVENT_NAME_FROZEN); + if (!vss_ctx.hEventFrozen) { + err_set(errset, GetLastError(), "failed to create event %s", + EVENT_NAME_FROZEN); + goto out; + } + vss_ctx.hEventThaw = CreateEvent(&sa, TRUE, FALSE, EVENT_NAME_THAW); + if (!vss_ctx.hEventThaw) { + err_set(errset, GetLastError(), "failed to create event %s", + EVENT_NAME_THAW); + goto out; + } + vss_ctx.hEventTimeout = CreateEvent(&sa, TRUE, FALSE, EVENT_NAME_TIMEOUT); + if (!vss_ctx.hEventTimeout) { + err_set(errset, GetLastError(), "failed to create event %s", + EVENT_NAME_TIMEOUT); + goto out; + } + assert(pCreateVssBackupComponents != NULL); hr = pCreateVssBackupComponents(&vss_ctx.pVssbc); if (FAILED(hr)) { @@ -366,32 +388,6 @@ void requester_freeze(int *num_vols, ErrorSet *errset) goto out; } - /* Allow unrestricted access to events */ - InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION); - SetSecurityDescriptorDacl(&sd, TRUE, NULL, FALSE); - sa.nLength = sizeof(sa); - sa.lpSecurityDescriptor = &sd; - sa.bInheritHandle = FALSE; - - vss_ctx.hEventFrozen = CreateEvent(&sa, TRUE, FALSE, EVENT_NAME_FROZEN); - if (vss_ctx.hEventFrozen == INVALID_HANDLE_VALUE) { - err_set(errset, GetLastError(), "failed to create event %s", - EVENT_NAME_FROZEN); - goto out; - } - vss_ctx.hEventThaw = CreateEvent(&sa, TRUE, FALSE, EVENT_NAME_THAW); - if (vss_ctx.hEventThaw == INVALID_HANDLE_VALUE) { - err_set(errset, GetLastError(), "failed to create event %s", - EVENT_NAME_THAW); - goto out; - } - vss_ctx.hEventTimeout = CreateEvent(&sa, TRUE, FALSE, EVENT_NAME_TIMEOUT); - if (vss_ctx.hEventTimeout == INVALID_HANDLE_VALUE) { - err_set(errset, GetLastError(), "failed to create event %s", - EVENT_NAME_TIMEOUT); - goto out; - } - /* * Start VSS quiescing operations. * CQGAVssProvider::CommitSnapshots will kick vss_ctx.hEventFrozen @@ -443,7 +439,7 @@ void requester_thaw(int *num_vols, ErrorSet *errset) { COMPointer<IVssAsync> pAsync; - if (vss_ctx.hEventThaw == INVALID_HANDLE_VALUE) { + if (!vss_ctx.hEventThaw) { /* * In this case, DoSnapshotSet is aborted or not started, * and no volumes must be frozen. We return without an error. diff --git a/qobject/qdict.c b/qobject/qdict.c index a3924f2..42ec4c0 100644 --- a/qobject/qdict.c +++ b/qobject/qdict.c @@ -597,18 +597,33 @@ void qdict_extract_subqdict(QDict *src, QDict **dst, const char *start) } } +static bool qdict_has_prefixed_entries(const QDict *src, const char *start) +{ + const QDictEntry *entry; + + for (entry = qdict_first(src); entry; entry = qdict_next(src, entry)) { + if (strstart(entry->key, start, NULL)) { + return true; + } + } + + return false; +} + /** * qdict_array_split(): This function moves array-like elements of a QDict into - * a new QList of QDicts. Every entry in the original QDict with a key prefixed - * "%u.", where %u designates an unsigned integer starting at 0 and + * a new QList. Every entry in the original QDict with a key "%u" or one + * prefixed "%u.", where %u designates an unsigned integer starting at 0 and * incrementally counting up, will be moved to a new QDict at index %u in the - * output QList with the key prefix removed. The function terminates when there - * is no entry in the QDict with a prefix directly (incrementally) following the - * last one. - * Example: {"0.a": 42, "0.b": 23, "1.x": 0, "3.y": 1, "o.o": 7} - * (or {"1.x": 0, "3.y": 1, "0.a": 42, "o.o": 7, "0.b": 23}) - * => [{"a": 42, "b": 23}, {"x": 0}] - * and {"3.y": 1, "o.o": 7} (remainder of the old QDict) + * output QList with the key prefix removed, if that prefix is "%u.". If the + * whole key is just "%u", the whole QObject will be moved unchanged without + * creating a new QDict. The function terminates when there is no entry in the + * QDict with a prefix directly (incrementally) following the last one; it also + * returns if there are both entries with "%u" and "%u." for the same index %u. + * Example: {"0.a": 42, "0.b": 23, "1.x": 0, "4.y": 1, "o.o": 7, "2": 66} + * (or {"1.x": 0, "4.y": 1, "0.a": 42, "o.o": 7, "0.b": 23, "2": 66}) + * => [{"a": 42, "b": 23}, {"x": 0}, 66] + * and {"4.y": 1, "o.o": 7} (remainder of the old QDict) */ void qdict_array_split(QDict *src, QList **dst) { @@ -617,19 +632,36 @@ void qdict_array_split(QDict *src, QList **dst) *dst = qlist_new(); for (i = 0; i < UINT_MAX; i++) { + QObject *subqobj; + bool is_subqdict; QDict *subqdict; - char prefix[32]; + char indexstr[32], prefix[32]; size_t snprintf_ret; + snprintf_ret = snprintf(indexstr, 32, "%u", i); + assert(snprintf_ret < 32); + + subqobj = qdict_get(src, indexstr); + snprintf_ret = snprintf(prefix, 32, "%u.", i); assert(snprintf_ret < 32); - qdict_extract_subqdict(src, &subqdict, prefix); - if (!qdict_size(subqdict)) { - QDECREF(subqdict); + is_subqdict = qdict_has_prefixed_entries(src, prefix); + + // There may be either a single subordinate object (named "%u") or + // multiple objects (each with a key prefixed "%u."), but not both. + if (!subqobj == !is_subqdict) { break; } - qlist_append_obj(*dst, QOBJECT(subqdict)); + if (is_subqdict) { + qdict_extract_subqdict(src, &subqdict, prefix); + assert(qdict_size(subqdict) > 0); + } else { + qobject_incref(subqobj); + qdict_del(src, indexstr); + } + + qlist_append_obj(*dst, subqobj ?: QOBJECT(subqdict)); } } @@ -22,8 +22,15 @@ QEMU_DGFLAGS += -MMD -MP -MT $@ -MF $(*D)/$(*F).d # Same as -I$(SRC_PATH) -I., but for the nested source/object directories QEMU_INCLUDES += -I$(<D) -I$(@D) +maybe-add = $(filter-out $1, $2) $1 +extract-libs = $(strip $(sort $(foreach o,$1,$($o-libs)) \ + $(foreach o,$(call expand-objs,$1),$($o-libs)))) +expand-objs = $(strip $(sort $(filter %.o,$1)) \ + $(foreach o,$(filter %.mo,$1),$($o-objs)) \ + $(filter-out %.o %.mo,$1)) + %.o: %.c - $(call quiet-command,$(CC) $(QEMU_INCLUDES) $(QEMU_CFLAGS) $(QEMU_DGFLAGS) $(CFLAGS) -c -o $@ $<," CC $(TARGET_DIR)$@") + $(call quiet-command,$(CC) $(QEMU_INCLUDES) $(QEMU_CFLAGS) $(QEMU_DGFLAGS) $(CFLAGS) $($@-cflags) -c -o $@ $<," CC $(TARGET_DIR)$@") %.o: %.rc $(call quiet-command,$(WINDRES) -I. -o $@ $<," RC $(TARGET_DIR)$@") @@ -33,8 +40,8 @@ LINKPROG = $(or $(CXX),$(CC)) ifeq ($(LIBTOOL),) LINK = $(call quiet-command,$(LINKPROG) $(QEMU_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ \ - $(sort $(filter %.o, $1)) $(filter-out %.o, $1) $(version-obj-y) \ - $(LIBS)," LINK $(TARGET_DIR)$@") + $(call expand-objs,$1) $(version-obj-y) \ + $(call extract-libs,$1) $(LIBS)," LINK $(TARGET_DIR)$@") else LIBTOOL += $(if $(V),,--quiet) %.lo: %.c @@ -45,12 +52,12 @@ LIBTOOL += $(if $(V),,--quiet) $(call quiet-command,$(LIBTOOL) --mode=compile --tag=CC dtrace -o $@ -G -s $<, " lt GEN $(TARGET_DIR)$@") LINK = $(call quiet-command,\ - $(if $(filter %.lo %.la,$^),$(LIBTOOL) --mode=link --tag=CC \ + $(if $(filter %.lo %.la,$1),$(LIBTOOL) --mode=link --tag=CC \ )$(LINKPROG) $(QEMU_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ \ - $(sort $(filter %.o, $1)) $(filter-out %.o, $1) \ - $(if $(filter %.lo %.la,$^),$(version-lobj-y),$(version-obj-y)) \ - $(if $(filter %.lo %.la,$^),$(LIBTOOLFLAGS)) \ - $(LIBS),$(if $(filter %.lo %.la,$^),"lt LINK ", " LINK ")"$(TARGET_DIR)$@") + $(call expand-objs,$1) \ + $(if $(filter %.lo %.la,$1),$(version-lobj-y),$(version-obj-y)) \ + $(if $(filter %.lo %.la,$1),$(LIBTOOLFLAGS)) \ + $(call extract-libs,$1) $(LIBS),$(if $(filter %.lo %.la,$1),"lt LINK ", " LINK ")"$(TARGET_DIR)$@") endif %.asm: %.S @@ -71,6 +78,16 @@ endif %.o: %.dtrace $(call quiet-command,dtrace -o $@ -G -s $<, " GEN $(TARGET_DIR)$@") +DSO_CFLAGS := -fPIC -DBUILD_DSO +%$(DSOSUF): LDFLAGS += $(LDFLAGS_SHARED) +%$(DSOSUF): %.mo libqemustub.a + $(call LINK,$^) + @# Copy to build root so modules can be loaded when program started without install + $(if $(findstring /,$@),$(call quiet-command,cp $@ $(subst /,-,$@), " CP $(subst /,-,$@)")) + +.PHONY: modules +modules: + %$(EXESUF): %.o $(call LINK,$^) @@ -146,9 +163,6 @@ clean: clean-timestamp # magic to descend into other directories -obj := . -old-nested-dirs := - define push-var $(eval save-$2-$1 = $(value $1)) $(eval $1 :=) @@ -160,11 +174,27 @@ $(eval $1 = $(value save-$2-$1) $$(subdir-$2-$1)) $(eval save-$2-$1 :=) endef +define fix-obj-vars +$(foreach v,$($1), \ + $(if $($v-cflags), \ + $(eval $2$v-cflags := $($v-cflags)) \ + $(eval $v-cflags := )) \ + $(if $($v-libs), \ + $(eval $2$v-libs := $($v-libs)) \ + $(eval $v-libs := )) \ + $(if $($v-objs), \ + $(eval $2$v-objs := $(addprefix $2,$($v-objs))) \ + $(eval $v-objs := ))) +endef + define unnest-dir $(foreach var,$(nested-vars),$(call push-var,$(var),$1/)) -$(eval obj := $(obj)/$1) +$(eval obj-parent-$1 := $(obj)) +$(eval obj := $(if $(obj),$(obj)/$1,$1)) $(eval include $(SRC_PATH)/$1/Makefile.objs) -$(eval obj := $(patsubst %/$1,%,$(obj))) +$(foreach v,$(nested-vars),$(call fix-obj-vars,$v,$(if $(obj),$(obj)/))) +$(eval obj := $(obj-parent-$1)) +$(eval obj-parent-$1 := ) $(foreach var,$(nested-vars),$(call pop-var,$(var),$1/)) endef @@ -178,10 +208,34 @@ $(if $(nested-dirs), $(call unnest-vars-1)) endef +define process-modules +$(foreach o,$(filter %.o,$($1)), + $(eval $(patsubst %.o,%.mo,$o): $o) \ + $(eval $(patsubst %.o,%.mo,$o)-objs := $o)) +$(foreach o,$(filter-out $(modules-m), $(patsubst %.o,%.mo,$($1))), \ + $(eval $o-objs += module-common.o) + $(eval $o: $($o-objs)) + $(eval modules-objs-m += $($o-objs)) + $(eval modules-m += $o) + $(eval $o:; $$(call quiet-command,touch $$@," GEN $$(TARGET_DIR)$$@")) + $(if $(CONFIG_MODULES),$(eval modules: $(patsubst %.mo,%$(DSOSUF),$o)))) \ +$(eval modules-objs-m := $(sort $(modules-objs-m))) +$(foreach o,$(modules-objs-m), \ + $(if $(CONFIG_MODULES),$(eval $o-cflags := $(call maybe-add, $(DSO_CFLAGS), $($o-cflags))))) +$(eval $(patsubst %-m,%-$(call lnot,$(CONFIG_MODULES)),$1) += $($1)) +endef + define unnest-vars +$(eval obj := $1) +$(eval nested-vars := $2) +$(eval old-nested-dirs := ) $(call unnest-vars-1) +$(if $1,$(foreach v,$(nested-vars),$(eval \ + $v := $(addprefix $1/,$($v))))) $(foreach var,$(nested-vars),$(eval $(var) := $(filter-out %/, $($(var))))) $(shell mkdir -p $(sort $(foreach var,$(nested-vars),$(dir $($(var)))))) $(foreach var,$(nested-vars), $(eval \ -include $(addsuffix *.d, $(sort $(dir $($(var))))))) +$(foreach v,$(filter %-m,$(nested-vars)), \ + $(call process-modules,$v)) endef diff --git a/scripts/create_config b/scripts/create_config index 06f5316..546f889 100755 --- a/scripts/create_config +++ b/scripts/create_config @@ -108,6 +108,9 @@ case $line in value=${line#*=} echo "#define $name $value" ;; + DSOSUF=*) + echo "#define HOST_DSOSUF \"${line#*=}\"" + ;; esac done # read diff --git a/target-xtensa/core-dc232b.c b/target-xtensa/core-dc232b.c index 0bfcf24..c51e11e 100644 --- a/target-xtensa/core-dc232b.c +++ b/target-xtensa/core-dc232b.c @@ -35,7 +35,6 @@ static const XtensaConfig dc232b = { .name = "dc232b", - .options = XTENSA_OPTIONS, .gdb_regmap = { .num_regs = 120, .num_core_regs = 52, @@ -43,13 +42,8 @@ static const XtensaConfig dc232b = { #include "core-dc232b/gdb-config.c" } }, - .nareg = XCHAL_NUM_AREGS, - .ndepc = 1, - EXCEPTIONS_SECTION, - INTERRUPTS_SECTION, - TLB_SECTION, - DEBUG_SECTION, .clock_freq_khz = 10000, + DEFAULT_SECTIONS }; REGISTER_CORE(dc232b) diff --git a/target-xtensa/core-dc233c.c b/target-xtensa/core-dc233c.c index 738d543..42dd64f 100644 --- a/target-xtensa/core-dc233c.c +++ b/target-xtensa/core-dc233c.c @@ -36,7 +36,6 @@ static const XtensaConfig dc233c = { .name = "dc233c", - .options = XTENSA_OPTIONS, .gdb_regmap = { .num_regs = 121, .num_core_regs = 52, @@ -44,13 +43,8 @@ static const XtensaConfig dc233c = { #include "core-dc233c/gdb-config.c" } }, - .nareg = XCHAL_NUM_AREGS, - .ndepc = 1, - EXCEPTIONS_SECTION, - INTERRUPTS_SECTION, - TLB_SECTION, - DEBUG_SECTION, .clock_freq_khz = 10000, + DEFAULT_SECTIONS }; REGISTER_CORE(dc233c) diff --git a/target-xtensa/core-fsf.c b/target-xtensa/core-fsf.c index d4660ed..6859bee 100644 --- a/target-xtensa/core-fsf.c +++ b/target-xtensa/core-fsf.c @@ -35,15 +35,9 @@ static const XtensaConfig fsf = { .name = "fsf", - .options = XTENSA_OPTIONS, /* GDB for this core is not supported currently */ - .nareg = XCHAL_NUM_AREGS, - .ndepc = 1, - EXCEPTIONS_SECTION, - INTERRUPTS_SECTION, - TLB_SECTION, - DEBUG_SECTION, .clock_freq_khz = 10000, + DEFAULT_SECTIONS }; REGISTER_CORE(fsf) diff --git a/target-xtensa/cpu.c b/target-xtensa/cpu.c index c19d17a..749e205 100644 --- a/target-xtensa/cpu.c +++ b/target-xtensa/cpu.c @@ -59,6 +59,8 @@ static void xtensa_cpu_reset(CPUState *s) env->sregs[CACHEATTR] = 0x22222222; env->sregs[ATOMCTL] = xtensa_option_enabled(env->config, XTENSA_OPTION_ATOMCTL) ? 0x28 : 0x15; + env->sregs[CONFIGID0] = env->config->configid[0]; + env->sregs[CONFIGID1] = env->config->configid[1]; env->pending_irq_level = 0; reset_mmu(env); diff --git a/target-xtensa/cpu.h b/target-xtensa/cpu.h index 95103e9..1cf5ea3 100644 --- a/target-xtensa/cpu.h +++ b/target-xtensa/cpu.h @@ -135,9 +135,11 @@ enum { IBREAKA = 128, DBREAKA = 144, DBREAKC = 160, + CONFIGID0 = 176, EPC1 = 177, DEPC = 192, EPS2 = 194, + CONFIGID1 = 208, EXCSAVE1 = 209, CPENABLE = 224, INTSET = 226, @@ -321,6 +323,8 @@ typedef struct XtensaConfig { unsigned nibreak; unsigned ndbreak; + uint32_t configid[2]; + uint32_t clock_freq_khz; xtensa_tlb itlb; diff --git a/target-xtensa/helper.h b/target-xtensa/helper.h index 38d7157..322b04c 100644 --- a/target-xtensa/helper.h +++ b/target-xtensa/helper.h @@ -25,6 +25,7 @@ DEF_HELPER_2(advance_ccount, void, env, i32) DEF_HELPER_1(check_interrupts, void, env) DEF_HELPER_3(check_atomctl, void, env, i32, i32) +DEF_HELPER_2(itlb_hit_test, void, env, i32) DEF_HELPER_2(wsr_rasid, void, env, i32) DEF_HELPER_FLAGS_3(rtlb0, TCG_CALL_NO_RWG_SE, i32, env, i32, i32) DEF_HELPER_FLAGS_3(rtlb1, TCG_CALL_NO_RWG_SE, i32, env, i32, i32) diff --git a/target-xtensa/op_helper.c b/target-xtensa/op_helper.c index 89a72b5..509ba49 100644 --- a/target-xtensa/op_helper.c +++ b/target-xtensa/op_helper.c @@ -415,6 +415,11 @@ void HELPER(check_interrupts)(CPUXtensaState *env) check_interrupts(env); } +void HELPER(itlb_hit_test)(CPUXtensaState *env, uint32_t vaddr) +{ + get_page_addr_code(env, vaddr); +} + /*! * Check vaddr accessibility/cache attributes and raise an exception if * specified by the ATOMCTL SR. diff --git a/target-xtensa/overlay_tool.h b/target-xtensa/overlay_tool.h index dd4f51a..4c0de7f 100644 --- a/target-xtensa/overlay_tool.h +++ b/target-xtensa/overlay_tool.h @@ -319,6 +319,23 @@ .nibreak = XCHAL_NUM_IBREAK, \ .ndbreak = XCHAL_NUM_DBREAK +#define CONFIG_SECTION \ + .configid = { \ + XCHAL_HW_CONFIGID0, \ + XCHAL_HW_CONFIGID1, \ + } + +#define DEFAULT_SECTIONS \ + .options = XTENSA_OPTIONS, \ + .nareg = XCHAL_NUM_AREGS, \ + .ndepc = (XCHAL_XEA_VERSION >= 2), \ + EXCEPTIONS_SECTION, \ + INTERRUPTS_SECTION, \ + TLB_SECTION, \ + DEBUG_SECTION, \ + CONFIG_SECTION + + #if XCHAL_NUM_INTLEVELS + XCHAL_HAVE_NMI + 1 <= 2 #define XCHAL_INTLEVEL2_VECTOR_VADDR 0 #endif diff --git a/target-xtensa/translate.c b/target-xtensa/translate.c index 2d2df33..9f5895e 100644 --- a/target-xtensa/translate.c +++ b/target-xtensa/translate.c @@ -98,12 +98,15 @@ typedef struct XtensaReg { #define XTENSA_REG(regname, opt) XTENSA_REG_ACCESS(regname, opt, SR_RWX) -#define XTENSA_REG_BITS(regname, opt) { \ +#define XTENSA_REG_BITS_ACCESS(regname, opt, acc) { \ .name = (regname), \ .opt_bits = (opt), \ - .access = SR_RWX, \ + .access = (acc), \ } +#define XTENSA_REG_BITS(regname, opt) \ + XTENSA_REG_BITS_ACCESS(regname, opt, SR_RWX) + static const XtensaReg sregnames[256] = { [LBEG] = XTENSA_REG("LBEG", XTENSA_OPTION_LOOP), [LEND] = XTENSA_REG("LEND", XTENSA_OPTION_LOOP), @@ -134,6 +137,7 @@ static const XtensaReg sregnames[256] = { [DBREAKA + 1] = XTENSA_REG("DBREAKA1", XTENSA_OPTION_DEBUG), [DBREAKC] = XTENSA_REG("DBREAKC0", XTENSA_OPTION_DEBUG), [DBREAKC + 1] = XTENSA_REG("DBREAKC1", XTENSA_OPTION_DEBUG), + [CONFIGID0] = XTENSA_REG_BITS_ACCESS("CONFIGID0", XTENSA_OPTION_ALL, SR_R), [EPC1] = XTENSA_REG("EPC1", XTENSA_OPTION_EXCEPTION), [EPC1 + 1] = XTENSA_REG("EPC2", XTENSA_OPTION_HIGH_PRIORITY_INTERRUPT), [EPC1 + 2] = XTENSA_REG("EPC3", XTENSA_OPTION_HIGH_PRIORITY_INTERRUPT), @@ -148,6 +152,7 @@ static const XtensaReg sregnames[256] = { [EPS2 + 3] = XTENSA_REG("EPS5", XTENSA_OPTION_HIGH_PRIORITY_INTERRUPT), [EPS2 + 4] = XTENSA_REG("EPS6", XTENSA_OPTION_HIGH_PRIORITY_INTERRUPT), [EPS2 + 5] = XTENSA_REG("EPS7", XTENSA_OPTION_HIGH_PRIORITY_INTERRUPT), + [CONFIGID1] = XTENSA_REG_BITS_ACCESS("CONFIGID1", XTENSA_OPTION_ALL, SR_R), [EXCSAVE1] = XTENSA_REG("EXCSAVE1", XTENSA_OPTION_EXCEPTION), [EXCSAVE1 + 1] = XTENSA_REG("EXCSAVE2", XTENSA_OPTION_HIGH_PRIORITY_INTERRUPT), @@ -922,6 +927,15 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) #define RRRN_S RRR_S #define RRRN_T RRR_T +#define RRI4_R RRR_R +#define RRI4_S RRR_S +#define RRI4_T RRR_T +#ifdef TARGET_WORDS_BIGENDIAN +#define RRI4_IMM4 ((b2) & 0xf) +#else +#define RRI4_IMM4 (((b2) & 0xf0) >> 4) +#endif + #define RRI8_R RRR_R #define RRI8_S RRR_S #define RRI8_T RRR_T @@ -2226,6 +2240,20 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) gen_load_store(st32, 2); break; +#define gen_dcache_hit_test(w, shift) do { \ + TCGv_i32 addr = tcg_temp_new_i32(); \ + TCGv_i32 res = tcg_temp_new_i32(); \ + gen_window_check1(dc, RRI##w##_S); \ + tcg_gen_addi_i32(addr, cpu_R[RRI##w##_S], \ + RRI##w##_IMM##w << shift); \ + tcg_gen_qemu_ld8u(res, addr, dc->cring); \ + tcg_temp_free(addr); \ + tcg_temp_free(res); \ + } while (0) + +#define gen_dcache_hit_test4() gen_dcache_hit_test(4, 4) +#define gen_dcache_hit_test8() gen_dcache_hit_test(8, 2) + case 7: /*CACHEc*/ if (RRI8_T < 8) { HAS_OPTION(XTENSA_OPTION_DCACHE); @@ -2233,49 +2261,69 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) switch (RRI8_T) { case 0: /*DPFRc*/ + gen_window_check1(dc, RRI8_S); break; case 1: /*DPFWc*/ + gen_window_check1(dc, RRI8_S); break; case 2: /*DPFROc*/ + gen_window_check1(dc, RRI8_S); break; case 3: /*DPFWOc*/ + gen_window_check1(dc, RRI8_S); break; case 4: /*DHWBc*/ + gen_dcache_hit_test8(); break; case 5: /*DHWBIc*/ + gen_dcache_hit_test8(); break; case 6: /*DHIc*/ + gen_check_privilege(dc); + gen_dcache_hit_test8(); break; case 7: /*DIIc*/ + gen_check_privilege(dc); + gen_window_check1(dc, RRI8_S); break; case 8: /*DCEc*/ switch (OP1) { case 0: /*DPFLl*/ HAS_OPTION(XTENSA_OPTION_DCACHE_INDEX_LOCK); + gen_check_privilege(dc); + gen_dcache_hit_test4(); break; case 2: /*DHUl*/ HAS_OPTION(XTENSA_OPTION_DCACHE_INDEX_LOCK); + gen_check_privilege(dc); + gen_dcache_hit_test4(); break; case 3: /*DIUl*/ HAS_OPTION(XTENSA_OPTION_DCACHE_INDEX_LOCK); + gen_check_privilege(dc); + gen_window_check1(dc, RRI4_S); break; case 4: /*DIWBc*/ HAS_OPTION(XTENSA_OPTION_DCACHE); + gen_check_privilege(dc); + gen_window_check1(dc, RRI4_S); break; case 5: /*DIWBIc*/ HAS_OPTION(XTENSA_OPTION_DCACHE); + gen_check_privilege(dc); + gen_window_check1(dc, RRI4_S); break; default: /*reserved*/ @@ -2285,22 +2333,46 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) } break; +#undef gen_dcache_hit_test +#undef gen_dcache_hit_test4 +#undef gen_dcache_hit_test8 + +#define gen_icache_hit_test(w, shift) do { \ + TCGv_i32 addr = tcg_temp_new_i32(); \ + gen_window_check1(dc, RRI##w##_S); \ + tcg_gen_movi_i32(cpu_pc, dc->pc); \ + tcg_gen_addi_i32(addr, cpu_R[RRI##w##_S], \ + RRI##w##_IMM##w << shift); \ + gen_helper_itlb_hit_test(cpu_env, addr); \ + tcg_temp_free(addr); \ + } while (0) + +#define gen_icache_hit_test4() gen_icache_hit_test(4, 4) +#define gen_icache_hit_test8() gen_icache_hit_test(8, 2) + case 12: /*IPFc*/ HAS_OPTION(XTENSA_OPTION_ICACHE); + gen_window_check1(dc, RRI8_S); break; case 13: /*ICEc*/ switch (OP1) { case 0: /*IPFLl*/ HAS_OPTION(XTENSA_OPTION_ICACHE_INDEX_LOCK); + gen_check_privilege(dc); + gen_icache_hit_test4(); break; case 2: /*IHUl*/ HAS_OPTION(XTENSA_OPTION_ICACHE_INDEX_LOCK); + gen_check_privilege(dc); + gen_icache_hit_test4(); break; case 3: /*IIUl*/ HAS_OPTION(XTENSA_OPTION_ICACHE_INDEX_LOCK); + gen_check_privilege(dc); + gen_window_check1(dc, RRI4_S); break; default: /*reserved*/ @@ -2311,10 +2383,13 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) case 14: /*IHIc*/ HAS_OPTION(XTENSA_OPTION_ICACHE); + gen_icache_hit_test8(); break; case 15: /*IIIc*/ HAS_OPTION(XTENSA_OPTION_ICACHE); + gen_check_privilege(dc); + gen_window_check1(dc, RRI8_S); break; default: /*reserved*/ @@ -2323,6 +2398,10 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) } break; +#undef gen_icache_hit_test +#undef gen_icache_hit_test4 +#undef gen_icache_hit_test8 + case 9: /*L16SI*/ gen_load_store(ld16s, 1); break; diff --git a/tests/check-qdict.c b/tests/check-qdict.c index 7a7461b..2ad0f78 100644 --- a/tests/check-qdict.c +++ b/tests/check-qdict.c @@ -306,6 +306,7 @@ static void qdict_array_split_test(void) { QDict *test_dict = qdict_new(); QDict *dict1, *dict2; + QInt *int1; QList *test_list; /* @@ -313,10 +314,11 @@ static void qdict_array_split_test(void) * * { * "1.x": 0, - * "3.y": 1, + * "4.y": 1, * "0.a": 42, * "o.o": 7, - * "0.b": 23 + * "0.b": 23, + * "2": 66 * } * * to @@ -328,13 +330,14 @@ static void qdict_array_split_test(void) * }, * { * "x": 0 - * } + * }, + * 66 * ] * * and * * { - * "3.y": 1, + * "4.y": 1, * "o.o": 7 * } * @@ -344,18 +347,21 @@ static void qdict_array_split_test(void) */ qdict_put(test_dict, "1.x", qint_from_int(0)); - qdict_put(test_dict, "3.y", qint_from_int(1)); + qdict_put(test_dict, "4.y", qint_from_int(1)); qdict_put(test_dict, "0.a", qint_from_int(42)); qdict_put(test_dict, "o.o", qint_from_int(7)); qdict_put(test_dict, "0.b", qint_from_int(23)); + qdict_put(test_dict, "2", qint_from_int(66)); qdict_array_split(test_dict, &test_list); dict1 = qobject_to_qdict(qlist_pop(test_list)); dict2 = qobject_to_qdict(qlist_pop(test_list)); + int1 = qobject_to_qint(qlist_pop(test_list)); g_assert(dict1); g_assert(dict2); + g_assert(int1); g_assert(qlist_empty(test_list)); QDECREF(test_list); @@ -373,12 +379,69 @@ static void qdict_array_split_test(void) QDECREF(dict2); - g_assert(qdict_get_int(test_dict, "3.y") == 1); + g_assert(qint_get_int(int1) == 66); + + QDECREF(int1); + + g_assert(qdict_get_int(test_dict, "4.y") == 1); g_assert(qdict_get_int(test_dict, "o.o") == 7); g_assert(qdict_size(test_dict) == 2); QDECREF(test_dict); + + + /* + * Test the split of + * + * { + * "0": 42, + * "1": 23, + * "1.x": 84 + * } + * + * to + * + * [ + * 42 + * ] + * + * and + * + * { + * "1": 23, + * "1.x": 84 + * } + * + * That is, test whether splitting stops if there is both an entry with key + * of "%u" and other entries with keys prefixed "%u." for the same index. + */ + + test_dict = qdict_new(); + + qdict_put(test_dict, "0", qint_from_int(42)); + qdict_put(test_dict, "1", qint_from_int(23)); + qdict_put(test_dict, "1.x", qint_from_int(84)); + + qdict_array_split(test_dict, &test_list); + + int1 = qobject_to_qint(qlist_pop(test_list)); + + g_assert(int1); + g_assert(qlist_empty(test_list)); + + QDECREF(test_list); + + g_assert(qint_get_int(int1) == 42); + + QDECREF(int1); + + g_assert(qdict_get_int(test_dict, "1") == 23); + g_assert(qdict_get_int(test_dict, "1.x") == 84); + + g_assert(qdict_size(test_dict) == 2); + + QDECREF(test_dict); } /* diff --git a/tests/qemu-iotests/046 b/tests/qemu-iotests/046 index 2d44bbb..e0be46c 100755 --- a/tests/qemu-iotests/046 +++ b/tests/qemu-iotests/046 @@ -193,6 +193,16 @@ echo "== Verify image content ==" function verify_io() { + if ($QEMU_IMG info -f "$IMGFMT" "$TEST_IMG" | grep "compat: 0.10" > /dev/null); then + # For v2 images, discarded clusters are read from the backing file + # Keep the variable empty so that the backing file value can be used as + # the default below + discarded= + else + # Discarded clusters are zeroed for v3 or later + discarded=0 + fi + echo read -P 0 0 0x10000 echo read -P 1 0x10000 0x2000 @@ -221,16 +231,16 @@ function verify_io() echo read -P 70 0x78000 0x6000 echo read -P 7 0x7e000 0x2000 - echo read -P 8 0x80000 0x6000 + echo read -P ${discarded:-8} 0x80000 0x6000 echo read -P 80 0x86000 0x2000 - echo read -P 8 0x88000 0x2000 + echo read -P ${discarded:-8} 0x88000 0x2000 echo read -P 81 0x8a000 0xe000 echo read -P 90 0x98000 0x6000 echo read -P 9 0x9e000 0x2000 - echo read -P 10 0xa0000 0x6000 + echo read -P ${discarded:-10} 0xa0000 0x6000 echo read -P 100 0xa6000 0x2000 - echo read -P 10 0xa8000 0x2000 + echo read -P ${discarded:-10} 0xa8000 0x2000 echo read -P 101 0xaa000 0xe000 echo read -P 110 0xb8000 0x8000 diff --git a/tests/qemu-iotests/051.out b/tests/qemu-iotests/051.out index 30e2dbd..7de1870 100644 --- a/tests/qemu-iotests/051.out +++ b/tests/qemu-iotests/051.out @@ -231,7 +231,7 @@ Testing: -drive driver=file QEMU_PROG: -drive driver=file: could not open disk image ide0-hd0: The 'file' block driver requires a file name Testing: -drive driver=nbd -QEMU_PROG: -drive driver=nbd: could not open disk image ide0-hd0: Could not open image: Invalid argument +QEMU_PROG: -drive driver=nbd: could not open disk image ide0-hd0: one of path and host must be specified. Testing: -drive driver=raw QEMU_PROG: -drive driver=raw: could not open disk image ide0-hd0: Can't use 'raw' as a block driver for the protocol level @@ -240,7 +240,7 @@ Testing: -drive file.driver=file QEMU_PROG: -drive file.driver=file: could not open disk image ide0-hd0: The 'file' block driver requires a file name Testing: -drive file.driver=nbd -QEMU_PROG: -drive file.driver=nbd: could not open disk image ide0-hd0: Could not open image: Invalid argument +QEMU_PROG: -drive file.driver=nbd: could not open disk image ide0-hd0: one of path and host must be specified. Testing: -drive file.driver=raw QEMU_PROG: -drive file.driver=raw: could not open disk image ide0-hd0: Can't use 'raw' as a block driver for the protocol level diff --git a/tests/qemu-iotests/059.out b/tests/qemu-iotests/059.out index 4ffeb54..3371c86 100644 --- a/tests/qemu-iotests/059.out +++ b/tests/qemu-iotests/059.out @@ -7,8 +7,7 @@ no file open, try 'help open' === Testing too big L2 table size === Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 -L2 table size too big -qemu-io: can't open device TEST_DIR/t.vmdk: Could not open 'TEST_DIR/t.vmdk': Wrong medium type +qemu-io: can't open device TEST_DIR/t.vmdk: L2 table size too big no file open, try 'help open' === Testing too big L1 table size === @@ -2045,8 +2044,7 @@ RW 12582912 VMFS "dummy.IMGFMT" 1 === Testing truncated sparse === Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=107374182400 -qemu-img: File truncated, expecting at least 13172736 bytes -qemu-img: Could not open 'TEST_DIR/t.IMGFMT': Could not open 'TEST_DIR/t.IMGFMT': Wrong medium type +qemu-img: Could not open 'TEST_DIR/t.IMGFMT': File truncated, expecting at least 13172736 bytes === Testing version 3 === image: TEST_DIR/iotest-version3.IMGFMT diff --git a/tests/qemu-iotests/081 b/tests/qemu-iotests/081 new file mode 100755 index 0000000..f053f11 --- /dev/null +++ b/tests/qemu-iotests/081 @@ -0,0 +1,146 @@ +#!/bin/bash +# +# Test Quorum block driver +# +# Copyright (C) 2013 Nodalink, SARL. +# +# 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=benoit@irqsave.net + +seq=`basename $0` +echo "QA output created by $seq" + +here=`pwd` +tmp=/tmp/$$ +status=1 # failure is the default! + +_cleanup() +{ + rm -rf $TEST_DIR/1.raw + rm -rf $TEST_DIR/2.raw + rm -rf $TEST_DIR/3.raw +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +_supported_fmt raw +_supported_proto generic +_supported_os Linux + +function do_run_qemu() +{ + echo Testing: "$@" | _filter_imgfmt + $QEMU -nographic -qmp stdio -serial none "$@" + echo +} + +function run_qemu() +{ + do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qmp | _filter_qemu_io +} + +quorum="file.driver=quorum,file.children.0.file.filename=$TEST_DIR/1.raw" +quorum="$quorum,file.children.1.file.filename=$TEST_DIR/2.raw" +quorum="$quorum,file.children.2.file.filename=$TEST_DIR/3.raw,file.vote-threshold=2" + +echo +echo "== creating quorum files ==" + +size=10M + +TEST_IMG="$TEST_DIR/1.raw" _make_test_img $size +TEST_IMG="$TEST_DIR/2.raw" _make_test_img $size +TEST_IMG="$TEST_DIR/3.raw" _make_test_img $size + +echo +echo "== writing images ==" + +$QEMU_IO -c "open -o $quorum" -c "write -P 0x32 0 $size" | _filter_qemu_io + +echo +echo "== checking quorum write ==" + +$QEMU_IO -c "read -P 0x32 0 $size" "$TEST_DIR/1.raw" | _filter_qemu_io +$QEMU_IO -c "read -P 0x32 0 $size" "$TEST_DIR/2.raw" | _filter_qemu_io +$QEMU_IO -c "read -P 0x32 0 $size" "$TEST_DIR/3.raw" | _filter_qemu_io + +echo +echo "== corrupting image ==" + +$QEMU_IO -c "write -P 0x42 0 $size" "$TEST_DIR/2.raw" | _filter_qemu_io + +echo +echo "== checking quorum correction ==" + +$QEMU_IO -c "open -o $quorum" -c "read -P 0x32 0 $size" | _filter_qemu_io + +echo +echo "== checking mixed reference/option specification ==" + +run_qemu -drive "file=$TEST_DIR/2.raw,format=$IMGFMT,if=none,id=drive2" <<EOF +{ "execute": "qmp_capabilities" } +{ "execute": "blockdev-add", + "arguments": { + "options": { + "driver": "quorum", + "id": "drive0-quorum", + "vote-threshold": 2, + "children": [ + { + "driver": "raw", + "file": { + "driver": "file", + "filename": "$TEST_DIR/1.raw" + } + }, + "drive2", + { + "driver": "raw", + "file": { + "driver": "file", + "filename": "$TEST_DIR/3.raw" + } + } + ] + } + } +} +{ "execute": "human-monitor-command", + "arguments": { + "command-line": 'qemu-io drive0-quorum "read -P 0x32 0 $size"' + } +} +{ "execute": "quit" } +EOF + +echo +echo "== breaking quorum ==" + +$QEMU_IO -c "write -P 0x41 0 $size" "$TEST_DIR/1.raw" | _filter_qemu_io +echo +echo "== checking that quorum is broken ==" + +$QEMU_IO -c "open -o $quorum" -c "read -P 0x32 0 $size" | _filter_qemu_io + + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/081.out b/tests/qemu-iotests/081.out new file mode 100644 index 0000000..4fe2f95 --- /dev/null +++ b/tests/qemu-iotests/081.out @@ -0,0 +1,49 @@ +QA output created by 081 + +== creating quorum files == +Formatting 'TEST_DIR/1.IMGFMT', fmt=IMGFMT size=10485760 +Formatting 'TEST_DIR/2.IMGFMT', fmt=IMGFMT size=10485760 +Formatting 'TEST_DIR/3.IMGFMT', fmt=IMGFMT size=10485760 + +== writing images == +wrote 10485760/10485760 bytes at offset 0 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +== checking quorum write == +read 10485760/10485760 bytes at offset 0 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 10485760/10485760 bytes at offset 0 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 10485760/10485760 bytes at offset 0 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +== corrupting image == +wrote 10485760/10485760 bytes at offset 0 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +== checking quorum correction == +read 10485760/10485760 bytes at offset 0 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +== checking mixed reference/option specification == +Testing: -drive file=TEST_DIR/2.IMGFMT,format=IMGFMT,if=none,id=drive2 +QMP_VERSION +{"return": {}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "QUORUM_REPORT_BAD", "data": {"node-name": "", "ret": 0, "sectors-count": 20480, "sector-num": 0}} +read 10485760/10485760 bytes at offset 0 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +{"return": ""} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN"} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_TRAY_MOVED", "data": {"device": "ide1-cd0", "tray-open": true}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_TRAY_MOVED", "data": {"device": "floppy0", "tray-open": true}} + + +== breaking quorum == +wrote 10485760/10485760 bytes at offset 0 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +== checking that quorum is broken == +qemu-io: can't open device (null): Could not read image for determining its format: Input/output error +*** done diff --git a/tests/qemu-iotests/082 b/tests/qemu-iotests/082 new file mode 100755 index 0000000..f6eb75f --- /dev/null +++ b/tests/qemu-iotests/082 @@ -0,0 +1,208 @@ +#!/bin/bash +# +# Test qemu-img command line parsing +# +# Copyright (C) 2014 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=kwolf@redhat.com + +seq=`basename $0` +echo "QA output created by $seq" + +here=`pwd` +tmp=/tmp/$$ +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +_supported_fmt qcow2 +_supported_proto file +_supported_os Linux + +function run_qemu_img() +{ + echo + echo Testing: "$@" | _filter_testdir + "$QEMU_IMG" "$@" 2>&1 | _filter_testdir +} + +size=128M + +echo +echo === create: Options specified more than once === + +# Last -f should win +run_qemu_img create -f foo -f $IMGFMT "$TEST_IMG" $size +run_qemu_img info "$TEST_IMG" + +# Multiple -o should be merged +run_qemu_img create -f $IMGFMT -o cluster_size=4k -o lazy_refcounts=on "$TEST_IMG" $size +run_qemu_img info "$TEST_IMG" + +# If the same -o key is specified more than once, the last one wins +run_qemu_img create -f $IMGFMT -o cluster_size=4k -o lazy_refcounts=on -o cluster_size=8k "$TEST_IMG" $size +run_qemu_img info "$TEST_IMG" +run_qemu_img create -f $IMGFMT -o cluster_size=4k,cluster_size=8k "$TEST_IMG" $size +run_qemu_img info "$TEST_IMG" + +echo +echo === create: help for -o === + +# Adding the help option to a command without other -o options +run_qemu_img create -f $IMGFMT -o help "$TEST_IMG" $size +run_qemu_img create -f $IMGFMT -o \? "$TEST_IMG" $size + +# Adding the help option to the same -o option +run_qemu_img create -f $IMGFMT -o cluster_size=4k,help "$TEST_IMG" $size +run_qemu_img create -f $IMGFMT -o cluster_size=4k,\? "$TEST_IMG" $size +run_qemu_img create -f $IMGFMT -o help,cluster_size=4k "$TEST_IMG" $size +run_qemu_img create -f $IMGFMT -o \?,cluster_size=4k "$TEST_IMG" $size + +# Adding the help option to a separate -o option +run_qemu_img create -f $IMGFMT -o cluster_size=4k -o help "$TEST_IMG" $size +run_qemu_img create -f $IMGFMT -o cluster_size=4k -o \? "$TEST_IMG" $size + +# Looks like a help option, but is part of the backing file name +run_qemu_img create -f $IMGFMT -o backing_file="$TEST_IMG",,help "$TEST_IMG" $size +run_qemu_img create -f $IMGFMT -o backing_file="$TEST_IMG",,\? "$TEST_IMG" $size + +# Try to trick qemu-img into creating escaped commas +run_qemu_img create -f $IMGFMT -o backing_file="$TEST_IMG", -o help "$TEST_IMG" $size +run_qemu_img create -f $IMGFMT -o backing_file="$TEST_IMG" -o ,help "$TEST_IMG" $size +run_qemu_img create -f $IMGFMT -o backing_file="$TEST_IMG" -o ,, -o help "$TEST_IMG" $size + +# Leave out everything that isn't needed +run_qemu_img create -f $IMGFMT -o help +run_qemu_img create -o help + +echo +echo === convert: Options specified more than once === + +# We need a valid source image +run_qemu_img create -f $IMGFMT "$TEST_IMG" $size + +# Last -f should win +run_qemu_img convert -f foo -f $IMGFMT "$TEST_IMG" "$TEST_IMG".base +run_qemu_img info "$TEST_IMG".base + +# Last -O should win +run_qemu_img convert -O foo -O $IMGFMT "$TEST_IMG" "$TEST_IMG".base +run_qemu_img info "$TEST_IMG".base + +# Multiple -o should be merged +run_qemu_img convert -O $IMGFMT -o cluster_size=4k -o lazy_refcounts=on "$TEST_IMG" "$TEST_IMG".base +run_qemu_img info "$TEST_IMG".base + +# If the same -o key is specified more than once, the last one wins +run_qemu_img convert -O $IMGFMT -o cluster_size=4k -o lazy_refcounts=on -o cluster_size=8k "$TEST_IMG" "$TEST_IMG".base +run_qemu_img info "$TEST_IMG".base +run_qemu_img convert -O $IMGFMT -o cluster_size=4k,cluster_size=8k "$TEST_IMG" "$TEST_IMG".base +run_qemu_img info "$TEST_IMG".base + +echo +echo === convert: help for -o === + +# Adding the help option to a command without other -o options +run_qemu_img convert -O $IMGFMT -o help "$TEST_IMG" "$TEST_IMG".base +run_qemu_img convert -O $IMGFMT -o \? "$TEST_IMG" "$TEST_IMG".base + +# Adding the help option to the same -o option +run_qemu_img convert -O $IMGFMT -o cluster_size=4k,help "$TEST_IMG" "$TEST_IMG".base +run_qemu_img convert -O $IMGFMT -o cluster_size=4k,\? "$TEST_IMG" "$TEST_IMG".base +run_qemu_img convert -O $IMGFMT -o help,cluster_size=4k "$TEST_IMG" "$TEST_IMG".base +run_qemu_img convert -O $IMGFMT -o \?,cluster_size=4k "$TEST_IMG" "$TEST_IMG".base + +# Adding the help option to a separate -o option +run_qemu_img convert -O $IMGFMT -o cluster_size=4k -o help "$TEST_IMG" "$TEST_IMG".base +run_qemu_img convert -O $IMGFMT -o cluster_size=4k -o \? "$TEST_IMG" "$TEST_IMG".base + +# Looks like a help option, but is part of the backing file name +run_qemu_img convert -O $IMGFMT -o backing_file="$TEST_IMG",,help "$TEST_IMG" "$TEST_IMG".base +run_qemu_img convert -O $IMGFMT -o backing_file="$TEST_IMG",,\? "$TEST_IMG" "$TEST_IMG".base + +# Try to trick qemu-img into creating escaped commas +run_qemu_img convert -O $IMGFMT -o backing_file="$TEST_IMG", -o help "$TEST_IMG" "$TEST_IMG".base +run_qemu_img convert -O $IMGFMT -o backing_file="$TEST_IMG" -o ,help "$TEST_IMG" "$TEST_IMG".base +run_qemu_img convert -O $IMGFMT -o backing_file="$TEST_IMG" -o ,, -o help "$TEST_IMG" "$TEST_IMG".base + +# Leave out everything that isn't needed +run_qemu_img convert -O $IMGFMT -o help +run_qemu_img convert -o help + +echo +echo === amend: Options specified more than once === + +# Last -f should win +run_qemu_img amend -f foo -f $IMGFMT -o lazy_refcounts=on "$TEST_IMG" +run_qemu_img info "$TEST_IMG" + +# Multiple -o should be merged +run_qemu_img amend -f $IMGFMT -o size=130M -o lazy_refcounts=off "$TEST_IMG" +run_qemu_img info "$TEST_IMG" + +# If the same -o key is specified more than once, the last one wins +run_qemu_img amend -f $IMGFMT -o size=8M -o lazy_refcounts=on -o size=132M "$TEST_IMG" +run_qemu_img info "$TEST_IMG" +run_qemu_img amend -f $IMGFMT -o size=4M,size=148M "$TEST_IMG" +run_qemu_img info "$TEST_IMG" + +echo +echo === amend: help for -o === + +# Adding the help option to a command without other -o options +run_qemu_img amend -f $IMGFMT -o help "$TEST_IMG" +run_qemu_img amend -f $IMGFMT -o \? "$TEST_IMG" + +# Adding the help option to the same -o option +run_qemu_img amend -f $IMGFMT -o cluster_size=4k,help "$TEST_IMG" +run_qemu_img amend -f $IMGFMT -o cluster_size=4k,\? "$TEST_IMG" +run_qemu_img amend -f $IMGFMT -o help,cluster_size=4k "$TEST_IMG" +run_qemu_img amend -f $IMGFMT -o \?,cluster_size=4k "$TEST_IMG" + +# Adding the help option to a separate -o option +run_qemu_img amend -f $IMGFMT -o cluster_size=4k -o help "$TEST_IMG" +run_qemu_img amend -f $IMGFMT -o cluster_size=4k -o \? "$TEST_IMG" + +# Looks like a help option, but is part of the backing file name +run_qemu_img amend -f $IMGFMT -o backing_file="$TEST_IMG",,help "$TEST_IMG" +run_qemu_img rebase -u -b "" -f $IMGFMT "$TEST_IMG" + +run_qemu_img amend -f $IMGFMT -o backing_file="$TEST_IMG",,\? "$TEST_IMG" +run_qemu_img rebase -u -b "" -f $IMGFMT "$TEST_IMG" + +# Try to trick qemu-img into creating escaped commas +run_qemu_img amend -f $IMGFMT -o backing_file="$TEST_IMG", -o help "$TEST_IMG" +run_qemu_img amend -f $IMGFMT -o backing_file="$TEST_IMG" -o ,help "$TEST_IMG" +run_qemu_img amend -f $IMGFMT -o backing_file="$TEST_IMG" -o ,, -o help "$TEST_IMG" + +# Leave out everything that isn't needed +run_qemu_img amend -f $IMGFMT -o help +run_qemu_img convert -o help + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/082.out b/tests/qemu-iotests/082.out new file mode 100644 index 0000000..28309a0 --- /dev/null +++ b/tests/qemu-iotests/082.out @@ -0,0 +1,529 @@ +QA output created by 082 + +=== create: Options specified more than once === + +Testing: create -f foo -f qcow2 TEST_DIR/t.qcow2 128M +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 encryption=off cluster_size=65536 lazy_refcounts=off + +Testing: info TEST_DIR/t.qcow2 +image: TEST_DIR/t.qcow2 +file format: qcow2 +virtual size: 128M (134217728 bytes) +disk size: 196K +cluster_size: 65536 +Format specific information: + compat: 1.1 + lazy refcounts: false + +Testing: create -f qcow2 -o cluster_size=4k -o lazy_refcounts=on TEST_DIR/t.qcow2 128M +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 encryption=off cluster_size=4096 lazy_refcounts=on + +Testing: info TEST_DIR/t.qcow2 +image: TEST_DIR/t.qcow2 +file format: qcow2 +virtual size: 128M (134217728 bytes) +disk size: 16K +cluster_size: 4096 +Format specific information: + compat: 1.1 + lazy refcounts: true + +Testing: create -f qcow2 -o cluster_size=4k -o lazy_refcounts=on -o cluster_size=8k TEST_DIR/t.qcow2 128M +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 encryption=off cluster_size=8192 lazy_refcounts=on + +Testing: info TEST_DIR/t.qcow2 +image: TEST_DIR/t.qcow2 +file format: qcow2 +virtual size: 128M (134217728 bytes) +disk size: 28K +cluster_size: 8192 +Format specific information: + compat: 1.1 + lazy refcounts: true + +Testing: create -f qcow2 -o cluster_size=4k,cluster_size=8k TEST_DIR/t.qcow2 128M +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 encryption=off cluster_size=8192 lazy_refcounts=off + +Testing: info TEST_DIR/t.qcow2 +image: TEST_DIR/t.qcow2 +file format: qcow2 +virtual size: 128M (134217728 bytes) +disk size: 28K +cluster_size: 8192 +Format specific information: + compat: 1.1 + lazy refcounts: false + +=== create: help for -o === + +Testing: create -f qcow2 -o help TEST_DIR/t.qcow2 128M +Supported options: +size Virtual disk size +compat Compatibility level (0.10 or 1.1) +backing_file File name of a base image +backing_fmt Image format of the base image +encryption Encrypt the image +cluster_size qcow2 cluster size +preallocation Preallocation mode (allowed values: off, metadata) +lazy_refcounts Postpone refcount updates + +Testing: create -f qcow2 -o ? TEST_DIR/t.qcow2 128M +Supported options: +size Virtual disk size +compat Compatibility level (0.10 or 1.1) +backing_file File name of a base image +backing_fmt Image format of the base image +encryption Encrypt the image +cluster_size qcow2 cluster size +preallocation Preallocation mode (allowed values: off, metadata) +lazy_refcounts Postpone refcount updates + +Testing: create -f qcow2 -o cluster_size=4k,help TEST_DIR/t.qcow2 128M +Supported options: +size Virtual disk size +compat Compatibility level (0.10 or 1.1) +backing_file File name of a base image +backing_fmt Image format of the base image +encryption Encrypt the image +cluster_size qcow2 cluster size +preallocation Preallocation mode (allowed values: off, metadata) +lazy_refcounts Postpone refcount updates + +Testing: create -f qcow2 -o cluster_size=4k,? TEST_DIR/t.qcow2 128M +Supported options: +size Virtual disk size +compat Compatibility level (0.10 or 1.1) +backing_file File name of a base image +backing_fmt Image format of the base image +encryption Encrypt the image +cluster_size qcow2 cluster size +preallocation Preallocation mode (allowed values: off, metadata) +lazy_refcounts Postpone refcount updates + +Testing: create -f qcow2 -o help,cluster_size=4k TEST_DIR/t.qcow2 128M +Supported options: +size Virtual disk size +compat Compatibility level (0.10 or 1.1) +backing_file File name of a base image +backing_fmt Image format of the base image +encryption Encrypt the image +cluster_size qcow2 cluster size +preallocation Preallocation mode (allowed values: off, metadata) +lazy_refcounts Postpone refcount updates + +Testing: create -f qcow2 -o ?,cluster_size=4k TEST_DIR/t.qcow2 128M +Supported options: +size Virtual disk size +compat Compatibility level (0.10 or 1.1) +backing_file File name of a base image +backing_fmt Image format of the base image +encryption Encrypt the image +cluster_size qcow2 cluster size +preallocation Preallocation mode (allowed values: off, metadata) +lazy_refcounts Postpone refcount updates + +Testing: create -f qcow2 -o cluster_size=4k -o help TEST_DIR/t.qcow2 128M +Supported options: +size Virtual disk size +compat Compatibility level (0.10 or 1.1) +backing_file File name of a base image +backing_fmt Image format of the base image +encryption Encrypt the image +cluster_size qcow2 cluster size +preallocation Preallocation mode (allowed values: off, metadata) +lazy_refcounts Postpone refcount updates + +Testing: create -f qcow2 -o cluster_size=4k -o ? TEST_DIR/t.qcow2 128M +Supported options: +size Virtual disk size +compat Compatibility level (0.10 or 1.1) +backing_file File name of a base image +backing_fmt Image format of the base image +encryption Encrypt the image +cluster_size qcow2 cluster size +preallocation Preallocation mode (allowed values: off, metadata) +lazy_refcounts Postpone refcount updates + +Testing: create -f qcow2 -o backing_file=TEST_DIR/t.qcow2,,help TEST_DIR/t.qcow2 128M +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/t.qcow2,help' encryption=off cluster_size=65536 lazy_refcounts=off + +Testing: create -f qcow2 -o backing_file=TEST_DIR/t.qcow2,,? TEST_DIR/t.qcow2 128M +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/t.qcow2,?' encryption=off cluster_size=65536 lazy_refcounts=off + +Testing: create -f qcow2 -o backing_file=TEST_DIR/t.qcow2, -o help TEST_DIR/t.qcow2 128M +qemu-img: Invalid option list: backing_file=TEST_DIR/t.qcow2, + +Testing: create -f qcow2 -o backing_file=TEST_DIR/t.qcow2 -o ,help TEST_DIR/t.qcow2 128M +qemu-img: Invalid option list: ,help + +Testing: create -f qcow2 -o backing_file=TEST_DIR/t.qcow2 -o ,, -o help TEST_DIR/t.qcow2 128M +qemu-img: Invalid option list: ,, + +Testing: create -f qcow2 -o help +Supported options: +size Virtual disk size +compat Compatibility level (0.10 or 1.1) +backing_file File name of a base image +backing_fmt Image format of the base image +encryption Encrypt the image +cluster_size qcow2 cluster size +preallocation Preallocation mode (allowed values: off, metadata) +lazy_refcounts Postpone refcount updates + +Testing: create -o help +Supported options: +size Virtual disk size + +=== convert: Options specified more than once === + +Testing: create -f qcow2 TEST_DIR/t.qcow2 128M +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 encryption=off cluster_size=65536 lazy_refcounts=off + +Testing: convert -f foo -f qcow2 TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base + +Testing: info TEST_DIR/t.qcow2.base +image: TEST_DIR/t.qcow2.base +file format: raw +virtual size: 128M (134217728 bytes) +disk size: 0 + +Testing: convert -O foo -O qcow2 TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base + +Testing: info TEST_DIR/t.qcow2.base +image: TEST_DIR/t.qcow2.base +file format: qcow2 +virtual size: 128M (134217728 bytes) +disk size: 196K +cluster_size: 65536 +Format specific information: + compat: 1.1 + lazy refcounts: false + +Testing: convert -O qcow2 -o cluster_size=4k -o lazy_refcounts=on TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base + +Testing: info TEST_DIR/t.qcow2.base +image: TEST_DIR/t.qcow2.base +file format: qcow2 +virtual size: 128M (134217728 bytes) +disk size: 16K +cluster_size: 4096 +Format specific information: + compat: 1.1 + lazy refcounts: true + +Testing: convert -O qcow2 -o cluster_size=4k -o lazy_refcounts=on -o cluster_size=8k TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base + +Testing: info TEST_DIR/t.qcow2.base +image: TEST_DIR/t.qcow2.base +file format: qcow2 +virtual size: 128M (134217728 bytes) +disk size: 28K +cluster_size: 8192 +Format specific information: + compat: 1.1 + lazy refcounts: true + +Testing: convert -O qcow2 -o cluster_size=4k,cluster_size=8k TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base + +Testing: info TEST_DIR/t.qcow2.base +image: TEST_DIR/t.qcow2.base +file format: qcow2 +virtual size: 128M (134217728 bytes) +disk size: 28K +cluster_size: 8192 +Format specific information: + compat: 1.1 + lazy refcounts: false + +=== convert: help for -o === + +Testing: convert -O qcow2 -o help TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base +Supported options: +size Virtual disk size +compat Compatibility level (0.10 or 1.1) +backing_file File name of a base image +backing_fmt Image format of the base image +encryption Encrypt the image +cluster_size qcow2 cluster size +preallocation Preallocation mode (allowed values: off, metadata) +lazy_refcounts Postpone refcount updates + +Testing: convert -O qcow2 -o ? TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base +Supported options: +size Virtual disk size +compat Compatibility level (0.10 or 1.1) +backing_file File name of a base image +backing_fmt Image format of the base image +encryption Encrypt the image +cluster_size qcow2 cluster size +preallocation Preallocation mode (allowed values: off, metadata) +lazy_refcounts Postpone refcount updates + +Testing: convert -O qcow2 -o cluster_size=4k,help TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base +Supported options: +size Virtual disk size +compat Compatibility level (0.10 or 1.1) +backing_file File name of a base image +backing_fmt Image format of the base image +encryption Encrypt the image +cluster_size qcow2 cluster size +preallocation Preallocation mode (allowed values: off, metadata) +lazy_refcounts Postpone refcount updates + +Testing: convert -O qcow2 -o cluster_size=4k,? TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base +Supported options: +size Virtual disk size +compat Compatibility level (0.10 or 1.1) +backing_file File name of a base image +backing_fmt Image format of the base image +encryption Encrypt the image +cluster_size qcow2 cluster size +preallocation Preallocation mode (allowed values: off, metadata) +lazy_refcounts Postpone refcount updates + +Testing: convert -O qcow2 -o help,cluster_size=4k TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base +Supported options: +size Virtual disk size +compat Compatibility level (0.10 or 1.1) +backing_file File name of a base image +backing_fmt Image format of the base image +encryption Encrypt the image +cluster_size qcow2 cluster size +preallocation Preallocation mode (allowed values: off, metadata) +lazy_refcounts Postpone refcount updates + +Testing: convert -O qcow2 -o ?,cluster_size=4k TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base +Supported options: +size Virtual disk size +compat Compatibility level (0.10 or 1.1) +backing_file File name of a base image +backing_fmt Image format of the base image +encryption Encrypt the image +cluster_size qcow2 cluster size +preallocation Preallocation mode (allowed values: off, metadata) +lazy_refcounts Postpone refcount updates + +Testing: convert -O qcow2 -o cluster_size=4k -o help TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base +Supported options: +size Virtual disk size +compat Compatibility level (0.10 or 1.1) +backing_file File name of a base image +backing_fmt Image format of the base image +encryption Encrypt the image +cluster_size qcow2 cluster size +preallocation Preallocation mode (allowed values: off, metadata) +lazy_refcounts Postpone refcount updates + +Testing: convert -O qcow2 -o cluster_size=4k -o ? TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base +Supported options: +size Virtual disk size +compat Compatibility level (0.10 or 1.1) +backing_file File name of a base image +backing_fmt Image format of the base image +encryption Encrypt the image +cluster_size qcow2 cluster size +preallocation Preallocation mode (allowed values: off, metadata) +lazy_refcounts Postpone refcount updates + +Testing: convert -O qcow2 -o backing_file=TEST_DIR/t.qcow2,,help TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base +qemu-img: Could not open 'TEST_DIR/t.qcow2.base': Could not open backing file: Could not open 'TEST_DIR/t.qcow2,help': No such file or directory + +Testing: convert -O qcow2 -o backing_file=TEST_DIR/t.qcow2,,? TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base +qemu-img: Could not open 'TEST_DIR/t.qcow2.base': Could not open backing file: Could not open 'TEST_DIR/t.qcow2,?': No such file or directory + +Testing: convert -O qcow2 -o backing_file=TEST_DIR/t.qcow2, -o help TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base +qemu-img: Invalid option list: backing_file=TEST_DIR/t.qcow2, + +Testing: convert -O qcow2 -o backing_file=TEST_DIR/t.qcow2 -o ,help TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base +qemu-img: Invalid option list: ,help + +Testing: convert -O qcow2 -o backing_file=TEST_DIR/t.qcow2 -o ,, -o help TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base +qemu-img: Invalid option list: ,, + +Testing: convert -O qcow2 -o help +Supported options: +size Virtual disk size +compat Compatibility level (0.10 or 1.1) +backing_file File name of a base image +backing_fmt Image format of the base image +encryption Encrypt the image +cluster_size qcow2 cluster size +preallocation Preallocation mode (allowed values: off, metadata) +lazy_refcounts Postpone refcount updates + +Testing: convert -o help +Supported options: +size Virtual disk size + +=== amend: Options specified more than once === + +Testing: amend -f foo -f qcow2 -o lazy_refcounts=on TEST_DIR/t.qcow2 + +Testing: info TEST_DIR/t.qcow2 +image: TEST_DIR/t.qcow2 +file format: qcow2 +virtual size: 128M (134217728 bytes) +disk size: 196K +cluster_size: 65536 +Format specific information: + compat: 1.1 + lazy refcounts: true + +Testing: amend -f qcow2 -o size=130M -o lazy_refcounts=off TEST_DIR/t.qcow2 + +Testing: info TEST_DIR/t.qcow2 +image: TEST_DIR/t.qcow2 +file format: qcow2 +virtual size: 130M (136314880 bytes) +disk size: 196K +cluster_size: 65536 +Format specific information: + compat: 1.1 + lazy refcounts: false + +Testing: amend -f qcow2 -o size=8M -o lazy_refcounts=on -o size=132M TEST_DIR/t.qcow2 + +Testing: info TEST_DIR/t.qcow2 +image: TEST_DIR/t.qcow2 +file format: qcow2 +virtual size: 132M (138412032 bytes) +disk size: 196K +cluster_size: 65536 +Format specific information: + compat: 1.1 + lazy refcounts: true + +Testing: amend -f qcow2 -o size=4M,size=148M TEST_DIR/t.qcow2 + +Testing: info TEST_DIR/t.qcow2 +image: TEST_DIR/t.qcow2 +file format: qcow2 +virtual size: 148M (155189248 bytes) +disk size: 196K +cluster_size: 65536 +Format specific information: + compat: 1.1 + lazy refcounts: true + +=== amend: help for -o === + +Testing: amend -f qcow2 -o help TEST_DIR/t.qcow2 +Supported options: +size Virtual disk size +compat Compatibility level (0.10 or 1.1) +backing_file File name of a base image +backing_fmt Image format of the base image +encryption Encrypt the image +cluster_size qcow2 cluster size +preallocation Preallocation mode (allowed values: off, metadata) +lazy_refcounts Postpone refcount updates + +Testing: amend -f qcow2 -o ? TEST_DIR/t.qcow2 +Supported options: +size Virtual disk size +compat Compatibility level (0.10 or 1.1) +backing_file File name of a base image +backing_fmt Image format of the base image +encryption Encrypt the image +cluster_size qcow2 cluster size +preallocation Preallocation mode (allowed values: off, metadata) +lazy_refcounts Postpone refcount updates + +Testing: amend -f qcow2 -o cluster_size=4k,help TEST_DIR/t.qcow2 +Supported options: +size Virtual disk size +compat Compatibility level (0.10 or 1.1) +backing_file File name of a base image +backing_fmt Image format of the base image +encryption Encrypt the image +cluster_size qcow2 cluster size +preallocation Preallocation mode (allowed values: off, metadata) +lazy_refcounts Postpone refcount updates + +Testing: amend -f qcow2 -o cluster_size=4k,? TEST_DIR/t.qcow2 +Supported options: +size Virtual disk size +compat Compatibility level (0.10 or 1.1) +backing_file File name of a base image +backing_fmt Image format of the base image +encryption Encrypt the image +cluster_size qcow2 cluster size +preallocation Preallocation mode (allowed values: off, metadata) +lazy_refcounts Postpone refcount updates + +Testing: amend -f qcow2 -o help,cluster_size=4k TEST_DIR/t.qcow2 +Supported options: +size Virtual disk size +compat Compatibility level (0.10 or 1.1) +backing_file File name of a base image +backing_fmt Image format of the base image +encryption Encrypt the image +cluster_size qcow2 cluster size +preallocation Preallocation mode (allowed values: off, metadata) +lazy_refcounts Postpone refcount updates + +Testing: amend -f qcow2 -o ?,cluster_size=4k TEST_DIR/t.qcow2 +Supported options: +size Virtual disk size +compat Compatibility level (0.10 or 1.1) +backing_file File name of a base image +backing_fmt Image format of the base image +encryption Encrypt the image +cluster_size qcow2 cluster size +preallocation Preallocation mode (allowed values: off, metadata) +lazy_refcounts Postpone refcount updates + +Testing: amend -f qcow2 -o cluster_size=4k -o help TEST_DIR/t.qcow2 +Supported options: +size Virtual disk size +compat Compatibility level (0.10 or 1.1) +backing_file File name of a base image +backing_fmt Image format of the base image +encryption Encrypt the image +cluster_size qcow2 cluster size +preallocation Preallocation mode (allowed values: off, metadata) +lazy_refcounts Postpone refcount updates + +Testing: amend -f qcow2 -o cluster_size=4k -o ? TEST_DIR/t.qcow2 +Supported options: +size Virtual disk size +compat Compatibility level (0.10 or 1.1) +backing_file File name of a base image +backing_fmt Image format of the base image +encryption Encrypt the image +cluster_size qcow2 cluster size +preallocation Preallocation mode (allowed values: off, metadata) +lazy_refcounts Postpone refcount updates + +Testing: amend -f qcow2 -o backing_file=TEST_DIR/t.qcow2,,help TEST_DIR/t.qcow2 + +Testing: rebase -u -b -f qcow2 TEST_DIR/t.qcow2 + +Testing: amend -f qcow2 -o backing_file=TEST_DIR/t.qcow2,,? TEST_DIR/t.qcow2 + +Testing: rebase -u -b -f qcow2 TEST_DIR/t.qcow2 + +Testing: amend -f qcow2 -o backing_file=TEST_DIR/t.qcow2, -o help TEST_DIR/t.qcow2 +qemu-img: Invalid option list: backing_file=TEST_DIR/t.qcow2, + +Testing: amend -f qcow2 -o backing_file=TEST_DIR/t.qcow2 -o ,help TEST_DIR/t.qcow2 +qemu-img: Invalid option list: ,help + +Testing: amend -f qcow2 -o backing_file=TEST_DIR/t.qcow2 -o ,, -o help TEST_DIR/t.qcow2 +qemu-img: Invalid option list: ,, + +Testing: amend -f qcow2 -o help +Supported options: +size Virtual disk size +compat Compatibility level (0.10 or 1.1) +backing_file File name of a base image +backing_fmt Image format of the base image +encryption Encrypt the image +cluster_size qcow2 cluster size +preallocation Preallocation mode (allowed values: off, metadata) +lazy_refcounts Postpone refcount updates + +Testing: convert -o help +Supported options: +size Virtual disk size +*** done diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group index d8be74a..db127d9 100644 --- a/tests/qemu-iotests/group +++ b/tests/qemu-iotests/group @@ -83,3 +83,5 @@ 074 rw auto 077 rw auto 079 rw auto +081 rw auto +082 rw auto quick diff --git a/tests/tcg/xtensa/Makefile b/tests/tcg/xtensa/Makefile index 1b519ca..a70c92b 100644 --- a/tests/tcg/xtensa/Makefile +++ b/tests/tcg/xtensa/Makefile @@ -1,10 +1,11 @@ -include ../../../config-host.mak -CROSS=xtensa-dc232b-elf- +CORE=dc232b +CROSS=xtensa-$(CORE)-elf- ifndef XT SIM = ../../../xtensa-softmmu/qemu-system-xtensa -SIMFLAGS = -M sim -cpu dc232b -nographic -semihosting $(EXTFLAGS) -kernel +SIMFLAGS = -M sim -cpu $(CORE) -nographic -semihosting $(EXTFLAGS) -kernel SIMDEBUG = -s -S else SIM = xt-run @@ -17,6 +18,8 @@ AS = $(CROSS)gcc -x assembler-with-cpp LD = $(CROSS)ld XTENSA_SRC_PATH = $(SRC_PATH)/tests/tcg/xtensa +INCLUDE_DIRS = $(XTENSA_SRC_PATH) $(SRC_PATH)/target-xtensa/core-$(CORE) +XTENSA_INC = $(addprefix -I,$(INCLUDE_DIRS)) LDFLAGS = -T$(XTENSA_SRC_PATH)/linker.ld @@ -27,6 +30,7 @@ TESTCASES += test_bi.tst #TESTCASES += test_boolean.tst TESTCASES += test_break.tst TESTCASES += test_bz.tst +TESTCASES += test_cache.tst TESTCASES += test_clamps.tst TESTCASES += test_extui.tst TESTCASES += test_fail.tst @@ -56,10 +60,10 @@ TESTCASES += test_windowed.tst all: build %.o: $(XTENSA_SRC_PATH)/%.c - $(CC) -I$(XTENSA_SRC_PATH) $(CFLAGS) -c $< -o $@ + $(CC) $(XTENSA_INC) $(CFLAGS) -c $< -o $@ %.o: $(XTENSA_SRC_PATH)/%.S - $(AS) -Wa,-I,$(XTENSA_SRC_PATH) $(ASFLAGS) -c $< -o $@ + $(CC) $(XTENSA_INC) $(ASFLAGS) -c $< -o $@ %.tst: %.o $(XTENSA_SRC_PATH)/macros.inc $(CRT) Makefile $(LD) $(LDFLAGS) $(NOSTDFLAGS) $(CRT) $< -o $@ diff --git a/tests/tcg/xtensa/macros.inc b/tests/tcg/xtensa/macros.inc index c9be1ce..4ebd30a 100644 --- a/tests/tcg/xtensa/macros.inc +++ b/tests/tcg/xtensa/macros.inc @@ -1,3 +1,5 @@ +#include "core-isa.h" + .macro test_suite name .data status: .word result @@ -43,8 +45,12 @@ main: simcall .endm +.macro test_init +.endm + .macro test name //print test_\name + test_init test_\name: .global test_\name .endm diff --git a/tests/tcg/xtensa/test_b.S b/tests/tcg/xtensa/test_b.S index 6cbe5f1..8e81f95 100644 --- a/tests/tcg/xtensa/test_b.S +++ b/tests/tcg/xtensa/test_b.S @@ -1,4 +1,4 @@ -.include "macros.inc" +#include "macros.inc" test_suite b diff --git a/tests/tcg/xtensa/test_bi.S b/tests/tcg/xtensa/test_bi.S index 6a5f1df..4f94c0c 100644 --- a/tests/tcg/xtensa/test_bi.S +++ b/tests/tcg/xtensa/test_bi.S @@ -1,4 +1,4 @@ -.include "macros.inc" +#include "macros.inc" test_suite bi diff --git a/tests/tcg/xtensa/test_boolean.S b/tests/tcg/xtensa/test_boolean.S index 50e6d2c..eac40e0 100644 --- a/tests/tcg/xtensa/test_boolean.S +++ b/tests/tcg/xtensa/test_boolean.S @@ -1,4 +1,4 @@ -.include "macros.inc" +#include "macros.inc" test_suite boolean diff --git a/tests/tcg/xtensa/test_break.S b/tests/tcg/xtensa/test_break.S index 7574cbe..775cd7c 100644 --- a/tests/tcg/xtensa/test_break.S +++ b/tests/tcg/xtensa/test_break.S @@ -1,4 +1,4 @@ -.include "macros.inc" +#include "macros.inc" #define debug_level 6 #define debug_vector level6 diff --git a/tests/tcg/xtensa/test_bz.S b/tests/tcg/xtensa/test_bz.S index f9ba6e2..b681350 100644 --- a/tests/tcg/xtensa/test_bz.S +++ b/tests/tcg/xtensa/test_bz.S @@ -1,4 +1,4 @@ -.include "macros.inc" +#include "macros.inc" test_suite bz diff --git a/tests/tcg/xtensa/test_cache.S b/tests/tcg/xtensa/test_cache.S new file mode 100644 index 0000000..6b2df97 --- /dev/null +++ b/tests/tcg/xtensa/test_cache.S @@ -0,0 +1,97 @@ +#include "macros.inc" + +.purgem test_init +.macro test_init + call0 cache_unlock_invalidate +.endm + +test_suite cache + +.macro pf_op op + \op a2, 0 + \op a3, 0 + \op a4, 0 +.endm + +test prefetch + movi a2, 0xd0000000 /* cacheable */ + movi a3, 0xd8000000 /* non-cacheable */ + movi a4, 0x00001235 /* unmapped */ + + pf_op dpfr + pf_op dpfro + pf_op dpfw + pf_op dpfwo + pf_op ipf + + dpfl a2, 0 + ipfl a2, 0 +test_end + +.macro cache_fault op, addr, exc_code + set_vector kernel, 2f + + movi a4, \addr +1: + \op a4, 0 + test_fail +2: + rsr a2, epc1 + movi a3, 1b + assert eq, a2, a3 + rsr a2, excvaddr + assert eq, a2, a4 + rsr a2, exccause + movi a3, \exc_code + assert eq, a2, a3 +.endm + +test dpfl_tlb_miss + cache_fault dpfl, 0x00002345, 24 +test_end + +test dhwb_tlb_miss + cache_fault dhwb, 0x00002345, 24 +test_end + +test dhwbi_tlb_miss + cache_fault dhwbi, 0x00002345, 24 +test_end + +test dhi_tlb_miss + cache_fault dhi, 0x00002345, 24 +test_end + +test dhu_tlb_miss + cache_fault dhu, 0x00002345, 24 +test_end + + +test ipfl_tlb_miss + cache_fault ipfl, 0x00002345, 16 +test_end + +test ihu_tlb_miss + cache_fault ihu, 0x00002345, 16 +test_end + +test ihi_tlb_miss + cache_fault ihi, 0x00002345, 16 +test_end + +test_suite_end + +.macro cache_all op1, op2, size, linesize + movi a2, 0 + movi a3, \size +1: + \op1 a2, 0 + \op2 a2, 0 + addi a2, a2, \linesize + bltu a2, a3, 1b +.endm + +cache_unlock_invalidate: + cache_all diu, dii, XCHAL_DCACHE_SIZE, XCHAL_DCACHE_LINESIZE + cache_all iiu, iii, XCHAL_ICACHE_SIZE, XCHAL_ICACHE_LINESIZE + ret diff --git a/tests/tcg/xtensa/test_clamps.S b/tests/tcg/xtensa/test_clamps.S index c186cc9..3efabfd 100644 --- a/tests/tcg/xtensa/test_clamps.S +++ b/tests/tcg/xtensa/test_clamps.S @@ -1,4 +1,4 @@ -.include "macros.inc" +#include "macros.inc" test_suite clamps diff --git a/tests/tcg/xtensa/test_extui.S b/tests/tcg/xtensa/test_extui.S index 5d55451..c32bb82 100644 --- a/tests/tcg/xtensa/test_extui.S +++ b/tests/tcg/xtensa/test_extui.S @@ -1,4 +1,4 @@ -.include "macros.inc" +#include "macros.inc" test_suite extui diff --git a/tests/tcg/xtensa/test_fail.S b/tests/tcg/xtensa/test_fail.S index e8d1b42..1c26d50 100644 --- a/tests/tcg/xtensa/test_fail.S +++ b/tests/tcg/xtensa/test_fail.S @@ -1,4 +1,4 @@ -.include "macros.inc" +#include "macros.inc" test_suite fail diff --git a/tests/tcg/xtensa/test_interrupt.S b/tests/tcg/xtensa/test_interrupt.S index 68b3ee1..334ddab 100644 --- a/tests/tcg/xtensa/test_interrupt.S +++ b/tests/tcg/xtensa/test_interrupt.S @@ -1,4 +1,4 @@ -.include "macros.inc" +#include "macros.inc" test_suite interrupt diff --git a/tests/tcg/xtensa/test_loop.S b/tests/tcg/xtensa/test_loop.S index 1c240e8..5755578 100644 --- a/tests/tcg/xtensa/test_loop.S +++ b/tests/tcg/xtensa/test_loop.S @@ -1,4 +1,4 @@ -.include "macros.inc" +#include "macros.inc" test_suite loop diff --git a/tests/tcg/xtensa/test_mac16.S b/tests/tcg/xtensa/test_mac16.S index 5ddd160..512025d 100644 --- a/tests/tcg/xtensa/test_mac16.S +++ b/tests/tcg/xtensa/test_mac16.S @@ -1,4 +1,4 @@ -.include "macros.inc" +#include "macros.inc" test_suite mac16 diff --git a/tests/tcg/xtensa/test_max.S b/tests/tcg/xtensa/test_max.S index 2534c9d..3caa207 100644 --- a/tests/tcg/xtensa/test_max.S +++ b/tests/tcg/xtensa/test_max.S @@ -1,4 +1,4 @@ -.include "macros.inc" +#include "macros.inc" test_suite max diff --git a/tests/tcg/xtensa/test_min.S b/tests/tcg/xtensa/test_min.S index 6d9ddeb..551cf59 100644 --- a/tests/tcg/xtensa/test_min.S +++ b/tests/tcg/xtensa/test_min.S @@ -1,4 +1,4 @@ -.include "macros.inc" +#include "macros.inc" test_suite min diff --git a/tests/tcg/xtensa/test_mmu.S b/tests/tcg/xtensa/test_mmu.S index 5d87fbb..099031f 100644 --- a/tests/tcg/xtensa/test_mmu.S +++ b/tests/tcg/xtensa/test_mmu.S @@ -1,10 +1,10 @@ -.include "macros.inc" +#include "macros.inc" test_suite mmu -.purgem test +.purgem test_init -.macro test name +.macro test_init movi a2, 0x00000004 idtlb a2 movi a2, 0x00100004 diff --git a/tests/tcg/xtensa/test_mul16.S b/tests/tcg/xtensa/test_mul16.S index bf94376..98fa704 100644 --- a/tests/tcg/xtensa/test_mul16.S +++ b/tests/tcg/xtensa/test_mul16.S @@ -1,4 +1,4 @@ -.include "macros.inc" +#include "macros.inc" test_suite mul16 diff --git a/tests/tcg/xtensa/test_mul32.S b/tests/tcg/xtensa/test_mul32.S index fdaf573..b288ead 100644 --- a/tests/tcg/xtensa/test_mul32.S +++ b/tests/tcg/xtensa/test_mul32.S @@ -1,4 +1,4 @@ -.include "macros.inc" +#include "macros.inc" test_suite mul32 diff --git a/tests/tcg/xtensa/test_nsa.S b/tests/tcg/xtensa/test_nsa.S index a5fe5de..479b2e2 100644 --- a/tests/tcg/xtensa/test_nsa.S +++ b/tests/tcg/xtensa/test_nsa.S @@ -1,4 +1,4 @@ -.include "macros.inc" +#include "macros.inc" test_suite nsa diff --git a/tests/tcg/xtensa/test_pipeline.S b/tests/tcg/xtensa/test_pipeline.S index 6be6085..f418c11 100644 --- a/tests/tcg/xtensa/test_pipeline.S +++ b/tests/tcg/xtensa/test_pipeline.S @@ -1,4 +1,4 @@ -.include "macros.inc" +#include "macros.inc" .purgem test .macro test name diff --git a/tests/tcg/xtensa/test_quo.S b/tests/tcg/xtensa/test_quo.S index 12debf1..5b3ae38 100644 --- a/tests/tcg/xtensa/test_quo.S +++ b/tests/tcg/xtensa/test_quo.S @@ -1,4 +1,4 @@ -.include "macros.inc" +#include "macros.inc" test_suite quo diff --git a/tests/tcg/xtensa/test_rem.S b/tests/tcg/xtensa/test_rem.S index bb0d5fe..6357e52 100644 --- a/tests/tcg/xtensa/test_rem.S +++ b/tests/tcg/xtensa/test_rem.S @@ -1,4 +1,4 @@ -.include "macros.inc" +#include "macros.inc" test_suite rem diff --git a/tests/tcg/xtensa/test_rst0.S b/tests/tcg/xtensa/test_rst0.S index 3eda565..a73366b 100644 --- a/tests/tcg/xtensa/test_rst0.S +++ b/tests/tcg/xtensa/test_rst0.S @@ -1,4 +1,4 @@ -.include "macros.inc" +#include "macros.inc" test_suite rst0 diff --git a/tests/tcg/xtensa/test_s32c1i.S b/tests/tcg/xtensa/test_s32c1i.S index 4536015..93b575d 100644 --- a/tests/tcg/xtensa/test_s32c1i.S +++ b/tests/tcg/xtensa/test_s32c1i.S @@ -1,4 +1,4 @@ -.include "macros.inc" +#include "macros.inc" test_suite s32c1i diff --git a/tests/tcg/xtensa/test_sar.S b/tests/tcg/xtensa/test_sar.S index 40c649f..b615a55 100644 --- a/tests/tcg/xtensa/test_sar.S +++ b/tests/tcg/xtensa/test_sar.S @@ -1,4 +1,4 @@ -.include "macros.inc" +#include "macros.inc" test_suite sar diff --git a/tests/tcg/xtensa/test_sext.S b/tests/tcg/xtensa/test_sext.S index 04dc650..087a633 100644 --- a/tests/tcg/xtensa/test_sext.S +++ b/tests/tcg/xtensa/test_sext.S @@ -1,4 +1,4 @@ -.include "macros.inc" +#include "macros.inc" test_suite sext diff --git a/tests/tcg/xtensa/test_shift.S b/tests/tcg/xtensa/test_shift.S index a8e4364..5df9ed4 100644 --- a/tests/tcg/xtensa/test_shift.S +++ b/tests/tcg/xtensa/test_shift.S @@ -1,4 +1,4 @@ -.include "macros.inc" +#include "macros.inc" test_suite shift diff --git a/tests/tcg/xtensa/test_sr.S b/tests/tcg/xtensa/test_sr.S index 470c03d..4fac46e 100644 --- a/tests/tcg/xtensa/test_sr.S +++ b/tests/tcg/xtensa/test_sr.S @@ -1,4 +1,4 @@ -.include "macros.inc" +#include "macros.inc" test_suite sr diff --git a/tests/tcg/xtensa/test_timer.S b/tests/tcg/xtensa/test_timer.S index 1041cc6..f8c6f74 100644 --- a/tests/tcg/xtensa/test_timer.S +++ b/tests/tcg/xtensa/test_timer.S @@ -1,4 +1,4 @@ -.include "macros.inc" +#include "macros.inc" test_suite timer diff --git a/tests/tcg/xtensa/test_windowed.S b/tests/tcg/xtensa/test_windowed.S index cb2d39e..3de6d37 100644 --- a/tests/tcg/xtensa/test_windowed.S +++ b/tests/tcg/xtensa/test_windowed.S @@ -1,4 +1,4 @@ -.include "macros.inc" +#include "macros.inc" test_suite windowed @@ -378,6 +378,112 @@ size_t qemu_iovec_memset(QEMUIOVector *qiov, size_t offset, return iov_memset(qiov->iov, qiov->niov, offset, fillc, bytes); } +/** + * Check that I/O vector contents are identical + * + * The IO vectors must have the same structure (same length of all parts). + * A typical usage is to compare vectors created with qemu_iovec_clone(). + * + * @a: I/O vector + * @b: I/O vector + * @ret: Offset to first mismatching byte or -1 if match + */ +ssize_t qemu_iovec_compare(QEMUIOVector *a, QEMUIOVector *b) +{ + int i; + ssize_t offset = 0; + + assert(a->niov == b->niov); + for (i = 0; i < a->niov; i++) { + size_t len = 0; + uint8_t *p = (uint8_t *)a->iov[i].iov_base; + uint8_t *q = (uint8_t *)b->iov[i].iov_base; + + assert(a->iov[i].iov_len == b->iov[i].iov_len); + while (len < a->iov[i].iov_len && *p++ == *q++) { + len++; + } + + offset += len; + + if (len != a->iov[i].iov_len) { + return offset; + } + } + return -1; +} + +typedef struct { + int src_index; + struct iovec *src_iov; + void *dest_base; +} IOVectorSortElem; + +static int sortelem_cmp_src_base(const void *a, const void *b) +{ + const IOVectorSortElem *elem_a = a; + const IOVectorSortElem *elem_b = b; + + /* Don't overflow */ + if (elem_a->src_iov->iov_base < elem_b->src_iov->iov_base) { + return -1; + } else if (elem_a->src_iov->iov_base > elem_b->src_iov->iov_base) { + return 1; + } else { + return 0; + } +} + +static int sortelem_cmp_src_index(const void *a, const void *b) +{ + const IOVectorSortElem *elem_a = a; + const IOVectorSortElem *elem_b = b; + + return elem_a->src_index - elem_b->src_index; +} + +/** + * Copy contents of I/O vector + * + * The relative relationships of overlapping iovecs are preserved. This is + * necessary to ensure identical semantics in the cloned I/O vector. + */ +void qemu_iovec_clone(QEMUIOVector *dest, const QEMUIOVector *src, void *buf) +{ + IOVectorSortElem sortelems[src->niov]; + void *last_end; + int i; + + /* Sort by source iovecs by base address */ + for (i = 0; i < src->niov; i++) { + sortelems[i].src_index = i; + sortelems[i].src_iov = &src->iov[i]; + } + qsort(sortelems, src->niov, sizeof(sortelems[0]), sortelem_cmp_src_base); + + /* Allocate buffer space taking into account overlapping iovecs */ + last_end = NULL; + for (i = 0; i < src->niov; i++) { + struct iovec *cur = sortelems[i].src_iov; + ptrdiff_t rewind = 0; + + /* Detect overlap */ + if (last_end && last_end > cur->iov_base) { + rewind = last_end - cur->iov_base; + } + + sortelems[i].dest_base = buf - rewind; + buf += cur->iov_len - MIN(rewind, cur->iov_len); + last_end = MAX(cur->iov_base + cur->iov_len, last_end); + } + + /* Sort by source iovec index and build destination iovec */ + qsort(sortelems, src->niov, sizeof(sortelems[0]), sortelem_cmp_src_index); + for (i = 0; i < src->niov; i++) { + qemu_iovec_add(dest, sortelems[i].dest_base, src->iov[i].iov_len); + } +} + size_t iov_discard_front(struct iovec **iov, unsigned int *iov_cnt, size_t bytes) { diff --git a/util/module.c b/util/module.c index 7acc33d..dc08c16 100644 --- a/util/module.c +++ b/util/module.c @@ -13,6 +13,10 @@ * GNU GPL, version 2 or (at your option) any later version. */ +#include <stdlib.h> +#ifdef CONFIG_MODULES +#include <gmodule.h> +#endif #include "qemu-common.h" #include "qemu/queue.h" #include "qemu/module.h" @@ -21,13 +25,16 @@ typedef struct ModuleEntry { void (*init)(void); QTAILQ_ENTRY(ModuleEntry) node; + module_init_type type; } ModuleEntry; typedef QTAILQ_HEAD(, ModuleEntry) ModuleTypeList; static ModuleTypeList init_type_list[MODULE_INIT_MAX]; -static void init_types(void) +static ModuleTypeList dso_init_list; + +static void init_lists(void) { static int inited; int i; @@ -40,6 +47,8 @@ static void init_types(void) QTAILQ_INIT(&init_type_list[i]); } + QTAILQ_INIT(&dso_init_list); + inited = 1; } @@ -48,7 +57,7 @@ static ModuleTypeList *find_type(module_init_type type) { ModuleTypeList *l; - init_types(); + init_lists(); l = &init_type_list[type]; @@ -62,20 +71,154 @@ void register_module_init(void (*fn)(void), module_init_type type) e = g_malloc0(sizeof(*e)); e->init = fn; + e->type = type; l = find_type(type); QTAILQ_INSERT_TAIL(l, e, node); } +void register_dso_module_init(void (*fn)(void), module_init_type type) +{ + ModuleEntry *e; + + init_lists(); + + e = g_malloc0(sizeof(*e)); + e->init = fn; + e->type = type; + + QTAILQ_INSERT_TAIL(&dso_init_list, e, node); +} + +static void module_load(module_init_type type); + void module_call_init(module_init_type type) { ModuleTypeList *l; ModuleEntry *e; + module_load(type); l = find_type(type); QTAILQ_FOREACH(e, l, node) { e->init(); } } + +#ifdef CONFIG_MODULES +static int module_load_file(const char *fname) +{ + GModule *g_module; + void (*sym)(void); + const char *dsosuf = HOST_DSOSUF; + int len = strlen(fname); + int suf_len = strlen(dsosuf); + ModuleEntry *e, *next; + int ret; + + if (len <= suf_len || strcmp(&fname[len - suf_len], dsosuf)) { + /* wrong suffix */ + ret = -EINVAL; + goto out; + } + if (access(fname, F_OK)) { + ret = -ENOENT; + goto out; + } + + assert(QTAILQ_EMPTY(&dso_init_list)); + + g_module = g_module_open(fname, G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL); + if (!g_module) { + fprintf(stderr, "Failed to open module: %s\n", + g_module_error()); + ret = -EINVAL; + goto out; + } + if (!g_module_symbol(g_module, DSO_STAMP_FUN_STR, (gpointer *)&sym)) { + fprintf(stderr, "Failed to initialize module: %s\n", + fname); + /* Print some info if this is a QEMU module (but from different build), + * this will make debugging user problems easier. */ + if (g_module_symbol(g_module, "qemu_module_dummy", (gpointer *)&sym)) { + fprintf(stderr, + "Note: only modules from the same build can be loaded.\n"); + } + g_module_close(g_module); + ret = -EINVAL; + } else { + QTAILQ_FOREACH(e, &dso_init_list, node) { + register_module_init(e->init, e->type); + } + ret = 0; + } + + QTAILQ_FOREACH_SAFE(e, &dso_init_list, node, next) { + QTAILQ_REMOVE(&dso_init_list, e, node); + g_free(e); + } +out: + return ret; +} +#endif + +void module_load(module_init_type type) +{ +#ifdef CONFIG_MODULES + char *fname = NULL; + const char **mp; + static const char *block_modules[] = { + CONFIG_BLOCK_MODULES + }; + char *exec_dir; + char *dirs[3]; + int i = 0; + int ret; + + if (!g_module_supported()) { + fprintf(stderr, "Module is not supported by system.\n"); + return; + } + + switch (type) { + case MODULE_INIT_BLOCK: + mp = block_modules; + break; + default: + /* no other types have dynamic modules for now*/ + return; + } + + exec_dir = qemu_get_exec_dir(); + dirs[i++] = g_strdup_printf("%s", CONFIG_QEMU_MODDIR); + dirs[i++] = g_strdup_printf("%s/..", exec_dir ? : ""); + dirs[i++] = g_strdup_printf("%s", exec_dir ? : ""); + assert(i == ARRAY_SIZE(dirs)); + g_free(exec_dir); + exec_dir = NULL; + + for ( ; *mp; mp++) { + for (i = 0; i < ARRAY_SIZE(dirs); i++) { + fname = g_strdup_printf("%s/%s%s", dirs[i], *mp, HOST_DSOSUF); + ret = module_load_file(fname); + /* Try loading until loaded a module file */ + if (!ret) { + break; + } + g_free(fname); + fname = NULL; + } + if (ret == -ENOENT) { + fprintf(stderr, "Can't find module: %s\n", *mp); + } + + g_free(fname); + } + + for (i = 0; i < ARRAY_SIZE(dirs); i++) { + g_free(dirs[i]); + } + +#endif +} diff --git a/util/oslib-posix.c b/util/oslib-posix.c index d5dca47..c2eeb4f 100644 --- a/util/oslib-posix.c +++ b/util/oslib-posix.c @@ -57,6 +57,7 @@ extern int daemon(int, int); #include "trace.h" #include "qemu/sockets.h" #include <sys/mman.h> +#include <libgen.h> #ifdef CONFIG_LINUX #include <sys/syscall.h> @@ -274,3 +275,56 @@ void qemu_set_tty_echo(int fd, bool echo) tcsetattr(fd, TCSANOW, &tty); } + +static char exec_dir[PATH_MAX]; + +void qemu_init_exec_dir(const char *argv0) +{ + char *dir; + char *p = NULL; + char buf[PATH_MAX]; + + assert(!exec_dir[0]); + +#if defined(__linux__) + { + int len; + len = readlink("/proc/self/exe", buf, sizeof(buf) - 1); + if (len > 0) { + buf[len] = 0; + p = buf; + } + } +#elif defined(__FreeBSD__) + { + static int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1}; + size_t len = sizeof(buf) - 1; + + *buf = '\0'; + if (!sysctl(mib, ARRAY_SIZE(mib), buf, &len, NULL, 0) && + *buf) { + buf[sizeof(buf) - 1] = '\0'; + p = buf; + } + } +#endif + /* If we don't have any way of figuring out the actual executable + location then try argv[0]. */ + if (!p) { + if (!argv0) { + return; + } + p = realpath(argv0, buf); + if (!p) { + return; + } + } + dir = dirname(p); + + pstrcpy(exec_dir, sizeof(exec_dir), dir); +} + +char *qemu_get_exec_dir(void) +{ + return g_strdup(exec_dir); +} diff --git a/util/oslib-win32.c b/util/oslib-win32.c index 50be044..93f7d35 100644 --- a/util/oslib-win32.c +++ b/util/oslib-win32.c @@ -208,3 +208,33 @@ void qemu_set_tty_echo(int fd, bool echo) dwMode & ~(ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT)); } } + +static char exec_dir[PATH_MAX]; + +void qemu_init_exec_dir(const char *argv0) +{ + + char *p; + char buf[MAX_PATH]; + DWORD len; + + len = GetModuleFileName(NULL, buf, sizeof(buf) - 1); + if (len == 0) { + return; + } + + buf[len] = 0; + p = buf + len - 1; + while (p != buf && *p != '\\') { + p--; + } + *p = 0; + if (access(buf, R_OK) == 0) { + pstrcpy(exec_dir, sizeof(exec_dir), buf); + } +} + +char *qemu_get_exec_dir(void) +{ + return g_strdup(exec_dir); +} diff --git a/util/qemu-config.c b/util/qemu-config.c index 797df71..f610101 100644 --- a/util/qemu-config.c +++ b/util/qemu-config.c @@ -413,6 +413,12 @@ static void config_parse_qdict_section(QDict *options, QemuOptsList *opts, QDict *section = qobject_to_qdict(qlist_entry_obj(list_entry)); char *opt_name; + if (!section) { + error_setg(errp, "[%s] section (index %u) does not consist of " + "keys", opts->name, i); + goto out; + } + opt_name = g_strdup_printf("%s.%u", opts->name, i++); subopts = qemu_opts_create(opts, opt_name, 1, &local_err); g_free(opt_name); diff --git a/util/qemu-option.c b/util/qemu-option.c index fd76cd2..9d898af 100644 --- a/util/qemu-option.c +++ b/util/qemu-option.c @@ -450,6 +450,55 @@ fail: return NULL; } +bool has_help_option(const char *param) +{ + size_t buflen = strlen(param) + 1; + char *buf = g_malloc0(buflen); + const char *p = param; + bool result = false; + + while (*p) { + p = get_opt_value(buf, buflen, p); + if (*p) { + p++; + } + + if (is_help_option(buf)) { + result = true; + goto out; + } + } + +out: + free(buf); + return result; +} + +bool is_valid_option_list(const char *param) +{ + size_t buflen = strlen(param) + 1; + char *buf = g_malloc0(buflen); + const char *p = param; + bool result = true; + + while (*p) { + p = get_opt_value(buf, buflen, p); + if (*p && !*++p) { + result = false; + goto out; + } + + if (!*buf || *buf == ',') { + result = false; + goto out; + } + } + +out: + free(buf); + return result; +} + /* * Prints all options of a list that have a value to stdout */ @@ -2827,6 +2827,7 @@ int main(int argc, char **argv, char **envp) atexit(qemu_run_exit_notifiers); error_set_progname(argv[0]); + qemu_init_exec_dir(argv[0]); g_mem_set_vtable(&mem_trace); if (!g_thread_supported()) { @@ -3865,7 +3866,7 @@ int main(int argc, char **argv, char **envp) /* If no data_dir is specified then try to find it relative to the executable path. */ if (data_dir_idx < ARRAY_SIZE(data_dir)) { - data_dir[data_dir_idx] = os_find_datadir(argv[0]); + data_dir[data_dir_idx] = os_find_datadir(); if (data_dir[data_dir_idx] != NULL) { data_dir_idx++; } |