aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--block.c232
-rw-r--r--block/Makefile.objs1
-rw-r--r--block/blkdebug.c3
-rw-r--r--block/blkverify.c114
-rw-r--r--block/bochs.c3
-rw-r--r--block/cow.c16
-rw-r--r--block/curl.c13
-rw-r--r--block/gluster.c28
-rw-r--r--block/iscsi.c142
-rw-r--r--block/nbd.c43
-rw-r--r--block/parallels.c3
-rw-r--r--block/qcow.c20
-rw-r--r--block/qcow2-cluster.c22
-rw-r--r--block/qcow2.c20
-rw-r--r--block/qed.c24
-rw-r--r--block/quorum.c870
-rw-r--r--block/sheepdog.c7
-rw-r--r--block/vdi.c29
-rw-r--r--block/vhdx.c25
-rw-r--r--block/vmdk.c142
-rw-r--r--block/vpc.c3
-rw-r--r--block/vvfat.c15
-rw-r--r--blockdev.c22
-rwxr-xr-xconfigure45
-rw-r--r--docs/qmp/qmp-events.txt36
-rw-r--r--hw/audio/hda-codec.c60
-rw-r--r--hw/block/xen_disk.c4
-rw-r--r--hw/display/qxl.c8
-rw-r--r--hw/microblaze/petalogix_ml605_mmu.c55
-rw-r--r--hw/microblaze/petalogix_s3adsp1800_mmu.c35
-rw-r--r--hw/ppc/virtex_ml507.c29
-rw-r--r--hw/xtensa/xtensa_lx60.c51
-rw-r--r--include/block/block.h13
-rw-r--r--include/block/nbd.h6
-rw-r--r--include/hw/xilinx.h90
-rw-r--r--include/monitor/monitor.h2
-rw-r--r--include/qemu-common.h2
-rw-r--r--include/qemu/option.h2
-rw-r--r--monitor.c5
-rw-r--r--nbd.c66
-rw-r--r--qapi-schema.json21
-rw-r--r--qemu-img.c145
-rw-r--r--qemu-io.c8
-rw-r--r--qemu-nbd.c54
-rw-r--r--qga/channel-win32.c20
-rw-r--r--qga/commands-posix.c39
-rw-r--r--qga/commands-win32.c34
-rw-r--r--qga/main.c17
-rw-r--r--qga/qapi-schema.json9
-rw-r--r--qga/vss-win32/provider.cpp21
-rw-r--r--qga/vss-win32/requester.cpp70
-rw-r--r--qobject/qdict.c60
-rw-r--r--target-xtensa/core-dc232b.c8
-rw-r--r--target-xtensa/core-dc233c.c8
-rw-r--r--target-xtensa/core-fsf.c8
-rw-r--r--target-xtensa/cpu.c2
-rw-r--r--target-xtensa/cpu.h4
-rw-r--r--target-xtensa/helper.h1
-rw-r--r--target-xtensa/op_helper.c5
-rw-r--r--target-xtensa/overlay_tool.h17
-rw-r--r--target-xtensa/translate.c83
-rw-r--r--tests/check-qdict.c75
-rwxr-xr-xtests/qemu-iotests/04618
-rw-r--r--tests/qemu-iotests/051.out4
-rw-r--r--tests/qemu-iotests/059.out6
-rwxr-xr-xtests/qemu-iotests/081146
-rw-r--r--tests/qemu-iotests/081.out49
-rwxr-xr-xtests/qemu-iotests/082208
-rw-r--r--tests/qemu-iotests/082.out529
-rw-r--r--tests/qemu-iotests/group2
-rw-r--r--tests/tcg/xtensa/Makefile12
-rw-r--r--tests/tcg/xtensa/macros.inc6
-rw-r--r--tests/tcg/xtensa/test_b.S2
-rw-r--r--tests/tcg/xtensa/test_bi.S2
-rw-r--r--tests/tcg/xtensa/test_boolean.S2
-rw-r--r--tests/tcg/xtensa/test_break.S2
-rw-r--r--tests/tcg/xtensa/test_bz.S2
-rw-r--r--tests/tcg/xtensa/test_cache.S97
-rw-r--r--tests/tcg/xtensa/test_clamps.S2
-rw-r--r--tests/tcg/xtensa/test_extui.S2
-rw-r--r--tests/tcg/xtensa/test_fail.S2
-rw-r--r--tests/tcg/xtensa/test_interrupt.S2
-rw-r--r--tests/tcg/xtensa/test_loop.S2
-rw-r--r--tests/tcg/xtensa/test_mac16.S2
-rw-r--r--tests/tcg/xtensa/test_max.S2
-rw-r--r--tests/tcg/xtensa/test_min.S2
-rw-r--r--tests/tcg/xtensa/test_mmu.S6
-rw-r--r--tests/tcg/xtensa/test_mul16.S2
-rw-r--r--tests/tcg/xtensa/test_mul32.S2
-rw-r--r--tests/tcg/xtensa/test_nsa.S2
-rw-r--r--tests/tcg/xtensa/test_pipeline.S2
-rw-r--r--tests/tcg/xtensa/test_quo.S2
-rw-r--r--tests/tcg/xtensa/test_rem.S2
-rw-r--r--tests/tcg/xtensa/test_rst0.S2
-rw-r--r--tests/tcg/xtensa/test_s32c1i.S2
-rw-r--r--tests/tcg/xtensa/test_sar.S2
-rw-r--r--tests/tcg/xtensa/test_sext.S2
-rw-r--r--tests/tcg/xtensa/test_shift.S2
-rw-r--r--tests/tcg/xtensa/test_sr.S2
-rw-r--r--tests/tcg/xtensa/test_timer.S2
-rw-r--r--tests/tcg/xtensa/test_windowed.S2
-rw-r--r--util/iov.c106
-rw-r--r--util/module.c2
-rw-r--r--util/qemu-config.c6
-rw-r--r--util/qemu-option.c49
105 files changed, 3397 insertions, 918 deletions
diff --git a/block.c b/block.c
index 6f4bacaa58..2fd5482572 100644
--- a/block.c
+++ b/block.c
@@ -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 673aa7c8c9..fd88c03ece 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
diff --git a/block/blkdebug.c b/block/blkdebug.c
index ee10013362..380c736101 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 1563c88324..b98b08bedf 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 51d9a90577..4d6403f904 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 7fc0b12163..30deb88deb 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 bb1fc4ae28..3494c6d662 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 58eab07829..14d390b4c7 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 f8e496f8ef..41ec09709d 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 abae506f04..55124239df 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 2121e43204..3f588f58dc 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 948b0c5601..1e128becf0 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 c57f39dd2b..36c1bed350 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 b1dbdb120e..cfe80befa0 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 b9ca7ac0da..8802ad3845 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 0000000000..6c28239718
--- /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 e6c0376566..f7bd0242e5 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 2d7490f173..ae49cd83ca 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 55689cf641..5390ba6d0f 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 ff6f5ee911..83839f9b7a 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 1d326cbf44..82bf2485a5 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 a19e4ca227..f966ea5da8 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;
}
diff --git a/blockdev.c b/blockdev.c
index 3cc8cda2bd..357f7607ff 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -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;
}
diff --git a/configure b/configure
index 9ad3ff3aeb..2b4e1af7eb 100755
--- a/configure
+++ b/configure
@@ -302,6 +302,7 @@ gtkabi="2.0"
tpm="no"
libssh2=""
vhdx=""
+quorum="no"
# parse CC options first
for opt do
@@ -1046,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
@@ -1303,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
@@ -1417,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
@@ -2034,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
@@ -2460,8 +2494,12 @@ if test "$mingw32" = yes; then
else
glib_req_ver=2.12
fi
+glib_modules=gthread-2.0
+if test "$modules" = yes; then
+ glib_modules="$glib_modules gmodule-2.0"
+fi
-for i in gthread-2.0 gmodule-2.0; do
+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`
@@ -4006,6 +4044,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"
@@ -4424,6 +4463,10 @@ if test "$libssh2" = "yes" ; then
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
echo 'CONFIG_VIRTIO_BLK_DATA_PLANE=$(CONFIG_VIRTIO)' >> $config_host_mak
fi
diff --git a/docs/qmp/qmp-events.txt b/docs/qmp/qmp-events.txt
index a378c87583..00f95154dd 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/audio/hda-codec.c b/hw/audio/hda-codec.c
index 986f2a9c92..a67ca91ca7 100644
--- a/hw/audio/hda-codec.c
+++ b/hw/audio/hda-codec.c
@@ -157,6 +157,9 @@ struct HDAAudioStream {
uint32_t bpos;
};
+#define TYPE_HDA_AUDIO "hda-audio"
+#define HDA_AUDIO(obj) OBJECT_CHECK(HDAAudioState, (obj), TYPE_HDA_AUDIO)
+
struct HDAAudioState {
HDACodecDevice hda;
const char *name;
@@ -288,7 +291,7 @@ static void hda_audio_setup(HDAAudioStream *st)
static void hda_audio_command(HDACodecDevice *hda, uint32_t nid, uint32_t data)
{
- HDAAudioState *a = DO_UPCAST(HDAAudioState, hda, hda);
+ HDAAudioState *a = HDA_AUDIO(hda);
HDAAudioStream *st;
const desc_node *node = NULL;
const desc_param *param;
@@ -448,7 +451,7 @@ fail:
static void hda_audio_stream(HDACodecDevice *hda, uint32_t stnr, bool running, bool output)
{
- HDAAudioState *a = DO_UPCAST(HDAAudioState, hda, hda);
+ HDAAudioState *a = HDA_AUDIO(hda);
int s;
a->running_compat[stnr] = running;
@@ -469,7 +472,7 @@ static void hda_audio_stream(HDACodecDevice *hda, uint32_t stnr, bool running, b
static int hda_audio_init(HDACodecDevice *hda, const struct desc_codec *desc)
{
- HDAAudioState *a = DO_UPCAST(HDAAudioState, hda, hda);
+ HDAAudioState *a = HDA_AUDIO(hda);
HDAAudioStream *st;
const desc_node *node;
const desc_param *param;
@@ -514,7 +517,7 @@ static int hda_audio_init(HDACodecDevice *hda, const struct desc_codec *desc)
static int hda_audio_exit(HDACodecDevice *hda)
{
- HDAAudioState *a = DO_UPCAST(HDAAudioState, hda, hda);
+ HDAAudioState *a = HDA_AUDIO(hda);
HDAAudioStream *st;
int i;
@@ -561,7 +564,7 @@ static int hda_audio_post_load(void *opaque, int version)
static void hda_audio_reset(DeviceState *dev)
{
- HDAAudioState *a = DO_UPCAST(HDAAudioState, hda.qdev, dev);
+ HDAAudioState *a = HDA_AUDIO(dev);
HDAAudioStream *st;
int i;
@@ -613,7 +616,7 @@ static Property hda_audio_properties[] = {
static int hda_audio_init_output(HDACodecDevice *hda)
{
- HDAAudioState *a = DO_UPCAST(HDAAudioState, hda, hda);
+ HDAAudioState *a = HDA_AUDIO(hda);
if (!a->mixer) {
return hda_audio_init(hda, &output_nomixemu);
@@ -624,7 +627,7 @@ static int hda_audio_init_output(HDACodecDevice *hda)
static int hda_audio_init_duplex(HDACodecDevice *hda)
{
- HDAAudioState *a = DO_UPCAST(HDAAudioState, hda, hda);
+ HDAAudioState *a = HDA_AUDIO(hda);
if (!a->mixer) {
return hda_audio_init(hda, &duplex_nomixemu);
@@ -635,7 +638,7 @@ static int hda_audio_init_duplex(HDACodecDevice *hda)
static int hda_audio_init_micro(HDACodecDevice *hda)
{
- HDAAudioState *a = DO_UPCAST(HDAAudioState, hda, hda);
+ HDAAudioState *a = HDA_AUDIO(hda);
if (!a->mixer) {
return hda_audio_init(hda, &micro_nomixemu);
@@ -644,25 +647,39 @@ static int hda_audio_init_micro(HDACodecDevice *hda)
}
}
-static void hda_audio_output_class_init(ObjectClass *klass, void *data)
+static void hda_audio_base_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
HDACodecDeviceClass *k = HDA_CODEC_DEVICE_CLASS(klass);
- k->init = hda_audio_init_output;
k->exit = hda_audio_exit;
k->command = hda_audio_command;
k->stream = hda_audio_stream;
set_bit(DEVICE_CATEGORY_SOUND, dc->categories);
- dc->desc = "HDA Audio Codec, output-only (line-out)";
dc->reset = hda_audio_reset;
dc->vmsd = &vmstate_hda_audio;
dc->props = hda_audio_properties;
}
+static const TypeInfo hda_audio_info = {
+ .name = TYPE_HDA_AUDIO,
+ .parent = TYPE_HDA_CODEC_DEVICE,
+ .class_init = hda_audio_base_class_init,
+ .abstract = true,
+};
+
+static void hda_audio_output_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ HDACodecDeviceClass *k = HDA_CODEC_DEVICE_CLASS(klass);
+
+ k->init = hda_audio_init_output;
+ dc->desc = "HDA Audio Codec, output-only (line-out)";
+}
+
static const TypeInfo hda_audio_output_info = {
.name = "hda-output",
- .parent = TYPE_HDA_CODEC_DEVICE,
+ .parent = TYPE_HDA_AUDIO,
.instance_size = sizeof(HDAAudioState),
.class_init = hda_audio_output_class_init,
};
@@ -673,19 +690,12 @@ static void hda_audio_duplex_class_init(ObjectClass *klass, void *data)
HDACodecDeviceClass *k = HDA_CODEC_DEVICE_CLASS(klass);
k->init = hda_audio_init_duplex;
- k->exit = hda_audio_exit;
- k->command = hda_audio_command;
- k->stream = hda_audio_stream;
- set_bit(DEVICE_CATEGORY_SOUND, dc->categories);
dc->desc = "HDA Audio Codec, duplex (line-out, line-in)";
- dc->reset = hda_audio_reset;
- dc->vmsd = &vmstate_hda_audio;
- dc->props = hda_audio_properties;
}
static const TypeInfo hda_audio_duplex_info = {
.name = "hda-duplex",
- .parent = TYPE_HDA_CODEC_DEVICE,
+ .parent = TYPE_HDA_AUDIO,
.instance_size = sizeof(HDAAudioState),
.class_init = hda_audio_duplex_class_init,
};
@@ -696,25 +706,19 @@ static void hda_audio_micro_class_init(ObjectClass *klass, void *data)
HDACodecDeviceClass *k = HDA_CODEC_DEVICE_CLASS(klass);
k->init = hda_audio_init_micro;
- k->exit = hda_audio_exit;
- k->command = hda_audio_command;
- k->stream = hda_audio_stream;
- set_bit(DEVICE_CATEGORY_SOUND, dc->categories);
dc->desc = "HDA Audio Codec, duplex (speaker, microphone)";
- dc->reset = hda_audio_reset;
- dc->vmsd = &vmstate_hda_audio;
- dc->props = hda_audio_properties;
}
static const TypeInfo hda_audio_micro_info = {
.name = "hda-micro",
- .parent = TYPE_HDA_CODEC_DEVICE,
+ .parent = TYPE_HDA_AUDIO,
.instance_size = sizeof(HDAAudioState),
.class_init = hda_audio_micro_class_init,
};
static void hda_audio_register_types(void)
{
+ type_register_static(&hda_audio_info);
type_register_static(&hda_audio_output_info);
type_register_static(&hda_audio_duplex_info);
type_register_static(&hda_audio_micro_info);
diff --git a/hw/block/xen_disk.c b/hw/block/xen_disk.c
index 7f0f14ae52..bc061e6403 100644
--- a/hw/block/xen_disk.c
+++ b/hw/block/xen_disk.c
@@ -824,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 1471cc093b..2a559ebcc9 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/microblaze/petalogix_ml605_mmu.c b/hw/microblaze/petalogix_ml605_mmu.c
index 37cbbfd592..40a9f5ccdb 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 f50021506c..6c45e206ec 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/ppc/virtex_ml507.c b/hw/ppc/virtex_ml507.c
index bdb057e36c..85a0e537b9 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/xtensa/xtensa_lx60.c b/hw/xtensa/xtensa_lx60.c
index 22e124d9ec..49c58d11a3 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 963a61fa4c..780f48b7b3 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 c90f5e4d9e..79502a090b 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/xilinx.h b/include/hw/xilinx.h
deleted file mode 100644
index 9d6debe4d0..0000000000
--- 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 7e5f752b7a..a49ea11eb4 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 46c2beacd2..c8a58a873a 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);
diff --git a/include/qemu/option.h b/include/qemu/option.h
index 3ea871a3ba..8c0ac3485e 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/monitor.c b/monitor.c
index de90fba41e..aebcbd8beb 100644
--- a/monitor.c
+++ b/monitor.c
@@ -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);
}
/**
diff --git a/nbd.c b/nbd.c
index 030f56b5c7..e5084b6e7c 100644
--- a/nbd.c
+++ b/nbd.c
@@ -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
diff --git a/qapi-schema.json b/qapi-schema.json
index 473c096fa9..fcb2280053 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'
} }
##
diff --git a/qemu-img.c b/qemu-img.c
index 1e5eaf0ac8..78fc86826c 100644
--- a/qemu-img.c
+++ b/qemu-img.c
@@ -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;
}
diff --git a/qemu-io.c b/qemu-io.c
index b74ac450f6..fc3860884c 100644
--- a/qemu-io.c
+++ b/qemu-io.c
@@ -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);
diff --git a/qemu-nbd.c b/qemu-nbd.c
index 2dc75aa27f..bdac1f3f1f 100644
--- a/qemu-nbd.c
+++ b/qemu-nbd.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;
@@ -598,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 8a303f35ec..0d5e5f511f 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 cae41716ca..6b5f11f83f 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 50094ddfd3..0ee07b6e23 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);
diff --git a/qga/main.c b/qga/main.c
index c58b26a9a0..cfca291b43 100644
--- a/qga/main.c
+++ b/qga/main.c
@@ -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 245f968bc2..80edca143a 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 bf42b5e95f..d5129f8f65 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 1e8dd3dfa8..922e74ddfc 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 a3924f24bd..42ec4c0d2c 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));
}
}
diff --git a/target-xtensa/core-dc232b.c b/target-xtensa/core-dc232b.c
index 0bfcf2414c..c51e11e6d7 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 738d543e53..42dd64f031 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 d4660edde9..6859bee062 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 c19d17ad04..749e20580f 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 95103e9e87..1cf5ea3aff 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 38d7157f34..322b04cd0a 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 89a72b5678..509ba49d60 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 dd4f51a7b7..4c0de7f06a 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 2d2df33115..9f5895e021 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 7a7461b0b2..2ad0f7827e 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 2d44bbb187..e0be46cf2b 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 30e2dbd6d7..7de18704f8 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 4ffeb54710..3371c867bb 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 0000000000..f053f11942
--- /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 0000000000..4fe2f95f63
--- /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 0000000000..f6eb75f624
--- /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 0000000000..28309a0327
--- /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 d8be74a17e..db127d924d 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 1b519cae45..a70c92be7e 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 c9be1ce516..4ebd30ab86 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 6cbe5f1fca..8e81f956df 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 6a5f1dffc9..4f94c0c7e6 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 50e6d2c22a..eac40e0973 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 7574cbefc8..775cd7c260 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 f9ba6e22e8..b68135011e 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 0000000000..6b2df9734b
--- /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 c186cc98d8..3efabfd9d3 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 5d55451704..c32bb824df 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 e8d1b425bc..1c26d50790 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 68b3ee1492..334ddab287 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 1c240e8e9b..5755578d01 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 5ddd160ffc..512025d842 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 2534c9d90b..3caa207ea5 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 6d9ddeb1ac..551cf591e5 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 5d87fbb703..099031fd14 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 bf94376649..98fa7042b5 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 fdaf57331b..b288ead9f6 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 a5fe5debe4..479b2e2429 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 6be6085fc3..f418c11974 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 12debf1fe0..5b3ae383d0 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 bb0d5fe202..6357e520d9 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 3eda565e8a..a73366b120 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 4536015a84..93b575db95 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 40c649ffb8..b615a55767 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 04dc6500c1..087a6333a4 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 a8e43645b7..5df9ed4b1e 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 470c03dae2..4fac46e80f 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 1041cc6658..f8c6f7423a 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 cb2d39e1fd..3de6d3763a 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
diff --git a/util/iov.c b/util/iov.c
index bb46c04e4d..03934da74d 100644
--- a/util/iov.c
+++ b/util/iov.c
@@ -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 42bc3732c9..dc08c16111 100644
--- a/util/module.c
+++ b/util/module.c
@@ -14,7 +14,9 @@
*/
#include <stdlib.h>
+#ifdef CONFIG_MODULES
#include <gmodule.h>
+#endif
#include "qemu-common.h"
#include "qemu/queue.h"
#include "qemu/module.h"
diff --git a/util/qemu-config.c b/util/qemu-config.c
index 797df71569..f6101012c0 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 fd76cd2ada..9d898af443 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
*/