aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--MAINTAINERS1
-rw-r--r--block.c25
-rw-r--r--block/file-posix.c64
-rw-r--r--block/qcow2-bitmap.c1
-rw-r--r--block/qcow2-refcount.c25
-rw-r--r--block/qcow2.c74
-rw-r--r--block/throttle.c54
-rw-r--r--hw/i386/pc_piix.c1
-rw-r--r--hw/i386/pc_q35.c1
-rw-r--r--hw/ide/ahci.c373
-rw-r--r--hw/ide/ahci_internal.h63
-rw-r--r--hw/ide/atapi.c44
-rw-r--r--hw/ide/core.c39
-rw-r--r--hw/ide/trace-events15
-rw-r--r--include/block/block.h4
-rw-r--r--include/block/block_int.h3
-rw-r--r--include/hw/ide/internal.h4
-rw-r--r--include/qemu-io.h9
-rw-r--r--qemu-doc.texi7
-rw-r--r--qemu-img-cmds.hx4
-rw-r--r--qemu-img.c108
-rw-r--r--qemu-img.texi7
-rw-r--r--qemu-io-cmds.c276
-rw-r--r--qemu-io.c62
-rw-r--r--target/i386/cpu.c234
-rw-r--r--target/i386/cpu.h14
-rw-r--r--target/i386/kvm.c29
-rw-r--r--target/m68k/translate.c356
-rw-r--r--tests/Makefile.include2
-rw-r--r--tests/boot-sector.c9
-rw-r--r--tests/cdrom-test.c222
-rw-r--r--tests/libqos/ahci.c45
-rw-r--r--tests/libqos/ahci.h3
-rwxr-xr-xtests/qemu-iotests/02482
-rw-r--r--tests/qemu-iotests/024.out30
-rwxr-xr-xtests/qemu-iotests/0292
-rwxr-xr-xtests/qemu-iotests/06030
-rw-r--r--tests/qemu-iotests/060.out18
-rw-r--r--tests/qemu-iotests/061.out7
-rwxr-xr-xtests/qemu-iotests/0804
-rw-r--r--tests/qemu-iotests/080.out4
-rwxr-xr-xtests/qemu-iotests/0829
-rw-r--r--tests/qemu-iotests/082.out53
-rw-r--r--tests/qemu-iotests/112.out5
-rwxr-xr-xtests/qemu-iotests/11319
-rw-r--r--tests/qemu-iotests/113.out7
-rwxr-xr-xtests/qemu-iotests/12242
-rw-r--r--tests/qemu-iotests/122.out18
-rwxr-xr-xtests/qemu-iotests/15318
-rw-r--r--tests/qemu-iotests/153.out13
-rwxr-xr-xtests/qemu-iotests/21623
-rw-r--r--tests/qemu-iotests/216.out17
-rwxr-xr-xtests/qemu-iotests/21790
-rw-r--r--tests/qemu-iotests/217.out42
-rwxr-xr-xtests/qemu-iotests/21926
-rw-r--r--tests/qemu-iotests/219.out10
-rw-r--r--tests/qemu-iotests/group1
-rw-r--r--tests/qemu-iotests/iotests.py18
-rw-r--r--util/qemu-option.c1
-rw-r--r--vl.c4
60 files changed, 1936 insertions, 835 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index 4c73c16fee..a40f558694 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1003,6 +1003,7 @@ F: hw/block/cdrom.c
F: hw/block/hd-geometry.c
F: tests/ide-test.c
F: tests/ahci-test.c
+F: tests/cdrom-test.c
F: tests/libqos/ahci*
T: git git://github.com/jnsnow/qemu.git ide
diff --git a/block.c b/block.c
index 501b64c819..50887087f3 100644
--- a/block.c
+++ b/block.c
@@ -1620,13 +1620,24 @@ static int bdrv_reopen_get_flags(BlockReopenQueue *q, BlockDriverState *bs)
/* Returns whether the image file can be written to after the reopen queue @q
* has been successfully applied, or right now if @q is NULL. */
-static bool bdrv_is_writable(BlockDriverState *bs, BlockReopenQueue *q)
+static bool bdrv_is_writable_after_reopen(BlockDriverState *bs,
+ BlockReopenQueue *q)
{
int flags = bdrv_reopen_get_flags(q, bs);
return (flags & (BDRV_O_RDWR | BDRV_O_INACTIVE)) == BDRV_O_RDWR;
}
+/*
+ * Return whether the BDS can be written to. This is not necessarily
+ * the same as !bdrv_is_read_only(bs), as inactivated images may not
+ * be written to but do not count as read-only images.
+ */
+bool bdrv_is_writable(BlockDriverState *bs)
+{
+ return bdrv_is_writable_after_reopen(bs, NULL);
+}
+
static void bdrv_child_perm(BlockDriverState *bs, BlockDriverState *child_bs,
BdrvChild *c, const BdrvChildRole *role,
BlockReopenQueue *reopen_queue,
@@ -1664,7 +1675,7 @@ static int bdrv_check_perm(BlockDriverState *bs, BlockReopenQueue *q,
/* Write permissions never work with read-only images */
if ((cumulative_perms & (BLK_PERM_WRITE | BLK_PERM_WRITE_UNCHANGED)) &&
- !bdrv_is_writable(bs, q))
+ !bdrv_is_writable_after_reopen(bs, q))
{
error_setg(errp, "Block node is read-only");
return -EPERM;
@@ -1956,7 +1967,7 @@ void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c,
&perm, &shared);
/* Format drivers may touch metadata even if the guest doesn't write */
- if (bdrv_is_writable(bs, reopen_queue)) {
+ if (bdrv_is_writable_after_reopen(bs, reopen_queue)) {
perm |= BLK_PERM_WRITE | BLK_PERM_RESIZE;
}
@@ -4996,15 +5007,19 @@ void bdrv_remove_aio_context_notifier(BlockDriverState *bs,
}
int bdrv_amend_options(BlockDriverState *bs, QemuOpts *opts,
- BlockDriverAmendStatusCB *status_cb, void *cb_opaque)
+ BlockDriverAmendStatusCB *status_cb, void *cb_opaque,
+ Error **errp)
{
if (!bs->drv) {
+ error_setg(errp, "Node is ejected");
return -ENOMEDIUM;
}
if (!bs->drv->bdrv_amend_options) {
+ error_setg(errp, "Block driver '%s' does not support option amendment",
+ bs->drv->format_name);
return -ENOTSUP;
}
- return bs->drv->bdrv_amend_options(bs, opts, status_cb, cb_opaque);
+ return bs->drv->bdrv_amend_options(bs, opts, status_cb, cb_opaque, errp);
}
/* This function will be called by the bdrv_recurse_is_first_non_filter method
diff --git a/block/file-posix.c b/block/file-posix.c
index 513d371bb1..07bb061fe4 100644
--- a/block/file-posix.c
+++ b/block/file-posix.c
@@ -643,7 +643,7 @@ typedef enum {
* file; if @unlock == true, also unlock the unneeded bytes.
* @shared_perm_lock_bits is the mask of all permissions that are NOT shared.
*/
-static int raw_apply_lock_bytes(BDRVRawState *s,
+static int raw_apply_lock_bytes(int fd,
uint64_t perm_lock_bits,
uint64_t shared_perm_lock_bits,
bool unlock, Error **errp)
@@ -654,13 +654,13 @@ static int raw_apply_lock_bytes(BDRVRawState *s,
PERM_FOREACH(i) {
int off = RAW_LOCK_PERM_BASE + i;
if (perm_lock_bits & (1ULL << i)) {
- ret = qemu_lock_fd(s->lock_fd, off, 1, false);
+ ret = qemu_lock_fd(fd, off, 1, false);
if (ret) {
error_setg(errp, "Failed to lock byte %d", off);
return ret;
}
} else if (unlock) {
- ret = qemu_unlock_fd(s->lock_fd, off, 1);
+ ret = qemu_unlock_fd(fd, off, 1);
if (ret) {
error_setg(errp, "Failed to unlock byte %d", off);
return ret;
@@ -670,13 +670,13 @@ static int raw_apply_lock_bytes(BDRVRawState *s,
PERM_FOREACH(i) {
int off = RAW_LOCK_SHARED_BASE + i;
if (shared_perm_lock_bits & (1ULL << i)) {
- ret = qemu_lock_fd(s->lock_fd, off, 1, false);
+ ret = qemu_lock_fd(fd, off, 1, false);
if (ret) {
error_setg(errp, "Failed to lock byte %d", off);
return ret;
}
} else if (unlock) {
- ret = qemu_unlock_fd(s->lock_fd, off, 1);
+ ret = qemu_unlock_fd(fd, off, 1);
if (ret) {
error_setg(errp, "Failed to unlock byte %d", off);
return ret;
@@ -687,8 +687,7 @@ static int raw_apply_lock_bytes(BDRVRawState *s,
}
/* Check "unshared" bytes implied by @perm and ~@shared_perm in the file. */
-static int raw_check_lock_bytes(BDRVRawState *s,
- uint64_t perm, uint64_t shared_perm,
+static int raw_check_lock_bytes(int fd, uint64_t perm, uint64_t shared_perm,
Error **errp)
{
int ret;
@@ -698,7 +697,7 @@ static int raw_check_lock_bytes(BDRVRawState *s,
int off = RAW_LOCK_SHARED_BASE + i;
uint64_t p = 1ULL << i;
if (perm & p) {
- ret = qemu_lock_fd_test(s->lock_fd, off, 1, true);
+ ret = qemu_lock_fd_test(fd, off, 1, true);
if (ret) {
char *perm_name = bdrv_perm_names(p);
error_setg(errp,
@@ -715,7 +714,7 @@ static int raw_check_lock_bytes(BDRVRawState *s,
int off = RAW_LOCK_PERM_BASE + i;
uint64_t p = 1ULL << i;
if (!(shared_perm & p)) {
- ret = qemu_lock_fd_test(s->lock_fd, off, 1, true);
+ ret = qemu_lock_fd_test(fd, off, 1, true);
if (ret) {
char *perm_name = bdrv_perm_names(p);
error_setg(errp,
@@ -752,11 +751,11 @@ static int raw_handle_perm_lock(BlockDriverState *bs,
switch (op) {
case RAW_PL_PREPARE:
- ret = raw_apply_lock_bytes(s, s->perm | new_perm,
+ ret = raw_apply_lock_bytes(s->lock_fd, s->perm | new_perm,
~s->shared_perm | ~new_shared,
false, errp);
if (!ret) {
- ret = raw_check_lock_bytes(s, new_perm, new_shared, errp);
+ ret = raw_check_lock_bytes(s->lock_fd, new_perm, new_shared, errp);
if (!ret) {
return 0;
}
@@ -764,7 +763,8 @@ static int raw_handle_perm_lock(BlockDriverState *bs,
op = RAW_PL_ABORT;
/* fall through to unlock bytes. */
case RAW_PL_ABORT:
- raw_apply_lock_bytes(s, s->perm, ~s->shared_perm, true, &local_err);
+ raw_apply_lock_bytes(s->lock_fd, s->perm, ~s->shared_perm,
+ true, &local_err);
if (local_err) {
/* Theoretically the above call only unlocks bytes and it cannot
* fail. Something weird happened, report it.
@@ -773,7 +773,8 @@ static int raw_handle_perm_lock(BlockDriverState *bs,
}
break;
case RAW_PL_COMMIT:
- raw_apply_lock_bytes(s, new_perm, ~new_shared, true, &local_err);
+ raw_apply_lock_bytes(s->lock_fd, new_perm, ~new_shared,
+ true, &local_err);
if (local_err) {
/* Theoretically the above call only unlocks bytes and it cannot
* fail. Something weird happened, report it.
@@ -2075,6 +2076,7 @@ static int raw_co_create(BlockdevCreateOptions *options, Error **errp)
{
BlockdevCreateOptionsFile *file_opts;
int fd;
+ int perm, shared;
int result = 0;
/* Validate options and set default values */
@@ -2089,14 +2091,44 @@ static int raw_co_create(BlockdevCreateOptions *options, Error **errp)
}
/* Create file */
- fd = qemu_open(file_opts->filename, O_RDWR | O_CREAT | O_TRUNC | O_BINARY,
- 0644);
+ fd = qemu_open(file_opts->filename, O_RDWR | O_CREAT | O_BINARY, 0644);
if (fd < 0) {
result = -errno;
error_setg_errno(errp, -result, "Could not create file");
goto out;
}
+ /* Take permissions: We want to discard everything, so we need
+ * BLK_PERM_WRITE; and truncation to the desired size requires
+ * BLK_PERM_RESIZE.
+ * On the other hand, we cannot share the RESIZE permission
+ * because we promise that after this function, the file has the
+ * size given in the options. If someone else were to resize it
+ * concurrently, we could not guarantee that.
+ * Note that after this function, we can no longer guarantee that
+ * the file is not touched by a third party, so it may be resized
+ * then. */
+ perm = BLK_PERM_WRITE | BLK_PERM_RESIZE;
+ shared = BLK_PERM_ALL & ~BLK_PERM_RESIZE;
+
+ /* Step one: Take locks */
+ result = raw_apply_lock_bytes(fd, perm, shared, false, errp);
+ if (result < 0) {
+ goto out_close;
+ }
+
+ /* Step two: Check that nobody else has taken conflicting locks */
+ result = raw_check_lock_bytes(fd, perm, shared, errp);
+ if (result < 0) {
+ goto out_close;
+ }
+
+ /* Clear the file by truncating it to 0 */
+ result = raw_regular_truncate(fd, 0, PREALLOC_MODE_OFF, errp);
+ if (result < 0) {
+ goto out_close;
+ }
+
if (file_opts->nocow) {
#ifdef __linux__
/* Set NOCOW flag to solve performance issue on fs like btrfs.
@@ -2112,6 +2144,8 @@ static int raw_co_create(BlockdevCreateOptions *options, Error **errp)
#endif
}
+ /* Resize and potentially preallocate the file to the desired
+ * final size */
result = raw_regular_truncate(fd, file_opts->size, file_opts->preallocation,
errp);
if (result < 0) {
diff --git a/block/qcow2-bitmap.c b/block/qcow2-bitmap.c
index 60d5290f10..69485aa1de 100644
--- a/block/qcow2-bitmap.c
+++ b/block/qcow2-bitmap.c
@@ -254,7 +254,6 @@ static int free_bitmap_clusters(BlockDriverState *bs, Qcow2BitmapTable *tb)
ret = bitmap_table_load(bs, tb, &bitmap_table);
if (ret < 0) {
- assert(bitmap_table == NULL);
return ret;
}
diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c
index 403236256b..18c729aa27 100644
--- a/block/qcow2-refcount.c
+++ b/block/qcow2-refcount.c
@@ -1799,6 +1799,19 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res,
int ret;
uint64_t refcount;
int i, j;
+ bool repair;
+
+ if (fix & BDRV_FIX_ERRORS) {
+ /* Always repair */
+ repair = true;
+ } else if (fix & BDRV_FIX_LEAKS) {
+ /* Repair only if that seems safe: This function is always
+ * called after the refcounts have been fixed, so the refcount
+ * is accurate if that repair was successful */
+ repair = !res->check_errors && !res->corruptions && !res->leaks;
+ } else {
+ repair = false;
+ }
for (i = 0; i < s->l1_size; i++) {
uint64_t l1_entry = s->l1_table[i];
@@ -1818,10 +1831,8 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res,
if ((refcount == 1) != ((l1_entry & QCOW_OFLAG_COPIED) != 0)) {
fprintf(stderr, "%s OFLAG_COPIED L2 cluster: l1_index=%d "
"l1_entry=%" PRIx64 " refcount=%" PRIu64 "\n",
- fix & BDRV_FIX_ERRORS ? "Repairing" :
- "ERROR",
- i, l1_entry, refcount);
- if (fix & BDRV_FIX_ERRORS) {
+ repair ? "Repairing" : "ERROR", i, l1_entry, refcount);
+ if (repair) {
s->l1_table[i] = refcount == 1
? l1_entry | QCOW_OFLAG_COPIED
: l1_entry & ~QCOW_OFLAG_COPIED;
@@ -1862,10 +1873,8 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res,
if ((refcount == 1) != ((l2_entry & QCOW_OFLAG_COPIED) != 0)) {
fprintf(stderr, "%s OFLAG_COPIED data cluster: "
"l2_entry=%" PRIx64 " refcount=%" PRIu64 "\n",
- fix & BDRV_FIX_ERRORS ? "Repairing" :
- "ERROR",
- l2_entry, refcount);
- if (fix & BDRV_FIX_ERRORS) {
+ repair ? "Repairing" : "ERROR", l2_entry, refcount);
+ if (repair) {
l2_table[j] = cpu_to_be64(refcount == 1
? l2_entry | QCOW_OFLAG_COPIED
: l2_entry & ~QCOW_OFLAG_COPIED);
diff --git a/block/qcow2.c b/block/qcow2.c
index 549fee9b69..6fa5e1d71a 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -4215,22 +4215,21 @@ static int qcow2_load_vmstate(BlockDriverState *bs, QEMUIOVector *qiov,
* have to be removed.
*/
static int qcow2_downgrade(BlockDriverState *bs, int target_version,
- BlockDriverAmendStatusCB *status_cb, void *cb_opaque)
+ BlockDriverAmendStatusCB *status_cb, void *cb_opaque,
+ Error **errp)
{
BDRVQcow2State *s = bs->opaque;
int current_version = s->qcow_version;
int ret;
- if (target_version == current_version) {
- return 0;
- } else if (target_version > current_version) {
- return -EINVAL;
- } else if (target_version != 2) {
- return -EINVAL;
- }
+ /* This is qcow2_downgrade(), not qcow2_upgrade() */
+ assert(target_version < current_version);
+
+ /* There are no other versions (now) that you can downgrade to */
+ assert(target_version == 2);
if (s->refcount_order != 4) {
- error_report("compat=0.10 requires refcount_bits=16");
+ error_setg(errp, "compat=0.10 requires refcount_bits=16");
return -ENOTSUP;
}
@@ -4238,6 +4237,7 @@ static int qcow2_downgrade(BlockDriverState *bs, int target_version,
if (s->incompatible_features & QCOW2_INCOMPAT_DIRTY) {
ret = qcow2_mark_clean(bs);
if (ret < 0) {
+ error_setg_errno(errp, -ret, "Failed to make the image clean");
return ret;
}
}
@@ -4247,6 +4247,8 @@ static int qcow2_downgrade(BlockDriverState *bs, int target_version,
* best thing to do anyway */
if (s->incompatible_features) {
+ error_setg(errp, "Cannot downgrade an image with incompatible features "
+ "%#" PRIx64 " set", s->incompatible_features);
return -ENOTSUP;
}
@@ -4260,6 +4262,7 @@ static int qcow2_downgrade(BlockDriverState *bs, int target_version,
ret = qcow2_expand_zero_clusters(bs, status_cb, cb_opaque);
if (ret < 0) {
+ error_setg_errno(errp, -ret, "Failed to turn zero into data clusters");
return ret;
}
@@ -4267,6 +4270,7 @@ static int qcow2_downgrade(BlockDriverState *bs, int target_version,
ret = qcow2_update_header(bs);
if (ret < 0) {
s->qcow_version = current_version;
+ error_setg_errno(errp, -ret, "Failed to update the image header");
return ret;
}
return 0;
@@ -4344,7 +4348,8 @@ static void qcow2_amend_helper_cb(BlockDriverState *bs,
static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
BlockDriverAmendStatusCB *status_cb,
- void *cb_opaque)
+ void *cb_opaque,
+ Error **errp)
{
BDRVQcow2State *s = bs->opaque;
int old_version = s->qcow_version, new_version = old_version;
@@ -4356,7 +4361,6 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
bool encrypt;
int encformat;
int refcount_bits = s->refcount_bits;
- Error *local_err = NULL;
int ret;
QemuOptDesc *desc = opts->list->desc;
Qcow2AmendHelperCBInfo helper_cb_info;
@@ -4377,11 +4381,11 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
} else if (!strcmp(compat, "1.1")) {
new_version = 3;
} else {
- error_report("Unknown compatibility level %s", compat);
+ error_setg(errp, "Unknown compatibility level %s", compat);
return -EINVAL;
}
} else if (!strcmp(desc->name, BLOCK_OPT_PREALLOC)) {
- error_report("Cannot change preallocation mode");
+ error_setg(errp, "Cannot change preallocation mode");
return -ENOTSUP;
} else if (!strcmp(desc->name, BLOCK_OPT_SIZE)) {
new_size = qemu_opt_get_size(opts, BLOCK_OPT_SIZE, 0);
@@ -4394,7 +4398,8 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
!!s->crypto);
if (encrypt != !!s->crypto) {
- error_report("Changing the encryption flag is not supported");
+ error_setg(errp,
+ "Changing the encryption flag is not supported");
return -ENOTSUP;
}
} else if (!strcmp(desc->name, BLOCK_OPT_ENCRYPT_FORMAT)) {
@@ -4402,17 +4407,19 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
qemu_opt_get(opts, BLOCK_OPT_ENCRYPT_FORMAT));
if (encformat != s->crypt_method_header) {
- error_report("Changing the encryption format is not supported");
+ error_setg(errp,
+ "Changing the encryption format is not supported");
return -ENOTSUP;
}
} else if (g_str_has_prefix(desc->name, "encrypt.")) {
- error_report("Changing the encryption parameters is not supported");
+ error_setg(errp,
+ "Changing the encryption parameters is not supported");
return -ENOTSUP;
} else if (!strcmp(desc->name, BLOCK_OPT_CLUSTER_SIZE)) {
cluster_size = qemu_opt_get_size(opts, BLOCK_OPT_CLUSTER_SIZE,
cluster_size);
if (cluster_size != s->cluster_size) {
- error_report("Changing the cluster size is not supported");
+ error_setg(errp, "Changing the cluster size is not supported");
return -ENOTSUP;
}
} else if (!strcmp(desc->name, BLOCK_OPT_LAZY_REFCOUNTS)) {
@@ -4425,8 +4432,8 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
if (refcount_bits <= 0 || refcount_bits > 64 ||
!is_power_of_2(refcount_bits))
{
- error_report("Refcount width must be a power of two and may "
- "not exceed 64 bits");
+ error_setg(errp, "Refcount width must be a power of two and "
+ "may not exceed 64 bits");
return -EINVAL;
}
} else {
@@ -4451,6 +4458,7 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
ret = qcow2_update_header(bs);
if (ret < 0) {
s->qcow_version = old_version;
+ error_setg_errno(errp, -ret, "Failed to update the image header");
return ret;
}
}
@@ -4459,18 +4467,17 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
int refcount_order = ctz32(refcount_bits);
if (new_version < 3 && refcount_bits != 16) {
- error_report("Different refcount widths than 16 bits require "
- "compatibility level 1.1 or above (use compat=1.1 or "
- "greater)");
+ error_setg(errp, "Refcount widths other than 16 bits require "
+ "compatibility level 1.1 or above (use compat=1.1 or "
+ "greater)");
return -EINVAL;
}
helper_cb_info.current_operation = QCOW2_CHANGING_REFCOUNT_ORDER;
ret = qcow2_change_refcount_order(bs, refcount_order,
&qcow2_amend_helper_cb,
- &helper_cb_info, &local_err);
+ &helper_cb_info, errp);
if (ret < 0) {
- error_report_err(local_err);
return ret;
}
}
@@ -4480,6 +4487,7 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
backing_file ?: s->image_backing_file,
backing_format ?: s->image_backing_format);
if (ret < 0) {
+ error_setg_errno(errp, -ret, "Failed to change the backing file");
return ret;
}
}
@@ -4487,14 +4495,16 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
if (s->use_lazy_refcounts != lazy_refcounts) {
if (lazy_refcounts) {
if (new_version < 3) {
- error_report("Lazy refcounts only supported with compatibility "
- "level 1.1 and above (use compat=1.1 or greater)");
+ error_setg(errp, "Lazy refcounts only supported with "
+ "compatibility level 1.1 and above (use compat=1.1 "
+ "or greater)");
return -EINVAL;
}
s->compatible_features |= QCOW2_COMPAT_LAZY_REFCOUNTS;
ret = qcow2_update_header(bs);
if (ret < 0) {
s->compatible_features &= ~QCOW2_COMPAT_LAZY_REFCOUNTS;
+ error_setg_errno(errp, -ret, "Failed to update the image header");
return ret;
}
s->use_lazy_refcounts = true;
@@ -4502,6 +4512,7 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
/* make image clean first */
ret = qcow2_mark_clean(bs);
if (ret < 0) {
+ error_setg_errno(errp, -ret, "Failed to make the image clean");
return ret;
}
/* now disallow lazy refcounts */
@@ -4509,6 +4520,7 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
ret = qcow2_update_header(bs);
if (ret < 0) {
s->compatible_features |= QCOW2_COMPAT_LAZY_REFCOUNTS;
+ error_setg_errno(errp, -ret, "Failed to update the image header");
return ret;
}
s->use_lazy_refcounts = false;
@@ -4517,17 +4529,15 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
if (new_size) {
BlockBackend *blk = blk_new(BLK_PERM_RESIZE, BLK_PERM_ALL);
- ret = blk_insert_bs(blk, bs, &local_err);
+ ret = blk_insert_bs(blk, bs, errp);
if (ret < 0) {
- error_report_err(local_err);
blk_unref(blk);
return ret;
}
- ret = blk_truncate(blk, new_size, PREALLOC_MODE_OFF, &local_err);
+ ret = blk_truncate(blk, new_size, PREALLOC_MODE_OFF, errp);
blk_unref(blk);
if (ret < 0) {
- error_report_err(local_err);
return ret;
}
}
@@ -4536,7 +4546,7 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
if (new_version < old_version) {
helper_cb_info.current_operation = QCOW2_DOWNGRADING;
ret = qcow2_downgrade(bs, new_version, &qcow2_amend_helper_cb,
- &helper_cb_info);
+ &helper_cb_info, errp);
if (ret < 0) {
return ret;
}
@@ -4559,7 +4569,7 @@ void qcow2_signal_corruption(BlockDriverState *bs, bool fatal, int64_t offset,
char *message;
va_list ap;
- fatal = fatal && !bs->read_only;
+ fatal = fatal && bdrv_is_writable(bs);
if (s->signaled_corruption &&
(!fatal || (s->incompatible_features & QCOW2_INCOMPAT_CORRUPT)))
diff --git a/block/throttle.c b/block/throttle.c
index e298827f95..f617f23a12 100644
--- a/block/throttle.c
+++ b/block/throttle.c
@@ -36,9 +36,12 @@ static QemuOptsList throttle_opts = {
},
};
-static int throttle_configure_tgm(BlockDriverState *bs,
- ThrottleGroupMember *tgm,
- QDict *options, Error **errp)
+/*
+ * If this function succeeds then the throttle group name is stored in
+ * @group and must be freed by the caller.
+ * If there's an error then @group remains unmodified.
+ */
+static int throttle_parse_options(QDict *options, char **group, Error **errp)
{
int ret;
const char *group_name;
@@ -63,8 +66,7 @@ static int throttle_configure_tgm(BlockDriverState *bs,
goto fin;
}
- /* Register membership to group with name group_name */
- throttle_group_register_tgm(tgm, group_name, bdrv_get_aio_context(bs));
+ *group = g_strdup(group_name);
ret = 0;
fin:
qemu_opts_del(opts);
@@ -75,6 +77,8 @@ static int throttle_open(BlockDriverState *bs, QDict *options,
int flags, Error **errp)
{
ThrottleGroupMember *tgm = bs->opaque;
+ char *group;
+ int ret;
bs->file = bdrv_open_child(NULL, options, "file", bs,
&child_file, false, errp);
@@ -86,7 +90,14 @@ static int throttle_open(BlockDriverState *bs, QDict *options,
bs->supported_zero_flags = bs->file->bs->supported_zero_flags |
BDRV_REQ_WRITE_UNCHANGED;
- return throttle_configure_tgm(bs, tgm, options, errp);
+ ret = throttle_parse_options(options, &group, errp);
+ if (ret == 0) {
+ /* Register membership to group with name group_name */
+ throttle_group_register_tgm(tgm, group, bdrv_get_aio_context(bs));
+ g_free(group);
+ }
+
+ return ret;
}
static void throttle_close(BlockDriverState *bs)
@@ -162,35 +173,36 @@ static void throttle_attach_aio_context(BlockDriverState *bs,
static int throttle_reopen_prepare(BDRVReopenState *reopen_state,
BlockReopenQueue *queue, Error **errp)
{
- ThrottleGroupMember *tgm;
+ int ret;
+ char *group = NULL;
assert(reopen_state != NULL);
assert(reopen_state->bs != NULL);
- reopen_state->opaque = g_new0(ThrottleGroupMember, 1);
- tgm = reopen_state->opaque;
-
- return throttle_configure_tgm(reopen_state->bs, tgm, reopen_state->options,
- errp);
+ ret = throttle_parse_options(reopen_state->options, &group, errp);
+ reopen_state->opaque = group;
+ return ret;
}
static void throttle_reopen_commit(BDRVReopenState *reopen_state)
{
- ThrottleGroupMember *old_tgm = reopen_state->bs->opaque;
- ThrottleGroupMember *new_tgm = reopen_state->opaque;
+ BlockDriverState *bs = reopen_state->bs;
+ ThrottleGroupMember *tgm = bs->opaque;
+ char *group = reopen_state->opaque;
+
+ assert(group);
- throttle_group_unregister_tgm(old_tgm);
- g_free(old_tgm);
- reopen_state->bs->opaque = new_tgm;
+ if (strcmp(group, throttle_group_get_name(tgm))) {
+ throttle_group_unregister_tgm(tgm);
+ throttle_group_register_tgm(tgm, group, bdrv_get_aio_context(bs));
+ }
+ g_free(reopen_state->opaque);
reopen_state->opaque = NULL;
}
static void throttle_reopen_abort(BDRVReopenState *reopen_state)
{
- ThrottleGroupMember *tgm = reopen_state->opaque;
-
- throttle_group_unregister_tgm(tgm);
- g_free(tgm);
+ g_free(reopen_state->opaque);
reopen_state->opaque = NULL;
}
diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c
index 3d81136065..b4c5b03274 100644
--- a/hw/i386/pc_piix.c
+++ b/hw/i386/pc_piix.c
@@ -430,7 +430,6 @@ static void pc_i440fx_3_0_machine_options(MachineClass *m)
pc_i440fx_machine_options(m);
m->alias = "pc";
m->is_default = 1;
- SET_MACHINE_COMPAT(m, PC_COMPAT_2_12);
}
DEFINE_I440FX_MACHINE(v3_0, "pc-i440fx-3.0", NULL,
diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c
index b60cbb9266..83d6d75efa 100644
--- a/hw/i386/pc_q35.c
+++ b/hw/i386/pc_q35.c
@@ -312,7 +312,6 @@ static void pc_q35_3_0_machine_options(MachineClass *m)
{
pc_q35_machine_options(m);
m->alias = "q35";
- SET_MACHINE_COMPAT(m, PC_COMPAT_2_12);
}
DEFINE_Q35_MACHINE(v3_0, "pc-q35-3.0", NULL,
diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c
index 24dbad5125..f7852be842 100644
--- a/hw/ide/ahci.c
+++ b/hw/ide/ahci.c
@@ -27,6 +27,7 @@
#include "hw/pci/pci.h"
#include "qemu/error-report.h"
+#include "qemu/log.h"
#include "sysemu/block-backend.h"
#include "sysemu/dma.h"
#include "hw/ide/internal.h"
@@ -46,6 +47,44 @@ static bool ahci_map_fis_address(AHCIDevice *ad);
static void ahci_unmap_clb_address(AHCIDevice *ad);
static void ahci_unmap_fis_address(AHCIDevice *ad);
+static const char *AHCIHostReg_lookup[AHCI_HOST_REG__COUNT] = {
+ [AHCI_HOST_REG_CAP] = "CAP",
+ [AHCI_HOST_REG_CTL] = "GHC",
+ [AHCI_HOST_REG_IRQ_STAT] = "IS",
+ [AHCI_HOST_REG_PORTS_IMPL] = "PI",
+ [AHCI_HOST_REG_VERSION] = "VS",
+ [AHCI_HOST_REG_CCC_CTL] = "CCC_CTL",
+ [AHCI_HOST_REG_CCC_PORTS] = "CCC_PORTS",
+ [AHCI_HOST_REG_EM_LOC] = "EM_LOC",
+ [AHCI_HOST_REG_EM_CTL] = "EM_CTL",
+ [AHCI_HOST_REG_CAP2] = "CAP2",
+ [AHCI_HOST_REG_BOHC] = "BOHC",
+};
+
+static const char *AHCIPortReg_lookup[AHCI_PORT_REG__COUNT] = {
+ [AHCI_PORT_REG_LST_ADDR] = "PxCLB",
+ [AHCI_PORT_REG_LST_ADDR_HI] = "PxCLBU",
+ [AHCI_PORT_REG_FIS_ADDR] = "PxFB",
+ [AHCI_PORT_REG_FIS_ADDR_HI] = "PxFBU",
+ [AHCI_PORT_REG_IRQ_STAT] = "PxIS",
+ [AHCI_PORT_REG_IRQ_MASK] = "PXIE",
+ [AHCI_PORT_REG_CMD] = "PxCMD",
+ [7] = "Reserved",
+ [AHCI_PORT_REG_TFDATA] = "PxTFD",
+ [AHCI_PORT_REG_SIG] = "PxSIG",
+ [AHCI_PORT_REG_SCR_STAT] = "PxSSTS",
+ [AHCI_PORT_REG_SCR_CTL] = "PxSCTL",
+ [AHCI_PORT_REG_SCR_ERR] = "PxSERR",
+ [AHCI_PORT_REG_SCR_ACT] = "PxSACT",
+ [AHCI_PORT_REG_CMD_ISSUE] = "PxCI",
+ [AHCI_PORT_REG_SCR_NOTIF] = "PxSNTF",
+ [AHCI_PORT_REG_FIS_CTL] = "PxFBS",
+ [AHCI_PORT_REG_DEV_SLEEP] = "PxDEVSLP",
+ [18 ... 27] = "Reserved",
+ [AHCI_PORT_REG_VENDOR_1 ...
+ AHCI_PORT_REG_VENDOR_4] = "PxVS",
+};
+
static const char *AHCIPortIRQ_lookup[AHCI_PORT_IRQ__COUNT] = {
[AHCI_PORT_IRQ_BIT_DHRS] = "DHRS",
[AHCI_PORT_IRQ_BIT_PSS] = "PSS",
@@ -68,41 +107,42 @@ static const char *AHCIPortIRQ_lookup[AHCI_PORT_IRQ__COUNT] = {
[AHCI_PORT_IRQ_BIT_CPDS] = "CPDS"
};
-static uint32_t ahci_port_read(AHCIState *s, int port, int offset)
+static uint32_t ahci_port_read(AHCIState *s, int port, int offset)
{
uint32_t val;
- AHCIPortRegs *pr;
- pr = &s->dev[port].port_regs;
+ AHCIPortRegs *pr = &s->dev[port].port_regs;
+ enum AHCIPortReg regnum = offset / sizeof(uint32_t);
+ assert(regnum < (AHCI_PORT_ADDR_OFFSET_LEN / sizeof(uint32_t)));
- switch (offset) {
- case PORT_LST_ADDR:
+ switch (regnum) {
+ case AHCI_PORT_REG_LST_ADDR:
val = pr->lst_addr;
break;
- case PORT_LST_ADDR_HI:
+ case AHCI_PORT_REG_LST_ADDR_HI:
val = pr->lst_addr_hi;
break;
- case PORT_FIS_ADDR:
+ case AHCI_PORT_REG_FIS_ADDR:
val = pr->fis_addr;
break;
- case PORT_FIS_ADDR_HI:
+ case AHCI_PORT_REG_FIS_ADDR_HI:
val = pr->fis_addr_hi;
break;
- case PORT_IRQ_STAT:
+ case AHCI_PORT_REG_IRQ_STAT:
val = pr->irq_stat;
break;
- case PORT_IRQ_MASK:
+ case AHCI_PORT_REG_IRQ_MASK:
val = pr->irq_mask;
break;
- case PORT_CMD:
+ case AHCI_PORT_REG_CMD:
val = pr->cmd;
break;
- case PORT_TFDATA:
+ case AHCI_PORT_REG_TFDATA:
val = pr->tfdata;
break;
- case PORT_SIG:
+ case AHCI_PORT_REG_SIG:
val = pr->sig;
break;
- case PORT_SCR_STAT:
+ case AHCI_PORT_REG_SCR_STAT:
if (s->dev[port].port.ifs[0].blk) {
val = SATA_SCR_SSTATUS_DET_DEV_PRESENT_PHY_UP |
SATA_SCR_SSTATUS_SPD_GEN1 | SATA_SCR_SSTATUS_IPM_ACTIVE;
@@ -110,28 +150,29 @@ static uint32_t ahci_port_read(AHCIState *s, int port, int offset)
val = SATA_SCR_SSTATUS_DET_NODEV;
}
break;
- case PORT_SCR_CTL:
+ case AHCI_PORT_REG_SCR_CTL:
val = pr->scr_ctl;
break;
- case PORT_SCR_ERR:
+ case AHCI_PORT_REG_SCR_ERR:
val = pr->scr_err;
break;
- case PORT_SCR_ACT:
+ case AHCI_PORT_REG_SCR_ACT:
val = pr->scr_act;
break;
- case PORT_CMD_ISSUE:
+ case AHCI_PORT_REG_CMD_ISSUE:
val = pr->cmd_issue;
break;
- case PORT_RESERVED:
default:
+ trace_ahci_port_read_default(s, port, AHCIPortReg_lookup[regnum],
+ offset);
val = 0;
}
- trace_ahci_port_read(s, port, offset, val);
+ trace_ahci_port_read(s, port, AHCIPortReg_lookup[regnum], offset, val);
return val;
}
-static void ahci_irq_raise(AHCIState *s, AHCIDevice *dev)
+static void ahci_irq_raise(AHCIState *s)
{
DeviceState *dev_state = s->container;
PCIDevice *pci_dev = (PCIDevice *) object_dynamic_cast(OBJECT(dev_state),
@@ -146,7 +187,7 @@ static void ahci_irq_raise(AHCIState *s, AHCIDevice *dev)
}
}
-static void ahci_irq_lower(AHCIState *s, AHCIDevice *dev)
+static void ahci_irq_lower(AHCIState *s)
{
DeviceState *dev_state = s->container;
PCIDevice *pci_dev = (PCIDevice *) object_dynamic_cast(OBJECT(dev_state),
@@ -174,9 +215,9 @@ static void ahci_check_irq(AHCIState *s)
trace_ahci_check_irq(s, old_irq, s->control_regs.irqstatus);
if (s->control_regs.irqstatus &&
(s->control_regs.ghc & HOST_CTL_IRQ_EN)) {
- ahci_irq_raise(s, NULL);
+ ahci_irq_raise(s);
} else {
- ahci_irq_lower(s, NULL);
+ ahci_irq_lower(s);
}
}
@@ -253,85 +294,88 @@ static int ahci_cond_start_engines(AHCIDevice *ad)
return 0;
}
-static void ahci_port_write(AHCIState *s, int port, int offset, uint32_t val)
+static void ahci_port_write(AHCIState *s, int port, int offset, uint32_t val)
{
AHCIPortRegs *pr = &s->dev[port].port_regs;
+ enum AHCIPortReg regnum = offset / sizeof(uint32_t);
+ assert(regnum < (AHCI_PORT_ADDR_OFFSET_LEN / sizeof(uint32_t)));
+ trace_ahci_port_write(s, port, AHCIPortReg_lookup[regnum], offset, val);
- trace_ahci_port_write(s, port, offset, val);
- switch (offset) {
- case PORT_LST_ADDR:
- pr->lst_addr = val;
- break;
- case PORT_LST_ADDR_HI:
- pr->lst_addr_hi = val;
- break;
- case PORT_FIS_ADDR:
- pr->fis_addr = val;
- break;
- case PORT_FIS_ADDR_HI:
- pr->fis_addr_hi = val;
- break;
- case PORT_IRQ_STAT:
- pr->irq_stat &= ~val;
- ahci_check_irq(s);
- break;
- case PORT_IRQ_MASK:
- pr->irq_mask = val & 0xfdc000ff;
- ahci_check_irq(s);
- break;
- case PORT_CMD:
- /* Block any Read-only fields from being set;
- * including LIST_ON and FIS_ON.
- * The spec requires to set ICC bits to zero after the ICC change
- * is done. We don't support ICC state changes, therefore always
- * force the ICC bits to zero.
- */
- pr->cmd = (pr->cmd & PORT_CMD_RO_MASK) |
- (val & ~(PORT_CMD_RO_MASK|PORT_CMD_ICC_MASK));
-
- /* Check FIS RX and CLB engines */
- ahci_cond_start_engines(&s->dev[port]);
-
- /* XXX usually the FIS would be pending on the bus here and
- issuing deferred until the OS enables FIS receival.
- Instead, we only submit it once - which works in most
- cases, but is a hack. */
- if ((pr->cmd & PORT_CMD_FIS_ON) &&
- !s->dev[port].init_d2h_sent) {
- ahci_init_d2h(&s->dev[port]);
- }
+ switch (regnum) {
+ case AHCI_PORT_REG_LST_ADDR:
+ pr->lst_addr = val;
+ break;
+ case AHCI_PORT_REG_LST_ADDR_HI:
+ pr->lst_addr_hi = val;
+ break;
+ case AHCI_PORT_REG_FIS_ADDR:
+ pr->fis_addr = val;
+ break;
+ case AHCI_PORT_REG_FIS_ADDR_HI:
+ pr->fis_addr_hi = val;
+ break;
+ case AHCI_PORT_REG_IRQ_STAT:
+ pr->irq_stat &= ~val;
+ ahci_check_irq(s);
+ break;
+ case AHCI_PORT_REG_IRQ_MASK:
+ pr->irq_mask = val & 0xfdc000ff;
+ ahci_check_irq(s);
+ break;
+ case AHCI_PORT_REG_CMD:
+ /* Block any Read-only fields from being set;
+ * including LIST_ON and FIS_ON.
+ * The spec requires to set ICC bits to zero after the ICC change
+ * is done. We don't support ICC state changes, therefore always
+ * force the ICC bits to zero.
+ */
+ pr->cmd = (pr->cmd & PORT_CMD_RO_MASK) |
+ (val & ~(PORT_CMD_RO_MASK | PORT_CMD_ICC_MASK));
+
+ /* Check FIS RX and CLB engines */
+ ahci_cond_start_engines(&s->dev[port]);
+
+ /* XXX usually the FIS would be pending on the bus here and
+ issuing deferred until the OS enables FIS receival.
+ Instead, we only submit it once - which works in most
+ cases, but is a hack. */
+ if ((pr->cmd & PORT_CMD_FIS_ON) &&
+ !s->dev[port].init_d2h_sent) {
+ ahci_init_d2h(&s->dev[port]);
+ }
- check_cmd(s, port);
- break;
- case PORT_TFDATA:
- /* Read Only. */
- break;
- case PORT_SIG:
- /* Read Only */
- break;
- case PORT_SCR_STAT:
- /* Read Only */
- break;
- case PORT_SCR_CTL:
- if (((pr->scr_ctl & AHCI_SCR_SCTL_DET) == 1) &&
- ((val & AHCI_SCR_SCTL_DET) == 0)) {
- ahci_reset_port(s, port);
- }
- pr->scr_ctl = val;
- break;
- case PORT_SCR_ERR:
- pr->scr_err &= ~val;
- break;
- case PORT_SCR_ACT:
- /* RW1 */
- pr->scr_act |= val;
- break;
- case PORT_CMD_ISSUE:
- pr->cmd_issue |= val;
- check_cmd(s, port);
- break;
- default:
- break;
+ check_cmd(s, port);
+ break;
+ case AHCI_PORT_REG_TFDATA:
+ case AHCI_PORT_REG_SIG:
+ case AHCI_PORT_REG_SCR_STAT:
+ /* Read Only */
+ break;
+ case AHCI_PORT_REG_SCR_CTL:
+ if (((pr->scr_ctl & AHCI_SCR_SCTL_DET) == 1) &&
+ ((val & AHCI_SCR_SCTL_DET) == 0)) {
+ ahci_reset_port(s, port);
+ }
+ pr->scr_ctl = val;
+ break;
+ case AHCI_PORT_REG_SCR_ERR:
+ pr->scr_err &= ~val;
+ break;
+ case AHCI_PORT_REG_SCR_ACT:
+ /* RW1 */
+ pr->scr_act |= val;
+ break;
+ case AHCI_PORT_REG_CMD_ISSUE:
+ pr->cmd_issue |= val;
+ check_cmd(s, port);
+ break;
+ default:
+ trace_ahci_port_write_unimpl(s, port, AHCIPortReg_lookup[regnum],
+ offset, val);
+ qemu_log_mask(LOG_UNIMP, "Attempted write to unimplemented register: "
+ "AHCI port %d register %s, offset 0x%x: 0x%"PRIx32,
+ port, AHCIPortReg_lookup[regnum], offset, val);
+ break;
}
}
@@ -341,28 +385,37 @@ static uint64_t ahci_mem_read_32(void *opaque, hwaddr addr)
uint32_t val = 0;
if (addr < AHCI_GENERIC_HOST_CONTROL_REGS_MAX_ADDR) {
- switch (addr) {
- case HOST_CAP:
+ enum AHCIHostReg regnum = addr / 4;
+ assert(regnum < AHCI_HOST_REG__COUNT);
+
+ switch (regnum) {
+ case AHCI_HOST_REG_CAP:
val = s->control_regs.cap;
break;
- case HOST_CTL:
+ case AHCI_HOST_REG_CTL:
val = s->control_regs.ghc;
break;
- case HOST_IRQ_STAT:
+ case AHCI_HOST_REG_IRQ_STAT:
val = s->control_regs.irqstatus;
break;
- case HOST_PORTS_IMPL:
+ case AHCI_HOST_REG_PORTS_IMPL:
val = s->control_regs.impl;
break;
- case HOST_VERSION:
+ case AHCI_HOST_REG_VERSION:
val = s->control_regs.version;
break;
+ default:
+ trace_ahci_mem_read_32_host_default(s, AHCIHostReg_lookup[regnum],
+ addr);
}
+ trace_ahci_mem_read_32_host(s, AHCIHostReg_lookup[regnum], addr, val);
} else if ((addr >= AHCI_PORT_REGS_START_ADDR) &&
(addr < (AHCI_PORT_REGS_START_ADDR +
(s->ports * AHCI_PORT_ADDR_OFFSET_LEN)))) {
val = ahci_port_read(s, (addr - AHCI_PORT_REGS_START_ADDR) >> 7,
addr & AHCI_PORT_ADDR_OFFSET_MASK);
+ } else {
+ trace_ahci_mem_read_32_default(s, addr, val);
}
trace_ahci_mem_read_32(s, addr, val);
@@ -415,38 +468,53 @@ static void ahci_mem_write(void *opaque, hwaddr addr,
}
if (addr < AHCI_GENERIC_HOST_CONTROL_REGS_MAX_ADDR) {
- switch (addr) {
- case HOST_CAP: /* R/WO, RO */
- /* FIXME handle R/WO */
- break;
- case HOST_CTL: /* R/W */
- if (val & HOST_CTL_RESET) {
- ahci_reset(s);
- } else {
- s->control_regs.ghc = (val & 0x3) | HOST_CTL_AHCI_EN;
- ahci_check_irq(s);
- }
- break;
- case HOST_IRQ_STAT: /* R/WC, RO */
- s->control_regs.irqstatus &= ~val;
+ enum AHCIHostReg regnum = addr / 4;
+ assert(regnum < AHCI_HOST_REG__COUNT);
+
+ switch (regnum) {
+ case AHCI_HOST_REG_CAP: /* R/WO, RO */
+ /* FIXME handle R/WO */
+ break;
+ case AHCI_HOST_REG_CTL: /* R/W */
+ if (val & HOST_CTL_RESET) {
+ ahci_reset(s);
+ } else {
+ s->control_regs.ghc = (val & 0x3) | HOST_CTL_AHCI_EN;
ahci_check_irq(s);
- break;
- case HOST_PORTS_IMPL: /* R/WO, RO */
- /* FIXME handle R/WO */
- break;
- case HOST_VERSION: /* RO */
- /* FIXME report write? */
- break;
- default:
- trace_ahci_mem_write_unknown(s, size, addr, val);
+ }
+ break;
+ case AHCI_HOST_REG_IRQ_STAT: /* R/WC, RO */
+ s->control_regs.irqstatus &= ~val;
+ ahci_check_irq(s);
+ break;
+ case AHCI_HOST_REG_PORTS_IMPL: /* R/WO, RO */
+ /* FIXME handle R/WO */
+ break;
+ case AHCI_HOST_REG_VERSION: /* RO */
+ /* FIXME report write? */
+ break;
+ default:
+ qemu_log_mask(LOG_UNIMP,
+ "Attempted write to unimplemented register: "
+ "AHCI host register %s, "
+ "offset 0x%"PRIx64": 0x%"PRIx64,
+ AHCIHostReg_lookup[regnum], addr, val);
+ trace_ahci_mem_write_host_unimpl(s, size,
+ AHCIHostReg_lookup[regnum], addr);
}
+ trace_ahci_mem_write_host(s, size, AHCIHostReg_lookup[regnum],
+ addr, val);
} else if ((addr >= AHCI_PORT_REGS_START_ADDR) &&
(addr < (AHCI_PORT_REGS_START_ADDR +
- (s->ports * AHCI_PORT_ADDR_OFFSET_LEN)))) {
+ (s->ports * AHCI_PORT_ADDR_OFFSET_LEN)))) {
ahci_port_write(s, (addr - AHCI_PORT_REGS_START_ADDR) >> 7,
addr & AHCI_PORT_ADDR_OFFSET_MASK, val);
+ } else {
+ qemu_log_mask(LOG_UNIMP, "Attempted write to unimplemented register: "
+ "AHCI global register at offset 0x%"PRIx64": 0x%"PRIx64,
+ addr, val);
+ trace_ahci_mem_write_unimpl(s, size, addr, val);
}
-
}
static const MemoryRegionOps ahci_mem_ops = {
@@ -532,13 +600,6 @@ static void ahci_check_cmd_bh(void *opaque)
qemu_bh_delete(ad->check_bh);
ad->check_bh = NULL;
- if ((ad->busy_slot != -1) &&
- !(ad->port.ifs[0].status & (BUSY_STAT|DRQ_STAT))) {
- /* no longer busy */
- ad->port_regs.cmd_issue &= ~(1 << ad->busy_slot);
- ad->busy_slot = -1;
- }
-
check_cmd(ad->hba, ad->port_no);
}
@@ -1198,7 +1259,6 @@ static void handle_reg_h2d_fis(AHCIState *s, int port,
g_free(pretty_fis);
}
s->dev[port].done_atapi_packet = false;
- /* XXX send PIO setup FIS */
}
ide_state->error = 0;
@@ -1280,8 +1340,8 @@ out:
return 0;
}
-/* DMA dev <-> ram */
-static void ahci_start_transfer(IDEDMA *dma)
+/* Transfer PIO data between RAM and device */
+static void ahci_pio_transfer(IDEDMA *dma)
{
AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma);
IDEState *s = &ad->port.ifs[0];
@@ -1292,10 +1352,12 @@ static void ahci_start_transfer(IDEDMA *dma)
int is_atapi = opts & AHCI_CMD_ATAPI;
int has_sglist = 0;
+ /* PIO FIS gets written prior to transfer */
+ ahci_write_fis_pio(ad, size);
+
if (is_atapi && !ad->done_atapi_packet) {
/* already prepopulated iobuffer */
ad->done_atapi_packet = true;
- size = 0;
goto out;
}
@@ -1303,9 +1365,9 @@ static void ahci_start_transfer(IDEDMA *dma)
has_sglist = 1;
}
- trace_ahci_start_transfer(ad->hba, ad->port_no, is_write ? "writ" : "read",
- size, is_atapi ? "atapi" : "ata",
- has_sglist ? "" : "o");
+ trace_ahci_pio_transfer(ad->hba, ad->port_no, is_write ? "writ" : "read",
+ size, is_atapi ? "atapi" : "ata",
+ has_sglist ? "" : "o");
if (has_sglist && size) {
if (is_write) {
@@ -1315,19 +1377,11 @@ static void ahci_start_transfer(IDEDMA *dma)
}
}
+ /* Update number of transferred bytes, destroy sglist */
+ dma_buf_commit(s, size);
out:
/* declare that we processed everything */
s->data_ptr = s->data_end;
-
- /* Update number of transferred bytes, destroy sglist */
- dma_buf_commit(s, size);
-
- s->end_transfer_func(s);
-
- if (!(s->status & DRQ_STAT)) {
- /* done with PIO send/receive */
- ahci_write_fis_pio(ad, le32_to_cpu(ad->cur_cmd->status));
- }
}
static void ahci_start_dma(IDEDMA *dma, IDEState *s,
@@ -1425,11 +1479,16 @@ static void ahci_cmd_done(IDEDMA *dma)
trace_ahci_cmd_done(ad->hba, ad->port_no);
+ /* no longer busy */
+ if (ad->busy_slot != -1) {
+ ad->port_regs.cmd_issue &= ~(1 << ad->busy_slot);
+ ad->busy_slot = -1;
+ }
+
/* update d2h status */
ahci_write_fis_d2h(ad);
- if (!ad->check_bh) {
- /* maybe we still have something to process, check later */
+ if (ad->port_regs.cmd_issue && !ad->check_bh) {
ad->check_bh = qemu_bh_new(ahci_check_cmd_bh, ad);
qemu_bh_schedule(ad->check_bh);
}
@@ -1443,7 +1502,7 @@ static const IDEDMAOps ahci_dma_ops = {
.start_dma = ahci_start_dma,
.restart = ahci_restart,
.restart_dma = ahci_restart_dma,
- .start_transfer = ahci_start_transfer,
+ .pio_transfer = ahci_pio_transfer,
.prepare_buf = ahci_dma_prepare_buf,
.commit_buf = ahci_commit_buf,
.rw_buf = ahci_dma_rw_buf,
diff --git a/hw/ide/ahci_internal.h b/hw/ide/ahci_internal.h
index 1a25d6c039..2953243929 100644
--- a/hw/ide/ahci_internal.h
+++ b/hw/ide/ahci_internal.h
@@ -55,11 +55,20 @@
#define RX_FIS_UNK 0x60 /* offset of Unknown FIS data */
/* global controller registers */
-#define HOST_CAP 0x00 /* host capabilities */
-#define HOST_CTL 0x04 /* global host control */
-#define HOST_IRQ_STAT 0x08 /* interrupt status */
-#define HOST_PORTS_IMPL 0x0c /* bitmap of implemented ports */
-#define HOST_VERSION 0x10 /* AHCI spec. version compliancy */
+enum AHCIHostReg {
+ AHCI_HOST_REG_CAP = 0, /* CAP: host capabilities */
+ AHCI_HOST_REG_CTL = 1, /* GHC: global host control */
+ AHCI_HOST_REG_IRQ_STAT = 2, /* IS: interrupt status */
+ AHCI_HOST_REG_PORTS_IMPL = 3, /* PI: bitmap of implemented ports */
+ AHCI_HOST_REG_VERSION = 4, /* VS: AHCI spec. version compliancy */
+ AHCI_HOST_REG_CCC_CTL = 5, /* CCC_CTL: CCC Control */
+ AHCI_HOST_REG_CCC_PORTS = 6, /* CCC_PORTS: CCC Ports */
+ AHCI_HOST_REG_EM_LOC = 7, /* EM_LOC: Enclosure Mgmt Location */
+ AHCI_HOST_REG_EM_CTL = 8, /* EM_CTL: Enclosure Mgmt Control */
+ AHCI_HOST_REG_CAP2 = 9, /* CAP2: host capabilities, extended */
+ AHCI_HOST_REG_BOHC = 10, /* BOHC: firmare/os handoff ctrl & status */
+ AHCI_HOST_REG__COUNT = 11
+};
/* HOST_CTL bits */
#define HOST_CTL_RESET (1 << 0) /* reset controller; self-clear */
@@ -75,21 +84,32 @@
#define HOST_CAP_64 (1U << 31) /* PCI DAC (64-bit DMA) support */
/* registers for each SATA port */
-#define PORT_LST_ADDR 0x00 /* command list DMA addr */
-#define PORT_LST_ADDR_HI 0x04 /* command list DMA addr hi */
-#define PORT_FIS_ADDR 0x08 /* FIS rx buf addr */
-#define PORT_FIS_ADDR_HI 0x0c /* FIS rx buf addr hi */
-#define PORT_IRQ_STAT 0x10 /* interrupt status */
-#define PORT_IRQ_MASK 0x14 /* interrupt enable/disable mask */
-#define PORT_CMD 0x18 /* port command */
-#define PORT_TFDATA 0x20 /* taskfile data */
-#define PORT_SIG 0x24 /* device TF signature */
-#define PORT_SCR_STAT 0x28 /* SATA phy register: SStatus */
-#define PORT_SCR_CTL 0x2c /* SATA phy register: SControl */
-#define PORT_SCR_ERR 0x30 /* SATA phy register: SError */
-#define PORT_SCR_ACT 0x34 /* SATA phy register: SActive */
-#define PORT_CMD_ISSUE 0x38 /* command issue */
-#define PORT_RESERVED 0x3c /* reserved */
+enum AHCIPortReg {
+ AHCI_PORT_REG_LST_ADDR = 0, /* PxCLB: command list DMA addr */
+ AHCI_PORT_REG_LST_ADDR_HI = 1, /* PxCLBU: command list DMA addr hi */
+ AHCI_PORT_REG_FIS_ADDR = 2, /* PxFB: FIS rx buf addr */
+ AHCI_PORT_REG_FIS_ADDR_HI = 3, /* PxFBU: FIX rx buf addr hi */
+ AHCI_PORT_REG_IRQ_STAT = 4, /* PxIS: interrupt status */
+ AHCI_PORT_REG_IRQ_MASK = 5, /* PxIE: interrupt enable/disable mask */
+ AHCI_PORT_REG_CMD = 6, /* PxCMD: port command */
+ /* RESERVED */
+ AHCI_PORT_REG_TFDATA = 8, /* PxTFD: taskfile data */
+ AHCI_PORT_REG_SIG = 9, /* PxSIG: device TF signature */
+ AHCI_PORT_REG_SCR_STAT = 10, /* PxSSTS: SATA phy register: SStatus */
+ AHCI_PORT_REG_SCR_CTL = 11, /* PxSCTL: SATA phy register: SControl */
+ AHCI_PORT_REG_SCR_ERR = 12, /* PxSERR: SATA phy register: SError */
+ AHCI_PORT_REG_SCR_ACT = 13, /* PxSACT: SATA phy register: SActive */
+ AHCI_PORT_REG_CMD_ISSUE = 14, /* PxCI: command issue */
+ AHCI_PORT_REG_SCR_NOTIF = 15, /* PxSNTF: SATA phy register: SNotification */
+ AHCI_PORT_REG_FIS_CTL = 16, /* PxFBS: Port multiplier switching ctl */
+ AHCI_PORT_REG_DEV_SLEEP = 17, /* PxDEVSLP: device sleep control */
+ /* RESERVED */
+ AHCI_PORT_REG_VENDOR_1 = 28, /* PxVS: Vendor Specific */
+ AHCI_PORT_REG_VENDOR_2 = 29,
+ AHCI_PORT_REG_VENDOR_3 = 30,
+ AHCI_PORT_REG_VENDOR_4 = 31,
+ AHCI_PORT_REG__COUNT = 32
+};
/* Port interrupt bit descriptors */
enum AHCIPortIRQ {
@@ -198,8 +218,7 @@ enum AHCIPortIRQ {
#define SATA_SIGNATURE_CDROM 0xeb140101
#define SATA_SIGNATURE_DISK 0x00000101
-#define AHCI_GENERIC_HOST_CONTROL_REGS_MAX_ADDR 0x20
- /* Shouldn't this be 0x2c? */
+#define AHCI_GENERIC_HOST_CONTROL_REGS_MAX_ADDR 0x2c
#define AHCI_PORT_REGS_START_ADDR 0x100
#define AHCI_PORT_ADDR_OFFSET_MASK 0x7f
diff --git a/hw/ide/atapi.c b/hw/ide/atapi.c
index c0509c8bf5..39e473f9c2 100644
--- a/hw/ide/atapi.c
+++ b/hw/ide/atapi.c
@@ -245,15 +245,11 @@ static uint16_t atapi_byte_count_limit(IDEState *s)
void ide_atapi_cmd_reply_end(IDEState *s)
{
int byte_count_limit, size, ret;
- trace_ide_atapi_cmd_reply_end(s, s->packet_transfer_size,
- s->elementary_transfer_size,
- s->io_buffer_index);
- if (s->packet_transfer_size <= 0) {
- /* end of transfer */
- ide_atapi_cmd_ok(s);
- ide_set_irq(s->bus);
- trace_ide_atapi_cmd_reply_end_eot(s, s->status);
- } else {
+ while (s->packet_transfer_size > 0) {
+ trace_ide_atapi_cmd_reply_end(s, s->packet_transfer_size,
+ s->elementary_transfer_size,
+ s->io_buffer_index);
+
/* see if a new sector must be read */
if (s->lba != -1 && s->io_buffer_index >= s->cd_sector_size) {
if (!s->elementary_transfer_size) {
@@ -279,14 +275,10 @@ void ide_atapi_cmd_reply_end(IDEState *s)
size = s->cd_sector_size - s->io_buffer_index;
if (size > s->elementary_transfer_size)
size = s->elementary_transfer_size;
- s->packet_transfer_size -= size;
- s->elementary_transfer_size -= size;
- s->io_buffer_index += size;
- ide_transfer_start(s, s->io_buffer + s->io_buffer_index - size,
- size, ide_atapi_cmd_reply_end);
} else {
/* a new transfer is needed */
s->nsector = (s->nsector & ~7) | ATAPI_INT_REASON_IO;
+ ide_set_irq(s->bus);
byte_count_limit = atapi_byte_count_limit(s);
trace_ide_atapi_cmd_reply_end_bcl(s, byte_count_limit);
size = s->packet_transfer_size;
@@ -304,15 +296,27 @@ void ide_atapi_cmd_reply_end(IDEState *s)
if (size > (s->cd_sector_size - s->io_buffer_index))
size = (s->cd_sector_size - s->io_buffer_index);
}
- s->packet_transfer_size -= size;
- s->elementary_transfer_size -= size;
- s->io_buffer_index += size;
- ide_transfer_start(s, s->io_buffer + s->io_buffer_index - size,
- size, ide_atapi_cmd_reply_end);
- ide_set_irq(s->bus);
trace_ide_atapi_cmd_reply_end_new(s, s->status);
}
+ s->packet_transfer_size -= size;
+ s->elementary_transfer_size -= size;
+ s->io_buffer_index += size;
+
+ /* Some adapters process PIO data right away. In that case, we need
+ * to avoid mutual recursion between ide_transfer_start
+ * and ide_atapi_cmd_reply_end.
+ */
+ if (!ide_transfer_start_norecurse(s,
+ s->io_buffer + s->io_buffer_index - size,
+ size, ide_atapi_cmd_reply_end)) {
+ return;
+ }
}
+
+ /* end of transfer */
+ trace_ide_atapi_cmd_reply_end_eot(s, s->status);
+ ide_atapi_cmd_ok(s);
+ ide_set_irq(s->bus);
}
/* send a reply of 'size' bytes in s->io_buffer to an ATAPI command */
diff --git a/hw/ide/core.c b/hw/ide/core.c
index cc9ca28c33..2c62efc536 100644
--- a/hw/ide/core.c
+++ b/hw/ide/core.c
@@ -523,18 +523,28 @@ static void ide_clear_retry(IDEState *s)
}
/* prepare data transfer and tell what to do after */
-void ide_transfer_start(IDEState *s, uint8_t *buf, int size,
- EndTransferFunc *end_transfer_func)
+bool ide_transfer_start_norecurse(IDEState *s, uint8_t *buf, int size,
+ EndTransferFunc *end_transfer_func)
{
- s->end_transfer_func = end_transfer_func;
s->data_ptr = buf;
s->data_end = buf + size;
ide_set_retry(s);
if (!(s->status & ERR_STAT)) {
s->status |= DRQ_STAT;
}
- if (s->bus->dma->ops->start_transfer) {
- s->bus->dma->ops->start_transfer(s->bus->dma);
+ if (!s->bus->dma->ops->pio_transfer) {
+ s->end_transfer_func = end_transfer_func;
+ return false;
+ }
+ s->bus->dma->ops->pio_transfer(s->bus->dma);
+ return true;
+}
+
+void ide_transfer_start(IDEState *s, uint8_t *buf, int size,
+ EndTransferFunc *end_transfer_func)
+{
+ if (ide_transfer_start_norecurse(s, buf, size, end_transfer_func)) {
+ end_transfer_func(s);
}
}
@@ -545,27 +555,18 @@ static void ide_cmd_done(IDEState *s)
}
}
-static void ide_transfer_halt(IDEState *s,
- void(*end_transfer_func)(IDEState *),
- bool notify)
+static void ide_transfer_halt(IDEState *s)
{
- s->end_transfer_func = end_transfer_func;
+ s->end_transfer_func = ide_transfer_stop;
s->data_ptr = s->io_buffer;
s->data_end = s->io_buffer;
s->status &= ~DRQ_STAT;
- if (notify) {
- ide_cmd_done(s);
- }
}
void ide_transfer_stop(IDEState *s)
{
- ide_transfer_halt(s, ide_transfer_stop, true);
-}
-
-static void ide_transfer_cancel(IDEState *s)
-{
- ide_transfer_halt(s, ide_transfer_cancel, false);
+ ide_transfer_halt(s);
+ ide_cmd_done(s);
}
int64_t ide_get_sector(IDEState *s)
@@ -1362,7 +1363,7 @@ static bool cmd_nop(IDEState *s, uint8_t cmd)
static bool cmd_device_reset(IDEState *s, uint8_t cmd)
{
/* Halt PIO (in the DRQ phase), then DMA */
- ide_transfer_cancel(s);
+ ide_transfer_halt(s);
ide_cancel_dma_sync(s);
/* Reset any PIO commands, reset signature, etc */
diff --git a/hw/ide/trace-events b/hw/ide/trace-events
index 5c0e59ec42..65d6f9034d 100644
--- a/hw/ide/trace-events
+++ b/hw/ide/trace-events
@@ -63,16 +63,23 @@ ide_atapi_cmd_read_dma_cb_aio(void *s, int lba, int n) "IDEState: %p; aio read:
ide_atapi_cmd_packet(void *s, uint16_t limit, const char *packet) "IDEState: %p; limit=0x%x packet: %s"
# hw/ide/ahci.c
-ahci_port_read(void *s, int port, int offset, uint32_t ret) "ahci(%p)[%d]: port read @ 0x%x: 0x%08x"
+ahci_port_read(void *s, int port, const char *reg, int offset, uint32_t ret) "ahci(%p)[%d]: port read [reg:%s] @ 0x%x: 0x%08x"
+ahci_port_read_default(void *s, int port, const char *reg, int offset) "ahci(%p)[%d]: unimplemented port read [reg:%s] @ 0x%x"
ahci_irq_raise(void *s) "ahci(%p): raise irq"
ahci_irq_lower(void *s) "ahci(%p): lower irq"
ahci_check_irq(void *s, uint32_t old, uint32_t new) "ahci(%p): check irq 0x%08x --> 0x%08x"
ahci_trigger_irq(void *s, int port, const char *name, uint32_t val, uint32_t old, uint32_t new, uint32_t effective) "ahci(%p)[%d]: trigger irq +%s (0x%08x); irqstat: 0x%08x --> 0x%08x; effective: 0x%08x"
-ahci_port_write(void *s, int port, int offset, uint32_t val) "ahci(%p)[%d]: port write @ 0x%x: 0x%08x"
+ahci_port_write(void *s, int port, const char *reg, int offset, uint32_t val) "ahci(%p)[%d]: port write [reg:%s] @ 0x%x: 0x%08x"
+ahci_port_write_unimpl(void *s, int port, const char *reg, int offset, uint32_t val) "ahci(%p)[%d]: unimplemented port write [reg:%s] @ 0x%x: 0x%08x"
ahci_mem_read_32(void *s, uint64_t addr, uint32_t val) "ahci(%p): mem read @ 0x%"PRIx64": 0x%08x"
+ahci_mem_read_32_default(void *s, uint64_t addr, uint32_t val) "ahci(%p): mem read @ 0x%"PRIx64": 0x%08x"
+ahci_mem_read_32_host(void *s, const char *reg, uint64_t addr, uint32_t val) "ahci(%p): mem read [reg:%s] @ 0x%"PRIx64": 0x%08x"
+ahci_mem_read_32_host_default(void *s, const char *reg, uint64_t addr) "ahci(%p): unimplemented mem read [reg:%s] @ 0x%"PRIx64
ahci_mem_read(void *s, unsigned size, uint64_t addr, uint64_t val) "ahci(%p): read%u @ 0x%"PRIx64": 0x%016"PRIx64
ahci_mem_write(void *s, unsigned size, uint64_t addr, uint64_t val) "ahci(%p): write%u @ 0x%"PRIx64": 0x%016"PRIx64
-ahci_mem_write_unknown(void *s, unsigned size, uint64_t addr, uint64_t val) "ahci(%p): write%u to unknown register 0x%"PRIx64": 0x%016"PRIx64
+ahci_mem_write_host_unimpl(void *s, unsigned size, const char *reg, uint64_t addr) "ahci(%p) unimplemented write%u [reg:%s] @ 0x%"PRIx64
+ahci_mem_write_host(void *s, unsigned size, const char *reg, uint64_t addr, uint64_t val) "ahci(%p) write%u [reg:%s] @ 0x%"PRIx64": 0x%016"PRIx64
+ahci_mem_write_unimpl(void *s, unsigned size, uint64_t addr, uint64_t val) "ahci(%p): write%u to unknown register 0x%"PRIx64": 0x%016"PRIx64
ahci_set_signature(void *s, int port, uint8_t nsector, uint8_t sector, uint8_t lcyl, uint8_t hcyl, uint32_t sig) "ahci(%p)[%d]: set signature sector:0x%02x nsector:0x%02x lcyl:0x%02x hcyl:0x%02x (cumulatively: 0x%08x)"
ahci_reset_port(void *s, int port) "ahci(%p)[%d]: reset port"
ahci_unmap_fis_address_null(void *s, int port) "ahci(%p)[%d]: Attempt to unmap NULL FIS address"
@@ -101,7 +108,7 @@ handle_cmd_badport(void *s, int port) "ahci(%p)[%d]: guest accessed unused port"
handle_cmd_badfis(void *s, int port) "ahci(%p)[%d]: guest provided an invalid cmd FIS"
handle_cmd_badmap(void *s, int port, uint64_t len) "ahci(%p)[%d]: dma_memory_map failed, 0x%02"PRIx64" != 0x80"
handle_cmd_unhandled_fis(void *s, int port, uint8_t b0, uint8_t b1, uint8_t b2) "ahci(%p)[%d]: unhandled FIS type. cmd_fis: 0x%02x-%02x-%02x"
-ahci_start_transfer(void *s, int port, const char *rw, uint32_t size, const char *tgt, const char *sgl) "ahci(%p)[%d]: %sing %d bytes on %s w/%s sglist"
+ahci_pio_transfer(void *s, int port, const char *rw, uint32_t size, const char *tgt, const char *sgl) "ahci(%p)[%d]: %sing %d bytes on %s w/%s sglist"
ahci_start_dma(void *s, int port) "ahci(%p)[%d]: start dma"
ahci_dma_prepare_buf(void *s, int port, int32_t io_buffer_size, int32_t limit) "ahci(%p)[%d]: prepare buf limit=%"PRId32" prepared=%"PRId32
ahci_dma_prepare_buf_fail(void *s, int port) "ahci(%p)[%d]: sglist population failed"
diff --git a/include/block/block.h b/include/block/block.h
index 6cc6c7e699..e677080c4e 100644
--- a/include/block/block.h
+++ b/include/block/block.h
@@ -343,7 +343,8 @@ int bdrv_check(BlockDriverState *bs, BdrvCheckResult *res, BdrvCheckMode fix);
typedef void BlockDriverAmendStatusCB(BlockDriverState *bs, int64_t offset,
int64_t total_work_size, void *opaque);
int bdrv_amend_options(BlockDriverState *bs_new, QemuOpts *opts,
- BlockDriverAmendStatusCB *status_cb, void *cb_opaque);
+ BlockDriverAmendStatusCB *status_cb, void *cb_opaque,
+ Error **errp);
/* external snapshots */
bool bdrv_recurse_is_first_non_filter(BlockDriverState *bs,
@@ -407,6 +408,7 @@ bool bdrv_is_read_only(BlockDriverState *bs);
int bdrv_can_set_read_only(BlockDriverState *bs, bool read_only,
bool ignore_allow_rdw, Error **errp);
int bdrv_set_read_only(BlockDriverState *bs, bool read_only, Error **errp);
+bool bdrv_is_writable(BlockDriverState *bs);
bool bdrv_is_sg(BlockDriverState *bs);
bool bdrv_is_inserted(BlockDriverState *bs);
void bdrv_lock_medium(BlockDriverState *bs, bool locked);
diff --git a/include/block/block_int.h b/include/block/block_int.h
index 888b7f7bff..327e478a73 100644
--- a/include/block/block_int.h
+++ b/include/block/block_int.h
@@ -353,7 +353,8 @@ struct BlockDriver {
int (*bdrv_amend_options)(BlockDriverState *bs, QemuOpts *opts,
BlockDriverAmendStatusCB *status_cb,
- void *cb_opaque);
+ void *cb_opaque,
+ Error **errp);
void (*bdrv_debug_event)(BlockDriverState *bs, BlkdebugEvent event);
diff --git a/include/hw/ide/internal.h b/include/hw/ide/internal.h
index 88212f59df..594081e57f 100644
--- a/include/hw/ide/internal.h
+++ b/include/hw/ide/internal.h
@@ -444,7 +444,7 @@ struct IDEState {
struct IDEDMAOps {
DMAStartFunc *start_dma;
- DMAVoidFunc *start_transfer;
+ DMAVoidFunc *pio_transfer;
DMAInt32Func *prepare_buf;
DMAu32Func *commit_buf;
DMAIntFunc *rw_buf;
@@ -623,6 +623,8 @@ void ide_exec_cmd(IDEBus *bus, uint32_t val);
void ide_transfer_start(IDEState *s, uint8_t *buf, int size,
EndTransferFunc *end_transfer_func);
+bool ide_transfer_start_norecurse(IDEState *s, uint8_t *buf, int size,
+ EndTransferFunc *end_transfer_func);
void ide_transfer_stop(IDEState *s);
void ide_set_inactive(IDEState *s, bool more);
BlockAIOCB *ide_issue_trim(
diff --git a/include/qemu-io.h b/include/qemu-io.h
index 196fde0f3a..7433239372 100644
--- a/include/qemu-io.h
+++ b/include/qemu-io.h
@@ -22,7 +22,12 @@
#define CMD_FLAG_GLOBAL ((int)0x80000000) /* don't iterate "args" */
+/* Implement a qemu-io command.
+ * Operate on @blk using @argc/@argv as the command's arguments, and
+ * return 0 on success or negative errno on failure.
+ */
typedef int (*cfunc_t)(BlockBackend *blk, int argc, char **argv);
+
typedef void (*helpfunc_t)(void);
typedef struct cmdinfo {
@@ -41,10 +46,10 @@ typedef struct cmdinfo {
extern bool qemuio_misalign;
-bool qemuio_command(BlockBackend *blk, const char *cmd);
+int qemuio_command(BlockBackend *blk, const char *cmd);
void qemuio_add_command(const cmdinfo_t *ci);
-int qemuio_command_usage(const cmdinfo_t *ci);
+void qemuio_command_usage(const cmdinfo_t *ci);
void qemuio_complete_command(const char *input,
void (*fn)(const char *cmd, void *opaque),
void *opaque);
diff --git a/qemu-doc.texi b/qemu-doc.texi
index 2effe66d6b..9aff6b4ea9 100644
--- a/qemu-doc.texi
+++ b/qemu-doc.texi
@@ -2927,13 +2927,6 @@ Option @option{-virtioconsole} has been replaced by
The @code{-clock} option is ignored since QEMU version 1.7.0. There is no
replacement since it is not needed anymore.
-@section qemu-img command line arguments
-
-@subsection convert -s (since 2.0.0)
-
-The ``convert -s snapshot_id_or_name'' argument is obsoleted
-by the ``convert -l snapshot_param'' argument instead.
-
@section QEMU Machine Protocol (QMP) commands
@subsection block-dirty-bitmap-add "autoload" parameter (since 2.12.0)
diff --git a/qemu-img-cmds.hx b/qemu-img-cmds.hx
index 3d2f7b26eb..69758fb6e8 100644
--- a/qemu-img-cmds.hx
+++ b/qemu-img-cmds.hx
@@ -44,9 +44,9 @@ STEXI
ETEXI
DEF("convert", img_convert,
- "convert [--object objectdef] [--image-opts] [--target-image-opts] [-U] [-c] [-p] [-q] [-n] [-f fmt] [-t cache] [-T src_cache] [-O output_fmt] [-B backing_file] [-o options] [-s snapshot_id_or_name] [-l snapshot_param] [-S sparse_size] [-m num_coroutines] [-W] filename [filename2 [...]] output_filename")
+ "convert [--object objectdef] [--image-opts] [--target-image-opts] [-U] [-c] [-p] [-q] [-n] [-f fmt] [-t cache] [-T src_cache] [-O output_fmt] [-B backing_file] [-o options] [-l snapshot_param] [-S sparse_size] [-m num_coroutines] [-W] filename [filename2 [...]] output_filename")
STEXI
-@item convert [--object @var{objectdef}] [--image-opts] [--target-image-opts] [-U] [-c] [-p] [-q] [-n] [-f @var{fmt}] [-t @var{cache}] [-T @var{src_cache}] [-O @var{output_fmt}] [-B @var{backing_file}] [-o @var{options}] [-s @var{snapshot_id_or_name}] [-l @var{snapshot_param}] [-S @var{sparse_size}] [-m @var{num_coroutines}] [-W] @var{filename} [@var{filename2} [...]] @var{output_filename}
+@item convert [--object @var{objectdef}] [--image-opts] [--target-image-opts] [-U] [-c] [-p] [-q] [-n] [-f @var{fmt}] [-t @var{cache}] [-T @var{src_cache}] [-O @var{output_fmt}] [-B @var{backing_file}] [-o @var{options}] [-l @var{snapshot_param}] [-S @var{sparse_size}] [-m @var{num_coroutines}] [-W] @var{filename} [@var{filename2} [...]] @var{output_filename}
ETEXI
DEF("create", img_create,
diff --git a/qemu-img.c b/qemu-img.c
index 75f1610aa0..1dcdd47254 100644
--- a/qemu-img.c
+++ b/qemu-img.c
@@ -148,8 +148,6 @@ static void QEMU_NORETURN help(void)
" 'snapshot_param' is param used for internal snapshot, format\n"
" is 'snapshot.id=[ID],snapshot.name=[NAME]', or\n"
" '[ID_OR_NAME]'\n"
- " 'snapshot_id_or_name' is deprecated, use 'snapshot_param'\n"
- " instead\n"
" '-c' indicates that target image must be compressed (qcow format only)\n"
" '-u' allows unsafe backing chains. For rebasing, it is assumed that old and\n"
" new backing file match exactly. The image doesn't need a working\n"
@@ -249,6 +247,11 @@ static int print_block_option_help(const char *filename, const char *fmt)
return 1;
}
+ if (!drv->create_opts) {
+ error_report("Format driver '%s' does not support image creation", fmt);
+ return 1;
+ }
+
create_opts = qemu_opts_append(create_opts, drv->create_opts);
if (filename) {
proto_drv = bdrv_find_protocol(filename, true, &local_err);
@@ -257,9 +260,15 @@ static int print_block_option_help(const char *filename, const char *fmt)
qemu_opts_free(create_opts);
return 1;
}
+ if (!proto_drv->create_opts) {
+ error_report("Protocal driver '%s' does not support image creation",
+ proto_drv->format_name);
+ return 1;
+ }
create_opts = qemu_opts_append(create_opts, proto_drv->create_opts);
}
+ printf("Supported options:\n");
qemu_opts_print_help(create_opts);
qemu_opts_free(create_opts);
return 0;
@@ -1545,7 +1554,9 @@ typedef struct ImgConvertState {
BlockBackend *target;
bool has_zero_init;
bool compressed;
+ bool unallocated_blocks_are_zero;
bool target_has_backing;
+ int64_t target_backing_sectors; /* negative if unknown */
bool wr_in_order;
bool copy_range;
int min_sparse;
@@ -1575,12 +1586,23 @@ static int convert_iteration_sectors(ImgConvertState *s, int64_t sector_num)
{
int64_t src_cur_offset;
int ret, n, src_cur;
+ bool post_backing_zero = false;
convert_select_part(s, sector_num, &src_cur, &src_cur_offset);
assert(s->total_sectors > sector_num);
n = MIN(s->total_sectors - sector_num, BDRV_REQUEST_MAX_SECTORS);
+ if (s->target_backing_sectors >= 0) {
+ if (sector_num >= s->target_backing_sectors) {
+ post_backing_zero = s->unallocated_blocks_are_zero;
+ } else if (sector_num + n > s->target_backing_sectors) {
+ /* Split requests around target_backing_sectors (because
+ * starting from there, zeros are handled differently) */
+ n = s->target_backing_sectors - sector_num;
+ }
+ }
+
if (s->sector_next_status <= sector_num) {
int64_t count = n * BDRV_SECTOR_SIZE;
@@ -1602,7 +1624,7 @@ static int convert_iteration_sectors(ImgConvertState *s, int64_t sector_num)
n = DIV_ROUND_UP(count, BDRV_SECTOR_SIZE);
if (ret & BDRV_BLOCK_ZERO) {
- s->status = BLK_ZERO;
+ s->status = post_backing_zero ? BLK_BACKING_FILE : BLK_ZERO;
} else if (ret & BDRV_BLOCK_DATA) {
s->status = BLK_DATA;
} else {
@@ -1994,7 +2016,7 @@ static int img_convert(int argc, char **argv)
{"target-image-opts", no_argument, 0, OPTION_TARGET_IMAGE_OPTS},
{0, 0, 0, 0}
};
- c = getopt_long(argc, argv, ":hf:O:B:co:s:l:S:pt:T:qnm:WU",
+ c = getopt_long(argc, argv, ":hf:O:B:co:l:S:pt:T:qnm:WU",
long_options, NULL);
if (c == -1) {
break;
@@ -2035,9 +2057,6 @@ static int img_convert(int argc, char **argv)
g_free(old_options);
}
break;
- case 's':
- snapshot_name = optarg;
- break;
case 'l':
if (strstart(optarg, SNAPSHOT_OPT_BASE, NULL)) {
sn_opts = qemu_opts_parse_noisily(&internal_snapshot_opts,
@@ -2368,6 +2387,16 @@ static int img_convert(int argc, char **argv)
}
}
+ if (s.target_has_backing) {
+ /* Errors are treated as "backing length unknown" (which means
+ * s.target_backing_sectors has to be negative, which it will
+ * be automatically). The backing file length is used only
+ * for optimizations, so such a case is not fatal. */
+ s.target_backing_sectors = bdrv_nb_sectors(out_bs->backing->bs);
+ } else {
+ s.target_backing_sectors = -1;
+ }
+
ret = bdrv_get_info(out_bs, &bdi);
if (ret < 0) {
if (s.compressed) {
@@ -2377,6 +2406,7 @@ static int img_convert(int argc, char **argv)
} else {
s.compressed = s.compressed || bdi.needs_compressed_writes;
s.cluster_sectors = bdi.cluster_size / BDRV_SECTOR_SIZE;
+ s.unallocated_blocks_are_zero = bdi.unallocated_blocks_are_zero;
}
ret = convert_do_copy(&s);
@@ -3240,6 +3270,9 @@ static int img_rebase(int argc, char **argv)
}
if (out_baseimg[0]) {
+ const char *overlay_filename;
+ char *out_real_path;
+
options = qdict_new();
if (out_basefmt) {
qdict_put_str(options, "driver", out_basefmt);
@@ -3248,8 +3281,26 @@ static int img_rebase(int argc, char **argv)
qdict_put_bool(options, BDRV_OPT_FORCE_SHARE, true);
}
- blk_new_backing = blk_new_open(out_baseimg, NULL,
+ overlay_filename = bs->exact_filename[0] ? bs->exact_filename
+ : bs->filename;
+ out_real_path = g_malloc(PATH_MAX);
+
+ bdrv_get_full_backing_filename_from_filename(overlay_filename,
+ out_baseimg,
+ out_real_path,
+ PATH_MAX,
+ &local_err);
+ if (local_err) {
+ error_reportf_err(local_err,
+ "Could not resolve backing filename: ");
+ ret = -1;
+ g_free(out_real_path);
+ goto out;
+ }
+
+ blk_new_backing = blk_new_open(out_real_path, NULL,
options, src_flags, &local_err);
+ g_free(out_real_path);
if (!blk_new_backing) {
error_reportf_err(local_err,
"Could not open new backing file '%s': ",
@@ -3657,6 +3708,32 @@ static void amend_status_cb(BlockDriverState *bs,
qemu_progress_print(100.f * offset / total_work_size, 0);
}
+static int print_amend_option_help(const char *format)
+{
+ BlockDriver *drv;
+
+ /* Find driver and parse its options */
+ drv = bdrv_find_format(format);
+ if (!drv) {
+ error_report("Unknown file format '%s'", format);
+ return 1;
+ }
+
+ if (!drv->bdrv_amend_options) {
+ error_report("Format driver '%s' does not support option amendment",
+ format);
+ return 1;
+ }
+
+ /* Every driver supporting amendment must have create_opts */
+ assert(drv->create_opts);
+
+ printf("Creation options for '%s':\n", format);
+ qemu_opts_print_help(drv->create_opts);
+ printf("\nNote that not all of these options may be amendable.\n");
+ return 0;
+}
+
static int img_amend(int argc, char **argv)
{
Error *err = NULL;
@@ -3756,7 +3833,7 @@ static int img_amend(int argc, char **argv)
if (fmt && has_help_option(options)) {
/* If a format is explicitly specified (and possibly no filename is
* given), print option help here */
- ret = print_block_option_help(filename, fmt);
+ ret = print_amend_option_help(fmt);
goto out;
}
@@ -3785,17 +3862,20 @@ static int img_amend(int argc, char **argv)
if (has_help_option(options)) {
/* If the format was auto-detected, print option help here */
- ret = print_block_option_help(filename, fmt);
+ ret = print_amend_option_help(fmt);
goto out;
}
- if (!bs->drv->create_opts) {
- error_report("Format driver '%s' does not support any options to amend",
+ if (!bs->drv->bdrv_amend_options) {
+ error_report("Format driver '%s' does not support option amendment",
fmt);
ret = -1;
goto out;
}
+ /* Every driver supporting amendment must have create_opts */
+ assert(bs->drv->create_opts);
+
create_opts = qemu_opts_append(create_opts, bs->drv->create_opts);
opts = qemu_opts_create(create_opts, NULL, 0, &error_abort);
qemu_opts_do_parse(opts, options, NULL, &err);
@@ -3807,10 +3887,10 @@ static int img_amend(int argc, char **argv)
/* In case the driver does not call amend_status_cb() */
qemu_progress_print(0.f, 0);
- ret = bdrv_amend_options(bs, opts, &amend_status_cb, NULL);
+ ret = bdrv_amend_options(bs, opts, &amend_status_cb, NULL, &err);
qemu_progress_print(100.f, 0);
if (ret < 0) {
- error_report("Error while amending options: %s", strerror(-ret));
+ error_report_err(err);
goto out;
}
diff --git a/qemu-img.texi b/qemu-img.texi
index 2be8206a05..aeb1b9e66c 100644
--- a/qemu-img.texi
+++ b/qemu-img.texi
@@ -61,9 +61,6 @@ by the used format or see the format descriptions below for details.
is param used for internal snapshot, format is
'snapshot.id=[ID],snapshot.name=[NAME]' or '[ID_OR_NAME]'
-@item snapshot_id_or_name
-is deprecated, use snapshot_param instead
-
@end table
@table @option
@@ -322,9 +319,9 @@ Error on reading data
@end table
-@item convert [--object @var{objectdef}] [--image-opts] [--target-image-opts] [-U] [-c] [-p] [-q] [-n] [-f @var{fmt}] [-t @var{cache}] [-T @var{src_cache}] [-O @var{output_fmt}] [-B @var{backing_file}] [-o @var{options}] [-s @var{snapshot_id_or_name}] [-l @var{snapshot_param}] [-S @var{sparse_size}] [-m @var{num_coroutines}] [-W] @var{filename} [@var{filename2} [...]] @var{output_filename}
+@item convert [--object @var{objectdef}] [--image-opts] [--target-image-opts] [-U] [-c] [-p] [-q] [-n] [-f @var{fmt}] [-t @var{cache}] [-T @var{src_cache}] [-O @var{output_fmt}] [-B @var{backing_file}] [-o @var{options}] [-l @var{snapshot_param}] [-S @var{sparse_size}] [-m @var{num_coroutines}] [-W] @var{filename} [@var{filename2} [...]] @var{output_filename}
-Convert the disk image @var{filename} or a snapshot @var{snapshot_param}(@var{snapshot_id_or_name} is deprecated)
+Convert the disk image @var{filename} or a snapshot @var{snapshot_param}
to disk image @var{output_filename} using format @var{output_fmt}. It can be optionally compressed (@code{-c}
option) or use any format specific options like encryption (@code{-o} option).
diff --git a/qemu-io-cmds.c b/qemu-io-cmds.c
index 9b3cd00af6..5bf5f28178 100644
--- a/qemu-io-cmds.c
+++ b/qemu-io-cmds.c
@@ -48,10 +48,9 @@ void qemuio_add_command(const cmdinfo_t *ci)
qsort(cmdtab, ncmds, sizeof(*cmdtab), compare_cmdname);
}
-int qemuio_command_usage(const cmdinfo_t *ci)
+void qemuio_command_usage(const cmdinfo_t *ci)
{
printf("%s %s -- %s\n", ci->name, ci->args, ci->oneline);
- return 0;
}
static int init_check_command(BlockBackend *blk, const cmdinfo_t *ct)
@@ -72,7 +71,7 @@ static int command(BlockBackend *blk, const cmdinfo_t *ct, int argc,
char *cmd = argv[0];
if (!init_check_command(blk, ct)) {
- return 0;
+ return -EINVAL;
}
if (argc - 1 < ct->argmin || (ct->argmax != -1 && argc - 1 > ct->argmax)) {
@@ -89,7 +88,7 @@ static int command(BlockBackend *blk, const cmdinfo_t *ct, int argc,
"bad argument count %d to %s, expected between %d and %d arguments\n",
argc-1, cmd, ct->argmin, ct->argmax);
}
- return 0;
+ return -EINVAL;
}
/* Request additional permissions if necessary for this command. The caller
@@ -109,7 +108,7 @@ static int command(BlockBackend *blk, const cmdinfo_t *ct, int argc,
ret = blk_set_perm(blk, new_perm, orig_shared_perm, &local_err);
if (ret < 0) {
error_report_err(local_err);
- return 0;
+ return ret;
}
}
}
@@ -652,7 +651,7 @@ static int read_f(BlockBackend *blk, int argc, char **argv)
struct timeval t1, t2;
bool Cflag = false, qflag = false, vflag = false;
bool Pflag = false, sflag = false, lflag = false, bflag = false;
- int c, cnt;
+ int c, cnt, ret;
char *buf;
int64_t offset;
int64_t count;
@@ -674,7 +673,7 @@ static int read_f(BlockBackend *blk, int argc, char **argv)
pattern_count = cvtnum(optarg);
if (pattern_count < 0) {
print_cvtnum_err(pattern_count, optarg);
- return 0;
+ return pattern_count;
}
break;
case 'p':
@@ -684,7 +683,7 @@ static int read_f(BlockBackend *blk, int argc, char **argv)
Pflag = true;
pattern = parse_pattern(optarg);
if (pattern < 0) {
- return 0;
+ return -EINVAL;
}
break;
case 'q':
@@ -695,40 +694,43 @@ static int read_f(BlockBackend *blk, int argc, char **argv)
pattern_offset = cvtnum(optarg);
if (pattern_offset < 0) {
print_cvtnum_err(pattern_offset, optarg);
- return 0;
+ return pattern_offset;
}
break;
case 'v':
vflag = true;
break;
default:
- return qemuio_command_usage(&read_cmd);
+ qemuio_command_usage(&read_cmd);
+ return -EINVAL;
}
}
if (optind != argc - 2) {
- return qemuio_command_usage(&read_cmd);
+ qemuio_command_usage(&read_cmd);
+ return -EINVAL;
}
offset = cvtnum(argv[optind]);
if (offset < 0) {
print_cvtnum_err(offset, argv[optind]);
- return 0;
+ return offset;
}
optind++;
count = cvtnum(argv[optind]);
if (count < 0) {
print_cvtnum_err(count, argv[optind]);
- return 0;
+ return count;
} else if (count > BDRV_REQUEST_MAX_BYTES) {
printf("length cannot exceed %" PRIu64 ", given %s\n",
(uint64_t)BDRV_REQUEST_MAX_BYTES, argv[optind]);
- return 0;
+ return -EINVAL;
}
if (!Pflag && (lflag || sflag)) {
- return qemuio_command_usage(&read_cmd);
+ qemuio_command_usage(&read_cmd);
+ return -EINVAL;
}
if (!lflag) {
@@ -737,19 +739,19 @@ static int read_f(BlockBackend *blk, int argc, char **argv)
if ((pattern_count < 0) || (pattern_count + pattern_offset > count)) {
printf("pattern verification range exceeds end of read data\n");
- return 0;
+ return -EINVAL;
}
if (bflag) {
if (!QEMU_IS_ALIGNED(offset, BDRV_SECTOR_SIZE)) {
printf("%" PRId64 " is not a sector-aligned value for 'offset'\n",
offset);
- return 0;
+ return -EINVAL;
}
if (!QEMU_IS_ALIGNED(count, BDRV_SECTOR_SIZE)) {
printf("%"PRId64" is not a sector-aligned value for 'count'\n",
count);
- return 0;
+ return -EINVAL;
}
}
@@ -757,16 +759,19 @@ static int read_f(BlockBackend *blk, int argc, char **argv)
gettimeofday(&t1, NULL);
if (bflag) {
- cnt = do_load_vmstate(blk, buf, offset, count, &total);
+ ret = do_load_vmstate(blk, buf, offset, count, &total);
} else {
- cnt = do_pread(blk, buf, offset, count, &total);
+ ret = do_pread(blk, buf, offset, count, &total);
}
gettimeofday(&t2, NULL);
- if (cnt < 0) {
- printf("read failed: %s\n", strerror(-cnt));
+ if (ret < 0) {
+ printf("read failed: %s\n", strerror(-ret));
goto out;
}
+ cnt = ret;
+
+ ret = 0;
if (Pflag) {
void *cmp_buf = g_malloc(pattern_count);
@@ -775,6 +780,7 @@ static int read_f(BlockBackend *blk, int argc, char **argv)
printf("Pattern verification failed at offset %"
PRId64 ", %"PRId64" bytes\n",
offset + pattern_offset, pattern_count);
+ ret = -EINVAL;
}
g_free(cmp_buf);
}
@@ -793,8 +799,7 @@ static int read_f(BlockBackend *blk, int argc, char **argv)
out:
qemu_io_free(buf);
-
- return 0;
+ return ret;
}
static void readv_help(void)
@@ -832,7 +837,7 @@ static int readv_f(BlockBackend *blk, int argc, char **argv)
{
struct timeval t1, t2;
bool Cflag = false, qflag = false, vflag = false;
- int c, cnt;
+ int c, cnt, ret;
char *buf;
int64_t offset;
/* Some compilers get confused and warn if this is not initialized. */
@@ -851,7 +856,7 @@ static int readv_f(BlockBackend *blk, int argc, char **argv)
Pflag = true;
pattern = parse_pattern(optarg);
if (pattern < 0) {
- return 0;
+ return -EINVAL;
}
break;
case 'q':
@@ -861,36 +866,41 @@ static int readv_f(BlockBackend *blk, int argc, char **argv)
vflag = true;
break;
default:
- return qemuio_command_usage(&readv_cmd);
+ qemuio_command_usage(&readv_cmd);
+ return -EINVAL;
}
}
if (optind > argc - 2) {
- return qemuio_command_usage(&readv_cmd);
+ qemuio_command_usage(&readv_cmd);
+ return -EINVAL;
}
offset = cvtnum(argv[optind]);
if (offset < 0) {
print_cvtnum_err(offset, argv[optind]);
- return 0;
+ return offset;
}
optind++;
nr_iov = argc - optind;
buf = create_iovec(blk, &qiov, &argv[optind], nr_iov, 0xab);
if (buf == NULL) {
- return 0;
+ return -EINVAL;
}
gettimeofday(&t1, NULL);
- cnt = do_aio_readv(blk, &qiov, offset, &total);
+ ret = do_aio_readv(blk, &qiov, offset, &total);
gettimeofday(&t2, NULL);
- if (cnt < 0) {
- printf("readv failed: %s\n", strerror(-cnt));
+ if (ret < 0) {
+ printf("readv failed: %s\n", strerror(-ret));
goto out;
}
+ cnt = ret;
+
+ ret = 0;
if (Pflag) {
void *cmp_buf = g_malloc(qiov.size);
@@ -898,6 +908,7 @@ static int readv_f(BlockBackend *blk, int argc, char **argv)
if (memcmp(buf, cmp_buf, qiov.size)) {
printf("Pattern verification failed at offset %"
PRId64 ", %zd bytes\n", offset, qiov.size);
+ ret = -EINVAL;
}
g_free(cmp_buf);
}
@@ -917,7 +928,7 @@ static int readv_f(BlockBackend *blk, int argc, char **argv)
out:
qemu_iovec_destroy(&qiov);
qemu_io_free(buf);
- return 0;
+ return ret;
}
static void write_help(void)
@@ -963,7 +974,7 @@ static int write_f(BlockBackend *blk, int argc, char **argv)
bool Cflag = false, qflag = false, bflag = false;
bool Pflag = false, zflag = false, cflag = false;
int flags = 0;
- int c, cnt;
+ int c, cnt, ret;
char *buf = NULL;
int64_t offset;
int64_t count;
@@ -992,7 +1003,7 @@ static int write_f(BlockBackend *blk, int argc, char **argv)
Pflag = true;
pattern = parse_pattern(optarg);
if (pattern < 0) {
- return 0;
+ return -EINVAL;
}
break;
case 'q':
@@ -1005,62 +1016,64 @@ static int write_f(BlockBackend *blk, int argc, char **argv)
zflag = true;
break;
default:
- return qemuio_command_usage(&write_cmd);
+ qemuio_command_usage(&write_cmd);
+ return -EINVAL;
}
}
if (optind != argc - 2) {
- return qemuio_command_usage(&write_cmd);
+ qemuio_command_usage(&write_cmd);
+ return -EINVAL;
}
if (bflag && zflag) {
printf("-b and -z cannot be specified at the same time\n");
- return 0;
+ return -EINVAL;
}
if ((flags & BDRV_REQ_FUA) && (bflag || cflag)) {
printf("-f and -b or -c cannot be specified at the same time\n");
- return 0;
+ return -EINVAL;
}
if ((flags & BDRV_REQ_MAY_UNMAP) && !zflag) {
printf("-u requires -z to be specified\n");
- return 0;
+ return -EINVAL;
}
if (zflag && Pflag) {
printf("-z and -P cannot be specified at the same time\n");
- return 0;
+ return -EINVAL;
}
offset = cvtnum(argv[optind]);
if (offset < 0) {
print_cvtnum_err(offset, argv[optind]);
- return 0;
+ return offset;
}
optind++;
count = cvtnum(argv[optind]);
if (count < 0) {
print_cvtnum_err(count, argv[optind]);
- return 0;
+ return count;
} else if (count > BDRV_REQUEST_MAX_BYTES) {
printf("length cannot exceed %" PRIu64 ", given %s\n",
(uint64_t)BDRV_REQUEST_MAX_BYTES, argv[optind]);
- return 0;
+ return -EINVAL;
}
if (bflag || cflag) {
if (!QEMU_IS_ALIGNED(offset, BDRV_SECTOR_SIZE)) {
printf("%" PRId64 " is not a sector-aligned value for 'offset'\n",
offset);
- return 0;
+ return -EINVAL;
}
if (!QEMU_IS_ALIGNED(count, BDRV_SECTOR_SIZE)) {
printf("%"PRId64" is not a sector-aligned value for 'count'\n",
count);
- return 0;
+ return -EINVAL;
}
}
@@ -1070,20 +1083,23 @@ static int write_f(BlockBackend *blk, int argc, char **argv)
gettimeofday(&t1, NULL);
if (bflag) {
- cnt = do_save_vmstate(blk, buf, offset, count, &total);
+ ret = do_save_vmstate(blk, buf, offset, count, &total);
} else if (zflag) {
- cnt = do_co_pwrite_zeroes(blk, offset, count, flags, &total);
+ ret = do_co_pwrite_zeroes(blk, offset, count, flags, &total);
} else if (cflag) {
- cnt = do_write_compressed(blk, buf, offset, count, &total);
+ ret = do_write_compressed(blk, buf, offset, count, &total);
} else {
- cnt = do_pwrite(blk, buf, offset, count, flags, &total);
+ ret = do_pwrite(blk, buf, offset, count, flags, &total);
}
gettimeofday(&t2, NULL);
- if (cnt < 0) {
- printf("write failed: %s\n", strerror(-cnt));
+ if (ret < 0) {
+ printf("write failed: %s\n", strerror(-ret));
goto out;
}
+ cnt = ret;
+
+ ret = 0;
if (qflag) {
goto out;
@@ -1097,8 +1113,7 @@ out:
if (!zflag) {
qemu_io_free(buf);
}
-
- return 0;
+ return ret;
}
static void
@@ -1138,7 +1153,7 @@ static int writev_f(BlockBackend *blk, int argc, char **argv)
struct timeval t1, t2;
bool Cflag = false, qflag = false;
int flags = 0;
- int c, cnt;
+ int c, cnt, ret;
char *buf;
int64_t offset;
/* Some compilers get confused and warn if this is not initialized. */
@@ -1161,39 +1176,44 @@ static int writev_f(BlockBackend *blk, int argc, char **argv)
case 'P':
pattern = parse_pattern(optarg);
if (pattern < 0) {
- return 0;
+ return -EINVAL;
}
break;
default:
- return qemuio_command_usage(&writev_cmd);
+ qemuio_command_usage(&writev_cmd);
+ return -EINVAL;
}
}
if (optind > argc - 2) {
- return qemuio_command_usage(&writev_cmd);
+ qemuio_command_usage(&writev_cmd);
+ return -EINVAL;
}
offset = cvtnum(argv[optind]);
if (offset < 0) {
print_cvtnum_err(offset, argv[optind]);
- return 0;
+ return offset;
}
optind++;
nr_iov = argc - optind;
buf = create_iovec(blk, &qiov, &argv[optind], nr_iov, pattern);
if (buf == NULL) {
- return 0;
+ return -EINVAL;
}
gettimeofday(&t1, NULL);
- cnt = do_aio_writev(blk, &qiov, offset, flags, &total);
+ ret = do_aio_writev(blk, &qiov, offset, flags, &total);
gettimeofday(&t2, NULL);
- if (cnt < 0) {
- printf("writev failed: %s\n", strerror(-cnt));
+ if (ret < 0) {
+ printf("writev failed: %s\n", strerror(-ret));
goto out;
}
+ cnt = ret;
+
+ ret = 0;
if (qflag) {
goto out;
@@ -1205,7 +1225,7 @@ static int writev_f(BlockBackend *blk, int argc, char **argv)
out:
qemu_iovec_destroy(&qiov);
qemu_io_free(buf);
- return 0;
+ return ret;
}
struct aio_ctx {
@@ -1312,6 +1332,9 @@ static void aio_read_help(void)
" standard output stream (with -v option) for subsequent inspection.\n"
" The read is performed asynchronously and the aio_flush command must be\n"
" used to ensure all outstanding aio requests have been completed.\n"
+" Note that due to its asynchronous nature, this command will be\n"
+" considered successful once the request is submitted, independently\n"
+" of potential I/O errors or pattern mismatches.\n"
" -C, -- report statistics in a machine parsable format\n"
" -P, -- use a pattern to verify read data\n"
" -i, -- treat request as invalid, for exercising stats\n"
@@ -1348,7 +1371,7 @@ static int aio_read_f(BlockBackend *blk, int argc, char **argv)
ctx->pattern = parse_pattern(optarg);
if (ctx->pattern < 0) {
g_free(ctx);
- return 0;
+ return -EINVAL;
}
break;
case 'i':
@@ -1364,20 +1387,23 @@ static int aio_read_f(BlockBackend *blk, int argc, char **argv)
break;
default:
g_free(ctx);
- return qemuio_command_usage(&aio_read_cmd);
+ qemuio_command_usage(&aio_read_cmd);
+ return -EINVAL;
}
}
if (optind > argc - 2) {
g_free(ctx);
- return qemuio_command_usage(&aio_read_cmd);
+ qemuio_command_usage(&aio_read_cmd);
+ return -EINVAL;
}
ctx->offset = cvtnum(argv[optind]);
if (ctx->offset < 0) {
- print_cvtnum_err(ctx->offset, argv[optind]);
+ int ret = ctx->offset;
+ print_cvtnum_err(ret, argv[optind]);
g_free(ctx);
- return 0;
+ return ret;
}
optind++;
@@ -1386,7 +1412,7 @@ static int aio_read_f(BlockBackend *blk, int argc, char **argv)
if (ctx->buf == NULL) {
block_acct_invalid(blk_get_stats(blk), BLOCK_ACCT_READ);
g_free(ctx);
- return 0;
+ return -EINVAL;
}
gettimeofday(&ctx->t1, NULL);
@@ -1410,6 +1436,9 @@ static void aio_write_help(void)
" filled with a set pattern (0xcdcdcdcd).\n"
" The write is performed asynchronously and the aio_flush command must be\n"
" used to ensure all outstanding aio requests have been completed.\n"
+" Note that due to its asynchronous nature, this command will be\n"
+" considered successful once the request is submitted, independently\n"
+" of potential I/O errors or pattern mismatches.\n"
" -P, -- use different pattern to fill file\n"
" -C, -- report statistics in a machine parsable format\n"
" -f, -- use Force Unit Access semantics\n"
@@ -1459,7 +1488,7 @@ static int aio_write_f(BlockBackend *blk, int argc, char **argv)
pattern = parse_pattern(optarg);
if (pattern < 0) {
g_free(ctx);
- return 0;
+ return -EINVAL;
}
break;
case 'i':
@@ -1472,38 +1501,41 @@ static int aio_write_f(BlockBackend *blk, int argc, char **argv)
break;
default:
g_free(ctx);
- return qemuio_command_usage(&aio_write_cmd);
+ qemuio_command_usage(&aio_write_cmd);
+ return -EINVAL;
}
}
if (optind > argc - 2) {
g_free(ctx);
- return qemuio_command_usage(&aio_write_cmd);
+ qemuio_command_usage(&aio_write_cmd);
+ return -EINVAL;
}
if (ctx->zflag && optind != argc - 2) {
printf("-z supports only a single length parameter\n");
g_free(ctx);
- return 0;
+ return -EINVAL;
}
if ((flags & BDRV_REQ_MAY_UNMAP) && !ctx->zflag) {
printf("-u requires -z to be specified\n");
g_free(ctx);
- return 0;
+ return -EINVAL;
}
if (ctx->zflag && ctx->Pflag) {
printf("-z and -P cannot be specified at the same time\n");
g_free(ctx);
- return 0;
+ return -EINVAL;
}
ctx->offset = cvtnum(argv[optind]);
if (ctx->offset < 0) {
- print_cvtnum_err(ctx->offset, argv[optind]);
+ int ret = ctx->offset;
+ print_cvtnum_err(ret, argv[optind]);
g_free(ctx);
- return 0;
+ return ret;
}
optind++;
@@ -1512,7 +1544,7 @@ static int aio_write_f(BlockBackend *blk, int argc, char **argv)
if (count < 0) {
print_cvtnum_err(count, argv[optind]);
g_free(ctx);
- return 0;
+ return count;
}
ctx->qiov.size = count;
@@ -1525,7 +1557,7 @@ static int aio_write_f(BlockBackend *blk, int argc, char **argv)
if (ctx->buf == NULL) {
block_acct_invalid(blk_get_stats(blk), BLOCK_ACCT_WRITE);
g_free(ctx);
- return 0;
+ return -EINVAL;
}
gettimeofday(&ctx->t1, NULL);
@@ -1535,6 +1567,7 @@ static int aio_write_f(BlockBackend *blk, int argc, char **argv)
blk_aio_pwritev(blk, ctx->offset, &ctx->qiov, flags, aio_write_done,
ctx);
}
+
return 0;
}
@@ -1555,8 +1588,7 @@ static const cmdinfo_t aio_flush_cmd = {
static int flush_f(BlockBackend *blk, int argc, char **argv)
{
- blk_flush(blk);
- return 0;
+ return blk_flush(blk);
}
static const cmdinfo_t flush_cmd = {
@@ -1575,13 +1607,13 @@ static int truncate_f(BlockBackend *blk, int argc, char **argv)
offset = cvtnum(argv[1]);
if (offset < 0) {
print_cvtnum_err(offset, argv[1]);
- return 0;
+ return offset;
}
ret = blk_truncate(blk, offset, PREALLOC_MODE_OFF, &local_err);
if (ret < 0) {
error_report_err(local_err);
- return 0;
+ return ret;
}
return 0;
@@ -1606,7 +1638,7 @@ static int length_f(BlockBackend *blk, int argc, char **argv)
size = blk_getlength(blk);
if (size < 0) {
printf("getlength: %s\n", strerror(-size));
- return 0;
+ return size;
}
cvtstr(size, s1, sizeof(s1));
@@ -1640,7 +1672,7 @@ static int info_f(BlockBackend *blk, int argc, char **argv)
ret = bdrv_get_info(bs, &bdi);
if (ret) {
- return 0;
+ return ret;
}
cvtstr(bdi.cluster_size, s1, sizeof(s1));
@@ -1713,30 +1745,32 @@ static int discard_f(BlockBackend *blk, int argc, char **argv)
qflag = true;
break;
default:
- return qemuio_command_usage(&discard_cmd);
+ qemuio_command_usage(&discard_cmd);
+ return -EINVAL;
}
}
if (optind != argc - 2) {
- return qemuio_command_usage(&discard_cmd);
+ qemuio_command_usage(&discard_cmd);
+ return -EINVAL;
}
offset = cvtnum(argv[optind]);
if (offset < 0) {
print_cvtnum_err(offset, argv[optind]);
- return 0;
+ return offset;
}
optind++;
bytes = cvtnum(argv[optind]);
if (bytes < 0) {
print_cvtnum_err(bytes, argv[optind]);
- return 0;
+ return bytes;
} else if (bytes >> BDRV_SECTOR_BITS > BDRV_REQUEST_MAX_SECTORS) {
printf("length cannot exceed %"PRIu64", given %s\n",
(uint64_t)BDRV_REQUEST_MAX_SECTORS << BDRV_SECTOR_BITS,
argv[optind]);
- return 0;
+ return -EINVAL;
}
gettimeofday(&t1, NULL);
@@ -1745,7 +1779,7 @@ static int discard_f(BlockBackend *blk, int argc, char **argv)
if (ret < 0) {
printf("discard failed: %s\n", strerror(-ret));
- goto out;
+ return ret;
}
/* Finally, report back -- -C gives a parsable format */
@@ -1754,7 +1788,6 @@ static int discard_f(BlockBackend *blk, int argc, char **argv)
print_report("discard", &t2, offset, bytes, bytes, 1, Cflag);
}
-out:
return 0;
}
@@ -1769,14 +1802,14 @@ static int alloc_f(BlockBackend *blk, int argc, char **argv)
start = offset = cvtnum(argv[1]);
if (offset < 0) {
print_cvtnum_err(offset, argv[1]);
- return 0;
+ return offset;
}
if (argc == 3) {
count = cvtnum(argv[2]);
if (count < 0) {
print_cvtnum_err(count, argv[2]);
- return 0;
+ return count;
}
} else {
count = BDRV_SECTOR_SIZE;
@@ -1788,7 +1821,7 @@ static int alloc_f(BlockBackend *blk, int argc, char **argv)
ret = bdrv_is_allocated(bs, offset, remaining, &num);
if (ret < 0) {
printf("is_allocated failed: %s\n", strerror(-ret));
- return 0;
+ return ret;
}
offset += num;
remaining -= num;
@@ -1863,17 +1896,17 @@ static int map_f(BlockBackend *blk, int argc, char **argv)
bytes = blk_getlength(blk);
if (bytes < 0) {
error_report("Failed to query image length: %s", strerror(-bytes));
- return 0;
+ return bytes;
}
while (bytes) {
ret = map_is_allocated(blk_bs(blk), offset, bytes, &num);
if (ret < 0) {
error_report("Failed to get allocation status: %s", strerror(-ret));
- return 0;
+ return ret;
} else if (!num) {
error_report("Unexpected end of image");
- return 0;
+ return -EIO;
}
retstr = ret ? " allocated" : "not allocated";
@@ -1954,19 +1987,19 @@ static int reopen_f(BlockBackend *blk, int argc, char **argv)
case 'c':
if (bdrv_parse_cache_mode(optarg, &flags, &writethrough) < 0) {
error_report("Invalid cache option: %s", optarg);
- return 0;
+ return -EINVAL;
}
break;
case 'o':
if (!qemu_opts_parse_noisily(&reopen_opts, optarg, 0)) {
qemu_opts_reset(&reopen_opts);
- return 0;
+ return -EINVAL;
}
break;
case 'r':
if (has_rw_option) {
error_report("Only one -r/-w option may be given");
- return 0;
+ return -EINVAL;
}
flags &= ~BDRV_O_RDWR;
has_rw_option = true;
@@ -1974,20 +2007,22 @@ static int reopen_f(BlockBackend *blk, int argc, char **argv)
case 'w':
if (has_rw_option) {
error_report("Only one -r/-w option may be given");
- return 0;
+ return -EINVAL;
}
flags |= BDRV_O_RDWR;
has_rw_option = true;
break;
default:
qemu_opts_reset(&reopen_opts);
- return qemuio_command_usage(&reopen_cmd);
+ qemuio_command_usage(&reopen_cmd);
+ return -EINVAL;
}
}
if (optind != argc) {
qemu_opts_reset(&reopen_opts);
- return qemuio_command_usage(&reopen_cmd);
+ qemuio_command_usage(&reopen_cmd);
+ return -EINVAL;
}
if (writethrough != blk_enable_write_cache(blk) &&
@@ -1995,7 +2030,7 @@ static int reopen_f(BlockBackend *blk, int argc, char **argv)
{
error_report("Cannot change cache.writeback: Device attached");
qemu_opts_reset(&reopen_opts);
- return 0;
+ return -EBUSY;
}
if (!(flags & BDRV_O_RDWR)) {
@@ -2021,10 +2056,10 @@ static int reopen_f(BlockBackend *blk, int argc, char **argv)
if (local_err) {
error_report_err(local_err);
- } else {
- blk_set_enable_write_cache(blk, !writethrough);
+ return -EINVAL;
}
+ blk_set_enable_write_cache(blk, !writethrough);
return 0;
}
@@ -2035,6 +2070,7 @@ static int break_f(BlockBackend *blk, int argc, char **argv)
ret = bdrv_debug_breakpoint(blk_bs(blk), argv[1], argv[2]);
if (ret < 0) {
printf("Could not set breakpoint: %s\n", strerror(-ret));
+ return ret;
}
return 0;
@@ -2047,6 +2083,7 @@ static int remove_break_f(BlockBackend *blk, int argc, char **argv)
ret = bdrv_debug_remove_breakpoint(blk_bs(blk), argv[1]);
if (ret < 0) {
printf("Could not remove breakpoint %s: %s\n", argv[1], strerror(-ret));
+ return ret;
}
return 0;
@@ -2078,6 +2115,7 @@ static int resume_f(BlockBackend *blk, int argc, char **argv)
ret = bdrv_debug_resume(blk_bs(blk), argv[1]);
if (ret < 0) {
printf("Could not resume request: %s\n", strerror(-ret));
+ return ret;
}
return 0;
@@ -2097,7 +2135,6 @@ static int wait_break_f(BlockBackend *blk, int argc, char **argv)
while (!bdrv_debug_is_suspended(blk_bs(blk), argv[1])) {
aio_poll(blk_get_aio_context(blk), true);
}
-
return 0;
}
@@ -2154,11 +2191,11 @@ static int sigraise_f(BlockBackend *blk, int argc, char **argv)
int64_t sig = cvtnum(argv[1]);
if (sig < 0) {
print_cvtnum_err(sig, argv[1]);
- return 0;
+ return sig;
} else if (sig > NSIG) {
printf("signal argument '%s' is too large to be a valid signal\n",
argv[1]);
- return 0;
+ return -EINVAL;
}
/* Using raise() to kill this process does not necessarily flush all open
@@ -2168,6 +2205,7 @@ static int sigraise_f(BlockBackend *blk, int argc, char **argv)
fflush(stderr);
raise(sig);
+
return 0;
}
@@ -2187,7 +2225,7 @@ static int sleep_f(BlockBackend *blk, int argc, char **argv)
ms = strtol(argv[1], &endptr, 0);
if (ms < 0 || *endptr != '\0') {
printf("%s is not a valid number\n", argv[1]);
- return 0;
+ return -EINVAL;
}
timer = timer_new_ns(QEMU_CLOCK_HOST, sleep_cb, &expired);
@@ -2198,7 +2236,6 @@ static int sleep_f(BlockBackend *blk, int argc, char **argv)
}
timer_free(timer);
-
return 0;
}
@@ -2258,7 +2295,7 @@ static int help_f(BlockBackend *blk, int argc, char **argv)
ct = find_command(argv[1]);
if (ct == NULL) {
printf("command %s not found\n", argv[1]);
- return 0;
+ return -EINVAL;
}
help_onecmd(argv[1], ct);
@@ -2276,14 +2313,14 @@ static const cmdinfo_t help_cmd = {
.oneline = "help for one or all commands",
};
-bool qemuio_command(BlockBackend *blk, const char *cmd)
+int qemuio_command(BlockBackend *blk, const char *cmd)
{
AioContext *ctx;
char *input;
const cmdinfo_t *ct;
char **v;
int c;
- bool done = false;
+ int ret = 0;
input = g_strdup(cmd);
v = breakline(input, &c);
@@ -2292,16 +2329,17 @@ bool qemuio_command(BlockBackend *blk, const char *cmd)
if (ct) {
ctx = blk ? blk_get_aio_context(blk) : qemu_get_aio_context();
aio_context_acquire(ctx);
- done = command(blk, ct, c, v);
+ ret = command(blk, ct, c, v);
aio_context_release(ctx);
} else {
fprintf(stderr, "command \"%s\" not found\n", v[0]);
+ ret = -EINVAL;
}
}
g_free(input);
g_free(v);
- return done;
+ return ret;
}
static void __attribute((constructor)) init_qemuio_commands(void)
diff --git a/qemu-io.c b/qemu-io.c
index 73c638ff8b..13829f5e21 100644
--- a/qemu-io.c
+++ b/qemu-io.c
@@ -37,6 +37,7 @@
static char *progname;
static BlockBackend *qemuio_blk;
+static bool quit_qemu_io;
/* qemu-io commands passed using -c */
static int ncmdline;
@@ -166,6 +167,7 @@ static int open_f(BlockBackend *blk, int argc, char **argv)
int readonly = 0;
bool writethrough = true;
int c;
+ int ret;
QemuOpts *qopts;
QDict *opts;
bool force_share = false;
@@ -192,25 +194,25 @@ static int open_f(BlockBackend *blk, int argc, char **argv)
if (bdrv_parse_cache_mode(optarg, &flags, &writethrough) < 0) {
error_report("Invalid cache option: %s", optarg);
qemu_opts_reset(&empty_opts);
- return 0;
+ return -EINVAL;
}
break;
case 'd':
if (bdrv_parse_discard_flags(optarg, &flags) < 0) {
error_report("Invalid discard option: %s", optarg);
qemu_opts_reset(&empty_opts);
- return 0;
+ return -EINVAL;
}
break;
case 'o':
if (imageOpts) {
printf("--image-opts and 'open -o' are mutually exclusive\n");
qemu_opts_reset(&empty_opts);
- return 0;
+ return -EINVAL;
}
if (!qemu_opts_parse_noisily(&empty_opts, optarg, false)) {
qemu_opts_reset(&empty_opts);
- return 0;
+ return -EINVAL;
}
break;
case 'U':
@@ -218,7 +220,8 @@ static int open_f(BlockBackend *blk, int argc, char **argv)
break;
default:
qemu_opts_reset(&empty_opts);
- return qemuio_command_usage(&open_cmd);
+ qemuio_command_usage(&open_cmd);
+ return -EINVAL;
}
}
@@ -229,7 +232,7 @@ static int open_f(BlockBackend *blk, int argc, char **argv)
if (imageOpts && (optind == argc - 1)) {
if (!qemu_opts_parse_noisily(&empty_opts, argv[optind], false)) {
qemu_opts_reset(&empty_opts);
- return 0;
+ return -EINVAL;
}
optind++;
}
@@ -239,19 +242,26 @@ static int open_f(BlockBackend *blk, int argc, char **argv)
qemu_opts_reset(&empty_opts);
if (optind == argc - 1) {
- openfile(argv[optind], flags, writethrough, force_share, opts);
+ ret = openfile(argv[optind], flags, writethrough, force_share, opts);
} else if (optind == argc) {
- openfile(NULL, flags, writethrough, force_share, opts);
+ ret = openfile(NULL, flags, writethrough, force_share, opts);
} else {
qobject_unref(opts);
qemuio_command_usage(&open_cmd);
+ return -EINVAL;
}
+
+ if (ret) {
+ return -EINVAL;
+ }
+
return 0;
}
static int quit_f(BlockBackend *blk, int argc, char **argv)
{
- return 1;
+ quit_qemu_io = true;
+ return 0;
}
static const cmdinfo_t quit_cmd = {
@@ -390,20 +400,24 @@ static void prep_fetchline(void *opaque)
*fetchable= 1;
}
-static void command_loop(void)
+static int command_loop(void)
{
- int i, done = 0, fetchable = 0, prompted = 0;
+ int i, fetchable = 0, prompted = 0;
+ int ret, last_error = 0;
char *input;
- for (i = 0; !done && i < ncmdline; i++) {
- done = qemuio_command(qemuio_blk, cmdline[i]);
+ for (i = 0; !quit_qemu_io && i < ncmdline; i++) {
+ ret = qemuio_command(qemuio_blk, cmdline[i]);
+ if (ret < 0) {
+ last_error = ret;
+ }
}
if (cmdline) {
g_free(cmdline);
- return;
+ return last_error;
}
- while (!done) {
+ while (!quit_qemu_io) {
if (!prompted) {
printf("%s", get_prompt());
fflush(stdout);
@@ -421,13 +435,19 @@ static void command_loop(void)
if (input == NULL) {
break;
}
- done = qemuio_command(qemuio_blk, input);
+ ret = qemuio_command(qemuio_blk, input);
g_free(input);
+ if (ret < 0) {
+ last_error = ret;
+ }
+
prompted = 0;
fetchable = 0;
}
qemu_set_fd_handler(STDIN_FILENO, NULL, NULL, NULL);
+
+ return last_error;
}
static void add_user_command(char *optarg)
@@ -492,6 +512,7 @@ int main(int argc, char **argv)
int c;
int opt_index = 0;
int flags = BDRV_O_UNMAP;
+ int ret;
bool writethrough = true;
Error *local_error = NULL;
QDict *opts = NULL;
@@ -653,7 +674,7 @@ int main(int argc, char **argv)
}
}
}
- command_loop();
+ ret = command_loop();
/*
* Make sure all outstanding requests complete before the program exits.
@@ -662,5 +683,10 @@ int main(int argc, char **argv)
blk_unref(qemuio_blk);
g_free(readline_state);
- return 0;
+
+ if (ret < 0) {
+ return 1;
+ } else {
+ return 0;
+ }
}
diff --git a/target/i386/cpu.c b/target/i386/cpu.c
index 94260412e2..1e69e68f25 100644
--- a/target/i386/cpu.c
+++ b/target/i386/cpu.c
@@ -335,6 +335,99 @@ static void encode_cache_cpuid80000006(CPUCacheInfo *l2,
}
/*
+ * Definitions used for building CPUID Leaf 0x8000001D and 0x8000001E
+ * Please refer to the AMD64 Architecture Programmer’s Manual Volume 3.
+ * Define the constants to build the cpu topology. Right now, TOPOEXT
+ * feature is enabled only on EPYC. So, these constants are based on
+ * EPYC supported configurations. We may need to handle the cases if
+ * these values change in future.
+ */
+/* Maximum core complexes in a node */
+#define MAX_CCX 2
+/* Maximum cores in a core complex */
+#define MAX_CORES_IN_CCX 4
+/* Maximum cores in a node */
+#define MAX_CORES_IN_NODE 8
+/* Maximum nodes in a socket */
+#define MAX_NODES_PER_SOCKET 4
+
+/*
+ * Figure out the number of nodes required to build this config.
+ * Max cores in a node is 8
+ */
+static int nodes_in_socket(int nr_cores)
+{
+ int nodes;
+
+ nodes = DIV_ROUND_UP(nr_cores, MAX_CORES_IN_NODE);
+
+ /* Hardware does not support config with 3 nodes, return 4 in that case */
+ return (nodes == 3) ? 4 : nodes;
+}
+
+/*
+ * Decide the number of cores in a core complex with the given nr_cores using
+ * following set constants MAX_CCX, MAX_CORES_IN_CCX, MAX_CORES_IN_NODE and
+ * MAX_NODES_PER_SOCKET. Maintain symmetry as much as possible
+ * L3 cache is shared across all cores in a core complex. So, this will also
+ * tell us how many cores are sharing the L3 cache.
+ */
+static int cores_in_core_complex(int nr_cores)
+{
+ int nodes;
+
+ /* Check if we can fit all the cores in one core complex */
+ if (nr_cores <= MAX_CORES_IN_CCX) {
+ return nr_cores;
+ }
+ /* Get the number of nodes required to build this config */
+ nodes = nodes_in_socket(nr_cores);
+
+ /*
+ * Divide the cores accros all the core complexes
+ * Return rounded up value
+ */
+ return DIV_ROUND_UP(nr_cores, nodes * MAX_CCX);
+}
+
+/* Encode cache info for CPUID[8000001D] */
+static void encode_cache_cpuid8000001d(CPUCacheInfo *cache, CPUState *cs,
+ uint32_t *eax, uint32_t *ebx,
+ uint32_t *ecx, uint32_t *edx)
+{
+ uint32_t l3_cores;
+ assert(cache->size == cache->line_size * cache->associativity *
+ cache->partitions * cache->sets);
+
+ *eax = CACHE_TYPE(cache->type) | CACHE_LEVEL(cache->level) |
+ (cache->self_init ? CACHE_SELF_INIT_LEVEL : 0);
+
+ /* L3 is shared among multiple cores */
+ if (cache->level == 3) {
+ l3_cores = cores_in_core_complex(cs->nr_cores);
+ *eax |= ((l3_cores * cs->nr_threads) - 1) << 14;
+ } else {
+ *eax |= ((cs->nr_threads - 1) << 14);
+ }
+
+ assert(cache->line_size > 0);
+ assert(cache->partitions > 0);
+ assert(cache->associativity > 0);
+ /* We don't implement fully-associative caches */
+ assert(cache->associativity < cache->sets);
+ *ebx = (cache->line_size - 1) |
+ ((cache->partitions - 1) << 12) |
+ ((cache->associativity - 1) << 22);
+
+ assert(cache->sets > 0);
+ *ecx = cache->sets - 1;
+
+ *edx = (cache->no_invd_sharing ? CACHE_NO_INVD_SHARING : 0) |
+ (cache->inclusive ? CACHE_INCLUSIVE : 0) |
+ (cache->complex_indexing ? CACHE_COMPLEX_IDX : 0);
+}
+
+/*
* Definitions of the hardcoded cache entries we expose:
* These are legacy cache values. If there is a need to change any
* of these values please use builtin_x86_defs
@@ -1112,7 +1205,7 @@ struct X86CPUDefinition {
};
static CPUCaches epyc_cache_info = {
- .l1d_cache = {
+ .l1d_cache = &(CPUCacheInfo) {
.type = DCACHE,
.level = 1,
.size = 32 * KiB,
@@ -1124,7 +1217,7 @@ static CPUCaches epyc_cache_info = {
.self_init = 1,
.no_invd_sharing = true,
},
- .l1i_cache = {
+ .l1i_cache = &(CPUCacheInfo) {
.type = ICACHE,
.level = 1,
.size = 64 * KiB,
@@ -1136,7 +1229,7 @@ static CPUCaches epyc_cache_info = {
.self_init = 1,
.no_invd_sharing = true,
},
- .l2_cache = {
+ .l2_cache = &(CPUCacheInfo) {
.type = UNIFIED_CACHE,
.level = 2,
.size = 512 * KiB,
@@ -1146,7 +1239,7 @@ static CPUCaches epyc_cache_info = {
.sets = 1024,
.lines_per_tag = 1,
},
- .l3_cache = {
+ .l3_cache = &(CPUCacheInfo) {
.type = UNIFIED_CACHE,
.level = 3,
.size = 8 * MiB,
@@ -3340,9 +3433,8 @@ static void x86_cpu_load_def(X86CPU *cpu, X86CPUDefinition *def, Error **errp)
env->features[w] = def->features[w];
}
- /* Store Cache information from the X86CPUDefinition if available */
- env->cache_info = def->cache_info;
- cpu->legacy_cache = def->cache_info ? 0 : 1;
+ /* legacy-cache defaults to 'off' if CPU model provides cache info */
+ cpu->legacy_cache = !def->cache_info;
/* Special cases not set in the X86CPUDefinition structs: */
/* TODO: in-kernel irqchip for hvf */
@@ -3693,21 +3785,11 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count,
if (!cpu->enable_l3_cache) {
*ecx = 0;
} else {
- if (env->cache_info && !cpu->legacy_cache) {
- *ecx = cpuid2_cache_descriptor(&env->cache_info->l3_cache);
- } else {
- *ecx = cpuid2_cache_descriptor(&legacy_l3_cache);
- }
- }
- if (env->cache_info && !cpu->legacy_cache) {
- *edx = (cpuid2_cache_descriptor(&env->cache_info->l1d_cache) << 16) |
- (cpuid2_cache_descriptor(&env->cache_info->l1i_cache) << 8) |
- (cpuid2_cache_descriptor(&env->cache_info->l2_cache));
- } else {
- *edx = (cpuid2_cache_descriptor(&legacy_l1d_cache) << 16) |
- (cpuid2_cache_descriptor(&legacy_l1i_cache) << 8) |
- (cpuid2_cache_descriptor(&legacy_l2_cache_cpuid2));
+ *ecx = cpuid2_cache_descriptor(env->cache_info_cpuid2.l3_cache);
}
+ *edx = (cpuid2_cache_descriptor(env->cache_info_cpuid2.l1d_cache) << 16) |
+ (cpuid2_cache_descriptor(env->cache_info_cpuid2.l1i_cache) << 8) |
+ (cpuid2_cache_descriptor(env->cache_info_cpuid2.l2_cache));
break;
case 4:
/* cache info: needed for Core compatibility */
@@ -3720,35 +3802,27 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count,
}
} else {
*eax = 0;
- CPUCacheInfo *l1d, *l1i, *l2, *l3;
- if (env->cache_info && !cpu->legacy_cache) {
- l1d = &env->cache_info->l1d_cache;
- l1i = &env->cache_info->l1i_cache;
- l2 = &env->cache_info->l2_cache;
- l3 = &env->cache_info->l3_cache;
- } else {
- l1d = &legacy_l1d_cache;
- l1i = &legacy_l1i_cache;
- l2 = &legacy_l2_cache;
- l3 = &legacy_l3_cache;
- }
switch (count) {
case 0: /* L1 dcache info */
- encode_cache_cpuid4(l1d, 1, cs->nr_cores,
+ encode_cache_cpuid4(env->cache_info_cpuid4.l1d_cache,
+ 1, cs->nr_cores,
eax, ebx, ecx, edx);
break;
case 1: /* L1 icache info */
- encode_cache_cpuid4(l1i, 1, cs->nr_cores,
+ encode_cache_cpuid4(env->cache_info_cpuid4.l1i_cache,
+ 1, cs->nr_cores,
eax, ebx, ecx, edx);
break;
case 2: /* L2 cache info */
- encode_cache_cpuid4(l2, cs->nr_threads, cs->nr_cores,
+ encode_cache_cpuid4(env->cache_info_cpuid4.l2_cache,
+ cs->nr_threads, cs->nr_cores,
eax, ebx, ecx, edx);
break;
case 3: /* L3 cache info */
pkg_offset = apicid_pkg_offset(cs->nr_cores, cs->nr_threads);
if (cpu->enable_l3_cache) {
- encode_cache_cpuid4(l3, (1 << pkg_offset), cs->nr_cores,
+ encode_cache_cpuid4(env->cache_info_cpuid4.l3_cache,
+ (1 << pkg_offset), cs->nr_cores,
eax, ebx, ecx, edx);
break;
}
@@ -3961,13 +4035,8 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count,
(L1_ITLB_2M_ASSOC << 8) | (L1_ITLB_2M_ENTRIES);
*ebx = (L1_DTLB_4K_ASSOC << 24) | (L1_DTLB_4K_ENTRIES << 16) | \
(L1_ITLB_4K_ASSOC << 8) | (L1_ITLB_4K_ENTRIES);
- if (env->cache_info && !cpu->legacy_cache) {
- *ecx = encode_cache_cpuid80000005(&env->cache_info->l1d_cache);
- *edx = encode_cache_cpuid80000005(&env->cache_info->l1i_cache);
- } else {
- *ecx = encode_cache_cpuid80000005(&legacy_l1d_cache_amd);
- *edx = encode_cache_cpuid80000005(&legacy_l1i_cache_amd);
- }
+ *ecx = encode_cache_cpuid80000005(env->cache_info_amd.l1d_cache);
+ *edx = encode_cache_cpuid80000005(env->cache_info_amd.l1i_cache);
break;
case 0x80000006:
/* cache info (L2 cache) */
@@ -3983,17 +4052,10 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count,
(L2_DTLB_4K_ENTRIES << 16) | \
(AMD_ENC_ASSOC(L2_ITLB_4K_ASSOC) << 12) | \
(L2_ITLB_4K_ENTRIES);
- if (env->cache_info && !cpu->legacy_cache) {
- encode_cache_cpuid80000006(&env->cache_info->l2_cache,
- cpu->enable_l3_cache ?
- &env->cache_info->l3_cache : NULL,
- ecx, edx);
- } else {
- encode_cache_cpuid80000006(&legacy_l2_cache_amd,
- cpu->enable_l3_cache ?
- &legacy_l3_cache : NULL,
- ecx, edx);
- }
+ encode_cache_cpuid80000006(env->cache_info_amd.l2_cache,
+ cpu->enable_l3_cache ?
+ env->cache_info_amd.l3_cache : NULL,
+ ecx, edx);
break;
case 0x80000007:
*eax = 0;
@@ -4034,6 +4096,30 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count,
*edx = 0;
}
break;
+ case 0x8000001D:
+ *eax = 0;
+ switch (count) {
+ case 0: /* L1 dcache info */
+ encode_cache_cpuid8000001d(env->cache_info_amd.l1d_cache, cs,
+ eax, ebx, ecx, edx);
+ break;
+ case 1: /* L1 icache info */
+ encode_cache_cpuid8000001d(env->cache_info_amd.l1i_cache, cs,
+ eax, ebx, ecx, edx);
+ break;
+ case 2: /* L2 cache info */
+ encode_cache_cpuid8000001d(env->cache_info_amd.l2_cache, cs,
+ eax, ebx, ecx, edx);
+ break;
+ case 3: /* L3 cache info */
+ encode_cache_cpuid8000001d(env->cache_info_amd.l3_cache, cs,
+ eax, ebx, ecx, edx);
+ break;
+ default: /* end of info */
+ *eax = *ebx = *ecx = *edx = 0;
+ break;
+ }
+ break;
case 0xC0000000:
*eax = env->cpuid_xlevel2;
*ebx = 0;
@@ -4690,6 +4776,37 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp)
cpu->phys_bits = 32;
}
}
+
+ /* Cache information initialization */
+ if (!cpu->legacy_cache) {
+ if (!xcc->cpu_def || !xcc->cpu_def->cache_info) {
+ char *name = x86_cpu_class_get_model_name(xcc);
+ error_setg(errp,
+ "CPU model '%s' doesn't support legacy-cache=off", name);
+ g_free(name);
+ return;
+ }
+ env->cache_info_cpuid2 = env->cache_info_cpuid4 = env->cache_info_amd =
+ *xcc->cpu_def->cache_info;
+ } else {
+ /* Build legacy cache information */
+ env->cache_info_cpuid2.l1d_cache = &legacy_l1d_cache;
+ env->cache_info_cpuid2.l1i_cache = &legacy_l1i_cache;
+ env->cache_info_cpuid2.l2_cache = &legacy_l2_cache_cpuid2;
+ env->cache_info_cpuid2.l3_cache = &legacy_l3_cache;
+
+ env->cache_info_cpuid4.l1d_cache = &legacy_l1d_cache;
+ env->cache_info_cpuid4.l1i_cache = &legacy_l1i_cache;
+ env->cache_info_cpuid4.l2_cache = &legacy_l2_cache;
+ env->cache_info_cpuid4.l3_cache = &legacy_l3_cache;
+
+ env->cache_info_amd.l1d_cache = &legacy_l1d_cache_amd;
+ env->cache_info_amd.l1i_cache = &legacy_l1i_cache_amd;
+ env->cache_info_amd.l2_cache = &legacy_l2_cache_amd;
+ env->cache_info_amd.l3_cache = &legacy_l3_cache;
+ }
+
+
cpu_exec_realizefn(cs, &local_err);
if (local_err != NULL) {
error_propagate(errp, local_err);
@@ -5173,11 +5290,10 @@ static Property x86_cpu_properties[] = {
DEFINE_PROP_BOOL("vmware-cpuid-freq", X86CPU, vmware_cpuid_freq, true),
DEFINE_PROP_BOOL("tcg-cpuid", X86CPU, expose_tcg, true),
/*
- * lecacy_cache defaults to CPU model being chosen. This is set in
- * x86_cpu_load_def based on cache_info which is initialized in
- * builtin_x86_defs
+ * lecacy_cache defaults to true unless the CPU model provides its
+ * own cache information (see x86_cpu_load_def()).
*/
- DEFINE_PROP_BOOL("legacy-cache", X86CPU, legacy_cache, false),
+ DEFINE_PROP_BOOL("legacy-cache", X86CPU, legacy_cache, true),
/*
* From "Requirements for Implementing the Microsoft
diff --git a/target/i386/cpu.h b/target/i386/cpu.h
index 664504610e..89c82be8d2 100644
--- a/target/i386/cpu.h
+++ b/target/i386/cpu.h
@@ -1098,10 +1098,10 @@ typedef struct CPUCacheInfo {
typedef struct CPUCaches {
- CPUCacheInfo l1d_cache;
- CPUCacheInfo l1i_cache;
- CPUCacheInfo l2_cache;
- CPUCacheInfo l3_cache;
+ CPUCacheInfo *l1d_cache;
+ CPUCacheInfo *l1i_cache;
+ CPUCacheInfo *l2_cache;
+ CPUCacheInfo *l3_cache;
} CPUCaches;
typedef struct CPUX86State {
@@ -1293,7 +1293,11 @@ typedef struct CPUX86State {
/* Features that were explicitly enabled/disabled */
FeatureWordArray user_features;
uint32_t cpuid_model[12];
- CPUCaches *cache_info;
+ /* Cache information for CPUID. When legacy-cache=on, the cache data
+ * on each CPUID leaf will be different, because we keep compatibility
+ * with old QEMU versions.
+ */
+ CPUCaches cache_info_cpuid2, cache_info_cpuid4, cache_info_amd;
/* MTRRs */
uint64_t mtrr_fixed[11];
diff --git a/target/i386/kvm.c b/target/i386/kvm.c
index 44f70733e7..445e0e0b11 100644
--- a/target/i386/kvm.c
+++ b/target/i386/kvm.c
@@ -979,9 +979,32 @@ int kvm_arch_init_vcpu(CPUState *cs)
}
c = &cpuid_data.entries[cpuid_i++];
- c->function = i;
- c->flags = 0;
- cpu_x86_cpuid(env, i, 0, &c->eax, &c->ebx, &c->ecx, &c->edx);
+ switch (i) {
+ case 0x8000001d:
+ /* Query for all AMD cache information leaves */
+ for (j = 0; ; j++) {
+ c->function = i;
+ c->flags = KVM_CPUID_FLAG_SIGNIFCANT_INDEX;
+ c->index = j;
+ cpu_x86_cpuid(env, i, j, &c->eax, &c->ebx, &c->ecx, &c->edx);
+
+ if (c->eax == 0) {
+ break;
+ }
+ if (cpuid_i == KVM_MAX_CPUID_ENTRIES) {
+ fprintf(stderr, "cpuid_data is full, no space for "
+ "cpuid(eax:0x%x,ecx:0x%x)\n", i, j);
+ abort();
+ }
+ c = &cpuid_data.entries[cpuid_i++];
+ }
+ break;
+ default:
+ c->function = i;
+ c->flags = 0;
+ cpu_x86_cpuid(env, i, 0, &c->eax, &c->ebx, &c->ecx, &c->edx);
+ break;
+ }
}
/* Call Centaur's CPUID instructions they are supported. */
diff --git a/target/m68k/translate.c b/target/m68k/translate.c
index 4b5dbdb51c..ae3651b867 100644
--- a/target/m68k/translate.c
+++ b/target/m68k/translate.c
@@ -111,14 +111,11 @@ void m68k_tcg_init(void)
/* internal defines */
typedef struct DisasContext {
+ DisasContextBase base;
CPUM68KState *env;
- target_ulong insn_pc; /* Start of the current instruction. */
target_ulong pc;
- int is_jmp;
CCOp cc_op; /* Current CC operation */
int cc_op_synced;
- struct TranslationBlock *tb;
- int singlestep_enabled;
TCGv_i64 mactmp;
int done_mac;
int writeback_mask;
@@ -198,17 +195,15 @@ static void do_writebacks(DisasContext *s)
/* is_jmp field values */
#define DISAS_JUMP DISAS_TARGET_0 /* only pc was modified dynamically */
-#define DISAS_UPDATE DISAS_TARGET_1 /* cpu state was modified dynamically */
-#define DISAS_TB_JUMP DISAS_TARGET_2 /* only pc was modified statically */
-#define DISAS_JUMP_NEXT DISAS_TARGET_3
+#define DISAS_EXIT DISAS_TARGET_1 /* cpu state was modified dynamically */
#if defined(CONFIG_USER_ONLY)
#define IS_USER(s) 1
#else
-#define IS_USER(s) (!(s->tb->flags & TB_FLAGS_MSR_S))
-#define SFC_INDEX(s) ((s->tb->flags & TB_FLAGS_SFC_S) ? \
+#define IS_USER(s) (!(s->base.tb->flags & TB_FLAGS_MSR_S))
+#define SFC_INDEX(s) ((s->base.tb->flags & TB_FLAGS_SFC_S) ? \
MMU_KERNEL_IDX : MMU_USER_IDX)
-#define DFC_INDEX(s) ((s->tb->flags & TB_FLAGS_DFC_S) ? \
+#define DFC_INDEX(s) ((s->base.tb->flags & TB_FLAGS_DFC_S) ? \
MMU_KERNEL_IDX : MMU_USER_IDX)
#endif
@@ -280,7 +275,7 @@ static void gen_jmp_im(DisasContext *s, uint32_t dest)
{
update_cc_op(s);
tcg_gen_movi_i32(QREG_PC, dest);
- s->is_jmp = DISAS_JUMP;
+ s->base.is_jmp = DISAS_JUMP;
}
/* Generate a jump to the address in qreg DEST. */
@@ -288,26 +283,26 @@ static void gen_jmp(DisasContext *s, TCGv dest)
{
update_cc_op(s);
tcg_gen_mov_i32(QREG_PC, dest);
- s->is_jmp = DISAS_JUMP;
+ s->base.is_jmp = DISAS_JUMP;
}
-static void gen_raise_exception(int nr)
+static void gen_exception(DisasContext *s, uint32_t dest, int nr)
{
- TCGv_i32 tmp = tcg_const_i32(nr);
+ TCGv_i32 tmp;
+ update_cc_op(s);
+ tcg_gen_movi_i32(QREG_PC, dest);
+
+ tmp = tcg_const_i32(nr);
gen_helper_raise_exception(cpu_env, tmp);
tcg_temp_free_i32(tmp);
-}
-static void gen_exception(DisasContext *s, uint32_t where, int nr)
-{
- gen_jmp_im(s, where);
- gen_raise_exception(nr);
+ s->base.is_jmp = DISAS_NORETURN;
}
static inline void gen_addr_fault(DisasContext *s)
{
- gen_exception(s, s->insn_pc, EXCP_ADDRESS);
+ gen_exception(s, s->base.pc_next, EXCP_ADDRESS);
}
/* Generate a load from the specified address. Narrow values are
@@ -1005,7 +1000,7 @@ static void gen_load_fp(DisasContext *s, int opsize, TCGv addr, TCGv_ptr fp,
break;
case OS_EXTENDED:
if (m68k_feature(s->env, M68K_FEATURE_CF_FPU)) {
- gen_exception(s, s->insn_pc, EXCP_FP_UNIMP);
+ gen_exception(s, s->base.pc_next, EXCP_FP_UNIMP);
break;
}
tcg_gen_qemu_ld32u(tmp, addr, index);
@@ -1019,7 +1014,7 @@ static void gen_load_fp(DisasContext *s, int opsize, TCGv addr, TCGv_ptr fp,
/* unimplemented data type on 68040/ColdFire
* FIXME if needed for another FPU
*/
- gen_exception(s, s->insn_pc, EXCP_FP_UNIMP);
+ gen_exception(s, s->base.pc_next, EXCP_FP_UNIMP);
break;
default:
g_assert_not_reached();
@@ -1059,7 +1054,7 @@ static void gen_store_fp(DisasContext *s, int opsize, TCGv addr, TCGv_ptr fp,
break;
case OS_EXTENDED:
if (m68k_feature(s->env, M68K_FEATURE_CF_FPU)) {
- gen_exception(s, s->insn_pc, EXCP_FP_UNIMP);
+ gen_exception(s, s->base.pc_next, EXCP_FP_UNIMP);
break;
}
tcg_gen_ld16u_i32(tmp, fp, offsetof(FPReg, l.upper));
@@ -1073,7 +1068,7 @@ static void gen_store_fp(DisasContext *s, int opsize, TCGv addr, TCGv_ptr fp,
/* unimplemented data type on 68040/ColdFire
* FIXME if needed for another FPU
*/
- gen_exception(s, s->insn_pc, EXCP_FP_UNIMP);
+ gen_exception(s, s->base.pc_next, EXCP_FP_UNIMP);
break;
default:
g_assert_not_reached();
@@ -1205,7 +1200,7 @@ static int gen_ea_mode_fp(CPUM68KState *env, DisasContext *s, int mode,
break;
case OS_EXTENDED:
if (m68k_feature(s->env, M68K_FEATURE_CF_FPU)) {
- gen_exception(s, s->insn_pc, EXCP_FP_UNIMP);
+ gen_exception(s, s->base.pc_next, EXCP_FP_UNIMP);
break;
}
tmp = tcg_const_i32(read_im32(env, s) >> 16);
@@ -1219,7 +1214,7 @@ static int gen_ea_mode_fp(CPUM68KState *env, DisasContext *s, int mode,
/* unimplemented data type on 68040/ColdFire
* FIXME if needed for another FPU
*/
- gen_exception(s, s->insn_pc, EXCP_FP_UNIMP);
+ gen_exception(s, s->base.pc_next, EXCP_FP_UNIMP);
break;
default:
g_assert_not_reached();
@@ -1448,11 +1443,11 @@ static void gen_jmpcc(DisasContext *s, int cond, TCGLabel *l1)
}
/* Force a TB lookup after an instruction that changes the CPU state. */
-static void gen_lookup_tb(DisasContext *s)
+static void gen_exit_tb(DisasContext *s)
{
update_cc_op(s);
tcg_gen_movi_i32(QREG_PC, s->pc);
- s->is_jmp = DISAS_UPDATE;
+ s->base.is_jmp = DISAS_EXIT;
}
#define SRC_EA(env, result, opsize, op_sign, addrp) do { \
@@ -1476,8 +1471,8 @@ static void gen_lookup_tb(DisasContext *s)
static inline bool use_goto_tb(DisasContext *s, uint32_t dest)
{
#ifndef CONFIG_USER_ONLY
- return (s->tb->pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK) ||
- (s->insn_pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK);
+ return (s->base.pc_first & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK)
+ || (s->base.pc_next & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK);
#else
return true;
#endif
@@ -1486,17 +1481,17 @@ static inline bool use_goto_tb(DisasContext *s, uint32_t dest)
/* Generate a jump to an immediate address. */
static void gen_jmp_tb(DisasContext *s, int n, uint32_t dest)
{
- if (unlikely(s->singlestep_enabled)) {
+ if (unlikely(s->base.singlestep_enabled)) {
gen_exception(s, dest, EXCP_DEBUG);
} else if (use_goto_tb(s, dest)) {
tcg_gen_goto_tb(n);
tcg_gen_movi_i32(QREG_PC, dest);
- tcg_gen_exit_tb(s->tb, n);
+ tcg_gen_exit_tb(s->base.tb, n);
} else {
gen_jmp_im(s, dest);
tcg_gen_exit_tb(NULL, 0);
}
- s->is_jmp = DISAS_TB_JUMP;
+ s->base.is_jmp = DISAS_NORETURN;
}
DISAS_INSN(scc)
@@ -1543,12 +1538,12 @@ DISAS_INSN(dbcc)
DISAS_INSN(undef_mac)
{
- gen_exception(s, s->insn_pc, EXCP_LINEA);
+ gen_exception(s, s->base.pc_next, EXCP_LINEA);
}
DISAS_INSN(undef_fpu)
{
- gen_exception(s, s->insn_pc, EXCP_LINEF);
+ gen_exception(s, s->base.pc_next, EXCP_LINEF);
}
DISAS_INSN(undef)
@@ -1557,8 +1552,8 @@ DISAS_INSN(undef)
for the 680x0 series, as well as those that are implemented
but actually illegal for CPU32 or pre-68020. */
qemu_log_mask(LOG_UNIMP, "Illegal instruction: %04x @ %08x\n",
- insn, s->insn_pc);
- gen_exception(s, s->insn_pc, EXCP_UNSUPPORTED);
+ insn, s->base.pc_next);
+ gen_exception(s, s->base.pc_next, EXCP_UNSUPPORTED);
}
DISAS_INSN(mulw)
@@ -1618,7 +1613,7 @@ DISAS_INSN(divl)
if (ext & 0x400) {
if (!m68k_feature(s->env, M68K_FEATURE_QUAD_MULDIV)) {
- gen_exception(s, s->insn_pc, EXCP_ILLEGAL);
+ gen_exception(s, s->base.pc_next, EXCP_ILLEGAL);
return;
}
@@ -2312,7 +2307,7 @@ DISAS_INSN(arith_im)
break;
case OS_WORD:
if (IS_USER(s)) {
- gen_exception(s, s->insn_pc, EXCP_PRIVILEGE);
+ gen_exception(s, s->base.pc_next, EXCP_PRIVILEGE);
return;
}
src1 = gen_get_sr(s);
@@ -2481,7 +2476,7 @@ DISAS_INSN(cas2w)
(REG(ext1, 6) << 3) |
(REG(ext2, 0) << 6) |
(REG(ext1, 0) << 9));
- if (tb_cflags(s->tb) & CF_PARALLEL) {
+ if (tb_cflags(s->base.tb) & CF_PARALLEL) {
gen_helper_exit_atomic(cpu_env);
} else {
gen_helper_cas2w(cpu_env, regs, addr1, addr2);
@@ -2531,7 +2526,7 @@ DISAS_INSN(cas2l)
(REG(ext1, 6) << 3) |
(REG(ext2, 0) << 6) |
(REG(ext1, 0) << 9));
- if (tb_cflags(s->tb) & CF_PARALLEL) {
+ if (tb_cflags(s->base.tb) & CF_PARALLEL) {
gen_helper_cas2l_parallel(cpu_env, regs, addr1, addr2);
} else {
gen_helper_cas2l(cpu_env, regs, addr1, addr2);
@@ -2722,7 +2717,7 @@ DISAS_INSN(swap)
DISAS_INSN(bkpt)
{
- gen_exception(s, s->insn_pc, EXCP_DEBUG);
+ gen_exception(s, s->base.pc_next, EXCP_DEBUG);
}
DISAS_INSN(pea)
@@ -2775,7 +2770,7 @@ DISAS_INSN(pulse)
DISAS_INSN(illegal)
{
- gen_exception(s, s->insn_pc, EXCP_ILLEGAL);
+ gen_exception(s, s->base.pc_next, EXCP_ILLEGAL);
}
/* ??? This should be atomic. */
@@ -2805,7 +2800,7 @@ DISAS_INSN(mull)
if (ext & 0x400) {
if (!m68k_feature(s->env, M68K_FEATURE_QUAD_MULDIV)) {
- gen_exception(s, s->insn_pc, EXCP_UNSUPPORTED);
+ gen_exception(s, s->base.pc_next, EXCP_UNSUPPORTED);
return;
}
@@ -2906,7 +2901,7 @@ DISAS_INSN(unlk)
DISAS_INSN(reset)
{
if (IS_USER(s)) {
- gen_exception(s, s->insn_pc, EXCP_PRIVILEGE);
+ gen_exception(s, s->base.pc_next, EXCP_PRIVILEGE);
return;
}
@@ -4377,7 +4372,7 @@ DISAS_INSN(chk)
}
/* fallthru */
default:
- gen_exception(s, s->insn_pc, EXCP_ILLEGAL);
+ gen_exception(s, s->base.pc_next, EXCP_ILLEGAL);
return;
}
SRC_EA(env, src, opsize, 1, NULL);
@@ -4404,13 +4399,13 @@ DISAS_INSN(chk2)
opsize = OS_LONG;
break;
default:
- gen_exception(s, s->insn_pc, EXCP_ILLEGAL);
+ gen_exception(s, s->base.pc_next, EXCP_ILLEGAL);
return;
}
ext = read_im16(env, s);
if ((ext & 0x0800) == 0) {
- gen_exception(s, s->insn_pc, EXCP_ILLEGAL);
+ gen_exception(s, s->base.pc_next, EXCP_ILLEGAL);
return;
}
@@ -4470,7 +4465,7 @@ DISAS_INSN(move16_reg)
ext = read_im16(env, s);
if ((ext & (1 << 15)) == 0) {
- gen_exception(s, s->insn_pc, EXCP_ILLEGAL);
+ gen_exception(s, s->base.pc_next, EXCP_ILLEGAL);
}
m68k_copy_line(AREG(ext, 12), AREG(insn, 0), index);
@@ -4532,7 +4527,7 @@ DISAS_INSN(move_from_sr)
TCGv sr;
if (IS_USER(s) && !m68k_feature(env, M68K_FEATURE_M68000)) {
- gen_exception(s, s->insn_pc, EXCP_PRIVILEGE);
+ gen_exception(s, s->base.pc_next, EXCP_PRIVILEGE);
return;
}
sr = gen_get_sr(s);
@@ -4549,7 +4544,7 @@ DISAS_INSN(moves)
int extend;
if (IS_USER(s)) {
- gen_exception(s, s->insn_pc, EXCP_PRIVILEGE);
+ gen_exception(s, s->base.pc_next, EXCP_PRIVILEGE);
return;
}
@@ -4602,17 +4597,17 @@ DISAS_INSN(moves)
DISAS_INSN(move_to_sr)
{
if (IS_USER(s)) {
- gen_exception(s, s->insn_pc, EXCP_PRIVILEGE);
+ gen_exception(s, s->base.pc_next, EXCP_PRIVILEGE);
return;
}
gen_move_to_sr(env, s, insn, false);
- gen_lookup_tb(s);
+ gen_exit_tb(s);
}
DISAS_INSN(move_from_usp)
{
if (IS_USER(s)) {
- gen_exception(s, s->insn_pc, EXCP_PRIVILEGE);
+ gen_exception(s, s->base.pc_next, EXCP_PRIVILEGE);
return;
}
tcg_gen_ld_i32(AREG(insn, 0), cpu_env,
@@ -4622,7 +4617,7 @@ DISAS_INSN(move_from_usp)
DISAS_INSN(move_to_usp)
{
if (IS_USER(s)) {
- gen_exception(s, s->insn_pc, EXCP_PRIVILEGE);
+ gen_exception(s, s->base.pc_next, EXCP_PRIVILEGE);
return;
}
tcg_gen_st_i32(AREG(insn, 0), cpu_env,
@@ -4632,7 +4627,7 @@ DISAS_INSN(move_to_usp)
DISAS_INSN(halt)
{
if (IS_USER(s)) {
- gen_exception(s, s->insn_pc, EXCP_PRIVILEGE);
+ gen_exception(s, s->base.pc_next, EXCP_PRIVILEGE);
return;
}
@@ -4644,7 +4639,7 @@ DISAS_INSN(stop)
uint16_t ext;
if (IS_USER(s)) {
- gen_exception(s, s->insn_pc, EXCP_PRIVILEGE);
+ gen_exception(s, s->base.pc_next, EXCP_PRIVILEGE);
return;
}
@@ -4658,10 +4653,10 @@ DISAS_INSN(stop)
DISAS_INSN(rte)
{
if (IS_USER(s)) {
- gen_exception(s, s->insn_pc, EXCP_PRIVILEGE);
+ gen_exception(s, s->base.pc_next, EXCP_PRIVILEGE);
return;
}
- gen_exception(s, s->insn_pc, EXCP_RTE);
+ gen_exception(s, s->base.pc_next, EXCP_RTE);
}
DISAS_INSN(cf_movec)
@@ -4670,7 +4665,7 @@ DISAS_INSN(cf_movec)
TCGv reg;
if (IS_USER(s)) {
- gen_exception(s, s->insn_pc, EXCP_PRIVILEGE);
+ gen_exception(s, s->base.pc_next, EXCP_PRIVILEGE);
return;
}
@@ -4682,7 +4677,7 @@ DISAS_INSN(cf_movec)
reg = DREG(ext, 12);
}
gen_helper_cf_movec_to(cpu_env, tcg_const_i32(ext & 0xfff), reg);
- gen_lookup_tb(s);
+ gen_exit_tb(s);
}
DISAS_INSN(m68k_movec)
@@ -4691,7 +4686,7 @@ DISAS_INSN(m68k_movec)
TCGv reg;
if (IS_USER(s)) {
- gen_exception(s, s->insn_pc, EXCP_PRIVILEGE);
+ gen_exception(s, s->base.pc_next, EXCP_PRIVILEGE);
return;
}
@@ -4707,13 +4702,13 @@ DISAS_INSN(m68k_movec)
} else {
gen_helper_m68k_movec_from(reg, cpu_env, tcg_const_i32(ext & 0xfff));
}
- gen_lookup_tb(s);
+ gen_exit_tb(s);
}
DISAS_INSN(intouch)
{
if (IS_USER(s)) {
- gen_exception(s, s->insn_pc, EXCP_PRIVILEGE);
+ gen_exception(s, s->base.pc_next, EXCP_PRIVILEGE);
return;
}
/* ICache fetch. Implement as no-op. */
@@ -4722,7 +4717,7 @@ DISAS_INSN(intouch)
DISAS_INSN(cpushl)
{
if (IS_USER(s)) {
- gen_exception(s, s->insn_pc, EXCP_PRIVILEGE);
+ gen_exception(s, s->base.pc_next, EXCP_PRIVILEGE);
return;
}
/* Cache push/invalidate. Implement as no-op. */
@@ -4731,7 +4726,7 @@ DISAS_INSN(cpushl)
DISAS_INSN(cpush)
{
if (IS_USER(s)) {
- gen_exception(s, s->insn_pc, EXCP_PRIVILEGE);
+ gen_exception(s, s->base.pc_next, EXCP_PRIVILEGE);
return;
}
/* Cache push/invalidate. Implement as no-op. */
@@ -4740,7 +4735,7 @@ DISAS_INSN(cpush)
DISAS_INSN(cinv)
{
if (IS_USER(s)) {
- gen_exception(s, s->insn_pc, EXCP_PRIVILEGE);
+ gen_exception(s, s->base.pc_next, EXCP_PRIVILEGE);
return;
}
/* Invalidate cache line. Implement as no-op. */
@@ -4752,7 +4747,7 @@ DISAS_INSN(pflush)
TCGv opmode;
if (IS_USER(s)) {
- gen_exception(s, s->insn_pc, EXCP_PRIVILEGE);
+ gen_exception(s, s->base.pc_next, EXCP_PRIVILEGE);
return;
}
@@ -4766,7 +4761,7 @@ DISAS_INSN(ptest)
TCGv is_read;
if (IS_USER(s)) {
- gen_exception(s, s->insn_pc, EXCP_PRIVILEGE);
+ gen_exception(s, s->base.pc_next, EXCP_PRIVILEGE);
return;
}
is_read = tcg_const_i32((insn >> 5) & 1);
@@ -4777,7 +4772,7 @@ DISAS_INSN(ptest)
DISAS_INSN(wddata)
{
- gen_exception(s, s->insn_pc, EXCP_PRIVILEGE);
+ gen_exception(s, s->base.pc_next, EXCP_PRIVILEGE);
}
DISAS_INSN(wdebug)
@@ -4785,7 +4780,7 @@ DISAS_INSN(wdebug)
M68kCPU *cpu = m68k_env_get_cpu(env);
if (IS_USER(s)) {
- gen_exception(s, s->insn_pc, EXCP_PRIVILEGE);
+ gen_exception(s, s->base.pc_next, EXCP_PRIVILEGE);
return;
}
/* TODO: Implement wdebug. */
@@ -4795,7 +4790,7 @@ DISAS_INSN(wdebug)
DISAS_INSN(trap)
{
- gen_exception(s, s->insn_pc, EXCP_TRAP0 + (insn & 0xf));
+ gen_exception(s, s->base.pc_next, EXCP_TRAP0 + (insn & 0xf));
}
static void gen_load_fcr(DisasContext *s, TCGv res, int reg)
@@ -4862,7 +4857,7 @@ static void gen_op_fmove_fcr(CPUM68KState *env, DisasContext *s,
switch (mode) {
case 0: /* Dn */
if (mask != M68K_FPIAR && mask != M68K_FPSR && mask != M68K_FPCR) {
- gen_exception(s, s->insn_pc, EXCP_ILLEGAL);
+ gen_exception(s, s->base.pc_next, EXCP_ILLEGAL);
return;
}
if (is_write) {
@@ -4873,7 +4868,7 @@ static void gen_op_fmove_fcr(CPUM68KState *env, DisasContext *s,
return;
case 1: /* An, only with FPIAR */
if (mask != M68K_FPIAR) {
- gen_exception(s, s->insn_pc, EXCP_ILLEGAL);
+ gen_exception(s, s->base.pc_next, EXCP_ILLEGAL);
return;
}
if (is_write) {
@@ -5431,7 +5426,7 @@ DISAS_INSN(frestore)
TCGv addr;
if (IS_USER(s)) {
- gen_exception(s, s->insn_pc, EXCP_PRIVILEGE);
+ gen_exception(s, s->base.pc_next, EXCP_PRIVILEGE);
return;
}
if (m68k_feature(s->env, M68K_FEATURE_M68040)) {
@@ -5445,7 +5440,7 @@ DISAS_INSN(frestore)
DISAS_INSN(fsave)
{
if (IS_USER(s)) {
- gen_exception(s, s->insn_pc, EXCP_PRIVILEGE);
+ gen_exception(s, s->base.pc_next, EXCP_PRIVILEGE);
return;
}
@@ -5751,7 +5746,7 @@ DISAS_INSN(to_macsr)
TCGv val;
SRC_EA(env, val, OS_LONG, 0, NULL);
gen_helper_set_macsr(cpu_env, val);
- gen_lookup_tb(s);
+ gen_exit_tb(s);
}
DISAS_INSN(to_mask)
@@ -6054,121 +6049,130 @@ void register_m68k_insns (CPUM68KState *env)
#undef INSN
}
-/* ??? Some of this implementation is not exception safe. We should always
- write back the result to memory before setting the condition codes. */
-static void disas_m68k_insn(CPUM68KState * env, DisasContext *s)
-{
- uint16_t insn = read_im16(env, s);
- opcode_table[insn](env, s, insn);
- do_writebacks(s);
- do_release(s);
-}
-
-/* generate intermediate code for basic block 'tb'. */
-void gen_intermediate_code(CPUState *cs, TranslationBlock *tb)
+static void m68k_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cpu)
{
- CPUM68KState *env = cs->env_ptr;
- DisasContext dc1, *dc = &dc1;
- target_ulong pc_start;
- int pc_offset;
- int num_insns;
- int max_insns;
-
- /* generate intermediate code */
- pc_start = tb->pc;
-
- dc->tb = tb;
+ DisasContext *dc = container_of(dcbase, DisasContext, base);
+ CPUM68KState *env = cpu->env_ptr;
dc->env = env;
- dc->is_jmp = DISAS_NEXT;
- dc->pc = pc_start;
+ dc->pc = dc->base.pc_first;
dc->cc_op = CC_OP_DYNAMIC;
dc->cc_op_synced = 1;
- dc->singlestep_enabled = cs->singlestep_enabled;
dc->done_mac = 0;
dc->writeback_mask = 0;
- num_insns = 0;
- max_insns = tb_cflags(tb) & CF_COUNT_MASK;
- if (max_insns == 0) {
- max_insns = CF_COUNT_MASK;
- }
- if (max_insns > TCG_MAX_INSNS) {
- max_insns = TCG_MAX_INSNS;
- }
-
init_release_array(dc);
+}
- gen_tb_start(tb);
- do {
- pc_offset = dc->pc - pc_start;
- tcg_gen_insn_start(dc->pc, dc->cc_op);
- num_insns++;
-
- if (unlikely(cpu_breakpoint_test(cs, dc->pc, BP_ANY))) {
- gen_exception(dc, dc->pc, EXCP_DEBUG);
- dc->is_jmp = DISAS_JUMP;
- /* The address covered by the breakpoint must be included in
- [tb->pc, tb->pc + tb->size) in order to for it to be
- properly cleared -- thus we increment the PC here so that
- the logic setting tb->size below does the right thing. */
- dc->pc += 2;
- break;
- }
+static void m68k_tr_tb_start(DisasContextBase *dcbase, CPUState *cpu)
+{
+}
- if (num_insns == max_insns && (tb_cflags(tb) & CF_LAST_IO)) {
- gen_io_start();
- }
+static void m68k_tr_insn_start(DisasContextBase *dcbase, CPUState *cpu)
+{
+ DisasContext *dc = container_of(dcbase, DisasContext, base);
+ tcg_gen_insn_start(dc->base.pc_next, dc->cc_op);
+}
+
+static bool m68k_tr_breakpoint_check(DisasContextBase *dcbase, CPUState *cpu,
+ const CPUBreakpoint *bp)
+{
+ DisasContext *dc = container_of(dcbase, DisasContext, base);
+
+ gen_exception(dc, dc->base.pc_next, EXCP_DEBUG);
+ /* The address covered by the breakpoint must be included in
+ [tb->pc, tb->pc + tb->size) in order to for it to be
+ properly cleared -- thus we increment the PC here so that
+ the logic setting tb->size below does the right thing. */
+ dc->base.pc_next += 2;
+
+ return true;
+}
+
+static void m68k_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu)
+{
+ DisasContext *dc = container_of(dcbase, DisasContext, base);
+ CPUM68KState *env = cpu->env_ptr;
+ uint16_t insn = read_im16(env, dc);
+
+ opcode_table[insn](env, dc, insn);
+ do_writebacks(dc);
+ do_release(dc);
+
+ dc->base.pc_next = dc->pc;
- dc->insn_pc = dc->pc;
- disas_m68k_insn(env, dc);
- } while (!dc->is_jmp && !tcg_op_buf_full() &&
- !cs->singlestep_enabled &&
- !singlestep &&
- (pc_offset) < (TARGET_PAGE_SIZE - 32) &&
- num_insns < max_insns);
-
- if (tb_cflags(tb) & CF_LAST_IO)
- gen_io_end();
- if (unlikely(cs->singlestep_enabled)) {
- /* Make sure the pc is updated, and raise a debug exception. */
- if (!dc->is_jmp) {
- update_cc_op(dc);
- tcg_gen_movi_i32(QREG_PC, dc->pc);
+ if (dc->base.is_jmp == DISAS_NEXT) {
+ /* Stop translation when the next insn might touch a new page.
+ * This ensures that prefetch aborts at the right place.
+ *
+ * We cannot determine the size of the next insn without
+ * completely decoding it. However, the maximum insn size
+ * is 32 bytes, so end if we do not have that much remaining.
+ * This may produce several small TBs at the end of each page,
+ * but they will all be linked with goto_tb.
+ *
+ * ??? ColdFire maximum is 4 bytes; MC68000's maximum is also
+ * smaller than MC68020's.
+ */
+ target_ulong start_page_offset
+ = dc->pc - (dc->base.pc_first & TARGET_PAGE_MASK);
+
+ if (start_page_offset >= TARGET_PAGE_SIZE - 32) {
+ dc->base.is_jmp = DISAS_TOO_MANY;
}
+ }
+}
+
+static void m68k_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu)
+{
+ DisasContext *dc = container_of(dcbase, DisasContext, base);
+
+ if (dc->base.is_jmp == DISAS_NORETURN) {
+ return;
+ }
+ if (dc->base.singlestep_enabled) {
gen_helper_raise_exception(cpu_env, tcg_const_i32(EXCP_DEBUG));
- } else {
- switch(dc->is_jmp) {
- case DISAS_NEXT:
- update_cc_op(dc);
- gen_jmp_tb(dc, 0, dc->pc);
- break;
- default:
- case DISAS_JUMP:
- case DISAS_UPDATE:
- update_cc_op(dc);
- /* indicate that the hash table must be used to find the next TB */
- tcg_gen_exit_tb(NULL, 0);
- break;
- case DISAS_TB_JUMP:
- /* nothing more to generate */
- break;
- }
+ return;
}
- gen_tb_end(tb, num_insns);
-#ifdef DEBUG_DISAS
- if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)
- && qemu_log_in_addr_range(pc_start)) {
- qemu_log_lock();
- qemu_log("----------------\n");
- qemu_log("IN: %s\n", lookup_symbol(pc_start));
- log_target_disas(cs, pc_start, dc->pc - pc_start);
- qemu_log("\n");
- qemu_log_unlock();
+ switch (dc->base.is_jmp) {
+ case DISAS_TOO_MANY:
+ update_cc_op(dc);
+ gen_jmp_tb(dc, 0, dc->pc);
+ break;
+ case DISAS_JUMP:
+ /* We updated CC_OP and PC in gen_jmp/gen_jmp_im. */
+ tcg_gen_lookup_and_goto_ptr();
+ break;
+ case DISAS_EXIT:
+ /* We updated CC_OP and PC in gen_exit_tb, but also modified
+ other state that may require returning to the main loop. */
+ tcg_gen_exit_tb(NULL, 0);
+ break;
+ default:
+ g_assert_not_reached();
}
-#endif
- tb->size = dc->pc - pc_start;
- tb->icount = num_insns;
+}
+
+static void m68k_tr_disas_log(const DisasContextBase *dcbase, CPUState *cpu)
+{
+ qemu_log("IN: %s\n", lookup_symbol(dcbase->pc_first));
+ log_target_disas(cpu, dcbase->pc_first, dcbase->tb->size);
+}
+
+static const TranslatorOps m68k_tr_ops = {
+ .init_disas_context = m68k_tr_init_disas_context,
+ .tb_start = m68k_tr_tb_start,
+ .insn_start = m68k_tr_insn_start,
+ .breakpoint_check = m68k_tr_breakpoint_check,
+ .translate_insn = m68k_tr_translate_insn,
+ .tb_stop = m68k_tr_tb_stop,
+ .disas_log = m68k_tr_disas_log,
+};
+
+void gen_intermediate_code(CPUState *cpu, TranslationBlock *tb)
+{
+ DisasContext dc;
+ translator_loop(&m68k_tr_ops, &dc.base, cpu, tb);
}
static double floatx80_to_double(CPUM68KState *env, uint16_t high, uint64_t low)
diff --git a/tests/Makefile.include b/tests/Makefile.include
index ccedacebd5..bb08e37b9d 100644
--- a/tests/Makefile.include
+++ b/tests/Makefile.include
@@ -178,6 +178,7 @@ check-qtest-generic-y = tests/qmp-test$(EXESUF)
gcov-files-generic-y = monitor.c qapi/qmp-dispatch.c
check-qtest-generic-y += tests/device-introspect-test$(EXESUF)
gcov-files-generic-y = qdev-monitor.c qmp.c
+check-qtest-generic-y += tests/cdrom-test$(EXESUF)
gcov-files-ipack-y += hw/ipack/ipack.c
check-qtest-ipack-y += tests/ipoctal232-test$(EXESUF)
@@ -843,6 +844,7 @@ tests/test-qapi-util$(EXESUF): tests/test-qapi-util.o $(test-util-obj-y)
tests/numa-test$(EXESUF): tests/numa-test.o
tests/vmgenid-test$(EXESUF): tests/vmgenid-test.o tests/boot-sector.o tests/acpi-utils.o
tests/sdhci-test$(EXESUF): tests/sdhci-test.o $(libqos-pc-obj-y)
+tests/cdrom-test$(EXESUF): tests/cdrom-test.o tests/boot-sector.o $(libqos-obj-y)
tests/migration/stress$(EXESUF): tests/migration/stress.o
$(call quiet-command, $(LINKPROG) -static -O3 $(PTHREAD_LIB) -o $@ $< ,"LINK","$(TARGET_DIR)$@")
diff --git a/tests/boot-sector.c b/tests/boot-sector.c
index c373f0e715..7824286b9a 100644
--- a/tests/boot-sector.c
+++ b/tests/boot-sector.c
@@ -68,8 +68,11 @@ static uint8_t x86_boot_sector[512] = {
};
/* For s390x, use a mini "kernel" with the appropriate signature */
-static const uint8_t s390x_psw[] = {
- 0x00, 0x08, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00
+static const uint8_t s390x_psw_and_magic[] = {
+ 0x00, 0x08, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, /* Program status word */
+ 0x02, 0x00, 0x00, 0x18, 0x60, 0x00, 0x00, 0x50, /* Magic: */
+ 0x02, 0x00, 0x00, 0x68, 0x60, 0x00, 0x00, 0x50, /* see linux_s390_magic */
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40 /* in the s390-ccw bios */
};
static const uint8_t s390x_code[] = {
0xa7, 0xf4, 0x00, 0x0a, /* j 0x10010 */
@@ -110,7 +113,7 @@ int boot_sector_init(char *fname)
} else if (g_str_equal(arch, "s390x")) {
len = 0x10000 + sizeof(s390x_code);
boot_code = g_malloc0(len);
- memcpy(boot_code, s390x_psw, sizeof(s390x_psw));
+ memcpy(boot_code, s390x_psw_and_magic, sizeof(s390x_psw_and_magic));
memcpy(&boot_code[0x10000], s390x_code, sizeof(s390x_code));
} else {
g_assert_not_reached();
diff --git a/tests/cdrom-test.c b/tests/cdrom-test.c
new file mode 100644
index 0000000000..7a1fce5dfb
--- /dev/null
+++ b/tests/cdrom-test.c
@@ -0,0 +1,222 @@
+/*
+ * Various tests for emulated CD-ROM drives.
+ *
+ * Copyright (c) 2018 Red Hat Inc.
+ *
+ * Author:
+ * Thomas Huth <thuth@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2
+ * or later. See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "libqtest.h"
+#include "boot-sector.h"
+#include "qapi/qmp/qdict.h"
+
+static char isoimage[] = "cdrom-boot-iso-XXXXXX";
+
+static int exec_genisoimg(const char **args)
+{
+ gchar *out_err = NULL;
+ gint exit_status = -1;
+ bool success;
+
+ success = g_spawn_sync(NULL, (gchar **)args, NULL,
+ G_SPAWN_SEARCH_PATH | G_SPAWN_STDOUT_TO_DEV_NULL,
+ NULL, NULL, NULL, &out_err, &exit_status, NULL);
+ if (!success) {
+ return -ENOENT;
+ }
+ if (out_err) {
+ fputs(out_err, stderr);
+ g_free(out_err);
+ }
+
+ return exit_status;
+}
+
+static int prepare_image(const char *arch, char *isoimage)
+{
+ char srcdir[] = "cdrom-test-dir-XXXXXX";
+ char *codefile = NULL;
+ int ifh, ret = -1;
+ const char *args[] = {
+ "genisoimage", "-quiet", "-l", "-no-emul-boot",
+ "-b", NULL, "-o", isoimage, srcdir, NULL
+ };
+
+ ifh = mkstemp(isoimage);
+ if (ifh < 0) {
+ perror("Error creating temporary iso image file");
+ return -1;
+ }
+ if (!mkdtemp(srcdir)) {
+ perror("Error creating temporary directory");
+ goto cleanup;
+ }
+
+ if (g_str_equal(arch, "i386") || g_str_equal(arch, "x86_64") ||
+ g_str_equal(arch, "s390x")) {
+ codefile = g_strdup_printf("%s/bootcode-XXXXXX", srcdir);
+ ret = boot_sector_init(codefile);
+ if (ret) {
+ goto cleanup;
+ }
+ } else {
+ /* Just create a dummy file */
+ char txt[] = "empty disc";
+ codefile = g_strdup_printf("%s/readme.txt", srcdir);
+ if (!g_file_set_contents(codefile, txt, sizeof(txt) - 1, NULL)) {
+ fprintf(stderr, "Failed to create '%s'\n", codefile);
+ goto cleanup;
+ }
+ }
+
+ args[5] = strchr(codefile, '/') + 1;
+ ret = exec_genisoimg(args);
+ if (ret) {
+ fprintf(stderr, "genisoimage failed: %i\n", ret);
+ }
+
+ unlink(codefile);
+
+cleanup:
+ g_free(codefile);
+ rmdir(srcdir);
+ close(ifh);
+
+ return ret;
+}
+
+/**
+ * Check that at least the -cdrom parameter is basically working, i.e. we can
+ * see the filename of the ISO image in the output of "info block" afterwards
+ */
+static void test_cdrom_param(gconstpointer data)
+{
+ QTestState *qts;
+ char *resp;
+
+ qts = qtest_startf("-M %s -cdrom %s", (const char *)data, isoimage);
+ resp = qtest_hmp(qts, "info block");
+ g_assert(strstr(resp, isoimage) != 0);
+ g_free(resp);
+ qtest_quit(qts);
+}
+
+static void add_cdrom_param_tests(const char **machines)
+{
+ while (*machines) {
+ char *testname = g_strdup_printf("cdrom/param/%s", *machines);
+ qtest_add_data_func(testname, *machines, test_cdrom_param);
+ g_free(testname);
+ machines++;
+ }
+}
+
+static void test_cdboot(gconstpointer data)
+{
+ QTestState *qts;
+
+ qts = qtest_startf("-accel kvm:tcg -no-shutdown %s%s", (const char *)data,
+ isoimage);
+ boot_sector_test(qts);
+ qtest_quit(qts);
+}
+
+static void add_x86_tests(void)
+{
+ qtest_add_data_func("cdrom/boot/default", "-cdrom ", test_cdboot);
+ qtest_add_data_func("cdrom/boot/virtio-scsi",
+ "-device virtio-scsi -device scsi-cd,drive=cdr "
+ "-blockdev file,node-name=cdr,filename=", test_cdboot);
+ qtest_add_data_func("cdrom/boot/isapc", "-M isapc "
+ "-drive if=ide,media=cdrom,file=", test_cdboot);
+ qtest_add_data_func("cdrom/boot/am53c974",
+ "-device am53c974 -device scsi-cd,drive=cd1 "
+ "-drive if=none,id=cd1,format=raw,file=", test_cdboot);
+ qtest_add_data_func("cdrom/boot/dc390",
+ "-device dc390 -device scsi-cd,drive=cd1 "
+ "-blockdev file,node-name=cd1,filename=", test_cdboot);
+ qtest_add_data_func("cdrom/boot/lsi53c895a",
+ "-device lsi53c895a -device scsi-cd,drive=cd1 "
+ "-blockdev file,node-name=cd1,filename=", test_cdboot);
+ qtest_add_data_func("cdrom/boot/megasas", "-M q35 "
+ "-device megasas -device scsi-cd,drive=cd1 "
+ "-blockdev file,node-name=cd1,filename=", test_cdboot);
+ qtest_add_data_func("cdrom/boot/megasas-gen2", "-M q35 "
+ "-device megasas-gen2 -device scsi-cd,drive=cd1 "
+ "-blockdev file,node-name=cd1,filename=", test_cdboot);
+}
+
+static void add_s390x_tests(void)
+{
+ qtest_add_data_func("cdrom/boot/default", "-cdrom ", test_cdboot);
+ qtest_add_data_func("cdrom/boot/virtio-scsi",
+ "-device virtio-scsi -device scsi-cd,drive=cdr "
+ "-blockdev file,node-name=cdr,filename=", test_cdboot);
+}
+
+int main(int argc, char **argv)
+{
+ int ret;
+ const char *arch = qtest_get_arch();
+ const char *genisocheck[] = { "genisoimage", "-version", NULL };
+
+ g_test_init(&argc, &argv, NULL);
+
+ if (exec_genisoimg(genisocheck)) {
+ /* genisoimage not available - so can't run tests */
+ return 0;
+ }
+
+ ret = prepare_image(arch, isoimage);
+ if (ret) {
+ return ret;
+ }
+
+ if (g_str_equal(arch, "i386") || g_str_equal(arch, "x86_64")) {
+ add_x86_tests();
+ } else if (g_str_equal(arch, "s390x")) {
+ add_s390x_tests();
+ } else if (g_str_equal(arch, "ppc64")) {
+ const char *ppcmachines[] = {
+ "pseries", "mac99", "g3beige", "40p", "prep", NULL
+ };
+ add_cdrom_param_tests(ppcmachines);
+ } else if (g_str_equal(arch, "sparc")) {
+ const char *sparcmachines[] = {
+ "LX", "SPARCClassic", "SPARCbook", "SS-10", "SS-20", "SS-4",
+ "SS-5", "SS-600MP", "Voyager", "leon3_generic", NULL
+ };
+ add_cdrom_param_tests(sparcmachines);
+ } else if (g_str_equal(arch, "sparc64")) {
+ const char *sparc64machines[] = {
+ "niagara", "sun4u", "sun4v", NULL
+ };
+ add_cdrom_param_tests(sparc64machines);
+ } else if (!strncmp(arch, "mips64", 6)) {
+ const char *mips64machines[] = {
+ "magnum", "malta", "mips", "pica61", NULL
+ };
+ add_cdrom_param_tests(mips64machines);
+ } else if (g_str_equal(arch, "arm") || g_str_equal(arch, "aarch64")) {
+ const char *armmachines[] = {
+ "realview-eb", "realview-eb-mpcore", "realview-pb-a8",
+ "realview-pbx-a9", "versatileab", "versatilepb", "vexpress-a15",
+ "vexpress-a9", "virt", NULL
+ };
+ add_cdrom_param_tests(armmachines);
+ } else {
+ const char *nonemachine[] = { "none", NULL };
+ add_cdrom_param_tests(nonemachine);
+ }
+
+ ret = g_test_run();
+
+ unlink(isoimage);
+
+ return ret;
+}
diff --git a/tests/libqos/ahci.c b/tests/libqos/ahci.c
index bc201d762b..7264e085d0 100644
--- a/tests/libqos/ahci.c
+++ b/tests/libqos/ahci.c
@@ -90,6 +90,7 @@ struct AHCICommand {
uint32_t interrupts;
uint64_t xbytes;
uint32_t prd_size;
+ uint32_t sector_size;
uint64_t buffer;
AHCICommandProp *props;
/* Data to be transferred to the guest */
@@ -477,10 +478,10 @@ void ahci_port_check_d2h_sanity(AHCIQState *ahci, uint8_t port, uint8_t slot)
g_free(d2h);
}
-void ahci_port_check_pio_sanity(AHCIQState *ahci, uint8_t port,
- uint8_t slot, size_t buffsize)
+void ahci_port_check_pio_sanity(AHCIQState *ahci, AHCICommand *cmd)
{
PIOSetupFIS *pio = g_malloc0(0x20);
+ uint8_t port = cmd->port;
/* We cannot check the Status or E_Status registers, because
* the status may have again changed between the PIO Setup FIS
@@ -488,15 +489,22 @@ void ahci_port_check_pio_sanity(AHCIQState *ahci, uint8_t port,
qtest_memread(ahci->parent->qts, ahci->port[port].fb + 0x20, pio, 0x20);
g_assert_cmphex(pio->fis_type, ==, 0x5f);
- /* BUG: PIO Setup FIS as utilized by QEMU tries to fit the entire
- * transfer size in a uint16_t field. The maximum transfer size can
- * eclipse this; the field is meant to convey the size of data per
- * each Data FIS, not the entire operation as a whole. For now,
- * we will sanity check the broken case where applicable. */
- if (buffsize <= UINT16_MAX) {
- g_assert_cmphex(le16_to_cpu(pio->tx_count), ==, buffsize);
+ /* Data transferred by PIO will either be:
+ * (1) 12 or 16 bytes for an ATAPI command packet (QEMU always uses 12), or
+ * (2) Actual data from the drive.
+ * If we do both, (2) winds up erasing any evidence of (1).
+ */
+ if (cmd->props->atapi && (cmd->xbytes == 0 || cmd->props->dma)) {
+ g_assert(le16_to_cpu(pio->tx_count) == 12 ||
+ le16_to_cpu(pio->tx_count) == 16);
+ } else {
+ /* The AHCI test suite here does not test any PIO command that specifies
+ * a DRQ block larger than one sector (like 0xC4), so this should always
+ * be one sector or less. */
+ size_t pio_len = ((cmd->xbytes % cmd->sector_size) ?
+ (cmd->xbytes % cmd->sector_size) : cmd->sector_size);
+ g_assert_cmphex(le16_to_cpu(pio->tx_count), ==, pio_len);
}
-
g_free(pio);
}
@@ -796,7 +804,7 @@ static void command_header_init(AHCICommand *cmd)
static void command_table_init(AHCICommand *cmd)
{
RegH2DFIS *fis = &(cmd->fis);
- uint16_t sect_count = (cmd->xbytes / AHCI_SECTOR_SIZE);
+ uint16_t sect_count = (cmd->xbytes / cmd->sector_size);
fis->fis_type = REG_H2D_FIS;
fis->flags = REG_H2D_FIS_CMD; /* "Command" bit */
@@ -819,7 +827,7 @@ static void command_table_init(AHCICommand *cmd)
if (cmd->props->lba28 || cmd->props->lba48) {
fis->device = ATA_DEVICE_LBA;
}
- fis->count = (cmd->xbytes / AHCI_SECTOR_SIZE);
+ fis->count = (cmd->xbytes / cmd->sector_size);
}
fis->icc = 0x00;
fis->control = 0x00;
@@ -831,9 +839,9 @@ void ahci_command_enable_atapi_dma(AHCICommand *cmd)
RegH2DFIS *fis = &(cmd->fis);
g_assert(cmd->props->atapi);
fis->feature_low |= 0x01;
- cmd->interrupts &= ~AHCI_PX_IS_PSS;
+ /* PIO is still used to transfer the ATAPI command */
+ g_assert(cmd->props->pio);
cmd->props->dma = true;
- cmd->props->pio = false;
/* BUG: We expect the DMA Setup interrupt for DMA commands */
/* cmd->interrupts |= AHCI_PX_IS_DSS; */
}
@@ -845,7 +853,7 @@ AHCICommand *ahci_command_create(uint8_t command_name)
g_assert(props);
cmd = g_new0(AHCICommand, 1);
- g_assert(!(props->dma && props->pio));
+ g_assert(!(props->dma && props->pio) || props->atapi);
g_assert(!(props->lba28 && props->lba48));
g_assert(!(props->read && props->write));
g_assert(!props->size || props->data);
@@ -857,6 +865,7 @@ AHCICommand *ahci_command_create(uint8_t command_name)
cmd->xbytes = props->size;
cmd->prd_size = 4096;
cmd->buffer = 0xabad1dea;
+ cmd->sector_size = props->atapi ? ATAPI_SECTOR_SIZE : AHCI_SECTOR_SIZE;
if (!cmd->props->ncq) {
cmd->interrupts = AHCI_PX_IS_DHRS;
@@ -1033,7 +1042,7 @@ void ahci_command_set_buffer(AHCICommand *cmd, uint64_t buffer)
static void ahci_atapi_set_size(AHCICommand *cmd, uint64_t xbytes)
{
unsigned char *cbd = cmd->atapi_cmd;
- uint64_t nsectors = xbytes / 2048;
+ uint64_t nsectors = xbytes / ATAPI_SECTOR_SIZE;
uint32_t tmp;
g_assert(cbd);
@@ -1080,7 +1089,7 @@ void ahci_command_set_sizes(AHCICommand *cmd, uint64_t xbytes,
cmd->prd_size = prd_size;
}
cmd->xbytes = xbytes;
- sect_count = (cmd->xbytes / AHCI_SECTOR_SIZE);
+ sect_count = (cmd->xbytes / cmd->sector_size);
if (cmd->props->ncq) {
NCQFIS *nfis = (NCQFIS *)&(cmd->fis);
@@ -1216,7 +1225,7 @@ void ahci_command_verify(AHCIQState *ahci, AHCICommand *cmd)
ahci_port_check_d2h_sanity(ahci, port, slot);
}
if (cmd->props->pio) {
- ahci_port_check_pio_sanity(ahci, port, slot, cmd->xbytes);
+ ahci_port_check_pio_sanity(ahci, cmd);
}
}
diff --git a/tests/libqos/ahci.h b/tests/libqos/ahci.h
index 715ca1e226..13f6d87b75 100644
--- a/tests/libqos/ahci.h
+++ b/tests/libqos/ahci.h
@@ -596,8 +596,7 @@ void ahci_port_check_interrupts(AHCIQState *ahci, uint8_t port,
uint32_t intr_mask);
void ahci_port_check_nonbusy(AHCIQState *ahci, uint8_t port, uint8_t slot);
void ahci_port_check_d2h_sanity(AHCIQState *ahci, uint8_t port, uint8_t slot);
-void ahci_port_check_pio_sanity(AHCIQState *ahci, uint8_t port,
- uint8_t slot, size_t buffsize);
+void ahci_port_check_pio_sanity(AHCIQState *ahci, AHCICommand *cmd);
void ahci_port_check_cmd_sanity(AHCIQState *ahci, AHCICommand *cmd);
/* Misc */
diff --git a/tests/qemu-iotests/024 b/tests/qemu-iotests/024
index e0d77ce2f5..4071ed6093 100755
--- a/tests/qemu-iotests/024
+++ b/tests/qemu-iotests/024
@@ -29,9 +29,14 @@ status=1 # failure is the default!
_cleanup()
{
- _cleanup_test_img
- rm -f "$TEST_DIR/t.$IMGFMT.base_old"
- rm -f "$TEST_DIR/t.$IMGFMT.base_new"
+ _cleanup_test_img
+ rm -f "$TEST_DIR/t.$IMGFMT.base_old"
+ rm -f "$TEST_DIR/t.$IMGFMT.base_new"
+
+ rm -f "$TEST_DIR/subdir/t.$IMGFMT"
+ rm -f "$TEST_DIR/subdir/t.$IMGFMT.base_old"
+ rm -f "$TEST_DIR/subdir/t.$IMGFMT.base_new"
+ rmdir "$TEST_DIR/subdir" 2> /dev/null
}
trap "_cleanup; exit \$status" 0 1 2 3 15
@@ -123,6 +128,77 @@ io_pattern readv $((13 * CLUSTER_SIZE)) $CLUSTER_SIZE 0 1 0x00
io_pattern readv $((14 * CLUSTER_SIZE)) $CLUSTER_SIZE 0 1 0x11
io_pattern readv $((15 * CLUSTER_SIZE)) $CLUSTER_SIZE 0 1 0x00
+echo
+echo "=== Test rebase in a subdirectory of the working directory ==="
+echo
+
+# Clean up the old images beforehand so they do not interfere with
+# this test
+_cleanup
+
+mkdir "$TEST_DIR/subdir"
+
+# Relative to the overlay
+BASE_OLD_OREL="t.$IMGFMT.base_old"
+BASE_NEW_OREL="t.$IMGFMT.base_new"
+
+# Relative to $TEST_DIR (which is going to be our working directory)
+OVERLAY_WREL="subdir/t.$IMGFMT"
+
+BASE_OLD="$TEST_DIR/subdir/$BASE_OLD_OREL"
+BASE_NEW="$TEST_DIR/subdir/$BASE_NEW_OREL"
+OVERLAY="$TEST_DIR/$OVERLAY_WREL"
+
+# Test done here:
+#
+# Backing (old): 11 11 -- 11
+# Backing (new): -- 22 22 11
+# Overlay: -- -- -- --
+#
+# Rebasing works, we have verified that above. Here, we just want to
+# see that rebasing is done for the correct target backing file.
+
+TEST_IMG=$BASE_OLD _make_test_img 1M
+TEST_IMG=$BASE_NEW _make_test_img 1M
+TEST_IMG=$OVERLAY _make_test_img -b "$BASE_OLD_OREL" 1M
+
+echo
+
+$QEMU_IO "$BASE_OLD" \
+ -c "write -P 0x11 $((0 * CLUSTER_SIZE)) $((2 * CLUSTER_SIZE))" \
+ -c "write -P 0x11 $((3 * CLUSTER_SIZE)) $((1 * CLUSTER_SIZE))" \
+ | _filter_qemu_io
+
+$QEMU_IO "$BASE_NEW" \
+ -c "write -P 0x22 $((1 * CLUSTER_SIZE)) $((2 * CLUSTER_SIZE))" \
+ -c "write -P 0x11 $((3 * CLUSTER_SIZE)) $((1 * CLUSTER_SIZE))" \
+ | _filter_qemu_io
+
+echo
+
+pushd "$TEST_DIR" >/dev/null
+$QEMU_IMG rebase -f "$IMGFMT" -b "$BASE_NEW_OREL" "$OVERLAY_WREL"
+popd >/dev/null
+
+# Verify the backing path is correct
+TEST_IMG=$OVERLAY _img_info | grep '^backing file'
+
+echo
+
+# Verify the data is correct
+$QEMU_IO "$OVERLAY" \
+ -c "read -P 0x11 $((0 * CLUSTER_SIZE)) $CLUSTER_SIZE" \
+ -c "read -P 0x11 $((1 * CLUSTER_SIZE)) $CLUSTER_SIZE" \
+ -c "read -P 0x00 $((2 * CLUSTER_SIZE)) $CLUSTER_SIZE" \
+ -c "read -P 0x11 $((3 * CLUSTER_SIZE)) $CLUSTER_SIZE" \
+ | _filter_qemu_io
+
+echo
+
+# Verify that cluster #3 is not allocated (because it is the same in
+# $BASE_OLD and $BASE_NEW)
+$QEMU_IMG map "$OVERLAY" | _filter_qemu_img_map
+
# success, all done
echo "*** done"
diff --git a/tests/qemu-iotests/024.out b/tests/qemu-iotests/024.out
index 33cfaf5cfc..024dc786b3 100644
--- a/tests/qemu-iotests/024.out
+++ b/tests/qemu-iotests/024.out
@@ -141,4 +141,34 @@ read 65536/65536 bytes at offset 917504
=== IO: pattern 0x00
read 65536/65536 bytes at offset 983040
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+=== Test rebase in a subdirectory of the working directory ===
+
+Formatting 'TEST_DIR/subdir/t.IMGFMT.base_old', fmt=IMGFMT size=1048576
+Formatting 'TEST_DIR/subdir/t.IMGFMT.base_new', fmt=IMGFMT size=1048576
+Formatting 'TEST_DIR/subdir/t.IMGFMT', fmt=IMGFMT size=1048576 backing_file=t.IMGFMT.base_old
+
+wrote 131072/131072 bytes at offset 0
+128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 65536/65536 bytes at offset 196608
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 131072/131072 bytes at offset 65536
+128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 65536/65536 bytes at offset 196608
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+backing file: t.IMGFMT.base_new (actual path: TEST_DIR/subdir/t.IMGFMT.base_new)
+
+read 65536/65536 bytes at offset 0
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 65536/65536 bytes at offset 65536
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 65536/65536 bytes at offset 131072
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 65536/65536 bytes at offset 196608
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+Offset Length File
+0 0x30000 TEST_DIR/subdir/t.IMGFMT
+0x30000 0x10000 TEST_DIR/subdir/t.IMGFMT.base_new
*** done
diff --git a/tests/qemu-iotests/029 b/tests/qemu-iotests/029
index 30bab24dc0..5cff6875bf 100755
--- a/tests/qemu-iotests/029
+++ b/tests/qemu-iotests/029
@@ -92,7 +92,7 @@ _make_test_img 64M
{ $QEMU_IMG snapshot -c foo $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir
poke_file "$TEST_IMG" "$offset_size" "\x00\x00\x00\x00\x00\x00\x02\x00"
poke_file "$TEST_IMG" "$offset_l1_size" "\x00\x00\x00\x01"
-{ $QEMU_IMG convert -s foo $TEST_IMG $TEST_IMG.snap; } 2>&1 | _filter_qemu_io | _filter_testdir
+{ $QEMU_IMG convert -l foo $TEST_IMG $TEST_IMG.snap; } 2>&1 | _filter_qemu_io | _filter_testdir
# success, all done
diff --git a/tests/qemu-iotests/060 b/tests/qemu-iotests/060
index 6c7407f499..7bdf609f3f 100755
--- a/tests/qemu-iotests/060
+++ b/tests/qemu-iotests/060
@@ -440,6 +440,36 @@ echo "{'execute': 'qmp_capabilities'}
-drive if=none,node-name=drive,file="$TEST_IMG",driver=qcow2 \
| _filter_qmp | _filter_qemu_io
+echo
+echo "=== Testing incoming inactive corrupted image ==="
+echo
+
+_make_test_img 64M
+# Create an unaligned L1 entry, so qemu will signal a corruption when
+# reading from the covered area
+poke_file "$TEST_IMG" "$l1_offset" "\x00\x00\x00\x00\x2a\x2a\x2a\x2a"
+
+# Inactive images are effectively read-only images, so this should be a
+# non-fatal corruption (which does not modify the image)
+echo "{'execute': 'qmp_capabilities'}
+ {'execute': 'human-monitor-command',
+ 'arguments': {'command-line': 'qemu-io drive \"read 0 512\"'}}
+ {'execute': 'quit'}" \
+ | $QEMU -qmp stdio -nographic -nodefaults \
+ -blockdev "{'node-name': 'drive',
+ 'driver': 'qcow2',
+ 'file': {
+ 'driver': 'file',
+ 'filename': '$TEST_IMG'
+ }}" \
+ -incoming exec:'cat /dev/null' \
+ 2>&1 \
+ | _filter_qmp | _filter_qemu_io
+
+echo
+# Image should not have been marked corrupt
+_img_info --format-specific | grep 'corrupt:'
+
# success, all done
echo "*** done"
rm -f $seq.full
diff --git a/tests/qemu-iotests/060.out b/tests/qemu-iotests/060.out
index 25d5c3938b..bff023d889 100644
--- a/tests/qemu-iotests/060.out
+++ b/tests/qemu-iotests/060.out
@@ -129,7 +129,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
wrote 65536/65536 bytes at offset 0
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
qcow2: Marking image as corrupt: L2 table offset 0x42a00 unaligned (L1 index: 0); further corruption events will be suppressed
-qemu-img: Error while amending options: Input/output error
+qemu-img: Failed to turn zero into data clusters: Input/output error
=== Testing unaligned L2 entry ===
@@ -145,7 +145,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
wrote 65536/65536 bytes at offset 0
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
qcow2: Marking image as corrupt: Cluster allocation offset 0x52a00 unaligned (L2 offset: 0x40000, L2 index: 0); further corruption events will be suppressed
-qemu-img: Error while amending options: Input/output error
+qemu-img: Failed to turn zero into data clusters: Input/output error
=== Testing unaligned reftable entry ===
@@ -420,4 +420,18 @@ write failed: Input/output error
{"return": ""}
{"return": {}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
+
+=== Testing incoming inactive corrupted image ===
+
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
+QMP_VERSION
+{"return": {}}
+qcow2: Image is corrupt: L2 table offset 0x2a2a2a00 unaligned (L1 index: 0); further non-fatal corruption events will be suppressed
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_IMAGE_CORRUPTED", "data": {"device": "", "msg": "L2 table offset 0x2a2a2a00 unaligned (L1 index: 0)", "node-name": "drive", "fatal": false}}
+read failed: Input/output error
+{"return": ""}
+{"return": {}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
+
+ corrupt: false
*** done
diff --git a/tests/qemu-iotests/061.out b/tests/qemu-iotests/061.out
index e857ef9a7d..183f7dd690 100644
--- a/tests/qemu-iotests/061.out
+++ b/tests/qemu-iotests/061.out
@@ -358,18 +358,12 @@ No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
qemu-img: Lazy refcounts only supported with compatibility level 1.1 and above (use compat=1.1 or greater)
-qemu-img: Error while amending options: Invalid argument
qemu-img: Lazy refcounts only supported with compatibility level 1.1 and above (use compat=1.1 or greater)
-qemu-img: Error while amending options: Invalid argument
qemu-img: Unknown compatibility level 0.42
-qemu-img: Error while amending options: Invalid argument
qemu-img: Invalid parameter 'foo'
qemu-img: Changing the cluster size is not supported
-qemu-img: Error while amending options: Operation not supported
qemu-img: Changing the encryption flag is not supported
-qemu-img: Error while amending options: Operation not supported
qemu-img: Cannot change preallocation mode
-qemu-img: Error while amending options: Operation not supported
=== Testing correct handling of unset value ===
@@ -377,7 +371,6 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
Should work:
Should not work:
qemu-img: Changing the cluster size is not supported
-qemu-img: Error while amending options: Operation not supported
=== Testing zero expansion on inactive clusters ===
diff --git a/tests/qemu-iotests/080 b/tests/qemu-iotests/080
index 4dbe68e950..f0eb42f390 100755
--- a/tests/qemu-iotests/080
+++ b/tests/qemu-iotests/080
@@ -176,7 +176,7 @@ _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 convert -l 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
@@ -190,7 +190,7 @@ _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 convert -l 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
diff --git a/tests/qemu-iotests/080.out b/tests/qemu-iotests/080.out
index 4e0f7f7b92..281c7e0d1d 100644
--- a/tests/qemu-iotests/080.out
+++ b/tests/qemu-iotests/080.out
@@ -65,7 +65,7 @@ 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
+qemu-img: Failed to turn zero into data clusters: Invalid argument
Failed to flush the refcount block cache: Invalid argument
write failed: Invalid argument
qemu-img: Snapshot L1 table offset invalid
@@ -88,7 +88,7 @@ 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
+qemu-img: Failed to turn zero into data clusters: 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
diff --git a/tests/qemu-iotests/082 b/tests/qemu-iotests/082
index d5c83d45ed..a872f771a6 100755
--- a/tests/qemu-iotests/082
+++ b/tests/qemu-iotests/082
@@ -97,6 +97,9 @@ run_qemu_img create -f $IMGFMT -o backing_file="$TEST_IMG" -o ,, -o help "$TEST_
run_qemu_img create -f $IMGFMT -o help
run_qemu_img create -o help
+# Try help option for a format that does not support creation
+run_qemu_img create -f bochs -o help
+
echo
echo === convert: Options specified more than once ===
@@ -151,6 +154,9 @@ run_qemu_img convert -O $IMGFMT -o backing_file="$TEST_IMG" -o ,, -o help "$TEST
run_qemu_img convert -O $IMGFMT -o help
run_qemu_img convert -o help
+# Try help option for a format that does not support creation
+run_qemu_img convert -O bochs -o help
+
echo
echo === amend: Options specified more than once ===
@@ -201,6 +207,9 @@ run_qemu_img amend -f $IMGFMT -o backing_file="$TEST_IMG" -o ,, -o help "$TEST_I
run_qemu_img amend -f $IMGFMT -o help
run_qemu_img convert -o help
+# Try help option for a format that does not support amendment
+run_qemu_img amend -f bochs -o help
+
# success, all done
echo "*** done"
rm -f $seq.full
diff --git a/tests/qemu-iotests/082.out b/tests/qemu-iotests/082.out
index 1527fbe1b7..60ef87c276 100644
--- a/tests/qemu-iotests/082.out
+++ b/tests/qemu-iotests/082.out
@@ -249,6 +249,9 @@ Testing: create -o help
Supported options:
size Virtual disk size
+Testing: create -f bochs -o help
+qemu-img: Format driver 'bochs' does not support image creation
+
=== convert: Options specified more than once ===
Testing: create -f qcow2 TEST_DIR/t.qcow2 128M
@@ -502,6 +505,9 @@ Testing: convert -o help
Supported options:
size Virtual disk size
+Testing: convert -O bochs -o help
+qemu-img: Format driver 'bochs' does not support image creation
+
=== amend: Options specified more than once ===
Testing: amend -f foo -f qcow2 -o lazy_refcounts=on TEST_DIR/t.qcow2
@@ -546,7 +552,7 @@ cluster_size: 65536
=== amend: help for -o ===
Testing: amend -f qcow2 -o help TEST_DIR/t.qcow2
-Supported options:
+Creation options for 'qcow2':
size Virtual disk size
compat Compatibility level (0.10 or 1.1)
backing_file File name of a base image
@@ -564,10 +570,11 @@ cluster_size qcow2 cluster size
preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
lazy_refcounts Postpone refcount updates
refcount_bits Width of a reference count entry in bits
-nocow Turn off copy-on-write (valid only on btrfs)
+
+Note that not all of these options may be amendable.
Testing: amend -f qcow2 -o ? TEST_DIR/t.qcow2
-Supported options:
+Creation options for 'qcow2':
size Virtual disk size
compat Compatibility level (0.10 or 1.1)
backing_file File name of a base image
@@ -585,10 +592,11 @@ cluster_size qcow2 cluster size
preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
lazy_refcounts Postpone refcount updates
refcount_bits Width of a reference count entry in bits
-nocow Turn off copy-on-write (valid only on btrfs)
+
+Note that not all of these options may be amendable.
Testing: amend -f qcow2 -o cluster_size=4k,help TEST_DIR/t.qcow2
-Supported options:
+Creation options for 'qcow2':
size Virtual disk size
compat Compatibility level (0.10 or 1.1)
backing_file File name of a base image
@@ -606,10 +614,11 @@ cluster_size qcow2 cluster size
preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
lazy_refcounts Postpone refcount updates
refcount_bits Width of a reference count entry in bits
-nocow Turn off copy-on-write (valid only on btrfs)
+
+Note that not all of these options may be amendable.
Testing: amend -f qcow2 -o cluster_size=4k,? TEST_DIR/t.qcow2
-Supported options:
+Creation options for 'qcow2':
size Virtual disk size
compat Compatibility level (0.10 or 1.1)
backing_file File name of a base image
@@ -627,10 +636,11 @@ cluster_size qcow2 cluster size
preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
lazy_refcounts Postpone refcount updates
refcount_bits Width of a reference count entry in bits
-nocow Turn off copy-on-write (valid only on btrfs)
+
+Note that not all of these options may be amendable.
Testing: amend -f qcow2 -o help,cluster_size=4k TEST_DIR/t.qcow2
-Supported options:
+Creation options for 'qcow2':
size Virtual disk size
compat Compatibility level (0.10 or 1.1)
backing_file File name of a base image
@@ -648,10 +658,11 @@ cluster_size qcow2 cluster size
preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
lazy_refcounts Postpone refcount updates
refcount_bits Width of a reference count entry in bits
-nocow Turn off copy-on-write (valid only on btrfs)
+
+Note that not all of these options may be amendable.
Testing: amend -f qcow2 -o ?,cluster_size=4k TEST_DIR/t.qcow2
-Supported options:
+Creation options for 'qcow2':
size Virtual disk size
compat Compatibility level (0.10 or 1.1)
backing_file File name of a base image
@@ -669,10 +680,11 @@ cluster_size qcow2 cluster size
preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
lazy_refcounts Postpone refcount updates
refcount_bits Width of a reference count entry in bits
-nocow Turn off copy-on-write (valid only on btrfs)
+
+Note that not all of these options may be amendable.
Testing: amend -f qcow2 -o cluster_size=4k -o help TEST_DIR/t.qcow2
-Supported options:
+Creation options for 'qcow2':
size Virtual disk size
compat Compatibility level (0.10 or 1.1)
backing_file File name of a base image
@@ -690,10 +702,11 @@ cluster_size qcow2 cluster size
preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
lazy_refcounts Postpone refcount updates
refcount_bits Width of a reference count entry in bits
-nocow Turn off copy-on-write (valid only on btrfs)
+
+Note that not all of these options may be amendable.
Testing: amend -f qcow2 -o cluster_size=4k -o ? TEST_DIR/t.qcow2
-Supported options:
+Creation options for 'qcow2':
size Virtual disk size
compat Compatibility level (0.10 or 1.1)
backing_file File name of a base image
@@ -711,7 +724,8 @@ cluster_size qcow2 cluster size
preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
lazy_refcounts Postpone refcount updates
refcount_bits Width of a reference count entry in bits
-nocow Turn off copy-on-write (valid only on btrfs)
+
+Note that not all of these options may be amendable.
Testing: amend -f qcow2 -o backing_file=TEST_DIR/t.qcow2,,help TEST_DIR/t.qcow2
@@ -731,7 +745,7 @@ Testing: amend -f qcow2 -o backing_file=TEST_DIR/t.qcow2 -o ,, -o help TEST_DIR/
qemu-img: Invalid option list: ,,
Testing: amend -f qcow2 -o help
-Supported options:
+Creation options for 'qcow2':
size Virtual disk size
compat Compatibility level (0.10 or 1.1)
backing_file File name of a base image
@@ -750,7 +764,12 @@ preallocation Preallocation mode (allowed values: off, metadata, falloc, full
lazy_refcounts Postpone refcount updates
refcount_bits Width of a reference count entry in bits
+Note that not all of these options may be amendable.
+
Testing: convert -o help
Supported options:
size Virtual disk size
+
+Testing: amend -f bochs -o help
+qemu-img: Format driver 'bochs' does not support option amendment
*** done
diff --git a/tests/qemu-iotests/112.out b/tests/qemu-iotests/112.out
index 86f041075d..ae0318cabe 100644
--- a/tests/qemu-iotests/112.out
+++ b/tests/qemu-iotests/112.out
@@ -99,13 +99,11 @@ refcount bits: 64
=== Amend to compat=0.10 ===
qemu-img: compat=0.10 requires refcount_bits=16
-qemu-img: Error while amending options: Operation not supported
refcount bits: 64
No errors were found on the image.
refcount bits: 16
refcount bits: 16
-qemu-img: Different refcount widths than 16 bits require compatibility level 1.1 or above (use compat=1.1 or greater)
-qemu-img: Error while amending options: Invalid argument
+qemu-img: Refcount widths other than 16 bits require compatibility level 1.1 or above (use compat=1.1 or greater)
refcount bits: 16
=== Amend with snapshot ===
@@ -113,7 +111,6 @@ refcount bits: 16
wrote 16777216/16777216 bytes at offset 0
16 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
qemu-img: Cannot decrease refcount entry width to 1 bits: Cluster at offset 0x50000 has a refcount of 2
-qemu-img: Error while amending options: Invalid argument
No errors were found on the image.
refcount bits: 16
No errors were found on the image.
diff --git a/tests/qemu-iotests/113 b/tests/qemu-iotests/113
index 19b68b2727..4e09810905 100755
--- a/tests/qemu-iotests/113
+++ b/tests/qemu-iotests/113
@@ -38,16 +38,17 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
. ./common.rc
. ./common.filter
-# We can only test one format here because we need its sample file
-_supported_fmt bochs
-_supported_proto nbd
+# Some of these test cases use bochs, but others do use raw, so this
+# is only half a lie.
+_supported_fmt raw
+_supported_proto file
_supported_os Linux
echo
echo '=== Unsupported image creation in qemu-img create ==='
echo
-$QEMU_IMG create -f $IMGFMT nbd://example.com 2>&1 64M | _filter_imgfmt
+$QEMU_IMG create -f bochs nbd://example.com 2>&1 64M
echo
echo '=== Unsupported image creation in qemu-img convert ==='
@@ -56,17 +57,15 @@ echo
# We could use any input image format here, but this is a bochs test, so just
# use the bochs image
_use_sample_img empty.bochs.bz2
-$QEMU_IMG convert -f $IMGFMT -O $IMGFMT "$TEST_IMG" nbd://example.com 2>&1 \
- | _filter_imgfmt
+$QEMU_IMG convert -f bochs -O bochs "$TEST_IMG" nbd://example.com
echo
echo '=== Unsupported format in qemu-img amend ==='
echo
-# The protocol does not matter here
-_use_sample_img empty.bochs.bz2
-$QEMU_IMG amend -f $IMGFMT -o foo=bar "$TEST_IMG" 2>&1 | _filter_imgfmt
-
+TEST_IMG="$TEST_DIR/t.$IMGFMT"
+_make_test_img 1M
+$QEMU_IMG amend -f $IMGFMT -o size=2M "$TEST_IMG" 2>&1 | _filter_imgfmt
# success, all done
echo
diff --git a/tests/qemu-iotests/113.out b/tests/qemu-iotests/113.out
index 00bdfd6887..3557e2bbf0 100644
--- a/tests/qemu-iotests/113.out
+++ b/tests/qemu-iotests/113.out
@@ -2,14 +2,15 @@ QA output created by 113
=== Unsupported image creation in qemu-img create ===
-qemu-img: nbd://example.com: Format driver 'IMGFMT' does not support image creation
+qemu-img: nbd://example.com: Format driver 'bochs' does not support image creation
=== Unsupported image creation in qemu-img convert ===
-qemu-img: Format driver 'IMGFMT' does not support image creation
+qemu-img: Format driver 'bochs' does not support image creation
=== Unsupported format in qemu-img amend ===
-qemu-img: Format driver 'IMGFMT' does not support any options to amend
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576
+qemu-img: Format driver 'IMGFMT' does not support option amendment
*** done
diff --git a/tests/qemu-iotests/122 b/tests/qemu-iotests/122
index 45b359c2ba..d8c8ad722d 100755
--- a/tests/qemu-iotests/122
+++ b/tests/qemu-iotests/122
@@ -77,6 +77,48 @@ $QEMU_IO -c "read -P 0 0 3M" "$TEST_IMG".orig 2>&1 | _filter_qemu_io | _filter_t
echo
+echo "=== Converting to an overlay larger than its backing file ==="
+echo
+
+TEST_IMG="$TEST_IMG".base _make_test_img 256M
+# Needs to be at least how much an L2 table covers
+# (64 kB/entry * 64 kB / 8 B/entry = 512 MB)
+# That way, qcow2 will yield at least two status request responses.
+# With just a single response, it would always say "Allocated in the
+# backing file", so the optimization qemu-img convert tries to do is
+# done automatically. Once it has to be queried twice, however (and
+# one of the queries is completely after the end of the backing file),
+# the block layer will automatically add a ZERO flag that qemu-img
+# convert used to follow up with a zero write to the target.
+# We do not want such a zero write, however, because we are past the
+# end of the backing file on the target as well, so we do not need to
+# write anything there.
+_make_test_img -b "$TEST_IMG".base 768M
+
+# Use compat=0.10 as the output so there is no zero cluster support
+$QEMU_IMG convert -O $IMGFMT -B "$TEST_IMG".base -o compat=0.10 \
+ "$TEST_IMG" "$TEST_IMG".orig
+# See that nothing has been allocated past 64M
+$QEMU_IMG map "$TEST_IMG".orig | _filter_qemu_img_map
+
+echo
+
+# Just before the end of the backing file
+$QEMU_IO -c 'write -P 0x11 255M 1M' "$TEST_IMG".base 2>&1 | _filter_qemu_io
+# Somewhere in the second L2 table
+$QEMU_IO -c 'write -P 0x22 600M 1M' "$TEST_IMG" 2>&1 | _filter_qemu_io
+
+$QEMU_IMG convert -O $IMGFMT -B "$TEST_IMG".base -o compat=0.10 \
+ "$TEST_IMG" "$TEST_IMG".orig
+
+$QEMU_IMG map "$TEST_IMG".orig | _filter_qemu_img_map
+$QEMU_IO -c 'read -P 0x11 255M 1M' \
+ -c 'read -P 0x22 600M 1M' \
+ "$TEST_IMG".orig \
+ | _filter_qemu_io
+
+
+echo
echo "=== Concatenate multiple source images ==="
echo
diff --git a/tests/qemu-iotests/122.out b/tests/qemu-iotests/122.out
index 47d8656db8..6c7ee1da6c 100644
--- a/tests/qemu-iotests/122.out
+++ b/tests/qemu-iotests/122.out
@@ -28,6 +28,24 @@ read 3145728/3145728 bytes at offset 0
read 3145728/3145728 bytes at offset 0
3 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+=== Converting to an overlay larger than its backing file ===
+
+Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=268435456
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=805306368 backing_file=TEST_DIR/t.IMGFMT.base
+Offset Length File
+
+wrote 1048576/1048576 bytes at offset 267386880
+1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 1048576/1048576 bytes at offset 629145600
+1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Offset Length File
+0xff00000 0x100000 TEST_DIR/t.IMGFMT.base
+0x25800000 0x100000 TEST_DIR/t.IMGFMT.orig
+read 1048576/1048576 bytes at offset 267386880
+1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 1048576/1048576 bytes at offset 629145600
+1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
=== Concatenate multiple source images ===
Formatting 'TEST_DIR/t.IMGFMT.1', fmt=IMGFMT size=4194304
diff --git a/tests/qemu-iotests/153 b/tests/qemu-iotests/153
index ec508c758f..673813cd39 100755
--- a/tests/qemu-iotests/153
+++ b/tests/qemu-iotests/153
@@ -137,6 +137,24 @@ for opts1 in "" "read-only=on" "read-only=on,force-share=on"; do
_run_cmd $QEMU_IMG dd $L if="${TEST_IMG}" of="${TEST_IMG}.convert" bs=512 count=1
_run_cmd $QEMU_IMG bench $L -c 1 "${TEST_IMG}"
_run_cmd $QEMU_IMG bench $L -w -c 1 "${TEST_IMG}"
+
+ # qemu-img create does not support -U
+ if [ -z "$L" ]; then
+ _run_cmd $QEMU_IMG create -f $IMGFMT "${TEST_IMG}" \
+ -b ${TEST_IMG}.base
+ # Read the file format. It used to be the case that
+ # file-posix simply truncated the file, but the qcow2
+ # driver then failed to format it because it was unable
+ # to acquire the necessary WRITE permission. However, the
+ # truncation was already wrong, and the whole process
+ # resulted in the file being completely empty and thus its
+ # format would be detected to be raw.
+ # So we read it here to see that creation either completed
+ # successfully (thus the format is qcow2) or it aborted
+ # before the file was changed at all (thus the format stays
+ # qcow2).
+ _img_info -U | grep 'file format'
+ fi
done
_send_qemu_cmd $h "{ 'execute': 'quit', }" ""
echo
diff --git a/tests/qemu-iotests/153.out b/tests/qemu-iotests/153.out
index 2510762ba1..3492ba7a29 100644
--- a/tests/qemu-iotests/153.out
+++ b/tests/qemu-iotests/153.out
@@ -92,6 +92,11 @@ _qemu_img_wrapper bench -w -c 1 TEST_DIR/t.qcow2
qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get "write" lock
Is another process using the image?
+_qemu_img_wrapper create -f qcow2 TEST_DIR/t.qcow2 -b TEST_DIR/t.qcow2.base
+qemu-img: TEST_DIR/t.qcow2: Failed to get "write" lock
+Is another process using the image?
+file format: IMGFMT
+
== Running utility commands -U ==
_qemu_io_wrapper -U -c read 0 512 TEST_DIR/t.qcow2
@@ -209,6 +214,11 @@ _qemu_img_wrapper bench -w -c 1 TEST_DIR/t.qcow2
qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get "write" lock
Is another process using the image?
+_qemu_img_wrapper create -f qcow2 TEST_DIR/t.qcow2 -b TEST_DIR/t.qcow2.base
+qemu-img: TEST_DIR/t.qcow2: Failed to get "write" lock
+Is another process using the image?
+file format: IMGFMT
+
== Running utility commands -U ==
_qemu_io_wrapper -U -c read 0 512 TEST_DIR/t.qcow2
@@ -309,6 +319,9 @@ _qemu_img_wrapper bench -c 1 TEST_DIR/t.qcow2
_qemu_img_wrapper bench -w -c 1 TEST_DIR/t.qcow2
+_qemu_img_wrapper create -f qcow2 TEST_DIR/t.qcow2 -b TEST_DIR/t.qcow2.base
+file format: IMGFMT
+
== Running utility commands -U ==
_qemu_io_wrapper -U -c read 0 512 TEST_DIR/t.qcow2
diff --git a/tests/qemu-iotests/216 b/tests/qemu-iotests/216
index ca9b47a7fd..3c0ae54b44 100755
--- a/tests/qemu-iotests/216
+++ b/tests/qemu-iotests/216
@@ -20,7 +20,7 @@
# Creator/Owner: Max Reitz <mreitz@redhat.com>
import iotests
-from iotests import log, qemu_img_pipe, qemu_io, filter_qemu_io
+from iotests import log, qemu_img, qemu_io_silent
# Need backing file support
iotests.verify_image_format(supported_fmts=['qcow2', 'qcow', 'qed', 'vmdk'])
@@ -50,14 +50,13 @@ with iotests.FilePath('base.img') as base_img_path, \
log('--- Setting up images ---')
log('')
- qemu_img_pipe('create', '-f', iotests.imgfmt, base_img_path, '64M')
+ assert qemu_img('create', '-f', iotests.imgfmt, base_img_path, '64M') == 0
+ assert qemu_io_silent(base_img_path, '-c', 'write -P 1 0M 1M') == 0
+ assert qemu_img('create', '-f', iotests.imgfmt, '-b', base_img_path,
+ top_img_path) == 0
+ assert qemu_io_silent(top_img_path, '-c', 'write -P 2 1M 1M') == 0
- log(filter_qemu_io(qemu_io(base_img_path, '-c', 'write -P 1 0M 1M')))
-
- qemu_img_pipe('create', '-f', iotests.imgfmt, '-b', base_img_path,
- top_img_path)
-
- log(filter_qemu_io(qemu_io(top_img_path, '-c', 'write -P 2 1M 1M')))
+ log('Done')
log('')
log('--- Doing COR ---')
@@ -110,6 +109,8 @@ with iotests.FilePath('base.img') as base_img_path, \
log('--- Checking COR result ---')
log('')
- log(filter_qemu_io(qemu_io(base_img_path, '-c', 'discard 0 64M')))
- log(filter_qemu_io(qemu_io(top_img_path, '-c', 'read -P 1 0M 1M')))
- log(filter_qemu_io(qemu_io(top_img_path, '-c', 'read -P 2 1M 1M')))
+ assert qemu_io_silent(base_img_path, '-c', 'discard 0 64M') == 0
+ assert qemu_io_silent(top_img_path, '-c', 'read -P 1 0M 1M') == 0
+ assert qemu_io_silent(top_img_path, '-c', 'read -P 2 1M 1M') == 0
+
+ log('Done')
diff --git a/tests/qemu-iotests/216.out b/tests/qemu-iotests/216.out
index d3fc590d29..45ea857ee1 100644
--- a/tests/qemu-iotests/216.out
+++ b/tests/qemu-iotests/216.out
@@ -3,12 +3,7 @@
--- Setting up images ---
-wrote 1048576/1048576 bytes at offset 0
-1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
-
-wrote 1048576/1048576 bytes at offset 1048576
-1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
-
+Done
--- Doing COR ---
@@ -17,12 +12,4 @@ wrote 1048576/1048576 bytes at offset 1048576
--- Checking COR result ---
-discard 67108864/67108864 bytes at offset 0
-64 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
-
-read 1048576/1048576 bytes at offset 0
-1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
-
-read 1048576/1048576 bytes at offset 1048576
-1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
-
+Done
diff --git a/tests/qemu-iotests/217 b/tests/qemu-iotests/217
new file mode 100755
index 0000000000..d3ab5d72be
--- /dev/null
+++ b/tests/qemu-iotests/217
@@ -0,0 +1,90 @@
+#!/bin/bash
+#
+# I/O errors when working with internal qcow2 snapshots, and repairing
+# the result
+#
+# 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/>.
+
+seq=$(basename $0)
+echo "QA output created by $seq"
+
+status=1 # failure is the default!
+
+_cleanup()
+{
+ _cleanup_test_img
+ rm -f "$TEST_DIR/blkdebug.conf"
+}
+trap "_cleanup; exit \$status" 0 1 2 3 15
+
+# get standard environment, filters and checks
+. ./common.rc
+. ./common.filter
+
+# This test is specific to qcow2
+_supported_fmt qcow2
+_supported_proto file
+_supported_os Linux
+
+# This test needs clusters with at least a refcount of 2 so that
+# OFLAG_COPIED is not set. refcount_bits=1 is therefore unsupported.
+_unsupported_imgopts 'refcount_bits=1[^0-9]'
+
+echo
+echo '=== Simulating an I/O error during snapshot deletion ==='
+echo
+
+_make_test_img 64M
+$QEMU_IO -c 'write 0 64k' "$TEST_IMG" | _filter_qemu_io
+
+# Create the snapshot
+$QEMU_IMG snapshot -c foo "$TEST_IMG"
+
+# Verify the snapshot is there
+echo
+_img_info | grep 'Snapshot list'
+echo '(Snapshot filtered)'
+echo
+
+# Try to delete the snapshot (with an error happening when freeing the
+# then leaked clusters)
+cat > "$TEST_DIR/blkdebug.conf" <<EOF
+[inject-error]
+event = "cluster_free"
+errno = "5"
+EOF
+$QEMU_IMG snapshot -d foo "blkdebug:$TEST_DIR/blkdebug.conf:$TEST_IMG"
+
+# Verify the snapshot is gone
+echo
+_img_info | grep 'Snapshot list'
+
+# Should only show leaks
+echo '--- Checking test image ---'
+_check_test_img
+
+echo
+
+# As there are only leaks, this should be able to fully repair the
+# image
+echo '--- Repairing test image ---'
+_check_test_img -r leaks
+
+
+# success, all done
+echo '*** done'
+rm -f $seq.full
+status=0
diff --git a/tests/qemu-iotests/217.out b/tests/qemu-iotests/217.out
new file mode 100644
index 0000000000..e3fc40a8c7
--- /dev/null
+++ b/tests/qemu-iotests/217.out
@@ -0,0 +1,42 @@
+QA output created by 217
+
+=== Simulating an I/O error during snapshot deletion ===
+
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
+wrote 65536/65536 bytes at offset 0
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+Snapshot list:
+(Snapshot filtered)
+
+qcow2_free_clusters failed: Input/output error
+qemu-img: Could not delete snapshot 'foo': Failed to free the cluster and L1 table: Input/output error
+
+--- Checking test image ---
+Leaked cluster 4 refcount=2 reference=1
+Leaked cluster 5 refcount=2 reference=1
+Leaked cluster 6 refcount=1 reference=0
+Leaked cluster 7 refcount=1 reference=0
+
+4 leaked clusters were found on the image.
+This means waste of disk space, but no harm to data.
+
+--- Repairing test image ---
+Leaked cluster 4 refcount=2 reference=1
+Leaked cluster 5 refcount=2 reference=1
+Leaked cluster 6 refcount=1 reference=0
+Leaked cluster 7 refcount=1 reference=0
+Repairing cluster 4 refcount=2 reference=1
+Repairing cluster 5 refcount=2 reference=1
+Repairing cluster 6 refcount=1 reference=0
+Repairing cluster 7 refcount=1 reference=0
+Repairing OFLAG_COPIED L2 cluster: l1_index=0 l1_entry=40000 refcount=1
+Repairing OFLAG_COPIED data cluster: l2_entry=50000 refcount=1
+The following inconsistencies were found and repaired:
+
+ 4 leaked clusters
+ 2 corruptions
+
+Double checking the fixed image now...
+No errors were found on the image.
+*** done
diff --git a/tests/qemu-iotests/219 b/tests/qemu-iotests/219
index 898a26eef0..c03bbdb294 100755
--- a/tests/qemu-iotests/219
+++ b/tests/qemu-iotests/219
@@ -42,11 +42,24 @@ def test_pause_resume(vm):
iotests.log(vm.qmp(pause_cmd, **{pause_arg: 'job0'}))
pause_wait(vm, 'job0')
iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE')))
- iotests.log(vm.qmp('query-jobs'))
+ result = vm.qmp('query-jobs')
+ iotests.log(result)
+
+ old_progress = result['return'][0]['current-progress']
+ total_progress = result['return'][0]['total-progress']
iotests.log(vm.qmp(resume_cmd, **{resume_arg: 'job0'}))
iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE')))
- iotests.log(vm.qmp('query-jobs'))
+ if old_progress < total_progress:
+ # Wait for the job to advance
+ while result['return'][0]['current-progress'] == old_progress:
+ result = vm.qmp('query-jobs')
+ iotests.log(result)
+ else:
+ # Already reached the end, so the job cannot advance
+ # any further; therefore, the query-jobs result can be
+ # logged immediately
+ iotests.log(vm.qmp('query-jobs'))
def test_job_lifecycle(vm, job, job_args, has_ready=False):
iotests.log('')
@@ -58,12 +71,13 @@ def test_job_lifecycle(vm, job, job_args, has_ready=False):
iotests.log(vm.qmp(job, job_id='job0', **job_args))
# Depending on the storage, the first request may or may not have completed
- # yet, so filter out the progress. Later query-job calls don't need the
- # filtering because the progress is made deterministic by the block job
- # speed
+ # yet (and the total progress may not have been fully determined yet), so
+ # filter out the progress. Later query-job calls don't need the filtering
+ # because the progress is made deterministic by the block job speed
result = vm.qmp('query-jobs')
for j in result['return']:
- del j['current-progress']
+ j['current-progress'] = 'FILTERED'
+ j['total-progress'] = 'FILTERED'
iotests.log(result)
# undefined -> created -> running
diff --git a/tests/qemu-iotests/219.out b/tests/qemu-iotests/219.out
index 346801b655..6dc07bc41e 100644
--- a/tests/qemu-iotests/219.out
+++ b/tests/qemu-iotests/219.out
@@ -3,7 +3,7 @@ Launching VM...
Starting block job: drive-mirror (auto-finalize: True; auto-dismiss: True)
{u'return': {}}
-{u'return': [{u'status': u'running', u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]}
+{u'return': [{u'status': u'running', u'current-progress': 'FILTERED', u'total-progress': 'FILTERED', u'id': u'job0', u'type': u'mirror'}]}
{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'created', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
@@ -93,7 +93,7 @@ Waiting for PENDING state...
Starting block job: drive-backup (auto-finalize: True; auto-dismiss: True)
{u'return': {}}
-{u'return': [{u'status': u'running', u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
+{u'return': [{u'status': u'running', u'current-progress': 'FILTERED', u'total-progress': 'FILTERED', u'id': u'job0', u'type': u'backup'}]}
{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'created', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
@@ -144,7 +144,7 @@ Waiting for PENDING state...
Starting block job: drive-backup (auto-finalize: True; auto-dismiss: False)
{u'return': {}}
-{u'return': [{u'status': u'running', u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
+{u'return': [{u'status': u'running', u'current-progress': 'FILTERED', u'total-progress': 'FILTERED', u'id': u'job0', u'type': u'backup'}]}
{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'created', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
@@ -203,7 +203,7 @@ Waiting for PENDING state...
Starting block job: drive-backup (auto-finalize: False; auto-dismiss: True)
{u'return': {}}
-{u'return': [{u'status': u'running', u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
+{u'return': [{u'status': u'running', u'current-progress': 'FILTERED', u'total-progress': 'FILTERED', u'id': u'job0', u'type': u'backup'}]}
{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'created', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
@@ -262,7 +262,7 @@ Waiting for PENDING state...
Starting block job: drive-backup (auto-finalize: False; auto-dismiss: False)
{u'return': {}}
-{u'return': [{u'status': u'running', u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
+{u'return': [{u'status': u'running', u'current-progress': 'FILTERED', u'total-progress': 'FILTERED', u'id': u'job0', u'type': u'backup'}]}
{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'created', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
index 93f93d71ba..0914c922d7 100644
--- a/tests/qemu-iotests/group
+++ b/tests/qemu-iotests/group
@@ -215,5 +215,6 @@
214 rw auto
215 rw auto quick
216 rw auto quick
+217 rw auto quick
218 rw auto quick
219 rw auto
diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
index fd8f79fef9..4e67fbbe96 100644
--- a/tests/qemu-iotests/iotests.py
+++ b/tests/qemu-iotests/iotests.py
@@ -134,6 +134,15 @@ def qemu_io(*args):
sys.stderr.write('qemu-io received signal %i: %s\n' % (-exitcode, ' '.join(args)))
return subp.communicate()[0]
+def qemu_io_silent(*args):
+ '''Run qemu-io and return the exit code, suppressing stdout'''
+ args = qemu_io_args + list(args)
+ exitcode = subprocess.call(args, stdout=open('/dev/null', 'w'))
+ if exitcode < 0:
+ sys.stderr.write('qemu-io received signal %i: %s\n' %
+ (-exitcode, ' '.join(args)))
+ return exitcode
+
class QemuIoInteractive:
def __init__(self, *args):
@@ -582,9 +591,14 @@ class QMPTestCase(unittest.TestCase):
with Timeout(1, "Timeout waiting for job to pause"):
while True:
result = self.vm.qmp('query-block-jobs')
+ found = False
for job in result['return']:
- if job['device'] == job_id and job['paused'] == True and job['busy'] == False:
- return job
+ if job['device'] == job_id:
+ found = True
+ if job['paused'] == True and job['busy'] == False:
+ return job
+ break
+ assert found
def pause_job(self, job_id='job0', wait=True):
result = self.vm.qmp('block-job-pause', device=job_id)
diff --git a/util/qemu-option.c b/util/qemu-option.c
index 58d1c23893..ba44a0895c 100644
--- a/util/qemu-option.c
+++ b/util/qemu-option.c
@@ -220,7 +220,6 @@ void qemu_opts_print_help(QemuOptsList *list)
assert(list);
desc = list->desc;
- printf("Supported options:\n");
while (desc && desc->name) {
printf("%-16s %s\n", desc->name,
desc->help ? desc->help : "No description available");
diff --git a/vl.c b/vl.c
index 06031715ac..6e34fb348d 100644
--- a/vl.c
+++ b/vl.c
@@ -1841,7 +1841,7 @@ static void main_loop(void)
#ifdef CONFIG_PROFILER
int64_t ti;
#endif
- do {
+ while (!main_loop_should_exit()) {
#ifdef CONFIG_PROFILER
ti = profile_getclock();
#endif
@@ -1849,7 +1849,7 @@ static void main_loop(void)
#ifdef CONFIG_PROFILER
dev_time += profile_getclock() - ti;
#endif
- } while (!main_loop_should_exit());
+ }
}
static void version(void)