summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--MAINTAINERS8
-rw-r--r--Makefile2
-rw-r--r--block.c138
-rw-r--r--block/Makefile.objs2
-rw-r--r--block/create.c76
-rw-r--r--block/crypto.c7
-rw-r--r--block/file-posix.c93
-rw-r--r--block/file-win32.c47
-rw-r--r--block/gluster.c135
-rw-r--r--block/iscsi.c6
-rw-r--r--block/nfs.c244
-rw-r--r--block/parallels.c17
-rw-r--r--block/qcow2-bitmap.c4
-rw-r--r--block/qcow2-cluster.c24
-rw-r--r--block/qcow2-refcount.c52
-rw-r--r--block/qcow2-snapshot.c24
-rw-r--r--block/qcow2.c552
-rw-r--r--block/qcow2.h12
-rw-r--r--block/qed-check.c1
-rw-r--r--block/qed-table.c26
-rw-r--r--block/qed.c66
-rw-r--r--block/rbd.c403
-rw-r--r--block/sheepdog.c321
-rw-r--r--block/ssh.c290
-rw-r--r--block/vdi.c6
-rw-r--r--block/vhdx.c7
-rw-r--r--block/vmdk.c7
-rwxr-xr-xconfigure3
-rw-r--r--default-configs/arm-softmmu.mak9
-rw-r--r--hmp-commands.hx7
-rw-r--r--hmp.c4
-rw-r--r--hw/arm/Makefile.objs31
-rw-r--r--hw/arm/boot.c4
-rw-r--r--hw/arm/fsl-imx7.c582
-rw-r--r--hw/arm/mcimx7d-sabre.c90
-rw-r--r--hw/arm/virt.c30
-rw-r--r--hw/arm/xlnx-zynqmp.c2
-rw-r--r--hw/pci-host/Makefile.objs2
-rw-r--r--hw/pci-host/designware.c754
-rw-r--r--hw/sd/Makefile.objs2
-rw-r--r--hw/sd/sd.c55
-rw-r--r--hw/sd/sdhci.c4
-rw-r--r--hw/sd/sdmmc-internal.c72
-rw-r--r--hw/sd/sdmmc-internal.h24
-rw-r--r--hw/sd/trace-events8
-rw-r--r--include/block/block.h2
-rw-r--r--include/block/block_int.h13
-rw-r--r--include/hw/arm/fsl-imx7.h222
-rw-r--r--include/hw/pci-host/designware.h102
-rw-r--r--include/hw/pci/pci_ids.h2
-rw-r--r--include/qapi/qmp/qdict.h6
-rw-r--r--include/qemu/option.h2
-rw-r--r--include/ui/console.h2
-rw-r--r--include/ui/gtk.h14
-rw-r--r--include/ui/spice-display.h9
-rw-r--r--linux-user/aarch64/target_syscall.h3
-rw-r--r--linux-user/signal.c409
-rw-r--r--linux-user/syscall.c27
-rw-r--r--migration/block.c12
-rw-r--r--migration/migration.c5
-rw-r--r--migration/ram.c8
-rw-r--r--qapi/block-core.json326
-rw-r--r--qapi/ui.json10
-rw-r--r--qobject/qdict.c34
-rw-r--r--target/arm/cpu-qom.h2
-rw-r--r--target/arm/cpu.c103
-rw-r--r--target/arm/cpu.h11
-rw-r--r--target/arm/cpu64.c113
-rw-r--r--target/arm/kvm.c51
-rw-r--r--target/arm/kvm32.c8
-rw-r--r--target/arm/kvm64.c8
-rw-r--r--target/arm/kvm_arm.h35
-rw-r--r--target/m68k/fpu_helper.c35
-rw-r--r--target/m68k/helper.h7
-rw-r--r--target/m68k/softfloat.c1019
-rw-r--r--target/m68k/softfloat.h8
-rw-r--r--target/m68k/softfloat_fpsp_tables.h374
-rw-r--r--target/m68k/translate.c21
-rw-r--r--tests/check-qdict.c129
-rw-r--r--tests/migration-test.c17
-rwxr-xr-xtests/qemu-iotests/03052
-rw-r--r--tests/qemu-iotests/030.out4
-rw-r--r--tests/qemu-iotests/049.out8
-rwxr-xr-xtests/qemu-iotests/0595
-rwxr-xr-xtests/qemu-iotests/08022
-rw-r--r--tests/qemu-iotests/080.out58
-rwxr-xr-x[-rw-r--r--]tests/qemu-iotests/0960
-rw-r--r--tests/qemu-iotests/112.out4
-rwxr-xr-x[-rw-r--r--]tests/qemu-iotests/1240
-rwxr-xr-x[-rw-r--r--]tests/qemu-iotests/1290
-rwxr-xr-x[-rw-r--r--]tests/qemu-iotests/1320
-rwxr-xr-x[-rw-r--r--]tests/qemu-iotests/1360
-rwxr-xr-x[-rw-r--r--]tests/qemu-iotests/1390
-rwxr-xr-x[-rw-r--r--]tests/qemu-iotests/1480
-rwxr-xr-x[-rw-r--r--]tests/qemu-iotests/1520
-rwxr-xr-xtests/qemu-iotests/1538
-rw-r--r--tests/qemu-iotests/153.out7
-rwxr-xr-x[-rw-r--r--]tests/qemu-iotests/1630
-rwxr-xr-xtests/qemu-iotests/20315
-rw-r--r--tests/qemu-iotests/203.out5
-rwxr-xr-x[-rw-r--r--]tests/qemu-iotests/2050
-rwxr-xr-xtests/qemu-iotests/206436
-rw-r--r--tests/qemu-iotests/206.out209
-rwxr-xr-xtests/qemu-iotests/207261
-rw-r--r--tests/qemu-iotests/207.out75
-rw-r--r--tests/qemu-iotests/group2
-rw-r--r--tests/test-qemu-opts.c253
-rw-r--r--ui/Makefile.objs25
-rw-r--r--ui/console.c34
-rw-r--r--ui/gtk-egl.c63
-rw-r--r--ui/gtk.c68
-rw-r--r--ui/spice-display.c194
-rw-r--r--ui/trace-events12
-rw-r--r--ui/vnc.c10
-rw-r--r--util/coroutine-ucontext.c4
-rw-r--r--util/qemu-option.c42
116 files changed, 7997 insertions, 1281 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index f409f3b158..354a18ce49 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1100,6 +1100,14 @@ M: Peter Crosthwaite <crosthwaite.peter@gmail.com>
S: Maintained
F: hw/ssi/xilinx_*
+SD (Secure Card)
+M: Philippe Mathieu-Daudé <f4bug@amsat.org>
+S: Odd Fixes
+F: include/hw/sd/sd*
+F: hw/sd/core.c
+F: hw/sd/sd*
+F: tests/sd*
+
USB
M: Gerd Hoffmann <kraxel@redhat.com>
S: Maintained
diff --git a/Makefile b/Makefile
index f198f19ba2..c8116694a0 100644
--- a/Makefile
+++ b/Makefile
@@ -855,7 +855,7 @@ ifneq ($(BLOBS),)
$(INSTALL_DATA) $(SRC_PATH)/pc-bios/$$x "$(DESTDIR)$(qemu_datadir)"; \
done
endif
-ifeq ($(CONFIG_GTK),y)
+ifeq ($(CONFIG_GTK),m)
$(MAKE) -C po $@
endif
$(INSTALL_DIR) "$(DESTDIR)$(qemu_datadir)/keymaps"
diff --git a/block.c b/block.c
index 4f76714f6b..75a9fd49de 100644
--- a/block.c
+++ b/block.c
@@ -34,6 +34,8 @@
#include "qapi/qmp/qdict.h"
#include "qapi/qmp/qjson.h"
#include "qapi/qmp/qstring.h"
+#include "qapi/qobject-output-visitor.h"
+#include "qapi/qapi-visit-block-core.h"
#include "sysemu/block-backend.h"
#include "sysemu/sysemu.h"
#include "qemu/notify.h"
@@ -368,7 +370,7 @@ BlockDriver *bdrv_find_format(const char *format_name)
return bdrv_do_find_format(format_name);
}
-static int bdrv_is_whitelisted(BlockDriver *drv, bool read_only)
+int bdrv_is_whitelisted(BlockDriver *drv, bool read_only)
{
static const char *whitelist_rw[] = {
CONFIG_BDRV_RW_WHITELIST
@@ -2406,6 +2408,51 @@ BdrvChild *bdrv_open_child(const char *filename,
return c;
}
+/* TODO Future callers may need to specify parent/child_role in order for
+ * option inheritance to work. Existing callers use it for the root node. */
+BlockDriverState *bdrv_open_blockdev_ref(BlockdevRef *ref, Error **errp)
+{
+ BlockDriverState *bs = NULL;
+ Error *local_err = NULL;
+ QObject *obj = NULL;
+ QDict *qdict = NULL;
+ const char *reference = NULL;
+ Visitor *v = NULL;
+
+ if (ref->type == QTYPE_QSTRING) {
+ reference = ref->u.reference;
+ } else {
+ BlockdevOptions *options = &ref->u.definition;
+ assert(ref->type == QTYPE_QDICT);
+
+ v = qobject_output_visitor_new(&obj);
+ visit_type_BlockdevOptions(v, NULL, &options, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ goto fail;
+ }
+ visit_complete(v, &obj);
+
+ qdict = qobject_to_qdict(obj);
+ qdict_flatten(qdict);
+
+ /* bdrv_open_inherit() defaults to the values in bdrv_flags (for
+ * compatibility with other callers) rather than what we want as the
+ * real defaults. Apply the defaults here instead. */
+ qdict_set_default_str(qdict, BDRV_OPT_CACHE_DIRECT, "off");
+ qdict_set_default_str(qdict, BDRV_OPT_CACHE_NO_FLUSH, "off");
+ qdict_set_default_str(qdict, BDRV_OPT_READ_ONLY, "off");
+ }
+
+ bs = bdrv_open_inherit(NULL, reference, qdict, 0, NULL, NULL, errp);
+ obj = NULL;
+
+fail:
+ qobject_decref(obj);
+ visit_free(v);
+ return bs;
+}
+
static BlockDriverState *bdrv_append_temp_snapshot(BlockDriverState *bs,
int flags,
QDict *snapshot_options,
@@ -3455,17 +3502,54 @@ static void bdrv_delete(BlockDriverState *bs)
* free of errors) or -errno when an internal error occurred. The results of the
* check are stored in res.
*/
-int bdrv_check(BlockDriverState *bs, BdrvCheckResult *res, BdrvCheckMode fix)
+static int coroutine_fn bdrv_co_check(BlockDriverState *bs,
+ BdrvCheckResult *res, BdrvCheckMode fix)
{
if (bs->drv == NULL) {
return -ENOMEDIUM;
}
- if (bs->drv->bdrv_check == NULL) {
+ if (bs->drv->bdrv_co_check == NULL) {
return -ENOTSUP;
}
memset(res, 0, sizeof(*res));
- return bs->drv->bdrv_check(bs, res, fix);
+ return bs->drv->bdrv_co_check(bs, res, fix);
+}
+
+typedef struct CheckCo {
+ BlockDriverState *bs;
+ BdrvCheckResult *res;
+ BdrvCheckMode fix;
+ int ret;
+} CheckCo;
+
+static void bdrv_check_co_entry(void *opaque)
+{
+ CheckCo *cco = opaque;
+ cco->ret = bdrv_co_check(cco->bs, cco->res, cco->fix);
+}
+
+int bdrv_check(BlockDriverState *bs,
+ BdrvCheckResult *res, BdrvCheckMode fix)
+{
+ Coroutine *co;
+ CheckCo cco = {
+ .bs = bs,
+ .res = res,
+ .ret = -EINPROGRESS,
+ .fix = fix,
+ };
+
+ if (qemu_in_coroutine()) {
+ /* Fast-path if already in coroutine context */
+ bdrv_check_co_entry(&cco);
+ } else {
+ co = qemu_coroutine_create(bdrv_check_co_entry, &cco);
+ qemu_coroutine_enter(co);
+ BDRV_POLL_WHILE(bs, cco.ret == -EINPROGRESS);
+ }
+
+ return cco.ret;
}
/*
@@ -3635,6 +3719,11 @@ int bdrv_truncate(BdrvChild *child, int64_t offset, PreallocMode prealloc,
error_setg(errp, "No medium inserted");
return -ENOMEDIUM;
}
+ if (offset < 0) {
+ error_setg(errp, "Image size cannot be negative");
+ return -EINVAL;
+ }
+
if (!drv->bdrv_truncate) {
if (bs->file && drv->is_filter) {
return bdrv_truncate(bs->file, offset, prealloc, errp);
@@ -4209,7 +4298,8 @@ void bdrv_init_with_whitelist(void)
bdrv_init();
}
-void bdrv_invalidate_cache(BlockDriverState *bs, Error **errp)
+static void coroutine_fn bdrv_co_invalidate_cache(BlockDriverState *bs,
+ Error **errp)
{
BdrvChild *child, *parent;
uint64_t perm, shared_perm;
@@ -4225,7 +4315,7 @@ void bdrv_invalidate_cache(BlockDriverState *bs, Error **errp)
}
QLIST_FOREACH(child, &bs->children, next) {
- bdrv_invalidate_cache(child->bs, &local_err);
+ bdrv_co_invalidate_cache(child->bs, &local_err);
if (local_err) {
error_propagate(errp, local_err);
return;
@@ -4255,8 +4345,8 @@ void bdrv_invalidate_cache(BlockDriverState *bs, Error **errp)
}
bdrv_set_perm(bs, perm, shared_perm);
- if (bs->drv->bdrv_invalidate_cache) {
- bs->drv->bdrv_invalidate_cache(bs, &local_err);
+ if (bs->drv->bdrv_co_invalidate_cache) {
+ bs->drv->bdrv_co_invalidate_cache(bs, &local_err);
if (local_err) {
bs->open_flags |= BDRV_O_INACTIVE;
error_propagate(errp, local_err);
@@ -4282,6 +4372,38 @@ void bdrv_invalidate_cache(BlockDriverState *bs, Error **errp)
}
}
+typedef struct InvalidateCacheCo {
+ BlockDriverState *bs;
+ Error **errp;
+ bool done;
+} InvalidateCacheCo;
+
+static void coroutine_fn bdrv_invalidate_cache_co_entry(void *opaque)
+{
+ InvalidateCacheCo *ico = opaque;
+ bdrv_co_invalidate_cache(ico->bs, ico->errp);
+ ico->done = true;
+}
+
+void bdrv_invalidate_cache(BlockDriverState *bs, Error **errp)
+{
+ Coroutine *co;
+ InvalidateCacheCo ico = {
+ .bs = bs,
+ .done = false,
+ .errp = errp
+ };
+
+ if (qemu_in_coroutine()) {
+ /* Fast-path if already in coroutine context */
+ bdrv_invalidate_cache_co_entry(&ico);
+ } else {
+ co = qemu_coroutine_create(bdrv_invalidate_cache_co_entry, &ico);
+ qemu_coroutine_enter(co);
+ BDRV_POLL_WHILE(bs, !ico.done);
+ }
+}
+
void bdrv_invalidate_cache_all(Error **errp)
{
BlockDriverState *bs;
diff --git a/block/Makefile.objs b/block/Makefile.objs
index aede94f105..d644bac60a 100644
--- a/block/Makefile.objs
+++ b/block/Makefile.objs
@@ -9,7 +9,7 @@ block-obj-y += block-backend.o snapshot.o qapi.o
block-obj-$(CONFIG_WIN32) += file-win32.o win32-aio.o
block-obj-$(CONFIG_POSIX) += file-posix.o
block-obj-$(CONFIG_LINUX_AIO) += linux-aio.o
-block-obj-y += null.o mirror.o commit.o io.o
+block-obj-y += null.o mirror.o commit.o io.o create.o
block-obj-y += throttle-groups.o
block-obj-$(CONFIG_LINUX) += nvme.o
diff --git a/block/create.c b/block/create.c
new file mode 100644
index 0000000000..8bd8a03719
--- /dev/null
+++ b/block/create.c
@@ -0,0 +1,76 @@
+/*
+ * Block layer code related to image creation
+ *
+ * Copyright (c) 2018 Kevin Wolf <kwolf@redhat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu/osdep.h"
+#include "block/block_int.h"
+#include "qapi/qapi-commands-block-core.h"
+#include "qapi/error.h"
+
+typedef struct BlockdevCreateCo {
+ BlockDriver *drv;
+ BlockdevCreateOptions *opts;
+ int ret;
+ Error **errp;
+} BlockdevCreateCo;
+
+static void coroutine_fn bdrv_co_create_co_entry(void *opaque)
+{
+ BlockdevCreateCo *cco = opaque;
+ cco->ret = cco->drv->bdrv_co_create(cco->opts, cco->errp);
+}
+
+void qmp_x_blockdev_create(BlockdevCreateOptions *options, Error **errp)
+{
+ const char *fmt = BlockdevDriver_str(options->driver);
+ BlockDriver *drv = bdrv_find_format(fmt);
+ Coroutine *co;
+ BlockdevCreateCo cco;
+
+ /* If the driver is in the schema, we know that it exists. But it may not
+ * be whitelisted. */
+ assert(drv);
+ if (bdrv_uses_whitelist() && !bdrv_is_whitelisted(drv, false)) {
+ error_setg(errp, "Driver is not whitelisted");
+ return;
+ }
+
+ /* Call callback if it exists */
+ if (!drv->bdrv_co_create) {
+ error_setg(errp, "Driver does not support blockdev-create");
+ return;
+ }
+
+ cco = (BlockdevCreateCo) {
+ .drv = drv,
+ .opts = options,
+ .ret = -EINPROGRESS,
+ .errp = errp,
+ };
+
+ co = qemu_coroutine_create(bdrv_co_create_co_entry, &cco);
+ qemu_coroutine_enter(co);
+ while (cco.ret == -EINPROGRESS) {
+ aio_poll(qemu_get_aio_context(), true);
+ }
+}
diff --git a/block/crypto.c b/block/crypto.c
index 17b5c0abad..e6095e7807 100644
--- a/block/crypto.c
+++ b/block/crypto.c
@@ -384,6 +384,12 @@ static void block_crypto_close(BlockDriverState *bs)
qcrypto_block_free(crypto->block);
}
+static int block_crypto_reopen_prepare(BDRVReopenState *state,
+ BlockReopenQueue *queue, Error **errp)
+{
+ /* nothing needs checking */
+ return 0;
+}
/*
* 1 MB bounce buffer gives good performance / memory tradeoff
@@ -621,6 +627,7 @@ BlockDriver bdrv_crypto_luks = {
.bdrv_truncate = block_crypto_truncate,
.create_opts = &block_crypto_create_opts_luks,
+ .bdrv_reopen_prepare = block_crypto_reopen_prepare,
.bdrv_refresh_limits = block_crypto_refresh_limits,
.bdrv_co_preadv = block_crypto_co_preadv,
.bdrv_co_pwritev = block_crypto_co_pwritev,
diff --git a/block/file-posix.c b/block/file-posix.c
index 7f2cc63c60..d7fb772c14 100644
--- a/block/file-posix.c
+++ b/block/file-posix.c
@@ -1686,11 +1686,15 @@ static int raw_regular_truncate(int fd, int64_t offset, PreallocMode prealloc,
* file systems that do not support fallocate(), trying to check if a
* block is allocated before allocating it, so don't do that here.
*/
- result = -posix_fallocate(fd, current_length, offset - current_length);
- if (result != 0) {
- /* posix_fallocate() doesn't set errno. */
- error_setg_errno(errp, -result,
- "Could not preallocate new data");
+ if (offset != current_length) {
+ result = -posix_fallocate(fd, current_length, offset - current_length);
+ if (result != 0) {
+ /* posix_fallocate() doesn't set errno. */
+ error_setg_errno(errp, -result,
+ "Could not preallocate new data");
+ }
+ } else {
+ result = 0;
}
goto out;
#endif
@@ -1982,34 +1986,25 @@ static int64_t raw_get_allocated_file_size(BlockDriverState *bs)
return (int64_t)st.st_blocks * 512;
}
-static int coroutine_fn raw_co_create_opts(const char *filename, QemuOpts *opts,
- Error **errp)
+static int raw_co_create(BlockdevCreateOptions *options, Error **errp)
{
+ BlockdevCreateOptionsFile *file_opts;
int fd;
int result = 0;
- int64_t total_size = 0;
- bool nocow = false;
- PreallocMode prealloc;
- char *buf = NULL;
- Error *local_err = NULL;
- strstart(filename, "file:", &filename);
+ /* Validate options and set default values */
+ assert(options->driver == BLOCKDEV_DRIVER_FILE);
+ file_opts = &options->u.file;
- /* Read out options */
- total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
- BDRV_SECTOR_SIZE);
- nocow = qemu_opt_get_bool(opts, BLOCK_OPT_NOCOW, false);
- buf = qemu_opt_get_del(opts, BLOCK_OPT_PREALLOC);
- prealloc = qapi_enum_parse(&PreallocMode_lookup, buf,
- PREALLOC_MODE_OFF, &local_err);
- g_free(buf);
- if (local_err) {
- error_propagate(errp, local_err);
- result = -EINVAL;
- goto out;
+ if (!file_opts->has_nocow) {
+ file_opts->nocow = false;
+ }
+ if (!file_opts->has_preallocation) {
+ file_opts->preallocation = PREALLOC_MODE_OFF;
}
- fd = qemu_open(filename, O_RDWR | O_CREAT | O_TRUNC | O_BINARY,
+ /* Create file */
+ fd = qemu_open(file_opts->filename, O_RDWR | O_CREAT | O_TRUNC | O_BINARY,
0644);
if (fd < 0) {
result = -errno;
@@ -2017,7 +2012,7 @@ static int coroutine_fn raw_co_create_opts(const char *filename, QemuOpts *opts,
goto out;
}
- if (nocow) {
+ if (file_opts->nocow) {
#ifdef __linux__
/* Set NOCOW flag to solve performance issue on fs like btrfs.
* This is an optimisation. The FS_IOC_SETFLAGS ioctl return value
@@ -2032,7 +2027,8 @@ static int coroutine_fn raw_co_create_opts(const char *filename, QemuOpts *opts,
#endif
}
- result = raw_regular_truncate(fd, total_size, prealloc, errp);
+ result = raw_regular_truncate(fd, file_opts->size, file_opts->preallocation,
+ errp);
if (result < 0) {
goto out_close;
}
@@ -2046,6 +2042,46 @@ out:
return result;
}
+static int coroutine_fn raw_co_create_opts(const char *filename, QemuOpts *opts,
+ Error **errp)
+{
+ BlockdevCreateOptions options;
+ int64_t total_size = 0;
+ bool nocow = false;
+ PreallocMode prealloc;
+ char *buf = NULL;
+ Error *local_err = NULL;
+
+ /* Skip file: protocol prefix */
+ strstart(filename, "file:", &filename);
+
+ /* Read out options */
+ total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
+ BDRV_SECTOR_SIZE);
+ nocow = qemu_opt_get_bool(opts, BLOCK_OPT_NOCOW, false);
+ buf = qemu_opt_get_del(opts, BLOCK_OPT_PREALLOC);
+ prealloc = qapi_enum_parse(&PreallocMode_lookup, buf,
+ PREALLOC_MODE_OFF, &local_err);
+ g_free(buf);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return -EINVAL;
+ }
+
+ options = (BlockdevCreateOptions) {
+ .driver = BLOCKDEV_DRIVER_FILE,
+ .u.file = {
+ .filename = (char *) filename,
+ .size = total_size,
+ .has_preallocation = true,
+ .preallocation = prealloc,
+ .has_nocow = true,
+ .nocow = nocow,
+ },
+ };
+ return raw_co_create(&options, errp);
+}
+
/*
* Find allocation range in @bs around offset @start.
* May change underlying file descriptor's file offset.
@@ -2277,6 +2313,7 @@ BlockDriver bdrv_file = {
.bdrv_reopen_commit = raw_reopen_commit,
.bdrv_reopen_abort = raw_reopen_abort,
.bdrv_close = raw_close,
+ .bdrv_co_create = raw_co_create,
.bdrv_co_create_opts = raw_co_create_opts,
.bdrv_has_zero_init = bdrv_has_zero_init_1,
.bdrv_co_block_status = raw_co_block_status,
diff --git a/block/file-win32.c b/block/file-win32.c
index 4a430d45f1..2e2f746bb1 100644
--- a/block/file-win32.c
+++ b/block/file-win32.c
@@ -553,30 +553,59 @@ static int64_t raw_get_allocated_file_size(BlockDriverState *bs)
return st.st_size;
}
-static int coroutine_fn raw_co_create_opts(const char *filename, QemuOpts *opts,
- Error **errp)
+static int raw_co_create(BlockdevCreateOptions *options, Error **errp)
{
+ BlockdevCreateOptionsFile *file_opts;
int fd;
- int64_t total_size = 0;
- strstart(filename, "file:", &filename);
+ assert(options->driver == BLOCKDEV_DRIVER_FILE);
+ file_opts = &options->u.file;
- /* Read out options */
- total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
- BDRV_SECTOR_SIZE);
+ if (file_opts->has_preallocation) {
+ error_setg(errp, "Preallocation is not supported on Windows");
+ return -EINVAL;
+ }
+ if (file_opts->has_nocow) {
+ error_setg(errp, "nocow is not supported on Windows");
+ return -EINVAL;
+ }
- fd = qemu_open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
+ fd = qemu_open(file_opts->filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
0644);
if (fd < 0) {
error_setg_errno(errp, errno, "Could not create file");
return -EIO;
}
set_sparse(fd);
- ftruncate(fd, total_size);
+ ftruncate(fd, file_opts->size);
qemu_close(fd);
+
return 0;
}
+static int coroutine_fn raw_co_create_opts(const char *filename, QemuOpts *opts,
+ Error **errp)
+{
+ BlockdevCreateOptions options;
+ int64_t total_size = 0;
+
+ strstart(filename, "file:", &filename);
+
+ /* Read out options */
+ total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
+ BDRV_SECTOR_SIZE);
+
+ options = (BlockdevCreateOptions) {
+ .driver = BLOCKDEV_DRIVER_FILE,
+ .u.file = {
+ .filename = (char *) filename,
+ .size = total_size,
+ .has_preallocation = false,
+ .has_nocow = false,
+ },
+ };
+ return raw_co_create(&options, errp);
+}
static QemuOptsList raw_create_opts = {
.name = "raw-create-opts",
diff --git a/block/gluster.c b/block/gluster.c
index 79b4cfdf74..63d3c37d4c 100644
--- a/block/gluster.c
+++ b/block/gluster.c
@@ -655,9 +655,11 @@ out:
return -errno;
}
-static struct glfs *qemu_gluster_init(BlockdevOptionsGluster *gconf,
- const char *filename,
- QDict *options, Error **errp)
+/* Converts options given in @filename and the @options QDict into the QAPI
+ * object @gconf. */
+static int qemu_gluster_parse(BlockdevOptionsGluster *gconf,
+ const char *filename,
+ QDict *options, Error **errp)
{
int ret;
if (filename) {
@@ -668,8 +670,7 @@ static struct glfs *qemu_gluster_init(BlockdevOptionsGluster *gconf,
"[host[:port]]volume/path[?socket=...]"
"[,file.debug=N]"
"[,file.logfile=/path/filename.log]\n");
- errno = -ret;
- return NULL;
+ return ret;
}
} else {
ret = qemu_gluster_parse_json(gconf, options, errp);
@@ -685,10 +686,23 @@ static struct glfs *qemu_gluster_init(BlockdevOptionsGluster *gconf,
"file.server.1.transport=unix,"
"file.server.1.socket=/var/run/glusterd.socket ..."
"\n");
- errno = -ret;
- return NULL;
+ return ret;
}
+ }
+ return 0;
+}
+
+static struct glfs *qemu_gluster_init(BlockdevOptionsGluster *gconf,
+ const char *filename,
+ QDict *options, Error **errp)
+{
+ int ret;
+
+ ret = qemu_gluster_parse(gconf, filename, options, errp);
+ if (ret < 0) {
+ errno = -ret;
+ return NULL;
}
return qemu_gluster_glfs_init(gconf, errp);
@@ -1021,20 +1035,72 @@ static int qemu_gluster_do_truncate(struct glfs_fd *fd, int64_t offset,
return 0;
}
+static int qemu_gluster_co_create(BlockdevCreateOptions *options,
+ Error **errp)
+{
+ BlockdevCreateOptionsGluster *opts = &options->u.gluster;
+ struct glfs *glfs;
+ struct glfs_fd *fd = NULL;
+ int ret = 0;
+
+ assert(options->driver == BLOCKDEV_DRIVER_GLUSTER);
+
+ glfs = qemu_gluster_glfs_init(opts->location, errp);
+ if (!glfs) {
+ ret = -errno;
+ goto out;
+ }
+
+ fd = glfs_creat(glfs, opts->location->path,
+ O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, S_IRUSR | S_IWUSR);
+ if (!fd) {
+ ret = -errno;
+ goto out;
+ }
+
+ ret = qemu_gluster_do_truncate(fd, opts->size, opts->preallocation, errp);
+
+out:
+ if (fd) {
+ if (glfs_close(fd) != 0 && ret == 0) {
+ ret = -errno;
+ }
+ }
+ glfs_clear_preopened(glfs);
+ return ret;
+}
+
static int coroutine_fn qemu_gluster_co_create_opts(const char *filename,
QemuOpts *opts,
Error **errp)
{
+ BlockdevCreateOptions *options;
+ BlockdevCreateOptionsGluster *gopts;
BlockdevOptionsGluster *gconf;
- struct glfs *glfs;
- struct glfs_fd *fd = NULL;
- int ret = 0;
- PreallocMode prealloc;
- int64_t total_size = 0;
char *tmp = NULL;
Error *local_err = NULL;
+ int ret;
+
+ options = g_new0(BlockdevCreateOptions, 1);
+ options->driver = BLOCKDEV_DRIVER_GLUSTER;
+ gopts = &options->u.gluster;
gconf = g_new0(BlockdevOptionsGluster, 1);
+ gopts->location = gconf;
+
+ gopts->size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
+ BDRV_SECTOR_SIZE);
+
+ tmp = qemu_opt_get_del(opts, BLOCK_OPT_PREALLOC);
+ gopts->preallocation = qapi_enum_parse(&PreallocMode_lookup, tmp,
+ PREALLOC_MODE_OFF, &local_err);
+ g_free(tmp);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ ret = -EINVAL;
+ goto fail;
+ }
+
gconf->debug = qemu_opt_get_number_del(opts, GLUSTER_OPT_DEBUG,
GLUSTER_DEBUG_DEFAULT);
if (gconf->debug < 0) {
@@ -1050,42 +1116,19 @@ static int coroutine_fn qemu_gluster_co_create_opts(const char *filename,
}
gconf->has_logfile = true;
- glfs = qemu_gluster_init(gconf, filename, NULL, errp);
- if (!glfs) {
- ret = -errno;
- goto out;
- }
-
- total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
- BDRV_SECTOR_SIZE);
-
- tmp = qemu_opt_get_del(opts, BLOCK_OPT_PREALLOC);
- prealloc = qapi_enum_parse(&PreallocMode_lookup, tmp, PREALLOC_MODE_OFF,
- &local_err);
- g_free(tmp);
- if (local_err) {
- error_propagate(errp, local_err);
- ret = -EINVAL;
- goto out;
+ ret = qemu_gluster_parse(gconf, filename, NULL, errp);
+ if (ret < 0) {
+ goto fail;
}
- fd = glfs_creat(glfs, gconf->path,
- O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, S_IRUSR | S_IWUSR);
- if (!fd) {
- ret = -errno;
- goto out;
+ ret = qemu_gluster_co_create(options, errp);
+ if (ret < 0) {
+ goto fail;
}
- ret = qemu_gluster_do_truncate(fd, total_size, prealloc, errp);
-
-out:
- if (fd) {
- if (glfs_close(fd) != 0 && ret == 0) {
- ret = -errno;
- }
- }
- qapi_free_BlockdevOptionsGluster(gconf);
- glfs_clear_preopened(glfs);
+ ret = 0;
+fail:
+ qapi_free_BlockdevCreateOptions(options);
return ret;
}
@@ -1436,6 +1479,7 @@ static BlockDriver bdrv_gluster = {
.bdrv_reopen_commit = qemu_gluster_reopen_commit,
.bdrv_reopen_abort = qemu_gluster_reopen_abort,
.bdrv_close = qemu_gluster_close,
+ .bdrv_co_create = qemu_gluster_co_create,
.bdrv_co_create_opts = qemu_gluster_co_create_opts,
.bdrv_getlength = qemu_gluster_getlength,
.bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size,
@@ -1464,6 +1508,7 @@ static BlockDriver bdrv_gluster_tcp = {
.bdrv_reopen_commit = qemu_gluster_reopen_commit,
.bdrv_reopen_abort = qemu_gluster_reopen_abort,
.bdrv_close = qemu_gluster_close,
+ .bdrv_co_create = qemu_gluster_co_create,
.bdrv_co_create_opts = qemu_gluster_co_create_opts,
.bdrv_getlength = qemu_gluster_getlength,
.bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size,
@@ -1492,6 +1537,7 @@ static BlockDriver bdrv_gluster_unix = {
.bdrv_reopen_commit = qemu_gluster_reopen_commit,
.bdrv_reopen_abort = qemu_gluster_reopen_abort,
.bdrv_close = qemu_gluster_close,
+ .bdrv_co_create = qemu_gluster_co_create,
.bdrv_co_create_opts = qemu_gluster_co_create_opts,
.bdrv_getlength = qemu_gluster_getlength,
.bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size,
@@ -1526,6 +1572,7 @@ static BlockDriver bdrv_gluster_rdma = {
.bdrv_reopen_commit = qemu_gluster_reopen_commit,
.bdrv_reopen_abort = qemu_gluster_reopen_abort,
.bdrv_close = qemu_gluster_close,
+ .bdrv_co_create = qemu_gluster_co_create,
.bdrv_co_create_opts = qemu_gluster_co_create_opts,
.bdrv_getlength = qemu_gluster_getlength,
.bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size,
diff --git a/block/iscsi.c b/block/iscsi.c
index 8bf0e87244..a82170f16e 100644
--- a/block/iscsi.c
+++ b/block/iscsi.c
@@ -2177,8 +2177,8 @@ static int iscsi_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
return 0;
}
-static void iscsi_invalidate_cache(BlockDriverState *bs,
- Error **errp)
+static void coroutine_fn iscsi_co_invalidate_cache(BlockDriverState *bs,
+ Error **errp)
{
IscsiLun *iscsilun = bs->opaque;
iscsi_allocmap_invalidate(iscsilun);
@@ -2209,7 +2209,7 @@ static BlockDriver bdrv_iscsi = {
.create_opts = &iscsi_create_opts,
.bdrv_reopen_prepare = iscsi_reopen_prepare,
.bdrv_reopen_commit = iscsi_reopen_commit,
- .bdrv_invalidate_cache = iscsi_invalidate_cache,
+ .bdrv_co_invalidate_cache = iscsi_co_invalidate_cache,
.bdrv_getlength = iscsi_getlength,
.bdrv_get_info = iscsi_get_info,
diff --git a/block/nfs.c b/block/nfs.c
index 1d82ff5042..2577df4b26 100644
--- a/block/nfs.c
+++ b/block/nfs.c
@@ -367,49 +367,6 @@ static int coroutine_fn nfs_co_flush(BlockDriverState *bs)
return task.ret;
}
-static QemuOptsList runtime_opts = {
- .name = "nfs",
- .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
- .desc = {
- {
- .name = "path",
- .type = QEMU_OPT_STRING,
- .help = "Path of the image on the host",
- },
- {
- .name = "user",
- .type = QEMU_OPT_NUMBER,
- .help = "UID value to use when talking to the server",
- },
- {
- .name = "group",
- .type = QEMU_OPT_NUMBER,
- .help = "GID value to use when talking to the server",
- },
- {
- .name = "tcp-syn-count",
- .type = QEMU_OPT_NUMBER,
- .help = "Number of SYNs to send during the session establish",
- },
- {
- .name = "readahead-size",
- .type = QEMU_OPT_NUMBER,
- .help = "Set the readahead size in bytes",
- },
- {
- .name = "page-cache-size",
- .type = QEMU_OPT_NUMBER,
- .help = "Set the pagecache size in bytes",
- },
- {
- .name = "debug",
- .type = QEMU_OPT_NUMBER,
- .help = "Set the NFS debug level (max 2)",
- },
- { /* end of list */ }
- },
-};
-
static void nfs_detach_aio_context(BlockDriverState *bs)
{
NFSClient *client = bs->opaque;
@@ -452,71 +409,16 @@ static void nfs_file_close(BlockDriverState *bs)
nfs_client_close(client);
}
-static NFSServer *nfs_config(QDict *options, Error **errp)
-{
- NFSServer *server = NULL;
- QDict *addr = NULL;
- QObject *crumpled_addr = NULL;
- Visitor *iv = NULL;
- Error *local_error = NULL;
-
- qdict_extract_subqdict(options, &addr, "server.");
- if (!qdict_size(addr)) {
- error_setg(errp, "NFS server address missing");
- goto out;
- }
-
- crumpled_addr = qdict_crumple(addr, errp);
- if (!crumpled_addr) {
- goto out;
- }
-
- /*
- * Caution: this works only because all scalar members of
- * NFSServer are QString in @crumpled_addr. The visitor expects
- * @crumpled_addr to be typed according to the QAPI schema. It
- * is when @options come from -blockdev or blockdev_add. But when
- * they come from -drive, they're all QString.
- */
- iv = qobject_input_visitor_new(crumpled_addr);
- visit_type_NFSServer(iv, NULL, &server, &local_error);
- if (local_error) {
- error_propagate(errp, local_error);
- goto out;
- }
-
-out:
- QDECREF(addr);
- qobject_decref(crumpled_addr);
- visit_free(iv);
- return server;
-}
-
-
-static int64_t nfs_client_open(NFSClient *client, QDict *options,
+static int64_t nfs_client_open(NFSClient *client, BlockdevOptionsNfs *opts,
int flags, int open_flags, Error **errp)
{
int64_t ret = -EINVAL;
- QemuOpts *opts = NULL;
- Error *local_err = NULL;
struct stat st;
char *file = NULL, *strp = NULL;
qemu_mutex_init(&client->mutex);
- opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
- qemu_opts_absorb_qdict(opts, options, &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
- ret = -EINVAL;
- goto fail;
- }
- client->path = g_strdup(qemu_opt_get(opts, "path"));
- if (!client->path) {
- ret = -EINVAL;
- error_setg(errp, "No path was specified");
- goto fail;
- }
+ client->path = g_strdup(opts->path);
strp = strrchr(client->path, '/');
if (strp == NULL) {
@@ -526,12 +428,10 @@ static int64_t nfs_client_open(NFSClient *client, QDict *options,
file = g_strdup(strp);
*strp = 0;
- /* Pop the config into our state object, Exit if invalid */
- client->server = nfs_config(options, errp);
- if (!client->server) {
- ret = -EINVAL;
- goto fail;
- }
+ /* Steal the NFSServer object from opts; set the original pointer to NULL
+ * to avoid use after free and double free. */
+ client->server = opts->server;
+ opts->server = NULL;
client->context = nfs_init_context();
if (client->context == NULL) {
@@ -539,29 +439,29 @@ static int64_t nfs_client_open(NFSClient *client, QDict *options,
goto fail;
}
- if (qemu_opt_get(opts, "user")) {
- client->uid = qemu_opt_get_number(opts, "user", 0);
+ if (opts->has_user) {
+ client->uid = opts->user;
nfs_set_uid(client->context, client->uid);
}
- if (qemu_opt_get(opts, "group")) {
- client->gid = qemu_opt_get_number(opts, "group", 0);
+ if (opts->has_group) {
+ client->gid = opts->group;
nfs_set_gid(client->context, client->gid);
}
- if (qemu_opt_get(opts, "tcp-syn-count")) {
- client->tcp_syncnt = qemu_opt_get_number(opts, "tcp-syn-count", 0);
+ if (opts->has_tcp_syn_count) {
+ client->tcp_syncnt = opts->tcp_syn_count;
nfs_set_tcp_syncnt(client->context, client->tcp_syncnt);
}
#ifdef LIBNFS_FEATURE_READAHEAD
- if (qemu_opt_get(opts, "readahead-size")) {
+ if (opts->has_readahead_size) {
if (open_flags & BDRV_O_NOCACHE) {
error_setg(errp, "Cannot enable NFS readahead "
"if cache.direct = on");
goto fail;
}
- client->readahead = qemu_opt_get_number(opts, "readahead-size", 0);
+ client->readahead = opts->readahead_size;
if (client->readahead > QEMU_NFS_MAX_READAHEAD_SIZE) {
warn_report("Truncating NFS readahead size to %d",
QEMU_NFS_MAX_READAHEAD_SIZE);
@@ -576,13 +476,13 @@ static int64_t nfs_client_open(NFSClient *client, QDict *options,
#endif
#ifdef LIBNFS_FEATURE_PAGECACHE
- if (qemu_opt_get(opts, "page-cache-size")) {
+ if (opts->has_page_cache_size) {
if (open_flags & BDRV_O_NOCACHE) {
error_setg(errp, "Cannot enable NFS pagecache "
"if cache.direct = on");
goto fail;
}
- client->pagecache = qemu_opt_get_number(opts, "page-cache-size", 0);
+ client->pagecache = opts->page_cache_size;
if (client->pagecache > QEMU_NFS_MAX_PAGECACHE_SIZE) {
warn_report("Truncating NFS pagecache size to %d pages",
QEMU_NFS_MAX_PAGECACHE_SIZE);
@@ -595,8 +495,8 @@ static int64_t nfs_client_open(NFSClient *client, QDict *options,
#endif
#ifdef LIBNFS_FEATURE_DEBUG
- if (qemu_opt_get(opts, "debug")) {
- client->debug = qemu_opt_get_number(opts, "debug", 0);
+ if (opts->has_debug) {
+ client->debug = opts->debug;
/* limit the maximum debug level to avoid potential flooding
* of our log files. */
if (client->debug > QEMU_NFS_MAX_DEBUG_LEVEL) {
@@ -647,11 +547,53 @@ static int64_t nfs_client_open(NFSClient *client, QDict *options,
fail:
nfs_client_close(client);
out:
- qemu_opts_del(opts);
g_free(file);
return ret;
}
+static BlockdevOptionsNfs *nfs_options_qdict_to_qapi(QDict *options,
+ Error **errp)
+{
+ BlockdevOptionsNfs *opts = NULL;
+ QObject *crumpled = NULL;
+ Visitor *v;
+ Error *local_err = NULL;
+
+ crumpled = qdict_crumple(options, errp);
+ if (crumpled == NULL) {
+ return NULL;
+ }
+
+ v = qobject_input_visitor_new_keyval(crumpled);
+ visit_type_BlockdevOptionsNfs(v, NULL, &opts, &local_err);
+ visit_free(v);
+ qobject_decref(crumpled);
+
+ if (local_err) {
+ return NULL;
+ }
+
+ return opts;
+}
+
+static int64_t nfs_client_open_qdict(NFSClient *client, QDict *options,
+ int flags, int open_flags, Error **errp)
+{
+ BlockdevOptionsNfs *opts;
+ int ret;
+
+ opts = nfs_options_qdict_to_qapi(options, errp);
+ if (opts == NULL) {
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ ret = nfs_client_open(client, opts, flags, open_flags, errp);
+fail:
+ qapi_free_BlockdevOptionsNfs(opts);
+ return ret;
+}
+
static int nfs_file_open(BlockDriverState *bs, QDict *options, int flags,
Error **errp) {
NFSClient *client = bs->opaque;
@@ -659,9 +601,9 @@ static int nfs_file_open(BlockDriverState *bs, QDict *options, int flags,
client->aio_context = bdrv_get_aio_context(bs);
- ret = nfs_client_open(client, options,
- (flags & BDRV_O_RDWR) ? O_RDWR : O_RDONLY,
- bs->open_flags, errp);
+ ret = nfs_client_open_qdict(client, options,
+ (flags & BDRV_O_RDWR) ? O_RDWR : O_RDONLY,
+ bs->open_flags, errp);
if (ret < 0) {
return ret;
}
@@ -684,18 +626,43 @@ static QemuOptsList nfs_create_opts = {
}
};
-static int coroutine_fn nfs_file_co_create_opts(const char *url, QemuOpts *opts,
- Error **errp)
+static int nfs_file_co_create(BlockdevCreateOptions *options, Error **errp)
{
- int64_t ret, total_size;
+ BlockdevCreateOptionsNfs *opts = &options->u.nfs;
NFSClient *client = g_new0(NFSClient, 1);
- QDict *options = NULL;
+ int ret;
+
+ assert(options->driver == BLOCKDEV_DRIVER_NFS);
client->aio_context = qemu_get_aio_context();
+ ret = nfs_client_open(client, opts->location, O_CREAT, 0, errp);
+ if (ret < 0) {
+ goto out;
+ }
+ ret = nfs_ftruncate(client->context, client->fh, opts->size);
+ nfs_client_close(client);
+
+out:
+ g_free(client);
+ return ret;
+}
+
+static int coroutine_fn nfs_file_co_create_opts(const char *url, QemuOpts *opts,
+ Error **errp)
+{
+ BlockdevCreateOptions *create_options;
+ BlockdevCreateOptionsNfs *nfs_opts;
+ QDict *options;
+ int ret;
+
+ create_options = g_new0(BlockdevCreateOptions, 1);
+ create_options->driver = BLOCKDEV_DRIVER_NFS;
+ nfs_opts = &create_options->u.nfs;
+
/* Read out options */
- total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
- BDRV_SECTOR_SIZE);
+ nfs_opts->size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
+ BDRV_SECTOR_SIZE);
options = qdict_new();
ret = nfs_parse_uri(url, options, errp);
@@ -703,15 +670,21 @@ static int coroutine_fn nfs_file_co_create_opts(const char *url, QemuOpts *opts,
goto out;
}
- ret = nfs_client_open(client, options, O_CREAT, 0, errp);
+ nfs_opts->location = nfs_options_qdict_to_qapi(options, errp);
+ if (nfs_opts->location == NULL) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = nfs_file_co_create(create_options, errp);
if (ret < 0) {
goto out;
}
- ret = nfs_ftruncate(client->context, client->fh, total_size);
- nfs_client_close(client);
+
+ ret = 0;
out:
QDECREF(options);
- g_free(client);
+ qapi_free_BlockdevCreateOptions(create_options);
return ret;
}
@@ -876,8 +849,8 @@ static void nfs_refresh_filename(BlockDriverState *bs, QDict *options)
}
#ifdef LIBNFS_FEATURE_PAGECACHE
-static void nfs_invalidate_cache(BlockDriverState *bs,
- Error **errp)
+static void coroutine_fn nfs_co_invalidate_cache(BlockDriverState *bs,
+ Error **errp)
{
NFSClient *client = bs->opaque;
nfs_pagecache_invalidate(client->context, client->fh);
@@ -898,6 +871,7 @@ static BlockDriver bdrv_nfs = {
.bdrv_file_open = nfs_file_open,
.bdrv_close = nfs_file_close,
+ .bdrv_co_create = nfs_file_co_create,
.bdrv_co_create_opts = nfs_file_co_create_opts,
.bdrv_reopen_prepare = nfs_reopen_prepare,
@@ -910,7 +884,7 @@ static BlockDriver bdrv_nfs = {
.bdrv_refresh_filename = nfs_refresh_filename,
#ifdef LIBNFS_FEATURE_PAGECACHE
- .bdrv_invalidate_cache = nfs_invalidate_cache,
+ .bdrv_co_invalidate_cache = nfs_co_invalidate_cache,
#endif
};
diff --git a/block/parallels.c b/block/parallels.c
index 81085795c2..c13cb619e6 100644
--- a/block/parallels.c
+++ b/block/parallels.c
@@ -378,8 +378,9 @@ static coroutine_fn int parallels_co_readv(BlockDriverState *bs,
}
-static int parallels_check(BlockDriverState *bs, BdrvCheckResult *res,
- BdrvCheckMode fix)
+static int coroutine_fn parallels_co_check(BlockDriverState *bs,
+ BdrvCheckResult *res,
+ BdrvCheckMode fix)
{
BDRVParallelsState *s = bs->opaque;
int64_t size, prev_off, high_off;
@@ -394,6 +395,7 @@ static int parallels_check(BlockDriverState *bs, BdrvCheckResult *res,
return size;
}
+ qemu_co_mutex_lock(&s->lock);
if (s->header_unclean) {
fprintf(stderr, "%s image was not closed correctly\n",
fix & BDRV_FIX_ERRORS ? "Repairing" : "ERROR");
@@ -442,11 +444,12 @@ static int parallels_check(BlockDriverState *bs, BdrvCheckResult *res,
prev_off = off;
}
+ ret = 0;
if (flush_bat) {
ret = bdrv_pwrite_sync(bs->file, 0, s->header, s->header_size);
if (ret < 0) {
res->check_errors++;
- return ret;
+ goto out;
}
}
@@ -465,13 +468,15 @@ static int parallels_check(BlockDriverState *bs, BdrvCheckResult *res,
if (ret < 0) {
error_report_err(local_err);
res->check_errors++;
- return ret;
+ goto out;
}
res->leaks_fixed += count;
}
}
- return 0;
+out:
+ qemu_co_mutex_unlock(&s->lock);
+ return ret;
}
@@ -799,7 +804,7 @@ static BlockDriver bdrv_parallels = {
.bdrv_co_writev = parallels_co_writev,
.supports_backing = true,
.bdrv_co_create_opts = parallels_co_create_opts,
- .bdrv_check = parallels_check,
+ .bdrv_co_check = parallels_co_check,
.create_opts = &parallels_create_opts,
};
diff --git a/block/qcow2-bitmap.c b/block/qcow2-bitmap.c
index 5127276f90..3010adb909 100644
--- a/block/qcow2-bitmap.c
+++ b/block/qcow2-bitmap.c
@@ -110,7 +110,7 @@ static int update_header_sync(BlockDriverState *bs)
return ret;
}
- return bdrv_flush(bs);
+ return bdrv_flush(bs->file->bs);
}
static inline void bitmap_table_to_be(uint64_t *bitmap_table, size_t size)
@@ -882,7 +882,7 @@ static int update_ext_header_and_dir(BlockDriverState *bs,
return ret;
}
- ret = bdrv_flush(bs->file->bs);
+ ret = qcow2_flush_caches(bs);
if (ret < 0) {
goto fail;
}
diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c
index 98908c4264..1aee726c6a 100644
--- a/block/qcow2-cluster.c
+++ b/block/qcow2-cluster.c
@@ -25,6 +25,7 @@
#include "qemu/osdep.h"
#include <zlib.h>
+#include "qapi/error.h"
#include "qemu-common.h"
#include "block/block_int.h"
#include "block/qcow2.h"
@@ -2092,11 +2093,21 @@ int qcow2_expand_zero_clusters(BlockDriverState *bs,
}
for (i = 0; i < s->nb_snapshots; i++) {
- int l1_sectors = DIV_ROUND_UP(s->snapshots[i].l1_size *
- sizeof(uint64_t), BDRV_SECTOR_SIZE);
+ int l1_size2;
+ uint64_t *new_l1_table;
+ Error *local_err = NULL;
+
+ ret = qcow2_validate_table(bs, s->snapshots[i].l1_table_offset,
+ s->snapshots[i].l1_size, sizeof(uint64_t),
+ QCOW_MAX_L1_SIZE, "Snapshot L1 table",
+ &local_err);
+ if (ret < 0) {
+ error_report_err(local_err);
+ goto fail;
+ }
- uint64_t *new_l1_table =
- g_try_realloc(l1_table, l1_sectors * BDRV_SECTOR_SIZE);
+ l1_size2 = s->snapshots[i].l1_size * sizeof(uint64_t);
+ new_l1_table = g_try_realloc(l1_table, l1_size2);
if (!new_l1_table) {
ret = -ENOMEM;
@@ -2105,9 +2116,8 @@ int qcow2_expand_zero_clusters(BlockDriverState *bs,
l1_table = new_l1_table;
- ret = bdrv_read(bs->file,
- s->snapshots[i].l1_table_offset / BDRV_SECTOR_SIZE,
- (void *)l1_table, l1_sectors);
+ ret = bdrv_pread(bs->file, s->snapshots[i].l1_table_offset,
+ l1_table, l1_size2);
if (ret < 0) {
goto fail;
}
diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c
index 126cca3276..362deaf303 100644
--- a/block/qcow2-refcount.c
+++ b/block/qcow2-refcount.c
@@ -1171,7 +1171,35 @@ void qcow2_free_any_clusters(BlockDriverState *bs, uint64_t l2_entry,
}
}
+int coroutine_fn qcow2_write_caches(BlockDriverState *bs)
+{
+ BDRVQcow2State *s = bs->opaque;
+ int ret;
+
+ ret = qcow2_cache_write(bs, s->l2_table_cache);
+ if (ret < 0) {
+ return ret;
+ }
+
+ if (qcow2_need_accurate_refcounts(s)) {
+ ret = qcow2_cache_write(bs, s->refcount_block_cache);
+ if (ret < 0) {
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+int coroutine_fn qcow2_flush_caches(BlockDriverState *bs)
+{
+ int ret = qcow2_write_caches(bs);
+ if (ret < 0) {
+ return ret;
+ }
+ return bdrv_flush(bs->file->bs);
+}
/*********************************************************/
/* snapshots and image creation */
@@ -2019,6 +2047,20 @@ static int calculate_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
/* snapshots */
for (i = 0; i < s->nb_snapshots; i++) {
sn = s->snapshots + i;
+ if (offset_into_cluster(s, sn->l1_table_offset)) {
+ fprintf(stderr, "ERROR snapshot %s (%s) l1_offset=%#" PRIx64 ": "
+ "L1 table is not cluster aligned; snapshot table entry "
+ "corrupted\n", sn->id_str, sn->name, sn->l1_table_offset);
+ res->corruptions++;
+ continue;
+ }
+ if (sn->l1_size > QCOW_MAX_L1_SIZE / sizeof(uint64_t)) {
+ fprintf(stderr, "ERROR snapshot %s (%s) l1_size=%#" PRIx32 ": "
+ "L1 table is too large; snapshot table entry corrupted\n",
+ sn->id_str, sn->name, sn->l1_size);
+ res->corruptions++;
+ continue;
+ }
ret = check_refcounts_l1(bs, res, refcount_table, nb_clusters,
sn->l1_table_offset, sn->l1_size, 0, fix);
if (ret < 0) {
@@ -2614,9 +2656,17 @@ int qcow2_check_metadata_overlap(BlockDriverState *bs, int ign, int64_t offset,
uint64_t l1_ofs = s->snapshots[i].l1_table_offset;
uint32_t l1_sz = s->snapshots[i].l1_size;
uint64_t l1_sz2 = l1_sz * sizeof(uint64_t);
- uint64_t *l1 = g_try_malloc(l1_sz2);
+ uint64_t *l1;
int ret;
+ ret = qcow2_validate_table(bs, l1_ofs, l1_sz, sizeof(uint64_t),
+ QCOW_MAX_L1_SIZE, "", NULL);
+ if (ret < 0) {
+ return ret;
+ }
+
+ l1 = g_try_malloc(l1_sz2);
+
if (l1_sz2 && l1 == NULL) {
return -ENOMEM;
}
diff --git a/block/qcow2-snapshot.c b/block/qcow2-snapshot.c
index cee25f582b..74293be470 100644
--- a/block/qcow2-snapshot.c
+++ b/block/qcow2-snapshot.c
@@ -465,6 +465,7 @@ int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id)
{
BDRVQcow2State *s = bs->opaque;
QCowSnapshot *sn;
+ Error *local_err = NULL;
int i, snapshot_index;
int cur_l1_bytes, sn_l1_bytes;
int ret;
@@ -477,6 +478,14 @@ int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id)
}
sn = &s->snapshots[snapshot_index];
+ ret = qcow2_validate_table(bs, sn->l1_table_offset, sn->l1_size,
+ sizeof(uint64_t), QCOW_MAX_L1_SIZE,
+ "Snapshot L1 table", &local_err);
+ if (ret < 0) {
+ error_report_err(local_err);
+ goto fail;
+ }
+
if (sn->disk_size != bs->total_sectors * BDRV_SECTOR_SIZE) {
error_report("qcow2: Loading snapshots with different disk "
"size is not implemented");
@@ -602,6 +611,13 @@ int qcow2_snapshot_delete(BlockDriverState *bs,
}
sn = s->snapshots[snapshot_index];
+ ret = qcow2_validate_table(bs, sn.l1_table_offset, sn.l1_size,
+ sizeof(uint64_t), QCOW_MAX_L1_SIZE,
+ "Snapshot L1 table", errp);
+ if (ret < 0) {
+ return ret;
+ }
+
/* Remove it from the snapshot list */
memmove(s->snapshots + snapshot_index,
s->snapshots + snapshot_index + 1,
@@ -704,9 +720,11 @@ int qcow2_snapshot_load_tmp(BlockDriverState *bs,
sn = &s->snapshots[snapshot_index];
/* Allocate and read in the snapshot's L1 table */
- if (sn->l1_size > QCOW_MAX_L1_SIZE / sizeof(uint64_t)) {
- error_setg(errp, "Snapshot L1 table too large");
- return -EFBIG;
+ ret = qcow2_validate_table(bs, sn->l1_table_offset, sn->l1_size,
+ sizeof(uint64_t), QCOW_MAX_L1_SIZE,
+ "Snapshot L1 table", errp);
+ if (ret < 0) {
+ return ret;
}
new_l1_bytes = sn->l1_size * sizeof(uint64_t);
new_l1_table = qemu_try_blockalign(bs->file->bs,
diff --git a/block/qcow2.c b/block/qcow2.c
index 071dc4d608..7472af6931 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -37,7 +37,8 @@
#include "qemu/option_int.h"
#include "qemu/cutils.h"
#include "qemu/bswap.h"
-#include "qapi/opts-visitor.h"
+#include "qapi/qobject-input-visitor.h"
+#include "qapi/qapi-visit-block-core.h"
#include "block/crypto.h"
/*
@@ -500,7 +501,7 @@ static int qcow2_mark_clean(BlockDriverState *bs)
s->incompatible_features &= ~QCOW2_INCOMPAT_DIRTY;
- ret = bdrv_flush(bs);
+ ret = qcow2_flush_caches(bs);
if (ret < 0) {
return ret;
}
@@ -530,7 +531,7 @@ int qcow2_mark_consistent(BlockDriverState *bs)
BDRVQcow2State *s = bs->opaque;
if (s->incompatible_features & QCOW2_INCOMPAT_CORRUPT) {
- int ret = bdrv_flush(bs);
+ int ret = qcow2_flush_caches(bs);
if (ret < 0) {
return ret;
}
@@ -541,8 +542,9 @@ int qcow2_mark_consistent(BlockDriverState *bs)
return 0;
}
-static int qcow2_check(BlockDriverState *bs, BdrvCheckResult *result,
- BdrvCheckMode fix)
+static int coroutine_fn qcow2_co_check_locked(BlockDriverState *bs,
+ BdrvCheckResult *result,
+ BdrvCheckMode fix)
{
int ret = qcow2_check_refcounts(bs, result, fix);
if (ret < 0) {
@@ -559,26 +561,36 @@ static int qcow2_check(BlockDriverState *bs, BdrvCheckResult *result,
return ret;
}
-static int validate_table_offset(BlockDriverState *bs, uint64_t offset,
- uint64_t entries, size_t entry_len)
+static int coroutine_fn qcow2_co_check(BlockDriverState *bs,
+ BdrvCheckResult *result,
+ BdrvCheckMode fix)
{
BDRVQcow2State *s = bs->opaque;
- uint64_t size;
+ int ret;
- /* Use signed INT64_MAX as the maximum even for uint64_t header fields,
- * because values will be passed to qemu functions taking int64_t. */
- if (entries > INT64_MAX / entry_len) {
- return -EINVAL;
- }
+ qemu_co_mutex_lock(&s->lock);
+ ret = qcow2_co_check_locked(bs, result, fix);
+ qemu_co_mutex_unlock(&s->lock);
+ return ret;
+}
- size = entries * entry_len;
+int qcow2_validate_table(BlockDriverState *bs, uint64_t offset,
+ uint64_t entries, size_t entry_len,
+ int64_t max_size_bytes, const char *table_name,
+ Error **errp)
+{
+ BDRVQcow2State *s = bs->opaque;
- if (INT64_MAX - size < offset) {
- return -EINVAL;
+ if (entries > max_size_bytes / entry_len) {
+ error_setg(errp, "%s too large", table_name);
+ return -EFBIG;
}
- /* Tables must be cluster aligned */
- if (offset_into_cluster(s, offset) != 0) {
+ /* Use signed INT64_MAX as the maximum even for uint64_t header fields,
+ * because values will be passed to qemu functions taking int64_t. */
+ if ((INT64_MAX - entries * entry_len < offset) ||
+ (offset_into_cluster(s, offset) != 0)) {
+ error_setg(errp, "%s offset invalid", table_name);
return -EINVAL;
}
@@ -1118,8 +1130,9 @@ static int qcow2_update_options(BlockDriverState *bs, QDict *options,
return ret;
}
-static int qcow2_do_open(BlockDriverState *bs, QDict *options, int flags,
- Error **errp)
+/* Called with s->lock held. */
+static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options,
+ int flags, Error **errp)
{
BDRVQcow2State *s = bs->opaque;
unsigned int len, i;
@@ -1308,47 +1321,42 @@ static int qcow2_do_open(BlockDriverState *bs, QDict *options, int flags,
s->refcount_table_size =
header.refcount_table_clusters << (s->cluster_bits - 3);
- if (header.refcount_table_clusters > qcow2_max_refcount_clusters(s)) {
- error_setg(errp, "Reference count table too large");
- ret = -EINVAL;
- goto fail;
- }
-
if (header.refcount_table_clusters == 0 && !(flags & BDRV_O_CHECK)) {
error_setg(errp, "Image does not contain a reference count table");
ret = -EINVAL;
goto fail;
}
- ret = validate_table_offset(bs, s->refcount_table_offset,
- s->refcount_table_size, sizeof(uint64_t));
+ ret = qcow2_validate_table(bs, s->refcount_table_offset,
+ header.refcount_table_clusters,
+ s->cluster_size, QCOW_MAX_REFTABLE_SIZE,
+ "Reference count table", errp);
if (ret < 0) {
- error_setg(errp, "Invalid reference count table offset");
goto fail;
}
- /* Snapshot table offset/length */
- if (header.nb_snapshots > QCOW_MAX_SNAPSHOTS) {
- error_setg(errp, "Too many snapshots");
- ret = -EINVAL;
- goto fail;
- }
-
- ret = validate_table_offset(bs, header.snapshots_offset,
- header.nb_snapshots,
- sizeof(QCowSnapshotHeader));
+ /* The total size in bytes of the snapshot table is checked in
+ * qcow2_read_snapshots() because the size of each snapshot is
+ * variable and we don't know it yet.
+ * Here we only check the offset and number of snapshots. */
+ ret = qcow2_validate_table(bs, header.snapshots_offset,
+ header.nb_snapshots,
+ sizeof(QCowSnapshotHeader),
+ sizeof(QCowSnapshotHeader) * QCOW_MAX_SNAPSHOTS,
+ "Snapshot table", errp);
if (ret < 0) {
- error_setg(errp, "Invalid snapshot table offset");
goto fail;
}
/* read the level 1 table */
- if (header.l1_size > QCOW_MAX_L1_SIZE / sizeof(uint64_t)) {
- error_setg(errp, "Active L1 table too large");
- ret = -EFBIG;
+ ret = qcow2_validate_table(bs, header.l1_table_offset,
+ header.l1_size, sizeof(uint64_t),
+ QCOW_MAX_L1_SIZE, "Active L1 table", errp);
+ if (ret < 0) {
goto fail;
}
s->l1_size = header.l1_size;
+ s->l1_table_offset = header.l1_table_offset;
l1_vm_state_index = size_to_l1(s, header.size);
if (l1_vm_state_index > INT_MAX) {
@@ -1366,15 +1374,6 @@ static int qcow2_do_open(BlockDriverState *bs, QDict *options, int flags,
goto fail;
}
- ret = validate_table_offset(bs, header.l1_table_offset,
- header.l1_size, sizeof(uint64_t));
- if (ret < 0) {
- error_setg(errp, "Invalid L1 table offset");
- goto fail;
- }
- s->l1_table_offset = header.l1_table_offset;
-
-
if (s->l1_size > 0) {
s->l1_table = qemu_try_blockalign(bs->file->bs,
ROUND_UP(s->l1_size * sizeof(uint64_t), 512));
@@ -1498,8 +1497,6 @@ static int qcow2_do_open(BlockDriverState *bs, QDict *options, int flags,
}
}
- /* Initialise locks */
- qemu_co_mutex_init(&s->lock);
bs->supported_zero_flags = header.version >= 3 ? BDRV_REQ_MAY_UNMAP : 0;
/* Repair image if dirty */
@@ -1507,7 +1504,8 @@ static int qcow2_do_open(BlockDriverState *bs, QDict *options, int flags,
(s->incompatible_features & QCOW2_INCOMPAT_DIRTY)) {
BdrvCheckResult result = {0};
- ret = qcow2_check(bs, &result, BDRV_FIX_ERRORS | BDRV_FIX_LEAKS);
+ ret = qcow2_co_check_locked(bs, &result,
+ BDRV_FIX_ERRORS | BDRV_FIX_LEAKS);
if (ret < 0 || result.check_errors) {
if (ret >= 0) {
ret = -EIO;
@@ -1545,16 +1543,53 @@ static int qcow2_do_open(BlockDriverState *bs, QDict *options, int flags,
return ret;
}
+typedef struct QCow2OpenCo {
+ BlockDriverState *bs;
+ QDict *options;
+ int flags;
+ Error **errp;
+ int ret;
+} QCow2OpenCo;
+
+static void coroutine_fn qcow2_open_entry(void *opaque)
+{
+ QCow2OpenCo *qoc = opaque;
+ BDRVQcow2State *s = qoc->bs->opaque;
+
+ qemu_co_mutex_lock(&s->lock);
+ qoc->ret = qcow2_do_open(qoc->bs, qoc->options, qoc->flags, qoc->errp);
+ qemu_co_mutex_unlock(&s->lock);
+}
+
static int qcow2_open(BlockDriverState *bs, QDict *options, int flags,
Error **errp)
{
+ BDRVQcow2State *s = bs->opaque;
+ QCow2OpenCo qoc = {
+ .bs = bs,
+ .options = options,
+ .flags = flags,
+ .errp = errp,
+ .ret = -EINPROGRESS
+ };
+
bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file,
false, errp);
if (!bs->file) {
return -EINVAL;
}
- return qcow2_do_open(bs, options, flags, errp);
+ /* Initialise locks */
+ qemu_co_mutex_init(&s->lock);
+
+ if (qemu_in_coroutine()) {
+ /* From bdrv_co_create. */
+ qcow2_open_entry(&qoc);
+ } else {
+ qemu_coroutine_enter(qemu_coroutine_create(qcow2_open_entry, &qoc));
+ BDRV_POLL_WHILE(bs, qoc.ret == -EINPROGRESS);
+ }
+ return qoc.ret;
}
static void qcow2_refresh_limits(BlockDriverState *bs, Error **errp)
@@ -2106,7 +2141,8 @@ static void qcow2_close(BlockDriverState *bs)
qcow2_free_snapshots(bs);
}
-static void qcow2_invalidate_cache(BlockDriverState *bs, Error **errp)
+static void coroutine_fn qcow2_co_invalidate_cache(BlockDriverState *bs,
+ Error **errp)
{
BDRVQcow2State *s = bs->opaque;
int flags = s->flags;
@@ -2129,7 +2165,9 @@ static void qcow2_invalidate_cache(BlockDriverState *bs, Error **errp)
options = qdict_clone_shallow(bs->options);
flags &= ~BDRV_O_INACTIVE;
+ qemu_co_mutex_lock(&s->lock);
ret = qcow2_do_open(bs, options, flags, &local_err);
+ qemu_co_mutex_unlock(&s->lock);
QDECREF(options);
if (local_err) {
error_propagate(errp, local_err);
@@ -2412,39 +2450,26 @@ static int qcow2_crypt_method_from_format(const char *encryptfmt)
}
}
-static int qcow2_set_up_encryption(BlockDriverState *bs, const char *encryptfmt,
- QemuOpts *opts, Error **errp)
+static int qcow2_set_up_encryption(BlockDriverState *bs,
+ QCryptoBlockCreateOptions *cryptoopts,
+ Error **errp)
{
BDRVQcow2State *s = bs->opaque;
- QCryptoBlockCreateOptions *cryptoopts = NULL;
QCryptoBlock *crypto = NULL;
- int ret = -EINVAL;
- QDict *options, *encryptopts;
- int fmt;
-
- options = qemu_opts_to_qdict(opts, NULL);
- qdict_extract_subqdict(options, &encryptopts, "encrypt.");
- QDECREF(options);
+ int fmt, ret;
- fmt = qcow2_crypt_method_from_format(encryptfmt);
-
- switch (fmt) {
- case QCOW_CRYPT_LUKS:
- cryptoopts = block_crypto_create_opts_init(
- Q_CRYPTO_BLOCK_FORMAT_LUKS, encryptopts, errp);
+ switch (cryptoopts->format) {
+ case Q_CRYPTO_BLOCK_FORMAT_LUKS:
+ fmt = QCOW_CRYPT_LUKS;
break;
- case QCOW_CRYPT_AES:
- cryptoopts = block_crypto_create_opts_init(
- Q_CRYPTO_BLOCK_FORMAT_QCOW, encryptopts, errp);
+ case Q_CRYPTO_BLOCK_FORMAT_QCOW:
+ fmt = QCOW_CRYPT_AES;
break;
default:
- error_setg(errp, "Unknown encryption format '%s'", encryptfmt);
- break;
- }
- if (!cryptoopts) {
- ret = -EINVAL;
- goto out;
+ error_setg(errp, "Crypto format not supported in qcow2");
+ return -EINVAL;
}
+
s->crypt_method_header = fmt;
crypto = qcrypto_block_create(cryptoopts, "encrypt.",
@@ -2452,8 +2477,7 @@ static int qcow2_set_up_encryption(BlockDriverState *bs, const char *encryptfmt,
qcow2_crypto_hdr_write_func,
bs, errp);
if (!crypto) {
- ret = -EINVAL;
- goto out;
+ return -EINVAL;
}
ret = qcow2_update_header(bs);
@@ -2462,10 +2486,9 @@ static int qcow2_set_up_encryption(BlockDriverState *bs, const char *encryptfmt,
goto out;
}
+ ret = 0;
out:
- QDECREF(encryptopts);
qcrypto_block_free(crypto);
- qapi_free_QCryptoBlockCreateOptions(cryptoopts);
return ret;
}
@@ -2663,19 +2686,26 @@ static int64_t qcow2_calc_prealloc_size(int64_t total_size,
return meta_size + aligned_total_size;
}
-static size_t qcow2_opt_get_cluster_size_del(QemuOpts *opts, Error **errp)
+static bool validate_cluster_size(size_t cluster_size, Error **errp)
{
- size_t cluster_size;
- int cluster_bits;
-
- cluster_size = qemu_opt_get_size_del(opts, BLOCK_OPT_CLUSTER_SIZE,
- DEFAULT_CLUSTER_SIZE);
- cluster_bits = ctz32(cluster_size);
+ int cluster_bits = ctz32(cluster_size);
if (cluster_bits < MIN_CLUSTER_BITS || cluster_bits > MAX_CLUSTER_BITS ||
(1 << cluster_bits) != cluster_size)
{
error_setg(errp, "Cluster size must be a power of two between %d and "
"%dk", 1 << MIN_CLUSTER_BITS, 1 << (MAX_CLUSTER_BITS - 10));
+ return false;
+ }
+ return true;
+}
+
+static size_t qcow2_opt_get_cluster_size_del(QemuOpts *opts, Error **errp)
+{
+ size_t cluster_size;
+
+ cluster_size = qemu_opt_get_size_del(opts, BLOCK_OPT_CLUSTER_SIZE,
+ DEFAULT_CLUSTER_SIZE);
+ if (!validate_cluster_size(cluster_size, errp)) {
return 0;
}
return cluster_size;
@@ -2724,12 +2754,9 @@ static uint64_t qcow2_opt_get_refcount_bits_del(QemuOpts *opts, int version,
}
static int coroutine_fn
-qcow2_co_create2(const char *filename, int64_t total_size,
- const char *backing_file, const char *backing_format,
- int flags, size_t cluster_size, PreallocMode prealloc,
- QemuOpts *opts, int version, int refcount_order,
- const char *encryptfmt, Error **errp)
+qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp)
{
+ BlockdevCreateOptionsQcow2 *qcow2_opts;
QDict *options;
/*
@@ -2744,36 +2771,132 @@ qcow2_co_create2(const char *filename, int64_t total_size,
* 2 GB for 64k clusters, and we don't want to have a 2 GB initial file
* size for any qcow2 image.
*/
- BlockBackend *blk;
+ BlockBackend *blk = NULL;
+ BlockDriverState *bs = NULL;
QCowHeader *header;
+ size_t cluster_size;
+ int version;
+ int refcount_order;
uint64_t* refcount_table;
Error *local_err = NULL;
int ret;
- if (prealloc == PREALLOC_MODE_FULL || prealloc == PREALLOC_MODE_FALLOC) {
- int64_t prealloc_size =
- qcow2_calc_prealloc_size(total_size, cluster_size, refcount_order);
- qemu_opt_set_number(opts, BLOCK_OPT_SIZE, prealloc_size, &error_abort);
- qemu_opt_set(opts, BLOCK_OPT_PREALLOC, PreallocMode_str(prealloc),
- &error_abort);
+ assert(create_options->driver == BLOCKDEV_DRIVER_QCOW2);
+ qcow2_opts = &create_options->u.qcow2;
+
+ bs = bdrv_open_blockdev_ref(qcow2_opts->file, errp);
+ if (bs == NULL) {
+ return -EIO;
}
- ret = bdrv_create_file(filename, opts, &local_err);
- if (ret < 0) {
- error_propagate(errp, local_err);
- return ret;
+ /* Validate options and set default values */
+ if (!QEMU_IS_ALIGNED(qcow2_opts->size, BDRV_SECTOR_SIZE)) {
+ error_setg(errp, "Image size must be a multiple of 512 bytes");
+ ret = -EINVAL;
+ goto out;
}
- blk = blk_new_open(filename, NULL, NULL,
- BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL,
- &local_err);
- if (blk == NULL) {
- error_propagate(errp, local_err);
- return -EIO;
+ if (qcow2_opts->has_version) {
+ switch (qcow2_opts->version) {
+ case BLOCKDEV_QCOW2_VERSION_V2:
+ version = 2;
+ break;
+ case BLOCKDEV_QCOW2_VERSION_V3:
+ version = 3;
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ } else {
+ version = 3;
+ }
+
+ if (qcow2_opts->has_cluster_size) {
+ cluster_size = qcow2_opts->cluster_size;
+ } else {
+ cluster_size = DEFAULT_CLUSTER_SIZE;
+ }
+
+ if (!validate_cluster_size(cluster_size, errp)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (!qcow2_opts->has_preallocation) {
+ qcow2_opts->preallocation = PREALLOC_MODE_OFF;
+ }
+ if (qcow2_opts->has_backing_file &&
+ qcow2_opts->preallocation != PREALLOC_MODE_OFF)
+ {
+ error_setg(errp, "Backing file and preallocation cannot be used at "
+ "the same time");
+ ret = -EINVAL;
+ goto out;
+ }
+ if (qcow2_opts->has_backing_fmt && !qcow2_opts->has_backing_file) {
+ error_setg(errp, "Backing format cannot be used without backing file");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (!qcow2_opts->has_lazy_refcounts) {
+ qcow2_opts->lazy_refcounts = false;
+ }
+ if (version < 3 && qcow2_opts->lazy_refcounts) {
+ error_setg(errp, "Lazy refcounts only supported with compatibility "
+ "level 1.1 and above (use version=v3 or greater)");
+ ret = -EINVAL;
+ goto out;
}
+ if (!qcow2_opts->has_refcount_bits) {
+ qcow2_opts->refcount_bits = 16;
+ }
+ if (qcow2_opts->refcount_bits > 64 ||
+ !is_power_of_2(qcow2_opts->refcount_bits))
+ {
+ error_setg(errp, "Refcount width must be a power of two and may not "
+ "exceed 64 bits");
+ ret = -EINVAL;
+ goto out;
+ }
+ if (version < 3 && qcow2_opts->refcount_bits != 16) {
+ error_setg(errp, "Different refcount widths than 16 bits require "
+ "compatibility level 1.1 or above (use version=v3 or "
+ "greater)");
+ ret = -EINVAL;
+ goto out;
+ }
+ refcount_order = ctz32(qcow2_opts->refcount_bits);
+
+
+ /* Create BlockBackend to write to the image */
+ blk = blk_new(BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL);
+ ret = blk_insert_bs(blk, bs, errp);
+ if (ret < 0) {
+ goto out;
+ }
blk_set_allow_write_beyond_eof(blk, true);
+ /* Clear the protocol layer and preallocate it if necessary */
+ ret = blk_truncate(blk, 0, PREALLOC_MODE_OFF, errp);
+ if (ret < 0) {
+ goto out;
+ }
+
+ if (qcow2_opts->preallocation == PREALLOC_MODE_FULL ||
+ qcow2_opts->preallocation == PREALLOC_MODE_FALLOC)
+ {
+ int64_t prealloc_size =
+ qcow2_calc_prealloc_size(qcow2_opts->size, cluster_size,
+ refcount_order);
+
+ ret = blk_truncate(blk, prealloc_size, qcow2_opts->preallocation, errp);
+ if (ret < 0) {
+ goto out;
+ }
+ }
+
/* Write the header */
QEMU_BUILD_BUG_ON((1 << MIN_CLUSTER_BITS) < sizeof(*header));
header = g_malloc0(cluster_size);
@@ -2793,7 +2916,7 @@ qcow2_co_create2(const char *filename, int64_t total_size,
/* We'll update this to correct value later */
header->crypt_method = cpu_to_be32(QCOW_CRYPT_NONE);
- if (flags & BLOCK_FLAG_LAZY_REFCOUNTS) {
+ if (qcow2_opts->lazy_refcounts) {
header->compatible_features |=
cpu_to_be64(QCOW2_COMPAT_LAZY_REFCOUNTS);
}
@@ -2826,7 +2949,8 @@ qcow2_co_create2(const char *filename, int64_t total_size,
*/
options = qdict_new();
qdict_put_str(options, "driver", "qcow2");
- blk = blk_new_open(filename, NULL, options,
+ qdict_put_str(options, "file", bs->node_name);
+ blk = blk_new_open(NULL, NULL, options,
BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_NO_FLUSH,
&local_err);
if (blk == NULL) {
@@ -2854,33 +2978,41 @@ qcow2_co_create2(const char *filename, int64_t total_size,
}
/* Okay, now that we have a valid image, let's give it the right size */
- ret = blk_truncate(blk, total_size, PREALLOC_MODE_OFF, errp);
+ ret = blk_truncate(blk, qcow2_opts->size, PREALLOC_MODE_OFF, errp);
if (ret < 0) {
error_prepend(errp, "Could not resize image: ");
goto out;
}
/* Want a backing file? There you go.*/
- if (backing_file) {
- ret = bdrv_change_backing_file(blk_bs(blk), backing_file, backing_format);
+ if (qcow2_opts->has_backing_file) {
+ const char *backing_format = NULL;
+
+ if (qcow2_opts->has_backing_fmt) {
+ backing_format = BlockdevDriver_str(qcow2_opts->backing_fmt);
+ }
+
+ ret = bdrv_change_backing_file(blk_bs(blk), qcow2_opts->backing_file,
+ backing_format);
if (ret < 0) {
error_setg_errno(errp, -ret, "Could not assign backing file '%s' "
- "with format '%s'", backing_file, backing_format);
+ "with format '%s'", qcow2_opts->backing_file,
+ backing_format);
goto out;
}
}
/* Want encryption? There you go. */
- if (encryptfmt) {
- ret = qcow2_set_up_encryption(blk_bs(blk), encryptfmt, opts, errp);
+ if (qcow2_opts->has_encrypt) {
+ ret = qcow2_set_up_encryption(blk_bs(blk), qcow2_opts->encrypt, errp);
if (ret < 0) {
goto out;
}
}
/* And if we're supposed to preallocate metadata, do that now */
- if (prealloc != PREALLOC_MODE_OFF) {
- ret = preallocate(blk_bs(blk), 0, total_size);
+ if (qcow2_opts->preallocation != PREALLOC_MODE_OFF) {
+ ret = preallocate(blk_bs(blk), 0, qcow2_opts->size);
if (ret < 0) {
error_setg_errno(errp, -ret, "Could not preallocate metadata");
goto out;
@@ -2898,7 +3030,8 @@ qcow2_co_create2(const char *filename, int64_t total_size,
*/
options = qdict_new();
qdict_put_str(options, "driver", "qcow2");
- blk = blk_new_open(filename, NULL, options,
+ qdict_put_str(options, "file", bs->node_name);
+ blk = blk_new_open(NULL, NULL, options,
BDRV_O_RDWR | BDRV_O_NO_BACKING | BDRV_O_NO_IO,
&local_err);
if (blk == NULL) {
@@ -2909,104 +3042,120 @@ qcow2_co_create2(const char *filename, int64_t total_size,
ret = 0;
out:
- if (blk) {
- blk_unref(blk);
- }
+ blk_unref(blk);
+ bdrv_unref(bs);
return ret;
}
static int coroutine_fn qcow2_co_create_opts(const char *filename, QemuOpts *opts,
Error **errp)
{
- char *backing_file = NULL;
- char *backing_fmt = NULL;
- char *buf = NULL;
- uint64_t size = 0;
- int flags = 0;
- size_t cluster_size = DEFAULT_CLUSTER_SIZE;
- PreallocMode prealloc;
- int version;
- uint64_t refcount_bits;
- int refcount_order;
- char *encryptfmt = NULL;
+ BlockdevCreateOptions *create_options = NULL;
+ QDict *qdict = NULL;
+ QObject *qobj;
+ Visitor *v;
+ BlockDriverState *bs = NULL;
Error *local_err = NULL;
+ const char *val;
int ret;
- /* Read out options */
- size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
- BDRV_SECTOR_SIZE);
- backing_file = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FILE);
- backing_fmt = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FMT);
- encryptfmt = qemu_opt_get_del(opts, BLOCK_OPT_ENCRYPT_FORMAT);
- if (encryptfmt) {
- if (qemu_opt_get(opts, BLOCK_OPT_ENCRYPT)) {
- error_setg(errp, "Options " BLOCK_OPT_ENCRYPT " and "
- BLOCK_OPT_ENCRYPT_FORMAT " are mutually exclusive");
- ret = -EINVAL;
- goto finish;
- }
- } else if (qemu_opt_get_bool_del(opts, BLOCK_OPT_ENCRYPT, false)) {
- encryptfmt = g_strdup("aes");
- }
- cluster_size = qcow2_opt_get_cluster_size_del(opts, &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
+ /* Only the keyval visitor supports the dotted syntax needed for
+ * encryption, so go through a QDict before getting a QAPI type. Ignore
+ * options meant for the protocol layer so that the visitor doesn't
+ * complain. */
+ qdict = qemu_opts_to_qdict_filtered(opts, NULL, bdrv_qcow2.create_opts,
+ true);
+
+ /* Handle encryption options */
+ val = qdict_get_try_str(qdict, BLOCK_OPT_ENCRYPT);
+ if (val && !strcmp(val, "on")) {
+ qdict_put_str(qdict, BLOCK_OPT_ENCRYPT, "qcow");
+ } else if (val && !strcmp(val, "off")) {
+ qdict_del(qdict, BLOCK_OPT_ENCRYPT);
+ }
+
+ val = qdict_get_try_str(qdict, BLOCK_OPT_ENCRYPT_FORMAT);
+ if (val && !strcmp(val, "aes")) {
+ qdict_put_str(qdict, BLOCK_OPT_ENCRYPT_FORMAT, "qcow");
+ }
+
+ /* Convert compat=0.10/1.1 into compat=v2/v3, to be renamed into
+ * version=v2/v3 below. */
+ val = qdict_get_try_str(qdict, BLOCK_OPT_COMPAT_LEVEL);
+ if (val && !strcmp(val, "0.10")) {
+ qdict_put_str(qdict, BLOCK_OPT_COMPAT_LEVEL, "v2");
+ } else if (val && !strcmp(val, "1.1")) {
+ qdict_put_str(qdict, BLOCK_OPT_COMPAT_LEVEL, "v3");
+ }
+
+ /* Change legacy command line options into QMP ones */
+ static const QDictRenames opt_renames[] = {
+ { BLOCK_OPT_BACKING_FILE, "backing-file" },
+ { BLOCK_OPT_BACKING_FMT, "backing-fmt" },
+ { BLOCK_OPT_CLUSTER_SIZE, "cluster-size" },
+ { BLOCK_OPT_LAZY_REFCOUNTS, "lazy-refcounts" },
+ { BLOCK_OPT_REFCOUNT_BITS, "refcount-bits" },
+ { BLOCK_OPT_ENCRYPT, BLOCK_OPT_ENCRYPT_FORMAT },
+ { BLOCK_OPT_COMPAT_LEVEL, "version" },
+ { NULL, NULL },
+ };
+
+ if (!qdict_rename_keys(qdict, opt_renames, errp)) {
ret = -EINVAL;
goto finish;
}
- buf = qemu_opt_get_del(opts, BLOCK_OPT_PREALLOC);
- prealloc = qapi_enum_parse(&PreallocMode_lookup, buf,
- PREALLOC_MODE_OFF, &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
- ret = -EINVAL;
+
+ /* Create and open the file (protocol layer) */
+ ret = bdrv_create_file(filename, opts, errp);
+ if (ret < 0) {
goto finish;
}
- version = qcow2_opt_get_version_del(opts, &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
- ret = -EINVAL;
+ bs = bdrv_open(filename, NULL, NULL,
+ BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, errp);
+ if (bs == NULL) {
+ ret = -EIO;
goto finish;
}
- if (qemu_opt_get_bool_del(opts, BLOCK_OPT_LAZY_REFCOUNTS, false)) {
- flags |= BLOCK_FLAG_LAZY_REFCOUNTS;
- }
+ /* Set 'driver' and 'node' options */
+ qdict_put_str(qdict, "driver", "qcow2");
+ qdict_put_str(qdict, "file", bs->node_name);
- if (backing_file && prealloc != PREALLOC_MODE_OFF) {
- error_setg(errp, "Backing file and preallocation cannot be used at "
- "the same time");
+ /* Now get the QAPI type BlockdevCreateOptions */
+ qobj = qdict_crumple(qdict, errp);
+ QDECREF(qdict);
+ qdict = qobject_to_qdict(qobj);
+ if (qdict == NULL) {
ret = -EINVAL;
goto finish;
}
- if (version < 3 && (flags & BLOCK_FLAG_LAZY_REFCOUNTS)) {
- error_setg(errp, "Lazy refcounts only supported with compatibility "
- "level 1.1 and above (use compat=1.1 or greater)");
- ret = -EINVAL;
- goto finish;
- }
+ v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
+ visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err);
+ visit_free(v);
- refcount_bits = qcow2_opt_get_refcount_bits_del(opts, version, &local_err);
if (local_err) {
error_propagate(errp, local_err);
ret = -EINVAL;
goto finish;
}
- refcount_order = ctz32(refcount_bits);
+ /* Silently round up size */
+ create_options->u.qcow2.size = ROUND_UP(create_options->u.qcow2.size,
+ BDRV_SECTOR_SIZE);
- ret = qcow2_co_create2(filename, size, backing_file, backing_fmt, flags,
- cluster_size, prealloc, opts, version, refcount_order,
- encryptfmt, &local_err);
- error_propagate(errp, local_err);
+ /* Create the qcow2 image (format layer) */
+ ret = qcow2_co_create(create_options, errp);
+ if (ret < 0) {
+ goto finish;
+ }
+ ret = 0;
finish:
- g_free(backing_file);
- g_free(backing_fmt);
- g_free(encryptfmt);
- g_free(buf);
+ QDECREF(qdict);
+ bdrv_unref(bs);
+ qapi_free_BlockdevCreateOptions(create_options);
return ret;
}
@@ -3647,22 +3796,10 @@ static coroutine_fn int qcow2_co_flush_to_os(BlockDriverState *bs)
int ret;
qemu_co_mutex_lock(&s->lock);
- ret = qcow2_cache_write(bs, s->l2_table_cache);
- if (ret < 0) {
- qemu_co_mutex_unlock(&s->lock);
- return ret;
- }
-
- if (qcow2_need_accurate_refcounts(s)) {
- ret = qcow2_cache_write(bs, s->refcount_block_cache);
- if (ret < 0) {
- qemu_co_mutex_unlock(&s->lock);
- return ret;
- }
- }
+ ret = qcow2_write_caches(bs);
qemu_co_mutex_unlock(&s->lock);
- return 0;
+ return ret;
}
static BlockMeasureInfo *qcow2_measure(QemuOpts *opts, BlockDriverState *in_bs,
@@ -4353,6 +4490,7 @@ BlockDriver bdrv_qcow2 = {
.bdrv_join_options = qcow2_join_options,
.bdrv_child_perm = bdrv_format_default_perms,
.bdrv_co_create_opts = qcow2_co_create_opts,
+ .bdrv_co_create = qcow2_co_create,
.bdrv_has_zero_init = bdrv_has_zero_init_1,
.bdrv_co_block_status = qcow2_co_block_status,
@@ -4382,11 +4520,11 @@ BlockDriver bdrv_qcow2 = {
.bdrv_change_backing_file = qcow2_change_backing_file,
.bdrv_refresh_limits = qcow2_refresh_limits,
- .bdrv_invalidate_cache = qcow2_invalidate_cache,
+ .bdrv_co_invalidate_cache = qcow2_co_invalidate_cache,
.bdrv_inactivate = qcow2_inactivate,
.create_opts = &qcow2_create_opts,
- .bdrv_check = qcow2_check,
+ .bdrv_co_check = qcow2_co_check,
.bdrv_amend_options = qcow2_amend_options,
.bdrv_detach_aio_context = qcow2_detach_aio_context,
diff --git a/block/qcow2.h b/block/qcow2.h
index 1a84cc77b0..ccb92a9696 100644
--- a/block/qcow2.h
+++ b/block/qcow2.h
@@ -485,11 +485,6 @@ static inline int64_t qcow2_vm_state_offset(BDRVQcow2State *s)
return (int64_t)s->l1_vm_state_index << (s->cluster_bits + s->l2_bits);
}
-static inline uint64_t qcow2_max_refcount_clusters(BDRVQcow2State *s)
-{
- return QCOW_MAX_REFTABLE_SIZE >> s->cluster_bits;
-}
-
static inline QCow2ClusterType qcow2_get_cluster_type(uint64_t l2_entry)
{
if (l2_entry & QCOW_OFLAG_COMPRESSED) {
@@ -547,6 +542,11 @@ void qcow2_signal_corruption(BlockDriverState *bs, bool fatal, int64_t offset,
int64_t size, const char *message_format, ...)
GCC_FMT_ATTR(5, 6);
+int qcow2_validate_table(BlockDriverState *bs, uint64_t offset,
+ uint64_t entries, size_t entry_len,
+ int64_t max_size_bytes, const char *table_name,
+ Error **errp);
+
/* qcow2-refcount.c functions */
int qcow2_refcount_init(BlockDriverState *bs);
void qcow2_refcount_close(BlockDriverState *bs);
@@ -576,6 +576,8 @@ void qcow2_free_any_clusters(BlockDriverState *bs, uint64_t l2_entry,
int qcow2_update_snapshot_refcount(BlockDriverState *bs,
int64_t l1_table_offset, int l1_size, int addend);
+int coroutine_fn qcow2_flush_caches(BlockDriverState *bs);
+int coroutine_fn qcow2_write_caches(BlockDriverState *bs);
int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
BdrvCheckMode fix);
diff --git a/block/qed-check.c b/block/qed-check.c
index dcd4f036b8..0edac03159 100644
--- a/block/qed-check.c
+++ b/block/qed-check.c
@@ -217,6 +217,7 @@ static void qed_check_mark_clean(BDRVQEDState *s, BdrvCheckResult *result)
qed_write_header_sync(s);
}
+/* Called with table_lock held. */
int qed_check(BDRVQEDState *s, BdrvCheckResult *result, bool fix)
{
QEDCheck check = {
diff --git a/block/qed-table.c b/block/qed-table.c
index eead8b0fc7..7df5680adb 100644
--- a/block/qed-table.c
+++ b/block/qed-table.c
@@ -18,7 +18,7 @@
#include "qed.h"
#include "qemu/bswap.h"
-/* Called either from qed_check or with table_lock held. */
+/* Called with table_lock held. */
static int qed_read_table(BDRVQEDState *s, uint64_t offset, QEDTable *table)
{
QEMUIOVector qiov;
@@ -33,13 +33,9 @@ static int qed_read_table(BDRVQEDState *s, uint64_t offset, QEDTable *table)
trace_qed_read_table(s, offset, table);
- if (qemu_in_coroutine()) {
- qemu_co_mutex_unlock(&s->table_lock);
- }
+ qemu_co_mutex_unlock(&s->table_lock);
ret = bdrv_preadv(s->bs->file, offset, &qiov);
- if (qemu_in_coroutine()) {
- qemu_co_mutex_lock(&s->table_lock);
- }
+ qemu_co_mutex_lock(&s->table_lock);
if (ret < 0) {
goto out;
}
@@ -67,7 +63,7 @@ out:
* @n: Number of elements
* @flush: Whether or not to sync to disk
*
- * Called either from qed_check or with table_lock held.
+ * Called with table_lock held.
*/
static int qed_write_table(BDRVQEDState *s, uint64_t offset, QEDTable *table,
unsigned int index, unsigned int n, bool flush)
@@ -104,13 +100,9 @@ static int qed_write_table(BDRVQEDState *s, uint64_t offset, QEDTable *table,
/* Adjust for offset into table */
offset += start * sizeof(uint64_t);
- if (qemu_in_coroutine()) {
- qemu_co_mutex_unlock(&s->table_lock);
- }
+ qemu_co_mutex_unlock(&s->table_lock);
ret = bdrv_pwritev(s->bs->file, offset, &qiov);
- if (qemu_in_coroutine()) {
- qemu_co_mutex_lock(&s->table_lock);
- }
+ qemu_co_mutex_lock(&s->table_lock);
trace_qed_write_table_cb(s, table, flush, ret);
if (ret < 0) {
goto out;
@@ -134,7 +126,7 @@ int qed_read_l1_table_sync(BDRVQEDState *s)
return qed_read_table(s, s->header.l1_table_offset, s->l1_table);
}
-/* Called either from qed_check or with table_lock held. */
+/* Called with table_lock held. */
int qed_write_l1_table(BDRVQEDState *s, unsigned int index, unsigned int n)
{
BLKDBG_EVENT(s->bs->file, BLKDBG_L1_UPDATE);
@@ -148,7 +140,7 @@ int qed_write_l1_table_sync(BDRVQEDState *s, unsigned int index,
return qed_write_l1_table(s, index, n);
}
-/* Called either from qed_check or with table_lock held. */
+/* Called with table_lock held. */
int qed_read_l2_table(BDRVQEDState *s, QEDRequest *request, uint64_t offset)
{
int ret;
@@ -191,7 +183,7 @@ int qed_read_l2_table_sync(BDRVQEDState *s, QEDRequest *request, uint64_t offset
return qed_read_l2_table(s, request, offset);
}
-/* Called either from qed_check or with table_lock held. */
+/* Called with table_lock held. */
int qed_write_l2_table(BDRVQEDState *s, QEDRequest *request,
unsigned int index, unsigned int n, bool flush)
{
diff --git a/block/qed.c b/block/qed.c
index 72cf2f58ab..5e6a6bfaa0 100644
--- a/block/qed.c
+++ b/block/qed.c
@@ -381,8 +381,9 @@ static void bdrv_qed_init_state(BlockDriverState *bs)
qemu_co_queue_init(&s->allocating_write_reqs);
}
-static int bdrv_qed_do_open(BlockDriverState *bs, QDict *options, int flags,
- Error **errp)
+/* Called with table_lock held. */
+static int coroutine_fn bdrv_qed_do_open(BlockDriverState *bs, QDict *options,
+ int flags, Error **errp)
{
BDRVQEDState *s = bs->opaque;
QEDHeader le_header;
@@ -513,9 +514,35 @@ out:
return ret;
}
+typedef struct QEDOpenCo {
+ BlockDriverState *bs;
+ QDict *options;
+ int flags;
+ Error **errp;
+ int ret;
+} QEDOpenCo;
+
+static void coroutine_fn bdrv_qed_open_entry(void *opaque)
+{
+ QEDOpenCo *qoc = opaque;
+ BDRVQEDState *s = qoc->bs->opaque;
+
+ qemu_co_mutex_lock(&s->table_lock);
+ qoc->ret = bdrv_qed_do_open(qoc->bs, qoc->options, qoc->flags, qoc->errp);
+ qemu_co_mutex_unlock(&s->table_lock);
+}
+
static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags,
Error **errp)
{
+ QEDOpenCo qoc = {
+ .bs = bs,
+ .options = options,
+ .flags = flags,
+ .errp = errp,
+ .ret = -EINPROGRESS
+ };
+
bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file,
false, errp);
if (!bs->file) {
@@ -523,7 +550,14 @@ static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags,
}
bdrv_qed_init_state(bs);
- return bdrv_qed_do_open(bs, options, flags, errp);
+ if (qemu_in_coroutine()) {
+ bdrv_qed_open_entry(&qoc);
+ } else {
+ qemu_coroutine_enter(qemu_coroutine_create(bdrv_qed_open_entry, &qoc));
+ BDRV_POLL_WHILE(bs, qoc.ret == -EINPROGRESS);
+ }
+ BDRV_POLL_WHILE(bs, qoc.ret == -EINPROGRESS);
+ return qoc.ret;
}
static void bdrv_qed_refresh_limits(BlockDriverState *bs, Error **errp)
@@ -1487,7 +1521,8 @@ static int bdrv_qed_change_backing_file(BlockDriverState *bs,
return ret;
}
-static void bdrv_qed_invalidate_cache(BlockDriverState *bs, Error **errp)
+static void coroutine_fn bdrv_qed_co_invalidate_cache(BlockDriverState *bs,
+ Error **errp)
{
BDRVQEDState *s = bs->opaque;
Error *local_err = NULL;
@@ -1496,13 +1531,9 @@ static void bdrv_qed_invalidate_cache(BlockDriverState *bs, Error **errp)
bdrv_qed_close(bs);
bdrv_qed_init_state(bs);
- if (qemu_in_coroutine()) {
- qemu_co_mutex_lock(&s->table_lock);
- }
+ qemu_co_mutex_lock(&s->table_lock);
ret = bdrv_qed_do_open(bs, NULL, bs->open_flags, &local_err);
- if (qemu_in_coroutine()) {
- qemu_co_mutex_unlock(&s->table_lock);
- }
+ qemu_co_mutex_unlock(&s->table_lock);
if (local_err) {
error_propagate(errp, local_err);
error_prepend(errp, "Could not reopen qed layer: ");
@@ -1513,12 +1544,17 @@ static void bdrv_qed_invalidate_cache(BlockDriverState *bs, Error **errp)
}
}
-static int bdrv_qed_check(BlockDriverState *bs, BdrvCheckResult *result,
- BdrvCheckMode fix)
+static int bdrv_qed_co_check(BlockDriverState *bs, BdrvCheckResult *result,
+ BdrvCheckMode fix)
{
BDRVQEDState *s = bs->opaque;
+ int ret;
+
+ qemu_co_mutex_lock(&s->table_lock);
+ ret = qed_check(s, result, !!fix);
+ qemu_co_mutex_unlock(&s->table_lock);
- return qed_check(s, result, !!fix);
+ return ret;
}
static QemuOptsList qed_create_opts = {
@@ -1577,8 +1613,8 @@ static BlockDriver bdrv_qed = {
.bdrv_get_info = bdrv_qed_get_info,
.bdrv_refresh_limits = bdrv_qed_refresh_limits,
.bdrv_change_backing_file = bdrv_qed_change_backing_file,
- .bdrv_invalidate_cache = bdrv_qed_invalidate_cache,
- .bdrv_check = bdrv_qed_check,
+ .bdrv_co_invalidate_cache = bdrv_qed_co_invalidate_cache,
+ .bdrv_co_check = bdrv_qed_co_check,
.bdrv_detach_aio_context = bdrv_qed_detach_aio_context,
.bdrv_attach_aio_context = bdrv_qed_attach_aio_context,
.bdrv_co_drain_begin = bdrv_qed_co_drain_begin,
diff --git a/block/rbd.c b/block/rbd.c
index c7dd32e213..294ed07ac4 100644
--- a/block/rbd.c
+++ b/block/rbd.c
@@ -24,6 +24,8 @@
#include "qapi/qmp/qdict.h"
#include "qapi/qmp/qjson.h"
#include "qapi/qmp/qlist.h"
+#include "qapi/qobject-input-visitor.h"
+#include "qapi/qapi-visit-block-core.h"
/*
* When specifying the image filename use:
@@ -101,6 +103,11 @@ typedef struct BDRVRBDState {
char *snap;
} BDRVRBDState;
+static int qemu_rbd_connect(rados_t *cluster, rados_ioctx_t *io_ctx,
+ BlockdevOptionsRbd *opts, bool cache,
+ const char *keypairs, const char *secretid,
+ Error **errp);
+
static char *qemu_rbd_next_tok(char *src, char delim, char **p)
{
char *end;
@@ -268,13 +275,14 @@ static int qemu_rbd_set_keypairs(rados_t cluster, const char *keypairs_json,
key = qstring_get_str(name);
ret = rados_conf_set(cluster, key, qstring_get_str(value));
- QDECREF(name);
QDECREF(value);
if (ret < 0) {
error_setg_errno(errp, -ret, "invalid conf option %s", key);
+ QDECREF(name);
ret = -EINVAL;
break;
}
+ QDECREF(name);
}
QDECREF(keypairs);
@@ -325,67 +333,93 @@ static QemuOptsList runtime_opts = {
/*
* server.* extracted manually, see qemu_rbd_mon_host()
*/
- {
- .name = "password-secret",
- .type = QEMU_OPT_STRING,
- .help = "ID of secret providing the password",
- },
-
- /*
- * Keys for qemu_rbd_parse_filename(), not in the QAPI schema
- */
- {
- /*
- * HACK: name starts with '=' so that qemu_opts_parse()
- * can't set it
- */
- .name = "=keyvalue-pairs",
- .type = QEMU_OPT_STRING,
- .help = "Legacy rados key/value option parameters",
- },
- {
- .name = "filename",
- .type = QEMU_OPT_STRING,
- },
{ /* end of list */ }
},
};
-static int coroutine_fn qemu_rbd_co_create_opts(const char *filename,
- QemuOpts *opts,
- Error **errp)
+/* FIXME Deprecate and remove keypairs or make it available in QMP.
+ * password_secret should eventually be configurable in opts->location. Support
+ * for it in .bdrv_open will make it work here as well. */
+static int qemu_rbd_do_create(BlockdevCreateOptions *options,
+ const char *keypairs, const char *password_secret,
+ Error **errp)
{
- Error *local_err = NULL;
- int64_t bytes = 0;
- int64_t objsize;
- int obj_order = 0;
- const char *pool, *image_name, *conf, *user, *keypairs;
- const char *secretid;
+ BlockdevCreateOptionsRbd *opts = &options->u.rbd;
rados_t cluster;
rados_ioctx_t io_ctx;
- QDict *options = NULL;
- int ret = 0;
+ int obj_order = 0;
+ int ret;
- secretid = qemu_opt_get(opts, "password-secret");
+ assert(options->driver == BLOCKDEV_DRIVER_RBD);
+ if (opts->location->has_snapshot) {
+ error_setg(errp, "Can't use snapshot name for image creation");
+ return -EINVAL;
+ }
- /* Read out options */
- bytes = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
- BDRV_SECTOR_SIZE);
- objsize = qemu_opt_get_size_del(opts, BLOCK_OPT_CLUSTER_SIZE, 0);
- if (objsize) {
+ if (opts->has_cluster_size) {
+ int64_t objsize = opts->cluster_size;
if ((objsize - 1) & objsize) { /* not a power of 2? */
error_setg(errp, "obj size needs to be power of 2");
- ret = -EINVAL;
- goto exit;
+ return -EINVAL;
}
if (objsize < 4096) {
error_setg(errp, "obj size too small");
- ret = -EINVAL;
- goto exit;
+ return -EINVAL;
}
obj_order = ctz32(objsize);
}
+ ret = qemu_rbd_connect(&cluster, &io_ctx, opts->location, false, keypairs,
+ password_secret, errp);
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = rbd_create(io_ctx, opts->location->image, opts->size, &obj_order);
+ if (ret < 0) {
+ error_setg_errno(errp, -ret, "error rbd create");
+ goto out;
+ }
+
+ ret = 0;
+out:
+ rados_ioctx_destroy(io_ctx);
+ rados_shutdown(cluster);
+ return ret;
+}
+
+static int qemu_rbd_co_create(BlockdevCreateOptions *options, Error **errp)
+{
+ return qemu_rbd_do_create(options, NULL, NULL, errp);
+}
+
+static int coroutine_fn qemu_rbd_co_create_opts(const char *filename,
+ QemuOpts *opts,
+ Error **errp)
+{
+ BlockdevCreateOptions *create_options;
+ BlockdevCreateOptionsRbd *rbd_opts;
+ BlockdevOptionsRbd *loc;
+ Error *local_err = NULL;
+ const char *keypairs, *password_secret;
+ QDict *options = NULL;
+ int ret = 0;
+
+ create_options = g_new0(BlockdevCreateOptions, 1);
+ create_options->driver = BLOCKDEV_DRIVER_RBD;
+ rbd_opts = &create_options->u.rbd;
+
+ rbd_opts->location = g_new0(BlockdevOptionsRbd, 1);
+
+ password_secret = qemu_opt_get(opts, "password-secret");
+
+ /* Read out options */
+ rbd_opts->size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
+ BDRV_SECTOR_SIZE);
+ rbd_opts->cluster_size = qemu_opt_get_size_del(opts,
+ BLOCK_OPT_CLUSTER_SIZE, 0);
+ rbd_opts->has_cluster_size = (rbd_opts->cluster_size != 0);
+
options = qdict_new();
qemu_rbd_parse_filename(filename, options, &local_err);
if (local_err) {
@@ -400,61 +434,23 @@ static int coroutine_fn qemu_rbd_co_create_opts(const char *filename,
* or blockdev_add, its members are typed according to the QAPI
* schema, but when they come from -drive, they're all QString.
*/
- pool = qdict_get_try_str(options, "pool");
- conf = qdict_get_try_str(options, "conf");
- user = qdict_get_try_str(options, "user");
- image_name = qdict_get_try_str(options, "image");
- keypairs = qdict_get_try_str(options, "=keyvalue-pairs");
-
- ret = rados_create(&cluster, user);
+ loc = rbd_opts->location;
+ loc->pool = g_strdup(qdict_get_try_str(options, "pool"));
+ loc->conf = g_strdup(qdict_get_try_str(options, "conf"));
+ loc->has_conf = !!loc->conf;
+ loc->user = g_strdup(qdict_get_try_str(options, "user"));
+ loc->has_user = !!loc->user;
+ loc->image = g_strdup(qdict_get_try_str(options, "image"));
+ keypairs = qdict_get_try_str(options, "=keyvalue-pairs");
+
+ ret = qemu_rbd_do_create(create_options, keypairs, password_secret, errp);
if (ret < 0) {
- error_setg_errno(errp, -ret, "error initializing");
goto exit;
}
- /* try default location when conf=NULL, but ignore failure */
- ret = rados_conf_read_file(cluster, conf);
- if (conf && ret < 0) {
- error_setg_errno(errp, -ret, "error reading conf file %s", conf);
- ret = -EIO;
- goto shutdown;
- }
-
- ret = qemu_rbd_set_keypairs(cluster, keypairs, errp);
- if (ret < 0) {
- ret = -EIO;
- goto shutdown;
- }
-
- if (qemu_rbd_set_auth(cluster, secretid, errp) < 0) {
- ret = -EIO;
- goto shutdown;
- }
-
- ret = rados_connect(cluster);
- if (ret < 0) {
- error_setg_errno(errp, -ret, "error connecting");
- goto shutdown;
- }
-
- ret = rados_ioctx_create(cluster, pool, &io_ctx);
- if (ret < 0) {
- error_setg_errno(errp, -ret, "error opening pool %s", pool);
- goto shutdown;
- }
-
- ret = rbd_create(io_ctx, image_name, bytes, &obj_order);
- if (ret < 0) {
- error_setg_errno(errp, -ret, "error rbd create");
- }
-
- rados_ioctx_destroy(io_ctx);
-
-shutdown:
- rados_shutdown(cluster);
-
exit:
QDECREF(options);
+ qapi_free_BlockdevCreateOptions(create_options);
return ret;
}
@@ -505,131 +501,83 @@ static void qemu_rbd_complete_aio(RADOSCB *rcb)
qemu_aio_unref(acb);
}
-static char *qemu_rbd_mon_host(QDict *options, Error **errp)
+static char *qemu_rbd_mon_host(BlockdevOptionsRbd *opts, Error **errp)
{
- const char **vals = g_new(const char *, qdict_size(options) + 1);
- char keybuf[32];
+ const char **vals;
const char *host, *port;
char *rados_str;
- int i;
-
- for (i = 0;; i++) {
- sprintf(keybuf, "server.%d.host", i);
- host = qdict_get_try_str(options, keybuf);
- qdict_del(options, keybuf);
- sprintf(keybuf, "server.%d.port", i);
- port = qdict_get_try_str(options, keybuf);
- qdict_del(options, keybuf);
- if (!host && !port) {
- break;
- }
- if (!host) {
- error_setg(errp, "Parameter server.%d.host is missing", i);
- rados_str = NULL;
- goto out;
- }
+ InetSocketAddressBaseList *p;
+ int i, cnt;
+
+ if (!opts->has_server) {
+ return NULL;
+ }
+
+ for (cnt = 0, p = opts->server; p; p = p->next) {
+ cnt++;
+ }
+
+ vals = g_new(const char *, cnt + 1);
+
+ for (i = 0, p = opts->server; p; p = p->next, i++) {
+ host = p->value->host;
+ port = p->value->port;
if (strchr(host, ':')) {
- vals[i] = port ? g_strdup_printf("[%s]:%s", host, port)
- : g_strdup_printf("[%s]", host);
+ vals[i] = g_strdup_printf("[%s]:%s", host, port);
} else {
- vals[i] = port ? g_strdup_printf("%s:%s", host, port)
- : g_strdup(host);
+ vals[i] = g_strdup_printf("%s:%s", host, port);
}
}
vals[i] = NULL;
rados_str = i ? g_strjoinv(";", (char **)vals) : NULL;
-out:
g_strfreev((char **)vals);
return rados_str;
}
-static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
- Error **errp)
+static int qemu_rbd_connect(rados_t *cluster, rados_ioctx_t *io_ctx,
+ BlockdevOptionsRbd *opts, bool cache,
+ const char *keypairs, const char *secretid,
+ Error **errp)
{
- BDRVRBDState *s = bs->opaque;
- const char *pool, *snap, *conf, *user, *image_name, *keypairs;
- const char *secretid, *filename;
- QemuOpts *opts;
- Error *local_err = NULL;
char *mon_host = NULL;
+ Error *local_err = NULL;
int r;
- /* If we are given a filename, parse the filename, with precedence given to
- * filename encoded options */
- filename = qdict_get_try_str(options, "filename");
- if (filename) {
- warn_report("'filename' option specified. "
- "This is an unsupported option, and may be deprecated "
- "in the future");
- qemu_rbd_parse_filename(filename, options, &local_err);
- if (local_err) {
- r = -EINVAL;
- error_propagate(errp, local_err);
- goto exit;
- }
- }
-
- opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
- qemu_opts_absorb_qdict(opts, options, &local_err);
+ mon_host = qemu_rbd_mon_host(opts, &local_err);
if (local_err) {
error_propagate(errp, local_err);
r = -EINVAL;
goto failed_opts;
}
- mon_host = qemu_rbd_mon_host(options, &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
- r = -EINVAL;
- goto failed_opts;
- }
-
- secretid = qemu_opt_get(opts, "password-secret");
-
- pool = qemu_opt_get(opts, "pool");
- conf = qemu_opt_get(opts, "conf");
- snap = qemu_opt_get(opts, "snapshot");
- user = qemu_opt_get(opts, "user");
- image_name = qemu_opt_get(opts, "image");
- keypairs = qemu_opt_get(opts, "=keyvalue-pairs");
-
- if (!pool || !image_name) {
- error_setg(errp, "Parameters 'pool' and 'image' are required");
- r = -EINVAL;
- goto failed_opts;
- }
-
- r = rados_create(&s->cluster, user);
+ r = rados_create(cluster, opts->user);
if (r < 0) {
error_setg_errno(errp, -r, "error initializing");
goto failed_opts;
}
- s->snap = g_strdup(snap);
- s->image_name = g_strdup(image_name);
-
/* try default location when conf=NULL, but ignore failure */
- r = rados_conf_read_file(s->cluster, conf);
- if (conf && r < 0) {
- error_setg_errno(errp, -r, "error reading conf file %s", conf);
+ r = rados_conf_read_file(*cluster, opts->conf);
+ if (opts->has_conf && r < 0) {
+ error_setg_errno(errp, -r, "error reading conf file %s", opts->conf);
goto failed_shutdown;
}
- r = qemu_rbd_set_keypairs(s->cluster, keypairs, errp);
+ r = qemu_rbd_set_keypairs(*cluster, keypairs, errp);
if (r < 0) {
goto failed_shutdown;
}
if (mon_host) {
- r = rados_conf_set(s->cluster, "mon_host", mon_host);
+ r = rados_conf_set(*cluster, "mon_host", mon_host);
if (r < 0) {
goto failed_shutdown;
}
}
- if (qemu_rbd_set_auth(s->cluster, secretid, errp) < 0) {
+ if (qemu_rbd_set_auth(*cluster, secretid, errp) < 0) {
r = -EIO;
goto failed_shutdown;
}
@@ -641,24 +589,97 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
* librbd defaults to no caching. If write through caching cannot
* be set up, fall back to no caching.
*/
- if (flags & BDRV_O_NOCACHE) {
- rados_conf_set(s->cluster, "rbd_cache", "false");
+ if (cache) {
+ rados_conf_set(*cluster, "rbd_cache", "true");
} else {
- rados_conf_set(s->cluster, "rbd_cache", "true");
+ rados_conf_set(*cluster, "rbd_cache", "false");
}
- r = rados_connect(s->cluster);
+ r = rados_connect(*cluster);
if (r < 0) {
error_setg_errno(errp, -r, "error connecting");
goto failed_shutdown;
}
- r = rados_ioctx_create(s->cluster, pool, &s->io_ctx);
+ r = rados_ioctx_create(*cluster, opts->pool, io_ctx);
if (r < 0) {
- error_setg_errno(errp, -r, "error opening pool %s", pool);
+ error_setg_errno(errp, -r, "error opening pool %s", opts->pool);
goto failed_shutdown;
}
+ return 0;
+
+failed_shutdown:
+ rados_shutdown(*cluster);
+failed_opts:
+ g_free(mon_host);
+ return r;
+}
+
+static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
+ Error **errp)
+{
+ BDRVRBDState *s = bs->opaque;
+ BlockdevOptionsRbd *opts = NULL;
+ Visitor *v;
+ QObject *crumpled = NULL;
+ Error *local_err = NULL;
+ const char *filename;
+ char *keypairs, *secretid;
+ int r;
+
+ /* If we are given a filename, parse the filename, with precedence given to
+ * filename encoded options */
+ filename = qdict_get_try_str(options, "filename");
+ if (filename) {
+ warn_report("'filename' option specified. "
+ "This is an unsupported option, and may be deprecated "
+ "in the future");
+ qemu_rbd_parse_filename(filename, options, &local_err);
+ qdict_del(options, "filename");
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return -EINVAL;
+ }
+ }
+
+ keypairs = g_strdup(qdict_get_try_str(options, "=keyvalue-pairs"));
+ if (keypairs) {
+ qdict_del(options, "=keyvalue-pairs");
+ }
+
+ secretid = g_strdup(qdict_get_try_str(options, "password-secret"));
+ if (secretid) {
+ qdict_del(options, "password-secret");
+ }
+
+ /* Convert the remaining options into a QAPI object */
+ crumpled = qdict_crumple(options, errp);
+ if (crumpled == NULL) {
+ r = -EINVAL;
+ goto out;
+ }
+
+ v = qobject_input_visitor_new_keyval(crumpled);
+ visit_type_BlockdevOptionsRbd(v, NULL, &opts, &local_err);
+ visit_free(v);
+ qobject_decref(crumpled);
+
+ if (local_err) {
+ error_propagate(errp, local_err);
+ r = -EINVAL;
+ goto out;
+ }
+
+ r = qemu_rbd_connect(&s->cluster, &s->io_ctx, opts,
+ !(flags & BDRV_O_NOCACHE), keypairs, secretid, errp);
+ if (r < 0) {
+ goto out;
+ }
+
+ s->snap = g_strdup(opts->snapshot);
+ s->image_name = g_strdup(opts->image);
+
/* rbd_open is always r/w */
r = rbd_open(s->io_ctx, s->image_name, &s->image, s->snap);
if (r < 0) {
@@ -683,19 +704,18 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
}
}
- qemu_opts_del(opts);
- return 0;
+ r = 0;
+ goto out;
failed_open:
rados_ioctx_destroy(s->io_ctx);
-failed_shutdown:
- rados_shutdown(s->cluster);
g_free(s->snap);
g_free(s->image_name);
-failed_opts:
- qemu_opts_del(opts);
- g_free(mon_host);
-exit:
+ rados_shutdown(s->cluster);
+out:
+ qapi_free_BlockdevOptionsRbd(opts);
+ g_free(keypairs);
+ g_free(secretid);
return r;
}
@@ -1093,8 +1113,8 @@ static BlockAIOCB *qemu_rbd_aio_pdiscard(BlockDriverState *bs,
#endif
#ifdef LIBRBD_SUPPORTS_INVALIDATE
-static void qemu_rbd_invalidate_cache(BlockDriverState *bs,
- Error **errp)
+static void coroutine_fn qemu_rbd_co_invalidate_cache(BlockDriverState *bs,
+ Error **errp)
{
BDRVRBDState *s = bs->opaque;
int r = rbd_invalidate_cache(s->image);
@@ -1134,6 +1154,7 @@ static BlockDriver bdrv_rbd = {
.bdrv_file_open = qemu_rbd_open,
.bdrv_close = qemu_rbd_close,
.bdrv_reopen_prepare = qemu_rbd_reopen_prepare,
+ .bdrv_co_create = qemu_rbd_co_create,
.bdrv_co_create_opts = qemu_rbd_co_create_opts,
.bdrv_has_zero_init = bdrv_has_zero_init_1,
.bdrv_get_info = qemu_rbd_getinfo,
@@ -1160,7 +1181,7 @@ static BlockDriver bdrv_rbd = {
.bdrv_snapshot_list = qemu_rbd_snap_list,
.bdrv_snapshot_goto = qemu_rbd_snap_rollback,
#ifdef LIBRBD_SUPPORTS_INVALIDATE
- .bdrv_invalidate_cache = qemu_rbd_invalidate_cache,
+ .bdrv_co_invalidate_cache = qemu_rbd_co_invalidate_cache,
#endif
};
diff --git a/block/sheepdog.c b/block/sheepdog.c
index d8c10b7cac..8680b2926f 100644
--- a/block/sheepdog.c
+++ b/block/sheepdog.c
@@ -15,8 +15,10 @@
#include "qemu/osdep.h"
#include "qapi/error.h"
#include "qapi/qapi-visit-sockets.h"
+#include "qapi/qapi-visit-block-core.h"
#include "qapi/qmp/qdict.h"
#include "qapi/qobject-input-visitor.h"
+#include "qapi/qobject-output-visitor.h"
#include "qemu/uri.h"
#include "qemu/error-report.h"
#include "qemu/option.h"
@@ -533,23 +535,6 @@ static void sd_aio_setup(SheepdogAIOCB *acb, BDRVSheepdogState *s,
qemu_co_mutex_unlock(&s->queue_lock);
}
-static SocketAddress *sd_socket_address(const char *path,
- const char *host, const char *port)
-{
- SocketAddress *addr = g_new0(SocketAddress, 1);
-
- if (path) {
- addr->type = SOCKET_ADDRESS_TYPE_UNIX;
- addr->u.q_unix.path = g_strdup(path);
- } else {
- addr->type = SOCKET_ADDRESS_TYPE_INET;
- addr->u.inet.host = g_strdup(host ?: SD_DEFAULT_ADDR);
- addr->u.inet.port = g_strdup(port ?: stringify(SD_DEFAULT_PORT));
- }
-
- return addr;
-}
-
static SocketAddress *sd_server_config(QDict *options, Error **errp)
{
QDict *server = NULL;
@@ -1882,6 +1867,86 @@ out_with_err_set:
return ret;
}
+static int sd_create_prealloc(BlockdevOptionsSheepdog *location, int64_t size,
+ Error **errp)
+{
+ BlockDriverState *bs;
+ Visitor *v;
+ QObject *obj = NULL;
+ QDict *qdict;
+ Error *local_err = NULL;
+ int ret;
+
+ v = qobject_output_visitor_new(&obj);
+ visit_type_BlockdevOptionsSheepdog(v, NULL, &location, &local_err);
+ visit_free(v);
+
+ if (local_err) {
+ error_propagate(errp, local_err);
+ qobject_decref(obj);
+ return -EINVAL;
+ }
+
+ qdict = qobject_to_qdict(obj);
+ qdict_flatten(qdict);
+
+ qdict_put_str(qdict, "driver", "sheepdog");
+
+ bs = bdrv_open(NULL, NULL, qdict, BDRV_O_PROTOCOL | BDRV_O_RDWR, errp);
+ if (bs == NULL) {
+ ret = -EIO;
+ goto fail;
+ }
+
+ ret = sd_prealloc(bs, 0, size, errp);
+fail:
+ bdrv_unref(bs);
+ QDECREF(qdict);
+ return ret;
+}
+
+static int parse_redundancy(BDRVSheepdogState *s, SheepdogRedundancy *opt)
+{
+ struct SheepdogInode *inode = &s->inode;
+
+ switch (opt->type) {
+ case SHEEPDOG_REDUNDANCY_TYPE_FULL:
+ if (opt->u.full.copies > SD_MAX_COPIES || opt->u.full.copies < 1) {
+ return -EINVAL;
+ }
+ inode->copy_policy = 0;
+ inode->nr_copies = opt->u.full.copies;
+ return 0;
+
+ case SHEEPDOG_REDUNDANCY_TYPE_ERASURE_CODED:
+ {
+ int64_t copy = opt->u.erasure_coded.data_strips;
+ int64_t parity = opt->u.erasure_coded.parity_strips;
+
+ if (copy != 2 && copy != 4 && copy != 8 && copy != 16) {
+ return -EINVAL;
+ }
+
+ if (parity >= SD_EC_MAX_STRIP || parity < 1) {
+ return -EINVAL;
+ }
+
+ /*
+ * 4 bits for parity and 4 bits for data.
+ * We have to compress upper data bits because it can't represent 16
+ */
+ inode->copy_policy = ((copy / 2) << 4) + parity;
+ inode->nr_copies = copy + parity;
+ return 0;
+ }
+
+ default:
+ g_assert_not_reached();
+ }
+
+ return -EINVAL;
+}
+
/*
* Sheepdog support two kinds of redundancy, full replication and erasure
* coding.
@@ -1892,60 +1957,61 @@ out_with_err_set:
* # create a erasure coded vdi with x data strips and y parity strips
* -o redundancy=x:y (x must be one of {2,4,8,16} and 1 <= y < SD_EC_MAX_STRIP)
*/
-static int parse_redundancy(BDRVSheepdogState *s, const char *opt)
+static SheepdogRedundancy *parse_redundancy_str(const char *opt)
{
- struct SheepdogInode *inode = &s->inode;
+ SheepdogRedundancy *redundancy;
const char *n1, *n2;
long copy, parity;
char p[10];
+ int ret;
pstrcpy(p, sizeof(p), opt);
n1 = strtok(p, ":");
n2 = strtok(NULL, ":");
if (!n1) {
- return -EINVAL;
+ return NULL;
}
- copy = strtol(n1, NULL, 10);
- /* FIXME fix error checking by switching to qemu_strtol() */
- if (copy > SD_MAX_COPIES || copy < 1) {
- return -EINVAL;
- }
- if (!n2) {
- inode->copy_policy = 0;
- inode->nr_copies = copy;
- return 0;
+ ret = qemu_strtol(n1, NULL, 10, &copy);
+ if (ret < 0) {
+ return NULL;
}
- if (copy != 2 && copy != 4 && copy != 8 && copy != 16) {
- return -EINVAL;
- }
+ redundancy = g_new0(SheepdogRedundancy, 1);
+ if (!n2) {
+ *redundancy = (SheepdogRedundancy) {
+ .type = SHEEPDOG_REDUNDANCY_TYPE_FULL,
+ .u.full.copies = copy,
+ };
+ } else {
+ ret = qemu_strtol(n2, NULL, 10, &parity);
+ if (ret < 0) {
+ return NULL;
+ }
- parity = strtol(n2, NULL, 10);
- /* FIXME fix error checking by switching to qemu_strtol() */
- if (parity >= SD_EC_MAX_STRIP || parity < 1) {
- return -EINVAL;
+ *redundancy = (SheepdogRedundancy) {
+ .type = SHEEPDOG_REDUNDANCY_TYPE_ERASURE_CODED,
+ .u.erasure_coded = {
+ .data_strips = copy,
+ .parity_strips = parity,
+ },
+ };
}
- /*
- * 4 bits for parity and 4 bits for data.
- * We have to compress upper data bits because it can't represent 16
- */
- inode->copy_policy = ((copy / 2) << 4) + parity;
- inode->nr_copies = copy + parity;
-
- return 0;
+ return redundancy;
}
-static int parse_block_size_shift(BDRVSheepdogState *s, QemuOpts *opt)
+static int parse_block_size_shift(BDRVSheepdogState *s,
+ BlockdevCreateOptionsSheepdog *opts)
{
struct SheepdogInode *inode = &s->inode;
uint64_t object_size;
int obj_order;
- object_size = qemu_opt_get_size_del(opt, BLOCK_OPT_OBJECT_SIZE, 0);
- if (object_size) {
+ if (opts->has_object_size) {
+ object_size = opts->object_size;
+
if ((object_size - 1) & object_size) { /* not a power of 2? */
return -EINVAL;
}
@@ -1959,57 +2025,55 @@ static int parse_block_size_shift(BDRVSheepdogState *s, QemuOpts *opt)
return 0;
}
-static int coroutine_fn sd_co_create_opts(const char *filename, QemuOpts *opts,
- Error **errp)
+static int sd_co_create(BlockdevCreateOptions *options, Error **errp)
{
- Error *err = NULL;
+ BlockdevCreateOptionsSheepdog *opts = &options->u.sheepdog;
int ret = 0;
uint32_t vid = 0;
char *backing_file = NULL;
char *buf = NULL;
BDRVSheepdogState *s;
- SheepdogConfig cfg;
uint64_t max_vdi_size;
bool prealloc = false;
+ assert(options->driver == BLOCKDEV_DRIVER_SHEEPDOG);
+
s = g_new0(BDRVSheepdogState, 1);
- if (strstr(filename, "://")) {
- sd_parse_uri(&cfg, filename, &err);
- } else {
- parse_vdiname(&cfg, filename, &err);
- }
- if (err) {
- error_propagate(errp, err);
+ /* Steal SocketAddress from QAPI, set NULL to prevent double free */
+ s->addr = opts->location->server;
+ opts->location->server = NULL;
+
+ if (strlen(opts->location->vdi) >= sizeof(s->name)) {
+ error_setg(errp, "'vdi' string too long");
+ ret = -EINVAL;
goto out;
}
+ pstrcpy(s->name, sizeof(s->name), opts->location->vdi);
- buf = cfg.port ? g_strdup_printf("%d", cfg.port) : NULL;
- s->addr = sd_socket_address(cfg.path, cfg.host, buf);
- g_free(buf);
- strcpy(s->name, cfg.vdi);
- sd_config_done(&cfg);
+ s->inode.vdi_size = opts->size;
+ backing_file = opts->backing_file;
- s->inode.vdi_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
- BDRV_SECTOR_SIZE);
- backing_file = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FILE);
- buf = qemu_opt_get_del(opts, BLOCK_OPT_PREALLOC);
- if (!buf || !strcmp(buf, "off")) {
+ if (!opts->has_preallocation) {
+ opts->preallocation = PREALLOC_MODE_OFF;
+ }
+ switch (opts->preallocation) {
+ case PREALLOC_MODE_OFF:
prealloc = false;
- } else if (!strcmp(buf, "full")) {
+ break;
+ case PREALLOC_MODE_FULL:
prealloc = true;
- } else {
- error_setg(errp, "Invalid preallocation mode: '%s'", buf);
+ break;
+ default:
+ error_setg(errp, "Preallocation mode not supported for Sheepdog");
ret = -EINVAL;
goto out;
}
- g_free(buf);
- buf = qemu_opt_get_del(opts, BLOCK_OPT_REDUNDANCY);
- if (buf) {
- ret = parse_redundancy(s, buf);
+ if (opts->has_redundancy) {
+ ret = parse_redundancy(s, opts->redundancy);
if (ret < 0) {
- error_setg(errp, "Invalid redundancy mode: '%s'", buf);
+ error_setg(errp, "Invalid redundancy mode");
goto out;
}
}
@@ -2021,20 +2085,20 @@ static int coroutine_fn sd_co_create_opts(const char *filename, QemuOpts *opts,
goto out;
}
- if (backing_file) {
+ if (opts->has_backing_file) {
BlockBackend *blk;
BDRVSheepdogState *base;
BlockDriver *drv;
/* Currently, only Sheepdog backing image is supported. */
- drv = bdrv_find_protocol(backing_file, true, NULL);
+ drv = bdrv_find_protocol(opts->backing_file, true, NULL);
if (!drv || strcmp(drv->protocol_name, "sheepdog") != 0) {
error_setg(errp, "backing_file must be a sheepdog image");
ret = -EINVAL;
goto out;
}
- blk = blk_new_open(backing_file, NULL, NULL,
+ blk = blk_new_open(opts->backing_file, NULL, NULL,
BDRV_O_PROTOCOL, errp);
if (blk == NULL) {
ret = -EIO;
@@ -2102,28 +2166,96 @@ static int coroutine_fn sd_co_create_opts(const char *filename, QemuOpts *opts,
}
if (prealloc) {
- BlockDriverState *bs;
- QDict *opts;
-
- opts = qdict_new();
- qdict_put_str(opts, "driver", "sheepdog");
- bs = bdrv_open(filename, NULL, opts, BDRV_O_PROTOCOL | BDRV_O_RDWR,
- errp);
- if (!bs) {
- goto out;
- }
-
- ret = sd_prealloc(bs, 0, s->inode.vdi_size, errp);
-
- bdrv_unref(bs);
+ ret = sd_create_prealloc(opts->location, opts->size, errp);
}
out:
g_free(backing_file);
g_free(buf);
+ g_free(s->addr);
g_free(s);
return ret;
}
+static int coroutine_fn sd_co_create_opts(const char *filename, QemuOpts *opts,
+ Error **errp)
+{
+ BlockdevCreateOptions *create_options = NULL;
+ QDict *qdict, *location_qdict;
+ QObject *crumpled;
+ Visitor *v;
+ const char *redundancy;
+ Error *local_err = NULL;
+ int ret;
+
+ redundancy = qemu_opt_get_del(opts, BLOCK_OPT_REDUNDANCY);
+
+ qdict = qemu_opts_to_qdict(opts, NULL);
+ qdict_put_str(qdict, "driver", "sheepdog");
+
+ location_qdict = qdict_new();
+ qdict_put(qdict, "location", location_qdict);
+
+ sd_parse_filename(filename, location_qdict, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ qdict_flatten(qdict);
+
+ /* Change legacy command line options into QMP ones */
+ static const QDictRenames opt_renames[] = {
+ { BLOCK_OPT_BACKING_FILE, "backing-file" },
+ { BLOCK_OPT_OBJECT_SIZE, "object-size" },
+ { NULL, NULL },
+ };
+
+ if (!qdict_rename_keys(qdict, opt_renames, errp)) {
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ /* Get the QAPI object */
+ crumpled = qdict_crumple(qdict, errp);
+ if (crumpled == NULL) {
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ v = qobject_input_visitor_new_keyval(crumpled);
+ visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err);
+ visit_free(v);
+ qobject_decref(crumpled);
+
+ if (local_err) {
+ error_propagate(errp, local_err);
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ assert(create_options->driver == BLOCKDEV_DRIVER_SHEEPDOG);
+ create_options->u.sheepdog.size =
+ ROUND_UP(create_options->u.sheepdog.size, BDRV_SECTOR_SIZE);
+
+ if (redundancy) {
+ create_options->u.sheepdog.has_redundancy = true;
+ create_options->u.sheepdog.redundancy =
+ parse_redundancy_str(redundancy);
+ if (create_options->u.sheepdog.redundancy == NULL) {
+ error_setg(errp, "Invalid redundancy mode");
+ ret = -EINVAL;
+ goto fail;
+ }
+ }
+
+ ret = sd_co_create(create_options, errp);
+fail:
+ qapi_free_BlockdevCreateOptions(create_options);
+ QDECREF(qdict);
+ return ret;
+}
+
static void sd_close(BlockDriverState *bs)
{
Error *local_err = NULL;
@@ -3103,6 +3235,7 @@ static BlockDriver bdrv_sheepdog = {
.bdrv_reopen_commit = sd_reopen_commit,
.bdrv_reopen_abort = sd_reopen_abort,
.bdrv_close = sd_close,
+ .bdrv_co_create = sd_co_create,
.bdrv_co_create_opts = sd_co_create_opts,
.bdrv_has_zero_init = bdrv_has_zero_init_1,
.bdrv_getlength = sd_getlength,
@@ -3139,6 +3272,7 @@ static BlockDriver bdrv_sheepdog_tcp = {
.bdrv_reopen_commit = sd_reopen_commit,
.bdrv_reopen_abort = sd_reopen_abort,
.bdrv_close = sd_close,
+ .bdrv_co_create = sd_co_create,
.bdrv_co_create_opts = sd_co_create_opts,
.bdrv_has_zero_init = bdrv_has_zero_init_1,
.bdrv_getlength = sd_getlength,
@@ -3175,6 +3309,7 @@ static BlockDriver bdrv_sheepdog_unix = {
.bdrv_reopen_commit = sd_reopen_commit,
.bdrv_reopen_abort = sd_reopen_abort,
.bdrv_close = sd_close,
+ .bdrv_co_create = sd_co_create,
.bdrv_co_create_opts = sd_co_create_opts,
.bdrv_has_zero_init = bdrv_has_zero_init_1,
.bdrv_getlength = sd_getlength,
diff --git a/block/ssh.c b/block/ssh.c
index ff9929497d..ab3acf0c22 100644
--- a/block/ssh.c
+++ b/block/ssh.c
@@ -35,6 +35,7 @@
#include "qemu/sockets.h"
#include "qemu/uri.h"
#include "qapi/qapi-visit-sockets.h"
+#include "qapi/qapi-visit-block-core.h"
#include "qapi/qmp/qdict.h"
#include "qapi/qmp/qstring.h"
#include "qapi/qobject-input-visitor.h"
@@ -430,31 +431,35 @@ check_host_key_hash(BDRVSSHState *s, const char *hash,
}
static int check_host_key(BDRVSSHState *s, const char *host, int port,
- const char *host_key_check, Error **errp)
+ SshHostKeyCheck *hkc, Error **errp)
{
- /* host_key_check=no */
- if (strcmp(host_key_check, "no") == 0) {
- return 0;
- }
-
- /* host_key_check=md5:xx:yy:zz:... */
- if (strncmp(host_key_check, "md5:", 4) == 0) {
- return check_host_key_hash(s, &host_key_check[4],
- LIBSSH2_HOSTKEY_HASH_MD5, 16, errp);
- }
+ SshHostKeyCheckMode mode;
- /* host_key_check=sha1:xx:yy:zz:... */
- if (strncmp(host_key_check, "sha1:", 5) == 0) {
- return check_host_key_hash(s, &host_key_check[5],
- LIBSSH2_HOSTKEY_HASH_SHA1, 20, errp);
+ if (hkc) {
+ mode = hkc->mode;
+ } else {
+ mode = SSH_HOST_KEY_CHECK_MODE_KNOWN_HOSTS;
}
- /* host_key_check=yes */
- if (strcmp(host_key_check, "yes") == 0) {
+ switch (mode) {
+ case SSH_HOST_KEY_CHECK_MODE_NONE:
+ return 0;
+ case SSH_HOST_KEY_CHECK_MODE_HASH:
+ if (hkc->u.hash.type == SSH_HOST_KEY_CHECK_HASH_TYPE_MD5) {
+ return check_host_key_hash(s, hkc->u.hash.hash,
+ LIBSSH2_HOSTKEY_HASH_MD5, 16, errp);
+ } else if (hkc->u.hash.type == SSH_HOST_KEY_CHECK_HASH_TYPE_SHA1) {
+ return check_host_key_hash(s, hkc->u.hash.hash,
+ LIBSSH2_HOSTKEY_HASH_SHA1, 20, errp);
+ }
+ g_assert_not_reached();
+ break;
+ case SSH_HOST_KEY_CHECK_MODE_KNOWN_HOSTS:
return check_host_key_knownhosts(s, host, port, errp);
+ default:
+ g_assert_not_reached();
}
- error_setg(errp, "unknown host_key_check setting (%s)", host_key_check);
return -EINVAL;
}
@@ -544,16 +549,6 @@ static QemuOptsList ssh_runtime_opts = {
.help = "Port to connect to",
},
{
- .name = "path",
- .type = QEMU_OPT_STRING,
- .help = "Path of the image on the host",
- },
- {
- .name = "user",
- .type = QEMU_OPT_STRING,
- .help = "User as which to connect",
- },
- {
.name = "host_key_check",
.type = QEMU_OPT_STRING,
.help = "Defines how and what to check the host key against",
@@ -562,12 +557,13 @@ static QemuOptsList ssh_runtime_opts = {
},
};
-static bool ssh_process_legacy_socket_options(QDict *output_opts,
- QemuOpts *legacy_opts,
- Error **errp)
+static bool ssh_process_legacy_options(QDict *output_opts,
+ QemuOpts *legacy_opts,
+ Error **errp)
{
const char *host = qemu_opt_get(legacy_opts, "host");
const char *port = qemu_opt_get(legacy_opts, "port");
+ const char *host_key_check = qemu_opt_get(legacy_opts, "host_key_check");
if (!host && port) {
error_setg(errp, "port may not be used without host");
@@ -579,26 +575,56 @@ static bool ssh_process_legacy_socket_options(QDict *output_opts,
qdict_put_str(output_opts, "server.port", port ?: stringify(22));
}
+ if (host_key_check) {
+ if (strcmp(host_key_check, "no") == 0) {
+ qdict_put_str(output_opts, "host-key-check.mode", "none");
+ } else if (strncmp(host_key_check, "md5:", 4) == 0) {
+ qdict_put_str(output_opts, "host-key-check.mode", "hash");
+ qdict_put_str(output_opts, "host-key-check.type", "md5");
+ qdict_put_str(output_opts, "host-key-check.hash",
+ &host_key_check[4]);
+ } else if (strncmp(host_key_check, "sha1:", 5) == 0) {
+ qdict_put_str(output_opts, "host-key-check.mode", "hash");
+ qdict_put_str(output_opts, "host-key-check.type", "sha1");
+ qdict_put_str(output_opts, "host-key-check.hash",
+ &host_key_check[5]);
+ } else if (strcmp(host_key_check, "yes") == 0) {
+ qdict_put_str(output_opts, "host-key-check.mode", "known_hosts");
+ } else {
+ error_setg(errp, "unknown host_key_check setting (%s)",
+ host_key_check);
+ return false;
+ }
+ }
+
return true;
}
-static InetSocketAddress *ssh_config(QDict *options, Error **errp)
+static BlockdevOptionsSsh *ssh_parse_options(QDict *options, Error **errp)
{
- InetSocketAddress *inet = NULL;
- QDict *addr = NULL;
- QObject *crumpled_addr = NULL;
- Visitor *iv = NULL;
- Error *local_error = NULL;
-
- qdict_extract_subqdict(options, &addr, "server.");
- if (!qdict_size(addr)) {
- error_setg(errp, "SSH server address missing");
- goto out;
+ BlockdevOptionsSsh *result = NULL;
+ QemuOpts *opts = NULL;
+ Error *local_err = NULL;
+ QObject *crumpled;
+ const QDictEntry *e;
+ Visitor *v;
+
+ /* Translate legacy options */
+ opts = qemu_opts_create(&ssh_runtime_opts, NULL, 0, &error_abort);
+ qemu_opts_absorb_qdict(opts, options, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ goto fail;
}
- crumpled_addr = qdict_crumple(addr, errp);
- if (!crumpled_addr) {
- goto out;
+ if (!ssh_process_legacy_options(options, opts, errp)) {
+ goto fail;
+ }
+
+ /* Create the QAPI object */
+ crumpled = qdict_crumple(options, errp);
+ if (crumpled == NULL) {
+ goto fail;
}
/*
@@ -609,51 +635,37 @@ static InetSocketAddress *ssh_config(QDict *options, Error **errp)
* but when they come from -drive, they're all QString. The
* visitor expects the former.
*/
- iv = qobject_input_visitor_new(crumpled_addr);
- visit_type_InetSocketAddress(iv, NULL, &inet, &local_error);
- if (local_error) {
- error_propagate(errp, local_error);
- goto out;
+ v = qobject_input_visitor_new(crumpled);
+ visit_type_BlockdevOptionsSsh(v, NULL, &result, &local_err);
+ visit_free(v);
+ qobject_decref(crumpled);
+
+ if (local_err) {
+ error_propagate(errp, local_err);
+ goto fail;
}
-out:
- QDECREF(addr);
- qobject_decref(crumpled_addr);
- visit_free(iv);
- return inet;
+ /* Remove the processed options from the QDict (the visitor processes
+ * _all_ options in the QDict) */
+ while ((e = qdict_first(options))) {
+ qdict_del(options, e->key);
+ }
+
+fail:
+ qemu_opts_del(opts);
+ return result;
}
-static int connect_to_ssh(BDRVSSHState *s, QDict *options,
+static int connect_to_ssh(BDRVSSHState *s, BlockdevOptionsSsh *opts,
int ssh_flags, int creat_mode, Error **errp)
{
int r, ret;
- QemuOpts *opts = NULL;
- Error *local_err = NULL;
- const char *user, *path, *host_key_check;
+ const char *user;
long port = 0;
- opts = qemu_opts_create(&ssh_runtime_opts, NULL, 0, &error_abort);
- qemu_opts_absorb_qdict(opts, options, &local_err);
- if (local_err) {
- ret = -EINVAL;
- error_propagate(errp, local_err);
- goto err;
- }
-
- if (!ssh_process_legacy_socket_options(options, opts, errp)) {
- ret = -EINVAL;
- goto err;
- }
-
- path = qemu_opt_get(opts, "path");
- if (!path) {
- ret = -EINVAL;
- error_setg(errp, "No path was specified");
- goto err;
- }
-
- user = qemu_opt_get(opts, "user");
- if (!user) {
+ if (opts->has_user) {
+ user = opts->user;
+ } else {
user = g_get_user_name();
if (!user) {
error_setg_errno(errp, errno, "Can't get user name");
@@ -662,17 +674,9 @@ static int connect_to_ssh(BDRVSSHState *s, QDict *options,
}
}
- host_key_check = qemu_opt_get(opts, "host_key_check");
- if (!host_key_check) {
- host_key_check = "yes";
- }
-
/* Pop the config into our state object, Exit if invalid */
- s->inet = ssh_config(options, errp);
- if (!s->inet) {
- ret = -EINVAL;
- goto err;
- }
+ s->inet = opts->server;
+ opts->server = NULL;
if (qemu_strtol(s->inet->port, NULL, 10, &port) < 0) {
error_setg(errp, "Use only numeric port value");
@@ -707,8 +711,7 @@ static int connect_to_ssh(BDRVSSHState *s, QDict *options,
}
/* Check the remote host's key against known_hosts. */
- ret = check_host_key(s, s->inet->host, port, host_key_check,
- errp);
+ ret = check_host_key(s, s->inet->host, port, opts->host_key_check, errp);
if (ret < 0) {
goto err;
}
@@ -729,16 +732,16 @@ static int connect_to_ssh(BDRVSSHState *s, QDict *options,
/* Open the remote file. */
DPRINTF("opening file %s flags=0x%x creat_mode=0%o",
- path, ssh_flags, creat_mode);
- s->sftp_handle = libssh2_sftp_open(s->sftp, path, ssh_flags, creat_mode);
+ opts->path, ssh_flags, creat_mode);
+ s->sftp_handle = libssh2_sftp_open(s->sftp, opts->path, ssh_flags,
+ creat_mode);
if (!s->sftp_handle) {
- session_error_setg(errp, s, "failed to open remote file '%s'", path);
+ session_error_setg(errp, s, "failed to open remote file '%s'",
+ opts->path);
ret = -EINVAL;
goto err;
}
- qemu_opts_del(opts);
-
r = libssh2_sftp_fstat(s->sftp_handle, &s->attrs);
if (r < 0) {
sftp_error_setg(errp, s, "failed to read file attributes");
@@ -764,8 +767,6 @@ static int connect_to_ssh(BDRVSSHState *s, QDict *options,
}
s->session = NULL;
- qemu_opts_del(opts);
-
return ret;
}
@@ -773,6 +774,7 @@ static int ssh_file_open(BlockDriverState *bs, QDict *options, int bdrv_flags,
Error **errp)
{
BDRVSSHState *s = bs->opaque;
+ BlockdevOptionsSsh *opts;
int ret;
int ssh_flags;
@@ -783,8 +785,13 @@ static int ssh_file_open(BlockDriverState *bs, QDict *options, int bdrv_flags,
ssh_flags |= LIBSSH2_FXF_WRITE;
}
+ opts = ssh_parse_options(options, errp);
+ if (opts == NULL) {
+ return -EINVAL;
+ }
+
/* Start up SSH. */
- ret = connect_to_ssh(s, options, ssh_flags, 0, errp);
+ ret = connect_to_ssh(s, opts, ssh_flags, 0, errp);
if (ret < 0) {
goto err;
}
@@ -792,6 +799,8 @@ static int ssh_file_open(BlockDriverState *bs, QDict *options, int bdrv_flags,
/* Go non-blocking. */
libssh2_session_set_blocking(s->session, 0);
+ qapi_free_BlockdevOptionsSsh(opts);
+
return 0;
err:
@@ -800,6 +809,8 @@ static int ssh_file_open(BlockDriverState *bs, QDict *options, int bdrv_flags,
}
s->sock = -1;
+ qapi_free_BlockdevOptionsSsh(opts);
+
return ret;
}
@@ -843,51 +854,71 @@ static QemuOptsList ssh_create_opts = {
}
};
+static int ssh_co_create(BlockdevCreateOptions *options, Error **errp)
+{
+ BlockdevCreateOptionsSsh *opts = &options->u.ssh;
+ BDRVSSHState s;
+ int ret;
+
+ assert(options->driver == BLOCKDEV_DRIVER_SSH);
+
+ ssh_state_init(&s);
+
+ ret = connect_to_ssh(&s, opts->location,
+ LIBSSH2_FXF_READ|LIBSSH2_FXF_WRITE|
+ LIBSSH2_FXF_CREAT|LIBSSH2_FXF_TRUNC,
+ 0644, errp);
+ if (ret < 0) {
+ goto fail;
+ }
+
+ if (opts->size > 0) {
+ ret = ssh_grow_file(&s, opts->size, errp);
+ if (ret < 0) {
+ goto fail;
+ }
+ }
+
+ ret = 0;
+fail:
+ ssh_state_free(&s);
+ return ret;
+}
+
static int coroutine_fn ssh_co_create_opts(const char *filename, QemuOpts *opts,
Error **errp)
{
- int r, ret;
- int64_t total_size = 0;
+ BlockdevCreateOptions *create_options;
+ BlockdevCreateOptionsSsh *ssh_opts;
+ int ret;
QDict *uri_options = NULL;
- BDRVSSHState s;
- ssh_state_init(&s);
+ create_options = g_new0(BlockdevCreateOptions, 1);
+ create_options->driver = BLOCKDEV_DRIVER_SSH;
+ ssh_opts = &create_options->u.ssh;
/* Get desired file size. */
- total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
- BDRV_SECTOR_SIZE);
- DPRINTF("total_size=%" PRIi64, total_size);
+ ssh_opts->size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
+ BDRV_SECTOR_SIZE);
+ DPRINTF("total_size=%" PRIi64, ssh_opts->size);
uri_options = qdict_new();
- r = parse_uri(filename, uri_options, errp);
- if (r < 0) {
- ret = r;
+ ret = parse_uri(filename, uri_options, errp);
+ if (ret < 0) {
goto out;
}
- r = connect_to_ssh(&s, uri_options,
- LIBSSH2_FXF_READ|LIBSSH2_FXF_WRITE|
- LIBSSH2_FXF_CREAT|LIBSSH2_FXF_TRUNC,
- 0644, errp);
- if (r < 0) {
- ret = r;
+ ssh_opts->location = ssh_parse_options(uri_options, errp);
+ if (ssh_opts->location == NULL) {
+ ret = -EINVAL;
goto out;
}
- if (total_size > 0) {
- ret = ssh_grow_file(&s, total_size, errp);
- if (ret < 0) {
- goto out;
- }
- }
-
- ret = 0;
+ ret = ssh_co_create(create_options, errp);
out:
- ssh_state_free(&s);
- if (uri_options != NULL) {
- QDECREF(uri_options);
- }
+ QDECREF(uri_options);
+ qapi_free_BlockdevCreateOptions(create_options);
return ret;
}
@@ -1249,6 +1280,7 @@ static BlockDriver bdrv_ssh = {
.instance_size = sizeof(BDRVSSHState),
.bdrv_parse_filename = ssh_parse_filename,
.bdrv_file_open = ssh_file_open,
+ .bdrv_co_create = ssh_co_create,
.bdrv_co_create_opts = ssh_co_create_opts,
.bdrv_close = ssh_close,
.bdrv_has_zero_init = ssh_has_zero_init,
diff --git a/block/vdi.c b/block/vdi.c
index 68592cc58d..2b5ddd0666 100644
--- a/block/vdi.c
+++ b/block/vdi.c
@@ -263,8 +263,8 @@ static void vdi_header_print(VdiHeader *header)
}
#endif
-static int vdi_check(BlockDriverState *bs, BdrvCheckResult *res,
- BdrvCheckMode fix)
+static int coroutine_fn vdi_co_check(BlockDriverState *bs, BdrvCheckResult *res,
+ BdrvCheckMode fix)
{
/* TODO: additional checks possible. */
BDRVVdiState *s = (BDRVVdiState *)bs->opaque;
@@ -908,7 +908,7 @@ static BlockDriver bdrv_vdi = {
.bdrv_get_info = vdi_get_info,
.create_opts = &vdi_create_opts,
- .bdrv_check = vdi_check,
+ .bdrv_co_check = vdi_co_check,
};
static void bdrv_vdi_init(void)
diff --git a/block/vhdx.c b/block/vhdx.c
index 3fbff5048b..d82350d07c 100644
--- a/block/vhdx.c
+++ b/block/vhdx.c
@@ -1944,8 +1944,9 @@ exit:
* r/w and any log has already been replayed, so there is nothing (currently)
* for us to do here
*/
-static int vhdx_check(BlockDriverState *bs, BdrvCheckResult *result,
- BdrvCheckMode fix)
+static int coroutine_fn vhdx_co_check(BlockDriverState *bs,
+ BdrvCheckResult *result,
+ BdrvCheckMode fix)
{
BDRVVHDXState *s = bs->opaque;
@@ -2006,7 +2007,7 @@ static BlockDriver bdrv_vhdx = {
.bdrv_co_writev = vhdx_co_writev,
.bdrv_co_create_opts = vhdx_co_create_opts,
.bdrv_get_info = vhdx_get_info,
- .bdrv_check = vhdx_check,
+ .bdrv_co_check = vhdx_co_check,
.bdrv_has_zero_init = bdrv_has_zero_init_1,
.create_opts = &vhdx_create_opts,
diff --git a/block/vmdk.c b/block/vmdk.c
index 67342ed69b..f94c49a9c0 100644
--- a/block/vmdk.c
+++ b/block/vmdk.c
@@ -2221,8 +2221,9 @@ static ImageInfo *vmdk_get_extent_info(VmdkExtent *extent)
return info;
}
-static int vmdk_check(BlockDriverState *bs, BdrvCheckResult *result,
- BdrvCheckMode fix)
+static int coroutine_fn vmdk_co_check(BlockDriverState *bs,
+ BdrvCheckResult *result,
+ BdrvCheckMode fix)
{
BDRVVmdkState *s = bs->opaque;
VmdkExtent *extent = NULL;
@@ -2391,7 +2392,7 @@ static BlockDriver bdrv_vmdk = {
.instance_size = sizeof(BDRVVmdkState),
.bdrv_probe = vmdk_probe,
.bdrv_open = vmdk_open,
- .bdrv_check = vmdk_check,
+ .bdrv_co_check = vmdk_co_check,
.bdrv_reopen_prepare = vmdk_reopen_prepare,
.bdrv_child_perm = bdrv_format_default_perms,
.bdrv_co_preadv = vmdk_co_preadv,
diff --git a/configure b/configure
index 9f9ffb2cf9..af72fc852e 100755
--- a/configure
+++ b/configure
@@ -1692,6 +1692,7 @@ gcc_flags="-Wno-missing-include-dirs -Wempty-body -Wnested-externs $gcc_flags"
gcc_flags="-Wendif-labels -Wno-shift-negative-value $gcc_flags"
gcc_flags="-Wno-initializer-overrides -Wexpansion-to-defined $gcc_flags"
gcc_flags="-Wno-string-plus-int $gcc_flags"
+gcc_flags="-Wno-error=address-of-packed-member $gcc_flags"
# Note that we do not add -Werror to gcc_flags here, because that would
# enable it for all configure tests. If a configure test failed due
# to -Werror this would just silently disable some features,
@@ -2874,6 +2875,7 @@ if test "$sdl" != "no" ; then
int main( void ) { return SDL_Init (SDL_INIT_VIDEO); }
EOF
sdl_cflags=$($sdlconfig --cflags 2>/dev/null)
+ sdl_cflags="$sdl_cflags -Wno-undef" # workaround 2.0.8 bug
if test "$static" = "yes" ; then
if $pkg_config $sdlname --exists; then
sdl_libs=$($pkg_config $sdlname --static --libs 2>/dev/null)
@@ -4860,7 +4862,6 @@ fi
pragma_disable_unused_but_set=no
cat > $TMPC << EOF
#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
#pragma GCC diagnostic ignored "-Wstrict-prototypes"
#pragma GCC diagnostic pop
diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak
index d724106bd3..dd29e741c2 100644
--- a/default-configs/arm-softmmu.mak
+++ b/default-configs/arm-softmmu.mak
@@ -47,6 +47,7 @@ CONFIG_A9MPCORE=y
CONFIG_A15MPCORE=y
CONFIG_ARM_V7M=y
+CONFIG_NETDUINO2=y
CONFIG_ARM_GIC=y
CONFIG_ARM_GIC_KVM=$(CONFIG_KVM)
@@ -109,6 +110,7 @@ CONFIG_TZ_PPC=y
CONFIG_IOTKIT=y
CONFIG_IOTKIT_SECCTL=y
+CONFIG_VERSATILE=y
CONFIG_VERSATILE_PCI=y
CONFIG_VERSATILE_I2C=y
@@ -117,6 +119,7 @@ CONFIG_VFIO_XGMAC=y
CONFIG_VFIO_AMD_XGBE=y
CONFIG_SDHCI=y
+CONFIG_INTEGRATOR=y
CONFIG_INTEGRATOR_DEBUG=y
CONFIG_ALLWINNER_A10_PIT=y
@@ -126,6 +129,7 @@ CONFIG_ALLWINNER_A10=y
CONFIG_FSL_IMX6=y
CONFIG_FSL_IMX31=y
CONFIG_FSL_IMX25=y
+CONFIG_FSL_IMX7=y
CONFIG_IMX_I2C=y
@@ -140,3 +144,8 @@ CONFIG_GPIO_KEY=y
CONFIG_MSF2=y
CONFIG_FW_CFG_DMA=y
CONFIG_XILINX_AXI=y
+CONFIG_PCI_DESIGNWARE=y
+
+CONFIG_STRONGARM=y
+CONFIG_HIGHBANK=y
+CONFIG_MUSICPAL=y
diff --git a/hmp-commands.hx b/hmp-commands.hx
index 964eb515cf..1723cbe1df 100644
--- a/hmp-commands.hx
+++ b/hmp-commands.hx
@@ -253,9 +253,10 @@ ETEXI
{
.name = "screendump",
- .args_type = "filename:F",
- .params = "filename",
- .help = "save screen into PPM image 'filename'",
+ .args_type = "filename:F,device:s?,head:i?",
+ .params = "filename [device [head]]",
+ .help = "save screen from head 'head' of display device 'device' "
+ "into PPM image 'filename'",
.cmd = hmp_screendump,
},
diff --git a/hmp.c b/hmp.c
index 016cb5c4f1..ba9e299ee2 100644
--- a/hmp.c
+++ b/hmp.c
@@ -2140,9 +2140,11 @@ err_out:
void hmp_screendump(Monitor *mon, const QDict *qdict)
{
const char *filename = qdict_get_str(qdict, "filename");
+ const char *id = qdict_get_try_str(qdict, "device");
+ int64_t head = qdict_get_try_int(qdict, "head", 0);
Error *err = NULL;
- qmp_screendump(filename, &err);
+ qmp_screendump(filename, id != NULL, id, id != NULL, head, &err);
hmp_handle_error(mon, &err);
}
diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
index 232258160a..2885e3e234 100644
--- a/hw/arm/Makefile.objs
+++ b/hw/arm/Makefile.objs
@@ -1,15 +1,27 @@
-obj-y += boot.o collie.o exynos4_boards.o gumstix.o highbank.o
-obj-$(CONFIG_DIGIC) += digic_boards.o
-obj-y += integratorcp.o mainstone.o musicpal.o nseries.o
-obj-y += omap_sx1.o palm.o realview.o spitz.o stellaris.o
-obj-y += tosa.o versatilepb.o vexpress.o virt.o xilinx_zynq.o z2.o
+obj-y += boot.o virt.o sysbus-fdt.o
obj-$(CONFIG_ACPI) += virt-acpi-build.o
-obj-y += netduino2.o
-obj-y += sysbus-fdt.o
+obj-$(CONFIG_DIGIC) += digic_boards.o
+obj-$(CONFIG_EXYNOS4) += exynos4_boards.o
+obj-$(CONFIG_HIGHBANK) += highbank.o
+obj-$(CONFIG_INTEGRATOR) += integratorcp.o
+obj-$(CONFIG_MAINSTONE) += mainstone.o
+obj-$(CONFIG_MUSICPAL) += musicpal.o
+obj-$(CONFIG_NETDUINO2) += netduino2.o
+obj-$(CONFIG_NSERIES) += nseries.o
+obj-$(CONFIG_OMAP) += omap_sx1.o palm.o
+obj-$(CONFIG_PXA2XX) += gumstix.o spitz.o tosa.o z2.o
+obj-$(CONFIG_REALVIEW) += realview.o
+obj-$(CONFIG_STELLARIS) += stellaris.o
+obj-$(CONFIG_STRONGARM) += collie.o
+obj-$(CONFIG_VERSATILE) += vexpress.o versatilepb.o
+obj-$(CONFIG_ZYNQ) += xilinx_zynq.o
-obj-y += armv7m.o exynos4210.o pxa2xx.o pxa2xx_gpio.o pxa2xx_pic.o
+obj-$(CONFIG_ARM_V7M) += armv7m.o
+obj-$(CONFIG_EXYNOS4) += exynos4210.o
+obj-$(CONFIG_PXA2XX) += pxa2xx.o pxa2xx_gpio.o pxa2xx_pic.o
obj-$(CONFIG_DIGIC) += digic.o
-obj-y += omap1.o omap2.o strongarm.o
+obj-$(CONFIG_OMAP) += omap1.o omap2.o
+obj-$(CONFIG_STRONGARM) += strongarm.o
obj-$(CONFIG_ALLWINNER_A10) += allwinner-a10.o cubieboard.o
obj-$(CONFIG_RASPI) += bcm2835_peripherals.o bcm2836.o raspi.o
obj-$(CONFIG_STM32F205_SOC) += stm32f205_soc.o
@@ -22,3 +34,4 @@ obj-$(CONFIG_MPS2) += mps2.o
obj-$(CONFIG_MPS2) += mps2-tz.o
obj-$(CONFIG_MSF2) += msf2-soc.o msf2-som.o
obj-$(CONFIG_IOTKIT) += iotkit.o
+obj-$(CONFIG_FSL_IMX7) += fsl-imx7.o mcimx7d-sabre.o
diff --git a/hw/arm/boot.c b/hw/arm/boot.c
index 6d0c92ab88..196c7fb242 100644
--- a/hw/arm/boot.c
+++ b/hw/arm/boot.c
@@ -829,6 +829,7 @@ static uint64_t arm_load_elf(struct arm_boot_info *info, uint64_t *pentry,
load_elf_hdr(info->kernel_filename, &elf_header, &elf_is64, &err);
if (err) {
+ error_free(err);
return ret;
}
@@ -890,7 +891,8 @@ static uint64_t load_aarch64_image(const char *filename, hwaddr mem_base,
}
/* check the arm64 magic header value -- very old kernels may not have it */
- if (memcmp(buffer + ARM64_MAGIC_OFFSET, "ARM\x64", 4) == 0) {
+ if (size > ARM64_MAGIC_OFFSET + 4 &&
+ memcmp(buffer + ARM64_MAGIC_OFFSET, "ARM\x64", 4) == 0) {
uint64_t hdrvals[2];
/* The arm64 Image header has text_offset and image_size fields at 8 and
diff --git a/hw/arm/fsl-imx7.c b/hw/arm/fsl-imx7.c
new file mode 100644
index 0000000000..26ef36c79a
--- /dev/null
+++ b/hw/arm/fsl-imx7.c
@@ -0,0 +1,582 @@
+/*
+ * Copyright (c) 2018, Impinj, Inc.
+ *
+ * i.MX7 SoC definitions
+ *
+ * Author: Andrey Smirnov <andrew.smirnov@gmail.com>
+ *
+ * Based on hw/arm/fsl-imx6.c
+ *
+ * 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.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu-common.h"
+#include "hw/arm/fsl-imx7.h"
+#include "hw/misc/unimp.h"
+#include "sysemu/sysemu.h"
+#include "qemu/error-report.h"
+
+#define NAME_SIZE 20
+
+static void fsl_imx7_init(Object *obj)
+{
+ BusState *sysbus = sysbus_get_default();
+ FslIMX7State *s = FSL_IMX7(obj);
+ char name[NAME_SIZE];
+ int i;
+
+ if (smp_cpus > FSL_IMX7_NUM_CPUS) {
+ error_report("%s: Only %d CPUs are supported (%d requested)",
+ TYPE_FSL_IMX7, FSL_IMX7_NUM_CPUS, smp_cpus);
+ exit(1);
+ }
+
+ for (i = 0; i < smp_cpus; i++) {
+ object_initialize(&s->cpu[i], sizeof(s->cpu[i]),
+ ARM_CPU_TYPE_NAME("cortex-a7"));
+ snprintf(name, NAME_SIZE, "cpu%d", i);
+ object_property_add_child(obj, name, OBJECT(&s->cpu[i]),
+ &error_fatal);
+ }
+
+ /*
+ * A7MPCORE
+ */
+ object_initialize(&s->a7mpcore, sizeof(s->a7mpcore), TYPE_A15MPCORE_PRIV);
+ qdev_set_parent_bus(DEVICE(&s->a7mpcore), sysbus);
+ object_property_add_child(obj, "a7mpcore",
+ OBJECT(&s->a7mpcore), &error_fatal);
+
+ /*
+ * GPIOs 1 to 7
+ */
+ for (i = 0; i < FSL_IMX7_NUM_GPIOS; i++) {
+ object_initialize(&s->gpio[i], sizeof(s->gpio[i]),
+ TYPE_IMX_GPIO);
+ qdev_set_parent_bus(DEVICE(&s->gpio[i]), sysbus);
+ snprintf(name, NAME_SIZE, "gpio%d", i);
+ object_property_add_child(obj, name,
+ OBJECT(&s->gpio[i]), &error_fatal);
+ }
+
+ /*
+ * GPT1, 2, 3, 4
+ */
+ for (i = 0; i < FSL_IMX7_NUM_GPTS; i++) {
+ object_initialize(&s->gpt[i], sizeof(s->gpt[i]), TYPE_IMX7_GPT);
+ qdev_set_parent_bus(DEVICE(&s->gpt[i]), sysbus);
+ snprintf(name, NAME_SIZE, "gpt%d", i);
+ object_property_add_child(obj, name, OBJECT(&s->gpt[i]),
+ &error_fatal);
+ }
+
+ /*
+ * CCM
+ */
+ object_initialize(&s->ccm, sizeof(s->ccm), TYPE_IMX7_CCM);
+ qdev_set_parent_bus(DEVICE(&s->ccm), sysbus);
+ object_property_add_child(obj, "ccm", OBJECT(&s->ccm), &error_fatal);
+
+ /*
+ * Analog
+ */
+ object_initialize(&s->analog, sizeof(s->analog), TYPE_IMX7_ANALOG);
+ qdev_set_parent_bus(DEVICE(&s->analog), sysbus);
+ object_property_add_child(obj, "analog", OBJECT(&s->analog), &error_fatal);
+
+ /*
+ * GPCv2
+ */
+ object_initialize(&s->gpcv2, sizeof(s->gpcv2), TYPE_IMX_GPCV2);
+ qdev_set_parent_bus(DEVICE(&s->gpcv2), sysbus);
+ object_property_add_child(obj, "gpcv2", OBJECT(&s->gpcv2), &error_fatal);
+
+ for (i = 0; i < FSL_IMX7_NUM_ECSPIS; i++) {
+ object_initialize(&s->spi[i], sizeof(s->spi[i]), TYPE_IMX_SPI);
+ qdev_set_parent_bus(DEVICE(&s->spi[i]), sysbus_get_default());
+ snprintf(name, NAME_SIZE, "spi%d", i + 1);
+ object_property_add_child(obj, name, OBJECT(&s->spi[i]), NULL);
+ }
+
+
+ for (i = 0; i < FSL_IMX7_NUM_I2CS; i++) {
+ object_initialize(&s->i2c[i], sizeof(s->i2c[i]), TYPE_IMX_I2C);
+ qdev_set_parent_bus(DEVICE(&s->i2c[i]), sysbus_get_default());
+ snprintf(name, NAME_SIZE, "i2c%d", i + 1);
+ object_property_add_child(obj, name, OBJECT(&s->i2c[i]), NULL);
+ }
+
+ /*
+ * UART
+ */
+ for (i = 0; i < FSL_IMX7_NUM_UARTS; i++) {
+ object_initialize(&s->uart[i], sizeof(s->uart[i]), TYPE_IMX_SERIAL);
+ qdev_set_parent_bus(DEVICE(&s->uart[i]), sysbus);
+ snprintf(name, NAME_SIZE, "uart%d", i);
+ object_property_add_child(obj, name, OBJECT(&s->uart[i]),
+ &error_fatal);
+ }
+
+ /*
+ * Ethernet
+ */
+ for (i = 0; i < FSL_IMX7_NUM_ETHS; i++) {
+ object_initialize(&s->eth[i], sizeof(s->eth[i]), TYPE_IMX_ENET);
+ qdev_set_parent_bus(DEVICE(&s->eth[i]), sysbus);
+ snprintf(name, NAME_SIZE, "eth%d", i);
+ object_property_add_child(obj, name, OBJECT(&s->eth[i]),
+ &error_fatal);
+ }
+
+ /*
+ * SDHCI
+ */
+ for (i = 0; i < FSL_IMX7_NUM_USDHCS; i++) {
+ object_initialize(&s->usdhc[i], sizeof(s->usdhc[i]),
+ TYPE_IMX_USDHC);
+ qdev_set_parent_bus(DEVICE(&s->usdhc[i]), sysbus);
+ snprintf(name, NAME_SIZE, "usdhc%d", i);
+ object_property_add_child(obj, name, OBJECT(&s->usdhc[i]),
+ &error_fatal);
+ }
+
+ /*
+ * SNVS
+ */
+ object_initialize(&s->snvs, sizeof(s->snvs), TYPE_IMX7_SNVS);
+ qdev_set_parent_bus(DEVICE(&s->snvs), sysbus);
+ object_property_add_child(obj, "snvs", OBJECT(&s->snvs), &error_fatal);
+
+ /*
+ * Watchdog
+ */
+ for (i = 0; i < FSL_IMX7_NUM_WDTS; i++) {
+ object_initialize(&s->wdt[i], sizeof(s->wdt[i]), TYPE_IMX2_WDT);
+ qdev_set_parent_bus(DEVICE(&s->wdt[i]), sysbus);
+ snprintf(name, NAME_SIZE, "wdt%d", i);
+ object_property_add_child(obj, name, OBJECT(&s->wdt[i]),
+ &error_fatal);
+ }
+
+ /*
+ * GPR
+ */
+ object_initialize(&s->gpr, sizeof(s->gpr), TYPE_IMX7_GPR);
+ qdev_set_parent_bus(DEVICE(&s->gpr), sysbus);
+ object_property_add_child(obj, "gpr", OBJECT(&s->gpr), &error_fatal);
+
+ object_initialize(&s->pcie, sizeof(s->pcie), TYPE_DESIGNWARE_PCIE_HOST);
+ qdev_set_parent_bus(DEVICE(&s->pcie), sysbus);
+ object_property_add_child(obj, "pcie", OBJECT(&s->pcie), &error_fatal);
+
+ for (i = 0; i < FSL_IMX7_NUM_USBS; i++) {
+ object_initialize(&s->usb[i],
+ sizeof(s->usb[i]), TYPE_CHIPIDEA);
+ qdev_set_parent_bus(DEVICE(&s->usb[i]), sysbus);
+ snprintf(name, NAME_SIZE, "usb%d", i);
+ object_property_add_child(obj, name,
+ OBJECT(&s->usb[i]), &error_fatal);
+ }
+}
+
+static void fsl_imx7_realize(DeviceState *dev, Error **errp)
+{
+ FslIMX7State *s = FSL_IMX7(dev);
+ Object *o;
+ int i;
+ qemu_irq irq;
+ char name[NAME_SIZE];
+
+ for (i = 0; i < smp_cpus; i++) {
+ o = OBJECT(&s->cpu[i]);
+
+ object_property_set_int(o, QEMU_PSCI_CONDUIT_SMC,
+ "psci-conduit", &error_abort);
+
+ /* On uniprocessor, the CBAR is set to 0 */
+ if (smp_cpus > 1) {
+ object_property_set_int(o, FSL_IMX7_A7MPCORE_ADDR,
+ "reset-cbar", &error_abort);
+ }
+
+ if (i) {
+ /* Secondary CPUs start in PSCI powered-down state */
+ object_property_set_bool(o, true,
+ "start-powered-off", &error_abort);
+ }
+
+ object_property_set_bool(o, true, "realized", &error_abort);
+ }
+
+ /*
+ * A7MPCORE
+ */
+ object_property_set_int(OBJECT(&s->a7mpcore), smp_cpus, "num-cpu",
+ &error_abort);
+ object_property_set_int(OBJECT(&s->a7mpcore),
+ FSL_IMX7_MAX_IRQ + GIC_INTERNAL,
+ "num-irq", &error_abort);
+
+ object_property_set_bool(OBJECT(&s->a7mpcore), true, "realized",
+ &error_abort);
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->a7mpcore), 0, FSL_IMX7_A7MPCORE_ADDR);
+
+ for (i = 0; i < smp_cpus; i++) {
+ SysBusDevice *sbd = SYS_BUS_DEVICE(&s->a7mpcore);
+ DeviceState *d = DEVICE(qemu_get_cpu(i));
+
+ irq = qdev_get_gpio_in(d, ARM_CPU_IRQ);
+ sysbus_connect_irq(sbd, i, irq);
+ irq = qdev_get_gpio_in(d, ARM_CPU_FIQ);
+ sysbus_connect_irq(sbd, i + smp_cpus, irq);
+ }
+
+ /*
+ * A7MPCORE DAP
+ */
+ create_unimplemented_device("a7mpcore-dap", FSL_IMX7_A7MPCORE_DAP_ADDR,
+ 0x100000);
+
+ /*
+ * GPT1, 2, 3, 4
+ */
+ for (i = 0; i < FSL_IMX7_NUM_GPTS; i++) {
+ static const hwaddr FSL_IMX7_GPTn_ADDR[FSL_IMX7_NUM_GPTS] = {
+ FSL_IMX7_GPT1_ADDR,
+ FSL_IMX7_GPT2_ADDR,
+ FSL_IMX7_GPT3_ADDR,
+ FSL_IMX7_GPT4_ADDR,
+ };
+
+ s->gpt[i].ccm = IMX_CCM(&s->ccm);
+ object_property_set_bool(OBJECT(&s->gpt[i]), true, "realized",
+ &error_abort);
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpt[i]), 0, FSL_IMX7_GPTn_ADDR[i]);
+ }
+
+ for (i = 0; i < FSL_IMX7_NUM_GPIOS; i++) {
+ static const hwaddr FSL_IMX7_GPIOn_ADDR[FSL_IMX7_NUM_GPIOS] = {
+ FSL_IMX7_GPIO1_ADDR,
+ FSL_IMX7_GPIO2_ADDR,
+ FSL_IMX7_GPIO3_ADDR,
+ FSL_IMX7_GPIO4_ADDR,
+ FSL_IMX7_GPIO5_ADDR,
+ FSL_IMX7_GPIO6_ADDR,
+ FSL_IMX7_GPIO7_ADDR,
+ };
+
+ object_property_set_bool(OBJECT(&s->gpio[i]), true, "realized",
+ &error_abort);
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpio[i]), 0, FSL_IMX7_GPIOn_ADDR[i]);
+ }
+
+ /*
+ * IOMUXC and IOMUXC_LPSR
+ */
+ for (i = 0; i < FSL_IMX7_NUM_IOMUXCS; i++) {
+ static const hwaddr FSL_IMX7_IOMUXCn_ADDR[FSL_IMX7_NUM_IOMUXCS] = {
+ FSL_IMX7_IOMUXC_ADDR,
+ FSL_IMX7_IOMUXC_LPSR_ADDR,
+ };
+
+ snprintf(name, NAME_SIZE, "iomuxc%d", i);
+ create_unimplemented_device(name, FSL_IMX7_IOMUXCn_ADDR[i],
+ FSL_IMX7_IOMUXCn_SIZE);
+ }
+
+ /*
+ * CCM
+ */
+ object_property_set_bool(OBJECT(&s->ccm), true, "realized", &error_abort);
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->ccm), 0, FSL_IMX7_CCM_ADDR);
+
+ /*
+ * Analog
+ */
+ object_property_set_bool(OBJECT(&s->analog), true, "realized",
+ &error_abort);
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->analog), 0, FSL_IMX7_ANALOG_ADDR);
+
+ /*
+ * GPCv2
+ */
+ object_property_set_bool(OBJECT(&s->gpcv2), true,
+ "realized", &error_abort);
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpcv2), 0, FSL_IMX7_GPC_ADDR);
+
+ /* Initialize all ECSPI */
+ for (i = 0; i < FSL_IMX7_NUM_ECSPIS; i++) {
+ static const hwaddr FSL_IMX7_SPIn_ADDR[FSL_IMX7_NUM_ECSPIS] = {
+ FSL_IMX7_ECSPI1_ADDR,
+ FSL_IMX7_ECSPI2_ADDR,
+ FSL_IMX7_ECSPI3_ADDR,
+ FSL_IMX7_ECSPI4_ADDR,
+ };
+
+ static const hwaddr FSL_IMX7_SPIn_IRQ[FSL_IMX7_NUM_ECSPIS] = {
+ FSL_IMX7_ECSPI1_IRQ,
+ FSL_IMX7_ECSPI2_IRQ,
+ FSL_IMX7_ECSPI3_IRQ,
+ FSL_IMX7_ECSPI4_IRQ,
+ };
+
+ /* Initialize the SPI */
+ object_property_set_bool(OBJECT(&s->spi[i]), true, "realized",
+ &error_abort);
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi[i]), 0,
+ FSL_IMX7_SPIn_ADDR[i]);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->spi[i]), 0,
+ qdev_get_gpio_in(DEVICE(&s->a7mpcore),
+ FSL_IMX7_SPIn_IRQ[i]));
+ }
+
+ for (i = 0; i < FSL_IMX7_NUM_I2CS; i++) {
+ static const hwaddr FSL_IMX7_I2Cn_ADDR[FSL_IMX7_NUM_I2CS] = {
+ FSL_IMX7_I2C1_ADDR,
+ FSL_IMX7_I2C2_ADDR,
+ FSL_IMX7_I2C3_ADDR,
+ FSL_IMX7_I2C4_ADDR,
+ };
+
+ static const hwaddr FSL_IMX7_I2Cn_IRQ[FSL_IMX7_NUM_I2CS] = {
+ FSL_IMX7_I2C1_IRQ,
+ FSL_IMX7_I2C2_IRQ,
+ FSL_IMX7_I2C3_IRQ,
+ FSL_IMX7_I2C4_IRQ,
+ };
+
+ object_property_set_bool(OBJECT(&s->i2c[i]), true, "realized",
+ &error_abort);
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->i2c[i]), 0, FSL_IMX7_I2Cn_ADDR[i]);
+
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c[i]), 0,
+ qdev_get_gpio_in(DEVICE(&s->a7mpcore),
+ FSL_IMX7_I2Cn_IRQ[i]));
+ }
+
+ /*
+ * UART
+ */
+ for (i = 0; i < FSL_IMX7_NUM_UARTS; i++) {
+ static const hwaddr FSL_IMX7_UARTn_ADDR[FSL_IMX7_NUM_UARTS] = {
+ FSL_IMX7_UART1_ADDR,
+ FSL_IMX7_UART2_ADDR,
+ FSL_IMX7_UART3_ADDR,
+ FSL_IMX7_UART4_ADDR,
+ FSL_IMX7_UART5_ADDR,
+ FSL_IMX7_UART6_ADDR,
+ FSL_IMX7_UART7_ADDR,
+ };
+
+ static const int FSL_IMX7_UARTn_IRQ[FSL_IMX7_NUM_UARTS] = {
+ FSL_IMX7_UART1_IRQ,
+ FSL_IMX7_UART2_IRQ,
+ FSL_IMX7_UART3_IRQ,
+ FSL_IMX7_UART4_IRQ,
+ FSL_IMX7_UART5_IRQ,
+ FSL_IMX7_UART6_IRQ,
+ FSL_IMX7_UART7_IRQ,
+ };
+
+
+ if (i < MAX_SERIAL_PORTS) {
+ qdev_prop_set_chr(DEVICE(&s->uart[i]), "chardev", serial_hds[i]);
+ }
+
+ object_property_set_bool(OBJECT(&s->uart[i]), true, "realized",
+ &error_abort);
+
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->uart[i]), 0, FSL_IMX7_UARTn_ADDR[i]);
+
+ irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX7_UARTn_IRQ[i]);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->uart[i]), 0, irq);
+ }
+
+ /*
+ * Ethernet
+ */
+ for (i = 0; i < FSL_IMX7_NUM_ETHS; i++) {
+ static const hwaddr FSL_IMX7_ENETn_ADDR[FSL_IMX7_NUM_ETHS] = {
+ FSL_IMX7_ENET1_ADDR,
+ FSL_IMX7_ENET2_ADDR,
+ };
+
+ object_property_set_uint(OBJECT(&s->eth[i]), FSL_IMX7_ETH_NUM_TX_RINGS,
+ "tx-ring-num", &error_abort);
+ qdev_set_nic_properties(DEVICE(&s->eth[i]), &nd_table[i]);
+ object_property_set_bool(OBJECT(&s->eth[i]), true, "realized",
+ &error_abort);
+
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->eth[i]), 0, FSL_IMX7_ENETn_ADDR[i]);
+
+ irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX7_ENET_IRQ(i, 0));
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->eth[i]), 0, irq);
+ irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX7_ENET_IRQ(i, 3));
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->eth[i]), 1, irq);
+ }
+
+ /*
+ * USDHC
+ */
+ for (i = 0; i < FSL_IMX7_NUM_USDHCS; i++) {
+ static const hwaddr FSL_IMX7_USDHCn_ADDR[FSL_IMX7_NUM_USDHCS] = {
+ FSL_IMX7_USDHC1_ADDR,
+ FSL_IMX7_USDHC2_ADDR,
+ FSL_IMX7_USDHC3_ADDR,
+ };
+
+ static const int FSL_IMX7_USDHCn_IRQ[FSL_IMX7_NUM_USDHCS] = {
+ FSL_IMX7_USDHC1_IRQ,
+ FSL_IMX7_USDHC2_IRQ,
+ FSL_IMX7_USDHC3_IRQ,
+ };
+
+ object_property_set_bool(OBJECT(&s->usdhc[i]), true, "realized",
+ &error_abort);
+
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->usdhc[i]), 0,
+ FSL_IMX7_USDHCn_ADDR[i]);
+
+ irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX7_USDHCn_IRQ[i]);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->usdhc[i]), 0, irq);
+ }
+
+ /*
+ * SNVS
+ */
+ object_property_set_bool(OBJECT(&s->snvs), true, "realized", &error_abort);
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->snvs), 0, FSL_IMX7_SNVS_ADDR);
+
+ /*
+ * SRC
+ */
+ create_unimplemented_device("sdma", FSL_IMX7_SRC_ADDR, FSL_IMX7_SRC_SIZE);
+
+ /*
+ * Watchdog
+ */
+ for (i = 0; i < FSL_IMX7_NUM_WDTS; i++) {
+ static const hwaddr FSL_IMX7_WDOGn_ADDR[FSL_IMX7_NUM_WDTS] = {
+ FSL_IMX7_WDOG1_ADDR,
+ FSL_IMX7_WDOG2_ADDR,
+ FSL_IMX7_WDOG3_ADDR,
+ FSL_IMX7_WDOG4_ADDR,
+ };
+
+ object_property_set_bool(OBJECT(&s->wdt[i]), true, "realized",
+ &error_abort);
+
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->wdt[i]), 0, FSL_IMX7_WDOGn_ADDR[i]);
+ }
+
+ /*
+ * SDMA
+ */
+ create_unimplemented_device("sdma", FSL_IMX7_SDMA_ADDR, FSL_IMX7_SDMA_SIZE);
+
+
+ object_property_set_bool(OBJECT(&s->gpr), true, "realized",
+ &error_abort);
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpr), 0, FSL_IMX7_GPR_ADDR);
+
+ object_property_set_bool(OBJECT(&s->pcie), true,
+ "realized", &error_abort);
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->pcie), 0, FSL_IMX7_PCIE_REG_ADDR);
+
+ irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX7_PCI_INTA_IRQ);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie), 0, irq);
+ irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX7_PCI_INTB_IRQ);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie), 1, irq);
+ irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX7_PCI_INTC_IRQ);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie), 2, irq);
+ irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX7_PCI_INTD_IRQ);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie), 3, irq);
+
+
+ for (i = 0; i < FSL_IMX7_NUM_USBS; i++) {
+ static const hwaddr FSL_IMX7_USBMISCn_ADDR[FSL_IMX7_NUM_USBS] = {
+ FSL_IMX7_USBMISC1_ADDR,
+ FSL_IMX7_USBMISC2_ADDR,
+ FSL_IMX7_USBMISC3_ADDR,
+ };
+
+ static const hwaddr FSL_IMX7_USBn_ADDR[FSL_IMX7_NUM_USBS] = {
+ FSL_IMX7_USB1_ADDR,
+ FSL_IMX7_USB2_ADDR,
+ FSL_IMX7_USB3_ADDR,
+ };
+
+ static const hwaddr FSL_IMX7_USBn_IRQ[FSL_IMX7_NUM_USBS] = {
+ FSL_IMX7_USB1_IRQ,
+ FSL_IMX7_USB2_IRQ,
+ FSL_IMX7_USB3_IRQ,
+ };
+
+ object_property_set_bool(OBJECT(&s->usb[i]), true, "realized",
+ &error_abort);
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->usb[i]), 0,
+ FSL_IMX7_USBn_ADDR[i]);
+
+ irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX7_USBn_IRQ[i]);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->usb[i]), 0, irq);
+
+ snprintf(name, NAME_SIZE, "usbmisc%d", i);
+ create_unimplemented_device(name, FSL_IMX7_USBMISCn_ADDR[i],
+ FSL_IMX7_USBMISCn_SIZE);
+ }
+
+ /*
+ * ADCs
+ */
+ for (i = 0; i < FSL_IMX7_NUM_ADCS; i++) {
+ static const hwaddr FSL_IMX7_ADCn_ADDR[FSL_IMX7_NUM_ADCS] = {
+ FSL_IMX7_ADC1_ADDR,
+ FSL_IMX7_ADC2_ADDR,
+ };
+
+ snprintf(name, NAME_SIZE, "adc%d", i);
+ create_unimplemented_device(name, FSL_IMX7_ADCn_ADDR[i],
+ FSL_IMX7_ADCn_SIZE);
+ }
+
+ /*
+ * LCD
+ */
+ create_unimplemented_device("lcdif", FSL_IMX7_LCDIF_ADDR,
+ FSL_IMX7_LCDIF_SIZE);
+}
+
+static void fsl_imx7_class_init(ObjectClass *oc, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(oc);
+
+ dc->realize = fsl_imx7_realize;
+
+ /* Reason: Uses serial_hds and nd_table in realize() directly */
+ dc->user_creatable = false;
+ dc->desc = "i.MX7 SOC";
+}
+
+static const TypeInfo fsl_imx7_type_info = {
+ .name = TYPE_FSL_IMX7,
+ .parent = TYPE_DEVICE,
+ .instance_size = sizeof(FslIMX7State),
+ .instance_init = fsl_imx7_init,
+ .class_init = fsl_imx7_class_init,
+};
+
+static void fsl_imx7_register_types(void)
+{
+ type_register_static(&fsl_imx7_type_info);
+}
+type_init(fsl_imx7_register_types)
diff --git a/hw/arm/mcimx7d-sabre.c b/hw/arm/mcimx7d-sabre.c
new file mode 100644
index 0000000000..95fb409d9c
--- /dev/null
+++ b/hw/arm/mcimx7d-sabre.c
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2018, Impinj, Inc.
+ *
+ * MCIMX7D_SABRE Board System emulation.
+ *
+ * Author: Andrey Smirnov <andrew.smirnov@gmail.com>
+ *
+ * This code is licensed under the GPL, version 2 or later.
+ * See the file `COPYING' in the top level directory.
+ *
+ * It (partially) emulates a mcimx7d_sabre board, with a Freescale
+ * i.MX7 SoC
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu-common.h"
+#include "hw/arm/fsl-imx7.h"
+#include "hw/boards.h"
+#include "sysemu/sysemu.h"
+#include "sysemu/device_tree.h"
+#include "qemu/error-report.h"
+#include "sysemu/qtest.h"
+#include "net/net.h"
+
+typedef struct {
+ FslIMX7State soc;
+ MemoryRegion ram;
+} MCIMX7Sabre;
+
+static void mcimx7d_sabre_init(MachineState *machine)
+{
+ static struct arm_boot_info boot_info;
+ MCIMX7Sabre *s = g_new0(MCIMX7Sabre, 1);
+ Object *soc;
+ int i;
+
+ if (machine->ram_size > FSL_IMX7_MMDC_SIZE) {
+ error_report("RAM size " RAM_ADDR_FMT " above max supported (%08x)",
+ machine->ram_size, FSL_IMX7_MMDC_SIZE);
+ exit(1);
+ }
+
+ boot_info = (struct arm_boot_info) {
+ .loader_start = FSL_IMX7_MMDC_ADDR,
+ .board_id = -1,
+ .ram_size = machine->ram_size,
+ .kernel_filename = machine->kernel_filename,
+ .kernel_cmdline = machine->kernel_cmdline,
+ .initrd_filename = machine->initrd_filename,
+ .nb_cpus = smp_cpus,
+ };
+
+ object_initialize(&s->soc, sizeof(s->soc), TYPE_FSL_IMX7);
+ soc = OBJECT(&s->soc);
+ object_property_add_child(OBJECT(machine), "soc", soc, &error_fatal);
+ object_property_set_bool(soc, true, "realized", &error_fatal);
+
+ memory_region_allocate_system_memory(&s->ram, NULL, "mcimx7d-sabre.ram",
+ machine->ram_size);
+ memory_region_add_subregion(get_system_memory(),
+ FSL_IMX7_MMDC_ADDR, &s->ram);
+
+ for (i = 0; i < FSL_IMX7_NUM_USDHCS; i++) {
+ BusState *bus;
+ DeviceState *carddev;
+ DriveInfo *di;
+ BlockBackend *blk;
+
+ di = drive_get_next(IF_SD);
+ blk = di ? blk_by_legacy_dinfo(di) : NULL;
+ bus = qdev_get_child_bus(DEVICE(&s->soc.usdhc[i]), "sd-bus");
+ carddev = qdev_create(bus, TYPE_SD_CARD);
+ qdev_prop_set_drive(carddev, "drive", blk, &error_fatal);
+ object_property_set_bool(OBJECT(carddev), true,
+ "realized", &error_fatal);
+ }
+
+ if (!qtest_enabled()) {
+ arm_load_kernel(&s->soc.cpu[0], &boot_info);
+ }
+}
+
+static void mcimx7d_sabre_machine_init(MachineClass *mc)
+{
+ mc->desc = "Freescale i.MX7 DUAL SABRE (Cortex A7)";
+ mc->init = mcimx7d_sabre_init;
+ mc->max_cpus = FSL_IMX7_NUM_CPUS;
+}
+DEFINE_MACHINE("mcimx7d-sabre", mcimx7d_sabre_machine_init)
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index dbb3c8036a..2c07245047 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -169,6 +169,7 @@ static const char *valid_cpus[] = {
ARM_CPU_TYPE_NAME("cortex-a53"),
ARM_CPU_TYPE_NAME("cortex-a57"),
ARM_CPU_TYPE_NAME("host"),
+ ARM_CPU_TYPE_NAME("max"),
};
static bool cpu_type_valid(const char *cpu)
@@ -1206,16 +1207,23 @@ static void machvirt_init(MachineState *machine)
/* We can probe only here because during property set
* KVM is not available yet
*/
- if (!vms->gic_version) {
+ if (vms->gic_version <= 0) {
+ /* "host" or "max" */
if (!kvm_enabled()) {
- error_report("gic-version=host requires KVM");
- exit(1);
- }
-
- vms->gic_version = kvm_arm_vgic_probe();
- if (!vms->gic_version) {
- error_report("Unable to determine GIC version supported by host");
- exit(1);
+ if (vms->gic_version == 0) {
+ error_report("gic-version=host requires KVM");
+ exit(1);
+ } else {
+ /* "max": currently means 3 for TCG */
+ vms->gic_version = 3;
+ }
+ } else {
+ vms->gic_version = kvm_arm_vgic_probe();
+ if (!vms->gic_version) {
+ error_report(
+ "Unable to determine GIC version supported by host");
+ exit(1);
+ }
}
}
@@ -1479,9 +1487,11 @@ static void virt_set_gic_version(Object *obj, const char *value, Error **errp)
vms->gic_version = 2;
} else if (!strcmp(value, "host")) {
vms->gic_version = 0; /* Will probe later */
+ } else if (!strcmp(value, "max")) {
+ vms->gic_version = -1; /* Will probe later */
} else {
error_setg(errp, "Invalid gic-version value");
- error_append_hint(errp, "Valid values are 3, 2, host.\n");
+ error_append_hint(errp, "Valid values are 3, 2, host, max.\n");
}
}
diff --git a/hw/arm/xlnx-zynqmp.c b/hw/arm/xlnx-zynqmp.c
index 69227fd4c9..465796e97c 100644
--- a/hw/arm/xlnx-zynqmp.c
+++ b/hw/arm/xlnx-zynqmp.c
@@ -282,6 +282,8 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp)
s->virt, "has_el2", NULL);
object_property_set_int(OBJECT(&s->apu_cpu[i]), GIC_BASE_ADDR,
"reset-cbar", &error_abort);
+ object_property_set_int(OBJECT(&s->apu_cpu[i]), num_apus,
+ "core-count", &error_abort);
object_property_set_bool(OBJECT(&s->apu_cpu[i]), true, "realized",
&err);
if (err) {
diff --git a/hw/pci-host/Makefile.objs b/hw/pci-host/Makefile.objs
index 4b69f737b5..6d6597c065 100644
--- a/hw/pci-host/Makefile.objs
+++ b/hw/pci-host/Makefile.objs
@@ -17,3 +17,5 @@ common-obj-$(CONFIG_PCI_PIIX) += piix.o
common-obj-$(CONFIG_PCI_Q35) += q35.o
common-obj-$(CONFIG_PCI_GENERIC) += gpex.o
common-obj-$(CONFIG_PCI_XILINX) += xilinx-pcie.o
+
+common-obj-$(CONFIG_PCI_DESIGNWARE) += designware.o
diff --git a/hw/pci-host/designware.c b/hw/pci-host/designware.c
new file mode 100644
index 0000000000..29ea313798
--- /dev/null
+++ b/hw/pci-host/designware.c
@@ -0,0 +1,754 @@
+/*
+ * Copyright (c) 2018, Impinj, Inc.
+ *
+ * Designware PCIe IP block emulation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "hw/pci/msi.h"
+#include "hw/pci/pci_bridge.h"
+#include "hw/pci/pci_host.h"
+#include "hw/pci/pcie_port.h"
+#include "hw/pci-host/designware.h"
+
+#define DESIGNWARE_PCIE_PORT_LINK_CONTROL 0x710
+#define DESIGNWARE_PCIE_PHY_DEBUG_R1 0x72C
+#define DESIGNWARE_PCIE_PHY_DEBUG_R1_XMLH_LINK_UP BIT(4)
+#define DESIGNWARE_PCIE_LINK_WIDTH_SPEED_CONTROL 0x80C
+#define DESIGNWARE_PCIE_PORT_LOGIC_SPEED_CHANGE BIT(17)
+#define DESIGNWARE_PCIE_MSI_ADDR_LO 0x820
+#define DESIGNWARE_PCIE_MSI_ADDR_HI 0x824
+#define DESIGNWARE_PCIE_MSI_INTR0_ENABLE 0x828
+#define DESIGNWARE_PCIE_MSI_INTR0_MASK 0x82C
+#define DESIGNWARE_PCIE_MSI_INTR0_STATUS 0x830
+#define DESIGNWARE_PCIE_ATU_VIEWPORT 0x900
+#define DESIGNWARE_PCIE_ATU_REGION_INBOUND BIT(31)
+#define DESIGNWARE_PCIE_ATU_CR1 0x904
+#define DESIGNWARE_PCIE_ATU_TYPE_MEM (0x0 << 0)
+#define DESIGNWARE_PCIE_ATU_CR2 0x908
+#define DESIGNWARE_PCIE_ATU_ENABLE BIT(31)
+#define DESIGNWARE_PCIE_ATU_LOWER_BASE 0x90C
+#define DESIGNWARE_PCIE_ATU_UPPER_BASE 0x910
+#define DESIGNWARE_PCIE_ATU_LIMIT 0x914
+#define DESIGNWARE_PCIE_ATU_LOWER_TARGET 0x918
+#define DESIGNWARE_PCIE_ATU_BUS(x) (((x) >> 24) & 0xff)
+#define DESIGNWARE_PCIE_ATU_DEVFN(x) (((x) >> 16) & 0xff)
+#define DESIGNWARE_PCIE_ATU_UPPER_TARGET 0x91C
+
+static DesignwarePCIEHost *
+designware_pcie_root_to_host(DesignwarePCIERoot *root)
+{
+ BusState *bus = qdev_get_parent_bus(DEVICE(root));
+ return DESIGNWARE_PCIE_HOST(bus->parent);
+}
+
+static void designware_pcie_root_msi_write(void *opaque, hwaddr addr,
+ uint64_t val, unsigned len)
+{
+ DesignwarePCIERoot *root = DESIGNWARE_PCIE_ROOT(opaque);
+ DesignwarePCIEHost *host = designware_pcie_root_to_host(root);
+
+ root->msi.intr[0].status |= BIT(val) & root->msi.intr[0].enable;
+
+ if (root->msi.intr[0].status & ~root->msi.intr[0].mask) {
+ qemu_set_irq(host->pci.irqs[0], 1);
+ }
+}
+
+static const MemoryRegionOps designware_pci_host_msi_ops = {
+ .write = designware_pcie_root_msi_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ },
+};
+
+static void designware_pcie_root_update_msi_mapping(DesignwarePCIERoot *root)
+
+{
+ MemoryRegion *mem = &root->msi.iomem;
+ const uint64_t base = root->msi.base;
+ const bool enable = root->msi.intr[0].enable;
+
+ memory_region_set_address(mem, base);
+ memory_region_set_enabled(mem, enable);
+}
+
+static DesignwarePCIEViewport *
+designware_pcie_root_get_current_viewport(DesignwarePCIERoot *root)
+{
+ const unsigned int idx = root->atu_viewport & 0xF;
+ const unsigned int dir =
+ !!(root->atu_viewport & DESIGNWARE_PCIE_ATU_REGION_INBOUND);
+ return &root->viewports[dir][idx];
+}
+
+static uint32_t
+designware_pcie_root_config_read(PCIDevice *d, uint32_t address, int len)
+{
+ DesignwarePCIERoot *root = DESIGNWARE_PCIE_ROOT(d);
+ DesignwarePCIEViewport *viewport =
+ designware_pcie_root_get_current_viewport(root);
+
+ uint32_t val;
+
+ switch (address) {
+ case DESIGNWARE_PCIE_PORT_LINK_CONTROL:
+ /*
+ * Linux guest uses this register only to configure number of
+ * PCIE lane (which in our case is irrelevant) and doesn't
+ * really care about the value it reads from this register
+ */
+ val = 0xDEADBEEF;
+ break;
+
+ case DESIGNWARE_PCIE_LINK_WIDTH_SPEED_CONTROL:
+ /*
+ * To make sure that any code in guest waiting for speed
+ * change does not time out we always report
+ * PORT_LOGIC_SPEED_CHANGE as set
+ */
+ val = DESIGNWARE_PCIE_PORT_LOGIC_SPEED_CHANGE;
+ break;
+
+ case DESIGNWARE_PCIE_MSI_ADDR_LO:
+ val = root->msi.base;
+ break;
+
+ case DESIGNWARE_PCIE_MSI_ADDR_HI:
+ val = root->msi.base >> 32;
+ break;
+
+ case DESIGNWARE_PCIE_MSI_INTR0_ENABLE:
+ val = root->msi.intr[0].enable;
+ break;
+
+ case DESIGNWARE_PCIE_MSI_INTR0_MASK:
+ val = root->msi.intr[0].mask;
+ break;
+
+ case DESIGNWARE_PCIE_MSI_INTR0_STATUS:
+ val = root->msi.intr[0].status;
+ break;
+
+ case DESIGNWARE_PCIE_PHY_DEBUG_R1:
+ val = DESIGNWARE_PCIE_PHY_DEBUG_R1_XMLH_LINK_UP;
+ break;
+
+ case DESIGNWARE_PCIE_ATU_VIEWPORT:
+ val = root->atu_viewport;
+ break;
+
+ case DESIGNWARE_PCIE_ATU_LOWER_BASE:
+ val = viewport->base;
+ break;
+
+ case DESIGNWARE_PCIE_ATU_UPPER_BASE:
+ val = viewport->base >> 32;
+ break;
+
+ case DESIGNWARE_PCIE_ATU_LOWER_TARGET:
+ val = viewport->target;
+ break;
+
+ case DESIGNWARE_PCIE_ATU_UPPER_TARGET:
+ val = viewport->target >> 32;
+ break;
+
+ case DESIGNWARE_PCIE_ATU_LIMIT:
+ val = viewport->limit;
+ break;
+
+ case DESIGNWARE_PCIE_ATU_CR1:
+ case DESIGNWARE_PCIE_ATU_CR2: /* FALLTHROUGH */
+ val = viewport->cr[(address - DESIGNWARE_PCIE_ATU_CR1) /
+ sizeof(uint32_t)];
+ break;
+
+ default:
+ val = pci_default_read_config(d, address, len);
+ break;
+ }
+
+ return val;
+}
+
+static uint64_t designware_pcie_root_data_access(void *opaque, hwaddr addr,
+ uint64_t *val, unsigned len)
+{
+ DesignwarePCIEViewport *viewport = opaque;
+ DesignwarePCIERoot *root = viewport->root;
+
+ const uint8_t busnum = DESIGNWARE_PCIE_ATU_BUS(viewport->target);
+ const uint8_t devfn = DESIGNWARE_PCIE_ATU_DEVFN(viewport->target);
+ PCIBus *pcibus = pci_get_bus(PCI_DEVICE(root));
+ PCIDevice *pcidev = pci_find_device(pcibus, busnum, devfn);
+
+ if (pcidev) {
+ addr &= pci_config_size(pcidev) - 1;
+
+ if (val) {
+ pci_host_config_write_common(pcidev, addr,
+ pci_config_size(pcidev),
+ *val, len);
+ } else {
+ return pci_host_config_read_common(pcidev, addr,
+ pci_config_size(pcidev),
+ len);
+ }
+ }
+
+ return UINT64_MAX;
+}
+
+static uint64_t designware_pcie_root_data_read(void *opaque, hwaddr addr,
+ unsigned len)
+{
+ return designware_pcie_root_data_access(opaque, addr, NULL, len);
+}
+
+static void designware_pcie_root_data_write(void *opaque, hwaddr addr,
+ uint64_t val, unsigned len)
+{
+ designware_pcie_root_data_access(opaque, addr, &val, len);
+}
+
+static const MemoryRegionOps designware_pci_host_conf_ops = {
+ .read = designware_pcie_root_data_read,
+ .write = designware_pcie_root_data_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid = {
+ .min_access_size = 1,
+ .max_access_size = 4,
+ },
+};
+
+static void designware_pcie_update_viewport(DesignwarePCIERoot *root,
+ DesignwarePCIEViewport *viewport)
+{
+ const uint64_t target = viewport->target;
+ const uint64_t base = viewport->base;
+ const uint64_t size = (uint64_t)viewport->limit - base + 1;
+ const bool enabled = viewport->cr[1] & DESIGNWARE_PCIE_ATU_ENABLE;
+
+ MemoryRegion *current, *other;
+
+ if (viewport->cr[0] == DESIGNWARE_PCIE_ATU_TYPE_MEM) {
+ current = &viewport->mem;
+ other = &viewport->cfg;
+ memory_region_set_alias_offset(current, target);
+ } else {
+ current = &viewport->cfg;
+ other = &viewport->mem;
+ }
+
+ /*
+ * An outbound viewport can be reconfigure from being MEM to CFG,
+ * to account for that we disable the "other" memory region that
+ * becomes unused due to that fact.
+ */
+ memory_region_set_enabled(other, false);
+ if (enabled) {
+ memory_region_set_size(current, size);
+ memory_region_set_address(current, base);
+ }
+ memory_region_set_enabled(current, enabled);
+}
+
+static void designware_pcie_root_config_write(PCIDevice *d, uint32_t address,
+ uint32_t val, int len)
+{
+ DesignwarePCIERoot *root = DESIGNWARE_PCIE_ROOT(d);
+ DesignwarePCIEHost *host = designware_pcie_root_to_host(root);
+ DesignwarePCIEViewport *viewport =
+ designware_pcie_root_get_current_viewport(root);
+
+ switch (address) {
+ case DESIGNWARE_PCIE_PORT_LINK_CONTROL:
+ case DESIGNWARE_PCIE_LINK_WIDTH_SPEED_CONTROL:
+ case DESIGNWARE_PCIE_PHY_DEBUG_R1:
+ /* No-op */
+ break;
+
+ case DESIGNWARE_PCIE_MSI_ADDR_LO:
+ root->msi.base &= 0xFFFFFFFF00000000ULL;
+ root->msi.base |= val;
+ break;
+
+ case DESIGNWARE_PCIE_MSI_ADDR_HI:
+ root->msi.base &= 0x00000000FFFFFFFFULL;
+ root->msi.base |= (uint64_t)val << 32;
+ break;
+
+ case DESIGNWARE_PCIE_MSI_INTR0_ENABLE: {
+ const bool update_msi_mapping = !root->msi.intr[0].enable ^ !!val;
+
+ root->msi.intr[0].enable = val;
+
+ if (update_msi_mapping) {
+ designware_pcie_root_update_msi_mapping(root);
+ }
+ break;
+ }
+
+ case DESIGNWARE_PCIE_MSI_INTR0_MASK:
+ root->msi.intr[0].mask = val;
+ break;
+
+ case DESIGNWARE_PCIE_MSI_INTR0_STATUS:
+ root->msi.intr[0].status ^= val;
+ if (!root->msi.intr[0].status) {
+ qemu_set_irq(host->pci.irqs[0], 0);
+ }
+ break;
+
+ case DESIGNWARE_PCIE_ATU_VIEWPORT:
+ root->atu_viewport = val;
+ break;
+
+ case DESIGNWARE_PCIE_ATU_LOWER_BASE:
+ viewport->base &= 0xFFFFFFFF00000000ULL;
+ viewport->base |= val;
+ break;
+
+ case DESIGNWARE_PCIE_ATU_UPPER_BASE:
+ viewport->base &= 0x00000000FFFFFFFFULL;
+ viewport->base |= (uint64_t)val << 32;
+ break;
+
+ case DESIGNWARE_PCIE_ATU_LOWER_TARGET:
+ viewport->target &= 0xFFFFFFFF00000000ULL;
+ viewport->target |= val;
+ break;
+
+ case DESIGNWARE_PCIE_ATU_UPPER_TARGET:
+ viewport->target &= 0x00000000FFFFFFFFULL;
+ viewport->target |= val;
+ break;
+
+ case DESIGNWARE_PCIE_ATU_LIMIT:
+ viewport->limit = val;
+ break;
+
+ case DESIGNWARE_PCIE_ATU_CR1:
+ viewport->cr[0] = val;
+ break;
+ case DESIGNWARE_PCIE_ATU_CR2:
+ viewport->cr[1] = val;
+ designware_pcie_update_viewport(root, viewport);
+ break;
+
+ default:
+ pci_bridge_write_config(d, address, val, len);
+ break;
+ }
+}
+
+static char *designware_pcie_viewport_name(const char *direction,
+ unsigned int i,
+ const char *type)
+{
+ return g_strdup_printf("PCI %s Viewport %u [%s]",
+ direction, i, type);
+}
+
+static void designware_pcie_root_realize(PCIDevice *dev, Error **errp)
+{
+ DesignwarePCIERoot *root = DESIGNWARE_PCIE_ROOT(dev);
+ DesignwarePCIEHost *host = designware_pcie_root_to_host(root);
+ MemoryRegion *address_space = &host->pci.memory;
+ PCIBridge *br = PCI_BRIDGE(dev);
+ DesignwarePCIEViewport *viewport;
+ /*
+ * Dummy values used for initial configuration of MemoryRegions
+ * that belong to a given viewport
+ */
+ const hwaddr dummy_offset = 0;
+ const uint64_t dummy_size = 4;
+ size_t i;
+
+ br->bus_name = "dw-pcie";
+
+ pci_set_word(dev->config + PCI_COMMAND,
+ PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
+
+ pci_config_set_interrupt_pin(dev->config, 1);
+ pci_bridge_initfn(dev, TYPE_PCIE_BUS);
+
+ pcie_port_init_reg(dev);
+
+ pcie_cap_init(dev, 0x70, PCI_EXP_TYPE_ROOT_PORT,
+ 0, &error_fatal);
+
+ msi_nonbroken = true;
+ msi_init(dev, 0x50, 32, true, true, &error_fatal);
+
+ for (i = 0; i < DESIGNWARE_PCIE_NUM_VIEWPORTS; i++) {
+ MemoryRegion *source, *destination, *mem;
+ const char *direction;
+ char *name;
+
+ viewport = &root->viewports[DESIGNWARE_PCIE_VIEWPORT_INBOUND][i];
+ viewport->inbound = true;
+ viewport->base = 0x0000000000000000ULL;
+ viewport->target = 0x0000000000000000ULL;
+ viewport->limit = UINT32_MAX;
+ viewport->cr[0] = DESIGNWARE_PCIE_ATU_TYPE_MEM;
+
+ source = &host->pci.address_space_root;
+ destination = get_system_memory();
+ direction = "Inbound";
+
+ /*
+ * Configure MemoryRegion implementing PCI -> CPU memory
+ * access
+ */
+ mem = &viewport->mem;
+ name = designware_pcie_viewport_name(direction, i, "MEM");
+ memory_region_init_alias(mem, OBJECT(root), name, destination,
+ dummy_offset, dummy_size);
+ memory_region_add_subregion_overlap(source, dummy_offset, mem, -1);
+ memory_region_set_enabled(mem, false);
+ g_free(name);
+
+ viewport = &root->viewports[DESIGNWARE_PCIE_VIEWPORT_OUTBOUND][i];
+ viewport->root = root;
+ viewport->inbound = false;
+ viewport->base = 0x0000000000000000ULL;
+ viewport->target = 0x0000000000000000ULL;
+ viewport->limit = UINT32_MAX;
+ viewport->cr[0] = DESIGNWARE_PCIE_ATU_TYPE_MEM;
+
+ destination = &host->pci.memory;
+ direction = "Outbound";
+ source = get_system_memory();
+
+ /*
+ * Configure MemoryRegion implementing CPU -> PCI memory
+ * access
+ */
+ mem = &viewport->mem;
+ name = designware_pcie_viewport_name(direction, i, "MEM");
+ memory_region_init_alias(mem, OBJECT(root), name, destination,
+ dummy_offset, dummy_size);
+ memory_region_add_subregion(source, dummy_offset, mem);
+ memory_region_set_enabled(mem, false);
+ g_free(name);
+
+ /*
+ * Configure MemoryRegion implementing access to configuration
+ * space
+ */
+ mem = &viewport->cfg;
+ name = designware_pcie_viewport_name(direction, i, "CFG");
+ memory_region_init_io(&viewport->cfg, OBJECT(root),
+ &designware_pci_host_conf_ops,
+ viewport, name, dummy_size);
+ memory_region_add_subregion(source, dummy_offset, mem);
+ memory_region_set_enabled(mem, false);
+ g_free(name);
+ }
+
+ /*
+ * If no inbound iATU windows are configured, HW defaults to
+ * letting inbound TLPs to pass in. We emulate that by exlicitly
+ * configuring first inbound window to cover all of target's
+ * address space.
+ *
+ * NOTE: This will not work correctly for the case when first
+ * configured inbound window is window 0
+ */
+ viewport = &root->viewports[DESIGNWARE_PCIE_VIEWPORT_INBOUND][0];
+ viewport->cr[1] = DESIGNWARE_PCIE_ATU_ENABLE;
+ designware_pcie_update_viewport(root, viewport);
+
+ memory_region_init_io(&root->msi.iomem, OBJECT(root),
+ &designware_pci_host_msi_ops,
+ root, "pcie-msi", 0x4);
+ /*
+ * We initially place MSI interrupt I/O region a adress 0 and
+ * disable it. It'll be later moved to correct offset and enabled
+ * in designware_pcie_root_update_msi_mapping() as a part of
+ * initialization done by guest OS
+ */
+ memory_region_add_subregion(address_space, dummy_offset, &root->msi.iomem);
+ memory_region_set_enabled(&root->msi.iomem, false);
+}
+
+static void designware_pcie_set_irq(void *opaque, int irq_num, int level)
+{
+ DesignwarePCIEHost *host = DESIGNWARE_PCIE_HOST(opaque);
+
+ qemu_set_irq(host->pci.irqs[irq_num], level);
+}
+
+static const char *
+designware_pcie_host_root_bus_path(PCIHostState *host_bridge, PCIBus *rootbus)
+{
+ return "0000:00";
+}
+
+static const VMStateDescription vmstate_designware_pcie_msi_bank = {
+ .name = "designware-pcie-msi-bank",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(enable, DesignwarePCIEMSIBank),
+ VMSTATE_UINT32(mask, DesignwarePCIEMSIBank),
+ VMSTATE_UINT32(status, DesignwarePCIEMSIBank),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_designware_pcie_msi = {
+ .name = "designware-pcie-msi",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT64(base, DesignwarePCIEMSI),
+ VMSTATE_STRUCT_ARRAY(intr,
+ DesignwarePCIEMSI,
+ DESIGNWARE_PCIE_NUM_MSI_BANKS,
+ 1,
+ vmstate_designware_pcie_msi_bank,
+ DesignwarePCIEMSIBank),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_designware_pcie_viewport = {
+ .name = "designware-pcie-viewport",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT64(base, DesignwarePCIEViewport),
+ VMSTATE_UINT64(target, DesignwarePCIEViewport),
+ VMSTATE_UINT32(limit, DesignwarePCIEViewport),
+ VMSTATE_UINT32_ARRAY(cr, DesignwarePCIEViewport, 2),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_designware_pcie_root = {
+ .name = "designware-pcie-root",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_PCI_DEVICE(parent_obj, PCIBridge),
+ VMSTATE_UINT32(atu_viewport, DesignwarePCIERoot),
+ VMSTATE_STRUCT_2DARRAY(viewports,
+ DesignwarePCIERoot,
+ 2,
+ DESIGNWARE_PCIE_NUM_VIEWPORTS,
+ 1,
+ vmstate_designware_pcie_viewport,
+ DesignwarePCIEViewport),
+ VMSTATE_STRUCT(msi,
+ DesignwarePCIERoot,
+ 1,
+ vmstate_designware_pcie_msi,
+ DesignwarePCIEMSI),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void designware_pcie_root_class_init(ObjectClass *klass, void *data)
+{
+ PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
+
+ k->vendor_id = PCI_VENDOR_ID_SYNOPSYS;
+ k->device_id = 0xABCD;
+ k->revision = 0;
+ k->class_id = PCI_CLASS_BRIDGE_PCI;
+ k->is_bridge = true;
+ k->exit = pci_bridge_exitfn;
+ k->realize = designware_pcie_root_realize;
+ k->config_read = designware_pcie_root_config_read;
+ k->config_write = designware_pcie_root_config_write;
+
+ dc->reset = pci_bridge_reset;
+ /*
+ * PCI-facing part of the host bridge, not usable without the
+ * host-facing part, which can't be device_add'ed, yet.
+ */
+ dc->user_creatable = false;
+ dc->vmsd = &vmstate_designware_pcie_root;
+}
+
+static uint64_t designware_pcie_host_mmio_read(void *opaque, hwaddr addr,
+ unsigned int size)
+{
+ PCIHostState *pci = PCI_HOST_BRIDGE(opaque);
+ PCIDevice *device = pci_find_device(pci->bus, 0, 0);
+
+ return pci_host_config_read_common(device,
+ addr,
+ pci_config_size(device),
+ size);
+}
+
+static void designware_pcie_host_mmio_write(void *opaque, hwaddr addr,
+ uint64_t val, unsigned int size)
+{
+ PCIHostState *pci = PCI_HOST_BRIDGE(opaque);
+ PCIDevice *device = pci_find_device(pci->bus, 0, 0);
+
+ return pci_host_config_write_common(device,
+ addr,
+ pci_config_size(device),
+ val, size);
+}
+
+static const MemoryRegionOps designware_pci_mmio_ops = {
+ .read = designware_pcie_host_mmio_read,
+ .write = designware_pcie_host_mmio_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .impl = {
+ /*
+ * Our device would not work correctly if the guest was doing
+ * unaligned access. This might not be a limitation on the real
+ * device but in practice there is no reason for a guest to access
+ * this device unaligned.
+ */
+ .min_access_size = 4,
+ .max_access_size = 4,
+ .unaligned = false,
+ },
+};
+
+static AddressSpace *designware_pcie_host_set_iommu(PCIBus *bus, void *opaque,
+ int devfn)
+{
+ DesignwarePCIEHost *s = DESIGNWARE_PCIE_HOST(opaque);
+
+ return &s->pci.address_space;
+}
+
+static void designware_pcie_host_realize(DeviceState *dev, Error **errp)
+{
+ PCIHostState *pci = PCI_HOST_BRIDGE(dev);
+ DesignwarePCIEHost *s = DESIGNWARE_PCIE_HOST(dev);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(s->pci.irqs); i++) {
+ sysbus_init_irq(sbd, &s->pci.irqs[i]);
+ }
+
+ memory_region_init_io(&s->mmio,
+ OBJECT(s),
+ &designware_pci_mmio_ops,
+ s,
+ "pcie.reg", 4 * 1024);
+ sysbus_init_mmio(sbd, &s->mmio);
+
+ memory_region_init(&s->pci.io, OBJECT(s), "pcie-pio", 16);
+ memory_region_init(&s->pci.memory, OBJECT(s),
+ "pcie-bus-memory",
+ UINT64_MAX);
+
+ pci->bus = pci_register_root_bus(dev, "pcie",
+ designware_pcie_set_irq,
+ pci_swizzle_map_irq_fn,
+ s,
+ &s->pci.memory,
+ &s->pci.io,
+ 0, 4,
+ TYPE_PCIE_BUS);
+
+ memory_region_init(&s->pci.address_space_root,
+ OBJECT(s),
+ "pcie-bus-address-space-root",
+ UINT64_MAX);
+ memory_region_add_subregion(&s->pci.address_space_root,
+ 0x0, &s->pci.memory);
+ address_space_init(&s->pci.address_space,
+ &s->pci.address_space_root,
+ "pcie-bus-address-space");
+ pci_setup_iommu(pci->bus, designware_pcie_host_set_iommu, s);
+
+ qdev_set_parent_bus(DEVICE(&s->root), BUS(pci->bus));
+ qdev_init_nofail(DEVICE(&s->root));
+}
+
+static const VMStateDescription vmstate_designware_pcie_host = {
+ .name = "designware-pcie-host",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_STRUCT(root,
+ DesignwarePCIEHost,
+ 1,
+ vmstate_designware_pcie_root,
+ DesignwarePCIERoot),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void designware_pcie_host_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(klass);
+
+ hc->root_bus_path = designware_pcie_host_root_bus_path;
+ dc->realize = designware_pcie_host_realize;
+ set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
+ dc->fw_name = "pci";
+ dc->vmsd = &vmstate_designware_pcie_host;
+}
+
+static void designware_pcie_host_init(Object *obj)
+{
+ DesignwarePCIEHost *s = DESIGNWARE_PCIE_HOST(obj);
+ DesignwarePCIERoot *root = &s->root;
+
+ object_initialize(root, sizeof(*root), TYPE_DESIGNWARE_PCIE_ROOT);
+ object_property_add_child(obj, "root", OBJECT(root), NULL);
+ qdev_prop_set_int32(DEVICE(root), "addr", PCI_DEVFN(0, 0));
+ qdev_prop_set_bit(DEVICE(root), "multifunction", false);
+}
+
+static const TypeInfo designware_pcie_root_info = {
+ .name = TYPE_DESIGNWARE_PCIE_ROOT,
+ .parent = TYPE_PCI_BRIDGE,
+ .instance_size = sizeof(DesignwarePCIERoot),
+ .class_init = designware_pcie_root_class_init,
+ .interfaces = (InterfaceInfo[]) {
+ { INTERFACE_PCIE_DEVICE },
+ { }
+ },
+};
+
+static const TypeInfo designware_pcie_host_info = {
+ .name = TYPE_DESIGNWARE_PCIE_HOST,
+ .parent = TYPE_PCI_HOST_BRIDGE,
+ .instance_size = sizeof(DesignwarePCIEHost),
+ .instance_init = designware_pcie_host_init,
+ .class_init = designware_pcie_host_class_init,
+};
+
+static void designware_pcie_register(void)
+{
+ type_register_static(&designware_pcie_root_info);
+ type_register_static(&designware_pcie_host_info);
+}
+type_init(designware_pcie_register)
diff --git a/hw/sd/Makefile.objs b/hw/sd/Makefile.objs
index c2b7664264..a99d9fbb04 100644
--- a/hw/sd/Makefile.objs
+++ b/hw/sd/Makefile.objs
@@ -1,6 +1,6 @@
common-obj-$(CONFIG_PL181) += pl181.o
common-obj-$(CONFIG_SSI_SD) += ssi-sd.o
-common-obj-$(CONFIG_SD) += sd.o core.o
+common-obj-$(CONFIG_SD) += sd.o core.o sdmmc-internal.o
common-obj-$(CONFIG_SDHCI) += sdhci.o
obj-$(CONFIG_MILKYMIST) += milkymist-memcard.o
diff --git a/hw/sd/sd.c b/hw/sd/sd.c
index 933890e86f..235e0518d6 100644
--- a/hw/sd/sd.c
+++ b/hw/sd/sd.c
@@ -120,6 +120,7 @@ struct SDState {
qemu_irq readonly_cb;
qemu_irq inserted_cb;
QEMUTimer *ocr_power_timer;
+ const char *proto_name;
bool enable;
uint8_t dat_lines;
bool cmd_line;
@@ -866,13 +867,19 @@ static void sd_lock_command(SDState *sd)
sd->card_status &= ~CARD_IS_LOCKED;
}
-static sd_rsp_type_t sd_normal_command(SDState *sd,
- SDRequest req)
+static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req)
{
uint32_t rca = 0x0000;
uint64_t addr = (sd->ocr & (1 << 30)) ? (uint64_t) req.arg << 9 : req.arg;
- trace_sdcard_normal_command(req.cmd, req.arg, sd_state_name(sd->state));
+ /* CMD55 precedes an ACMD, so we are not interested in tracing it.
+ * However there is no ACMD55, so we want to trace this particular case.
+ */
+ if (req.cmd != 55 || sd->expecting_acmd) {
+ trace_sdcard_normal_command(sd->proto_name,
+ sd_cmd_name(req.cmd), req.cmd,
+ req.arg, sd_state_name(sd->state));
+ }
/* Not interpreting this as an app command */
sd->card_status &= ~APP_CMD;
@@ -1162,6 +1169,14 @@ static sd_rsp_type_t sd_normal_command(SDState *sd,
}
break;
+ case 19: /* CMD19: SEND_TUNING_BLOCK (SD) */
+ if (sd->state == sd_transfer_state) {
+ sd->state = sd_sendingdata_state;
+ sd->data_offset = 0;
+ return sd_r1;
+ }
+ break;
+
case 23: /* CMD23: SET_BLOCK_COUNT */
switch (sd->state) {
case sd_transfer_state:
@@ -1450,7 +1465,8 @@ static sd_rsp_type_t sd_normal_command(SDState *sd,
static sd_rsp_type_t sd_app_command(SDState *sd,
SDRequest req)
{
- trace_sdcard_app_command(req.cmd, req.arg);
+ trace_sdcard_app_command(sd->proto_name, sd_acmd_name(req.cmd),
+ req.cmd, req.arg, sd_state_name(sd->state));
sd->card_status |= APP_CMD;
switch (req.cmd) {
case 6: /* ACMD6: SET_BUS_WIDTH */
@@ -1765,7 +1781,9 @@ void sd_write_data(SDState *sd, uint8_t value)
if (sd->card_status & (ADDRESS_ERROR | WP_VIOLATION))
return;
- trace_sdcard_write_data(sd->current_cmd, value);
+ trace_sdcard_write_data(sd->proto_name,
+ sd_acmd_name(sd->current_cmd),
+ sd->current_cmd, value);
switch (sd->current_cmd) {
case 24: /* CMD24: WRITE_SINGLE_BLOCK */
sd->data[sd->data_offset ++] = value;
@@ -1883,6 +1901,20 @@ void sd_write_data(SDState *sd, uint8_t value)
}
}
+#define SD_TUNING_BLOCK_SIZE 64
+
+static const uint8_t sd_tuning_block_pattern[SD_TUNING_BLOCK_SIZE] = {
+ /* See: Physical Layer Simplified Specification Version 3.01, Table 4-2 */
+ 0xff, 0x0f, 0xff, 0x00, 0x0f, 0xfc, 0xc3, 0xcc,
+ 0xc3, 0x3c, 0xcc, 0xff, 0xfe, 0xff, 0xfe, 0xef,
+ 0xff, 0xdf, 0xff, 0xdd, 0xff, 0xfb, 0xff, 0xfb,
+ 0xbf, 0xff, 0x7f, 0xff, 0x77, 0xf7, 0xbd, 0xef,
+ 0xff, 0xf0, 0xff, 0xf0, 0x0f, 0xfc, 0xcc, 0x3c,
+ 0xcc, 0x33, 0xcc, 0xcf, 0xff, 0xef, 0xff, 0xee,
+ 0xff, 0xfd, 0xff, 0xfd, 0xdf, 0xff, 0xbf, 0xff,
+ 0xbb, 0xff, 0xf7, 0xff, 0xf7, 0x7f, 0x7b, 0xde,
+};
+
uint8_t sd_read_data(SDState *sd)
{
/* TODO: Append CRCs */
@@ -1903,7 +1935,9 @@ uint8_t sd_read_data(SDState *sd)
io_len = (sd->ocr & (1 << 30)) ? 512 : sd->blk_len;
- trace_sdcard_read_data(sd->current_cmd, io_len);
+ trace_sdcard_read_data(sd->proto_name,
+ sd_acmd_name(sd->current_cmd),
+ sd->current_cmd, io_len);
switch (sd->current_cmd) {
case 6: /* CMD6: SWITCH_FUNCTION */
ret = sd->data[sd->data_offset ++];
@@ -1960,6 +1994,13 @@ uint8_t sd_read_data(SDState *sd)
}
break;
+ case 19: /* CMD19: SEND_TUNING_BLOCK (SD) */
+ if (sd->data_offset >= SD_TUNING_BLOCK_SIZE - 1) {
+ sd->state = sd_transfer_state;
+ }
+ ret = sd_tuning_block_pattern[sd->data_offset++];
+ break;
+
case 22: /* ACMD22: SEND_NUM_WR_BLOCKS */
ret = sd->data[sd->data_offset ++];
@@ -2029,6 +2070,8 @@ static void sd_realize(DeviceState *dev, Error **errp)
SDState *sd = SD_CARD(dev);
int ret;
+ sd->proto_name = sd->spi ? "SPI" : "SD";
+
if (sd->blk && blk_is_read_only(sd->blk)) {
error_setg(errp, "Cannot use read-only drive as SD card");
return;
diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c
index 97b4a473c8..1b828b104d 100644
--- a/hw/sd/sdhci.c
+++ b/hw/sd/sdhci.c
@@ -433,13 +433,13 @@ static void sdhci_read_block_from_card(SDHCIState *s)
for (index = 0; index < blk_size; index++) {
data = sdbus_read_data(&s->sdbus);
if (!FIELD_EX32(s->hostctl2, SDHC_HOSTCTL2, EXECUTE_TUNING)) {
- /* Device is not in tunning */
+ /* Device is not in tuning */
s->fifo_buffer[index] = data;
}
}
if (FIELD_EX32(s->hostctl2, SDHC_HOSTCTL2, EXECUTE_TUNING)) {
- /* Device is in tunning */
+ /* Device is in tuning */
s->hostctl2 &= ~R_SDHC_HOSTCTL2_EXECUTE_TUNING_MASK;
s->hostctl2 |= R_SDHC_HOSTCTL2_SAMPLING_CLKSEL_MASK;
s->prnsts &= ~(SDHC_DAT_LINE_ACTIVE | SDHC_DOING_READ |
diff --git a/hw/sd/sdmmc-internal.c b/hw/sd/sdmmc-internal.c
new file mode 100644
index 0000000000..2053def3f1
--- /dev/null
+++ b/hw/sd/sdmmc-internal.c
@@ -0,0 +1,72 @@
+/*
+ * SD/MMC cards common helpers
+ *
+ * Copyright (c) 2018 Philippe Mathieu-Daudé <f4bug@amsat.org>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "sdmmc-internal.h"
+
+const char *sd_cmd_name(uint8_t cmd)
+{
+ static const char *cmd_abbrev[SDMMC_CMD_MAX] = {
+ [0] = "GO_IDLE_STATE",
+ [2] = "ALL_SEND_CID", [3] = "SEND_RELATIVE_ADDR",
+ [4] = "SET_DSR", [5] = "IO_SEND_OP_COND",
+ [6] = "SWITCH_FUNC", [7] = "SELECT/DESELECT_CARD",
+ [8] = "SEND_IF_COND", [9] = "SEND_CSD",
+ [10] = "SEND_CID", [11] = "VOLTAGE_SWITCH",
+ [12] = "STOP_TRANSMISSION", [13] = "SEND_STATUS",
+ [15] = "GO_INACTIVE_STATE",
+ [16] = "SET_BLOCKLEN", [17] = "READ_SINGLE_BLOCK",
+ [18] = "READ_MULTIPLE_BLOCK", [19] = "SEND_TUNING_BLOCK",
+ [20] = "SPEED_CLASS_CONTROL", [21] = "DPS_spec",
+ [23] = "SET_BLOCK_COUNT",
+ [24] = "WRITE_BLOCK", [25] = "WRITE_MULTIPLE_BLOCK",
+ [26] = "MANUF_RSVD", [27] = "PROGRAM_CSD",
+ [28] = "SET_WRITE_PROT", [29] = "CLR_WRITE_PROT",
+ [30] = "SEND_WRITE_PROT",
+ [32] = "ERASE_WR_BLK_START", [33] = "ERASE_WR_BLK_END",
+ [34] = "SW_FUNC_RSVD", [35] = "SW_FUNC_RSVD",
+ [36] = "SW_FUNC_RSVD", [37] = "SW_FUNC_RSVD",
+ [38] = "ERASE",
+ [40] = "DPS_spec",
+ [42] = "LOCK_UNLOCK", [43] = "Q_MANAGEMENT",
+ [44] = "Q_TASK_INFO_A", [45] = "Q_TASK_INFO_B",
+ [46] = "Q_RD_TASK", [47] = "Q_WR_TASK",
+ [48] = "READ_EXTR_SINGLE", [49] = "WRITE_EXTR_SINGLE",
+ [50] = "SW_FUNC_RSVD",
+ [52] = "IO_RW_DIRECT", [53] = "IO_RW_EXTENDED",
+ [54] = "SDIO_RSVD", [55] = "APP_CMD",
+ [56] = "GEN_CMD", [57] = "SW_FUNC_RSVD",
+ [58] = "READ_EXTR_MULTI", [59] = "WRITE_EXTR_MULTI",
+ [60] = "MANUF_RSVD", [61] = "MANUF_RSVD",
+ [62] = "MANUF_RSVD", [63] = "MANUF_RSVD",
+ };
+ return cmd_abbrev[cmd] ? cmd_abbrev[cmd] : "UNKNOWN_CMD";
+}
+
+const char *sd_acmd_name(uint8_t cmd)
+{
+ static const char *acmd_abbrev[SDMMC_CMD_MAX] = {
+ [6] = "SET_BUS_WIDTH",
+ [13] = "SD_STATUS",
+ [14] = "DPS_spec", [15] = "DPS_spec",
+ [16] = "DPS_spec",
+ [18] = "SECU_spec",
+ [22] = "SEND_NUM_WR_BLOCKS", [23] = "SET_WR_BLK_ERASE_COUNT",
+ [41] = "SD_SEND_OP_COND",
+ [42] = "SET_CLR_CARD_DETECT",
+ [51] = "SEND_SCR",
+ [52] = "SECU_spec", [53] = "SECU_spec",
+ [54] = "SECU_spec",
+ [56] = "SECU_spec", [57] = "SECU_spec",
+ [58] = "SECU_spec", [59] = "SECU_spec",
+ };
+
+ return acmd_abbrev[cmd] ? acmd_abbrev[cmd] : "UNKNOWN_ACMD";
+}
diff --git a/hw/sd/sdmmc-internal.h b/hw/sd/sdmmc-internal.h
index 0e96cb0081..9aa04766fc 100644
--- a/hw/sd/sdmmc-internal.h
+++ b/hw/sd/sdmmc-internal.h
@@ -12,4 +12,28 @@
#define SDMMC_CMD_MAX 64
+/**
+ * sd_cmd_name:
+ * @cmd: A SD "normal" command, up to SDMMC_CMD_MAX.
+ *
+ * Returns a human-readable name describing the command.
+ * The return value is always a static string which does not need
+ * to be freed after use.
+ *
+ * Returns: The command name of @cmd or "UNKNOWN_CMD".
+ */
+const char *sd_cmd_name(uint8_t cmd);
+
+/**
+ * sd_acmd_name:
+ * @cmd: A SD "Application-Specific" command, up to SDMMC_CMD_MAX.
+ *
+ * Returns a human-readable name describing the application command.
+ * The return value is always a static string which does not need
+ * to be freed after use.
+ *
+ * Returns: The application command name of @cmd or "UNKNOWN_ACMD".
+ */
+const char *sd_acmd_name(uint8_t cmd);
+
#endif
diff --git a/hw/sd/trace-events b/hw/sd/trace-events
index 3040d32560..2059ace61f 100644
--- a/hw/sd/trace-events
+++ b/hw/sd/trace-events
@@ -24,8 +24,8 @@ sdhci_write_dataport(uint16_t data_count) "write buffer filled with %u bytes of
sdhci_capareg(const char *desc, uint16_t val) "%s: %u"
# hw/sd/sd.c
-sdcard_normal_command(uint8_t cmd, uint32_t arg, const char *state) "CMD%d arg 0x%08x (state %s)"
-sdcard_app_command(uint8_t acmd, uint32_t arg) "ACMD%d arg 0x%08x"
+sdcard_normal_command(const char *proto, const char *cmd_desc, uint8_t cmd, uint32_t arg, const char *state) "%s %20s/ CMD%02d arg 0x%08x (state %s)"
+sdcard_app_command(const char *proto, const char *acmd_desc, uint8_t acmd, uint32_t arg, const char *state) "%s %23s/ACMD%02d arg 0x%08x (state %s)"
sdcard_response(const char *rspdesc, int rsplen) "%s (sz:%d)"
sdcard_powerup(void) ""
sdcard_inquiry_cmd41(void) ""
@@ -39,8 +39,8 @@ sdcard_lock(void) ""
sdcard_unlock(void) ""
sdcard_read_block(uint64_t addr, uint32_t len) "addr 0x%" PRIx64 " size 0x%x"
sdcard_write_block(uint64_t addr, uint32_t len) "addr 0x%" PRIx64 " size 0x%x"
-sdcard_write_data(uint8_t cmd, uint8_t value) "CMD%02d value 0x%02x"
-sdcard_read_data(uint8_t cmd, int length) "CMD%02d len %d"
+sdcard_write_data(const char *proto, const char *cmd_desc, uint8_t cmd, uint8_t value) "%s %20s/ CMD%02d value 0x%02x"
+sdcard_read_data(const char *proto, const char *cmd_desc, uint8_t cmd, int length) "%s %20s/ CMD%02d len %d"
sdcard_set_voltage(uint16_t millivolts) "%u mV"
# hw/sd/milkymist-memcard.c
diff --git a/include/block/block.h b/include/block/block.h
index 8b6db952a2..cdec3639a3 100644
--- a/include/block/block.h
+++ b/include/block/block.h
@@ -226,6 +226,7 @@ char *bdrv_perm_names(uint64_t perm);
void bdrv_init(void);
void bdrv_init_with_whitelist(void);
bool bdrv_uses_whitelist(void);
+int bdrv_is_whitelisted(BlockDriver *drv, bool read_only);
BlockDriver *bdrv_find_protocol(const char *filename,
bool allow_protocol_prefix,
Error **errp);
@@ -246,6 +247,7 @@ BdrvChild *bdrv_open_child(const char *filename,
BlockDriverState* parent,
const BdrvChildRole *child_role,
bool allow_none, Error **errp);
+BlockDriverState *bdrv_open_blockdev_ref(BlockdevRef *ref, Error **errp);
void bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd,
Error **errp);
int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options,
diff --git a/include/block/block_int.h b/include/block/block_int.h
index 64a5700f2b..27e17addba 100644
--- a/include/block/block_int.h
+++ b/include/block/block_int.h
@@ -129,8 +129,11 @@ struct BlockDriver {
int (*bdrv_file_open)(BlockDriverState *bs, QDict *options, int flags,
Error **errp);
void (*bdrv_close)(BlockDriverState *bs);
- int coroutine_fn (*bdrv_co_create_opts)(const char *filename, QemuOpts *opts,
+ int coroutine_fn (*bdrv_co_create)(BlockdevCreateOptions *opts,
Error **errp);
+ int coroutine_fn (*bdrv_co_create_opts)(const char *filename,
+ QemuOpts *opts,
+ Error **errp);
int (*bdrv_make_empty)(BlockDriverState *bs);
void (*bdrv_refresh_filename)(BlockDriverState *bs, QDict *options);
@@ -224,7 +227,8 @@ struct BlockDriver {
/*
* Invalidate any cached meta-data.
*/
- void (*bdrv_invalidate_cache)(BlockDriverState *bs, Error **errp);
+ void coroutine_fn (*bdrv_co_invalidate_cache)(BlockDriverState *bs,
+ Error **errp);
int (*bdrv_inactivate)(BlockDriverState *bs);
/*
@@ -306,8 +310,9 @@ struct BlockDriver {
* Returns 0 for completed check, -errno for internal errors.
* The check results are stored in result.
*/
- int (*bdrv_check)(BlockDriverState *bs, BdrvCheckResult *result,
- BdrvCheckMode fix);
+ int coroutine_fn (*bdrv_co_check)(BlockDriverState *bs,
+ BdrvCheckResult *result,
+ BdrvCheckMode fix);
int (*bdrv_amend_options)(BlockDriverState *bs, QemuOpts *opts,
BlockDriverAmendStatusCB *status_cb,
diff --git a/include/hw/arm/fsl-imx7.h b/include/hw/arm/fsl-imx7.h
new file mode 100644
index 0000000000..d848262bfd
--- /dev/null
+++ b/include/hw/arm/fsl-imx7.h
@@ -0,0 +1,222 @@
+/*
+ * Copyright (c) 2018, Impinj, Inc.
+ *
+ * i.MX7 SoC definitions
+ *
+ * Author: Andrey Smirnov <andrew.smirnov@gmail.com>
+ *
+ * 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.
+ */
+
+#ifndef FSL_IMX7_H
+#define FSL_IMX7_H
+
+#include "hw/arm/arm.h"
+#include "hw/cpu/a15mpcore.h"
+#include "hw/intc/imx_gpcv2.h"
+#include "hw/misc/imx7_ccm.h"
+#include "hw/misc/imx7_snvs.h"
+#include "hw/misc/imx7_gpr.h"
+#include "hw/misc/imx6_src.h"
+#include "hw/misc/imx2_wdt.h"
+#include "hw/gpio/imx_gpio.h"
+#include "hw/char/imx_serial.h"
+#include "hw/timer/imx_gpt.h"
+#include "hw/timer/imx_epit.h"
+#include "hw/i2c/imx_i2c.h"
+#include "hw/gpio/imx_gpio.h"
+#include "hw/sd/sdhci.h"
+#include "hw/ssi/imx_spi.h"
+#include "hw/net/imx_fec.h"
+#include "hw/pci-host/designware.h"
+#include "hw/usb/chipidea.h"
+#include "exec/memory.h"
+#include "cpu.h"
+
+#define TYPE_FSL_IMX7 "fsl,imx7"
+#define FSL_IMX7(obj) OBJECT_CHECK(FslIMX7State, (obj), TYPE_FSL_IMX7)
+
+enum FslIMX7Configuration {
+ FSL_IMX7_NUM_CPUS = 2,
+ FSL_IMX7_NUM_UARTS = 7,
+ FSL_IMX7_NUM_ETHS = 2,
+ FSL_IMX7_ETH_NUM_TX_RINGS = 3,
+ FSL_IMX7_NUM_USDHCS = 3,
+ FSL_IMX7_NUM_WDTS = 4,
+ FSL_IMX7_NUM_GPTS = 4,
+ FSL_IMX7_NUM_IOMUXCS = 2,
+ FSL_IMX7_NUM_GPIOS = 7,
+ FSL_IMX7_NUM_I2CS = 4,
+ FSL_IMX7_NUM_ECSPIS = 4,
+ FSL_IMX7_NUM_USBS = 3,
+ FSL_IMX7_NUM_ADCS = 2,
+};
+
+typedef struct FslIMX7State {
+ /*< private >*/
+ DeviceState parent_obj;
+
+ /*< public >*/
+ ARMCPU cpu[FSL_IMX7_NUM_CPUS];
+ A15MPPrivState a7mpcore;
+ IMXGPTState gpt[FSL_IMX7_NUM_GPTS];
+ IMXGPIOState gpio[FSL_IMX7_NUM_GPIOS];
+ IMX7CCMState ccm;
+ IMX7AnalogState analog;
+ IMX7SNVSState snvs;
+ IMXGPCv2State gpcv2;
+ IMXSPIState spi[FSL_IMX7_NUM_ECSPIS];
+ IMXI2CState i2c[FSL_IMX7_NUM_I2CS];
+ IMXSerialState uart[FSL_IMX7_NUM_UARTS];
+ IMXFECState eth[FSL_IMX7_NUM_ETHS];
+ SDHCIState usdhc[FSL_IMX7_NUM_USDHCS];
+ IMX2WdtState wdt[FSL_IMX7_NUM_WDTS];
+ IMX7GPRState gpr;
+ ChipideaState usb[FSL_IMX7_NUM_USBS];
+ DesignwarePCIEHost pcie;
+} FslIMX7State;
+
+enum FslIMX7MemoryMap {
+ FSL_IMX7_MMDC_ADDR = 0x80000000,
+ FSL_IMX7_MMDC_SIZE = 2 * 1024 * 1024 * 1024UL,
+
+ FSL_IMX7_GPIO1_ADDR = 0x30200000,
+ FSL_IMX7_GPIO2_ADDR = 0x30210000,
+ FSL_IMX7_GPIO3_ADDR = 0x30220000,
+ FSL_IMX7_GPIO4_ADDR = 0x30230000,
+ FSL_IMX7_GPIO5_ADDR = 0x30240000,
+ FSL_IMX7_GPIO6_ADDR = 0x30250000,
+ FSL_IMX7_GPIO7_ADDR = 0x30260000,
+
+ FSL_IMX7_IOMUXC_LPSR_GPR_ADDR = 0x30270000,
+
+ FSL_IMX7_WDOG1_ADDR = 0x30280000,
+ FSL_IMX7_WDOG2_ADDR = 0x30290000,
+ FSL_IMX7_WDOG3_ADDR = 0x302A0000,
+ FSL_IMX7_WDOG4_ADDR = 0x302B0000,
+
+ FSL_IMX7_IOMUXC_LPSR_ADDR = 0x302C0000,
+
+ FSL_IMX7_GPT1_ADDR = 0x302D0000,
+ FSL_IMX7_GPT2_ADDR = 0x302E0000,
+ FSL_IMX7_GPT3_ADDR = 0x302F0000,
+ FSL_IMX7_GPT4_ADDR = 0x30300000,
+
+ FSL_IMX7_IOMUXC_ADDR = 0x30330000,
+ FSL_IMX7_IOMUXC_GPR_ADDR = 0x30340000,
+ FSL_IMX7_IOMUXCn_SIZE = 0x1000,
+
+ FSL_IMX7_ANALOG_ADDR = 0x30360000,
+ FSL_IMX7_SNVS_ADDR = 0x30370000,
+ FSL_IMX7_CCM_ADDR = 0x30380000,
+
+ FSL_IMX7_SRC_ADDR = 0x30390000,
+ FSL_IMX7_SRC_SIZE = 0x1000,
+
+ FSL_IMX7_ADC1_ADDR = 0x30610000,
+ FSL_IMX7_ADC2_ADDR = 0x30620000,
+ FSL_IMX7_ADCn_SIZE = 0x1000,
+
+ FSL_IMX7_GPC_ADDR = 0x303A0000,
+
+ FSL_IMX7_I2C1_ADDR = 0x30A20000,
+ FSL_IMX7_I2C2_ADDR = 0x30A30000,
+ FSL_IMX7_I2C3_ADDR = 0x30A40000,
+ FSL_IMX7_I2C4_ADDR = 0x30A50000,
+
+ FSL_IMX7_ECSPI1_ADDR = 0x30820000,
+ FSL_IMX7_ECSPI2_ADDR = 0x30830000,
+ FSL_IMX7_ECSPI3_ADDR = 0x30840000,
+ FSL_IMX7_ECSPI4_ADDR = 0x30630000,
+
+ FSL_IMX7_LCDIF_ADDR = 0x30730000,
+ FSL_IMX7_LCDIF_SIZE = 0x1000,
+
+ FSL_IMX7_UART1_ADDR = 0x30860000,
+ /*
+ * Some versions of the reference manual claim that UART2 is @
+ * 0x30870000, but experiments with HW + DT files in upstream
+ * Linux kernel show that not to be true and that block is
+ * acutally located @ 0x30890000
+ */
+ FSL_IMX7_UART2_ADDR = 0x30890000,
+ FSL_IMX7_UART3_ADDR = 0x30880000,
+ FSL_IMX7_UART4_ADDR = 0x30A60000,
+ FSL_IMX7_UART5_ADDR = 0x30A70000,
+ FSL_IMX7_UART6_ADDR = 0x30A80000,
+ FSL_IMX7_UART7_ADDR = 0x30A90000,
+
+ FSL_IMX7_ENET1_ADDR = 0x30BE0000,
+ FSL_IMX7_ENET2_ADDR = 0x30BF0000,
+
+ FSL_IMX7_USB1_ADDR = 0x30B10000,
+ FSL_IMX7_USBMISC1_ADDR = 0x30B10200,
+ FSL_IMX7_USB2_ADDR = 0x30B20000,
+ FSL_IMX7_USBMISC2_ADDR = 0x30B20200,
+ FSL_IMX7_USB3_ADDR = 0x30B30000,
+ FSL_IMX7_USBMISC3_ADDR = 0x30B30200,
+ FSL_IMX7_USBMISCn_SIZE = 0x200,
+
+ FSL_IMX7_USDHC1_ADDR = 0x30B40000,
+ FSL_IMX7_USDHC2_ADDR = 0x30B50000,
+ FSL_IMX7_USDHC3_ADDR = 0x30B60000,
+
+ FSL_IMX7_SDMA_ADDR = 0x30BD0000,
+ FSL_IMX7_SDMA_SIZE = 0x1000,
+
+ FSL_IMX7_A7MPCORE_ADDR = 0x31000000,
+ FSL_IMX7_A7MPCORE_DAP_ADDR = 0x30000000,
+
+ FSL_IMX7_PCIE_REG_ADDR = 0x33800000,
+ FSL_IMX7_PCIE_REG_SIZE = 16 * 1024,
+
+ FSL_IMX7_GPR_ADDR = 0x30340000,
+};
+
+enum FslIMX7IRQs {
+ FSL_IMX7_USDHC1_IRQ = 22,
+ FSL_IMX7_USDHC2_IRQ = 23,
+ FSL_IMX7_USDHC3_IRQ = 24,
+
+ FSL_IMX7_UART1_IRQ = 26,
+ FSL_IMX7_UART2_IRQ = 27,
+ FSL_IMX7_UART3_IRQ = 28,
+ FSL_IMX7_UART4_IRQ = 29,
+ FSL_IMX7_UART5_IRQ = 30,
+ FSL_IMX7_UART6_IRQ = 16,
+
+ FSL_IMX7_ECSPI1_IRQ = 31,
+ FSL_IMX7_ECSPI2_IRQ = 32,
+ FSL_IMX7_ECSPI3_IRQ = 33,
+ FSL_IMX7_ECSPI4_IRQ = 34,
+
+ FSL_IMX7_I2C1_IRQ = 35,
+ FSL_IMX7_I2C2_IRQ = 36,
+ FSL_IMX7_I2C3_IRQ = 37,
+ FSL_IMX7_I2C4_IRQ = 38,
+
+ FSL_IMX7_USB1_IRQ = 43,
+ FSL_IMX7_USB2_IRQ = 42,
+ FSL_IMX7_USB3_IRQ = 40,
+
+ FSL_IMX7_PCI_INTA_IRQ = 122,
+ FSL_IMX7_PCI_INTB_IRQ = 123,
+ FSL_IMX7_PCI_INTC_IRQ = 124,
+ FSL_IMX7_PCI_INTD_IRQ = 125,
+
+ FSL_IMX7_UART7_IRQ = 126,
+
+#define FSL_IMX7_ENET_IRQ(i, n) ((n) + ((i) ? 100 : 118))
+
+ FSL_IMX7_MAX_IRQ = 128,
+};
+
+#endif /* FSL_IMX7_H */
diff --git a/include/hw/pci-host/designware.h b/include/hw/pci-host/designware.h
new file mode 100644
index 0000000000..a4f2c0695b
--- /dev/null
+++ b/include/hw/pci-host/designware.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2017, Impinj, Inc.
+ *
+ * Designware PCIe IP block emulation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef DESIGNWARE_H
+#define DESIGNWARE_H
+
+#include "hw/hw.h"
+#include "hw/sysbus.h"
+#include "hw/pci/pci.h"
+#include "hw/pci/pci_bus.h"
+#include "hw/pci/pcie_host.h"
+#include "hw/pci/pci_bridge.h"
+
+#define TYPE_DESIGNWARE_PCIE_HOST "designware-pcie-host"
+#define DESIGNWARE_PCIE_HOST(obj) \
+ OBJECT_CHECK(DesignwarePCIEHost, (obj), TYPE_DESIGNWARE_PCIE_HOST)
+
+#define TYPE_DESIGNWARE_PCIE_ROOT "designware-pcie-root"
+#define DESIGNWARE_PCIE_ROOT(obj) \
+ OBJECT_CHECK(DesignwarePCIERoot, (obj), TYPE_DESIGNWARE_PCIE_ROOT)
+
+struct DesignwarePCIERoot;
+typedef struct DesignwarePCIERoot DesignwarePCIERoot;
+
+typedef struct DesignwarePCIEViewport {
+ DesignwarePCIERoot *root;
+
+ MemoryRegion cfg;
+ MemoryRegion mem;
+
+ uint64_t base;
+ uint64_t target;
+ uint32_t limit;
+ uint32_t cr[2];
+
+ bool inbound;
+} DesignwarePCIEViewport;
+
+typedef struct DesignwarePCIEMSIBank {
+ uint32_t enable;
+ uint32_t mask;
+ uint32_t status;
+} DesignwarePCIEMSIBank;
+
+typedef struct DesignwarePCIEMSI {
+ uint64_t base;
+ MemoryRegion iomem;
+
+#define DESIGNWARE_PCIE_NUM_MSI_BANKS 1
+
+ DesignwarePCIEMSIBank intr[DESIGNWARE_PCIE_NUM_MSI_BANKS];
+} DesignwarePCIEMSI;
+
+struct DesignwarePCIERoot {
+ PCIBridge parent_obj;
+
+ uint32_t atu_viewport;
+
+#define DESIGNWARE_PCIE_VIEWPORT_OUTBOUND 0
+#define DESIGNWARE_PCIE_VIEWPORT_INBOUND 1
+#define DESIGNWARE_PCIE_NUM_VIEWPORTS 4
+
+ DesignwarePCIEViewport viewports[2][DESIGNWARE_PCIE_NUM_VIEWPORTS];
+ DesignwarePCIEMSI msi;
+};
+
+typedef struct DesignwarePCIEHost {
+ PCIHostState parent_obj;
+
+ DesignwarePCIERoot root;
+
+ struct {
+ AddressSpace address_space;
+ MemoryRegion address_space_root;
+
+ MemoryRegion memory;
+ MemoryRegion io;
+
+ qemu_irq irqs[4];
+ } pci;
+
+ MemoryRegion mmio;
+} DesignwarePCIEHost;
+
+#endif /* DESIGNWARE_H */
diff --git a/include/hw/pci/pci_ids.h b/include/hw/pci/pci_ids.h
index 1dbf53627c..63acc722a9 100644
--- a/include/hw/pci/pci_ids.h
+++ b/include/hw/pci/pci_ids.h
@@ -269,4 +269,6 @@
#define PCI_VENDOR_ID_VMWARE 0x15ad
#define PCI_DEVICE_ID_VMWARE_PVRDMA 0x0820
+#define PCI_VENDOR_ID_SYNOPSYS 0x16C3
+
#endif
diff --git a/include/qapi/qmp/qdict.h b/include/qapi/qmp/qdict.h
index ff6f7842c3..7c6d844549 100644
--- a/include/qapi/qmp/qdict.h
+++ b/include/qapi/qmp/qdict.h
@@ -81,4 +81,10 @@ QObject *qdict_crumple(const QDict *src, Error **errp);
void qdict_join(QDict *dest, QDict *src, bool overwrite);
+typedef struct QDictRenames {
+ const char *from;
+ const char *to;
+} QDictRenames;
+bool qdict_rename_keys(QDict *qdict, const QDictRenames *renames, Error **errp);
+
#endif /* QDICT_H */
diff --git a/include/qemu/option.h b/include/qemu/option.h
index b127fb6db6..306fdb5f7a 100644
--- a/include/qemu/option.h
+++ b/include/qemu/option.h
@@ -124,6 +124,8 @@ void qemu_opts_set_defaults(QemuOptsList *list, const char *params,
int permit_abbrev);
QemuOpts *qemu_opts_from_qdict(QemuOptsList *list, const QDict *qdict,
Error **errp);
+QDict *qemu_opts_to_qdict_filtered(QemuOpts *opts, QDict *qdict,
+ QemuOptsList *list, bool del);
QDict *qemu_opts_to_qdict(QemuOpts *opts, QDict *qdict);
void qemu_opts_absorb_qdict(QemuOpts *opts, QDict *qdict, Error **errp);
diff --git a/include/ui/console.h b/include/ui/console.h
index aae9e44cb3..5fca9afcbc 100644
--- a/include/ui/console.h
+++ b/include/ui/console.h
@@ -260,6 +260,8 @@ DisplaySurface *qemu_create_displaysurface_guestmem(int width, int height,
pixman_format_code_t format,
int linesize,
uint64_t addr);
+DisplaySurface *qemu_create_message_surface(int w, int h,
+ const char *msg);
PixelFormat qemu_default_pixelformat(int bpp);
DisplaySurface *qemu_create_displaysurface(int width, int height);
diff --git a/include/ui/gtk.h b/include/ui/gtk.h
index 849c896eef..2922fc64b2 100644
--- a/include/ui/gtk.h
+++ b/include/ui/gtk.h
@@ -54,6 +54,9 @@ typedef struct VirtualGfxConsole {
int x, y, w, h;
egl_fb guest_fb;
egl_fb win_fb;
+ egl_fb cursor_fb;
+ int cursor_x;
+ int cursor_y;
bool y0_top;
bool scanout_mode;
#endif
@@ -90,6 +93,8 @@ typedef struct VirtualConsole {
};
} VirtualConsole;
+extern bool gtk_use_gl_area;
+
/* ui/gtk.c */
void gd_update_windowsize(VirtualConsole *vc);
@@ -111,6 +116,15 @@ void gd_egl_scanout_texture(DisplayChangeListener *dcl,
uint32_t backing_height,
uint32_t x, uint32_t y,
uint32_t w, uint32_t h);
+void gd_egl_scanout_dmabuf(DisplayChangeListener *dcl,
+ QemuDmaBuf *dmabuf);
+void gd_egl_cursor_dmabuf(DisplayChangeListener *dcl,
+ QemuDmaBuf *dmabuf, bool have_hot,
+ uint32_t hot_x, uint32_t hot_y);
+void gd_egl_cursor_position(DisplayChangeListener *dcl,
+ uint32_t pos_x, uint32_t pos_y);
+void gd_egl_release_dmabuf(DisplayChangeListener *dcl,
+ QemuDmaBuf *dmabuf);
void gd_egl_scanout_flush(DisplayChangeListener *dcl,
uint32_t x, uint32_t y, uint32_t w, uint32_t h);
void gtk_egl_init(void);
diff --git a/include/ui/spice-display.h b/include/ui/spice-display.h
index 6b5c73b21c..87a84a59d4 100644
--- a/include/ui/spice-display.h
+++ b/include/ui/spice-display.h
@@ -122,6 +122,15 @@ struct SimpleSpiceDisplay {
int gl_updates;
bool have_scanout;
bool have_surface;
+
+ QemuDmaBuf *guest_dmabuf;
+ bool guest_dmabuf_refresh;
+ bool render_cursor;
+
+ egl_fb guest_fb;
+ egl_fb blit_fb;
+ egl_fb cursor_fb;
+ bool have_hot;
#endif
};
diff --git a/linux-user/aarch64/target_syscall.h b/linux-user/aarch64/target_syscall.h
index 604ab99b14..205265e619 100644
--- a/linux-user/aarch64/target_syscall.h
+++ b/linux-user/aarch64/target_syscall.h
@@ -19,4 +19,7 @@ struct target_pt_regs {
#define TARGET_MLOCKALL_MCL_CURRENT 1
#define TARGET_MLOCKALL_MCL_FUTURE 2
+#define TARGET_PR_SVE_SET_VL 50
+#define TARGET_PR_SVE_GET_VL 51
+
#endif /* AARCH64_TARGET_SYSCALL_H */
diff --git a/linux-user/signal.c b/linux-user/signal.c
index 4d3f244612..2ce5d7a3c7 100644
--- a/linux-user/signal.c
+++ b/linux-user/signal.c
@@ -1446,35 +1446,65 @@ struct target_fpsimd_context {
uint64_t vregs[32 * 2]; /* really uint128_t vregs[32] */
};
-/*
- * Auxiliary context saved in the sigcontext.__reserved array. Not exported to
- * user space as it will change with the addition of new context. User space
- * should check the magic/size information.
- */
-struct target_aux_context {
- struct target_fpsimd_context fpsimd;
- /* additional context to be added before "end" */
- struct target_aarch64_ctx end;
+#define TARGET_EXTRA_MAGIC 0x45585401
+
+struct target_extra_context {
+ struct target_aarch64_ctx head;
+ uint64_t datap; /* 16-byte aligned pointer to extra space cast to __u64 */
+ uint32_t size; /* size in bytes of the extra space */
+ uint32_t reserved[3];
+};
+
+#define TARGET_SVE_MAGIC 0x53564501
+
+struct target_sve_context {
+ struct target_aarch64_ctx head;
+ uint16_t vl;
+ uint16_t reserved[3];
+ /* The actual SVE data immediately follows. It is layed out
+ * according to TARGET_SVE_SIG_{Z,P}REG_OFFSET, based off of
+ * the original struct pointer.
+ */
};
+#define TARGET_SVE_VQ_BYTES 16
+
+#define TARGET_SVE_SIG_ZREG_SIZE(VQ) ((VQ) * TARGET_SVE_VQ_BYTES)
+#define TARGET_SVE_SIG_PREG_SIZE(VQ) ((VQ) * (TARGET_SVE_VQ_BYTES / 8))
+
+#define TARGET_SVE_SIG_REGS_OFFSET \
+ QEMU_ALIGN_UP(sizeof(struct target_sve_context), TARGET_SVE_VQ_BYTES)
+#define TARGET_SVE_SIG_ZREG_OFFSET(VQ, N) \
+ (TARGET_SVE_SIG_REGS_OFFSET + TARGET_SVE_SIG_ZREG_SIZE(VQ) * (N))
+#define TARGET_SVE_SIG_PREG_OFFSET(VQ, N) \
+ (TARGET_SVE_SIG_ZREG_OFFSET(VQ, 32) + TARGET_SVE_SIG_PREG_SIZE(VQ) * (N))
+#define TARGET_SVE_SIG_FFR_OFFSET(VQ) \
+ (TARGET_SVE_SIG_PREG_OFFSET(VQ, 16))
+#define TARGET_SVE_SIG_CONTEXT_SIZE(VQ) \
+ (TARGET_SVE_SIG_PREG_OFFSET(VQ, 17))
+
struct target_rt_sigframe {
struct target_siginfo info;
struct target_ucontext uc;
+};
+
+struct target_rt_frame_record {
uint64_t fp;
uint64_t lr;
uint32_t tramp[2];
};
-static int target_setup_sigframe(struct target_rt_sigframe *sf,
- CPUARMState *env, target_sigset_t *set)
+static void target_setup_general_frame(struct target_rt_sigframe *sf,
+ CPUARMState *env, target_sigset_t *set)
{
int i;
- struct target_aux_context *aux =
- (struct target_aux_context *)sf->uc.tuc_mcontext.__reserved;
- /* set up the stack frame for unwinding */
- __put_user(env->xregs[29], &sf->fp);
- __put_user(env->xregs[30], &sf->lr);
+ __put_user(0, &sf->uc.tuc_flags);
+ __put_user(0, &sf->uc.tuc_link);
+
+ __put_user(target_sigaltstack_used.ss_sp, &sf->uc.tuc_stack.ss_sp);
+ __put_user(sas_ss_flags(env->xregs[31]), &sf->uc.tuc_stack.ss_flags);
+ __put_user(target_sigaltstack_used.ss_size, &sf->uc.tuc_stack.ss_size);
for (i = 0; i < 31; i++) {
__put_user(env->xregs[i], &sf->uc.tuc_mcontext.regs[i]);
@@ -1488,39 +1518,79 @@ static int target_setup_sigframe(struct target_rt_sigframe *sf,
for (i = 0; i < TARGET_NSIG_WORDS; i++) {
__put_user(set->sig[i], &sf->uc.tuc_sigmask.sig[i]);
}
+}
+
+static void target_setup_fpsimd_record(struct target_fpsimd_context *fpsimd,
+ CPUARMState *env)
+{
+ int i;
+
+ __put_user(TARGET_FPSIMD_MAGIC, &fpsimd->head.magic);
+ __put_user(sizeof(struct target_fpsimd_context), &fpsimd->head.size);
+ __put_user(vfp_get_fpsr(env), &fpsimd->fpsr);
+ __put_user(vfp_get_fpcr(env), &fpsimd->fpcr);
for (i = 0; i < 32; i++) {
uint64_t *q = aa64_vfp_qreg(env, i);
#ifdef TARGET_WORDS_BIGENDIAN
- __put_user(q[0], &aux->fpsimd.vregs[i * 2 + 1]);
- __put_user(q[1], &aux->fpsimd.vregs[i * 2]);
+ __put_user(q[0], &fpsimd->vregs[i * 2 + 1]);
+ __put_user(q[1], &fpsimd->vregs[i * 2]);
#else
- __put_user(q[0], &aux->fpsimd.vregs[i * 2]);
- __put_user(q[1], &aux->fpsimd.vregs[i * 2 + 1]);
+ __put_user(q[0], &fpsimd->vregs[i * 2]);
+ __put_user(q[1], &fpsimd->vregs[i * 2 + 1]);
#endif
}
- __put_user(vfp_get_fpsr(env), &aux->fpsimd.fpsr);
- __put_user(vfp_get_fpcr(env), &aux->fpsimd.fpcr);
- __put_user(TARGET_FPSIMD_MAGIC, &aux->fpsimd.head.magic);
- __put_user(sizeof(struct target_fpsimd_context),
- &aux->fpsimd.head.size);
+}
- /* set the "end" magic */
- __put_user(0, &aux->end.magic);
- __put_user(0, &aux->end.size);
+static void target_setup_extra_record(struct target_extra_context *extra,
+ uint64_t datap, uint32_t extra_size)
+{
+ __put_user(TARGET_EXTRA_MAGIC, &extra->head.magic);
+ __put_user(sizeof(struct target_extra_context), &extra->head.size);
+ __put_user(datap, &extra->datap);
+ __put_user(extra_size, &extra->size);
+}
- return 0;
+static void target_setup_end_record(struct target_aarch64_ctx *end)
+{
+ __put_user(0, &end->magic);
+ __put_user(0, &end->size);
}
-static int target_restore_sigframe(CPUARMState *env,
- struct target_rt_sigframe *sf)
+static void target_setup_sve_record(struct target_sve_context *sve,
+ CPUARMState *env, int vq, int size)
+{
+ int i, j;
+
+ __put_user(TARGET_SVE_MAGIC, &sve->head.magic);
+ __put_user(size, &sve->head.size);
+ __put_user(vq * TARGET_SVE_VQ_BYTES, &sve->vl);
+
+ /* Note that SVE regs are stored as a byte stream, with each byte element
+ * at a subsequent address. This corresponds to a little-endian store
+ * of our 64-bit hunks.
+ */
+ for (i = 0; i < 32; ++i) {
+ uint64_t *z = (void *)sve + TARGET_SVE_SIG_ZREG_OFFSET(vq, i);
+ for (j = 0; j < vq * 2; ++j) {
+ __put_user_e(env->vfp.zregs[i].d[j], z + j, le);
+ }
+ }
+ for (i = 0; i <= 16; ++i) {
+ uint16_t *p = (void *)sve + TARGET_SVE_SIG_PREG_OFFSET(vq, i);
+ for (j = 0; j < vq; ++j) {
+ uint64_t r = env->vfp.pregs[i].p[j >> 2];
+ __put_user_e(r >> ((j & 3) * 16), p + j, le);
+ }
+ }
+}
+
+static void target_restore_general_frame(CPUARMState *env,
+ struct target_rt_sigframe *sf)
{
sigset_t set;
- int i;
- struct target_aux_context *aux =
- (struct target_aux_context *)sf->uc.tuc_mcontext.__reserved;
- uint32_t magic, size, fpsr, fpcr;
uint64_t pstate;
+ int i;
target_to_host_sigset(&set, &sf->uc.tuc_sigmask);
set_sigmask(&set);
@@ -1533,34 +1603,154 @@ static int target_restore_sigframe(CPUARMState *env,
__get_user(env->pc, &sf->uc.tuc_mcontext.pc);
__get_user(pstate, &sf->uc.tuc_mcontext.pstate);
pstate_write(env, pstate);
+}
- __get_user(magic, &aux->fpsimd.head.magic);
- __get_user(size, &aux->fpsimd.head.size);
+static void target_restore_fpsimd_record(CPUARMState *env,
+ struct target_fpsimd_context *fpsimd)
+{
+ uint32_t fpsr, fpcr;
+ int i;
- if (magic != TARGET_FPSIMD_MAGIC
- || size != sizeof(struct target_fpsimd_context)) {
- return 1;
- }
+ __get_user(fpsr, &fpsimd->fpsr);
+ vfp_set_fpsr(env, fpsr);
+ __get_user(fpcr, &fpsimd->fpcr);
+ vfp_set_fpcr(env, fpcr);
for (i = 0; i < 32; i++) {
uint64_t *q = aa64_vfp_qreg(env, i);
#ifdef TARGET_WORDS_BIGENDIAN
- __get_user(q[0], &aux->fpsimd.vregs[i * 2 + 1]);
- __get_user(q[1], &aux->fpsimd.vregs[i * 2]);
+ __get_user(q[0], &fpsimd->vregs[i * 2 + 1]);
+ __get_user(q[1], &fpsimd->vregs[i * 2]);
#else
- __get_user(q[0], &aux->fpsimd.vregs[i * 2]);
- __get_user(q[1], &aux->fpsimd.vregs[i * 2 + 1]);
+ __get_user(q[0], &fpsimd->vregs[i * 2]);
+ __get_user(q[1], &fpsimd->vregs[i * 2 + 1]);
#endif
}
- __get_user(fpsr, &aux->fpsimd.fpsr);
- vfp_set_fpsr(env, fpsr);
- __get_user(fpcr, &aux->fpsimd.fpcr);
- vfp_set_fpcr(env, fpcr);
+}
- return 0;
+static void target_restore_sve_record(CPUARMState *env,
+ struct target_sve_context *sve, int vq)
+{
+ int i, j;
+
+ /* Note that SVE regs are stored as a byte stream, with each byte element
+ * at a subsequent address. This corresponds to a little-endian load
+ * of our 64-bit hunks.
+ */
+ for (i = 0; i < 32; ++i) {
+ uint64_t *z = (void *)sve + TARGET_SVE_SIG_ZREG_OFFSET(vq, i);
+ for (j = 0; j < vq * 2; ++j) {
+ __get_user_e(env->vfp.zregs[i].d[j], z + j, le);
+ }
+ }
+ for (i = 0; i <= 16; ++i) {
+ uint16_t *p = (void *)sve + TARGET_SVE_SIG_PREG_OFFSET(vq, i);
+ for (j = 0; j < vq; ++j) {
+ uint16_t r;
+ __get_user_e(r, p + j, le);
+ if (j & 3) {
+ env->vfp.pregs[i].p[j >> 2] |= (uint64_t)r << ((j & 3) * 16);
+ } else {
+ env->vfp.pregs[i].p[j >> 2] = r;
+ }
+ }
+ }
+}
+
+static int target_restore_sigframe(CPUARMState *env,
+ struct target_rt_sigframe *sf)
+{
+ struct target_aarch64_ctx *ctx, *extra = NULL;
+ struct target_fpsimd_context *fpsimd = NULL;
+ struct target_sve_context *sve = NULL;
+ uint64_t extra_datap = 0;
+ bool used_extra = false;
+ bool err = false;
+ int vq = 0, sve_size = 0;
+
+ target_restore_general_frame(env, sf);
+
+ ctx = (struct target_aarch64_ctx *)sf->uc.tuc_mcontext.__reserved;
+ while (ctx) {
+ uint32_t magic, size, extra_size;
+
+ __get_user(magic, &ctx->magic);
+ __get_user(size, &ctx->size);
+ switch (magic) {
+ case 0:
+ if (size != 0) {
+ err = true;
+ goto exit;
+ }
+ if (used_extra) {
+ ctx = NULL;
+ } else {
+ ctx = extra;
+ used_extra = true;
+ }
+ continue;
+
+ case TARGET_FPSIMD_MAGIC:
+ if (fpsimd || size != sizeof(struct target_fpsimd_context)) {
+ err = true;
+ goto exit;
+ }
+ fpsimd = (struct target_fpsimd_context *)ctx;
+ break;
+
+ case TARGET_SVE_MAGIC:
+ if (arm_feature(env, ARM_FEATURE_SVE)) {
+ vq = (env->vfp.zcr_el[1] & 0xf) + 1;
+ sve_size = QEMU_ALIGN_UP(TARGET_SVE_SIG_CONTEXT_SIZE(vq), 16);
+ if (!sve && size == sve_size) {
+ sve = (struct target_sve_context *)ctx;
+ break;
+ }
+ }
+ err = true;
+ goto exit;
+
+ case TARGET_EXTRA_MAGIC:
+ if (extra || size != sizeof(struct target_extra_context)) {
+ err = true;
+ goto exit;
+ }
+ __get_user(extra_datap,
+ &((struct target_extra_context *)ctx)->datap);
+ __get_user(extra_size,
+ &((struct target_extra_context *)ctx)->size);
+ extra = lock_user(VERIFY_READ, extra_datap, extra_size, 0);
+ break;
+
+ default:
+ /* Unknown record -- we certainly didn't generate it.
+ * Did we in fact get out of sync?
+ */
+ err = true;
+ goto exit;
+ }
+ ctx = (void *)ctx + size;
+ }
+
+ /* Require FPSIMD always. */
+ if (fpsimd) {
+ target_restore_fpsimd_record(env, fpsimd);
+ } else {
+ err = true;
+ }
+
+ /* SVE data, if present, overwrites FPSIMD data. */
+ if (sve) {
+ target_restore_sve_record(env, sve, vq);
+ }
+
+ exit:
+ unlock_user(extra, extra_datap, 0);
+ return err;
}
-static abi_ulong get_sigframe(struct target_sigaction *ka, CPUARMState *env)
+static abi_ulong get_sigframe(struct target_sigaction *ka,
+ CPUARMState *env, int size)
{
abi_ulong sp;
@@ -1573,34 +1763,120 @@ static abi_ulong get_sigframe(struct target_sigaction *ka, CPUARMState *env)
sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size;
}
- sp = (sp - sizeof(struct target_rt_sigframe)) & ~15;
+ sp = (sp - size) & ~15;
return sp;
}
+typedef struct {
+ int total_size;
+ int extra_base;
+ int extra_size;
+ int std_end_ofs;
+ int extra_ofs;
+ int extra_end_ofs;
+} target_sigframe_layout;
+
+static int alloc_sigframe_space(int this_size, target_sigframe_layout *l)
+{
+ /* Make sure there will always be space for the end marker. */
+ const int std_size = sizeof(struct target_rt_sigframe)
+ - sizeof(struct target_aarch64_ctx);
+ int this_loc = l->total_size;
+
+ if (l->extra_base) {
+ /* Once we have begun an extra space, all allocations go there. */
+ l->extra_size += this_size;
+ } else if (this_size + this_loc > std_size) {
+ /* This allocation does not fit in the standard space. */
+ /* Allocate the extra record. */
+ l->extra_ofs = this_loc;
+ l->total_size += sizeof(struct target_extra_context);
+
+ /* Allocate the standard end record. */
+ l->std_end_ofs = l->total_size;
+ l->total_size += sizeof(struct target_aarch64_ctx);
+
+ /* Allocate the requested record. */
+ l->extra_base = this_loc = l->total_size;
+ l->extra_size = this_size;
+ }
+ l->total_size += this_size;
+
+ return this_loc;
+}
+
static void target_setup_frame(int usig, struct target_sigaction *ka,
target_siginfo_t *info, target_sigset_t *set,
CPUARMState *env)
{
+ target_sigframe_layout layout = {
+ /* Begin with the size pointing to the reserved space. */
+ .total_size = offsetof(struct target_rt_sigframe,
+ uc.tuc_mcontext.__reserved),
+ };
+ int fpsimd_ofs, fr_ofs, sve_ofs = 0, vq = 0, sve_size = 0;
struct target_rt_sigframe *frame;
+ struct target_rt_frame_record *fr;
abi_ulong frame_addr, return_addr;
- frame_addr = get_sigframe(ka, env);
+ /* FPSIMD record is always in the standard space. */
+ fpsimd_ofs = alloc_sigframe_space(sizeof(struct target_fpsimd_context),
+ &layout);
+
+ /* SVE state needs saving only if it exists. */
+ if (arm_feature(env, ARM_FEATURE_SVE)) {
+ vq = (env->vfp.zcr_el[1] & 0xf) + 1;
+ sve_size = QEMU_ALIGN_UP(TARGET_SVE_SIG_CONTEXT_SIZE(vq), 16);
+ sve_ofs = alloc_sigframe_space(sve_size, &layout);
+ }
+
+ if (layout.extra_ofs) {
+ /* Reserve space for the extra end marker. The standard end marker
+ * will have been allocated when we allocated the extra record.
+ */
+ layout.extra_end_ofs
+ = alloc_sigframe_space(sizeof(struct target_aarch64_ctx), &layout);
+ } else {
+ /* Reserve space for the standard end marker.
+ * Do not use alloc_sigframe_space because we cheat
+ * std_size therein to reserve space for this.
+ */
+ layout.std_end_ofs = layout.total_size;
+ layout.total_size += sizeof(struct target_aarch64_ctx);
+ }
+
+ /* Reserve space for the return code. On a real system this would
+ * be within the VDSO. So, despite the name this is not a "real"
+ * record within the frame.
+ */
+ fr_ofs = layout.total_size;
+ layout.total_size += sizeof(struct target_rt_frame_record);
+
+ frame_addr = get_sigframe(ka, env, layout.total_size);
trace_user_setup_frame(env, frame_addr);
if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) {
goto give_sigsegv;
}
- __put_user(0, &frame->uc.tuc_flags);
- __put_user(0, &frame->uc.tuc_link);
+ target_setup_general_frame(frame, env, set);
+ target_setup_fpsimd_record((void *)frame + fpsimd_ofs, env);
+ target_setup_end_record((void *)frame + layout.std_end_ofs);
+ if (layout.extra_ofs) {
+ target_setup_extra_record((void *)frame + layout.extra_ofs,
+ frame_addr + layout.extra_base,
+ layout.extra_size);
+ target_setup_end_record((void *)frame + layout.extra_end_ofs);
+ }
+ if (sve_ofs) {
+ target_setup_sve_record((void *)frame + sve_ofs, env, vq, sve_size);
+ }
+
+ /* Set up the stack frame for unwinding. */
+ fr = (void *)frame + fr_ofs;
+ __put_user(env->xregs[29], &fr->fp);
+ __put_user(env->xregs[30], &fr->lr);
- __put_user(target_sigaltstack_used.ss_sp,
- &frame->uc.tuc_stack.ss_sp);
- __put_user(sas_ss_flags(env->xregs[31]),
- &frame->uc.tuc_stack.ss_flags);
- __put_user(target_sigaltstack_used.ss_size,
- &frame->uc.tuc_stack.ss_size);
- target_setup_sigframe(frame, env, set);
if (ka->sa_flags & TARGET_SA_RESTORER) {
return_addr = ka->sa_restorer;
} else {
@@ -1609,13 +1885,14 @@ static void target_setup_frame(int usig, struct target_sigaction *ka,
* Since these are instructions they need to be put as little-endian
* regardless of target default or current CPU endianness.
*/
- __put_user_e(0xd2801168, &frame->tramp[0], le);
- __put_user_e(0xd4000001, &frame->tramp[1], le);
- return_addr = frame_addr + offsetof(struct target_rt_sigframe, tramp);
+ __put_user_e(0xd2801168, &fr->tramp[0], le);
+ __put_user_e(0xd4000001, &fr->tramp[1], le);
+ return_addr = frame_addr + fr_ofs
+ + offsetof(struct target_rt_frame_record, tramp);
}
env->xregs[0] = usig;
env->xregs[31] = frame_addr;
- env->xregs[29] = env->xregs[31] + offsetof(struct target_rt_sigframe, fp);
+ env->xregs[29] = frame_addr + fr_ofs;
env->pc = ka->_sa_handler;
env->xregs[30] = return_addr;
if (info) {
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index a8abfd421d..b4f7b14fbe 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -10672,6 +10672,33 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
break;
}
#endif
+#ifdef TARGET_AARCH64
+ case TARGET_PR_SVE_SET_VL:
+ /* We cannot support either PR_SVE_SET_VL_ONEXEC
+ or PR_SVE_VL_INHERIT. Therefore, anything above
+ ARM_MAX_VQ results in EINVAL. */
+ ret = -TARGET_EINVAL;
+ if (arm_feature(cpu_env, ARM_FEATURE_SVE)
+ && arg2 >= 0 && arg2 <= ARM_MAX_VQ * 16 && !(arg2 & 15)) {
+ CPUARMState *env = cpu_env;
+ int old_vq = (env->vfp.zcr_el[1] & 0xf) + 1;
+ int vq = MAX(arg2 / 16, 1);
+
+ if (vq < old_vq) {
+ aarch64_sve_narrow_vq(env, vq);
+ }
+ env->vfp.zcr_el[1] = vq - 1;
+ ret = vq * 16;
+ }
+ break;
+ case TARGET_PR_SVE_GET_VL:
+ ret = -TARGET_EINVAL;
+ if (arm_feature(cpu_env, ARM_FEATURE_SVE)) {
+ CPUARMState *env = cpu_env;
+ ret = ((env->vfp.zcr_el[1] & 0xf) + 1) * 16;
+ }
+ break;
+#endif /* AARCH64 */
case PR_GET_SECCOMP:
case PR_SET_SECCOMP:
/* Disable seccomp to prevent the target disabling syscalls we
diff --git a/migration/block.c b/migration/block.c
index 1f03946797..41b95d1dd8 100644
--- a/migration/block.c
+++ b/migration/block.c
@@ -36,7 +36,7 @@
#define MAX_IS_ALLOCATED_SEARCH (65536 * BDRV_SECTOR_SIZE)
-#define MAX_INFLIGHT_IO 512
+#define MAX_IO_BUFFERS 512
//#define DEBUG_BLK_MIGRATION
@@ -331,11 +331,10 @@ static int mig_save_device_bulk(QEMUFile *f, BlkMigDevState *bmds)
*/
qemu_mutex_lock_iothread();
aio_context_acquire(blk_get_aio_context(bmds->blk));
- blk->aiocb = blk_aio_preadv(bb, cur_sector * BDRV_SECTOR_SIZE, &blk->qiov,
- 0, blk_mig_read_cb, blk);
-
bdrv_reset_dirty_bitmap(bmds->dirty_bitmap, cur_sector * BDRV_SECTOR_SIZE,
nr_sectors * BDRV_SECTOR_SIZE);
+ blk->aiocb = blk_aio_preadv(bb, cur_sector * BDRV_SECTOR_SIZE, &blk->qiov,
+ 0, blk_mig_read_cb, blk);
aio_context_release(blk_get_aio_context(bmds->blk));
qemu_mutex_unlock_iothread();
@@ -776,9 +775,8 @@ static int block_save_iterate(QEMUFile *f, void *opaque)
while ((block_mig_state.submitted +
block_mig_state.read_done) * BLOCK_SIZE <
qemu_file_get_rate_limit(f) &&
- (block_mig_state.submitted +
- block_mig_state.read_done) <
- MAX_INFLIGHT_IO) {
+ (block_mig_state.submitted + block_mig_state.read_done) <
+ MAX_IO_BUFFERS) {
blk_mig_unlock();
if (block_mig_state.bulk_completed == 0) {
/* first finish the bulk phase */
diff --git a/migration/migration.c b/migration/migration.c
index e345d0cc7e..6a4780ef6f 100644
--- a/migration/migration.c
+++ b/migration/migration.c
@@ -747,13 +747,15 @@ void qmp_migrate_set_capabilities(MigrationCapabilityStatusList *params,
{
MigrationState *s = migrate_get_current();
MigrationCapabilityStatusList *cap;
+ bool cap_list[MIGRATION_CAPABILITY__MAX];
if (migration_is_setup_or_active(s->state)) {
error_setg(errp, QERR_MIGRATION_ACTIVE);
return;
}
- if (!migrate_caps_check(s->enabled_capabilities, params, errp)) {
+ memcpy(cap_list, s->enabled_capabilities, sizeof(cap_list));
+ if (!migrate_caps_check(cap_list, params, errp)) {
return;
}
@@ -2541,6 +2543,7 @@ static void migration_instance_finalize(Object *obj)
g_free(params->tls_hostname);
g_free(params->tls_creds);
qemu_sem_destroy(&ms->pause_sem);
+ error_free(ms->error);
}
static void migration_instance_init(Object *obj)
diff --git a/migration/ram.c b/migration/ram.c
index 3b6c077964..7266351fd0 100644
--- a/migration/ram.c
+++ b/migration/ram.c
@@ -2258,6 +2258,13 @@ static int ram_save_iterate(QEMUFile *f, void *opaque)
int64_t t0;
int done = 0;
+ if (blk_mig_bulk_active()) {
+ /* Avoid transferring ram during bulk phase of block migration as
+ * the bulk phase will usually take a long time and transferring
+ * ram updates during that time is pointless. */
+ goto out;
+ }
+
rcu_read_lock();
if (ram_list.version != rs->last_version) {
ram_state_reset(rs);
@@ -2304,6 +2311,7 @@ static int ram_save_iterate(QEMUFile *f, void *opaque)
*/
ram_control_after_iterate(f, RAM_CONTROL_ROUND);
+out:
qemu_put_be64(f, RAM_SAVE_FLAG_EOS);
ram_counters.transferred += 8;
diff --git a/qapi/block-core.json b/qapi/block-core.json
index 00475f08d4..524d51567a 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -2553,6 +2553,63 @@
'*encrypt': 'BlockdevQcow2Encryption' } }
##
+# @SshHostKeyCheckMode:
+#
+# @none Don't check the host key at all
+# @hash Compare the host key with a given hash
+# @known_hosts Check the host key against the known_hosts file
+#
+# Since: 2.12
+##
+{ 'enum': 'SshHostKeyCheckMode',
+ 'data': [ 'none', 'hash', 'known_hosts' ] }
+
+##
+# @SshHostKeyCheckHashType:
+#
+# @md5 The given hash is an md5 hash
+# @sha1 The given hash is an sha1 hash
+#
+# Since: 2.12
+##
+{ 'enum': 'SshHostKeyCheckHashType',
+ 'data': [ 'md5', 'sha1' ] }
+
+##
+# @SshHostKeyHash:
+#
+# @type The hash algorithm used for the hash
+# @hash The expected hash value
+#
+# Since: 2.12
+##
+{ 'struct': 'SshHostKeyHash',
+ 'data': { 'type': 'SshHostKeyCheckHashType',
+ 'hash': 'str' }}
+
+##
+# @SshHostKeyDummy:
+#
+# For those union branches that don't need additional fields.
+#
+# Since: 2.12
+##
+{ 'struct': 'SshHostKeyDummy',
+ 'data': {} }
+
+##
+# @SshHostKeyCheck:
+#
+# Since: 2.12
+##
+{ 'union': 'SshHostKeyCheck',
+ 'base': { 'mode': 'SshHostKeyCheckMode' },
+ 'discriminator': 'mode',
+ 'data': { 'none': 'SshHostKeyDummy',
+ 'hash': 'SshHostKeyHash',
+ 'known_hosts': 'SshHostKeyDummy' } }
+
+##
# @BlockdevOptionsSsh:
#
# @server: host address
@@ -2562,14 +2619,16 @@
# @user: user as which to connect, defaults to current
# local user name
#
-# TODO: Expose the host_key_check option in QMP
+# @host-key-check: Defines how and what to check the host key against
+# (default: known_hosts)
#
# Since: 2.9
##
{ 'struct': 'BlockdevOptionsSsh',
'data': { 'server': 'InetSocketAddress',
'path': 'str',
- '*user': 'str' } }
+ '*user': 'str',
+ '*host-key-check': 'SshHostKeyCheck' } }
##
@@ -3359,6 +3418,269 @@
{ 'command': 'blockdev-del', 'data': { 'node-name': 'str' } }
##
+# @BlockdevCreateOptionsFile:
+#
+# Driver specific image creation options for file.
+#
+# @filename Filename for the new image file
+# @size Size of the virtual disk in bytes
+# @preallocation Preallocation mode for the new image (default: off)
+# @nocow Turn off copy-on-write (valid only on btrfs; default: off)
+#
+# Since: 2.12
+##
+{ 'struct': 'BlockdevCreateOptionsFile',
+ 'data': { 'filename': 'str',
+ 'size': 'size',
+ '*preallocation': 'PreallocMode',
+ '*nocow': 'bool' } }
+
+##
+# @BlockdevCreateOptionsGluster:
+#
+# Driver specific image creation options for gluster.
+#
+# @location Where to store the new image file
+# @size Size of the virtual disk in bytes
+# @preallocation Preallocation mode for the new image (default: off)
+#
+# Since: 2.12
+##
+{ 'struct': 'BlockdevCreateOptionsGluster',
+ 'data': { 'location': 'BlockdevOptionsGluster',
+ 'size': 'size',
+ '*preallocation': 'PreallocMode' } }
+
+##
+# @BlockdevCreateOptionsNfs:
+#
+# Driver specific image creation options for NFS.
+#
+# @location Where to store the new image file
+# @size Size of the virtual disk in bytes
+#
+# Since: 2.12
+##
+{ 'struct': 'BlockdevCreateOptionsNfs',
+ 'data': { 'location': 'BlockdevOptionsNfs',
+ 'size': 'size' } }
+
+##
+# @BlockdevQcow2Version:
+#
+# @v2: The original QCOW2 format as introduced in qemu 0.10 (version 2)
+# @v3: The extended QCOW2 format as introduced in qemu 1.1 (version 3)
+#
+# Since: 2.12
+##
+{ 'enum': 'BlockdevQcow2Version',
+ 'data': [ 'v2', 'v3' ] }
+
+
+##
+# @BlockdevCreateOptionsQcow2:
+#
+# Driver specific image creation options for qcow2.
+#
+# @file Node to create the image format on
+# @size Size of the virtual disk in bytes
+# @version Compatibility level (default: v3)
+# @backing-file File name of the backing file if a backing file
+# should be used
+# @backing-fmt Name of the block driver to use for the backing file
+# @encrypt Encryption options if the image should be encrypted
+# @cluster-size qcow2 cluster size in bytes (default: 65536)
+# @preallocation Preallocation mode for the new image (default: off)
+# @lazy-refcounts True if refcounts may be updated lazily (default: off)
+# @refcount-bits Width of reference counts in bits (default: 16)
+#
+# Since: 2.12
+##
+{ 'struct': 'BlockdevCreateOptionsQcow2',
+ 'data': { 'file': 'BlockdevRef',
+ 'size': 'size',
+ '*version': 'BlockdevQcow2Version',
+ '*backing-file': 'str',
+ '*backing-fmt': 'BlockdevDriver',
+ '*encrypt': 'QCryptoBlockCreateOptions',
+ '*cluster-size': 'size',
+ '*preallocation': 'PreallocMode',
+ '*lazy-refcounts': 'bool',
+ '*refcount-bits': 'int' } }
+
+##
+# @BlockdevCreateOptionsRbd:
+#
+# Driver specific image creation options for rbd/Ceph.
+#
+# @location Where to store the new image file. This location cannot
+# point to a snapshot.
+# @size Size of the virtual disk in bytes
+# @cluster-size RBD object size
+#
+# Since: 2.12
+##
+{ 'struct': 'BlockdevCreateOptionsRbd',
+ 'data': { 'location': 'BlockdevOptionsRbd',
+ 'size': 'size',
+ '*cluster-size' : 'size' } }
+
+##
+# @SheepdogRedundancyType:
+#
+# @full Create a fully replicated vdi with x copies
+# @erasure-coded Create an erasure coded vdi with x data strips and
+# y parity strips
+#
+# Since: 2.12
+##
+{ 'enum': 'SheepdogRedundancyType',
+ 'data': [ 'full', 'erasure-coded' ] }
+
+##
+# @SheepdogRedundancyFull:
+#
+# @copies Number of copies to use (between 1 and 31)
+#
+# Since: 2.12
+##
+{ 'struct': 'SheepdogRedundancyFull',
+ 'data': { 'copies': 'int' }}
+
+##
+# @SheepdogRedundancyErasureCoded:
+#
+# @data-strips Number of data strips to use (one of {2,4,8,16})
+# @parity-strips Number of parity strips to use (between 1 and 15)
+#
+# Since: 2.12
+##
+{ 'struct': 'SheepdogRedundancyErasureCoded',
+ 'data': { 'data-strips': 'int',
+ 'parity-strips': 'int' }}
+
+##
+# @SheepdogRedundancy:
+#
+# Since: 2.12
+##
+{ 'union': 'SheepdogRedundancy',
+ 'base': { 'type': 'SheepdogRedundancyType' },
+ 'discriminator': 'type',
+ 'data': { 'full': 'SheepdogRedundancyFull',
+ 'erasure-coded': 'SheepdogRedundancyErasureCoded' } }
+
+##
+# @BlockdevCreateOptionsSheepdog:
+#
+# Driver specific image creation options for Sheepdog.
+#
+# @location Where to store the new image file
+# @size Size of the virtual disk in bytes
+# @backing-file File name of a base image
+# @preallocation Preallocation mode (allowed values: off, full)
+# @redundancy Redundancy of the image
+# @object-size Object size of the image
+#
+# Since: 2.12
+##
+{ 'struct': 'BlockdevCreateOptionsSheepdog',
+ 'data': { 'location': 'BlockdevOptionsSheepdog',
+ 'size': 'size',
+ '*backing-file': 'str',
+ '*preallocation': 'PreallocMode',
+ '*redundancy': 'SheepdogRedundancy',
+ '*object-size': 'size' } }
+
+##
+# @BlockdevCreateOptionsSsh:
+#
+# Driver specific image creation options for SSH.
+#
+# @location Where to store the new image file
+# @size Size of the virtual disk in bytes
+#
+# Since: 2.12
+##
+{ 'struct': 'BlockdevCreateOptionsSsh',
+ 'data': { 'location': 'BlockdevOptionsSsh',
+ 'size': 'size' } }
+
+##
+# @BlockdevCreateNotSupported:
+#
+# This is used for all drivers that don't support creating images.
+#
+# Since: 2.12
+##
+{ 'struct': 'BlockdevCreateNotSupported', 'data': {}}
+
+##
+# @BlockdevCreateOptions:
+#
+# Options for creating an image format on a given node.
+#
+# @driver block driver to create the image format
+#
+# Since: 2.12
+##
+{ 'union': 'BlockdevCreateOptions',
+ 'base': {
+ 'driver': 'BlockdevDriver' },
+ 'discriminator': 'driver',
+ 'data': {
+ 'blkdebug': 'BlockdevCreateNotSupported',
+ 'blkverify': 'BlockdevCreateNotSupported',
+ 'bochs': 'BlockdevCreateNotSupported',
+ 'cloop': 'BlockdevCreateNotSupported',
+ 'dmg': 'BlockdevCreateNotSupported',
+ 'file': 'BlockdevCreateOptionsFile',
+ 'ftp': 'BlockdevCreateNotSupported',
+ 'ftps': 'BlockdevCreateNotSupported',
+ 'gluster': 'BlockdevCreateOptionsGluster',
+ 'host_cdrom': 'BlockdevCreateNotSupported',
+ 'host_device': 'BlockdevCreateNotSupported',
+ 'http': 'BlockdevCreateNotSupported',
+ 'https': 'BlockdevCreateNotSupported',
+ 'iscsi': 'BlockdevCreateNotSupported',
+ 'luks': 'BlockdevCreateNotSupported',
+ 'nbd': 'BlockdevCreateNotSupported',
+ 'nfs': 'BlockdevCreateOptionsNfs',
+ 'null-aio': 'BlockdevCreateNotSupported',
+ 'null-co': 'BlockdevCreateNotSupported',
+ 'nvme': 'BlockdevCreateNotSupported',
+ 'parallels': 'BlockdevCreateNotSupported',
+ 'qcow2': 'BlockdevCreateOptionsQcow2',
+ 'qcow': 'BlockdevCreateNotSupported',
+ 'qed': 'BlockdevCreateNotSupported',
+ 'quorum': 'BlockdevCreateNotSupported',
+ 'raw': 'BlockdevCreateNotSupported',
+ 'rbd': 'BlockdevCreateOptionsRbd',
+ 'replication': 'BlockdevCreateNotSupported',
+ 'sheepdog': 'BlockdevCreateOptionsSheepdog',
+ 'ssh': 'BlockdevCreateOptionsSsh',
+ 'throttle': 'BlockdevCreateNotSupported',
+ 'vdi': 'BlockdevCreateNotSupported',
+ 'vhdx': 'BlockdevCreateNotSupported',
+ 'vmdk': 'BlockdevCreateNotSupported',
+ 'vpc': 'BlockdevCreateNotSupported',
+ 'vvfat': 'BlockdevCreateNotSupported',
+ 'vxhs': 'BlockdevCreateNotSupported'
+ } }
+
+##
+# @x-blockdev-create:
+#
+# Create an image format on a given node.
+# TODO Replace with something asynchronous (block job?)
+#
+# Since: 2.12
+##
+{ 'command': 'x-blockdev-create',
+ 'data': 'BlockdevCreateOptions',
+ 'boxed': true }
+
+##
# @blockdev-open-tray:
#
# Opens a block device's tray. If there is a block driver state tree inserted as
diff --git a/qapi/ui.json b/qapi/ui.json
index 3e82f25ac5..5d01ad4304 100644
--- a/qapi/ui.json
+++ b/qapi/ui.json
@@ -77,6 +77,13 @@
#
# @filename: the path of a new PPM file to store the image
#
+# @device: ID of the display device that should be dumped. If this parameter
+# is missing, the primary display will be used. (Since 2.12)
+#
+# @head: head to use in case the device supports multiple heads. If this
+# parameter is missing, head #0 will be used. Also note that the head
+# can only be specified in conjunction with the device ID. (Since 2.12)
+#
# Returns: Nothing on success
#
# Since: 0.14.0
@@ -88,7 +95,8 @@
# <- { "return": {} }
#
##
-{ 'command': 'screendump', 'data': {'filename': 'str'} }
+{ 'command': 'screendump',
+ 'data': {'filename': 'str', '*device': 'str', '*head': 'int'} }
##
# == Spice
diff --git a/qobject/qdict.c b/qobject/qdict.c
index 23df84f9cd..229b8c840b 100644
--- a/qobject/qdict.c
+++ b/qobject/qdict.c
@@ -1072,3 +1072,37 @@ void qdict_join(QDict *dest, QDict *src, bool overwrite)
entry = next;
}
}
+
+/**
+ * qdict_rename_keys(): Rename keys in qdict according to the replacements
+ * specified in the array renames. The array must be terminated by an entry
+ * with from = NULL.
+ *
+ * The renames are performed individually in the order of the array, so entries
+ * may be renamed multiple times and may or may not conflict depending on the
+ * order of the renames array.
+ *
+ * Returns true for success, false in error cases.
+ */
+bool qdict_rename_keys(QDict *qdict, const QDictRenames *renames, Error **errp)
+{
+ QObject *qobj;
+
+ while (renames->from) {
+ if (qdict_haskey(qdict, renames->from)) {
+ if (qdict_haskey(qdict, renames->to)) {
+ error_setg(errp, "'%s' and its alias '%s' can't be used at the "
+ "same time", renames->to, renames->from);
+ return false;
+ }
+
+ qobj = qdict_get(qdict, renames->from);
+ qobject_incref(qobj);
+ qdict_put_obj(qdict, renames->to, qobj);
+ qdict_del(qdict, renames->from);
+ }
+
+ renames++;
+ }
+ return true;
+}
diff --git a/target/arm/cpu-qom.h b/target/arm/cpu-qom.h
index a42495bac9..d135ff8e06 100644
--- a/target/arm/cpu-qom.h
+++ b/target/arm/cpu-qom.h
@@ -33,6 +33,8 @@ struct arm_boot_info;
#define ARM_CPU_GET_CLASS(obj) \
OBJECT_GET_CLASS(ARMCPUClass, (obj), TYPE_ARM_CPU)
+#define TYPE_ARM_MAX_CPU "max-" TYPE_ARM_CPU
+
/**
* ARMCPUClass:
* @parent_realize: The parent class' realize handler.
diff --git a/target/arm/cpu.c b/target/arm/cpu.c
index 6b77aaa445..022d8c5787 100644
--- a/target/arm/cpu.c
+++ b/target/arm/cpu.c
@@ -725,6 +725,19 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp)
int pagebits;
Error *local_err = NULL;
+ /* If we needed to query the host kernel for the CPU features
+ * then it's possible that might have failed in the initfn, but
+ * this is the first point where we can report it.
+ */
+ if (cpu->host_cpu_probe_failed) {
+ if (!kvm_enabled()) {
+ error_setg(errp, "The 'host' CPU type can only be used with KVM");
+ } else {
+ error_setg(errp, "Failed to retrieve host CPU features");
+ }
+ return;
+ }
+
cpu_exec_realizefn(cs, &local_err);
if (local_err != NULL) {
error_propagate(errp, local_err);
@@ -939,6 +952,11 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp)
cs->num_ases = 1;
}
cpu_address_space_init(cs, ARMASIdx_NS, "cpu-memory", cs->memory);
+
+ /* No core_count specified, default to smp_cpus. */
+ if (cpu->core_count == -1) {
+ cpu->core_count = smp_cpus;
+ }
#endif
qemu_init_vcpu(cs);
@@ -952,9 +970,19 @@ static ObjectClass *arm_cpu_class_by_name(const char *cpu_model)
ObjectClass *oc;
char *typename;
char **cpuname;
+ const char *cpunamestr;
cpuname = g_strsplit(cpu_model, ",", 1);
- typename = g_strdup_printf(ARM_CPU_TYPE_NAME("%s"), cpuname[0]);
+ cpunamestr = cpuname[0];
+#ifdef CONFIG_USER_ONLY
+ /* For backwards compatibility usermode emulation allows "-cpu any",
+ * which has the same semantics as "-cpu max".
+ */
+ if (!strcmp(cpunamestr, "any")) {
+ cpunamestr = "max";
+ }
+#endif
+ typename = g_strdup_printf(ARM_CPU_TYPE_NAME("%s"), cpunamestr);
oc = object_class_by_name(typename);
g_strfreev(cpuname);
g_free(typename);
@@ -1684,22 +1712,37 @@ static void pxa270c5_initfn(Object *obj)
cpu->reset_sctlr = 0x00000078;
}
-#ifdef CONFIG_USER_ONLY
-static void arm_any_initfn(Object *obj)
+#ifndef TARGET_AARCH64
+/* -cpu max: if KVM is enabled, like -cpu host (best possible with this host);
+ * otherwise, a CPU with as many features enabled as our emulation supports.
+ * The version of '-cpu max' for qemu-system-aarch64 is defined in cpu64.c;
+ * this only needs to handle 32 bits.
+ */
+static void arm_max_initfn(Object *obj)
{
ARMCPU *cpu = ARM_CPU(obj);
- set_feature(&cpu->env, ARM_FEATURE_V8);
- set_feature(&cpu->env, ARM_FEATURE_VFP4);
- set_feature(&cpu->env, ARM_FEATURE_NEON);
- set_feature(&cpu->env, ARM_FEATURE_THUMB2EE);
- set_feature(&cpu->env, ARM_FEATURE_V8_AES);
- set_feature(&cpu->env, ARM_FEATURE_V8_SHA1);
- set_feature(&cpu->env, ARM_FEATURE_V8_SHA256);
- set_feature(&cpu->env, ARM_FEATURE_V8_PMULL);
- set_feature(&cpu->env, ARM_FEATURE_CRC);
- set_feature(&cpu->env, ARM_FEATURE_V8_RDM);
- set_feature(&cpu->env, ARM_FEATURE_V8_FCMA);
- cpu->midr = 0xffffffff;
+
+ if (kvm_enabled()) {
+ kvm_arm_set_cpu_features_from_host(cpu);
+ } else {
+ cortex_a15_initfn(obj);
+#ifdef CONFIG_USER_ONLY
+ /* We don't set these in system emulation mode for the moment,
+ * since we don't correctly set the ID registers to advertise them,
+ */
+ set_feature(&cpu->env, ARM_FEATURE_V8);
+ set_feature(&cpu->env, ARM_FEATURE_VFP4);
+ set_feature(&cpu->env, ARM_FEATURE_NEON);
+ set_feature(&cpu->env, ARM_FEATURE_THUMB2EE);
+ set_feature(&cpu->env, ARM_FEATURE_V8_AES);
+ set_feature(&cpu->env, ARM_FEATURE_V8_SHA1);
+ set_feature(&cpu->env, ARM_FEATURE_V8_SHA256);
+ set_feature(&cpu->env, ARM_FEATURE_V8_PMULL);
+ set_feature(&cpu->env, ARM_FEATURE_CRC);
+ set_feature(&cpu->env, ARM_FEATURE_V8_RDM);
+ set_feature(&cpu->env, ARM_FEATURE_V8_FCMA);
+#endif
+ }
}
#endif
@@ -1751,8 +1794,11 @@ static const ARMCPUInfo arm_cpus[] = {
{ .name = "pxa270-b1", .initfn = pxa270b1_initfn },
{ .name = "pxa270-c0", .initfn = pxa270c0_initfn },
{ .name = "pxa270-c5", .initfn = pxa270c5_initfn },
+#ifndef TARGET_AARCH64
+ { .name = "max", .initfn = arm_max_initfn },
+#endif
#ifdef CONFIG_USER_ONLY
- { .name = "any", .initfn = arm_any_initfn },
+ { .name = "any", .initfn = arm_max_initfn },
#endif
#endif
{ .name = NULL }
@@ -1765,6 +1811,7 @@ static Property arm_cpu_properties[] = {
DEFINE_PROP_UINT64("mp-affinity", ARMCPU,
mp_affinity, ARM64_AFFINITY_INVALID),
DEFINE_PROP_INT32("node-id", ARMCPU, node_id, CPU_UNSET_NUMA_NODE_ID),
+ DEFINE_PROP_INT32("core-count", ARMCPU, core_count, -1),
DEFINE_PROP_END_OF_LIST()
};
@@ -1845,6 +1892,26 @@ static void arm_cpu_class_init(ObjectClass *oc, void *data)
#endif
}
+#ifdef CONFIG_KVM
+static void arm_host_initfn(Object *obj)
+{
+ ARMCPU *cpu = ARM_CPU(obj);
+
+ kvm_arm_set_cpu_features_from_host(cpu);
+}
+
+static const TypeInfo host_arm_cpu_type_info = {
+ .name = TYPE_ARM_HOST_CPU,
+#ifdef TARGET_AARCH64
+ .parent = TYPE_AARCH64_CPU,
+#else
+ .parent = TYPE_ARM_CPU,
+#endif
+ .instance_init = arm_host_initfn,
+};
+
+#endif
+
static void cpu_register(const ARMCPUInfo *info)
{
TypeInfo type_info = {
@@ -1889,6 +1956,10 @@ static void arm_cpu_register_types(void)
cpu_register(info);
info++;
}
+
+#ifdef CONFIG_KVM
+ type_register_static(&host_arm_cpu_type_info);
+#endif
}
type_init(arm_cpu_register_types)
diff --git a/target/arm/cpu.h b/target/arm/cpu.h
index 8dd6b788df..1e7e1f8a7e 100644
--- a/target/arm/cpu.h
+++ b/target/arm/cpu.h
@@ -745,6 +745,16 @@ struct ARMCPU {
/* Uniprocessor system with MP extensions */
bool mp_is_up;
+ /* True if we tried kvm_arm_host_cpu_features() during CPU instance_init
+ * and the probe failed (so we need to report the error in realize)
+ */
+ bool host_cpu_probe_failed;
+
+ /* Specify the number of cores in this CPU cluster. Used for the L2CTLR
+ * register.
+ */
+ int32_t core_count;
+
/* The instance init functions for implementation-specific subclasses
* set these fields to specify the implementation-dependent values of
* various constant registers and reset values of non-constant
@@ -861,6 +871,7 @@ int arm_cpu_write_elf32_note(WriteCoreDumpFunction f, CPUState *cs,
#ifdef TARGET_AARCH64
int aarch64_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg);
int aarch64_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg);
+void aarch64_sve_narrow_vq(CPUARMState *env, unsigned vq);
#endif
target_ulong do_arm_semihosting(CPUARMState *env);
diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c
index 4228713b19..991d764674 100644
--- a/target/arm/cpu64.c
+++ b/target/arm/cpu64.c
@@ -28,6 +28,7 @@
#include "hw/arm/arm.h"
#include "sysemu/sysemu.h"
#include "sysemu/kvm.h"
+#include "kvm_arm.h"
static inline void set_feature(CPUARMState *env, int feature)
{
@@ -42,8 +43,10 @@ static inline void unset_feature(CPUARMState *env, int feature)
#ifndef CONFIG_USER_ONLY
static uint64_t a57_a53_l2ctlr_read(CPUARMState *env, const ARMCPRegInfo *ri)
{
- /* Number of processors is in [25:24]; otherwise we RAZ */
- return (smp_cpus - 1) << 24;
+ ARMCPU *cpu = arm_env_get_cpu(env);
+
+ /* Number of cores is in [25:24]; otherwise we RAZ */
+ return (cpu->core_count - 1) << 24;
}
#endif
@@ -212,31 +215,50 @@ static void aarch64_a53_initfn(Object *obj)
define_arm_cp_regs(cpu, cortex_a57_a53_cp_reginfo);
}
-#ifdef CONFIG_USER_ONLY
-static void aarch64_any_initfn(Object *obj)
+/* -cpu max: if KVM is enabled, like -cpu host (best possible with this host);
+ * otherwise, a CPU with as many features enabled as our emulation supports.
+ * The version of '-cpu max' for qemu-system-arm is defined in cpu.c;
+ * this only needs to handle 64 bits.
+ */
+static void aarch64_max_initfn(Object *obj)
{
ARMCPU *cpu = ARM_CPU(obj);
- set_feature(&cpu->env, ARM_FEATURE_V8);
- set_feature(&cpu->env, ARM_FEATURE_VFP4);
- set_feature(&cpu->env, ARM_FEATURE_NEON);
- set_feature(&cpu->env, ARM_FEATURE_AARCH64);
- set_feature(&cpu->env, ARM_FEATURE_V8_AES);
- set_feature(&cpu->env, ARM_FEATURE_V8_SHA1);
- set_feature(&cpu->env, ARM_FEATURE_V8_SHA256);
- set_feature(&cpu->env, ARM_FEATURE_V8_SHA512);
- set_feature(&cpu->env, ARM_FEATURE_V8_SHA3);
- set_feature(&cpu->env, ARM_FEATURE_V8_SM3);
- set_feature(&cpu->env, ARM_FEATURE_V8_SM4);
- set_feature(&cpu->env, ARM_FEATURE_V8_PMULL);
- set_feature(&cpu->env, ARM_FEATURE_CRC);
- set_feature(&cpu->env, ARM_FEATURE_V8_RDM);
- set_feature(&cpu->env, ARM_FEATURE_V8_FP16);
- set_feature(&cpu->env, ARM_FEATURE_V8_FCMA);
- cpu->ctr = 0x80038003; /* 32 byte I and D cacheline size, VIPT icache */
- cpu->dcz_blocksize = 7; /* 512 bytes */
-}
+ if (kvm_enabled()) {
+ kvm_arm_set_cpu_features_from_host(cpu);
+ } else {
+ aarch64_a57_initfn(obj);
+#ifdef CONFIG_USER_ONLY
+ /* We don't set these in system emulation mode for the moment,
+ * since we don't correctly set the ID registers to advertise them,
+ * and in some cases they're only available in AArch64 and not AArch32,
+ * whereas the architecture requires them to be present in both if
+ * present in either.
+ */
+ set_feature(&cpu->env, ARM_FEATURE_V8);
+ set_feature(&cpu->env, ARM_FEATURE_VFP4);
+ set_feature(&cpu->env, ARM_FEATURE_NEON);
+ set_feature(&cpu->env, ARM_FEATURE_AARCH64);
+ set_feature(&cpu->env, ARM_FEATURE_V8_AES);
+ set_feature(&cpu->env, ARM_FEATURE_V8_SHA1);
+ set_feature(&cpu->env, ARM_FEATURE_V8_SHA256);
+ set_feature(&cpu->env, ARM_FEATURE_V8_SHA512);
+ set_feature(&cpu->env, ARM_FEATURE_V8_SHA3);
+ set_feature(&cpu->env, ARM_FEATURE_V8_SM3);
+ set_feature(&cpu->env, ARM_FEATURE_V8_SM4);
+ set_feature(&cpu->env, ARM_FEATURE_V8_PMULL);
+ set_feature(&cpu->env, ARM_FEATURE_CRC);
+ set_feature(&cpu->env, ARM_FEATURE_V8_RDM);
+ set_feature(&cpu->env, ARM_FEATURE_V8_FP16);
+ set_feature(&cpu->env, ARM_FEATURE_V8_FCMA);
+ /* For usermode -cpu max we can use a larger and more efficient DCZ
+ * blocksize since we don't have to follow what the hardware does.
+ */
+ cpu->ctr = 0x80038003; /* 32 byte I and D cacheline size, VIPT icache */
+ cpu->dcz_blocksize = 7; /* 512 bytes */
#endif
+ }
+}
typedef struct ARMCPUInfo {
const char *name;
@@ -247,9 +269,7 @@ typedef struct ARMCPUInfo {
static const ARMCPUInfo aarch64_cpus[] = {
{ .name = "cortex-a57", .initfn = aarch64_a57_initfn },
{ .name = "cortex-a53", .initfn = aarch64_a53_initfn },
-#ifdef CONFIG_USER_ONLY
- { .name = "any", .initfn = aarch64_any_initfn },
-#endif
+ { .name = "max", .initfn = aarch64_max_initfn },
{ .name = NULL }
};
@@ -366,3 +386,44 @@ static void aarch64_cpu_register_types(void)
}
type_init(aarch64_cpu_register_types)
+
+/* The manual says that when SVE is enabled and VQ is widened the
+ * implementation is allowed to zero the previously inaccessible
+ * portion of the registers. The corollary to that is that when
+ * SVE is enabled and VQ is narrowed we are also allowed to zero
+ * the now inaccessible portion of the registers.
+ *
+ * The intent of this is that no predicate bit beyond VQ is ever set.
+ * Which means that some operations on predicate registers themselves
+ * may operate on full uint64_t or even unrolled across the maximum
+ * uint64_t[4]. Performing 4 bits of host arithmetic unconditionally
+ * may well be cheaper than conditionals to restrict the operation
+ * to the relevant portion of a uint16_t[16].
+ *
+ * TODO: Need to call this for changes to the real system registers
+ * and EL state changes.
+ */
+void aarch64_sve_narrow_vq(CPUARMState *env, unsigned vq)
+{
+ int i, j;
+ uint64_t pmask;
+
+ assert(vq >= 1 && vq <= ARM_MAX_VQ);
+
+ /* Zap the high bits of the zregs. */
+ for (i = 0; i < 32; i++) {
+ memset(&env->vfp.zregs[i].d[2 * vq], 0, 16 * (ARM_MAX_VQ - vq));
+ }
+
+ /* Zap the high bits of the pregs and ffr. */
+ pmask = 0;
+ if (vq & 3) {
+ pmask = ~(-1ULL << (16 * (vq & 3)));
+ }
+ for (j = vq / 4; j < ARM_MAX_VQ / 4; j++) {
+ for (i = 0; i < 17; ++i) {
+ env->vfp.pregs[i].p[j] &= pmask;
+ }
+ pmask = 0;
+ }
+}
diff --git a/target/arm/kvm.c b/target/arm/kvm.c
index 1219d0062b..ecc39ac295 100644
--- a/target/arm/kvm.c
+++ b/target/arm/kvm.c
@@ -33,6 +33,8 @@ const KVMCapabilityInfo kvm_arch_required_capabilities[] = {
static bool cap_has_mp_state;
+static ARMHostCPUFeatures arm_host_cpu_features;
+
int kvm_arm_vcpu_init(CPUState *cs)
{
ARMCPU *cpu = ARM_CPU(cs);
@@ -129,44 +131,27 @@ void kvm_arm_destroy_scratch_host_vcpu(int *fdarray)
}
}
-static void kvm_arm_host_cpu_class_init(ObjectClass *oc, void *data)
+void kvm_arm_set_cpu_features_from_host(ARMCPU *cpu)
{
- ARMHostCPUClass *ahcc = ARM_HOST_CPU_CLASS(oc);
+ CPUARMState *env = &cpu->env;
- /* All we really need to set up for the 'host' CPU
- * is the feature bits -- we rely on the fact that the
- * various ID register values in ARMCPU are only used for
- * TCG CPUs.
- */
- if (!kvm_arm_get_host_cpu_features(ahcc)) {
- fprintf(stderr, "Failed to retrieve host CPU features!\n");
- abort();
+ if (!arm_host_cpu_features.dtb_compatible) {
+ if (!kvm_enabled() ||
+ !kvm_arm_get_host_cpu_features(&arm_host_cpu_features)) {
+ /* We can't report this error yet, so flag that we need to
+ * in arm_cpu_realizefn().
+ */
+ cpu->kvm_target = QEMU_KVM_ARM_TARGET_NONE;
+ cpu->host_cpu_probe_failed = true;
+ return;
+ }
}
-}
-static void kvm_arm_host_cpu_initfn(Object *obj)
-{
- ARMHostCPUClass *ahcc = ARM_HOST_CPU_GET_CLASS(obj);
- ARMCPU *cpu = ARM_CPU(obj);
- CPUARMState *env = &cpu->env;
-
- cpu->kvm_target = ahcc->target;
- cpu->dtb_compatible = ahcc->dtb_compatible;
- env->features = ahcc->features;
+ cpu->kvm_target = arm_host_cpu_features.target;
+ cpu->dtb_compatible = arm_host_cpu_features.dtb_compatible;
+ env->features = arm_host_cpu_features.features;
}
-static const TypeInfo host_arm_cpu_type_info = {
- .name = TYPE_ARM_HOST_CPU,
-#ifdef TARGET_AARCH64
- .parent = TYPE_AARCH64_CPU,
-#else
- .parent = TYPE_ARM_CPU,
-#endif
- .instance_init = kvm_arm_host_cpu_initfn,
- .class_init = kvm_arm_host_cpu_class_init,
- .class_size = sizeof(ARMHostCPUClass),
-};
-
int kvm_arch_init(MachineState *ms, KVMState *s)
{
/* For ARM interrupt delivery is always asynchronous,
@@ -182,8 +167,6 @@ int kvm_arch_init(MachineState *ms, KVMState *s)
cap_has_mp_state = kvm_check_extension(s, KVM_CAP_MP_STATE);
- type_register_static(&host_arm_cpu_type_info);
-
return 0;
}
diff --git a/target/arm/kvm32.c b/target/arm/kvm32.c
index f77c9c494b..1740cda47d 100644
--- a/target/arm/kvm32.c
+++ b/target/arm/kvm32.c
@@ -28,7 +28,7 @@ static inline void set_feature(uint64_t *features, int feature)
*features |= 1ULL << feature;
}
-bool kvm_arm_get_host_cpu_features(ARMHostCPUClass *ahcc)
+bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf)
{
/* Identify the feature bits corresponding to the host CPU, and
* fill out the ARMHostCPUClass fields accordingly. To do this
@@ -74,13 +74,13 @@ bool kvm_arm_get_host_cpu_features(ARMHostCPUClass *ahcc)
return false;
}
- ahcc->target = init.target;
+ ahcf->target = init.target;
/* This is not strictly blessed by the device tree binding docs yet,
* but in practice the kernel does not care about this string so
* there is no point maintaining an KVM_ARM_TARGET_* -> string table.
*/
- ahcc->dtb_compatible = "arm,arm-v7";
+ ahcf->dtb_compatible = "arm,arm-v7";
for (i = 0; i < ARRAY_SIZE(idregs); i++) {
ret = ioctl(fdarray[2], KVM_GET_ONE_REG, &idregs[i]);
@@ -132,7 +132,7 @@ bool kvm_arm_get_host_cpu_features(ARMHostCPUClass *ahcc)
set_feature(&features, ARM_FEATURE_VFP4);
}
- ahcc->features = features;
+ ahcf->features = features;
return true;
}
diff --git a/target/arm/kvm64.c b/target/arm/kvm64.c
index ac728494a4..e0b8246283 100644
--- a/target/arm/kvm64.c
+++ b/target/arm/kvm64.c
@@ -443,7 +443,7 @@ static inline void unset_feature(uint64_t *features, int feature)
*features &= ~(1ULL << feature);
}
-bool kvm_arm_get_host_cpu_features(ARMHostCPUClass *ahcc)
+bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf)
{
/* Identify the feature bits corresponding to the host CPU, and
* fill out the ARMHostCPUClass fields accordingly. To do this
@@ -471,8 +471,8 @@ bool kvm_arm_get_host_cpu_features(ARMHostCPUClass *ahcc)
return false;
}
- ahcc->target = init.target;
- ahcc->dtb_compatible = "arm,arm-v8";
+ ahcf->target = init.target;
+ ahcf->dtb_compatible = "arm,arm-v8";
kvm_arm_destroy_scratch_host_vcpu(fdarray);
@@ -486,7 +486,7 @@ bool kvm_arm_get_host_cpu_features(ARMHostCPUClass *ahcc)
set_feature(&features, ARM_FEATURE_AARCH64);
set_feature(&features, ARM_FEATURE_PMU);
- ahcc->features = features;
+ ahcf->features = features;
return true;
}
diff --git a/target/arm/kvm_arm.h b/target/arm/kvm_arm.h
index cfb7e5af72..1e2364007d 100644
--- a/target/arm/kvm_arm.h
+++ b/target/arm/kvm_arm.h
@@ -152,20 +152,16 @@ bool kvm_arm_create_scratch_host_vcpu(const uint32_t *cpus_to_try,
void kvm_arm_destroy_scratch_host_vcpu(int *fdarray);
#define TYPE_ARM_HOST_CPU "host-" TYPE_ARM_CPU
-#define ARM_HOST_CPU_CLASS(klass) \
- OBJECT_CLASS_CHECK(ARMHostCPUClass, (klass), TYPE_ARM_HOST_CPU)
-#define ARM_HOST_CPU_GET_CLASS(obj) \
- OBJECT_GET_CLASS(ARMHostCPUClass, (obj), TYPE_ARM_HOST_CPU)
-
-typedef struct ARMHostCPUClass {
- /*< private >*/
- ARMCPUClass parent_class;
- /*< public >*/
+/**
+ * ARMHostCPUFeatures: information about the host CPU (identified
+ * by asking the host kernel)
+ */
+typedef struct ARMHostCPUFeatures {
uint64_t features;
uint32_t target;
const char *dtb_compatible;
-} ARMHostCPUClass;
+} ARMHostCPUFeatures;
/**
* kvm_arm_get_host_cpu_features:
@@ -174,8 +170,16 @@ typedef struct ARMHostCPUClass {
* Probe the capabilities of the host kernel's preferred CPU and fill
* in the ARMHostCPUClass struct accordingly.
*/
-bool kvm_arm_get_host_cpu_features(ARMHostCPUClass *ahcc);
+bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf);
+/**
+ * kvm_arm_set_cpu_features_from_host:
+ * @cpu: ARMCPU to set the features for
+ *
+ * Set up the ARMCPU struct fields up to match the information probed
+ * from the host CPU.
+ */
+void kvm_arm_set_cpu_features_from_host(ARMCPU *cpu);
/**
* kvm_arm_sync_mpstate_to_kvm
@@ -200,6 +204,15 @@ void kvm_arm_pmu_init(CPUState *cs);
#else
+static inline void kvm_arm_set_cpu_features_from_host(ARMCPU *cpu)
+{
+ /* This should never actually be called in the "not KVM" case,
+ * but set up the fields to indicate an error anyway.
+ */
+ cpu->kvm_target = QEMU_KVM_ARM_TARGET_NONE;
+ cpu->host_cpu_probe_failed = true;
+}
+
static inline int kvm_arm_vgic_probe(void)
{
return 0;
diff --git a/target/m68k/fpu_helper.c b/target/m68k/fpu_helper.c
index cdb9b50462..62cbb0dff1 100644
--- a/target/m68k/fpu_helper.c
+++ b/target/m68k/fpu_helper.c
@@ -557,3 +557,38 @@ void HELPER(fscale)(CPUM68KState *env, FPReg *res, FPReg *val0, FPReg *val1)
{
res->d = floatx80_scale(val1->d, val0->d, &env->fp_status);
}
+
+void HELPER(flognp1)(CPUM68KState *env, FPReg *res, FPReg *val)
+{
+ res->d = floatx80_lognp1(val->d, &env->fp_status);
+}
+
+void HELPER(flogn)(CPUM68KState *env, FPReg *res, FPReg *val)
+{
+ res->d = floatx80_logn(val->d, &env->fp_status);
+}
+
+void HELPER(flog10)(CPUM68KState *env, FPReg *res, FPReg *val)
+{
+ res->d = floatx80_log10(val->d, &env->fp_status);
+}
+
+void HELPER(flog2)(CPUM68KState *env, FPReg *res, FPReg *val)
+{
+ res->d = floatx80_log2(val->d, &env->fp_status);
+}
+
+void HELPER(fetox)(CPUM68KState *env, FPReg *res, FPReg *val)
+{
+ res->d = floatx80_etox(val->d, &env->fp_status);
+}
+
+void HELPER(ftwotox)(CPUM68KState *env, FPReg *res, FPReg *val)
+{
+ res->d = floatx80_twotox(val->d, &env->fp_status);
+}
+
+void HELPER(ftentox)(CPUM68KState *env, FPReg *res, FPReg *val)
+{
+ res->d = floatx80_tentox(val->d, &env->fp_status);
+}
diff --git a/target/m68k/helper.h b/target/m68k/helper.h
index c348dced3a..9a9734c196 100644
--- a/target/m68k/helper.h
+++ b/target/m68k/helper.h
@@ -68,6 +68,13 @@ DEF_HELPER_4(frem, void, env, fp, fp, fp)
DEF_HELPER_3(fgetexp, void, env, fp, fp)
DEF_HELPER_3(fgetman, void, env, fp, fp)
DEF_HELPER_4(fscale, void, env, fp, fp, fp)
+DEF_HELPER_3(flognp1, void, env, fp, fp)
+DEF_HELPER_3(flogn, void, env, fp, fp)
+DEF_HELPER_3(flog10, void, env, fp, fp)
+DEF_HELPER_3(flog2, void, env, fp, fp)
+DEF_HELPER_3(fetox, void, env, fp, fp)
+DEF_HELPER_3(ftwotox, void, env, fp, fp)
+DEF_HELPER_3(ftentox, void, env, fp, fp)
DEF_HELPER_3(mac_move, void, env, i32, i32)
DEF_HELPER_3(macmulf, i64, env, i32, i32)
diff --git a/target/m68k/softfloat.c b/target/m68k/softfloat.c
index 9cb141900c..4bd5b9e6b7 100644
--- a/target/m68k/softfloat.c
+++ b/target/m68k/softfloat.c
@@ -21,6 +21,7 @@
#include "qemu/osdep.h"
#include "softfloat.h"
#include "fpu/softfloat-macros.h"
+#include "softfloat_fpsp_tables.h"
static floatx80 propagateFloatx80NaNOneArg(floatx80 a, float_status *status)
{
@@ -247,3 +248,1021 @@ floatx80 floatx80_scale(floatx80 a, floatx80 b, float_status *status)
return roundAndPackFloatx80(status->floatx80_rounding_precision,
aSign, aExp, aSig, 0, status);
}
+
+floatx80 floatx80_move(floatx80 a, float_status *status)
+{
+ flag aSign;
+ int32_t aExp;
+ uint64_t aSig;
+
+ aSig = extractFloatx80Frac(a);
+ aExp = extractFloatx80Exp(a);
+ aSign = extractFloatx80Sign(a);
+
+ if (aExp == 0x7FFF) {
+ if ((uint64_t)(aSig << 1)) {
+ return propagateFloatx80NaNOneArg(a, status);
+ }
+ return a;
+ }
+ if (aExp == 0) {
+ if (aSig == 0) {
+ return a;
+ }
+ normalizeRoundAndPackFloatx80(status->floatx80_rounding_precision,
+ aSign, aExp, aSig, 0, status);
+ }
+ return roundAndPackFloatx80(status->floatx80_rounding_precision, aSign,
+ aExp, aSig, 0, status);
+}
+
+/*----------------------------------------------------------------------------
+| Algorithms for transcendental functions supported by MC68881 and MC68882
+| mathematical coprocessors. The functions are derived from FPSP library.
+*----------------------------------------------------------------------------*/
+
+#define one_exp 0x3FFF
+#define one_sig LIT64(0x8000000000000000)
+
+/*----------------------------------------------------------------------------
+ | Function for compactifying extended double-precision floating point values.
+ *----------------------------------------------------------------------------*/
+
+static int32_t floatx80_make_compact(int32_t aExp, uint64_t aSig)
+{
+ return (aExp << 16) | (aSig >> 48);
+}
+
+/*----------------------------------------------------------------------------
+ | Log base e of x plus 1
+ *----------------------------------------------------------------------------*/
+
+floatx80 floatx80_lognp1(floatx80 a, float_status *status)
+{
+ flag aSign;
+ int32_t aExp;
+ uint64_t aSig, fSig;
+
+ int8_t user_rnd_mode, user_rnd_prec;
+
+ int32_t compact, j, k;
+ floatx80 fp0, fp1, fp2, fp3, f, logof2, klog2, saveu;
+
+ aSig = extractFloatx80Frac(a);
+ aExp = extractFloatx80Exp(a);
+ aSign = extractFloatx80Sign(a);
+
+ if (aExp == 0x7FFF) {
+ if ((uint64_t) (aSig << 1)) {
+ propagateFloatx80NaNOneArg(a, status);
+ }
+ if (aSign) {
+ float_raise(float_flag_invalid, status);
+ return floatx80_default_nan(status);
+ }
+ return packFloatx80(0, floatx80_infinity.high, floatx80_infinity.low);
+ }
+
+ if (aExp == 0 && aSig == 0) {
+ return packFloatx80(aSign, 0, 0);
+ }
+
+ if (aSign && aExp >= one_exp) {
+ if (aExp == one_exp && aSig == one_sig) {
+ float_raise(float_flag_divbyzero, status);
+ packFloatx80(aSign, floatx80_infinity.high, floatx80_infinity.low);
+ }
+ float_raise(float_flag_invalid, status);
+ return floatx80_default_nan(status);
+ }
+
+ if (aExp < 0x3f99 || (aExp == 0x3f99 && aSig == one_sig)) {
+ /* <= min threshold */
+ float_raise(float_flag_inexact, status);
+ return floatx80_move(a, status);
+ }
+
+ user_rnd_mode = status->float_rounding_mode;
+ user_rnd_prec = status->floatx80_rounding_precision;
+ status->float_rounding_mode = float_round_nearest_even;
+ status->floatx80_rounding_precision = 80;
+
+ compact = floatx80_make_compact(aExp, aSig);
+
+ fp0 = a; /* Z */
+ fp1 = a;
+
+ fp0 = floatx80_add(fp0, float32_to_floatx80(make_float32(0x3F800000),
+ status), status); /* X = (1+Z) */
+
+ aExp = extractFloatx80Exp(fp0);
+ aSig = extractFloatx80Frac(fp0);
+
+ compact = floatx80_make_compact(aExp, aSig);
+
+ if (compact < 0x3FFE8000 || compact > 0x3FFFC000) {
+ /* |X| < 1/2 or |X| > 3/2 */
+ k = aExp - 0x3FFF;
+ fp1 = int32_to_floatx80(k, status);
+
+ fSig = (aSig & LIT64(0xFE00000000000000)) | LIT64(0x0100000000000000);
+ j = (fSig >> 56) & 0x7E; /* DISPLACEMENT FOR 1/F */
+
+ f = packFloatx80(0, 0x3FFF, fSig); /* F */
+ fp0 = packFloatx80(0, 0x3FFF, aSig); /* Y */
+
+ fp0 = floatx80_sub(fp0, f, status); /* Y-F */
+
+ lp1cont1:
+ /* LP1CONT1 */
+ fp0 = floatx80_mul(fp0, log_tbl[j], status); /* FP0 IS U = (Y-F)/F */
+ logof2 = packFloatx80(0, 0x3FFE, LIT64(0xB17217F7D1CF79AC));
+ klog2 = floatx80_mul(fp1, logof2, status); /* FP1 IS K*LOG2 */
+ fp2 = floatx80_mul(fp0, fp0, status); /* FP2 IS V=U*U */
+
+ fp3 = fp2;
+ fp1 = fp2;
+
+ fp1 = floatx80_mul(fp1, float64_to_floatx80(
+ make_float64(0x3FC2499AB5E4040B), status),
+ status); /* V*A6 */
+ fp2 = floatx80_mul(fp2, float64_to_floatx80(
+ make_float64(0xBFC555B5848CB7DB), status),
+ status); /* V*A5 */
+ fp1 = floatx80_add(fp1, float64_to_floatx80(
+ make_float64(0x3FC99999987D8730), status),
+ status); /* A4+V*A6 */
+ fp2 = floatx80_add(fp2, float64_to_floatx80(
+ make_float64(0xBFCFFFFFFF6F7E97), status),
+ status); /* A3+V*A5 */
+ fp1 = floatx80_mul(fp1, fp3, status); /* V*(A4+V*A6) */
+ fp2 = floatx80_mul(fp2, fp3, status); /* V*(A3+V*A5) */
+ fp1 = floatx80_add(fp1, float64_to_floatx80(
+ make_float64(0x3FD55555555555A4), status),
+ status); /* A2+V*(A4+V*A6) */
+ fp2 = floatx80_add(fp2, float64_to_floatx80(
+ make_float64(0xBFE0000000000008), status),
+ status); /* A1+V*(A3+V*A5) */
+ fp1 = floatx80_mul(fp1, fp3, status); /* V*(A2+V*(A4+V*A6)) */
+ fp2 = floatx80_mul(fp2, fp3, status); /* V*(A1+V*(A3+V*A5)) */
+ fp1 = floatx80_mul(fp1, fp0, status); /* U*V*(A2+V*(A4+V*A6)) */
+ fp0 = floatx80_add(fp0, fp2, status); /* U+V*(A1+V*(A3+V*A5)) */
+
+ fp1 = floatx80_add(fp1, log_tbl[j + 1],
+ status); /* LOG(F)+U*V*(A2+V*(A4+V*A6)) */
+ fp0 = floatx80_add(fp0, fp1, status); /* FP0 IS LOG(F) + LOG(1+U) */
+
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+
+ a = floatx80_add(fp0, klog2, status);
+
+ float_raise(float_flag_inexact, status);
+
+ return a;
+ } else if (compact < 0x3FFEF07D || compact > 0x3FFF8841) {
+ /* |X| < 1/16 or |X| > -1/16 */
+ /* LP1CARE */
+ fSig = (aSig & LIT64(0xFE00000000000000)) | LIT64(0x0100000000000000);
+ f = packFloatx80(0, 0x3FFF, fSig); /* F */
+ j = (fSig >> 56) & 0x7E; /* DISPLACEMENT FOR 1/F */
+
+ if (compact >= 0x3FFF8000) { /* 1+Z >= 1 */
+ /* KISZERO */
+ fp0 = floatx80_sub(float32_to_floatx80(make_float32(0x3F800000),
+ status), f, status); /* 1-F */
+ fp0 = floatx80_add(fp0, fp1, status); /* FP0 IS Y-F = (1-F)+Z */
+ fp1 = packFloatx80(0, 0, 0); /* K = 0 */
+ } else {
+ /* KISNEG */
+ fp0 = floatx80_sub(float32_to_floatx80(make_float32(0x40000000),
+ status), f, status); /* 2-F */
+ fp1 = floatx80_add(fp1, fp1, status); /* 2Z */
+ fp0 = floatx80_add(fp0, fp1, status); /* FP0 IS Y-F = (2-F)+2Z */
+ fp1 = packFloatx80(1, one_exp, one_sig); /* K = -1 */
+ }
+ goto lp1cont1;
+ } else {
+ /* LP1ONE16 */
+ fp1 = floatx80_add(fp1, fp1, status); /* FP1 IS 2Z */
+ fp0 = floatx80_add(fp0, float32_to_floatx80(make_float32(0x3F800000),
+ status), status); /* FP0 IS 1+X */
+
+ /* LP1CONT2 */
+ fp1 = floatx80_div(fp1, fp0, status); /* U */
+ saveu = fp1;
+ fp0 = floatx80_mul(fp1, fp1, status); /* FP0 IS V = U*U */
+ fp1 = floatx80_mul(fp0, fp0, status); /* FP1 IS W = V*V */
+
+ fp3 = float64_to_floatx80(make_float64(0x3F175496ADD7DAD6),
+ status); /* B5 */
+ fp2 = float64_to_floatx80(make_float64(0x3F3C71C2FE80C7E0),
+ status); /* B4 */
+ fp3 = floatx80_mul(fp3, fp1, status); /* W*B5 */
+ fp2 = floatx80_mul(fp2, fp1, status); /* W*B4 */
+ fp3 = floatx80_add(fp3, float64_to_floatx80(
+ make_float64(0x3F624924928BCCFF), status),
+ status); /* B3+W*B5 */
+ fp2 = floatx80_add(fp2, float64_to_floatx80(
+ make_float64(0x3F899999999995EC), status),
+ status); /* B2+W*B4 */
+ fp1 = floatx80_mul(fp1, fp3, status); /* W*(B3+W*B5) */
+ fp2 = floatx80_mul(fp2, fp0, status); /* V*(B2+W*B4) */
+ fp1 = floatx80_add(fp1, float64_to_floatx80(
+ make_float64(0x3FB5555555555555), status),
+ status); /* B1+W*(B3+W*B5) */
+
+ fp0 = floatx80_mul(fp0, saveu, status); /* FP0 IS U*V */
+ fp1 = floatx80_add(fp1, fp2,
+ status); /* B1+W*(B3+W*B5) + V*(B2+W*B4) */
+ fp0 = floatx80_mul(fp0, fp1,
+ status); /* U*V*([B1+W*(B3+W*B5)] + [V*(B2+W*B4)]) */
+
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+
+ a = floatx80_add(fp0, saveu, status);
+
+ /*if (!floatx80_is_zero(a)) { */
+ float_raise(float_flag_inexact, status);
+ /*} */
+
+ return a;
+ }
+}
+
+/*----------------------------------------------------------------------------
+ | Log base e
+ *----------------------------------------------------------------------------*/
+
+floatx80 floatx80_logn(floatx80 a, float_status *status)
+{
+ flag aSign;
+ int32_t aExp;
+ uint64_t aSig, fSig;
+
+ int8_t user_rnd_mode, user_rnd_prec;
+
+ int32_t compact, j, k, adjk;
+ floatx80 fp0, fp1, fp2, fp3, f, logof2, klog2, saveu;
+
+ aSig = extractFloatx80Frac(a);
+ aExp = extractFloatx80Exp(a);
+ aSign = extractFloatx80Sign(a);
+
+ if (aExp == 0x7FFF) {
+ if ((uint64_t) (aSig << 1)) {
+ propagateFloatx80NaNOneArg(a, status);
+ }
+ if (aSign == 0) {
+ return packFloatx80(0, floatx80_infinity.high,
+ floatx80_infinity.low);
+ }
+ }
+
+ adjk = 0;
+
+ if (aExp == 0) {
+ if (aSig == 0) { /* zero */
+ float_raise(float_flag_divbyzero, status);
+ return packFloatx80(1, floatx80_infinity.high,
+ floatx80_infinity.low);
+ }
+ if ((aSig & one_sig) == 0) { /* denormal */
+ normalizeFloatx80Subnormal(aSig, &aExp, &aSig);
+ adjk = -100;
+ aExp += 100;
+ a = packFloatx80(aSign, aExp, aSig);
+ }
+ }
+
+ if (aSign) {
+ float_raise(float_flag_invalid, status);
+ return floatx80_default_nan(status);
+ }
+
+ user_rnd_mode = status->float_rounding_mode;
+ user_rnd_prec = status->floatx80_rounding_precision;
+ status->float_rounding_mode = float_round_nearest_even;
+ status->floatx80_rounding_precision = 80;
+
+ compact = floatx80_make_compact(aExp, aSig);
+
+ if (compact < 0x3FFEF07D || compact > 0x3FFF8841) {
+ /* |X| < 15/16 or |X| > 17/16 */
+ k = aExp - 0x3FFF;
+ k += adjk;
+ fp1 = int32_to_floatx80(k, status);
+
+ fSig = (aSig & LIT64(0xFE00000000000000)) | LIT64(0x0100000000000000);
+ j = (fSig >> 56) & 0x7E; /* DISPLACEMENT FOR 1/F */
+
+ f = packFloatx80(0, 0x3FFF, fSig); /* F */
+ fp0 = packFloatx80(0, 0x3FFF, aSig); /* Y */
+
+ fp0 = floatx80_sub(fp0, f, status); /* Y-F */
+
+ /* LP1CONT1 */
+ fp0 = floatx80_mul(fp0, log_tbl[j], status); /* FP0 IS U = (Y-F)/F */
+ logof2 = packFloatx80(0, 0x3FFE, LIT64(0xB17217F7D1CF79AC));
+ klog2 = floatx80_mul(fp1, logof2, status); /* FP1 IS K*LOG2 */
+ fp2 = floatx80_mul(fp0, fp0, status); /* FP2 IS V=U*U */
+
+ fp3 = fp2;
+ fp1 = fp2;
+
+ fp1 = floatx80_mul(fp1, float64_to_floatx80(
+ make_float64(0x3FC2499AB5E4040B), status),
+ status); /* V*A6 */
+ fp2 = floatx80_mul(fp2, float64_to_floatx80(
+ make_float64(0xBFC555B5848CB7DB), status),
+ status); /* V*A5 */
+ fp1 = floatx80_add(fp1, float64_to_floatx80(
+ make_float64(0x3FC99999987D8730), status),
+ status); /* A4+V*A6 */
+ fp2 = floatx80_add(fp2, float64_to_floatx80(
+ make_float64(0xBFCFFFFFFF6F7E97), status),
+ status); /* A3+V*A5 */
+ fp1 = floatx80_mul(fp1, fp3, status); /* V*(A4+V*A6) */
+ fp2 = floatx80_mul(fp2, fp3, status); /* V*(A3+V*A5) */
+ fp1 = floatx80_add(fp1, float64_to_floatx80(
+ make_float64(0x3FD55555555555A4), status),
+ status); /* A2+V*(A4+V*A6) */
+ fp2 = floatx80_add(fp2, float64_to_floatx80(
+ make_float64(0xBFE0000000000008), status),
+ status); /* A1+V*(A3+V*A5) */
+ fp1 = floatx80_mul(fp1, fp3, status); /* V*(A2+V*(A4+V*A6)) */
+ fp2 = floatx80_mul(fp2, fp3, status); /* V*(A1+V*(A3+V*A5)) */
+ fp1 = floatx80_mul(fp1, fp0, status); /* U*V*(A2+V*(A4+V*A6)) */
+ fp0 = floatx80_add(fp0, fp2, status); /* U+V*(A1+V*(A3+V*A5)) */
+
+ fp1 = floatx80_add(fp1, log_tbl[j + 1],
+ status); /* LOG(F)+U*V*(A2+V*(A4+V*A6)) */
+ fp0 = floatx80_add(fp0, fp1, status); /* FP0 IS LOG(F) + LOG(1+U) */
+
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+
+ a = floatx80_add(fp0, klog2, status);
+
+ float_raise(float_flag_inexact, status);
+
+ return a;
+ } else { /* |X-1| >= 1/16 */
+ fp0 = a;
+ fp1 = a;
+ fp1 = floatx80_sub(fp1, float32_to_floatx80(make_float32(0x3F800000),
+ status), status); /* FP1 IS X-1 */
+ fp0 = floatx80_add(fp0, float32_to_floatx80(make_float32(0x3F800000),
+ status), status); /* FP0 IS X+1 */
+ fp1 = floatx80_add(fp1, fp1, status); /* FP1 IS 2(X-1) */
+
+ /* LP1CONT2 */
+ fp1 = floatx80_div(fp1, fp0, status); /* U */
+ saveu = fp1;
+ fp0 = floatx80_mul(fp1, fp1, status); /* FP0 IS V = U*U */
+ fp1 = floatx80_mul(fp0, fp0, status); /* FP1 IS W = V*V */
+
+ fp3 = float64_to_floatx80(make_float64(0x3F175496ADD7DAD6),
+ status); /* B5 */
+ fp2 = float64_to_floatx80(make_float64(0x3F3C71C2FE80C7E0),
+ status); /* B4 */
+ fp3 = floatx80_mul(fp3, fp1, status); /* W*B5 */
+ fp2 = floatx80_mul(fp2, fp1, status); /* W*B4 */
+ fp3 = floatx80_add(fp3, float64_to_floatx80(
+ make_float64(0x3F624924928BCCFF), status),
+ status); /* B3+W*B5 */
+ fp2 = floatx80_add(fp2, float64_to_floatx80(
+ make_float64(0x3F899999999995EC), status),
+ status); /* B2+W*B4 */
+ fp1 = floatx80_mul(fp1, fp3, status); /* W*(B3+W*B5) */
+ fp2 = floatx80_mul(fp2, fp0, status); /* V*(B2+W*B4) */
+ fp1 = floatx80_add(fp1, float64_to_floatx80(
+ make_float64(0x3FB5555555555555), status),
+ status); /* B1+W*(B3+W*B5) */
+
+ fp0 = floatx80_mul(fp0, saveu, status); /* FP0 IS U*V */
+ fp1 = floatx80_add(fp1, fp2, status); /* B1+W*(B3+W*B5) + V*(B2+W*B4) */
+ fp0 = floatx80_mul(fp0, fp1,
+ status); /* U*V*([B1+W*(B3+W*B5)] + [V*(B2+W*B4)]) */
+
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+
+ a = floatx80_add(fp0, saveu, status);
+
+ /*if (!floatx80_is_zero(a)) { */
+ float_raise(float_flag_inexact, status);
+ /*} */
+
+ return a;
+ }
+}
+
+/*----------------------------------------------------------------------------
+ | Log base 10
+ *----------------------------------------------------------------------------*/
+
+floatx80 floatx80_log10(floatx80 a, float_status *status)
+{
+ flag aSign;
+ int32_t aExp;
+ uint64_t aSig;
+
+ int8_t user_rnd_mode, user_rnd_prec;
+
+ floatx80 fp0, fp1;
+
+ aSig = extractFloatx80Frac(a);
+ aExp = extractFloatx80Exp(a);
+ aSign = extractFloatx80Sign(a);
+
+ if (aExp == 0x7FFF) {
+ if ((uint64_t) (aSig << 1)) {
+ propagateFloatx80NaNOneArg(a, status);
+ }
+ if (aSign == 0) {
+ return packFloatx80(0, floatx80_infinity.high,
+ floatx80_infinity.low);
+ }
+ }
+
+ if (aExp == 0 && aSig == 0) {
+ float_raise(float_flag_divbyzero, status);
+ return packFloatx80(1, floatx80_infinity.high,
+ floatx80_infinity.low);
+ }
+
+ if (aSign) {
+ float_raise(float_flag_invalid, status);
+ return floatx80_default_nan(status);
+ }
+
+ user_rnd_mode = status->float_rounding_mode;
+ user_rnd_prec = status->floatx80_rounding_precision;
+ status->float_rounding_mode = float_round_nearest_even;
+ status->floatx80_rounding_precision = 80;
+
+ fp0 = floatx80_logn(a, status);
+ fp1 = packFloatx80(0, 0x3FFD, LIT64(0xDE5BD8A937287195)); /* INV_L10 */
+
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+
+ a = floatx80_mul(fp0, fp1, status); /* LOGN(X)*INV_L10 */
+
+ float_raise(float_flag_inexact, status);
+
+ return a;
+}
+
+/*----------------------------------------------------------------------------
+ | Log base 2
+ *----------------------------------------------------------------------------*/
+
+floatx80 floatx80_log2(floatx80 a, float_status *status)
+{
+ flag aSign;
+ int32_t aExp;
+ uint64_t aSig;
+
+ int8_t user_rnd_mode, user_rnd_prec;
+
+ floatx80 fp0, fp1;
+
+ aSig = extractFloatx80Frac(a);
+ aExp = extractFloatx80Exp(a);
+ aSign = extractFloatx80Sign(a);
+
+ if (aExp == 0x7FFF) {
+ if ((uint64_t) (aSig << 1)) {
+ propagateFloatx80NaNOneArg(a, status);
+ }
+ if (aSign == 0) {
+ return packFloatx80(0, floatx80_infinity.high,
+ floatx80_infinity.low);
+ }
+ }
+
+ if (aExp == 0) {
+ if (aSig == 0) {
+ float_raise(float_flag_divbyzero, status);
+ return packFloatx80(1, floatx80_infinity.high,
+ floatx80_infinity.low);
+ }
+ normalizeFloatx80Subnormal(aSig, &aExp, &aSig);
+ }
+
+ if (aSign) {
+ float_raise(float_flag_invalid, status);
+ return floatx80_default_nan(status);
+ }
+
+ user_rnd_mode = status->float_rounding_mode;
+ user_rnd_prec = status->floatx80_rounding_precision;
+ status->float_rounding_mode = float_round_nearest_even;
+ status->floatx80_rounding_precision = 80;
+
+ if (aSig == one_sig) { /* X is 2^k */
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+
+ a = int32_to_floatx80(aExp - 0x3FFF, status);
+ } else {
+ fp0 = floatx80_logn(a, status);
+ fp1 = packFloatx80(0, 0x3FFF, LIT64(0xB8AA3B295C17F0BC)); /* INV_L2 */
+
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+
+ a = floatx80_mul(fp0, fp1, status); /* LOGN(X)*INV_L2 */
+ }
+
+ float_raise(float_flag_inexact, status);
+
+ return a;
+}
+
+/*----------------------------------------------------------------------------
+ | e to x
+ *----------------------------------------------------------------------------*/
+
+floatx80 floatx80_etox(floatx80 a, float_status *status)
+{
+ flag aSign;
+ int32_t aExp;
+ uint64_t aSig;
+
+ int8_t user_rnd_mode, user_rnd_prec;
+
+ int32_t compact, n, j, k, m, m1;
+ floatx80 fp0, fp1, fp2, fp3, l2, scale, adjscale;
+ flag adjflag;
+
+ aSig = extractFloatx80Frac(a);
+ aExp = extractFloatx80Exp(a);
+ aSign = extractFloatx80Sign(a);
+
+ if (aExp == 0x7FFF) {
+ if ((uint64_t) (aSig << 1)) {
+ return propagateFloatx80NaNOneArg(a, status);
+ }
+ if (aSign) {
+ return packFloatx80(0, 0, 0);
+ }
+ return packFloatx80(0, floatx80_infinity.high,
+ floatx80_infinity.low);
+ }
+
+ if (aExp == 0 && aSig == 0) {
+ return packFloatx80(0, one_exp, one_sig);
+ }
+
+ user_rnd_mode = status->float_rounding_mode;
+ user_rnd_prec = status->floatx80_rounding_precision;
+ status->float_rounding_mode = float_round_nearest_even;
+ status->floatx80_rounding_precision = 80;
+
+ adjflag = 0;
+
+ if (aExp >= 0x3FBE) { /* |X| >= 2^(-65) */
+ compact = floatx80_make_compact(aExp, aSig);
+
+ if (compact < 0x400CB167) { /* |X| < 16380 log2 */
+ fp0 = a;
+ fp1 = a;
+ fp0 = floatx80_mul(fp0, float32_to_floatx80(
+ make_float32(0x42B8AA3B), status),
+ status); /* 64/log2 * X */
+ adjflag = 0;
+ n = floatx80_to_int32(fp0, status); /* int(64/log2*X) */
+ fp0 = int32_to_floatx80(n, status);
+
+ j = n & 0x3F; /* J = N mod 64 */
+ m = n / 64; /* NOTE: this is really arithmetic right shift by 6 */
+ if (n < 0 && j) {
+ /* arithmetic right shift is division and
+ * round towards minus infinity
+ */
+ m--;
+ }
+ m += 0x3FFF; /* biased exponent of 2^(M) */
+
+ expcont1:
+ fp2 = fp0; /* N */
+ fp0 = floatx80_mul(fp0, float32_to_floatx80(
+ make_float32(0xBC317218), status),
+ status); /* N * L1, L1 = lead(-log2/64) */
+ l2 = packFloatx80(0, 0x3FDC, LIT64(0x82E308654361C4C6));
+ fp2 = floatx80_mul(fp2, l2, status); /* N * L2, L1+L2 = -log2/64 */
+ fp0 = floatx80_add(fp0, fp1, status); /* X + N*L1 */
+ fp0 = floatx80_add(fp0, fp2, status); /* R */
+
+ fp1 = floatx80_mul(fp0, fp0, status); /* S = R*R */
+ fp2 = float32_to_floatx80(make_float32(0x3AB60B70),
+ status); /* A5 */
+ fp2 = floatx80_mul(fp2, fp1, status); /* fp2 is S*A5 */
+ fp3 = floatx80_mul(float32_to_floatx80(make_float32(0x3C088895),
+ status), fp1,
+ status); /* fp3 is S*A4 */
+ fp2 = floatx80_add(fp2, float64_to_floatx80(make_float64(
+ 0x3FA5555555554431), status),
+ status); /* fp2 is A3+S*A5 */
+ fp3 = floatx80_add(fp3, float64_to_floatx80(make_float64(
+ 0x3FC5555555554018), status),
+ status); /* fp3 is A2+S*A4 */
+ fp2 = floatx80_mul(fp2, fp1, status); /* fp2 is S*(A3+S*A5) */
+ fp3 = floatx80_mul(fp3, fp1, status); /* fp3 is S*(A2+S*A4) */
+ fp2 = floatx80_add(fp2, float32_to_floatx80(
+ make_float32(0x3F000000), status),
+ status); /* fp2 is A1+S*(A3+S*A5) */
+ fp3 = floatx80_mul(fp3, fp0, status); /* fp3 IS R*S*(A2+S*A4) */
+ fp2 = floatx80_mul(fp2, fp1,
+ status); /* fp2 IS S*(A1+S*(A3+S*A5)) */
+ fp0 = floatx80_add(fp0, fp3, status); /* fp0 IS R+R*S*(A2+S*A4) */
+ fp0 = floatx80_add(fp0, fp2, status); /* fp0 IS EXP(R) - 1 */
+
+ fp1 = exp_tbl[j];
+ fp0 = floatx80_mul(fp0, fp1, status); /* 2^(J/64)*(Exp(R)-1) */
+ fp0 = floatx80_add(fp0, float32_to_floatx80(exp_tbl2[j], status),
+ status); /* accurate 2^(J/64) */
+ fp0 = floatx80_add(fp0, fp1,
+ status); /* 2^(J/64) + 2^(J/64)*(Exp(R)-1) */
+
+ scale = packFloatx80(0, m, one_sig);
+ if (adjflag) {
+ adjscale = packFloatx80(0, m1, one_sig);
+ fp0 = floatx80_mul(fp0, adjscale, status);
+ }
+
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+
+ a = floatx80_mul(fp0, scale, status);
+
+ float_raise(float_flag_inexact, status);
+
+ return a;
+ } else { /* |X| >= 16380 log2 */
+ if (compact > 0x400CB27C) { /* |X| >= 16480 log2 */
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+ if (aSign) {
+ a = roundAndPackFloatx80(
+ status->floatx80_rounding_precision,
+ 0, -0x1000, aSig, 0, status);
+ } else {
+ a = roundAndPackFloatx80(
+ status->floatx80_rounding_precision,
+ 0, 0x8000, aSig, 0, status);
+ }
+ float_raise(float_flag_inexact, status);
+
+ return a;
+ } else {
+ fp0 = a;
+ fp1 = a;
+ fp0 = floatx80_mul(fp0, float32_to_floatx80(
+ make_float32(0x42B8AA3B), status),
+ status); /* 64/log2 * X */
+ adjflag = 1;
+ n = floatx80_to_int32(fp0, status); /* int(64/log2*X) */
+ fp0 = int32_to_floatx80(n, status);
+
+ j = n & 0x3F; /* J = N mod 64 */
+ /* NOTE: this is really arithmetic right shift by 6 */
+ k = n / 64;
+ if (n < 0 && j) {
+ /* arithmetic right shift is division and
+ * round towards minus infinity
+ */
+ k--;
+ }
+ /* NOTE: this is really arithmetic right shift by 1 */
+ m1 = k / 2;
+ if (k < 0 && (k & 1)) {
+ /* arithmetic right shift is division and
+ * round towards minus infinity
+ */
+ m1--;
+ }
+ m = k - m1;
+ m1 += 0x3FFF; /* biased exponent of 2^(M1) */
+ m += 0x3FFF; /* biased exponent of 2^(M) */
+
+ goto expcont1;
+ }
+ }
+ } else { /* |X| < 2^(-65) */
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+
+ a = floatx80_add(a, float32_to_floatx80(make_float32(0x3F800000),
+ status), status); /* 1 + X */
+
+ float_raise(float_flag_inexact, status);
+
+ return a;
+ }
+}
+
+/*----------------------------------------------------------------------------
+ | 2 to x
+ *----------------------------------------------------------------------------*/
+
+floatx80 floatx80_twotox(floatx80 a, float_status *status)
+{
+ flag aSign;
+ int32_t aExp;
+ uint64_t aSig;
+
+ int8_t user_rnd_mode, user_rnd_prec;
+
+ int32_t compact, n, j, l, m, m1;
+ floatx80 fp0, fp1, fp2, fp3, adjfact, fact1, fact2;
+
+ aSig = extractFloatx80Frac(a);
+ aExp = extractFloatx80Exp(a);
+ aSign = extractFloatx80Sign(a);
+
+ if (aExp == 0x7FFF) {
+ if ((uint64_t) (aSig << 1)) {
+ return propagateFloatx80NaNOneArg(a, status);
+ }
+ if (aSign) {
+ return packFloatx80(0, 0, 0);
+ }
+ return packFloatx80(0, floatx80_infinity.high,
+ floatx80_infinity.low);
+ }
+
+ if (aExp == 0 && aSig == 0) {
+ return packFloatx80(0, one_exp, one_sig);
+ }
+
+ user_rnd_mode = status->float_rounding_mode;
+ user_rnd_prec = status->floatx80_rounding_precision;
+ status->float_rounding_mode = float_round_nearest_even;
+ status->floatx80_rounding_precision = 80;
+
+ fp0 = a;
+
+ compact = floatx80_make_compact(aExp, aSig);
+
+ if (compact < 0x3FB98000 || compact > 0x400D80C0) {
+ /* |X| > 16480 or |X| < 2^(-70) */
+ if (compact > 0x3FFF8000) { /* |X| > 16480 */
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+
+ if (aSign) {
+ return roundAndPackFloatx80(status->floatx80_rounding_precision,
+ 0, -0x1000, aSig, 0, status);
+ } else {
+ return roundAndPackFloatx80(status->floatx80_rounding_precision,
+ 0, 0x8000, aSig, 0, status);
+ }
+ } else { /* |X| < 2^(-70) */
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+
+ a = floatx80_add(fp0, float32_to_floatx80(
+ make_float32(0x3F800000), status),
+ status); /* 1 + X */
+
+ float_raise(float_flag_inexact, status);
+
+ return a;
+ }
+ } else { /* 2^(-70) <= |X| <= 16480 */
+ fp1 = fp0; /* X */
+ fp1 = floatx80_mul(fp1, float32_to_floatx80(
+ make_float32(0x42800000), status),
+ status); /* X * 64 */
+ n = floatx80_to_int32(fp1, status);
+ fp1 = int32_to_floatx80(n, status);
+ j = n & 0x3F;
+ l = n / 64; /* NOTE: this is really arithmetic right shift by 6 */
+ if (n < 0 && j) {
+ /* arithmetic right shift is division and
+ * round towards minus infinity
+ */
+ l--;
+ }
+ m = l / 2; /* NOTE: this is really arithmetic right shift by 1 */
+ if (l < 0 && (l & 1)) {
+ /* arithmetic right shift is division and
+ * round towards minus infinity
+ */
+ m--;
+ }
+ m1 = l - m;
+ m1 += 0x3FFF; /* ADJFACT IS 2^(M') */
+
+ adjfact = packFloatx80(0, m1, one_sig);
+ fact1 = exp2_tbl[j];
+ fact1.high += m;
+ fact2.high = exp2_tbl2[j] >> 16;
+ fact2.high += m;
+ fact2.low = (uint64_t)(exp2_tbl2[j] & 0xFFFF);
+ fact2.low <<= 48;
+
+ fp1 = floatx80_mul(fp1, float32_to_floatx80(
+ make_float32(0x3C800000), status),
+ status); /* (1/64)*N */
+ fp0 = floatx80_sub(fp0, fp1, status); /* X - (1/64)*INT(64 X) */
+ fp2 = packFloatx80(0, 0x3FFE, LIT64(0xB17217F7D1CF79AC)); /* LOG2 */
+ fp0 = floatx80_mul(fp0, fp2, status); /* R */
+
+ /* EXPR */
+ fp1 = floatx80_mul(fp0, fp0, status); /* S = R*R */
+ fp2 = float64_to_floatx80(make_float64(0x3F56C16D6F7BD0B2),
+ status); /* A5 */
+ fp3 = float64_to_floatx80(make_float64(0x3F811112302C712C),
+ status); /* A4 */
+ fp2 = floatx80_mul(fp2, fp1, status); /* S*A5 */
+ fp3 = floatx80_mul(fp3, fp1, status); /* S*A4 */
+ fp2 = floatx80_add(fp2, float64_to_floatx80(
+ make_float64(0x3FA5555555554CC1), status),
+ status); /* A3+S*A5 */
+ fp3 = floatx80_add(fp3, float64_to_floatx80(
+ make_float64(0x3FC5555555554A54), status),
+ status); /* A2+S*A4 */
+ fp2 = floatx80_mul(fp2, fp1, status); /* S*(A3+S*A5) */
+ fp3 = floatx80_mul(fp3, fp1, status); /* S*(A2+S*A4) */
+ fp2 = floatx80_add(fp2, float64_to_floatx80(
+ make_float64(0x3FE0000000000000), status),
+ status); /* A1+S*(A3+S*A5) */
+ fp3 = floatx80_mul(fp3, fp0, status); /* R*S*(A2+S*A4) */
+
+ fp2 = floatx80_mul(fp2, fp1, status); /* S*(A1+S*(A3+S*A5)) */
+ fp0 = floatx80_add(fp0, fp3, status); /* R+R*S*(A2+S*A4) */
+ fp0 = floatx80_add(fp0, fp2, status); /* EXP(R) - 1 */
+
+ fp0 = floatx80_mul(fp0, fact1, status);
+ fp0 = floatx80_add(fp0, fact2, status);
+ fp0 = floatx80_add(fp0, fact1, status);
+
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+
+ a = floatx80_mul(fp0, adjfact, status);
+
+ float_raise(float_flag_inexact, status);
+
+ return a;
+ }
+}
+
+/*----------------------------------------------------------------------------
+ | 10 to x
+ *----------------------------------------------------------------------------*/
+
+floatx80 floatx80_tentox(floatx80 a, float_status *status)
+{
+ flag aSign;
+ int32_t aExp;
+ uint64_t aSig;
+
+ int8_t user_rnd_mode, user_rnd_prec;
+
+ int32_t compact, n, j, l, m, m1;
+ floatx80 fp0, fp1, fp2, fp3, adjfact, fact1, fact2;
+
+ aSig = extractFloatx80Frac(a);
+ aExp = extractFloatx80Exp(a);
+ aSign = extractFloatx80Sign(a);
+
+ if (aExp == 0x7FFF) {
+ if ((uint64_t) (aSig << 1)) {
+ return propagateFloatx80NaNOneArg(a, status);
+ }
+ if (aSign) {
+ return packFloatx80(0, 0, 0);
+ }
+ return packFloatx80(0, floatx80_infinity.high,
+ floatx80_infinity.low);
+ }
+
+ if (aExp == 0 && aSig == 0) {
+ return packFloatx80(0, one_exp, one_sig);
+ }
+
+ user_rnd_mode = status->float_rounding_mode;
+ user_rnd_prec = status->floatx80_rounding_precision;
+ status->float_rounding_mode = float_round_nearest_even;
+ status->floatx80_rounding_precision = 80;
+
+ fp0 = a;
+
+ compact = floatx80_make_compact(aExp, aSig);
+
+ if (compact < 0x3FB98000 || compact > 0x400B9B07) {
+ /* |X| > 16480 LOG2/LOG10 or |X| < 2^(-70) */
+ if (compact > 0x3FFF8000) { /* |X| > 16480 */
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+
+ if (aSign) {
+ return roundAndPackFloatx80(status->floatx80_rounding_precision,
+ 0, -0x1000, aSig, 0, status);
+ } else {
+ return roundAndPackFloatx80(status->floatx80_rounding_precision,
+ 0, 0x8000, aSig, 0, status);
+ }
+ } else { /* |X| < 2^(-70) */
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+
+ a = floatx80_add(fp0, float32_to_floatx80(
+ make_float32(0x3F800000), status),
+ status); /* 1 + X */
+
+ float_raise(float_flag_inexact, status);
+
+ return a;
+ }
+ } else { /* 2^(-70) <= |X| <= 16480 LOG 2 / LOG 10 */
+ fp1 = fp0; /* X */
+ fp1 = floatx80_mul(fp1, float64_to_floatx80(
+ make_float64(0x406A934F0979A371),
+ status), status); /* X*64*LOG10/LOG2 */
+ n = floatx80_to_int32(fp1, status); /* N=INT(X*64*LOG10/LOG2) */
+ fp1 = int32_to_floatx80(n, status);
+
+ j = n & 0x3F;
+ l = n / 64; /* NOTE: this is really arithmetic right shift by 6 */
+ if (n < 0 && j) {
+ /* arithmetic right shift is division and
+ * round towards minus infinity
+ */
+ l--;
+ }
+ m = l / 2; /* NOTE: this is really arithmetic right shift by 1 */
+ if (l < 0 && (l & 1)) {
+ /* arithmetic right shift is division and
+ * round towards minus infinity
+ */
+ m--;
+ }
+ m1 = l - m;
+ m1 += 0x3FFF; /* ADJFACT IS 2^(M') */
+
+ adjfact = packFloatx80(0, m1, one_sig);
+ fact1 = exp2_tbl[j];
+ fact1.high += m;
+ fact2.high = exp2_tbl2[j] >> 16;
+ fact2.high += m;
+ fact2.low = (uint64_t)(exp2_tbl2[j] & 0xFFFF);
+ fact2.low <<= 48;
+
+ fp2 = fp1; /* N */
+ fp1 = floatx80_mul(fp1, float64_to_floatx80(
+ make_float64(0x3F734413509F8000), status),
+ status); /* N*(LOG2/64LOG10)_LEAD */
+ fp3 = packFloatx80(1, 0x3FCD, LIT64(0xC0219DC1DA994FD2));
+ fp2 = floatx80_mul(fp2, fp3, status); /* N*(LOG2/64LOG10)_TRAIL */
+ fp0 = floatx80_sub(fp0, fp1, status); /* X - N L_LEAD */
+ fp0 = floatx80_sub(fp0, fp2, status); /* X - N L_TRAIL */
+ fp2 = packFloatx80(0, 0x4000, LIT64(0x935D8DDDAAA8AC17)); /* LOG10 */
+ fp0 = floatx80_mul(fp0, fp2, status); /* R */
+
+ /* EXPR */
+ fp1 = floatx80_mul(fp0, fp0, status); /* S = R*R */
+ fp2 = float64_to_floatx80(make_float64(0x3F56C16D6F7BD0B2),
+ status); /* A5 */
+ fp3 = float64_to_floatx80(make_float64(0x3F811112302C712C),
+ status); /* A4 */
+ fp2 = floatx80_mul(fp2, fp1, status); /* S*A5 */
+ fp3 = floatx80_mul(fp3, fp1, status); /* S*A4 */
+ fp2 = floatx80_add(fp2, float64_to_floatx80(
+ make_float64(0x3FA5555555554CC1), status),
+ status); /* A3+S*A5 */
+ fp3 = floatx80_add(fp3, float64_to_floatx80(
+ make_float64(0x3FC5555555554A54), status),
+ status); /* A2+S*A4 */
+ fp2 = floatx80_mul(fp2, fp1, status); /* S*(A3+S*A5) */
+ fp3 = floatx80_mul(fp3, fp1, status); /* S*(A2+S*A4) */
+ fp2 = floatx80_add(fp2, float64_to_floatx80(
+ make_float64(0x3FE0000000000000), status),
+ status); /* A1+S*(A3+S*A5) */
+ fp3 = floatx80_mul(fp3, fp0, status); /* R*S*(A2+S*A4) */
+
+ fp2 = floatx80_mul(fp2, fp1, status); /* S*(A1+S*(A3+S*A5)) */
+ fp0 = floatx80_add(fp0, fp3, status); /* R+R*S*(A2+S*A4) */
+ fp0 = floatx80_add(fp0, fp2, status); /* EXP(R) - 1 */
+
+ fp0 = floatx80_mul(fp0, fact1, status);
+ fp0 = floatx80_add(fp0, fact2, status);
+ fp0 = floatx80_add(fp0, fact1, status);
+
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+
+ a = floatx80_mul(fp0, adjfact, status);
+
+ float_raise(float_flag_inexact, status);
+
+ return a;
+ }
+}
diff --git a/target/m68k/softfloat.h b/target/m68k/softfloat.h
index 78fbc0cd0c..d28e49fe9f 100644
--- a/target/m68k/softfloat.h
+++ b/target/m68k/softfloat.h
@@ -26,4 +26,12 @@ floatx80 floatx80_mod(floatx80 a, floatx80 b, float_status *status);
floatx80 floatx80_getman(floatx80 a, float_status *status);
floatx80 floatx80_getexp(floatx80 a, float_status *status);
floatx80 floatx80_scale(floatx80 a, floatx80 b, float_status *status);
+floatx80 floatx80_move(floatx80 a, float_status *status);
+floatx80 floatx80_lognp1(floatx80 a, float_status *status);
+floatx80 floatx80_logn(floatx80 a, float_status *status);
+floatx80 floatx80_log10(floatx80 a, float_status *status);
+floatx80 floatx80_log2(floatx80 a, float_status *status);
+floatx80 floatx80_etox(floatx80 a, float_status *status);
+floatx80 floatx80_twotox(floatx80 a, float_status *status);
+floatx80 floatx80_tentox(floatx80 a, float_status *status);
#endif
diff --git a/target/m68k/softfloat_fpsp_tables.h b/target/m68k/softfloat_fpsp_tables.h
new file mode 100644
index 0000000000..dd76dc0373
--- /dev/null
+++ b/target/m68k/softfloat_fpsp_tables.h
@@ -0,0 +1,374 @@
+/*
+ * Ported from a work by Andreas Grabher for Previous, NeXT Computer Emulator,
+ * derived from NetBSD M68040 FPSP functions,
+ * derived from release 2a of the SoftFloat IEC/IEEE Floating-point Arithmetic
+ * Package. Those parts of the code (and some later contributions) are
+ * provided under that license, as detailed below.
+ * It has subsequently been modified by contributors to the QEMU Project,
+ * so some portions are provided under:
+ * the SoftFloat-2a license
+ * the BSD license
+ * GPL-v2-or-later
+ *
+ * Any future contributions to this file will be taken to be licensed under
+ * the Softfloat-2a license unless specifically indicated otherwise.
+ */
+
+/* Portions of this work are licensed under the terms of the GNU GPL,
+ * version 2 or later. See the COPYING file in the top-level directory.
+ */
+
+#ifndef TARGET_M68K_SOFTFLOAT_FPSP_TABLES_H
+#define TARGET_M68K_SOFTFLOAT_FPSP_TABLES_H
+
+static const floatx80 log_tbl[128] = {
+ make_floatx80_init(0x3FFE, 0xFE03F80FE03F80FE),
+ make_floatx80_init(0x3FF7, 0xFF015358833C47E2),
+ make_floatx80_init(0x3FFE, 0xFA232CF252138AC0),
+ make_floatx80_init(0x3FF9, 0xBDC8D83EAD88D549),
+ make_floatx80_init(0x3FFE, 0xF6603D980F6603DA),
+ make_floatx80_init(0x3FFA, 0x9CF43DCFF5EAFD48),
+ make_floatx80_init(0x3FFE, 0xF2B9D6480F2B9D65),
+ make_floatx80_init(0x3FFA, 0xDA16EB88CB8DF614),
+ make_floatx80_init(0x3FFE, 0xEF2EB71FC4345238),
+ make_floatx80_init(0x3FFB, 0x8B29B7751BD70743),
+ make_floatx80_init(0x3FFE, 0xEBBDB2A5C1619C8C),
+ make_floatx80_init(0x3FFB, 0xA8D839F830C1FB49),
+ make_floatx80_init(0x3FFE, 0xE865AC7B7603A197),
+ make_floatx80_init(0x3FFB, 0xC61A2EB18CD907AD),
+ make_floatx80_init(0x3FFE, 0xE525982AF70C880E),
+ make_floatx80_init(0x3FFB, 0xE2F2A47ADE3A18AF),
+ make_floatx80_init(0x3FFE, 0xE1FC780E1FC780E2),
+ make_floatx80_init(0x3FFB, 0xFF64898EDF55D551),
+ make_floatx80_init(0x3FFE, 0xDEE95C4CA037BA57),
+ make_floatx80_init(0x3FFC, 0x8DB956A97B3D0148),
+ make_floatx80_init(0x3FFE, 0xDBEB61EED19C5958),
+ make_floatx80_init(0x3FFC, 0x9B8FE100F47BA1DE),
+ make_floatx80_init(0x3FFE, 0xD901B2036406C80E),
+ make_floatx80_init(0x3FFC, 0xA9372F1D0DA1BD17),
+ make_floatx80_init(0x3FFE, 0xD62B80D62B80D62C),
+ make_floatx80_init(0x3FFC, 0xB6B07F38CE90E46B),
+ make_floatx80_init(0x3FFE, 0xD3680D3680D3680D),
+ make_floatx80_init(0x3FFC, 0xC3FD032906488481),
+ make_floatx80_init(0x3FFE, 0xD0B69FCBD2580D0B),
+ make_floatx80_init(0x3FFC, 0xD11DE0FF15AB18CA),
+ make_floatx80_init(0x3FFE, 0xCE168A7725080CE1),
+ make_floatx80_init(0x3FFC, 0xDE1433A16C66B150),
+ make_floatx80_init(0x3FFE, 0xCB8727C065C393E0),
+ make_floatx80_init(0x3FFC, 0xEAE10B5A7DDC8ADD),
+ make_floatx80_init(0x3FFE, 0xC907DA4E871146AD),
+ make_floatx80_init(0x3FFC, 0xF7856E5EE2C9B291),
+ make_floatx80_init(0x3FFE, 0xC6980C6980C6980C),
+ make_floatx80_init(0x3FFD, 0x82012CA5A68206D7),
+ make_floatx80_init(0x3FFE, 0xC4372F855D824CA6),
+ make_floatx80_init(0x3FFD, 0x882C5FCD7256A8C5),
+ make_floatx80_init(0x3FFE, 0xC1E4BBD595F6E947),
+ make_floatx80_init(0x3FFD, 0x8E44C60B4CCFD7DE),
+ make_floatx80_init(0x3FFE, 0xBFA02FE80BFA02FF),
+ make_floatx80_init(0x3FFD, 0x944AD09EF4351AF6),
+ make_floatx80_init(0x3FFE, 0xBD69104707661AA3),
+ make_floatx80_init(0x3FFD, 0x9A3EECD4C3EAA6B2),
+ make_floatx80_init(0x3FFE, 0xBB3EE721A54D880C),
+ make_floatx80_init(0x3FFD, 0xA0218434353F1DE8),
+ make_floatx80_init(0x3FFE, 0xB92143FA36F5E02E),
+ make_floatx80_init(0x3FFD, 0xA5F2FCABBBC506DA),
+ make_floatx80_init(0x3FFE, 0xB70FBB5A19BE3659),
+ make_floatx80_init(0x3FFD, 0xABB3B8BA2AD362A5),
+ make_floatx80_init(0x3FFE, 0xB509E68A9B94821F),
+ make_floatx80_init(0x3FFD, 0xB1641795CE3CA97B),
+ make_floatx80_init(0x3FFE, 0xB30F63528917C80B),
+ make_floatx80_init(0x3FFD, 0xB70475515D0F1C61),
+ make_floatx80_init(0x3FFE, 0xB11FD3B80B11FD3C),
+ make_floatx80_init(0x3FFD, 0xBC952AFEEA3D13E1),
+ make_floatx80_init(0x3FFE, 0xAF3ADDC680AF3ADE),
+ make_floatx80_init(0x3FFD, 0xC2168ED0F458BA4A),
+ make_floatx80_init(0x3FFE, 0xAD602B580AD602B6),
+ make_floatx80_init(0x3FFD, 0xC788F439B3163BF1),
+ make_floatx80_init(0x3FFE, 0xAB8F69E28359CD11),
+ make_floatx80_init(0x3FFD, 0xCCECAC08BF04565D),
+ make_floatx80_init(0x3FFE, 0xA9C84A47A07F5638),
+ make_floatx80_init(0x3FFD, 0xD24204872DD85160),
+ make_floatx80_init(0x3FFE, 0xA80A80A80A80A80B),
+ make_floatx80_init(0x3FFD, 0xD78949923BC3588A),
+ make_floatx80_init(0x3FFE, 0xA655C4392D7B73A8),
+ make_floatx80_init(0x3FFD, 0xDCC2C4B49887DACC),
+ make_floatx80_init(0x3FFE, 0xA4A9CF1D96833751),
+ make_floatx80_init(0x3FFD, 0xE1EEBD3E6D6A6B9E),
+ make_floatx80_init(0x3FFE, 0xA3065E3FAE7CD0E0),
+ make_floatx80_init(0x3FFD, 0xE70D785C2F9F5BDC),
+ make_floatx80_init(0x3FFE, 0xA16B312EA8FC377D),
+ make_floatx80_init(0x3FFD, 0xEC1F392C5179F283),
+ make_floatx80_init(0x3FFE, 0x9FD809FD809FD80A),
+ make_floatx80_init(0x3FFD, 0xF12440D3E36130E6),
+ make_floatx80_init(0x3FFE, 0x9E4CAD23DD5F3A20),
+ make_floatx80_init(0x3FFD, 0xF61CCE92346600BB),
+ make_floatx80_init(0x3FFE, 0x9CC8E160C3FB19B9),
+ make_floatx80_init(0x3FFD, 0xFB091FD38145630A),
+ make_floatx80_init(0x3FFE, 0x9B4C6F9EF03A3CAA),
+ make_floatx80_init(0x3FFD, 0xFFE97042BFA4C2AD),
+ make_floatx80_init(0x3FFE, 0x99D722DABDE58F06),
+ make_floatx80_init(0x3FFE, 0x825EFCED49369330),
+ make_floatx80_init(0x3FFE, 0x9868C809868C8098),
+ make_floatx80_init(0x3FFE, 0x84C37A7AB9A905C9),
+ make_floatx80_init(0x3FFE, 0x97012E025C04B809),
+ make_floatx80_init(0x3FFE, 0x87224C2E8E645FB7),
+ make_floatx80_init(0x3FFE, 0x95A02568095A0257),
+ make_floatx80_init(0x3FFE, 0x897B8CAC9F7DE298),
+ make_floatx80_init(0x3FFE, 0x9445809445809446),
+ make_floatx80_init(0x3FFE, 0x8BCF55DEC4CD05FE),
+ make_floatx80_init(0x3FFE, 0x92F113840497889C),
+ make_floatx80_init(0x3FFE, 0x8E1DC0FB89E125E5),
+ make_floatx80_init(0x3FFE, 0x91A2B3C4D5E6F809),
+ make_floatx80_init(0x3FFE, 0x9066E68C955B6C9B),
+ make_floatx80_init(0x3FFE, 0x905A38633E06C43B),
+ make_floatx80_init(0x3FFE, 0x92AADE74C7BE59E0),
+ make_floatx80_init(0x3FFE, 0x8F1779D9FDC3A219),
+ make_floatx80_init(0x3FFE, 0x94E9BFF615845643),
+ make_floatx80_init(0x3FFE, 0x8DDA520237694809),
+ make_floatx80_init(0x3FFE, 0x9723A1B720134203),
+ make_floatx80_init(0x3FFE, 0x8CA29C046514E023),
+ make_floatx80_init(0x3FFE, 0x995899C890EB8990),
+ make_floatx80_init(0x3FFE, 0x8B70344A139BC75A),
+ make_floatx80_init(0x3FFE, 0x9B88BDAA3A3DAE2F),
+ make_floatx80_init(0x3FFE, 0x8A42F8705669DB46),
+ make_floatx80_init(0x3FFE, 0x9DB4224FFFE1157C),
+ make_floatx80_init(0x3FFE, 0x891AC73AE9819B50),
+ make_floatx80_init(0x3FFE, 0x9FDADC268B7A12DA),
+ make_floatx80_init(0x3FFE, 0x87F78087F78087F8),
+ make_floatx80_init(0x3FFE, 0xA1FCFF17CE733BD4),
+ make_floatx80_init(0x3FFE, 0x86D905447A34ACC6),
+ make_floatx80_init(0x3FFE, 0xA41A9E8F5446FB9F),
+ make_floatx80_init(0x3FFE, 0x85BF37612CEE3C9B),
+ make_floatx80_init(0x3FFE, 0xA633CD7E6771CD8B),
+ make_floatx80_init(0x3FFE, 0x84A9F9C8084A9F9D),
+ make_floatx80_init(0x3FFE, 0xA8489E600B435A5E),
+ make_floatx80_init(0x3FFE, 0x839930523FBE3368),
+ make_floatx80_init(0x3FFE, 0xAA59233CCCA4BD49),
+ make_floatx80_init(0x3FFE, 0x828CBFBEB9A020A3),
+ make_floatx80_init(0x3FFE, 0xAC656DAE6BCC4985),
+ make_floatx80_init(0x3FFE, 0x81848DA8FAF0D277),
+ make_floatx80_init(0x3FFE, 0xAE6D8EE360BB2468),
+ make_floatx80_init(0x3FFE, 0x8080808080808081),
+ make_floatx80_init(0x3FFE, 0xB07197A23C46C654)
+};
+
+static const floatx80 exp_tbl[64] = {
+ make_floatx80_init(0x3FFF, 0x8000000000000000),
+ make_floatx80_init(0x3FFF, 0x8164D1F3BC030774),
+ make_floatx80_init(0x3FFF, 0x82CD8698AC2BA1D8),
+ make_floatx80_init(0x3FFF, 0x843A28C3ACDE4048),
+ make_floatx80_init(0x3FFF, 0x85AAC367CC487B14),
+ make_floatx80_init(0x3FFF, 0x871F61969E8D1010),
+ make_floatx80_init(0x3FFF, 0x88980E8092DA8528),
+ make_floatx80_init(0x3FFF, 0x8A14D575496EFD9C),
+ make_floatx80_init(0x3FFF, 0x8B95C1E3EA8BD6E8),
+ make_floatx80_init(0x3FFF, 0x8D1ADF5B7E5BA9E4),
+ make_floatx80_init(0x3FFF, 0x8EA4398B45CD53C0),
+ make_floatx80_init(0x3FFF, 0x9031DC431466B1DC),
+ make_floatx80_init(0x3FFF, 0x91C3D373AB11C338),
+ make_floatx80_init(0x3FFF, 0x935A2B2F13E6E92C),
+ make_floatx80_init(0x3FFF, 0x94F4EFA8FEF70960),
+ make_floatx80_init(0x3FFF, 0x96942D3720185A00),
+ make_floatx80_init(0x3FFF, 0x9837F0518DB8A970),
+ make_floatx80_init(0x3FFF, 0x99E0459320B7FA64),
+ make_floatx80_init(0x3FFF, 0x9B8D39B9D54E5538),
+ make_floatx80_init(0x3FFF, 0x9D3ED9A72CFFB750),
+ make_floatx80_init(0x3FFF, 0x9EF5326091A111AC),
+ make_floatx80_init(0x3FFF, 0xA0B0510FB9714FC4),
+ make_floatx80_init(0x3FFF, 0xA27043030C496818),
+ make_floatx80_init(0x3FFF, 0xA43515AE09E680A0),
+ make_floatx80_init(0x3FFF, 0xA5FED6A9B15138EC),
+ make_floatx80_init(0x3FFF, 0xA7CD93B4E9653568),
+ make_floatx80_init(0x3FFF, 0xA9A15AB4EA7C0EF8),
+ make_floatx80_init(0x3FFF, 0xAB7A39B5A93ED338),
+ make_floatx80_init(0x3FFF, 0xAD583EEA42A14AC8),
+ make_floatx80_init(0x3FFF, 0xAF3B78AD690A4374),
+ make_floatx80_init(0x3FFF, 0xB123F581D2AC2590),
+ make_floatx80_init(0x3FFF, 0xB311C412A9112488),
+ make_floatx80_init(0x3FFF, 0xB504F333F9DE6484),
+ make_floatx80_init(0x3FFF, 0xB6FD91E328D17790),
+ make_floatx80_init(0x3FFF, 0xB8FBAF4762FB9EE8),
+ make_floatx80_init(0x3FFF, 0xBAFF5AB2133E45FC),
+ make_floatx80_init(0x3FFF, 0xBD08A39F580C36C0),
+ make_floatx80_init(0x3FFF, 0xBF1799B67A731084),
+ make_floatx80_init(0x3FFF, 0xC12C4CCA66709458),
+ make_floatx80_init(0x3FFF, 0xC346CCDA24976408),
+ make_floatx80_init(0x3FFF, 0xC5672A115506DADC),
+ make_floatx80_init(0x3FFF, 0xC78D74C8ABB9B15C),
+ make_floatx80_init(0x3FFF, 0xC9B9BD866E2F27A4),
+ make_floatx80_init(0x3FFF, 0xCBEC14FEF2727C5C),
+ make_floatx80_init(0x3FFF, 0xCE248C151F8480E4),
+ make_floatx80_init(0x3FFF, 0xD06333DAEF2B2594),
+ make_floatx80_init(0x3FFF, 0xD2A81D91F12AE45C),
+ make_floatx80_init(0x3FFF, 0xD4F35AABCFEDFA20),
+ make_floatx80_init(0x3FFF, 0xD744FCCAD69D6AF4),
+ make_floatx80_init(0x3FFF, 0xD99D15C278AFD7B4),
+ make_floatx80_init(0x3FFF, 0xDBFBB797DAF23754),
+ make_floatx80_init(0x3FFF, 0xDE60F4825E0E9124),
+ make_floatx80_init(0x3FFF, 0xE0CCDEEC2A94E110),
+ make_floatx80_init(0x3FFF, 0xE33F8972BE8A5A50),
+ make_floatx80_init(0x3FFF, 0xE5B906E77C8348A8),
+ make_floatx80_init(0x3FFF, 0xE8396A503C4BDC68),
+ make_floatx80_init(0x3FFF, 0xEAC0C6E7DD243930),
+ make_floatx80_init(0x3FFF, 0xED4F301ED9942B84),
+ make_floatx80_init(0x3FFF, 0xEFE4B99BDCDAF5CC),
+ make_floatx80_init(0x3FFF, 0xF281773C59FFB138),
+ make_floatx80_init(0x3FFF, 0xF5257D152486CC2C),
+ make_floatx80_init(0x3FFF, 0xF7D0DF730AD13BB8),
+ make_floatx80_init(0x3FFF, 0xFA83B2DB722A033C),
+ make_floatx80_init(0x3FFF, 0xFD3E0C0CF486C174)
+};
+
+static const float32 exp_tbl2[64] = {
+ const_float32(0x00000000),
+ const_float32(0x9F841A9B),
+ const_float32(0x9FC1D5B9),
+ const_float32(0xA0728369),
+ const_float32(0x1FC5C95C),
+ const_float32(0x1EE85C9F),
+ const_float32(0x9FA20729),
+ const_float32(0xA07BF9AF),
+ const_float32(0xA0020DCF),
+ const_float32(0x205A63DA),
+ const_float32(0x1EB70051),
+ const_float32(0x1F6EB029),
+ const_float32(0xA0781494),
+ const_float32(0x9EB319B0),
+ const_float32(0x2017457D),
+ const_float32(0x1F11D537),
+ const_float32(0x9FB952DD),
+ const_float32(0x1FE43087),
+ const_float32(0x1FA2A818),
+ const_float32(0x1FDE494D),
+ const_float32(0x20504890),
+ const_float32(0xA073691C),
+ const_float32(0x1F9B7A05),
+ const_float32(0xA0797126),
+ const_float32(0xA071A140),
+ const_float32(0x204F62DA),
+ const_float32(0x1F283C4A),
+ const_float32(0x9F9A7FDC),
+ const_float32(0xA05B3FAC),
+ const_float32(0x1FDF2610),
+ const_float32(0x9F705F90),
+ const_float32(0x201F678A),
+ const_float32(0x1F32FB13),
+ const_float32(0x20038B30),
+ const_float32(0x200DC3CC),
+ const_float32(0x9F8B2AE6),
+ const_float32(0xA02BBF70),
+ const_float32(0xA00BF518),
+ const_float32(0xA041DD41),
+ const_float32(0x9FDF137B),
+ const_float32(0x201F1568),
+ const_float32(0x1FC13A2E),
+ const_float32(0xA03F8F03),
+ const_float32(0x1FF4907D),
+ const_float32(0x9E6E53E4),
+ const_float32(0x1FD6D45C),
+ const_float32(0xA076EDB9),
+ const_float32(0x9FA6DE21),
+ const_float32(0x1EE69A2F),
+ const_float32(0x207F439F),
+ const_float32(0x201EC207),
+ const_float32(0x9E8BE175),
+ const_float32(0x20032C4B),
+ const_float32(0x2004DFF5),
+ const_float32(0x1E72F47A),
+ const_float32(0x1F722F22),
+ const_float32(0xA017E945),
+ const_float32(0x1F401A5B),
+ const_float32(0x9FB9A9E3),
+ const_float32(0x20744C05),
+ const_float32(0x1F773A19),
+ const_float32(0x1FFE90D5),
+ const_float32(0xA041ED22),
+ const_float32(0x1F853F3A),
+};
+
+static const floatx80 exp2_tbl[64] = {
+ make_floatx80_init(0x3FFF, 0x8000000000000000),
+ make_floatx80_init(0x3FFF, 0x8164D1F3BC030773),
+ make_floatx80_init(0x3FFF, 0x82CD8698AC2BA1D7),
+ make_floatx80_init(0x3FFF, 0x843A28C3ACDE4046),
+ make_floatx80_init(0x3FFF, 0x85AAC367CC487B15),
+ make_floatx80_init(0x3FFF, 0x871F61969E8D1010),
+ make_floatx80_init(0x3FFF, 0x88980E8092DA8527),
+ make_floatx80_init(0x3FFF, 0x8A14D575496EFD9A),
+ make_floatx80_init(0x3FFF, 0x8B95C1E3EA8BD6E7),
+ make_floatx80_init(0x3FFF, 0x8D1ADF5B7E5BA9E6),
+ make_floatx80_init(0x3FFF, 0x8EA4398B45CD53C0),
+ make_floatx80_init(0x3FFF, 0x9031DC431466B1DC),
+ make_floatx80_init(0x3FFF, 0x91C3D373AB11C336),
+ make_floatx80_init(0x3FFF, 0x935A2B2F13E6E92C),
+ make_floatx80_init(0x3FFF, 0x94F4EFA8FEF70961),
+ make_floatx80_init(0x3FFF, 0x96942D3720185A00),
+ make_floatx80_init(0x3FFF, 0x9837F0518DB8A96F),
+ make_floatx80_init(0x3FFF, 0x99E0459320B7FA65),
+ make_floatx80_init(0x3FFF, 0x9B8D39B9D54E5539),
+ make_floatx80_init(0x3FFF, 0x9D3ED9A72CFFB751),
+ make_floatx80_init(0x3FFF, 0x9EF5326091A111AE),
+ make_floatx80_init(0x3FFF, 0xA0B0510FB9714FC2),
+ make_floatx80_init(0x3FFF, 0xA27043030C496819),
+ make_floatx80_init(0x3FFF, 0xA43515AE09E6809E),
+ make_floatx80_init(0x3FFF, 0xA5FED6A9B15138EA),
+ make_floatx80_init(0x3FFF, 0xA7CD93B4E965356A),
+ make_floatx80_init(0x3FFF, 0xA9A15AB4EA7C0EF8),
+ make_floatx80_init(0x3FFF, 0xAB7A39B5A93ED337),
+ make_floatx80_init(0x3FFF, 0xAD583EEA42A14AC6),
+ make_floatx80_init(0x3FFF, 0xAF3B78AD690A4375),
+ make_floatx80_init(0x3FFF, 0xB123F581D2AC2590),
+ make_floatx80_init(0x3FFF, 0xB311C412A9112489),
+ make_floatx80_init(0x3FFF, 0xB504F333F9DE6484),
+ make_floatx80_init(0x3FFF, 0xB6FD91E328D17791),
+ make_floatx80_init(0x3FFF, 0xB8FBAF4762FB9EE9),
+ make_floatx80_init(0x3FFF, 0xBAFF5AB2133E45FB),
+ make_floatx80_init(0x3FFF, 0xBD08A39F580C36BF),
+ make_floatx80_init(0x3FFF, 0xBF1799B67A731083),
+ make_floatx80_init(0x3FFF, 0xC12C4CCA66709456),
+ make_floatx80_init(0x3FFF, 0xC346CCDA24976407),
+ make_floatx80_init(0x3FFF, 0xC5672A115506DADD),
+ make_floatx80_init(0x3FFF, 0xC78D74C8ABB9B15D),
+ make_floatx80_init(0x3FFF, 0xC9B9BD866E2F27A3),
+ make_floatx80_init(0x3FFF, 0xCBEC14FEF2727C5D),
+ make_floatx80_init(0x3FFF, 0xCE248C151F8480E4),
+ make_floatx80_init(0x3FFF, 0xD06333DAEF2B2595),
+ make_floatx80_init(0x3FFF, 0xD2A81D91F12AE45A),
+ make_floatx80_init(0x3FFF, 0xD4F35AABCFEDFA1F),
+ make_floatx80_init(0x3FFF, 0xD744FCCAD69D6AF4),
+ make_floatx80_init(0x3FFF, 0xD99D15C278AFD7B6),
+ make_floatx80_init(0x3FFF, 0xDBFBB797DAF23755),
+ make_floatx80_init(0x3FFF, 0xDE60F4825E0E9124),
+ make_floatx80_init(0x3FFF, 0xE0CCDEEC2A94E111),
+ make_floatx80_init(0x3FFF, 0xE33F8972BE8A5A51),
+ make_floatx80_init(0x3FFF, 0xE5B906E77C8348A8),
+ make_floatx80_init(0x3FFF, 0xE8396A503C4BDC68),
+ make_floatx80_init(0x3FFF, 0xEAC0C6E7DD24392F),
+ make_floatx80_init(0x3FFF, 0xED4F301ED9942B84),
+ make_floatx80_init(0x3FFF, 0xEFE4B99BDCDAF5CB),
+ make_floatx80_init(0x3FFF, 0xF281773C59FFB13A),
+ make_floatx80_init(0x3FFF, 0xF5257D152486CC2C),
+ make_floatx80_init(0x3FFF, 0xF7D0DF730AD13BB9),
+ make_floatx80_init(0x3FFF, 0xFA83B2DB722A033A),
+ make_floatx80_init(0x3FFF, 0xFD3E0C0CF486C175)
+};
+
+static const uint32_t exp2_tbl2[64] = {
+ 0x3F738000, 0x3FBEF7CA, 0x3FBDF8A9, 0x3FBCD7C9,
+ 0xBFBDE8DA, 0x3FBDE85C, 0x3FBEBBF1, 0x3FBB80CA,
+ 0xBFBA8373, 0xBFBE9670, 0x3FBDB700, 0x3FBEEEB0,
+ 0x3FBBFD6D, 0xBFBDB319, 0x3FBDBA2B, 0x3FBE91D5,
+ 0x3FBE8D5A, 0xBFBCDE7B, 0xBFBEBAAF, 0xBFBD86DA,
+ 0xBFBEBEDD, 0x3FBCC96E, 0xBFBEC90B, 0x3FBBD1DB,
+ 0x3FBCE5EB, 0xBFBEC274, 0x3FBEA83C, 0x3FBECB00,
+ 0x3FBE9301, 0xBFBD8367, 0xBFBEF05F, 0x3FBDFB3C,
+ 0x3FBEB2FB, 0x3FBAE2CB, 0x3FBCDC3C, 0x3FBEE9AA,
+ 0xBFBEAEFD, 0xBFBCBF51, 0x3FBEF88A, 0x3FBD83B2,
+ 0x3FBDF8AB, 0xBFBDFB17, 0xBFBEFE3C, 0xBFBBB6F8,
+ 0xBFBCEE53, 0xBFBDA4AE, 0x3FBC9124, 0x3FBEB243,
+ 0x3FBDE69A, 0xBFB8BC61, 0x3FBDF610, 0xBFBD8BE1,
+ 0x3FBACB12, 0x3FBB9BFE, 0x3FBCF2F4, 0x3FBEF22F,
+ 0xBFBDBF4A, 0x3FBEC01A, 0x3FBE8CAC, 0xBFBCBB3F,
+ 0x3FBEF73A, 0xBFB8B795, 0x3FBEF84B, 0xBFBEF581
+};
+#endif
diff --git a/target/m68k/translate.c b/target/m68k/translate.c
index dbb24f8d84..6d5bde0777 100644
--- a/target/m68k/translate.c
+++ b/target/m68k/translate.c
@@ -5054,6 +5054,27 @@ DISAS_INSN(fpu)
case 0x45: /* fdsqrt */
gen_helper_fdsqrt(cpu_env, cpu_dest, cpu_src);
break;
+ case 0x06: /* flognp1 */
+ gen_helper_flognp1(cpu_env, cpu_dest, cpu_src);
+ break;
+ case 0x10: /* fetox */
+ gen_helper_fetox(cpu_env, cpu_dest, cpu_src);
+ break;
+ case 0x11: /* ftwotox */
+ gen_helper_ftwotox(cpu_env, cpu_dest, cpu_src);
+ break;
+ case 0x12: /* ftentox */
+ gen_helper_ftentox(cpu_env, cpu_dest, cpu_src);
+ break;
+ case 0x14: /* flogn */
+ gen_helper_flogn(cpu_env, cpu_dest, cpu_src);
+ break;
+ case 0x15: /* flog10 */
+ gen_helper_flog10(cpu_env, cpu_dest, cpu_src);
+ break;
+ case 0x16: /* flog2 */
+ gen_helper_flog2(cpu_env, cpu_dest, cpu_src);
+ break;
case 0x18: /* fabs */
gen_helper_fabs(cpu_env, cpu_dest, cpu_src);
break;
diff --git a/tests/check-qdict.c b/tests/check-qdict.c
index ec628f3453..a3faea8bfc 100644
--- a/tests/check-qdict.c
+++ b/tests/check-qdict.c
@@ -665,6 +665,133 @@ static void qdict_crumple_test_empty(void)
QDECREF(dst);
}
+static int qdict_count_entries(QDict *dict)
+{
+ const QDictEntry *e;
+ int count = 0;
+
+ for (e = qdict_first(dict); e; e = qdict_next(dict, e)) {
+ count++;
+ }
+
+ return count;
+}
+
+static void qdict_rename_keys_test(void)
+{
+ QDict *dict = qdict_new();
+ QDict *copy;
+ QDictRenames *renames;
+ Error *local_err = NULL;
+
+ qdict_put_str(dict, "abc", "foo");
+ qdict_put_str(dict, "abcdef", "bar");
+ qdict_put_int(dict, "number", 42);
+ qdict_put_bool(dict, "flag", true);
+ qdict_put_null(dict, "nothing");
+
+ /* Empty rename list */
+ renames = (QDictRenames[]) {
+ { NULL, "this can be anything" }
+ };
+ copy = qdict_clone_shallow(dict);
+ qdict_rename_keys(copy, renames, &error_abort);
+
+ g_assert_cmpstr(qdict_get_str(copy, "abc"), ==, "foo");
+ g_assert_cmpstr(qdict_get_str(copy, "abcdef"), ==, "bar");
+ g_assert_cmpint(qdict_get_int(copy, "number"), ==, 42);
+ g_assert_cmpint(qdict_get_bool(copy, "flag"), ==, true);
+ g_assert(qobject_type(qdict_get(copy, "nothing")) == QTYPE_QNULL);
+ g_assert_cmpint(qdict_count_entries(copy), ==, 5);
+
+ QDECREF(copy);
+
+ /* Simple rename of all entries */
+ renames = (QDictRenames[]) {
+ { "abc", "str1" },
+ { "abcdef", "str2" },
+ { "number", "int" },
+ { "flag", "bool" },
+ { "nothing", "null" },
+ { NULL , NULL }
+ };
+ copy = qdict_clone_shallow(dict);
+ qdict_rename_keys(copy, renames, &error_abort);
+
+ g_assert(!qdict_haskey(copy, "abc"));
+ g_assert(!qdict_haskey(copy, "abcdef"));
+ g_assert(!qdict_haskey(copy, "number"));
+ g_assert(!qdict_haskey(copy, "flag"));
+ g_assert(!qdict_haskey(copy, "nothing"));
+
+ g_assert_cmpstr(qdict_get_str(copy, "str1"), ==, "foo");
+ g_assert_cmpstr(qdict_get_str(copy, "str2"), ==, "bar");
+ g_assert_cmpint(qdict_get_int(copy, "int"), ==, 42);
+ g_assert_cmpint(qdict_get_bool(copy, "bool"), ==, true);
+ g_assert(qobject_type(qdict_get(copy, "null")) == QTYPE_QNULL);
+ g_assert_cmpint(qdict_count_entries(copy), ==, 5);
+
+ QDECREF(copy);
+
+ /* Renames are processed top to bottom */
+ renames = (QDictRenames[]) {
+ { "abc", "tmp" },
+ { "abcdef", "abc" },
+ { "number", "abcdef" },
+ { "flag", "number" },
+ { "nothing", "flag" },
+ { "tmp", "nothing" },
+ { NULL , NULL }
+ };
+ copy = qdict_clone_shallow(dict);
+ qdict_rename_keys(copy, renames, &error_abort);
+
+ g_assert_cmpstr(qdict_get_str(copy, "nothing"), ==, "foo");
+ g_assert_cmpstr(qdict_get_str(copy, "abc"), ==, "bar");
+ g_assert_cmpint(qdict_get_int(copy, "abcdef"), ==, 42);
+ g_assert_cmpint(qdict_get_bool(copy, "number"), ==, true);
+ g_assert(qobject_type(qdict_get(copy, "flag")) == QTYPE_QNULL);
+ g_assert(!qdict_haskey(copy, "tmp"));
+ g_assert_cmpint(qdict_count_entries(copy), ==, 5);
+
+ QDECREF(copy);
+
+ /* Conflicting rename */
+ renames = (QDictRenames[]) {
+ { "abcdef", "abc" },
+ { NULL , NULL }
+ };
+ copy = qdict_clone_shallow(dict);
+ qdict_rename_keys(copy, renames, &local_err);
+
+ g_assert(local_err != NULL);
+ error_free(local_err);
+ local_err = NULL;
+
+ g_assert_cmpstr(qdict_get_str(copy, "abc"), ==, "foo");
+ g_assert_cmpstr(qdict_get_str(copy, "abcdef"), ==, "bar");
+ g_assert_cmpint(qdict_get_int(copy, "number"), ==, 42);
+ g_assert_cmpint(qdict_get_bool(copy, "flag"), ==, true);
+ g_assert(qobject_type(qdict_get(copy, "nothing")) == QTYPE_QNULL);
+ g_assert_cmpint(qdict_count_entries(copy), ==, 5);
+
+ QDECREF(copy);
+
+ /* Renames in an empty dict */
+ renames = (QDictRenames[]) {
+ { "abcdef", "abc" },
+ { NULL , NULL }
+ };
+
+ QDECREF(dict);
+ dict = qdict_new();
+
+ qdict_rename_keys(dict, renames, &error_abort);
+ g_assert(qdict_first(dict) == NULL);
+
+ QDECREF(dict);
+}
+
static void qdict_crumple_test_bad_inputs(void)
{
QDict *src;
@@ -880,6 +1007,8 @@ int main(int argc, char **argv)
g_test_add_func("/public/crumple/bad_inputs",
qdict_crumple_test_bad_inputs);
+ g_test_add_func("/public/rename_keys", qdict_rename_keys_test);
+
/* The Big one */
if (g_test_slow()) {
g_test_add_func("/stress/test", qdict_stress_test);
diff --git a/tests/migration-test.c b/tests/migration-test.c
index 74f9361bdd..422bf1afdf 100644
--- a/tests/migration-test.c
+++ b/tests/migration-test.c
@@ -382,7 +382,7 @@ static void migrate_start_postcopy(QTestState *who)
}
static void test_migrate_start(QTestState **from, QTestState **to,
- const char *uri)
+ const char *uri, bool hide_stderr)
{
gchar *cmd_src, *cmd_dst;
char *bootpath = g_strdup_printf("%s/bootsect", tmpfs);
@@ -427,6 +427,17 @@ static void test_migrate_start(QTestState **from, QTestState **to,
g_free(bootpath);
+ if (hide_stderr) {
+ gchar *tmp;
+ tmp = g_strdup_printf("%s 2>/dev/null", cmd_src);
+ g_free(cmd_src);
+ cmd_src = tmp;
+
+ tmp = g_strdup_printf("%s 2>/dev/null", cmd_dst);
+ g_free(cmd_dst);
+ cmd_dst = tmp;
+ }
+
*from = qtest_start(cmd_src);
g_free(cmd_src);
@@ -518,7 +529,7 @@ static void test_migrate(void)
char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs);
QTestState *from, *to;
- test_migrate_start(&from, &to, uri);
+ test_migrate_start(&from, &to, uri, false);
migrate_set_capability(from, "postcopy-ram", "true");
migrate_set_capability(to, "postcopy-ram", "true");
@@ -560,7 +571,7 @@ static void test_baddest(void)
const char *status;
bool failed;
- test_migrate_start(&from, &to, "tcp:0:0");
+ test_migrate_start(&from, &to, "tcp:0:0", true);
migrate(from, "tcp:0:0");
do {
rsp = wait_command(from, "{ 'execute': 'query-migrate' }");
diff --git a/tests/qemu-iotests/030 b/tests/qemu-iotests/030
index 457984b8e9..b5f88959aa 100755
--- a/tests/qemu-iotests/030
+++ b/tests/qemu-iotests/030
@@ -156,7 +156,7 @@ class TestSingleDrive(iotests.QMPTestCase):
class TestParallelOps(iotests.QMPTestCase):
num_ops = 4 # Number of parallel block-stream operations
num_imgs = num_ops * 2 + 1
- image_len = num_ops * 1024 * 1024
+ image_len = num_ops * 512 * 1024
imgs = []
def setUp(self):
@@ -176,14 +176,14 @@ class TestParallelOps(iotests.QMPTestCase):
'-o', 'backing_file=%s' % self.imgs[i-1], self.imgs[i])
# Put data into the images we are copying data from
- for i in range(self.num_imgs / 2):
- img_index = i * 2 + 1
- # Alternate between 512k and 1M.
+ odd_img_indexes = [x for x in reversed(range(self.num_imgs)) if x % 2 == 1]
+ for i in range(len(odd_img_indexes)):
+ # Alternate between 256KB and 512KB.
# This way jobs will not finish in the same order they were created
- num_kb = 512 + 512 * (i % 2)
+ num_kb = 256 + 256 * (i % 2)
qemu_io('-f', iotests.imgfmt,
- '-c', 'write -P %d %d %d' % (i, i*1024*1024, num_kb * 1024),
- self.imgs[img_index])
+ '-c', 'write -P 0xFF %dk %dk' % (i * 512, num_kb),
+ self.imgs[odd_img_indexes[i]])
# Attach the drive to the VM
self.vm = iotests.VM()
@@ -318,12 +318,14 @@ class TestParallelOps(iotests.QMPTestCase):
self.wait_until_completed(drive='commit-drive0')
# Test a block-stream and a block-commit job in parallel
- def test_stream_commit(self):
+ # Here the stream job is supposed to finish quickly in order to reproduce
+ # the scenario that triggers the bug fixed in 3d5d319e1221 and 1a63a907507
+ def test_stream_commit_1(self):
self.assertLessEqual(8, self.num_imgs)
self.assert_no_active_block_jobs()
# Stream from node0 into node2
- result = self.vm.qmp('block-stream', device='node2', job_id='node2')
+ result = self.vm.qmp('block-stream', device='node2', base_node='node0', job_id='node2')
self.assert_qmp(result, 'return', {})
# Commit from the active layer into node3
@@ -348,6 +350,38 @@ class TestParallelOps(iotests.QMPTestCase):
self.assert_no_active_block_jobs()
+ # This is similar to test_stream_commit_1 but both jobs are slowed
+ # down so they can run in parallel for a little while.
+ def test_stream_commit_2(self):
+ self.assertLessEqual(8, self.num_imgs)
+ self.assert_no_active_block_jobs()
+
+ # Stream from node0 into node4
+ result = self.vm.qmp('block-stream', device='node4', base_node='node0', job_id='node4', speed=1024*1024)
+ self.assert_qmp(result, 'return', {})
+
+ # Commit from the active layer into node5
+ result = self.vm.qmp('block-commit', device='drive0', base=self.imgs[5], speed=1024*1024)
+ self.assert_qmp(result, 'return', {})
+
+ # Wait for all jobs to be finished.
+ pending_jobs = ['node4', 'drive0']
+ while len(pending_jobs) > 0:
+ for event in self.vm.get_qmp_events(wait=True):
+ if event['event'] == 'BLOCK_JOB_COMPLETED':
+ node_name = self.dictpath(event, 'data/device')
+ self.assertTrue(node_name in pending_jobs)
+ self.assert_qmp_absent(event, 'data/error')
+ pending_jobs.remove(node_name)
+ if event['event'] == 'BLOCK_JOB_READY':
+ self.assert_qmp(event, 'data/device', 'drive0')
+ self.assert_qmp(event, 'data/type', 'commit')
+ self.assert_qmp_absent(event, 'data/error')
+ self.assertTrue('drive0' in pending_jobs)
+ self.vm.qmp('block-job-complete', device='drive0')
+
+ self.assert_no_active_block_jobs()
+
# Test the base_node parameter
def test_stream_base_node_name(self):
self.assert_no_active_block_jobs()
diff --git a/tests/qemu-iotests/030.out b/tests/qemu-iotests/030.out
index 391c8573ca..42314e9c00 100644
--- a/tests/qemu-iotests/030.out
+++ b/tests/qemu-iotests/030.out
@@ -1,5 +1,5 @@
-.......................
+........................
----------------------------------------------------------------------
-Ran 23 tests
+Ran 24 tests
OK
diff --git a/tests/qemu-iotests/049.out b/tests/qemu-iotests/049.out
index 003247023e..0871bff564 100644
--- a/tests/qemu-iotests/049.out
+++ b/tests/qemu-iotests/049.out
@@ -166,11 +166,11 @@ qemu-img create -f qcow2 -o compat=1.1 TEST_DIR/t.qcow2 64M
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat=1.1 cluster_size=65536 lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 -o compat=0.42 TEST_DIR/t.qcow2 64M
-qemu-img: TEST_DIR/t.qcow2: Invalid compatibility level: '0.42'
+qemu-img: TEST_DIR/t.qcow2: Invalid parameter '0.42'
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat=0.42 cluster_size=65536 lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 -o compat=foobar TEST_DIR/t.qcow2 64M
-qemu-img: TEST_DIR/t.qcow2: Invalid compatibility level: 'foobar'
+qemu-img: TEST_DIR/t.qcow2: Invalid parameter 'foobar'
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat=foobar cluster_size=65536 lazy_refcounts=off refcount_bits=16
== Check preallocation option ==
@@ -182,7 +182,7 @@ qemu-img create -f qcow2 -o preallocation=metadata TEST_DIR/t.qcow2 64M
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 cluster_size=65536 preallocation=metadata lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 -o preallocation=1234 TEST_DIR/t.qcow2 64M
-qemu-img: TEST_DIR/t.qcow2: invalid parameter value: 1234
+qemu-img: TEST_DIR/t.qcow2: Invalid parameter '1234'
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 cluster_size=65536 preallocation=1234 lazy_refcounts=off refcount_bits=16
== Check encryption option ==
@@ -205,7 +205,7 @@ qemu-img create -f qcow2 -o compat=0.10,lazy_refcounts=off TEST_DIR/t.qcow2 64M
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat=0.10 cluster_size=65536 lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 -o compat=0.10,lazy_refcounts=on TEST_DIR/t.qcow2 64M
-qemu-img: TEST_DIR/t.qcow2: Lazy refcounts only supported with compatibility level 1.1 and above (use compat=1.1 or greater)
+qemu-img: TEST_DIR/t.qcow2: Lazy refcounts only supported with compatibility level 1.1 and above (use version=v3 or greater)
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat=0.10 cluster_size=65536 lazy_refcounts=on refcount_bits=16
*** done
diff --git a/tests/qemu-iotests/059 b/tests/qemu-iotests/059
index 40f89eae18..530bbbe6ce 100755
--- a/tests/qemu-iotests/059
+++ b/tests/qemu-iotests/059
@@ -152,9 +152,8 @@ done
echo
echo "=== Testing afl image with a very large capacity ==="
_use_sample_img afl9.vmdk.bz2
-# The sed makes this test pass on machines with little RAM
-# (and also with 32 bit builds)
-_img_info | sed -e 's/Cannot allocate memory/Invalid argument/'
+_img_info | grep -q 'Cannot allocate memory' && _notrun "Insufficent memory, skipped test"
+_img_info
_cleanup_test_img
# success, all done
diff --git a/tests/qemu-iotests/080 b/tests/qemu-iotests/080
index 1c2bd85742..4dbe68e950 100755
--- a/tests/qemu-iotests/080
+++ b/tests/qemu-iotests/080
@@ -171,12 +171,32 @@ poke_file "$TEST_IMG" "$offset_l2_table_0" "\x80\x00\x00\xff\xff\xff\x00\x00"
{ $QEMU_IMG snapshot -c test $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir
echo
-echo "== Invalid snapshot L1 table =="
+echo "== Invalid snapshot L1 table offset =="
+_make_test_img 64M
+{ $QEMU_IO -c "write 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir
+{ $QEMU_IMG snapshot -c test $TEST_IMG; } 2>&1 | _filter_testdir
+poke_file "$TEST_IMG" "$offset_snap1_l1_offset" "\x00\x00\x00\x00\x00\x40\x02\x00"
+{ $QEMU_IMG convert -s test $TEST_IMG $TEST_IMG.snap; } 2>&1 | _filter_testdir
+{ $QEMU_IMG amend -o compat=0.10 $TEST_IMG; } 2>&1 | _filter_testdir
+{ $QEMU_IO -c "open -o overlap-check.inactive-l2=on $TEST_IMG" \
+ -c 'write 0 4k'; } 2>&1 | _filter_qemu_io | _filter_testdir
+{ $QEMU_IMG snapshot -a test $TEST_IMG; } 2>&1 | _filter_testdir
+{ $QEMU_IMG snapshot -d test $TEST_IMG; } 2>&1 | _filter_testdir
+_check_test_img
+
+echo
+echo "== Invalid snapshot L1 table size =="
_make_test_img 64M
{ $QEMU_IO -c "write 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir
{ $QEMU_IMG snapshot -c test $TEST_IMG; } 2>&1 | _filter_testdir
poke_file "$TEST_IMG" "$offset_snap1_l1_size" "\x10\x00\x00\x00"
{ $QEMU_IMG convert -s test $TEST_IMG $TEST_IMG.snap; } 2>&1 | _filter_testdir
+{ $QEMU_IMG amend -o compat=0.10 $TEST_IMG; } 2>&1 | _filter_testdir
+{ $QEMU_IO -c "open -o overlap-check.inactive-l2=on $TEST_IMG" \
+ -c 'write 0 4k'; } 2>&1 | _filter_qemu_io | _filter_testdir
+{ $QEMU_IMG snapshot -a test $TEST_IMG; } 2>&1 | _filter_testdir
+{ $QEMU_IMG snapshot -d test $TEST_IMG; } 2>&1 | _filter_testdir
+_check_test_img
# success, all done
echo "*** done"
diff --git a/tests/qemu-iotests/080.out b/tests/qemu-iotests/080.out
index 6a7fda1356..4e0f7f7b92 100644
--- a/tests/qemu-iotests/080.out
+++ b/tests/qemu-iotests/080.out
@@ -18,18 +18,18 @@ can't open device TEST_DIR/t.qcow2: Reference count table too large
== Misaligned refcount table ==
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
-can't open device TEST_DIR/t.qcow2: Invalid reference count table offset
+can't open device TEST_DIR/t.qcow2: Reference count table offset invalid
== Huge refcount offset ==
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
-can't open device TEST_DIR/t.qcow2: Invalid reference count table offset
+can't open device TEST_DIR/t.qcow2: Reference count table offset invalid
== Invalid snapshot table ==
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
-can't open device TEST_DIR/t.qcow2: Too many snapshots
-can't open device TEST_DIR/t.qcow2: Too many snapshots
-can't open device TEST_DIR/t.qcow2: Invalid snapshot table offset
-can't open device TEST_DIR/t.qcow2: Invalid snapshot table offset
+can't open device TEST_DIR/t.qcow2: Snapshot table too large
+can't open device TEST_DIR/t.qcow2: Snapshot table too large
+can't open device TEST_DIR/t.qcow2: Snapshot table offset invalid
+can't open device TEST_DIR/t.qcow2: Snapshot table offset invalid
== Hitting snapshot table size limit ==
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
@@ -41,8 +41,8 @@ read 512/512 bytes at offset 0
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
can't open device TEST_DIR/t.qcow2: Active L1 table too large
can't open device TEST_DIR/t.qcow2: Active L1 table too large
-can't open device TEST_DIR/t.qcow2: Invalid L1 table offset
-can't open device TEST_DIR/t.qcow2: Invalid L1 table offset
+can't open device TEST_DIR/t.qcow2: Active L1 table offset invalid
+can't open device TEST_DIR/t.qcow2: Active L1 table offset invalid
== Invalid L1 table (with internal snapshot in the image) ==
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
@@ -59,9 +59,49 @@ wrote 512/512 bytes at offset 0
qemu-img: Could not create snapshot 'test': -27 (File too large)
qemu-img: Could not create snapshot 'test': -11 (Resource temporarily unavailable)
-== Invalid snapshot L1 table ==
+== Invalid snapshot L1 table offset ==
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
+wrote 512/512 bytes at offset 0
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+qemu-img: Failed to load snapshot: Snapshot L1 table offset invalid
+qemu-img: Snapshot L1 table offset invalid
+qemu-img: Error while amending options: Invalid argument
+Failed to flush the refcount block cache: Invalid argument
+write failed: Invalid argument
+qemu-img: Snapshot L1 table offset invalid
+qemu-img: Could not apply snapshot 'test': Failed to load snapshot: Invalid argument
+qemu-img: Could not delete snapshot 'test': Snapshot L1 table offset invalid
+ERROR snapshot 1 (test) l1_offset=0x400200: L1 table is not cluster aligned; snapshot table entry corrupted
+Leaked cluster 4 refcount=2 reference=1
+Leaked cluster 5 refcount=2 reference=1
+Leaked cluster 6 refcount=1 reference=0
+
+1 errors were found on the image.
+Data may be corrupted, or further writes to the image may corrupt it.
+
+3 leaked clusters were found on the image.
+This means waste of disk space, but no harm to data.
+
+== Invalid snapshot L1 table size ==
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
wrote 512/512 bytes at offset 0
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
qemu-img: Failed to load snapshot: Snapshot L1 table too large
+qemu-img: Snapshot L1 table too large
+qemu-img: Error while amending options: File too large
+Failed to flush the refcount block cache: File too large
+write failed: File too large
+qemu-img: Snapshot L1 table too large
+qemu-img: Could not apply snapshot 'test': Failed to load snapshot: File too large
+qemu-img: Could not delete snapshot 'test': Snapshot L1 table too large
+ERROR snapshot 1 (test) l1_size=0x10000000: L1 table is too large; snapshot table entry corrupted
+Leaked cluster 4 refcount=2 reference=1
+Leaked cluster 5 refcount=2 reference=1
+Leaked cluster 6 refcount=1 reference=0
+
+1 errors were found on the image.
+Data may be corrupted, or further writes to the image may corrupt it.
+
+3 leaked clusters were found on the image.
+This means waste of disk space, but no harm to data.
*** done
diff --git a/tests/qemu-iotests/096 b/tests/qemu-iotests/096
index aeeb3753cf..aeeb3753cf 100644..100755
--- a/tests/qemu-iotests/096
+++ b/tests/qemu-iotests/096
diff --git a/tests/qemu-iotests/112.out b/tests/qemu-iotests/112.out
index 81b04d1452..86f041075d 100644
--- a/tests/qemu-iotests/112.out
+++ b/tests/qemu-iotests/112.out
@@ -21,9 +21,9 @@ refcount bits: 16
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
refcount bits: 16
-qemu-img: TEST_DIR/t.IMGFMT: Different refcount widths than 16 bits require compatibility level 1.1 or above (use or greater)
+qemu-img: TEST_DIR/t.IMGFMT: Different refcount widths than 16 bits require compatibility level 1.1 or above (use version=v3 or greater)
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
-qemu-img: TEST_DIR/t.IMGFMT: Different refcount widths than 16 bits require compatibility level 1.1 or above (use or greater)
+qemu-img: TEST_DIR/t.IMGFMT: Different refcount widths than 16 bits require compatibility level 1.1 or above (use version=v3 or greater)
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
=== Snapshot limit on refcount_bits=1 ===
diff --git a/tests/qemu-iotests/124 b/tests/qemu-iotests/124
index 8e76e62f93..8e76e62f93 100644..100755
--- a/tests/qemu-iotests/124
+++ b/tests/qemu-iotests/124
diff --git a/tests/qemu-iotests/129 b/tests/qemu-iotests/129
index 9e87e1c8d9..9e87e1c8d9 100644..100755
--- a/tests/qemu-iotests/129
+++ b/tests/qemu-iotests/129
diff --git a/tests/qemu-iotests/132 b/tests/qemu-iotests/132
index f53ef6e391..f53ef6e391 100644..100755
--- a/tests/qemu-iotests/132
+++ b/tests/qemu-iotests/132
diff --git a/tests/qemu-iotests/136 b/tests/qemu-iotests/136
index 88b97ea7c6..88b97ea7c6 100644..100755
--- a/tests/qemu-iotests/136
+++ b/tests/qemu-iotests/136
diff --git a/tests/qemu-iotests/139 b/tests/qemu-iotests/139
index cc7fe337f3..cc7fe337f3 100644..100755
--- a/tests/qemu-iotests/139
+++ b/tests/qemu-iotests/139
diff --git a/tests/qemu-iotests/148 b/tests/qemu-iotests/148
index e01b061fe7..e01b061fe7 100644..100755
--- a/tests/qemu-iotests/148
+++ b/tests/qemu-iotests/148
diff --git a/tests/qemu-iotests/152 b/tests/qemu-iotests/152
index fec546d033..fec546d033 100644..100755
--- a/tests/qemu-iotests/152
+++ b/tests/qemu-iotests/152
diff --git a/tests/qemu-iotests/153 b/tests/qemu-iotests/153
index fa25eb24bd..adfd02695b 100755
--- a/tests/qemu-iotests/153
+++ b/tests/qemu-iotests/153
@@ -32,6 +32,7 @@ _cleanup()
{
_cleanup_test_img
rm -f "${TEST_IMG}.base"
+ rm -f "${TEST_IMG}.overlay"
rm -f "${TEST_IMG}.convert"
rm -f "${TEST_IMG}.a"
rm -f "${TEST_IMG}.b"
@@ -177,8 +178,6 @@ rm -f "${TEST_IMG}.lnk" &>/dev/null
ln -s ${TEST_IMG} "${TEST_IMG}.lnk" || echo "Failed to create link"
_run_qemu_with_images "${TEST_IMG}.lnk" "${TEST_IMG}"
-echo
-echo "== Closing an image should unlock it =="
_launch_qemu
_send_qemu_cmd $QEMU_HANDLE \
@@ -193,7 +192,10 @@ _send_qemu_cmd $QEMU_HANDLE \
_run_cmd $QEMU_IO "${TEST_IMG}" -c 'write 0 512'
-echo "Closing drive"
+echo "Creating overlay with qemu-img when the guest is running should be allowed"
+_run_cmd $QEMU_IMG create -f $IMGFMT -b "${TEST_IMG}" "${TEST_IMG}.overlay"
+
+echo "== Closing an image should unlock it =="
_send_qemu_cmd $QEMU_HANDLE \
"{ 'execute': 'human-monitor-command',
'arguments': { 'command-line': 'drive_del d0' } }" \
diff --git a/tests/qemu-iotests/153.out b/tests/qemu-iotests/153.out
index 5b917b177c..34309cfb20 100644
--- a/tests/qemu-iotests/153.out
+++ b/tests/qemu-iotests/153.out
@@ -372,15 +372,16 @@ Is another process using the image?
== Symbolic link ==
QEMU_PROG: -drive if=none,file=TEST_DIR/t.qcow2: Failed to get "write" lock
Is another process using the image?
-
-== Closing an image should unlock it ==
{"return": {}}
Adding drive
_qemu_io_wrapper TEST_DIR/t.qcow2 -c write 0 512
can't open device TEST_DIR/t.qcow2: Failed to get "write" lock
Is another process using the image?
-Closing drive
+Creating overlay with qemu-img when the guest is running should be allowed
+
+_qemu_img_wrapper create -f qcow2 -b TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.overlay
+== Closing an image should unlock it ==
_qemu_io_wrapper TEST_DIR/t.qcow2 -c write 0 512
Adding two and closing one
diff --git a/tests/qemu-iotests/163 b/tests/qemu-iotests/163
index 403842354e..403842354e 100644..100755
--- a/tests/qemu-iotests/163
+++ b/tests/qemu-iotests/163
diff --git a/tests/qemu-iotests/203 b/tests/qemu-iotests/203
index 2c811917d8..4874a1a0d8 100755
--- a/tests/qemu-iotests/203
+++ b/tests/qemu-iotests/203
@@ -49,11 +49,18 @@ with iotests.FilePath('disk0.img') as disk0_img_path, \
node_name='drive1-node', iothread='iothread0',
force=True))
+ iotests.log('Enabling migration QMP events...')
+ iotests.log(vm.qmp('migrate-set-capabilities', capabilities=[
+ {
+ 'capability': 'events',
+ 'state': True
+ }
+ ]))
+
iotests.log('Starting migration...')
iotests.log(vm.qmp('migrate', uri='exec:cat >/dev/null'))
while True:
- vm.get_qmp_event(wait=60.0)
- result = vm.qmp('query-migrate')
- status = result.get('return', {}).get('status', None)
- if status == 'completed':
+ event = vm.event_wait('MIGRATION')
+ iotests.log(event, filters=[iotests.filter_qmp_event])
+ if event['data']['status'] == 'completed':
break
diff --git a/tests/qemu-iotests/203.out b/tests/qemu-iotests/203.out
index 3f1ff900e4..1a11f0975c 100644
--- a/tests/qemu-iotests/203.out
+++ b/tests/qemu-iotests/203.out
@@ -2,5 +2,10 @@ Launching VM...
Setting IOThreads...
{u'return': {}}
{u'return': {}}
+Enabling migration QMP events...
+{u'return': {}}
Starting migration...
{u'return': {}}
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'setup'}, u'event': u'MIGRATION'}
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'active'}, u'event': u'MIGRATION'}
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'completed'}, u'event': u'MIGRATION'}
diff --git a/tests/qemu-iotests/205 b/tests/qemu-iotests/205
index e7b2eae51d..e7b2eae51d 100644..100755
--- a/tests/qemu-iotests/205
+++ b/tests/qemu-iotests/205
diff --git a/tests/qemu-iotests/206 b/tests/qemu-iotests/206
new file mode 100755
index 0000000000..0a18b2b19a
--- /dev/null
+++ b/tests/qemu-iotests/206
@@ -0,0 +1,436 @@
+#!/bin/bash
+#
+# Test qcow2 and file image creation
+#
+# Copyright (C) 2018 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`
+status=1 # failure is the default!
+
+# get standard environment, filters and checks
+. ./common.rc
+. ./common.filter
+
+_supported_fmt qcow2
+_supported_proto file
+_supported_os Linux
+
+function do_run_qemu()
+{
+ echo Testing: "$@"
+ $QEMU -nographic -qmp stdio -serial none "$@"
+ echo
+}
+
+function run_qemu()
+{
+ do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qmp \
+ | _filter_qemu | _filter_imgfmt \
+ | _filter_actual_image_size
+}
+
+echo
+echo "=== Successful image creation (defaults) ==="
+echo
+
+size=$((128 * 1024 * 1024))
+
+run_qemu <<EOF
+{ "execute": "qmp_capabilities" }
+{ "execute": "x-blockdev-create",
+ "arguments": {
+ "driver": "file",
+ "filename": "$TEST_IMG",
+ "size": 0
+ }
+}
+{ "execute": "blockdev-add",
+ "arguments": {
+ "driver": "file",
+ "node-name": "imgfile",
+ "filename": "$TEST_IMG"
+ }
+}
+{ "execute": "x-blockdev-create",
+ "arguments": {
+ "driver": "$IMGFMT",
+ "file": "imgfile",
+ "size": $size
+ }
+}
+{ "execute": "quit" }
+EOF
+
+_img_info --format-specific
+
+echo
+echo "=== Successful image creation (inline blockdev-add, explicit defaults) ==="
+echo
+
+# Choose a different size to show that we got a new image
+size=$((64 * 1024 * 1024))
+
+run_qemu <<EOF
+{ "execute": "qmp_capabilities" }
+{ "execute": "x-blockdev-create",
+ "arguments": {
+ "driver": "file",
+ "filename": "$TEST_IMG",
+ "size": 0,
+ "preallocation": "off",
+ "nocow": false
+ }
+}
+{ "execute": "x-blockdev-create",
+ "arguments": {
+ "driver": "$IMGFMT",
+ "file": {
+ "driver": "file",
+ "filename": "$TEST_IMG"
+ },
+ "size": $size,
+ "version": "v3",
+ "cluster-size": 65536,
+ "preallocation": "off",
+ "lazy-refcounts": false,
+ "refcount-bits": 16
+ }
+}
+{ "execute": "quit" }
+EOF
+
+_img_info --format-specific
+
+echo
+echo "=== Successful image creation (v3 non-default options) ==="
+echo
+
+# Choose a different size to show that we got a new image
+size=$((32 * 1024 * 1024))
+
+run_qemu <<EOF
+{ "execute": "qmp_capabilities" }
+{ "execute": "x-blockdev-create",
+ "arguments": {
+ "driver": "file",
+ "filename": "$TEST_IMG",
+ "size": 0,
+ "preallocation": "falloc",
+ "nocow": true
+ }
+}
+{ "execute": "x-blockdev-create",
+ "arguments": {
+ "driver": "$IMGFMT",
+ "file": {
+ "driver": "file",
+ "filename": "$TEST_IMG"
+ },
+ "size": $size,
+ "version": "v3",
+ "cluster-size": 2097152,
+ "preallocation": "metadata",
+ "lazy-refcounts": true,
+ "refcount-bits": 1
+ }
+}
+{ "execute": "quit" }
+EOF
+
+_img_info --format-specific
+
+echo
+echo "=== Successful image creation (v2 non-default options) ==="
+echo
+
+mv $TEST_IMG $TEST_IMG.base
+
+run_qemu <<EOF
+{ "execute": "qmp_capabilities" }
+{ "execute": "x-blockdev-create",
+ "arguments": {
+ "driver": "file",
+ "filename": "$TEST_IMG",
+ "size": 0
+ }
+}
+{ "execute": "x-blockdev-create",
+ "arguments": {
+ "driver": "$IMGFMT",
+ "file": {
+ "driver": "file",
+ "filename": "$TEST_IMG"
+ },
+ "size": $size,
+ "backing-file": "$TEST_IMG.base",
+ "backing-fmt": "qcow2",
+ "version": "v2",
+ "cluster-size": 512
+ }
+}
+{ "execute": "quit" }
+EOF
+
+_img_info --format-specific
+
+echo
+echo "=== Successful image creation (encrypted) ==="
+echo
+
+run_qemu -object secret,id=keysec0,data="foo" <<EOF
+{ "execute": "qmp_capabilities" }
+{ "execute": "x-blockdev-create",
+ "arguments": {
+ "driver": "$IMGFMT",
+ "file": {
+ "driver": "file",
+ "filename": "$TEST_IMG"
+ },
+ "size": $size,
+ "encrypt": {
+ "format": "luks",
+ "key-secret": "keysec0",
+ "cipher-alg": "twofish-128",
+ "cipher-mode": "ctr",
+ "ivgen-alg": "plain64",
+ "ivgen-hash-alg": "md5",
+ "hash-alg": "sha1",
+ "iter-time": 10
+ }
+ }
+}
+{ "execute": "quit" }
+EOF
+
+_img_info --format-specific | _filter_img_info --format-specific
+
+echo
+echo "=== Invalid BlockdevRef ==="
+echo
+
+run_qemu <<EOF
+{ "execute": "qmp_capabilities" }
+{ "execute": "x-blockdev-create",
+ "arguments": {
+ "driver": "$IMGFMT",
+ "file": "this doesn't exist",
+ "size": $size
+ }
+}
+{ "execute": "quit" }
+EOF
+
+
+echo
+echo "=== Invalid sizes ==="
+echo
+
+# TODO Negative image sizes aren't handled correctly, but this is a problem
+# with QAPI's implementation of the 'size' type and affects other commands as
+# well. Once this is fixed, we may want to add a test case here.
+
+# 1. Misaligned image size
+# 2. 2^64 - 512
+# 3. 2^63 = 8 EB (qemu-img enforces image sizes less than this)
+# 4. 2^63 - 512 (generally valid, but qcow2 can't handle images this size)
+
+run_qemu -blockdev driver=file,filename="$TEST_IMG",node-name=node0 <<EOF
+{ "execute": "qmp_capabilities" }
+{ "execute": "x-blockdev-create",
+ "arguments": {
+ "driver": "$IMGFMT",
+ "file": "node0",
+ "size": 1234
+ }
+}
+{ "execute": "x-blockdev-create",
+ "arguments": {
+ "driver": "$IMGFMT",
+ "file": "node0",
+ "size": 18446744073709551104
+ }
+}
+{ "execute": "x-blockdev-create",
+ "arguments": {
+ "driver": "$IMGFMT",
+ "file": "node0",
+ "size": 9223372036854775808
+ }
+}
+{ "execute": "x-blockdev-create",
+ "arguments": {
+ "driver": "$IMGFMT",
+ "file": "node0",
+ "size": 9223372036854775296
+ }
+}
+{ "execute": "quit" }
+EOF
+
+echo
+echo "=== Invalid version ==="
+echo
+
+run_qemu -blockdev driver=file,filename="$TEST_IMG",node-name=node0 <<EOF
+{ "execute": "qmp_capabilities" }
+{ "execute": "x-blockdev-create",
+ "arguments": {
+ "driver": "$IMGFMT",
+ "file": "node0",
+ "size": 67108864,
+ "version": "v1"
+ }
+}
+{ "execute": "x-blockdev-create",
+ "arguments": {
+ "driver": "$IMGFMT",
+ "file": "node0",
+ "size": 67108864,
+ "version": "v2",
+ "lazy-refcounts": true
+ }
+}
+{ "execute": "x-blockdev-create",
+ "arguments": {
+ "driver": "$IMGFMT",
+ "file": "node0",
+ "size": 67108864,
+ "version": "v2",
+ "refcount-bits": 8
+ }
+}
+{ "execute": "quit" }
+EOF
+
+echo
+echo "=== Invalid backing file options ==="
+echo
+
+run_qemu -blockdev driver=file,filename="$TEST_IMG",node-name=node0 <<EOF
+{ "execute": "qmp_capabilities" }
+{ "execute": "x-blockdev-create",
+ "arguments": {
+ "driver": "$IMGFMT",
+ "file": "node0",
+ "size": 67108864,
+ "backing-file": "/dev/null",
+ "preallocation": "full"
+ }
+}
+{ "execute": "x-blockdev-create",
+ "arguments": {
+ "driver": "$IMGFMT",
+ "file": "node0",
+ "size": 67108864,
+ "backing-fmt": "$IMGFMT"
+ }
+}
+{ "execute": "quit" }
+EOF
+
+echo
+echo "=== Invalid cluster size ==="
+echo
+
+run_qemu -blockdev driver=file,filename="$TEST_IMG",node-name=node0 <<EOF
+{ "execute": "qmp_capabilities" }
+{ "execute": "x-blockdev-create",
+ "arguments": {
+ "driver": "$IMGFMT",
+ "file": "node0",
+ "size": 67108864,
+ "cluster-size": 1234
+ }
+}
+{ "execute": "x-blockdev-create",
+ "arguments": {
+ "driver": "$IMGFMT",
+ "file": "node0",
+ "size": 67108864,
+ "cluster-size": 128
+ }
+}
+{ "execute": "x-blockdev-create",
+ "arguments": {
+ "driver": "$IMGFMT",
+ "file": "node0",
+ "size": 67108864,
+ "cluster-size": 4194304
+ }
+}
+{ "execute": "x-blockdev-create",
+ "arguments": {
+ "driver": "$IMGFMT",
+ "file": "node0",
+ "size": 67108864,
+ "cluster-size": 0
+ }
+}
+{ "execute": "x-blockdev-create",
+ "arguments": {
+ "driver": "$IMGFMT",
+ "file": "node0",
+ "size": 281474976710656,
+ "cluster-size": 512
+ }
+}
+{ "execute": "quit" }
+EOF
+
+echo
+echo "=== Invalid refcount width ==="
+echo
+
+run_qemu -blockdev driver=file,filename="$TEST_IMG",node-name=node0 <<EOF
+{ "execute": "qmp_capabilities" }
+{ "execute": "x-blockdev-create",
+ "arguments": {
+ "driver": "$IMGFMT",
+ "file": "node0",
+ "size": 67108864,
+ "refcount-bits": 128
+ }
+}
+{ "execute": "x-blockdev-create",
+ "arguments": {
+ "driver": "$IMGFMT",
+ "file": "node0",
+ "size": 67108864,
+ "refcount-bits": 0
+ }
+}
+{ "execute": "x-blockdev-create",
+ "arguments": {
+ "driver": "$IMGFMT",
+ "file": "node0",
+ "size": 67108864,
+ "refcount-bits": 7
+ }
+}
+{ "execute": "quit" }
+EOF
+
+# success, all done
+echo "*** done"
+rm -f $seq.full
+status=0
diff --git a/tests/qemu-iotests/206.out b/tests/qemu-iotests/206.out
new file mode 100644
index 0000000000..042342ae9d
--- /dev/null
+++ b/tests/qemu-iotests/206.out
@@ -0,0 +1,209 @@
+QA output created by 206
+
+=== Successful image creation (defaults) ===
+
+Testing:
+QMP_VERSION
+{"return": {}}
+{"return": {}}
+{"return": {}}
+{"return": {}}
+{"return": {}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
+
+image: TEST_DIR/t.IMGFMT
+file format: IMGFMT
+virtual size: 128M (134217728 bytes)
+cluster_size: 65536
+Format specific information:
+ compat: 1.1
+ lazy refcounts: false
+ refcount bits: 16
+ corrupt: false
+
+=== Successful image creation (inline blockdev-add, explicit defaults) ===
+
+Testing:
+QMP_VERSION
+{"return": {}}
+{"return": {}}
+{"return": {}}
+{"return": {}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
+
+image: TEST_DIR/t.IMGFMT
+file format: IMGFMT
+virtual size: 64M (67108864 bytes)
+cluster_size: 65536
+Format specific information:
+ compat: 1.1
+ lazy refcounts: false
+ refcount bits: 16
+ corrupt: false
+
+=== Successful image creation (v3 non-default options) ===
+
+Testing:
+QMP_VERSION
+{"return": {}}
+{"return": {}}
+{"return": {}}
+{"return": {}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
+
+image: TEST_DIR/t.IMGFMT
+file format: IMGFMT
+virtual size: 32M (33554432 bytes)
+cluster_size: 2097152
+Format specific information:
+ compat: 1.1
+ lazy refcounts: true
+ refcount bits: 1
+ corrupt: false
+
+=== Successful image creation (v2 non-default options) ===
+
+Testing:
+QMP_VERSION
+{"return": {}}
+{"return": {}}
+{"return": {}}
+{"return": {}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
+
+image: TEST_DIR/t.IMGFMT
+file format: IMGFMT
+virtual size: 32M (33554432 bytes)
+cluster_size: 512
+backing file: TEST_DIR/t.IMGFMT.base
+backing file format: IMGFMT
+Format specific information:
+ compat: 0.10
+ refcount bits: 16
+
+=== Successful image creation (encrypted) ===
+
+Testing: -object secret,id=keysec0,data=foo
+QMP_VERSION
+{"return": {}}
+{"return": {}}
+{"return": {}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
+
+image: TEST_DIR/t.IMGFMT
+file format: IMGFMT
+virtual size: 32M (33554432 bytes)
+Format specific information:
+ compat: 1.1
+ lazy refcounts: false
+ refcount bits: 16
+ encrypt:
+ ivgen alg: plain64
+ hash alg: sha1
+ cipher alg: twofish-128
+ uuid: 00000000-0000-0000-0000-000000000000
+ format: luks
+ cipher mode: ctr
+ slots:
+ [0]:
+ active: true
+ iters: 1024
+ key offset: 4096
+ stripes: 4000
+ [1]:
+ active: false
+ key offset: 69632
+ [2]:
+ active: false
+ key offset: 135168
+ [3]:
+ active: false
+ key offset: 200704
+ [4]:
+ active: false
+ key offset: 266240
+ [5]:
+ active: false
+ key offset: 331776
+ [6]:
+ active: false
+ key offset: 397312
+ [7]:
+ active: false
+ key offset: 462848
+ payload offset: 528384
+ master key iters: 1024
+ corrupt: false
+
+=== Invalid BlockdevRef ===
+
+Testing:
+QMP_VERSION
+{"return": {}}
+{"error": {"class": "GenericError", "desc": "Cannot find device=this doesn't exist nor node_name=this doesn't exist"}}
+{"return": {}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
+
+
+=== Invalid sizes ===
+
+Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0
+QMP_VERSION
+{"return": {}}
+{"error": {"class": "GenericError", "desc": "Image size must be a multiple of 512 bytes"}}
+{"error": {"class": "GenericError", "desc": "Could not resize image: Image size cannot be negative"}}
+{"error": {"class": "GenericError", "desc": "Could not resize image: Image size cannot be negative"}}
+{"error": {"class": "GenericError", "desc": "Could not resize image: Failed to grow the L1 table: File too large"}}
+{"return": {}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
+
+
+=== Invalid version ===
+
+Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0
+QMP_VERSION
+{"return": {}}
+{"error": {"class": "GenericError", "desc": "Invalid parameter 'v1'"}}
+{"error": {"class": "GenericError", "desc": "Lazy refcounts only supported with compatibility level 1.1 and above (use version=v3 or greater)"}}
+{"error": {"class": "GenericError", "desc": "Different refcount widths than 16 bits require compatibility level 1.1 or above (use version=v3 or greater)"}}
+{"return": {}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
+
+
+=== Invalid backing file options ===
+
+Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0
+QMP_VERSION
+{"return": {}}
+{"error": {"class": "GenericError", "desc": "Backing file and preallocation cannot be used at the same time"}}
+{"error": {"class": "GenericError", "desc": "Backing format cannot be used without backing file"}}
+{"return": {}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
+
+
+=== Invalid cluster size ===
+
+Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0
+QMP_VERSION
+{"return": {}}
+{"error": {"class": "GenericError", "desc": "Cluster size must be a power of two between 512 and 2048k"}}
+{"error": {"class": "GenericError", "desc": "Cluster size must be a power of two between 512 and 2048k"}}
+{"error": {"class": "GenericError", "desc": "Cluster size must be a power of two between 512 and 2048k"}}
+{"error": {"class": "GenericError", "desc": "Cluster size must be a power of two between 512 and 2048k"}}
+{"error": {"class": "GenericError", "desc": "Could not resize image: Failed to grow the L1 table: File too large"}}
+{"return": {}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
+
+
+=== Invalid refcount width ===
+
+Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0
+QMP_VERSION
+{"return": {}}
+{"error": {"class": "GenericError", "desc": "Refcount width must be a power of two and may not exceed 64 bits"}}
+{"error": {"class": "GenericError", "desc": "Refcount width must be a power of two and may not exceed 64 bits"}}
+{"error": {"class": "GenericError", "desc": "Refcount width must be a power of two and may not exceed 64 bits"}}
+{"return": {}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
+
+*** done
diff --git a/tests/qemu-iotests/207 b/tests/qemu-iotests/207
new file mode 100755
index 0000000000..f5c77852d1
--- /dev/null
+++ b/tests/qemu-iotests/207
@@ -0,0 +1,261 @@
+#!/bin/bash
+#
+# Test ssh image creation
+#
+# Copyright (C) 2018 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`
+status=1 # failure is the default!
+
+# get standard environment, filters and checks
+. ./common.rc
+. ./common.filter
+
+_supported_fmt raw
+_supported_proto ssh
+_supported_os Linux
+
+function do_run_qemu()
+{
+ echo Testing: "$@"
+ $QEMU -nographic -qmp stdio -serial none "$@"
+ echo
+}
+
+function run_qemu()
+{
+ do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qmp \
+ | _filter_qemu | _filter_imgfmt \
+ | _filter_actual_image_size
+}
+
+echo
+echo "=== Successful image creation (defaults) ==="
+echo
+
+run_qemu <<EOF
+{ "execute": "qmp_capabilities" }
+{ "execute": "x-blockdev-create",
+ "arguments": {
+ "driver": "ssh",
+ "location": {
+ "path": "$TEST_IMG_FILE",
+ "server": {
+ "host": "127.0.0.1",
+ "port": "22"
+ }
+ },
+ "size": 4194304
+ }
+}
+{ "execute": "quit" }
+EOF
+
+_img_info | _filter_img_info
+echo
+TEST_IMG=$TEST_IMG_FILE _img_info | _filter_img_info
+
+echo
+echo "=== Test host-key-check options ==="
+echo
+
+run_qemu <<EOF
+{ "execute": "qmp_capabilities" }
+{ "execute": "x-blockdev-create",
+ "arguments": {
+ "driver": "ssh",
+ "location": {
+ "path": "$TEST_IMG_FILE",
+ "server": {
+ "host": "127.0.0.1",
+ "port": "22"
+ },
+ "host-key-check": {
+ "mode": "none"
+ }
+ },
+ "size": 8388608
+ }
+}
+{ "execute": "quit" }
+EOF
+
+_img_info | _filter_img_info
+
+run_qemu <<EOF
+{ "execute": "qmp_capabilities" }
+{ "execute": "x-blockdev-create",
+ "arguments": {
+ "driver": "ssh",
+ "location": {
+ "path": "$TEST_IMG_FILE",
+ "server": {
+ "host": "127.0.0.1",
+ "port": "22"
+ },
+ "host-key-check": {
+ "mode": "known_hosts"
+ }
+ },
+ "size": 4194304
+ }
+}
+{ "execute": "quit" }
+EOF
+
+_img_info | _filter_img_info
+
+
+key=$(ssh-keyscan -t rsa 127.0.0.1 2>/dev/null | grep -v "\\^#" |
+ cut -d" " -f3 | base64 -d | md5sum -b | cut -d" " -f1)
+
+run_qemu <<EOF
+{ "execute": "qmp_capabilities" }
+{ "execute": "x-blockdev-create",
+ "arguments": {
+ "driver": "ssh",
+ "location": {
+ "path": "$TEST_IMG_FILE",
+ "server": {
+ "host": "127.0.0.1",
+ "port": "22"
+ },
+ "host-key-check": {
+ "mode": "hash",
+ "type": "md5",
+ "hash": "wrong"
+ }
+ },
+ "size": 8388608
+ }
+}
+{ "execute": "x-blockdev-create",
+ "arguments": {
+ "driver": "ssh",
+ "location": {
+ "path": "$TEST_IMG_FILE",
+ "server": {
+ "host": "127.0.0.1",
+ "port": "22"
+ },
+ "host-key-check": {
+ "mode": "hash",
+ "type": "md5",
+ "hash": "$key"
+ }
+ },
+ "size": 8388608
+ }
+}
+{ "execute": "quit" }
+EOF
+
+_img_info | _filter_img_info
+
+
+key=$(ssh-keyscan -t rsa 127.0.0.1 2>/dev/null | grep -v "\\^#" |
+ cut -d" " -f3 | base64 -d | sha1sum -b | cut -d" " -f1)
+
+run_qemu <<EOF
+{ "execute": "qmp_capabilities" }
+{ "execute": "x-blockdev-create",
+ "arguments": {
+ "driver": "ssh",
+ "location": {
+ "path": "$TEST_IMG_FILE",
+ "server": {
+ "host": "127.0.0.1",
+ "port": "22"
+ },
+ "host-key-check": {
+ "mode": "hash",
+ "type": "sha1",
+ "hash": "wrong"
+ }
+ },
+ "size": 4194304
+ }
+}
+{ "execute": "x-blockdev-create",
+ "arguments": {
+ "driver": "ssh",
+ "location": {
+ "path": "$TEST_IMG_FILE",
+ "server": {
+ "host": "127.0.0.1",
+ "port": "22"
+ },
+ "host-key-check": {
+ "mode": "hash",
+ "type": "sha1",
+ "hash": "$key"
+ }
+ },
+ "size": 4194304
+ }
+}
+{ "execute": "quit" }
+EOF
+
+_img_info | _filter_img_info
+
+echo
+echo "=== Invalid path and user ==="
+echo
+
+run_qemu <<EOF
+{ "execute": "qmp_capabilities" }
+{ "execute": "x-blockdev-create",
+ "arguments": {
+ "driver": "ssh",
+ "location": {
+ "path": "/this/is/not/an/existing/path",
+ "server": {
+ "host": "127.0.0.1",
+ "port": "22"
+ }
+ },
+ "size": 4194304
+ }
+}
+{ "execute": "x-blockdev-create",
+ "arguments": {
+ "driver": "ssh",
+ "location": {
+ "path": "$TEST_IMG_FILE",
+ "user": "invalid user",
+ "server": {
+ "host": "127.0.0.1",
+ "port": "22"
+ }
+ },
+ "size": 4194304
+ }
+}
+{ "execute": "quit" }
+EOF
+
+# success, all done
+echo "*** done"
+rm -f $seq.full
+status=0
diff --git a/tests/qemu-iotests/207.out b/tests/qemu-iotests/207.out
new file mode 100644
index 0000000000..417deee970
--- /dev/null
+++ b/tests/qemu-iotests/207.out
@@ -0,0 +1,75 @@
+QA output created by 207
+
+=== Successful image creation (defaults) ===
+
+Testing:
+QMP_VERSION
+{"return": {}}
+{"return": {}}
+{"return": {}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
+
+image: json:{"driver": "IMGFMT", "file": {"server.host": "127.0.0.1", "server.port": "22", "driver": "ssh", "path": "TEST_DIR/t.IMGFMT"}}
+file format: IMGFMT
+virtual size: 4.0M (4194304 bytes)
+
+image: TEST_DIR/t.IMGFMT
+file format: IMGFMT
+virtual size: 4.0M (4194304 bytes)
+
+=== Test host-key-check options ===
+
+Testing:
+QMP_VERSION
+{"return": {}}
+{"return": {}}
+{"return": {}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
+
+image: json:{"driver": "IMGFMT", "file": {"server.host": "127.0.0.1", "server.port": "22", "driver": "ssh", "path": "TEST_DIR/t.IMGFMT"}}
+file format: IMGFMT
+virtual size: 8.0M (8388608 bytes)
+Testing:
+QMP_VERSION
+{"return": {}}
+{"return": {}}
+{"return": {}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
+
+image: json:{"driver": "IMGFMT", "file": {"server.host": "127.0.0.1", "server.port": "22", "driver": "ssh", "path": "TEST_DIR/t.IMGFMT"}}
+file format: IMGFMT
+virtual size: 4.0M (4194304 bytes)
+Testing:
+QMP_VERSION
+{"return": {}}
+{"error": {"class": "GenericError", "desc": "remote host key does not match host_key_check 'wrong'"}}
+{"return": {}}
+{"return": {}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
+
+image: json:{"driver": "IMGFMT", "file": {"server.host": "127.0.0.1", "server.port": "22", "driver": "ssh", "path": "TEST_DIR/t.IMGFMT"}}
+file format: IMGFMT
+virtual size: 8.0M (8388608 bytes)
+Testing:
+QMP_VERSION
+{"return": {}}
+{"error": {"class": "GenericError", "desc": "remote host key does not match host_key_check 'wrong'"}}
+{"return": {}}
+{"return": {}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
+
+image: json:{"driver": "IMGFMT", "file": {"server.host": "127.0.0.1", "server.port": "22", "driver": "ssh", "path": "TEST_DIR/t.IMGFMT"}}
+file format: IMGFMT
+virtual size: 4.0M (4194304 bytes)
+
+=== Invalid path and user ===
+
+Testing:
+QMP_VERSION
+{"return": {}}
+{"error": {"class": "GenericError", "desc": "failed to open remote file '/this/is/not/an/existing/path': Failed opening remote file (libssh2 error code: -31)"}}
+{"error": {"class": "GenericError", "desc": "failed to authenticate using publickey authentication and the identities held by your ssh-agent"}}
+{"return": {}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
+
+*** done
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
index a2dfe79d86..c401791fcd 100644
--- a/tests/qemu-iotests/group
+++ b/tests/qemu-iotests/group
@@ -202,3 +202,5 @@
203 rw auto
204 rw auto quick
205 rw auto quick
+206 rw auto
+207 rw auto
diff --git a/tests/test-qemu-opts.c b/tests/test-qemu-opts.c
index 5d5a3daa7b..2c422abcd4 100644
--- a/tests/test-qemu-opts.c
+++ b/tests/test-qemu-opts.c
@@ -10,6 +10,7 @@
#include "qemu/osdep.h"
#include "qemu/cutils.h"
#include "qemu/option.h"
+#include "qemu/option_int.h"
#include "qapi/error.h"
#include "qapi/qmp/qdict.h"
#include "qapi/qmp/qstring.h"
@@ -23,6 +24,8 @@ static QemuOptsList opts_list_01 = {
{
.name = "str1",
.type = QEMU_OPT_STRING,
+ .help = "Help texts are preserved in qemu_opts_append",
+ .def_value_str = "default",
},{
.name = "str2",
.type = QEMU_OPT_STRING,
@@ -32,6 +35,7 @@ static QemuOptsList opts_list_01 = {
},{
.name = "number1",
.type = QEMU_OPT_NUMBER,
+ .help = "Having help texts only for some options is okay",
},{
.name = "number2",
.type = QEMU_OPT_NUMBER,
@@ -743,6 +747,250 @@ static void test_opts_parse_size(void)
qemu_opts_reset(&opts_list_02);
}
+static void append_verify_list_01(QemuOptDesc *desc, bool with_overlapping)
+{
+ int i = 0;
+
+ if (with_overlapping) {
+ g_assert_cmpstr(desc[i].name, ==, "str1");
+ g_assert_cmpint(desc[i].type, ==, QEMU_OPT_STRING);
+ g_assert_cmpstr(desc[i].help, ==,
+ "Help texts are preserved in qemu_opts_append");
+ g_assert_cmpstr(desc[i].def_value_str, ==, "default");
+ i++;
+
+ g_assert_cmpstr(desc[i].name, ==, "str2");
+ g_assert_cmpint(desc[i].type, ==, QEMU_OPT_STRING);
+ g_assert_cmpstr(desc[i].help, ==, NULL);
+ g_assert_cmpstr(desc[i].def_value_str, ==, NULL);
+ i++;
+ }
+
+ g_assert_cmpstr(desc[i].name, ==, "str3");
+ g_assert_cmpint(desc[i].type, ==, QEMU_OPT_STRING);
+ g_assert_cmpstr(desc[i].help, ==, NULL);
+ g_assert_cmpstr(desc[i].def_value_str, ==, NULL);
+ i++;
+
+ g_assert_cmpstr(desc[i].name, ==, "number1");
+ g_assert_cmpint(desc[i].type, ==, QEMU_OPT_NUMBER);
+ g_assert_cmpstr(desc[i].help, ==,
+ "Having help texts only for some options is okay");
+ g_assert_cmpstr(desc[i].def_value_str, ==, NULL);
+ i++;
+
+ g_assert_cmpstr(desc[i].name, ==, "number2");
+ g_assert_cmpint(desc[i].type, ==, QEMU_OPT_NUMBER);
+ g_assert_cmpstr(desc[i].help, ==, NULL);
+ g_assert_cmpstr(desc[i].def_value_str, ==, NULL);
+ i++;
+
+ g_assert_cmpstr(desc[i].name, ==, NULL);
+}
+
+static void append_verify_list_02(QemuOptDesc *desc)
+{
+ int i = 0;
+
+ g_assert_cmpstr(desc[i].name, ==, "str1");
+ g_assert_cmpint(desc[i].type, ==, QEMU_OPT_STRING);
+ g_assert_cmpstr(desc[i].help, ==, NULL);
+ g_assert_cmpstr(desc[i].def_value_str, ==, NULL);
+ i++;
+
+ g_assert_cmpstr(desc[i].name, ==, "str2");
+ g_assert_cmpint(desc[i].type, ==, QEMU_OPT_STRING);
+ g_assert_cmpstr(desc[i].help, ==, NULL);
+ g_assert_cmpstr(desc[i].def_value_str, ==, NULL);
+ i++;
+
+ g_assert_cmpstr(desc[i].name, ==, "bool1");
+ g_assert_cmpint(desc[i].type, ==, QEMU_OPT_BOOL);
+ g_assert_cmpstr(desc[i].help, ==, NULL);
+ g_assert_cmpstr(desc[i].def_value_str, ==, NULL);
+ i++;
+
+ g_assert_cmpstr(desc[i].name, ==, "bool2");
+ g_assert_cmpint(desc[i].type, ==, QEMU_OPT_BOOL);
+ g_assert_cmpstr(desc[i].help, ==, NULL);
+ g_assert_cmpstr(desc[i].def_value_str, ==, NULL);
+ i++;
+
+ g_assert_cmpstr(desc[i].name, ==, "size1");
+ g_assert_cmpint(desc[i].type, ==, QEMU_OPT_SIZE);
+ g_assert_cmpstr(desc[i].help, ==, NULL);
+ g_assert_cmpstr(desc[i].def_value_str, ==, NULL);
+ i++;
+
+ g_assert_cmpstr(desc[i].name, ==, "size2");
+ g_assert_cmpint(desc[i].type, ==, QEMU_OPT_SIZE);
+ g_assert_cmpstr(desc[i].help, ==, NULL);
+ g_assert_cmpstr(desc[i].def_value_str, ==, NULL);
+ i++;
+
+ g_assert_cmpstr(desc[i].name, ==, "size3");
+ g_assert_cmpint(desc[i].type, ==, QEMU_OPT_SIZE);
+ g_assert_cmpstr(desc[i].help, ==, NULL);
+ g_assert_cmpstr(desc[i].def_value_str, ==, NULL);
+}
+
+static void test_opts_append_to_null(void)
+{
+ QemuOptsList *merged;
+
+ merged = qemu_opts_append(NULL, &opts_list_01);
+ g_assert(merged != &opts_list_01);
+
+ g_assert_cmpstr(merged->name, ==, NULL);
+ g_assert_cmpstr(merged->implied_opt_name, ==, NULL);
+ g_assert_false(merged->merge_lists);
+
+ append_verify_list_01(merged->desc, true);
+
+ qemu_opts_free(merged);
+}
+
+static void test_opts_append(void)
+{
+ QemuOptsList *first, *merged;
+
+ first = qemu_opts_append(NULL, &opts_list_02);
+ merged = qemu_opts_append(first, &opts_list_01);
+ g_assert(first != &opts_list_02);
+ g_assert(merged != &opts_list_01);
+
+ g_assert_cmpstr(merged->name, ==, NULL);
+ g_assert_cmpstr(merged->implied_opt_name, ==, NULL);
+ g_assert_false(merged->merge_lists);
+
+ append_verify_list_02(&merged->desc[0]);
+ append_verify_list_01(&merged->desc[7], false);
+
+ qemu_opts_free(merged);
+}
+
+static void test_opts_to_qdict_basic(void)
+{
+ QemuOpts *opts;
+ QDict *dict;
+
+ opts = qemu_opts_parse(&opts_list_01, "str1=foo,str2=,str3=bar,number1=42",
+ false, &error_abort);
+ g_assert(opts != NULL);
+
+ dict = qemu_opts_to_qdict(opts, NULL);
+ g_assert(dict != NULL);
+
+ g_assert_cmpstr(qdict_get_str(dict, "str1"), ==, "foo");
+ g_assert_cmpstr(qdict_get_str(dict, "str2"), ==, "");
+ g_assert_cmpstr(qdict_get_str(dict, "str3"), ==, "bar");
+ g_assert_cmpstr(qdict_get_str(dict, "number1"), ==, "42");
+ g_assert_false(qdict_haskey(dict, "number2"));
+
+ QDECREF(dict);
+ qemu_opts_del(opts);
+}
+
+static void test_opts_to_qdict_filtered(void)
+{
+ QemuOptsList *first, *merged;
+ QemuOpts *opts;
+ QDict *dict;
+
+ first = qemu_opts_append(NULL, &opts_list_02);
+ merged = qemu_opts_append(first, &opts_list_01);
+
+ opts = qemu_opts_parse(merged,
+ "str1=foo,str2=,str3=bar,bool1=off,number1=42",
+ false, &error_abort);
+ g_assert(opts != NULL);
+
+ /* Convert to QDict without deleting from opts */
+ dict = qemu_opts_to_qdict_filtered(opts, NULL, &opts_list_01, false);
+ g_assert(dict != NULL);
+ g_assert_cmpstr(qdict_get_str(dict, "str1"), ==, "foo");
+ g_assert_cmpstr(qdict_get_str(dict, "str2"), ==, "");
+ g_assert_cmpstr(qdict_get_str(dict, "str3"), ==, "bar");
+ g_assert_cmpstr(qdict_get_str(dict, "number1"), ==, "42");
+ g_assert_false(qdict_haskey(dict, "number2"));
+ g_assert_false(qdict_haskey(dict, "bool1"));
+ QDECREF(dict);
+
+ dict = qemu_opts_to_qdict_filtered(opts, NULL, &opts_list_02, false);
+ g_assert(dict != NULL);
+ g_assert_cmpstr(qdict_get_str(dict, "str1"), ==, "foo");
+ g_assert_cmpstr(qdict_get_str(dict, "str2"), ==, "");
+ g_assert_cmpstr(qdict_get_str(dict, "bool1"), ==, "off");
+ g_assert_false(qdict_haskey(dict, "str3"));
+ g_assert_false(qdict_haskey(dict, "number1"));
+ g_assert_false(qdict_haskey(dict, "number2"));
+ QDECREF(dict);
+
+ /* Now delete converted options from opts */
+ dict = qemu_opts_to_qdict_filtered(opts, NULL, &opts_list_01, true);
+ g_assert(dict != NULL);
+ g_assert_cmpstr(qdict_get_str(dict, "str1"), ==, "foo");
+ g_assert_cmpstr(qdict_get_str(dict, "str2"), ==, "");
+ g_assert_cmpstr(qdict_get_str(dict, "str3"), ==, "bar");
+ g_assert_cmpstr(qdict_get_str(dict, "number1"), ==, "42");
+ g_assert_false(qdict_haskey(dict, "number2"));
+ g_assert_false(qdict_haskey(dict, "bool1"));
+ QDECREF(dict);
+
+ dict = qemu_opts_to_qdict_filtered(opts, NULL, &opts_list_02, true);
+ g_assert(dict != NULL);
+ g_assert_cmpstr(qdict_get_str(dict, "bool1"), ==, "off");
+ g_assert_false(qdict_haskey(dict, "str1"));
+ g_assert_false(qdict_haskey(dict, "str2"));
+ g_assert_false(qdict_haskey(dict, "str3"));
+ g_assert_false(qdict_haskey(dict, "number1"));
+ g_assert_false(qdict_haskey(dict, "number2"));
+ QDECREF(dict);
+
+ g_assert_true(QTAILQ_EMPTY(&opts->head));
+
+ qemu_opts_del(opts);
+ qemu_opts_free(merged);
+}
+
+static void test_opts_to_qdict_duplicates(void)
+{
+ QemuOpts *opts;
+ QemuOpt *opt;
+ QDict *dict;
+
+ opts = qemu_opts_parse(&opts_list_03, "foo=a,foo=b", false, &error_abort);
+ g_assert(opts != NULL);
+
+ /* Verify that opts has two options with the same name */
+ opt = QTAILQ_FIRST(&opts->head);
+ g_assert_cmpstr(opt->name, ==, "foo");
+ g_assert_cmpstr(opt->str , ==, "a");
+
+ opt = QTAILQ_NEXT(opt, next);
+ g_assert_cmpstr(opt->name, ==, "foo");
+ g_assert_cmpstr(opt->str , ==, "b");
+
+ opt = QTAILQ_NEXT(opt, next);
+ g_assert(opt == NULL);
+
+ /* In the conversion to QDict, the last one wins */
+ dict = qemu_opts_to_qdict(opts, NULL);
+ g_assert(dict != NULL);
+ g_assert_cmpstr(qdict_get_str(dict, "foo"), ==, "b");
+ QDECREF(dict);
+
+ /* The last one still wins if entries are deleted, and both are deleted */
+ dict = qemu_opts_to_qdict_filtered(opts, NULL, NULL, true);
+ g_assert(dict != NULL);
+ g_assert_cmpstr(qdict_get_str(dict, "foo"), ==, "b");
+ QDECREF(dict);
+
+ g_assert_true(QTAILQ_EMPTY(&opts->head));
+
+ qemu_opts_del(opts);
+}
+
int main(int argc, char *argv[])
{
register_opts();
@@ -761,6 +1009,11 @@ int main(int argc, char *argv[])
g_test_add_func("/qemu-opts/opts_parse/bool", test_opts_parse_bool);
g_test_add_func("/qemu-opts/opts_parse/number", test_opts_parse_number);
g_test_add_func("/qemu-opts/opts_parse/size", test_opts_parse_size);
+ g_test_add_func("/qemu-opts/append_to_null", test_opts_append_to_null);
+ g_test_add_func("/qemu-opts/append", test_opts_append);
+ g_test_add_func("/qemu-opts/to_qdict/basic", test_opts_to_qdict_basic);
+ g_test_add_func("/qemu-opts/to_qdict/filtered", test_opts_to_qdict_filtered);
+ g_test_add_func("/qemu-opts/to_qdict/duplicates", test_opts_to_qdict_duplicates);
g_test_run();
return 0;
}
diff --git a/ui/Makefile.objs b/ui/Makefile.objs
index dcd54a5287..cc784346cb 100644
--- a/ui/Makefile.objs
+++ b/ui/Makefile.objs
@@ -38,26 +38,27 @@ common-obj-$(CONFIG_GTK) += gtk.mo
gtk.mo-objs := gtk.o
gtk.mo-cflags := $(GTK_CFLAGS) $(VTE_CFLAGS)
gtk.mo-libs := $(GTK_LIBS) $(VTE_LIBS)
+ifeq ($(CONFIG_OPENGL),y)
+gtk.mo-objs += gtk-egl.o
+gtk.mo-libs += $(OPENGL_LIBS)
+ifeq ($(CONFIG_GTK_GL),y)
+gtk.mo-objs += gtk-gl-area.o
+endif
+endif
common-obj-$(CONFIG_CURSES) += curses.mo
curses.mo-objs := curses.o
curses.mo-cflags := $(CURSES_CFLAGS)
curses.mo-libs := $(CURSES_LIBS)
-ifeq ($(CONFIG_OPENGL),y)
-common-obj-y += shader.o
-common-obj-y += console-gl.o
-common-obj-y += egl-helpers.o
-common-obj-y += egl-context.o
+common-obj-$(CONFIG_OPENGL) += shader.o
+common-obj-$(CONFIG_OPENGL) += console-gl.o
+common-obj-$(CONFIG_OPENGL) += egl-helpers.o
+common-obj-$(CONFIG_OPENGL) += egl-context.o
common-obj-$(CONFIG_OPENGL_DMABUF) += egl-headless.o
-ifeq ($(CONFIG_GTK_GL),y)
-gtk.mo-objs += gtk-gl-area.o
-else
-gtk.mo-objs += gtk-egl.o
-gtk.mo-libs += $(OPENGL_LIBS)
-endif
-endif
shader.o-libs += $(OPENGL_LIBS)
console-gl.o-libs += $(OPENGL_LIBS)
egl-helpers.o-libs += $(OPENGL_LIBS)
+egl-context.o-libs += $(OPENGL_LIBS)
+egl-headless.o-libs += $(OPENGL_LIBS)
diff --git a/ui/console.c b/ui/console.c
index 6ab4ff3baf..a8868fc04f 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -344,14 +344,28 @@ write_err:
goto out;
}
-void qmp_screendump(const char *filename, Error **errp)
+void qmp_screendump(const char *filename, bool has_device, const char *device,
+ bool has_head, int64_t head, Error **errp)
{
- QemuConsole *con = qemu_console_lookup_by_index(0);
+ QemuConsole *con;
DisplaySurface *surface;
- if (con == NULL) {
- error_setg(errp, "There is no QemuConsole I can screendump from.");
- return;
+ if (has_device) {
+ con = qemu_console_lookup_by_device_name(device, has_head ? head : 0,
+ errp);
+ if (!con) {
+ return;
+ }
+ } else {
+ if (has_head) {
+ error_setg(errp, "'head' must be specified together with 'device'");
+ return;
+ }
+ con = qemu_console_lookup_by_index(0);
+ if (!con) {
+ error_setg(errp, "There is no console to take a screendump from");
+ return;
+ }
}
graphic_hw_update(con);
@@ -1039,8 +1053,10 @@ void console_select(unsigned int index)
dcl->ops->dpy_gfx_switch(dcl, s->surface);
}
}
- dpy_gfx_update(s, 0, 0, surface_width(s->surface),
- surface_height(s->surface));
+ if (s->surface) {
+ dpy_gfx_update(s, 0, 0, surface_width(s->surface),
+ surface_height(s->surface));
+ }
}
if (ds->have_text) {
dpy_text_resize(s, s->width, s->height);
@@ -1370,8 +1386,8 @@ DisplaySurface *qemu_create_displaysurface_guestmem(int width, int height,
return surface;
}
-static DisplaySurface *qemu_create_message_surface(int w, int h,
- const char *msg)
+DisplaySurface *qemu_create_message_surface(int w, int h,
+ const char *msg)
{
DisplaySurface *surface = qemu_create_displaysurface(w, h);
pixman_color_t bg = color_table_rgb[0][QEMU_COLOR_BLACK];
diff --git a/ui/gtk-egl.c b/ui/gtk-egl.c
index eb86c26a1d..9390c6762e 100644
--- a/ui/gtk-egl.c
+++ b/ui/gtk-egl.c
@@ -19,6 +19,7 @@
#include "ui/console.h"
#include "ui/gtk.h"
#include "ui/egl-helpers.h"
+#include "ui/shader.h"
#include "sysemu/sysemu.h"
@@ -194,6 +195,58 @@ void gd_egl_scanout_texture(DisplayChangeListener *dcl,
backing_id, false);
}
+void gd_egl_scanout_dmabuf(DisplayChangeListener *dcl,
+ QemuDmaBuf *dmabuf)
+{
+#ifdef CONFIG_OPENGL_DMABUF
+ egl_dmabuf_import_texture(dmabuf);
+ if (!dmabuf->texture) {
+ return;
+ }
+
+ gd_egl_scanout_texture(dcl, dmabuf->texture,
+ false, dmabuf->width, dmabuf->height,
+ 0, 0, dmabuf->width, dmabuf->height);
+#endif
+}
+
+void gd_egl_cursor_dmabuf(DisplayChangeListener *dcl,
+ QemuDmaBuf *dmabuf, bool have_hot,
+ uint32_t hot_x, uint32_t hot_y)
+{
+#ifdef CONFIG_OPENGL_DMABUF
+ VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
+
+ if (dmabuf) {
+ egl_dmabuf_import_texture(dmabuf);
+ if (!dmabuf->texture) {
+ return;
+ }
+ egl_fb_setup_for_tex(&vc->gfx.cursor_fb, dmabuf->width, dmabuf->height,
+ dmabuf->texture, false);
+ } else {
+ egl_fb_destroy(&vc->gfx.cursor_fb);
+ }
+#endif
+}
+
+void gd_egl_cursor_position(DisplayChangeListener *dcl,
+ uint32_t pos_x, uint32_t pos_y)
+{
+ VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
+
+ vc->gfx.cursor_x = pos_x;
+ vc->gfx.cursor_y = pos_y;
+}
+
+void gd_egl_release_dmabuf(DisplayChangeListener *dcl,
+ QemuDmaBuf *dmabuf)
+{
+#ifdef CONFIG_OPENGL_DMABUF
+ egl_dmabuf_release_texture(dmabuf);
+#endif
+}
+
void gd_egl_scanout_flush(DisplayChangeListener *dcl,
uint32_t x, uint32_t y, uint32_t w, uint32_t h)
{
@@ -214,7 +267,15 @@ void gd_egl_scanout_flush(DisplayChangeListener *dcl,
window = gtk_widget_get_window(vc->gfx.drawing_area);
gdk_drawable_get_size(window, &ww, &wh);
egl_fb_setup_default(&vc->gfx.win_fb, ww, wh);
- egl_fb_blit(&vc->gfx.win_fb, &vc->gfx.guest_fb, !vc->gfx.y0_top);
+ if (vc->gfx.cursor_fb.texture) {
+ egl_texture_blit(vc->gfx.gls, &vc->gfx.win_fb, &vc->gfx.guest_fb,
+ vc->gfx.y0_top);
+ egl_texture_blend(vc->gfx.gls, &vc->gfx.win_fb, &vc->gfx.cursor_fb,
+ vc->gfx.y0_top,
+ vc->gfx.cursor_x, vc->gfx.cursor_y);
+ } else {
+ egl_fb_blit(&vc->gfx.win_fb, &vc->gfx.guest_fb, !vc->gfx.y0_top);
+ }
eglSwapBuffers(qemu_egl_display, vc->gfx.esurface);
}
diff --git a/ui/gtk.c b/ui/gtk.c
index 563cff32b8..ef5bc42094 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -243,6 +243,8 @@ typedef struct VCChardev {
#define TYPE_CHARDEV_VC "chardev-vc"
#define VC_CHARDEV(obj) OBJECT_CHECK(VCChardev, (obj), TYPE_CHARDEV_VC)
+bool gtk_use_gl_area;
+
static void gd_grab_pointer(VirtualConsole *vc, const char *reason);
static void gd_ungrab_pointer(GtkDisplayState *s);
static void gd_grab_keyboard(VirtualConsole *vc, const char *reason);
@@ -453,7 +455,7 @@ static void gd_update_full_redraw(VirtualConsole *vc)
int ww, wh;
gdk_drawable_get_size(gtk_widget_get_window(area), &ww, &wh);
#if defined(CONFIG_GTK_GL)
- if (vc->gfx.gls) {
+ if (vc->gfx.gls && gtk_use_gl_area) {
gtk_gl_area_queue_render(GTK_GL_AREA(vc->gfx.drawing_area));
return;
}
@@ -725,7 +727,7 @@ static const DisplayChangeListenerOps dcl_gl_area_ops = {
.dpy_gl_update = gd_gl_area_scanout_flush,
};
-#else
+#endif /* CONFIG_GTK_GL */
static const DisplayChangeListenerOps dcl_egl_ops = {
.dpy_name = "gtk-egl",
@@ -742,10 +744,13 @@ static const DisplayChangeListenerOps dcl_egl_ops = {
.dpy_gl_ctx_get_current = qemu_egl_get_current_context,
.dpy_gl_scanout_disable = gd_egl_scanout_disable,
.dpy_gl_scanout_texture = gd_egl_scanout_texture,
+ .dpy_gl_scanout_dmabuf = gd_egl_scanout_dmabuf,
+ .dpy_gl_cursor_dmabuf = gd_egl_cursor_dmabuf,
+ .dpy_gl_cursor_position = gd_egl_cursor_position,
+ .dpy_gl_release_dmabuf = gd_egl_release_dmabuf,
.dpy_gl_update = gd_egl_scanout_flush,
};
-#endif /* CONFIG_GTK_GL */
#endif /* CONFIG_OPENGL */
/** QEMU Events **/
@@ -844,13 +849,13 @@ static gboolean gd_draw_event(GtkWidget *widget, cairo_t *cr, void *opaque)
#if defined(CONFIG_OPENGL)
if (vc->gfx.gls) {
-#if defined(CONFIG_GTK_GL)
- /* invoke render callback please */
- return FALSE;
-#else
- gd_egl_draw(vc);
- return TRUE;
-#endif
+ if (gtk_use_gl_area) {
+ /* invoke render callback please */
+ return FALSE;
+ } else {
+ gd_egl_draw(vc);
+ return TRUE;
+ }
}
#endif
@@ -1993,7 +1998,7 @@ static void gd_connect_vc_gfx_signals(VirtualConsole *vc)
g_signal_connect(vc->gfx.drawing_area, "draw",
G_CALLBACK(gd_draw_event), vc);
#if defined(CONFIG_GTK_GL)
- if (display_opengl) {
+ if (gtk_use_gl_area) {
/* wire up GtkGlArea events */
g_signal_connect(vc->gfx.drawing_area, "render",
G_CALLBACK(gd_render_event), vc);
@@ -2116,26 +2121,29 @@ static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc,
#if defined(CONFIG_OPENGL)
if (display_opengl) {
#if defined(CONFIG_GTK_GL)
- vc->gfx.drawing_area = gtk_gl_area_new();
- vc->gfx.dcl.ops = &dcl_gl_area_ops;
-#else
- vc->gfx.drawing_area = gtk_drawing_area_new();
- /*
- * gtk_widget_set_double_buffered() was deprecated in 3.14.
- * It is required for opengl rendering on X11 though. A
- * proper replacement (native opengl support) is only
- * available in 3.16+. Silence the warning if possible.
- */
+ if (gtk_use_gl_area) {
+ vc->gfx.drawing_area = gtk_gl_area_new();
+ vc->gfx.dcl.ops = &dcl_gl_area_ops;
+ } else
+#endif /* CONFIG_GTK_GL */
+ {
+ vc->gfx.drawing_area = gtk_drawing_area_new();
+ /*
+ * gtk_widget_set_double_buffered() was deprecated in 3.14.
+ * It is required for opengl rendering on X11 though. A
+ * proper replacement (native opengl support) is only
+ * available in 3.16+. Silence the warning if possible.
+ */
#ifdef CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif
- gtk_widget_set_double_buffered(vc->gfx.drawing_area, FALSE);
+ gtk_widget_set_double_buffered(vc->gfx.drawing_area, FALSE);
#ifdef CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE
#pragma GCC diagnostic pop
#endif
- vc->gfx.dcl.ops = &dcl_egl_ops;
-#endif /* CONFIG_GTK_GL */
+ vc->gfx.dcl.ops = &dcl_egl_ops;
+ }
} else
#endif
{
@@ -2436,11 +2444,15 @@ static void early_gtk_display_init(DisplayOptions *opts)
assert(opts->type == DISPLAY_TYPE_GTK);
if (opts->has_gl && opts->gl) {
#if defined(CONFIG_OPENGL)
-#if defined(CONFIG_GTK_GL)
- gtk_gl_area_init();
-#else
- gtk_egl_init();
+#if defined(CONFIG_GTK_GL) && defined(GDK_WINDOWING_WAYLAND)
+ if (GDK_IS_WAYLAND_DISPLAY(gdk_display_get_default())) {
+ gtk_use_gl_area = true;
+ gtk_gl_area_init();
+ }
#endif
+ {
+ gtk_egl_init();
+ }
#endif
}
diff --git a/ui/spice-display.c b/ui/spice-display.c
index 98ccdfb687..fe734821dd 100644
--- a/ui/spice-display.c
+++ b/ui/spice-display.c
@@ -26,20 +26,8 @@
#include "ui/spice-display.h"
-static int debug = 0;
bool spice_opengl;
-static void GCC_FMT_ATTR(2, 3) dprint(int level, const char *fmt, ...)
-{
- va_list args;
-
- if (level <= debug) {
- va_start(args, fmt);
- vfprintf(stderr, fmt, args);
- va_end(args);
- }
-}
-
int qemu_spice_rect_is_empty(const QXLRect* r)
{
return r->top == r->bottom || r->left == r->right;
@@ -322,8 +310,6 @@ void qemu_spice_create_host_memslot(SimpleSpiceDisplay *ssd)
{
QXLDevMemSlot memslot;
- dprint(1, "%s/%d:\n", __func__, ssd->qxl.id);
-
memset(&memslot, 0, sizeof(memslot));
memslot.slot_group_id = MEMSLOT_GROUP_HOST;
memslot.virt_end = ~0;
@@ -347,10 +333,6 @@ void qemu_spice_create_host_primary(SimpleSpiceDisplay *ssd)
ssd->buf = g_malloc(ssd->bufsize);
}
- dprint(1, "%s/%d: %ux%u (size %" PRIu64 "/%d)\n", __func__, ssd->qxl.id,
- surface_width(ssd->ds), surface_height(ssd->ds),
- surface_size, ssd->bufsize);
-
surface.format = SPICE_SURFACE_FMT_32_xRGB;
surface.width = surface_width(ssd->ds);
surface.height = surface_height(ssd->ds);
@@ -366,8 +348,6 @@ void qemu_spice_create_host_primary(SimpleSpiceDisplay *ssd)
void qemu_spice_destroy_host_primary(SimpleSpiceDisplay *ssd)
{
- dprint(1, "%s/%d:\n", __func__, ssd->qxl.id);
-
qemu_spice_destroy_primary_surface(ssd, 0, QXL_SYNC);
}
@@ -389,8 +369,7 @@ void qemu_spice_display_update(SimpleSpiceDisplay *ssd,
{
QXLRect update_area;
- dprint(2, "%s/%d: x %d y %d w %d h %d\n", __func__,
- ssd->qxl.id, x, y, w, h);
+ trace_qemu_spice_display_update(ssd->qxl.id, x, y, w, h);
update_area.left = x,
update_area.right = x + w;
update_area.top = y;
@@ -413,8 +392,10 @@ void qemu_spice_display_switch(SimpleSpiceDisplay *ssd,
surface_height(surface) == pixman_image_get_height(ssd->surface) &&
surface_format(surface) == pixman_image_get_format(ssd->surface)) {
/* no-resize fast path: just swap backing store */
- dprint(1, "%s/%d: fast (%dx%d)\n", __func__, ssd->qxl.id,
- surface_width(surface), surface_height(surface));
+ trace_qemu_spice_display_surface(ssd->qxl.id,
+ surface_width(surface),
+ surface_height(surface),
+ true);
qemu_mutex_lock(&ssd->lock);
ssd->ds = surface;
pixman_image_unref(ssd->surface);
@@ -427,11 +408,10 @@ void qemu_spice_display_switch(SimpleSpiceDisplay *ssd,
}
/* full mode switch */
- dprint(1, "%s/%d: full (%dx%d -> %dx%d)\n", __func__, ssd->qxl.id,
- ssd->surface ? pixman_image_get_width(ssd->surface) : 0,
- ssd->surface ? pixman_image_get_height(ssd->surface) : 0,
- surface ? surface_width(surface) : 0,
- surface ? surface_height(surface) : 0);
+ trace_qemu_spice_display_surface(ssd->qxl.id,
+ surface ? surface_width(surface) : 0,
+ surface ? surface_height(surface) : 0,
+ false);
memset(&ssd->dirty, 0, sizeof(ssd->dirty));
if (ssd->surface) {
@@ -495,7 +475,6 @@ void qemu_spice_cursor_refresh_bh(void *opaque)
void qemu_spice_display_refresh(SimpleSpiceDisplay *ssd)
{
- dprint(3, "%s/%d:\n", __func__, ssd->qxl.id);
graphic_hw_update(ssd->dcl.con);
qemu_mutex_lock(&ssd->lock);
@@ -505,10 +484,10 @@ void qemu_spice_display_refresh(SimpleSpiceDisplay *ssd)
}
qemu_mutex_unlock(&ssd->lock);
+ trace_qemu_spice_display_refresh(ssd->qxl.id, ssd->notify);
if (ssd->notify) {
ssd->notify = 0;
qemu_spice_wakeup(ssd);
- dprint(2, "%s/%d: notify\n", __func__, ssd->qxl.id);
}
}
@@ -516,21 +495,17 @@ void qemu_spice_display_refresh(SimpleSpiceDisplay *ssd)
static void interface_attach_worker(QXLInstance *sin, QXLWorker *qxl_worker)
{
- SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl);
-
- dprint(1, "%s/%d:\n", __func__, ssd->qxl.id);
+ /* nothing to do */
}
static void interface_set_compression_level(QXLInstance *sin, int level)
{
- dprint(1, "%s/%d:\n", __func__, sin->id);
/* nothing to do */
}
#if SPICE_NEEDS_SET_MM_TIME
static void interface_set_mm_time(QXLInstance *sin, uint32_t mm_time)
{
- dprint(3, "%s/%d:\n", __func__, sin->id);
/* nothing to do */
}
#endif
@@ -554,8 +529,6 @@ static int interface_get_command(QXLInstance *sin, QXLCommandExt *ext)
SimpleSpiceUpdate *update;
int ret = false;
- dprint(3, "%s/%d:\n", __func__, ssd->qxl.id);
-
qemu_mutex_lock(&ssd->lock);
update = QTAILQ_FIRST(&ssd->updates);
if (update != NULL) {
@@ -570,7 +543,6 @@ static int interface_get_command(QXLInstance *sin, QXLCommandExt *ext)
static int interface_req_cmd_notification(QXLInstance *sin)
{
- dprint(2, "%s/%d:\n", __func__, sin->id);
return 1;
}
@@ -582,7 +554,6 @@ static void interface_release_resource(QXLInstance *sin,
SimpleSpiceCursor *cursor;
QXLCommandExt *ext;
- dprint(2, "%s/%d:\n", __func__, ssd->qxl.id);
ext = (void *)(intptr_t)(rext.info->id);
switch (ext->cmd.type) {
case QXL_CMD_DRAW:
@@ -603,8 +574,6 @@ static int interface_get_cursor_command(QXLInstance *sin, QXLCommandExt *ext)
SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl);
int ret;
- dprint(3, "%s/%d:\n", __func__, ssd->qxl.id);
-
qemu_mutex_lock(&ssd->lock);
if (ssd->ptr_define) {
*ext = ssd->ptr_define->ext;
@@ -623,7 +592,6 @@ static int interface_get_cursor_command(QXLInstance *sin, QXLCommandExt *ext)
static int interface_req_cursor_notification(QXLInstance *sin)
{
- dprint(2, "%s:\n", __func__);
return 1;
}
@@ -680,7 +648,7 @@ static void interface_set_client_capabilities(QXLInstance *sin,
uint8_t client_present,
uint8_t caps[58])
{
- dprint(3, "%s:\n", __func__);
+ /* nothing to do */
}
static int interface_client_monitors_config(QXLInstance *sin,
@@ -705,9 +673,9 @@ static int interface_client_monitors_config(QXLInstance *sin,
info.width = mc->monitors[head].width;
info.height = mc->monitors[head].height;
}
+
+ trace_qemu_spice_ui_info(ssd->qxl.id, info.width, info.height);
dpy_set_ui_info(ssd->dcl.con, &info);
- dprint(1, "%s/%d: size %dx%d\n", __func__, ssd->qxl.id,
- info.width, info.height);
return 1;
}
@@ -902,9 +870,10 @@ static void spice_gl_switch(DisplayChangeListener *dcl,
return;
}
- dprint(1, "%s: %dx%d (stride %d/%d, fourcc 0x%x)\n", __func__,
- surface_width(ssd->ds), surface_height(ssd->ds),
- surface_stride(ssd->ds), stride, fourcc);
+ trace_qemu_spice_gl_surface(ssd->qxl.id,
+ surface_width(ssd->ds),
+ surface_height(ssd->ds),
+ fourcc);
/* note: spice server will close the fd */
spice_qxl_gl_scanout(&ssd->qxl, fd,
@@ -932,7 +901,7 @@ static void qemu_spice_gl_scanout_disable(DisplayChangeListener *dcl)
{
SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl);
- dprint(1, "%s: no framebuffer\n", __func__);
+ trace_qemu_spice_gl_scanout_disable(ssd->qxl.id);
spice_qxl_gl_scanout(&ssd->qxl, -1, 0, 0, 0, 0, false);
qemu_spice_gl_monitor_config(ssd, 0, 0, 0, 0);
ssd->have_surface = false;
@@ -957,8 +926,7 @@ static void qemu_spice_gl_scanout_texture(DisplayChangeListener *dcl,
fprintf(stderr, "%s: failed to get fd for texture\n", __func__);
return;
}
- dprint(1, "%s: %dx%d (stride %d, fourcc 0x%x)\n", __func__,
- w, h, stride, fourcc);
+ trace_qemu_spice_gl_scanout_texture(ssd->qxl.id, w, h, fourcc);
/* note: spice server will close the fd */
spice_qxl_gl_scanout(&ssd->qxl, fd, backing_width, backing_height,
@@ -968,17 +936,133 @@ static void qemu_spice_gl_scanout_texture(DisplayChangeListener *dcl,
ssd->have_scanout = true;
}
+static void qemu_spice_gl_scanout_dmabuf(DisplayChangeListener *dcl,
+ QemuDmaBuf *dmabuf)
+{
+ SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl);
+
+ ssd->guest_dmabuf = dmabuf;
+ ssd->guest_dmabuf_refresh = true;
+
+ ssd->have_surface = false;
+ ssd->have_scanout = true;
+}
+
+static void qemu_spice_gl_cursor_dmabuf(DisplayChangeListener *dcl,
+ QemuDmaBuf *dmabuf, bool have_hot,
+ uint32_t hot_x, uint32_t hot_y)
+{
+ SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl);
+
+ ssd->have_hot = have_hot;
+ ssd->hot_x = hot_x;
+ ssd->hot_y = hot_y;
+
+ trace_qemu_spice_gl_cursor(ssd->qxl.id, dmabuf != NULL, have_hot);
+ if (dmabuf) {
+ egl_dmabuf_import_texture(dmabuf);
+ if (!dmabuf->texture) {
+ return;
+ }
+ egl_fb_setup_for_tex(&ssd->cursor_fb, dmabuf->width, dmabuf->height,
+ dmabuf->texture, false);
+ } else {
+ egl_fb_destroy(&ssd->cursor_fb);
+ }
+}
+
+static void qemu_spice_gl_cursor_position(DisplayChangeListener *dcl,
+ uint32_t pos_x, uint32_t pos_y)
+{
+ SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl);
+
+ ssd->ptr_x = pos_x;
+ ssd->ptr_y = pos_y;
+}
+
+static void qemu_spice_gl_release_dmabuf(DisplayChangeListener *dcl,
+ QemuDmaBuf *dmabuf)
+{
+ SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl);
+
+ if (ssd->guest_dmabuf == dmabuf) {
+ ssd->guest_dmabuf = NULL;
+ ssd->guest_dmabuf_refresh = false;
+ }
+ egl_dmabuf_release_texture(dmabuf);
+}
+
static void qemu_spice_gl_update(DisplayChangeListener *dcl,
uint32_t x, uint32_t y, uint32_t w, uint32_t h)
{
SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl);
+ EGLint stride = 0, fourcc = 0;
+ bool render_cursor = false;
+ bool y_0_top = false; /* FIXME */
uint64_t cookie;
+ int fd;
if (!ssd->have_scanout) {
return;
}
- dprint(2, "%s: %dx%d+%d+%d\n", __func__, w, h, x, y);
+ if (ssd->cursor_fb.texture) {
+ render_cursor = true;
+ }
+ if (ssd->render_cursor != render_cursor) {
+ ssd->render_cursor = render_cursor;
+ ssd->guest_dmabuf_refresh = true;
+ egl_fb_destroy(&ssd->blit_fb);
+ }
+
+ if (ssd->guest_dmabuf_refresh) {
+ QemuDmaBuf *dmabuf = ssd->guest_dmabuf;
+ if (render_cursor) {
+ egl_dmabuf_import_texture(dmabuf);
+ if (!dmabuf->texture) {
+ return;
+ }
+
+ /* source framebuffer */
+ egl_fb_setup_for_tex(&ssd->guest_fb,
+ dmabuf->width, dmabuf->height,
+ dmabuf->texture, false);
+
+ /* dest framebuffer */
+ if (ssd->blit_fb.width != dmabuf->width ||
+ ssd->blit_fb.height != dmabuf->height) {
+ trace_qemu_spice_gl_render_dmabuf(ssd->qxl.id, dmabuf->width,
+ dmabuf->height);
+ egl_fb_destroy(&ssd->blit_fb);
+ egl_fb_setup_new_tex(&ssd->blit_fb,
+ dmabuf->width, dmabuf->height);
+ fd = egl_get_fd_for_texture(ssd->blit_fb.texture,
+ &stride, &fourcc);
+ spice_qxl_gl_scanout(&ssd->qxl, fd,
+ dmabuf->width, dmabuf->height,
+ stride, fourcc, false);
+ }
+ } else {
+ trace_qemu_spice_gl_forward_dmabuf(ssd->qxl.id,
+ dmabuf->width, dmabuf->height);
+ /* note: spice server will close the fd, so hand over a dup */
+ spice_qxl_gl_scanout(&ssd->qxl, dup(dmabuf->fd),
+ dmabuf->width, dmabuf->height,
+ dmabuf->stride, dmabuf->fourcc, false);
+ }
+ qemu_spice_gl_monitor_config(ssd, 0, 0, dmabuf->width, dmabuf->height);
+ ssd->guest_dmabuf_refresh = false;
+ }
+
+ if (render_cursor) {
+ egl_texture_blit(ssd->gls, &ssd->blit_fb, &ssd->guest_fb,
+ !y_0_top);
+ egl_texture_blend(ssd->gls, &ssd->blit_fb, &ssd->cursor_fb,
+ !y_0_top, ssd->ptr_x, ssd->ptr_y);
+ glFlush();
+ }
+
+ trace_qemu_spice_gl_update(ssd->qxl.id, w, h, x, y);
qemu_spice_gl_block(ssd, true);
cookie = (uintptr_t)qxl_cookie_new(QXL_COOKIE_TYPE_GL_DRAW_DONE, 0);
spice_qxl_gl_draw_async(&ssd->qxl, x, y, w, h, cookie);
@@ -1000,6 +1084,10 @@ static const DisplayChangeListenerOps display_listener_gl_ops = {
.dpy_gl_scanout_disable = qemu_spice_gl_scanout_disable,
.dpy_gl_scanout_texture = qemu_spice_gl_scanout_texture,
+ .dpy_gl_scanout_dmabuf = qemu_spice_gl_scanout_dmabuf,
+ .dpy_gl_cursor_dmabuf = qemu_spice_gl_cursor_dmabuf,
+ .dpy_gl_cursor_position = qemu_spice_gl_cursor_position,
+ .dpy_gl_release_dmabuf = qemu_spice_gl_release_dmabuf,
.dpy_gl_update = qemu_spice_gl_update,
};
diff --git a/ui/trace-events b/ui/trace-events
index 861b68a305..a957f363f1 100644
--- a/ui/trace-events
+++ b/ui/trace-events
@@ -75,6 +75,18 @@ qemu_spice_create_primary_surface(int qid, uint32_t sid, void *surface, int asyn
qemu_spice_destroy_primary_surface(int qid, uint32_t sid, int async) "%d sid=%u async=%d"
qemu_spice_wakeup(uint32_t qid) "%d"
qemu_spice_create_update(uint32_t left, uint32_t right, uint32_t top, uint32_t bottom) "lr %d -> %d, tb -> %d -> %d"
+qemu_spice_display_update(int qid, uint32_t x, uint32_t y, uint32_t w, uint32_t h) "%d +%d+%d %dx%d"
+qemu_spice_display_surface(int qid, uint32_t w, uint32_t h, int fast) "%d %dx%d, fast %d"
+qemu_spice_display_refresh(int qid, int notify) "%d notify %d"
+qemu_spice_ui_info(int qid, uint32_t width, uint32_t height) "%d %dx%d"
+
+qemu_spice_gl_surface(int qid, uint32_t w, uint32_t h, uint32_t fourcc) "%d %dx%d, fourcc 0x%x"
+qemu_spice_gl_scanout_disable(int qid) "%d"
+qemu_spice_gl_scanout_texture(int qid, uint32_t w, uint32_t h, uint32_t fourcc) "%d %dx%d, fourcc 0x%x"
+qemu_spice_gl_cursor(int qid, bool enabled, bool hotspot) "%d enabled %d, hotspot %d"
+qemu_spice_gl_forward_dmabuf(int qid, uint32_t width, uint32_t height) "%d %dx%d"
+qemu_spice_gl_render_dmabuf(int qid, uint32_t width, uint32_t height) "%d %dx%d"
+qemu_spice_gl_update(int qid, uint32_t x, uint32_t y, uint32_t w, uint32_t h) "%d +%d+%d %dx%d"
# ui/keymaps.c
keymap_parse(const char *file) "file %s"
diff --git a/ui/vnc.c b/ui/vnc.c
index 13c28cabb0..e164eb798c 100644
--- a/ui/vnc.c
+++ b/ui/vnc.c
@@ -746,9 +746,19 @@ static void vnc_update_server_surface(VncDisplay *vd)
static void vnc_dpy_switch(DisplayChangeListener *dcl,
DisplaySurface *surface)
{
+ static const char placeholder_msg[] =
+ "Display output is not active.";
+ static DisplaySurface *placeholder;
VncDisplay *vd = container_of(dcl, VncDisplay, dcl);
VncState *vs;
+ if (surface == NULL) {
+ if (placeholder == NULL) {
+ placeholder = qemu_create_message_surface(640, 480, placeholder_msg);
+ }
+ surface = placeholder;
+ }
+
vnc_abort_display_jobs(vd);
vd->ds = surface;
diff --git a/util/coroutine-ucontext.c b/util/coroutine-ucontext.c
index 926d3402e3..090ba21a13 100644
--- a/util/coroutine-ucontext.c
+++ b/util/coroutine-ucontext.c
@@ -170,7 +170,7 @@ Coroutine *qemu_coroutine_new(void)
}
#ifdef CONFIG_VALGRIND_H
-#ifdef CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE
+#if defined(CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE) && !defined(__clang__)
/* Work around an unused variable in the valgrind.h macro... */
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
@@ -179,7 +179,7 @@ static inline void valgrind_stack_deregister(CoroutineUContext *co)
{
VALGRIND_STACK_DEREGISTER(co->valgrind_stack_id);
}
-#ifdef CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE
+#if defined(CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE) && !defined(__clang__)
#pragma GCC diagnostic pop
#endif
#endif
diff --git a/util/qemu-option.c b/util/qemu-option.c
index a401e936da..2b412eff5e 100644
--- a/util/qemu-option.c
+++ b/util/qemu-option.c
@@ -1007,14 +1007,23 @@ void qemu_opts_absorb_qdict(QemuOpts *opts, QDict *qdict, Error **errp)
}
/*
- * Convert from QemuOpts to QDict.
- * The QDict values are of type QString.
+ * Convert from QemuOpts to QDict. The QDict values are of type QString.
+ *
+ * If @list is given, only add those options to the QDict that are contained in
+ * the list. If @del is true, any options added to the QDict are removed from
+ * the QemuOpts, otherwise they remain there.
+ *
+ * If two options in @opts have the same name, they are processed in order
+ * so that the last one wins (consistent with the reverse iteration in
+ * qemu_opt_find()), but all of them are deleted if @del is true.
+ *
* TODO We'll want to use types appropriate for opt->desc->type, but
* this is enough for now.
*/
-QDict *qemu_opts_to_qdict(QemuOpts *opts, QDict *qdict)
+QDict *qemu_opts_to_qdict_filtered(QemuOpts *opts, QDict *qdict,
+ QemuOptsList *list, bool del)
{
- QemuOpt *opt;
+ QemuOpt *opt, *next;
if (!qdict) {
qdict = qdict_new();
@@ -1022,12 +1031,35 @@ QDict *qemu_opts_to_qdict(QemuOpts *opts, QDict *qdict)
if (opts->id) {
qdict_put_str(qdict, "id", opts->id);
}
- QTAILQ_FOREACH(opt, &opts->head, next) {
+ QTAILQ_FOREACH_SAFE(opt, &opts->head, next, next) {
+ if (list) {
+ QemuOptDesc *desc;
+ bool found = false;
+ for (desc = list->desc; desc->name; desc++) {
+ if (!strcmp(desc->name, opt->name)) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ continue;
+ }
+ }
qdict_put_str(qdict, opt->name, opt->str);
+ if (del) {
+ qemu_opt_del(opt);
+ }
}
return qdict;
}
+/* Copy all options in a QemuOpts to the given QDict. See
+ * qemu_opts_to_qdict_filtered() for details. */
+QDict *qemu_opts_to_qdict(QemuOpts *opts, QDict *qdict)
+{
+ return qemu_opts_to_qdict_filtered(opts, qdict, NULL, false);
+}
+
/* Validate parsed opts against descriptions where no
* descriptions were provided in the QemuOptsList.
*/