summary refs log tree commit diff stats
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/.gitignore1
-rw-r--r--tests/Makefile12
-rw-r--r--tests/acpi-test.c397
-rw-r--r--tests/check-qdict.c156
-rw-r--r--tests/check-qom-interface.c105
-rw-r--r--tests/fdc-test.c5
-rw-r--r--tests/i440fx-test.c167
-rw-r--r--tests/ide-test.c3
-rw-r--r--tests/qdev-monitor-test.c4
-rwxr-xr-xtests/qemu-iotests/0171
-rwxr-xr-xtests/qemu-iotests/0181
-rwxr-xr-xtests/qemu-iotests/0193
-rwxr-xr-xtests/qemu-iotests/0203
-rwxr-xr-xtests/qemu-iotests/0343
-rwxr-xr-xtests/qemu-iotests/0373
-rwxr-xr-xtests/qemu-iotests/04074
-rw-r--r--tests/qemu-iotests/051.out3
-rwxr-xr-xtests/qemu-iotests/05930
-rw-r--r--tests/qemu-iotests/059.out84
-rwxr-xr-xtests/qemu-iotests/0633
-rwxr-xr-xtests/qemu-iotests/0691
-rwxr-xr-xtests/qemu-iotests/071239
-rw-r--r--tests/qemu-iotests/071.out90
-rwxr-xr-xtests/qemu-iotests/07269
-rw-r--r--tests/qemu-iotests/072.out21
-rwxr-xr-xtests/qemu-iotests/077278
-rw-r--r--tests/qemu-iotests/077.out202
-rw-r--r--tests/qemu-iotests/common.rc28
-rw-r--r--tests/qemu-iotests/group3
-rw-r--r--tests/qom-test.c2
-rw-r--r--tests/test-vmstate.c357
31 files changed, 2273 insertions, 75 deletions
diff --git a/tests/.gitignore b/tests/.gitignore
index 425757cfe1..1aed2249ff 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -20,6 +20,7 @@ test-qmp-commands
 test-qmp-input-strict
 test-qmp-marshal.c
 test-thread-pool
+test-vmstate
 test-x86-cpuid
 test-xbzrle
 *-test
diff --git a/tests/Makefile b/tests/Makefile
index 379cdd9ad1..fd36eee641 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -52,6 +52,9 @@ check-unit-y += tests/test-int128$(EXESUF)
 gcov-files-test-int128-y =
 check-unit-y += tests/test-bitops$(EXESUF)
 check-unit-y += tests/test-qdev-global-props$(EXESUF)
+check-unit-y += tests/check-qom-interface$(EXESUF)
+gcov-files-check-qom-interface-y = qom/object.c
+check-unit-y += tests/test-vmstate$(EXESUF)
 
 check-block-$(CONFIG_POSIX) += tests/qemu-iotests-quick.sh
 
@@ -64,6 +67,7 @@ check-qtest-i386-y += tests/ide-test$(EXESUF)
 check-qtest-i386-y += tests/hd-geo-test$(EXESUF)
 gcov-files-i386-y += hw/hd-geometry.c
 check-qtest-i386-y += tests/boot-order-test$(EXESUF)
+check-qtest-i386-y += tests/acpi-test$(EXESUF)
 check-qtest-i386-y += tests/rtc-test$(EXESUF)
 check-qtest-i386-y += tests/i440fx-test$(EXESUF)
 check-qtest-i386-y += tests/fw_cfg-test$(EXESUF)
@@ -137,6 +141,7 @@ test-qapi-obj-y = tests/test-qapi-visit.o tests/test-qapi-types.o
 
 $(test-obj-y): QEMU_INCLUDES += -Itests
 QEMU_CFLAGS += -I$(SRC_PATH)/tests
+qom-core-obj = qom/object.o qom/qom-qobject.o qom/container.o
 
 tests/test-x86-cpuid.o: QEMU_INCLUDES += -I$(SRC_PATH)/target-i386
 
@@ -146,6 +151,7 @@ tests/check-qdict$(EXESUF): tests/check-qdict.o libqemuutil.a
 tests/check-qlist$(EXESUF): tests/check-qlist.o libqemuutil.a
 tests/check-qfloat$(EXESUF): tests/check-qfloat.o libqemuutil.a
 tests/check-qjson$(EXESUF): tests/check-qjson.o libqemuutil.a libqemustub.a
+tests/check-qom-interface$(EXESUF): tests/check-qom-interface.o $(qom-core-obj) libqemuutil.a libqemustub.a
 tests/test-coroutine$(EXESUF): tests/test-coroutine.o $(block-obj-y) libqemuutil.a libqemustub.a
 tests/test-aio$(EXESUF): tests/test-aio.o $(block-obj-y) libqemuutil.a libqemustub.a
 tests/test-throttle$(EXESUF): tests/test-throttle.o $(block-obj-y) libqemuutil.a libqemustub.a
@@ -159,9 +165,12 @@ tests/test-int128$(EXESUF): tests/test-int128.o
 tests/test-qdev-global-props$(EXESUF): tests/test-qdev-global-props.o \
 	hw/core/qdev.o hw/core/qdev-properties.o \
 	hw/core/irq.o \
-	qom/object.o qom/container.o qom/qom-qobject.o \
+	$(qom-core-obj) \
 	$(test-qapi-obj-y) \
 	libqemuutil.a libqemustub.a
+tests/test-vmstate$(EXESUF): tests/test-vmstate.o \
+	vmstate.o qemu-file.o \
+	libqemuutil.a
 
 tests/test-qapi-types.c tests/test-qapi-types.h :\
 $(SRC_PATH)/tests/qapi-schema/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-types.py
@@ -198,6 +207,7 @@ tests/fdc-test$(EXESUF): tests/fdc-test.o
 tests/ide-test$(EXESUF): tests/ide-test.o $(libqos-pc-obj-y)
 tests/hd-geo-test$(EXESUF): tests/hd-geo-test.o
 tests/boot-order-test$(EXESUF): tests/boot-order-test.o $(libqos-obj-y)
+tests/acpi-test$(EXESUF): tests/acpi-test.o $(libqos-obj-y)
 tests/tmp105-test$(EXESUF): tests/tmp105-test.o $(libqos-omap-obj-y)
 tests/i440fx-test$(EXESUF): tests/i440fx-test.o $(libqos-pc-obj-y)
 tests/fw_cfg-test$(EXESUF): tests/fw_cfg-test.o $(libqos-pc-obj-y)
diff --git a/tests/acpi-test.c b/tests/acpi-test.c
new file mode 100644
index 0000000000..df1af83158
--- /dev/null
+++ b/tests/acpi-test.c
@@ -0,0 +1,397 @@
+/*
+ * Boot order test cases.
+ *
+ * Copyright (c) 2013 Red Hat Inc.
+ *
+ * Authors:
+ *  Michael S. Tsirkin <mst@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 <string.h>
+#include <stdio.h>
+#include <glib.h>
+#include "qemu-common.h"
+#include "libqtest.h"
+#include "qemu/compiler.h"
+#include "hw/i386/acpi-defs.h"
+
+/* DSDT and SSDTs format */
+typedef struct {
+    AcpiTableHeader header;
+    uint8_t *aml;
+    int aml_len;
+} AcpiSdtTable;
+
+typedef struct {
+    uint32_t rsdp_addr;
+    AcpiRsdpDescriptor rsdp_table;
+    AcpiRsdtDescriptorRev1 rsdt_table;
+    AcpiFadtDescriptorRev1 fadt_table;
+    AcpiFacsDescriptorRev1 facs_table;
+    uint32_t *rsdt_tables_addr;
+    int rsdt_tables_nr;
+    AcpiSdtTable dsdt_table;
+    GArray *ssdt_tables;
+} test_data;
+
+#define LOW(x) ((x) & 0xff)
+#define HIGH(x) ((x) >> 8)
+
+#define SIGNATURE 0xdead
+#define SIGNATURE_OFFSET 0x10
+#define BOOT_SECTOR_ADDRESS 0x7c00
+
+#define ACPI_READ_FIELD(field, addr)           \
+    do {                                       \
+        switch (sizeof(field)) {               \
+        case 1:                                \
+            field = readb(addr);               \
+            break;                             \
+        case 2:                                \
+            field = le16_to_cpu(readw(addr));  \
+            break;                             \
+        case 4:                                \
+            field = le32_to_cpu(readl(addr));  \
+            break;                             \
+        case 8:                                \
+            field = le64_to_cpu(readq(addr));  \
+            break;                             \
+        default:                               \
+            g_assert(false);                   \
+        }                                      \
+        addr += sizeof(field);                  \
+    } while (0);
+
+#define ACPI_READ_ARRAY_PTR(arr, length, addr)  \
+    do {                                        \
+        int idx;                                \
+        for (idx = 0; idx < length; ++idx) {    \
+            ACPI_READ_FIELD(arr[idx], addr);    \
+        }                                       \
+    } while (0);
+
+#define ACPI_READ_ARRAY(arr, addr)                               \
+    ACPI_READ_ARRAY_PTR(arr, sizeof(arr)/sizeof(arr[0]), addr)
+
+#define ACPI_READ_TABLE_HEADER(table, addr)                      \
+    do {                                                         \
+        ACPI_READ_FIELD((table)->signature, addr);               \
+        ACPI_READ_FIELD((table)->length, addr);                  \
+        ACPI_READ_FIELD((table)->revision, addr);                \
+        ACPI_READ_FIELD((table)->checksum, addr);                \
+        ACPI_READ_ARRAY((table)->oem_id, addr);                  \
+        ACPI_READ_ARRAY((table)->oem_table_id, addr);            \
+        ACPI_READ_FIELD((table)->oem_revision, addr);            \
+        ACPI_READ_ARRAY((table)->asl_compiler_id, addr);         \
+        ACPI_READ_FIELD((table)->asl_compiler_revision, addr);   \
+    } while (0);
+
+/* Boot sector code: write SIGNATURE into memory,
+ * then halt.
+ */
+static uint8_t boot_sector[0x200] = {
+    /* 7c00: mov $0xdead,%ax */
+    [0x00] = 0xb8,
+    [0x01] = LOW(SIGNATURE),
+    [0x02] = HIGH(SIGNATURE),
+    /* 7c03:  mov %ax,0x7c10 */
+    [0x03] = 0xa3,
+    [0x04] = LOW(BOOT_SECTOR_ADDRESS + SIGNATURE_OFFSET),
+    [0x05] = HIGH(BOOT_SECTOR_ADDRESS + SIGNATURE_OFFSET),
+    /* 7c06: cli */
+    [0x06] = 0xfa,
+    /* 7c07: hlt */
+    [0x07] = 0xf4,
+    /* 7c08: jmp 0x7c07=0x7c0a-3 */
+    [0x08] = 0xeb,
+    [0x09] = LOW(-3),
+    /* We mov 0xdead here: set value to make debugging easier */
+    [SIGNATURE_OFFSET] = LOW(0xface),
+    [SIGNATURE_OFFSET + 1] = HIGH(0xface),
+    /* End of boot sector marker */
+    [0x1FE] = 0x55,
+    [0x1FF] = 0xAA,
+};
+
+static const char *disk = "tests/acpi-test-disk.raw";
+
+static void free_test_data(test_data *data)
+{
+    int i;
+
+    g_free(data->rsdt_tables_addr);
+    for (i = 0; i < data->ssdt_tables->len; ++i) {
+        g_free(g_array_index(data->ssdt_tables, AcpiSdtTable, i).aml);
+    }
+    g_array_free(data->ssdt_tables, false);
+    g_free(data->dsdt_table.aml);
+}
+
+static uint8_t acpi_checksum(const uint8_t *data, int len)
+{
+    int i;
+    uint8_t sum = 0;
+
+    for (i = 0; i < len; i++) {
+        sum += data[i];
+    }
+
+    return sum;
+}
+
+static void test_acpi_rsdp_address(test_data *data)
+{
+    uint32_t off;
+
+    /* OK, now find RSDP */
+    for (off = 0xf0000; off < 0x100000; off += 0x10) {
+        uint8_t sig[] = "RSD PTR ";
+        int i;
+
+        for (i = 0; i < sizeof sig - 1; ++i) {
+            sig[i] = readb(off + i);
+        }
+
+        if (!memcmp(sig, "RSD PTR ", sizeof sig)) {
+            break;
+        }
+    }
+
+    g_assert_cmphex(off, <, 0x100000);
+    data->rsdp_addr = off;
+}
+
+static void test_acpi_rsdp_table(test_data *data)
+{
+    AcpiRsdpDescriptor *rsdp_table = &data->rsdp_table;
+    uint32_t addr = data->rsdp_addr;
+
+    ACPI_READ_FIELD(rsdp_table->signature, addr);
+    g_assert_cmphex(rsdp_table->signature, ==, ACPI_RSDP_SIGNATURE);
+
+    ACPI_READ_FIELD(rsdp_table->checksum, addr);
+    ACPI_READ_ARRAY(rsdp_table->oem_id, addr);
+    ACPI_READ_FIELD(rsdp_table->revision, addr);
+    ACPI_READ_FIELD(rsdp_table->rsdt_physical_address, addr);
+    ACPI_READ_FIELD(rsdp_table->length, addr);
+
+    /* rsdp checksum is not for the whole table, but for the first 20 bytes */
+    g_assert(!acpi_checksum((uint8_t *)rsdp_table, 20));
+}
+
+static void test_acpi_rsdt_table(test_data *data)
+{
+    AcpiRsdtDescriptorRev1 *rsdt_table = &data->rsdt_table;
+    uint32_t addr = data->rsdp_table.rsdt_physical_address;
+    uint32_t *tables;
+    int tables_nr;
+    uint8_t checksum;
+
+    /* read the header */
+    ACPI_READ_TABLE_HEADER(rsdt_table, addr);
+    g_assert_cmphex(rsdt_table->signature, ==, ACPI_RSDT_SIGNATURE);
+
+    /* compute the table entries in rsdt */
+    tables_nr = (rsdt_table->length - sizeof(AcpiRsdtDescriptorRev1)) /
+                sizeof(uint32_t);
+    g_assert_cmpint(tables_nr, >, 0);
+
+    /* get the addresses of the tables pointed by rsdt */
+    tables = g_new0(uint32_t, tables_nr);
+    ACPI_READ_ARRAY_PTR(tables, tables_nr, addr);
+
+    checksum = acpi_checksum((uint8_t *)rsdt_table, rsdt_table->length) +
+               acpi_checksum((uint8_t *)tables, tables_nr * sizeof(uint32_t));
+    g_assert(!checksum);
+
+   /* SSDT tables after FADT */
+    data->rsdt_tables_addr = tables;
+    data->rsdt_tables_nr = tables_nr;
+}
+
+static void test_acpi_fadt_table(test_data *data)
+{
+    AcpiFadtDescriptorRev1 *fadt_table = &data->fadt_table;
+    uint32_t addr;
+
+    /* FADT table comes first */
+    addr = data->rsdt_tables_addr[0];
+    ACPI_READ_TABLE_HEADER(fadt_table, addr);
+
+    ACPI_READ_FIELD(fadt_table->firmware_ctrl, addr);
+    ACPI_READ_FIELD(fadt_table->dsdt, addr);
+    ACPI_READ_FIELD(fadt_table->model, addr);
+    ACPI_READ_FIELD(fadt_table->reserved1, addr);
+    ACPI_READ_FIELD(fadt_table->sci_int, addr);
+    ACPI_READ_FIELD(fadt_table->smi_cmd, addr);
+    ACPI_READ_FIELD(fadt_table->acpi_enable, addr);
+    ACPI_READ_FIELD(fadt_table->acpi_disable, addr);
+    ACPI_READ_FIELD(fadt_table->S4bios_req, addr);
+    ACPI_READ_FIELD(fadt_table->reserved2, addr);
+    ACPI_READ_FIELD(fadt_table->pm1a_evt_blk, addr);
+    ACPI_READ_FIELD(fadt_table->pm1b_evt_blk, addr);
+    ACPI_READ_FIELD(fadt_table->pm1a_cnt_blk, addr);
+    ACPI_READ_FIELD(fadt_table->pm1b_cnt_blk, addr);
+    ACPI_READ_FIELD(fadt_table->pm2_cnt_blk, addr);
+    ACPI_READ_FIELD(fadt_table->pm_tmr_blk, addr);
+    ACPI_READ_FIELD(fadt_table->gpe0_blk, addr);
+    ACPI_READ_FIELD(fadt_table->gpe1_blk, addr);
+    ACPI_READ_FIELD(fadt_table->pm1_evt_len, addr);
+    ACPI_READ_FIELD(fadt_table->pm1_cnt_len, addr);
+    ACPI_READ_FIELD(fadt_table->pm2_cnt_len, addr);
+    ACPI_READ_FIELD(fadt_table->pm_tmr_len, addr);
+    ACPI_READ_FIELD(fadt_table->gpe0_blk_len, addr);
+    ACPI_READ_FIELD(fadt_table->gpe1_blk_len, addr);
+    ACPI_READ_FIELD(fadt_table->gpe1_base, addr);
+    ACPI_READ_FIELD(fadt_table->reserved3, addr);
+    ACPI_READ_FIELD(fadt_table->plvl2_lat, addr);
+    ACPI_READ_FIELD(fadt_table->plvl3_lat, addr);
+    ACPI_READ_FIELD(fadt_table->flush_size, addr);
+    ACPI_READ_FIELD(fadt_table->flush_stride, addr);
+    ACPI_READ_FIELD(fadt_table->duty_offset, addr);
+    ACPI_READ_FIELD(fadt_table->duty_width, addr);
+    ACPI_READ_FIELD(fadt_table->day_alrm, addr);
+    ACPI_READ_FIELD(fadt_table->mon_alrm, addr);
+    ACPI_READ_FIELD(fadt_table->century, addr);
+    ACPI_READ_FIELD(fadt_table->reserved4, addr);
+    ACPI_READ_FIELD(fadt_table->reserved4a, addr);
+    ACPI_READ_FIELD(fadt_table->reserved4b, addr);
+    ACPI_READ_FIELD(fadt_table->flags, addr);
+
+    g_assert_cmphex(fadt_table->signature, ==, ACPI_FACP_SIGNATURE);
+    g_assert(!acpi_checksum((uint8_t *)fadt_table, fadt_table->length));
+}
+
+static void test_acpi_facs_table(test_data *data)
+{
+    AcpiFacsDescriptorRev1 *facs_table = &data->facs_table;
+    uint32_t addr = data->fadt_table.firmware_ctrl;
+
+    ACPI_READ_FIELD(facs_table->signature, addr);
+    ACPI_READ_FIELD(facs_table->length, addr);
+    ACPI_READ_FIELD(facs_table->hardware_signature, addr);
+    ACPI_READ_FIELD(facs_table->firmware_waking_vector, addr);
+    ACPI_READ_FIELD(facs_table->global_lock, addr);
+    ACPI_READ_FIELD(facs_table->flags, addr);
+    ACPI_READ_ARRAY(facs_table->resverved3, addr);
+
+    g_assert_cmphex(facs_table->signature, ==, ACPI_FACS_SIGNATURE);
+}
+
+static void test_dst_table(AcpiSdtTable *sdt_table, uint32_t addr)
+{
+    uint8_t checksum;
+
+    ACPI_READ_TABLE_HEADER(&sdt_table->header, addr);
+
+    sdt_table->aml_len = sdt_table->header.length - sizeof(AcpiTableHeader);
+    sdt_table->aml = g_malloc0(sdt_table->aml_len);
+    ACPI_READ_ARRAY_PTR(sdt_table->aml, sdt_table->aml_len, addr);
+
+    checksum = acpi_checksum((uint8_t *)sdt_table, sizeof(AcpiTableHeader)) +
+               acpi_checksum(sdt_table->aml, sdt_table->aml_len);
+    g_assert(!checksum);
+}
+
+static void test_acpi_dsdt_table(test_data *data)
+{
+    AcpiSdtTable *dsdt_table = &data->dsdt_table;
+    uint32_t addr = data->fadt_table.dsdt;
+
+    test_dst_table(dsdt_table, addr);
+    g_assert_cmphex(dsdt_table->header.signature, ==, ACPI_DSDT_SIGNATURE);
+}
+
+static void test_acpi_ssdt_tables(test_data *data)
+{
+    GArray *ssdt_tables;
+    int ssdt_tables_nr = data->rsdt_tables_nr - 1; /* fadt is first */
+    int i;
+
+    ssdt_tables = g_array_sized_new(false, true, sizeof(AcpiSdtTable),
+                                    ssdt_tables_nr);
+    for (i = 0; i < ssdt_tables_nr; i++) {
+        AcpiSdtTable ssdt_table;
+        uint32_t addr = data->rsdt_tables_addr[i + 1]; /* fadt is first */
+        test_dst_table(&ssdt_table, addr);
+        g_array_append_val(ssdt_tables, ssdt_table);
+    }
+    data->ssdt_tables = ssdt_tables;
+}
+
+static void test_acpi_one(const char *params, test_data *data)
+{
+    char *args;
+    uint8_t signature_low;
+    uint8_t signature_high;
+    uint16_t signature;
+    int i;
+
+    memset(data, 0, sizeof(*data));
+    args = g_strdup_printf("-net none -display none %s %s",
+                           params ? params : "", disk);
+    qtest_start(args);
+
+   /* Wait at most 1 minute */
+#define TEST_DELAY (1 * G_USEC_PER_SEC / 10)
+#define TEST_CYCLES MAX((60 * G_USEC_PER_SEC / TEST_DELAY), 1)
+
+    /* Poll until code has run and modified memory.  Once it has we know BIOS
+     * initialization is done.  TODO: check that IP reached the halt
+     * instruction.
+     */
+    for (i = 0; i < TEST_CYCLES; ++i) {
+        signature_low = readb(BOOT_SECTOR_ADDRESS + SIGNATURE_OFFSET);
+        signature_high = readb(BOOT_SECTOR_ADDRESS + SIGNATURE_OFFSET + 1);
+        signature = (signature_high << 8) | signature_low;
+        if (signature == SIGNATURE) {
+            break;
+        }
+        g_usleep(TEST_DELAY);
+    }
+    g_assert_cmphex(signature, ==, SIGNATURE);
+
+    test_acpi_rsdp_address(data);
+    test_acpi_rsdp_table(data);
+    test_acpi_rsdt_table(data);
+    test_acpi_fadt_table(data);
+    test_acpi_facs_table(data);
+    test_acpi_dsdt_table(data);
+    test_acpi_ssdt_tables(data);
+
+    qtest_quit(global_qtest);
+    g_free(args);
+}
+
+static void test_acpi_tcg(void)
+{
+    test_data data;
+
+    /* Supplying -machine accel argument overrides the default (qtest).
+     * This is to make guest actually run.
+     */
+    test_acpi_one("-machine accel=tcg", &data);
+
+    free_test_data(&data);
+}
+
+int main(int argc, char *argv[])
+{
+    const char *arch = qtest_get_arch();
+    FILE *f = fopen(disk, "w");
+    int ret;
+    fwrite(boot_sector, 1, sizeof boot_sector, f);
+    fclose(f);
+
+    g_test_init(&argc, &argv, NULL);
+
+    if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) {
+        qtest_add_func("acpi/tcg", test_acpi_tcg);
+    }
+    ret = g_test_run();
+    unlink(disk);
+    return ret;
+}
diff --git a/tests/check-qdict.c b/tests/check-qdict.c
index dc5f05a85f..7a7461b0b2 100644
--- a/tests/check-qdict.c
+++ b/tests/check-qdict.c
@@ -227,6 +227,160 @@ static void qdict_iterapi_test(void)
     QDECREF(tests_dict);
 }
 
+static void qdict_flatten_test(void)
+{
+    QList *list1 = qlist_new();
+    QList *list2 = qlist_new();
+    QDict *dict1 = qdict_new();
+    QDict *dict2 = qdict_new();
+    QDict *dict3 = qdict_new();
+
+    /*
+     * Test the flattening of
+     *
+     * {
+     *     "e": [
+     *         42,
+     *         [
+     *             23,
+     *             66,
+     *             {
+     *                 "a": 0,
+     *                 "b": 1
+     *             }
+     *         ]
+     *     ],
+     *     "f": {
+     *         "c": 2,
+     *         "d": 3,
+     *     },
+     *     "g": 4
+     * }
+     *
+     * to
+     *
+     * {
+     *     "e.0": 42,
+     *     "e.1.0": 23,
+     *     "e.1.1": 66,
+     *     "e.1.2.a": 0,
+     *     "e.1.2.b": 1,
+     *     "f.c": 2,
+     *     "f.d": 3,
+     *     "g": 4
+     * }
+     */
+
+    qdict_put(dict1, "a", qint_from_int(0));
+    qdict_put(dict1, "b", qint_from_int(1));
+
+    qlist_append_obj(list1, QOBJECT(qint_from_int(23)));
+    qlist_append_obj(list1, QOBJECT(qint_from_int(66)));
+    qlist_append_obj(list1, QOBJECT(dict1));
+    qlist_append_obj(list2, QOBJECT(qint_from_int(42)));
+    qlist_append_obj(list2, QOBJECT(list1));
+
+    qdict_put(dict2, "c", qint_from_int(2));
+    qdict_put(dict2, "d", qint_from_int(3));
+    qdict_put_obj(dict3, "e", QOBJECT(list2));
+    qdict_put_obj(dict3, "f", QOBJECT(dict2));
+    qdict_put(dict3, "g", qint_from_int(4));
+
+    qdict_flatten(dict3);
+
+    g_assert(qdict_get_int(dict3, "e.0") == 42);
+    g_assert(qdict_get_int(dict3, "e.1.0") == 23);
+    g_assert(qdict_get_int(dict3, "e.1.1") == 66);
+    g_assert(qdict_get_int(dict3, "e.1.2.a") == 0);
+    g_assert(qdict_get_int(dict3, "e.1.2.b") == 1);
+    g_assert(qdict_get_int(dict3, "f.c") == 2);
+    g_assert(qdict_get_int(dict3, "f.d") == 3);
+    g_assert(qdict_get_int(dict3, "g") == 4);
+
+    g_assert(qdict_size(dict3) == 8);
+
+    QDECREF(dict3);
+}
+
+static void qdict_array_split_test(void)
+{
+    QDict *test_dict = qdict_new();
+    QDict *dict1, *dict2;
+    QList *test_list;
+
+    /*
+     * Test the split of
+     *
+     * {
+     *     "1.x": 0,
+     *     "3.y": 1,
+     *     "0.a": 42,
+     *     "o.o": 7,
+     *     "0.b": 23
+     * }
+     *
+     * to
+     *
+     * [
+     *     {
+     *         "a": 42,
+     *         "b": 23
+     *     },
+     *     {
+     *         "x": 0
+     *     }
+     * ]
+     *
+     * and
+     *
+     * {
+     *     "3.y": 1,
+     *     "o.o": 7
+     * }
+     *
+     * (remaining in the old QDict)
+     *
+     * This example is given in the comment of qdict_array_split().
+     */
+
+    qdict_put(test_dict, "1.x", qint_from_int(0));
+    qdict_put(test_dict, "3.y", qint_from_int(1));
+    qdict_put(test_dict, "0.a", qint_from_int(42));
+    qdict_put(test_dict, "o.o", qint_from_int(7));
+    qdict_put(test_dict, "0.b", qint_from_int(23));
+
+    qdict_array_split(test_dict, &test_list);
+
+    dict1 = qobject_to_qdict(qlist_pop(test_list));
+    dict2 = qobject_to_qdict(qlist_pop(test_list));
+
+    g_assert(dict1);
+    g_assert(dict2);
+    g_assert(qlist_empty(test_list));
+
+    QDECREF(test_list);
+
+    g_assert(qdict_get_int(dict1, "a") == 42);
+    g_assert(qdict_get_int(dict1, "b") == 23);
+
+    g_assert(qdict_size(dict1) == 2);
+
+    QDECREF(dict1);
+
+    g_assert(qdict_get_int(dict2, "x") == 0);
+
+    g_assert(qdict_size(dict2) == 1);
+
+    QDECREF(dict2);
+
+    g_assert(qdict_get_int(test_dict, "3.y") == 1);
+    g_assert(qdict_get_int(test_dict, "o.o") == 7);
+
+    g_assert(qdict_size(test_dict) == 2);
+
+    QDECREF(test_dict);
+}
+
 /*
  * Errors test-cases
  */
@@ -365,6 +519,8 @@ int main(int argc, char **argv)
     g_test_add_func("/public/del", qdict_del_test);
     g_test_add_func("/public/to_qdict", qobject_to_qdict_test);
     g_test_add_func("/public/iterapi", qdict_iterapi_test);
+    g_test_add_func("/public/flatten", qdict_flatten_test);
+    g_test_add_func("/public/array_split", qdict_array_split_test);
 
     g_test_add_func("/errors/put_exists", qdict_put_exists_test);
     g_test_add_func("/errors/get_not_exists", qdict_get_not_exists_test);
diff --git a/tests/check-qom-interface.c b/tests/check-qom-interface.c
new file mode 100644
index 0000000000..f06380ef14
--- /dev/null
+++ b/tests/check-qom-interface.c
@@ -0,0 +1,105 @@
+/*
+ * QOM interface test.
+ *
+ * Copyright (C) 2013 Red Hat Inc.
+ *
+ * Authors:
+ *  Igor Mammedov <imammedo@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ */
+#include <glib.h>
+
+#include "qom/object.h"
+#include "qemu/module.h"
+
+
+#define TYPE_TEST_IF "test-interface"
+#define TEST_IF_CLASS(klass) \
+     OBJECT_CLASS_CHECK(TestIfClass, (klass), TYPE_TEST_IF)
+#define TEST_IF_GET_CLASS(obj) \
+     OBJECT_GET_CLASS(TestIfClass, (obj), TYPE_TEST_IF)
+#define TEST_IF(obj) \
+     INTERFACE_CHECK(TestIf, (obj), TYPE_TEST_IF)
+
+typedef struct TestIf {
+    Object parent_obj;
+} TestIf;
+
+typedef struct TestIfClass {
+    InterfaceClass parent_class;
+
+    uint32_t test;
+} TestIfClass;
+
+static const TypeInfo test_if_info = {
+    .name          = TYPE_TEST_IF,
+    .parent        = TYPE_INTERFACE,
+    .class_size = sizeof(TestIfClass),
+};
+
+#define PATTERN 0xFAFBFCFD
+
+static void test_class_init(ObjectClass *oc, void *data)
+{
+    TestIfClass *tc = TEST_IF_CLASS(oc);
+
+    g_assert(tc);
+    tc->test = PATTERN;
+}
+
+#define TYPE_DIRECT_IMPL "direct-impl"
+
+static const TypeInfo direct_impl_info = {
+    .name = TYPE_DIRECT_IMPL,
+    .parent = TYPE_OBJECT,
+    .class_init = test_class_init,
+    .interfaces = (InterfaceInfo[]) {
+        { TYPE_TEST_IF },
+        { }
+    }
+};
+
+#define TYPE_INTERMEDIATE_IMPL "intermediate-impl"
+
+static const TypeInfo intermediate_impl_info = {
+    .name = TYPE_INTERMEDIATE_IMPL,
+    .parent = TYPE_DIRECT_IMPL,
+};
+
+static void test_interface_impl(const char *type)
+{
+    Object *obj = object_new(type);
+    TestIf *iobj = TEST_IF(obj);
+    TestIfClass *ioc = TEST_IF_GET_CLASS(iobj);
+
+    g_assert(iobj);
+    g_assert(ioc->test == PATTERN);
+}
+
+static void interface_direct_test(void)
+{
+    test_interface_impl(TYPE_DIRECT_IMPL);
+}
+
+static void interface_intermediate_test(void)
+{
+    test_interface_impl(TYPE_INTERMEDIATE_IMPL);
+}
+
+int main(int argc, char **argv)
+{
+    g_test_init(&argc, &argv, NULL);
+
+    module_call_init(MODULE_INIT_QOM);
+    type_register_static(&test_if_info);
+    type_register_static(&direct_impl_info);
+    type_register_static(&intermediate_impl_info);
+
+    g_test_add_func("/qom/interface/direct_impl", interface_direct_test);
+    g_test_add_func("/qom/interface/intermediate_impl",
+                    interface_intermediate_test);
+
+    return g_test_run();
+}
diff --git a/tests/fdc-test.c b/tests/fdc-test.c
index 38b5b178d0..37096dcc13 100644
--- a/tests/fdc-test.c
+++ b/tests/fdc-test.c
@@ -518,7 +518,6 @@ static void fuzz_registers(void)
 int main(int argc, char **argv)
 {
     const char *arch = qtest_get_arch();
-    char *cmdline;
     int fd;
     int ret;
 
@@ -538,9 +537,7 @@ int main(int argc, char **argv)
     /* Run the tests */
     g_test_init(&argc, &argv, NULL);
 
-    cmdline = g_strdup_printf("-vnc none ");
-
-    qtest_start(cmdline);
+    qtest_start(NULL);
     qtest_irq_intercept_in(global_qtest, "ioapic");
     qtest_add_func("/fdc/cmos", test_cmos);
     qtest_add_func("/fdc/no_media_on_start", test_no_media_on_start);
diff --git a/tests/i440fx-test.c b/tests/i440fx-test.c
index 65c786ca1e..fa3e3d6b87 100644
--- a/tests/i440fx-test.c
+++ b/tests/i440fx-test.c
@@ -2,9 +2,11 @@
  * qtest I440FX test case
  *
  * Copyright IBM, Corp. 2012-2013
+ * Copyright Red Hat, Inc. 2013
  *
  * Authors:
  *  Anthony Liguori   <aliguori@us.ibm.com>
+ *  Laszlo Ersek      <lersek@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.
@@ -18,6 +20,11 @@
 
 #include <glib.h>
 #include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/mman.h>
+#include <stdlib.h>
 
 #define BROKEN 1
 
@@ -26,16 +33,32 @@
 typedef struct TestData
 {
     int num_cpus;
-    QPCIBus *bus;
 } TestData;
 
+typedef struct FirmwareTestFixture {
+    /* decides whether we're testing -bios or -pflash */
+    bool is_bios;
+} FirmwareTestFixture;
+
+static QPCIBus *test_start_get_bus(const TestData *s)
+{
+    char *cmdline;
+
+    cmdline = g_strdup_printf("-smp %d", s->num_cpus);
+    qtest_start(cmdline);
+    g_free(cmdline);
+    return qpci_init_pc();
+}
+
 static void test_i440fx_defaults(gconstpointer opaque)
 {
     const TestData *s = opaque;
+    QPCIBus *bus;
     QPCIDevice *dev;
     uint32_t value;
 
-    dev = qpci_device_find(s->bus, QPCI_DEVFN(0, 0));
+    bus = test_start_get_bus(s);
+    dev = qpci_device_find(bus, QPCI_DEVFN(0, 0));
     g_assert(dev != NULL);
 
     /* 3.2.2 */
@@ -119,6 +142,8 @@ static void test_i440fx_defaults(gconstpointer opaque)
     g_assert_cmpint(qpci_config_readb(dev, 0x91), ==, 0x00); /* ERRSTS */
     /* 3.2.26 */
     g_assert_cmpint(qpci_config_readb(dev, 0x93), ==, 0x00); /* TRC */
+
+    qtest_end();
 }
 
 #define PAM_RE 1
@@ -177,6 +202,7 @@ static void write_area(uint32_t start, uint32_t end, uint8_t value)
 static void test_i440fx_pam(gconstpointer opaque)
 {
     const TestData *s = opaque;
+    QPCIBus *bus;
     QPCIDevice *dev;
     int i;
     static struct {
@@ -199,7 +225,8 @@ static void test_i440fx_pam(gconstpointer opaque)
         { 0xEC000, 0xEFFFF }, /* BIOS Extension */
     };
 
-    dev = qpci_device_find(s->bus, QPCI_DEVFN(0, 0));
+    bus = test_start_get_bus(s);
+    dev = qpci_device_find(bus, QPCI_DEVFN(0, 0));
     g_assert(dev != NULL);
 
     for (i = 0; i < ARRAY_SIZE(pam_area); i++) {
@@ -252,34 +279,140 @@ static void test_i440fx_pam(gconstpointer opaque)
         /* Verify the area is not our new mask */
         g_assert(!verify_area(pam_area[i].start, pam_area[i].end, 0x82));
     }
+    qtest_end();
+}
+
+#define BLOB_SIZE ((size_t)65536)
+#define ISA_BIOS_MAXSZ ((size_t)(128 * 1024))
+
+/* Create a blob file, and return its absolute pathname as a dynamically
+ * allocated string.
+ * The file is closed before the function returns.
+ * In case of error, NULL is returned. The function prints the error message.
+ */
+static char *create_blob_file(void)
+{
+    int ret, fd;
+    char *pathname;
+    GError *error = NULL;
+
+    ret = -1;
+    fd = g_file_open_tmp("blob_XXXXXX", &pathname, &error);
+    if (fd == -1) {
+        fprintf(stderr, "unable to create blob file: %s\n", error->message);
+        g_error_free(error);
+    } else {
+        if (ftruncate(fd, BLOB_SIZE) == -1) {
+            fprintf(stderr, "ftruncate(\"%s\", %zu): %s\n", pathname,
+                    BLOB_SIZE, strerror(errno));
+        } else {
+            void *buf;
+
+            buf = mmap(NULL, BLOB_SIZE, PROT_WRITE, MAP_SHARED, fd, 0);
+            if (buf == MAP_FAILED) {
+                fprintf(stderr, "mmap(\"%s\", %zu): %s\n", pathname, BLOB_SIZE,
+                        strerror(errno));
+            } else {
+                size_t i;
+
+                for (i = 0; i < BLOB_SIZE; ++i) {
+                    ((uint8_t *)buf)[i] = i;
+                }
+                munmap(buf, BLOB_SIZE);
+                ret = 0;
+            }
+        }
+        close(fd);
+        if (ret == -1) {
+            unlink(pathname);
+            g_free(pathname);
+        }
+    }
+
+    return ret == -1 ? NULL : pathname;
+}
+
+static void test_i440fx_firmware(FirmwareTestFixture *fixture,
+                                 gconstpointer user_data)
+{
+    char *fw_pathname, *cmdline;
+    uint8_t *buf;
+    size_t i, isa_bios_size;
+
+    fw_pathname = create_blob_file();
+    g_assert(fw_pathname != NULL);
+
+    /* Better hope the user didn't put metacharacters in TMPDIR and co. */
+    cmdline = g_strdup_printf("-S %s %s",
+                              fixture->is_bios ? "-bios" : "-pflash",
+                              fw_pathname);
+    g_test_message("qemu cmdline: %s", cmdline);
+    qtest_start(cmdline);
+    g_free(cmdline);
+
+    /* Qemu has loaded the firmware (because qtest_start() only returns after
+     * the QMP handshake completes). We must unlink the firmware blob right
+     * here, because any assertion firing below would leak it in the
+     * filesystem. This is also the reason why we recreate the blob every time
+     * this function is invoked.
+     */
+    unlink(fw_pathname);
+    g_free(fw_pathname);
+
+    /* check below 4G */
+    buf = g_malloc0(BLOB_SIZE);
+    memread(0x100000000ULL - BLOB_SIZE, buf, BLOB_SIZE);
+    for (i = 0; i < BLOB_SIZE; ++i) {
+        g_assert_cmphex(buf[i], ==, (uint8_t)i);
+    }
+
+    /* check in ISA space too */
+    memset(buf, 0, BLOB_SIZE);
+    isa_bios_size = ISA_BIOS_MAXSZ < BLOB_SIZE ? ISA_BIOS_MAXSZ : BLOB_SIZE;
+    memread(0x100000 - isa_bios_size, buf, isa_bios_size);
+    for (i = 0; i < isa_bios_size; ++i) {
+        g_assert_cmphex(buf[i], ==,
+                        (uint8_t)((BLOB_SIZE - isa_bios_size) + i));
+    }
+
+    g_free(buf);
+    qtest_end();
+}
+
+static void add_firmware_test(const char *testpath,
+                              void (*setup_fixture)(FirmwareTestFixture *f,
+                                                    gconstpointer test_data))
+{
+    g_test_add(testpath, FirmwareTestFixture, NULL, setup_fixture,
+               test_i440fx_firmware, NULL);
+}
+
+static void request_bios(FirmwareTestFixture *fixture,
+                         gconstpointer user_data)
+{
+    fixture->is_bios = true;
+}
+
+static void request_pflash(FirmwareTestFixture *fixture,
+                           gconstpointer user_data)
+{
+    fixture->is_bios = false;
 }
 
 int main(int argc, char **argv)
 {
-    QTestState *s;
     TestData data;
-    char *cmdline;
     int ret;
 
     g_test_init(&argc, &argv, NULL);
 
     data.num_cpus = 1;
 
-    cmdline = g_strdup_printf("-smp %d", data.num_cpus);
-    s = qtest_start(cmdline);
-    g_free(cmdline);
-
-    data.bus = qpci_init_pc();
-
     g_test_add_data_func("/i440fx/defaults", &data, test_i440fx_defaults);
     g_test_add_data_func("/i440fx/pam", &data, test_i440fx_pam);
-    
+    add_firmware_test("/i440fx/firmware/bios", request_bios);
+    add_firmware_test("/i440fx/firmware/pflash", request_pflash);
 
     ret = g_test_run();
-
-    if (s) {
-        qtest_quit(s);
-    }
-
     return ret;
 }
diff --git a/tests/ide-test.c b/tests/ide-test.c
index d5cec5a1fc..4a0d97f197 100644
--- a/tests/ide-test.c
+++ b/tests/ide-test.c
@@ -380,7 +380,6 @@ static void test_bmdma_no_busmaster(void)
 static void test_bmdma_setup(void)
 {
     ide_test_start(
-        "-vnc none "
         "-drive file=%s,if=ide,serial=%s,cache=writeback "
         "-global ide-hd.ver=%s",
         tmp_path, "testdisk", "version");
@@ -410,7 +409,6 @@ static void test_identify(void)
     int ret;
 
     ide_test_start(
-        "-vnc none "
         "-drive file=%s,if=ide,serial=%s,cache=writeback "
         "-global ide-hd.ver=%s",
         tmp_path, "testdisk", "version");
@@ -455,7 +453,6 @@ static void test_flush(void)
     uint8_t data;
 
     ide_test_start(
-        "-vnc none "
         "-drive file=blkdebug::%s,if=ide,cache=writeback",
         tmp_path);
 
diff --git a/tests/qdev-monitor-test.c b/tests/qdev-monitor-test.c
index 33a8ea4b9c..ba7f9cc238 100644
--- a/tests/qdev-monitor-test.c
+++ b/tests/qdev-monitor-test.c
@@ -32,10 +32,8 @@ static void test_device_add(void)
                    "}}");
     g_assert(response);
     error = qdict_get_qdict(response, "error");
-    g_assert(!strcmp(qdict_get_try_str(error, "class") ?: "",
-                     "GenericError"));
     g_assert(!strcmp(qdict_get_try_str(error, "desc") ?: "",
-                     "Device initialization failed."));
+                     "Device needs media, but drive is empty"));
     QDECREF(response);
 
     /* Delete the drive */
diff --git a/tests/qemu-iotests/017 b/tests/qemu-iotests/017
index aba3faf712..3af3cdfbc3 100755
--- a/tests/qemu-iotests/017
+++ b/tests/qemu-iotests/017
@@ -43,6 +43,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
 _supported_fmt qcow qcow2 vmdk qed
 _supported_proto generic
 _supported_os Linux
+_unsupported_imgopts "subformat=monolithicFlat" "subformat=twoGbMaxExtentFlat"
 
 TEST_OFFSETS="0 4294967296"
 
diff --git a/tests/qemu-iotests/018 b/tests/qemu-iotests/018
index 15fcfe5670..6f7f0545d0 100755
--- a/tests/qemu-iotests/018
+++ b/tests/qemu-iotests/018
@@ -43,6 +43,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
 _supported_fmt qcow qcow2 vmdk qed
 _supported_proto generic
 _supported_os Linux
+_unsupported_imgopts "subformat=monolithicFlat" "subformat=twoGbMaxExtentFlat"
 
 TEST_OFFSETS="0 4294967296"
 
diff --git a/tests/qemu-iotests/019 b/tests/qemu-iotests/019
index 5bb18d0c0a..b43e70f3cb 100755
--- a/tests/qemu-iotests/019
+++ b/tests/qemu-iotests/019
@@ -47,6 +47,9 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
 _supported_fmt qcow qcow2 vmdk qed
 _supported_proto generic
 _supported_os Linux
+_unsupported_imgopts "subformat=monolithicFlat" \
+                     "subformat=twoGbMaxExtentFlat" \
+                     "subformat=twoGbMaxExtentSparse"
 
 TEST_OFFSETS="0 4294967296"
 CLUSTER_SIZE=65536
diff --git a/tests/qemu-iotests/020 b/tests/qemu-iotests/020
index b3c86d844e..73a0429481 100755
--- a/tests/qemu-iotests/020
+++ b/tests/qemu-iotests/020
@@ -45,6 +45,9 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
 _supported_fmt qcow qcow2 vmdk qed
 _supported_proto generic
 _supported_os Linux
+_unsupported_imgopts "subformat=monolithicFlat" \
+                     "subformat=twoGbMaxExtentFlat" \
+                     "subformat=twoGbMaxExtentSparse"
 
 TEST_OFFSETS="0 4294967296"
 
diff --git a/tests/qemu-iotests/034 b/tests/qemu-iotests/034
index 67f1959690..7349789583 100755
--- a/tests/qemu-iotests/034
+++ b/tests/qemu-iotests/034
@@ -41,6 +41,9 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
 _supported_fmt qcow qcow2 vmdk qed
 _supported_proto generic
 _supported_os Linux
+_unsupported_imgopts "subformat=monolithicFlat" \
+                     "subformat=twoGbMaxExtentFlat" \
+                     "subformat=twoGbMaxExtentSparse"
 
 CLUSTER_SIZE=4k
 size=128M
diff --git a/tests/qemu-iotests/037 b/tests/qemu-iotests/037
index 743bae33d3..e444349e6d 100755
--- a/tests/qemu-iotests/037
+++ b/tests/qemu-iotests/037
@@ -41,6 +41,9 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
 _supported_fmt qcow qcow2 vmdk qed
 _supported_proto generic
 _supported_os Linux
+_unsupported_imgopts "subformat=monolithicFlat" \
+                     "subformat=twoGbMaxExtentFlat" \
+                     "subformat=twoGbMaxExtentSparse"
 
 CLUSTER_SIZE=4k
 size=128M
diff --git a/tests/qemu-iotests/040 b/tests/qemu-iotests/040
index 18dcd61ef2..72eaad5b08 100755
--- a/tests/qemu-iotests/040
+++ b/tests/qemu-iotests/040
@@ -39,6 +39,29 @@ class ImageCommitTestCase(iotests.QMPTestCase):
         result = self.vm.qmp('query-block-jobs')
         self.assert_qmp(result, 'return', [])
 
+    def run_commit_test(self, top, base):
+        self.assert_no_active_commit()
+        result = self.vm.qmp('block-commit', device='drive0', top=top, base=base)
+        self.assert_qmp(result, 'return', {})
+
+        completed = False
+        while not completed:
+            for event in self.vm.get_qmp_events(wait=True):
+                if event['event'] == 'BLOCK_JOB_COMPLETED':
+                    self.assert_qmp(event, 'data/type', 'commit')
+                    self.assert_qmp(event, 'data/device', 'drive0')
+                    self.assert_qmp(event, 'data/offset', self.image_len)
+                    self.assert_qmp(event, 'data/len', self.image_len)
+                    completed = True
+                elif event['event'] == 'BLOCK_JOB_READY':
+                    self.assert_qmp(event, 'data/type', 'commit')
+                    self.assert_qmp(event, 'data/device', 'drive0')
+                    self.assert_qmp(event, 'data/len', self.image_len)
+                    self.vm.qmp('block-job-complete', device='drive0')
+
+        self.assert_no_active_commit()
+        self.vm.shutdown()
+
 class TestSingleDrive(ImageCommitTestCase):
     image_len = 1 * 1024 * 1024
     test_len = 1 * 1024 * 256
@@ -59,23 +82,7 @@ class TestSingleDrive(ImageCommitTestCase):
         os.remove(backing_img)
 
     def test_commit(self):
-        self.assert_no_active_commit()
-        result = self.vm.qmp('block-commit', device='drive0', top='%s' % mid_img)
-        self.assert_qmp(result, 'return', {})
-
-        completed = False
-        while not completed:
-            for event in self.vm.get_qmp_events(wait=True):
-                if event['event'] == 'BLOCK_JOB_COMPLETED':
-                    self.assert_qmp(event, 'data/type', 'commit')
-                    self.assert_qmp(event, 'data/device', 'drive0')
-                    self.assert_qmp(event, 'data/offset', self.image_len)
-                    self.assert_qmp(event, 'data/len', self.image_len)
-                    completed = True
-
-        self.assert_no_active_commit()
-        self.vm.shutdown()
-
+        self.run_commit_test(mid_img, backing_img)
         self.assertEqual(-1, qemu_io('-c', 'read -P 0xab 0 524288', backing_img).find("verification failed"))
         self.assertEqual(-1, qemu_io('-c', 'read -P 0xef 524288 524288', backing_img).find("verification failed"))
 
@@ -102,10 +109,9 @@ class TestSingleDrive(ImageCommitTestCase):
         self.assert_qmp(result, 'error/desc', 'Base \'badfile\' not found')
 
     def test_top_is_active(self):
-        self.assert_no_active_commit()
-        result = self.vm.qmp('block-commit', device='drive0', top='%s' % test_img, base='%s' % backing_img)
-        self.assert_qmp(result, 'error/class', 'GenericError')
-        self.assert_qmp(result, 'error/desc', 'Top image as the active layer is currently unsupported')
+        self.run_commit_test(test_img, backing_img)
+        self.assertEqual(-1, qemu_io('-c', 'read -P 0xab 0 524288', backing_img).find("verification failed"))
+        self.assertEqual(-1, qemu_io('-c', 'read -P 0xef 524288 524288', backing_img).find("verification failed"))
 
     def test_top_and_base_reversed(self):
         self.assert_no_active_commit()
@@ -166,23 +172,7 @@ class TestRelativePaths(ImageCommitTestCase):
                 raise
 
     def test_commit(self):
-        self.assert_no_active_commit()
-        result = self.vm.qmp('block-commit', device='drive0', top='%s' % self.mid_img)
-        self.assert_qmp(result, 'return', {})
-
-        completed = False
-        while not completed:
-            for event in self.vm.get_qmp_events(wait=True):
-                if event['event'] == 'BLOCK_JOB_COMPLETED':
-                    self.assert_qmp(event, 'data/type', 'commit')
-                    self.assert_qmp(event, 'data/device', 'drive0')
-                    self.assert_qmp(event, 'data/offset', self.image_len)
-                    self.assert_qmp(event, 'data/len', self.image_len)
-                    completed = True
-
-        self.assert_no_active_commit()
-        self.vm.shutdown()
-
+        self.run_commit_test(self.mid_img, self.backing_img)
         self.assertEqual(-1, qemu_io('-c', 'read -P 0xab 0 524288', self.backing_img_abs).find("verification failed"))
         self.assertEqual(-1, qemu_io('-c', 'read -P 0xef 524288 524288', self.backing_img_abs).find("verification failed"))
 
@@ -209,10 +199,9 @@ class TestRelativePaths(ImageCommitTestCase):
         self.assert_qmp(result, 'error/desc', 'Base \'badfile\' not found')
 
     def test_top_is_active(self):
-        self.assert_no_active_commit()
-        result = self.vm.qmp('block-commit', device='drive0', top='%s' % self.test_img, base='%s' % self.backing_img)
-        self.assert_qmp(result, 'error/class', 'GenericError')
-        self.assert_qmp(result, 'error/desc', 'Top image as the active layer is currently unsupported')
+        self.run_commit_test(self.test_img, self.backing_img)
+        self.assertEqual(-1, qemu_io('-c', 'read -P 0xab 0 524288', self.backing_img_abs).find("verification failed"))
+        self.assertEqual(-1, qemu_io('-c', 'read -P 0xef 524288 524288', self.backing_img_abs).find("verification failed"))
 
     def test_top_and_base_reversed(self):
         self.assert_no_active_commit()
@@ -229,6 +218,7 @@ class TestSetSpeed(ImageCommitTestCase):
         qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, mid_img)
         qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % mid_img, test_img)
         qemu_io('-c', 'write -P 0x1 0 512', test_img)
+        qemu_io('-c', 'write -P 0xef 524288 524288', mid_img)
         self.vm = iotests.VM().add_drive(test_img)
         self.vm.launch()
 
diff --git a/tests/qemu-iotests/051.out b/tests/qemu-iotests/051.out
index 49e95a20cf..d0c5173626 100644
--- a/tests/qemu-iotests/051.out
+++ b/tests/qemu-iotests/051.out
@@ -91,7 +91,6 @@ Testing: -drive if=virtio
 QEMU X.Y.Z monitor - type 'help' for more information
 (qemu) QEMU_PROG: -drive if=virtio: Device needs media, but drive is empty
 QEMU_PROG: -drive if=virtio: Device initialization failed.
-QEMU_PROG: -drive if=virtio: Device initialization failed.
 QEMU_PROG: -drive if=virtio: Device 'virtio-blk-pci' could not be initialized
 
 Testing: -drive if=scsi
@@ -223,7 +222,7 @@ QEMU X.Y.Z monitor - type 'help' for more information
 (qemu) qququiquit
 
 Testing: -drive file=TEST_DIR/t.qcow2,file.driver=qcow2
-QEMU_PROG: -drive file=TEST_DIR/t.qcow2,file.driver=qcow2: could not open disk image TEST_DIR/t.qcow2: Can't use 'qcow2' as a block driver for the protocol level
+QEMU_PROG: -drive file=TEST_DIR/t.qcow2,file.driver=qcow2: could not open disk image TEST_DIR/t.qcow2: Block format 'qcow2' used by device '' doesn't support the option 'filename'
 
 
 === Parsing protocol from file name ===
diff --git a/tests/qemu-iotests/059 b/tests/qemu-iotests/059
index 73941c3e61..2d604d3a91 100755
--- a/tests/qemu-iotests/059
+++ b/tests/qemu-iotests/059
@@ -42,6 +42,9 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
 _supported_fmt vmdk
 _supported_proto generic
 _supported_os Linux
+_unsupported_imgopts "subformat=monolithicFlat" \
+                     "subformat=twoGbMaxExtentFlat" \
+                     "subformat=twoGbMaxExtentSparse"
 
 capacity_offset=16
 granularity_offset=20
@@ -81,10 +84,37 @@ IMGOPTS="subformat=twoGbMaxExtentFlat" _make_test_img 1000G
 $QEMU_IMG info $TEST_IMG | _filter_testdir | sed -e 's/cid: [0-9]*/cid: XXXXXXXX/'
 
 echo
+echo "=== Testing malformed VMFS extent description line ==="
+cat >"$TEST_IMG" <<EOF
+# Disk DescriptorFile
+version=1
+CID=58ab4847
+parentCID=ffffffff
+createType="vmfs"
+
+# Extent description
+RW 12582912 VMFS "dummy.vmdk" 1
+EOF
+_img_info
+
+echo
+echo "=== Testing truncated sparse ==="
+IMGOPTS="subformat=monolithicSparse" _make_test_img 100G
+truncate -s 10M $TEST_IMG
+_img_info
+
+echo
 echo "=== Testing version 3 ==="
 _use_sample_img iotest-version3.vmdk.bz2
 _img_info
 
+echo
+echo "=== Testing 4TB monolithicFlat creation and IO ==="
+IMGOPTS="subformat=monolithicFlat" _make_test_img 4T
+_img_info
+$QEMU_IO -c "write -P 0xa 900G 512" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IO -c "read -v 900G 1024" "$TEST_IMG" | _filter_qemu_io
+
 # success, all done
 echo "*** done"
 rm -f $seq.full
diff --git a/tests/qemu-iotests/059.out b/tests/qemu-iotests/059.out
index 4ff935c6f4..4ffeb54710 100644
--- a/tests/qemu-iotests/059.out
+++ b/tests/qemu-iotests/059.out
@@ -2038,8 +2038,92 @@ Format specific information:
             filename: TEST_DIR/t-f500.vmdk
             format: FLAT
 
+=== Testing malformed VMFS extent description line ===
+qemu-img: Could not open 'TEST_DIR/t.IMGFMT': Invalid extent lines:
+RW 12582912 VMFS "dummy.IMGFMT" 1
+
+
+=== Testing truncated sparse ===
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=107374182400
+qemu-img: File truncated, expecting at least 13172736 bytes
+qemu-img: Could not open 'TEST_DIR/t.IMGFMT': Could not open 'TEST_DIR/t.IMGFMT': Wrong medium type
+
 === Testing version 3 ===
 image: TEST_DIR/iotest-version3.IMGFMT
 file format: IMGFMT
 virtual size: 1.0G (1073741824 bytes)
+
+=== Testing 4TB monolithicFlat creation and IO ===
+Formatting 'TEST_DIR/iotest-version3.IMGFMT', fmt=IMGFMT size=4398046511104
+image: TEST_DIR/iotest-version3.IMGFMT
+file format: IMGFMT
+virtual size: 4.0T (4398046511104 bytes)
+wrote 512/512 bytes at offset 966367641600
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+e100000000:  0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a  ................
+e100000010:  0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a  ................
+e100000020:  0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a  ................
+e100000030:  0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a  ................
+e100000040:  0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a  ................
+e100000050:  0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a  ................
+e100000060:  0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a  ................
+e100000070:  0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a  ................
+e100000080:  0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a  ................
+e100000090:  0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a  ................
+e1000000a0:  0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a  ................
+e1000000b0:  0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a  ................
+e1000000c0:  0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a  ................
+e1000000d0:  0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a  ................
+e1000000e0:  0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a  ................
+e1000000f0:  0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a  ................
+e100000100:  0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a  ................
+e100000110:  0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a  ................
+e100000120:  0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a  ................
+e100000130:  0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a  ................
+e100000140:  0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a  ................
+e100000150:  0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a  ................
+e100000160:  0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a  ................
+e100000170:  0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a  ................
+e100000180:  0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a  ................
+e100000190:  0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a  ................
+e1000001a0:  0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a  ................
+e1000001b0:  0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a  ................
+e1000001c0:  0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a  ................
+e1000001d0:  0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a  ................
+e1000001e0:  0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a  ................
+e1000001f0:  0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a  ................
+e100000200:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
+e100000210:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
+e100000220:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
+e100000230:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
+e100000240:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
+e100000250:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
+e100000260:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
+e100000270:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
+e100000280:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
+e100000290:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
+e1000002a0:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
+e1000002b0:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
+e1000002c0:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
+e1000002d0:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
+e1000002e0:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
+e1000002f0:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
+e100000300:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
+e100000310:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
+e100000320:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
+e100000330:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
+e100000340:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
+e100000350:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
+e100000360:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
+e100000370:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
+e100000380:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
+e100000390:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
+e1000003a0:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
+e1000003b0:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
+e1000003c0:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
+e1000003d0:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
+e1000003e0:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
+e1000003f0:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
+read 1024/1024 bytes at offset 966367641600
+1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
 *** done
diff --git a/tests/qemu-iotests/063 b/tests/qemu-iotests/063
index 2ab8f20e02..77503a2984 100755
--- a/tests/qemu-iotests/063
+++ b/tests/qemu-iotests/063
@@ -44,6 +44,9 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
 _supported_fmt qcow qcow2 vmdk qed raw
 _supported_proto generic
 _supported_os Linux
+_unsupported_imgopts "subformat=monolithicFlat" \
+                     "subformat=twoGbMaxExtentFlat" \
+                     "subformat=twoGbMaxExtentSparse"
 
 _make_test_img 4M
 
diff --git a/tests/qemu-iotests/069 b/tests/qemu-iotests/069
index 3042803a81..50347d91d2 100755
--- a/tests/qemu-iotests/069
+++ b/tests/qemu-iotests/069
@@ -41,6 +41,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
 _supported_fmt cow qed qcow qcow2 vmdk
 _supported_proto generic
 _supported_os Linux
+_unsupported_imgopts "subformat=monolithicFlat" "subformat=twoGbMaxExtentFlat"
 
 IMG_SIZE=128K
 
diff --git a/tests/qemu-iotests/071 b/tests/qemu-iotests/071
new file mode 100755
index 0000000000..2a22546e1a
--- /dev/null
+++ b/tests/qemu-iotests/071
@@ -0,0 +1,239 @@
+#!/bin/bash
+#
+# Test case for the QMP blkdebug and blkverify interfaces
+#
+# Copyright (C) 2013 Red Hat, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+# creator
+owner=mreitz@redhat.com
+
+seq="$(basename $0)"
+echo "QA output created by $seq"
+
+here="$PWD"
+tmp=/tmp/$$
+status=1	# failure is the default!
+
+_cleanup()
+{
+	_cleanup_test_img
+}
+trap "_cleanup; exit \$status" 0 1 2 3 15
+
+# get standard environment, filters and checks
+. ./common.rc
+. ./common.filter
+
+_supported_fmt generic
+_supported_proto generic
+_supported_os Linux
+
+function do_run_qemu()
+{
+    echo Testing: "$@" | _filter_imgfmt
+    $QEMU -nographic -qmp stdio -serial none "$@"
+    echo
+}
+
+function run_qemu()
+{
+    do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qmp | _filter_qemu_io
+}
+
+IMG_SIZE=64M
+
+echo
+echo "=== Testing blkverify through filename ==="
+echo
+
+TEST_IMG="$TEST_IMG.base" IMGOPTS="" IMGFMT="raw" _make_test_img $IMG_SIZE |\
+    _filter_imgfmt
+_make_test_img $IMG_SIZE
+$QEMU_IO -c "open -o file.driver=blkverify,file.raw.filename=$TEST_IMG.base $TEST_IMG" \
+         -c 'read 0 512' -c 'write -P 42 0x38000 512' -c 'read -P 42 0x38000 512' | _filter_qemu_io
+
+$QEMU_IO -c 'write -P 42 0 512' "$TEST_IMG" | _filter_qemu_io
+
+$QEMU_IO -c "open -o file.driver=blkverify,file.raw.filename=$TEST_IMG.base $TEST_IMG" \
+         -c 'read -P 42 0 512' | _filter_qemu_io
+
+echo
+echo "=== Testing blkverify through file blockref ==="
+echo
+
+TEST_IMG="$TEST_IMG.base" IMGOPTS="" IMGFMT="raw" _make_test_img $IMG_SIZE |\
+    _filter_imgfmt
+_make_test_img $IMG_SIZE
+$QEMU_IO -c "open -o file.driver=blkverify,file.raw.filename=$TEST_IMG.base,file.test.driver=$IMGFMT,file.test.file.filename=$TEST_IMG" \
+         -c 'read 0 512' -c 'write -P 42 0x38000 512' -c 'read -P 42 0x38000 512' | _filter_qemu_io
+
+$QEMU_IO -c 'write -P 42 0 512' "$TEST_IMG" | _filter_qemu_io
+
+$QEMU_IO -c "open -o file.driver=blkverify,file.raw.filename=$TEST_IMG.base $TEST_IMG" \
+         -c 'read -P 42 0 512' | _filter_qemu_io
+
+echo
+echo "=== Testing blkdebug through filename ==="
+echo
+
+$QEMU_IO -c "open -o file.driver=blkdebug,file.inject-error.event=l2_load $TEST_IMG" \
+         -c 'read -P 42 0x38000 512'
+
+echo
+echo "=== Testing blkdebug through file blockref ==="
+echo
+
+$QEMU_IO -c "open -o driver=$IMGFMT,file.driver=blkdebug,file.inject-error.event=l2_load,file.image.filename=$TEST_IMG" \
+         -c 'read -P 42 0x38000 512'
+
+echo
+echo "=== Testing blkdebug on existing block device ==="
+echo
+
+run_qemu -drive "file=$TEST_IMG,format=raw,if=none,id=drive0" <<EOF
+{ "execute": "qmp_capabilities" }
+{ "execute": "blockdev-add",
+    "arguments": {
+        "options": {
+            "driver": "$IMGFMT",
+            "id": "drive0-debug",
+            "file": {
+                "driver": "blkdebug",
+                "image": "drive0",
+                "inject-error": [{
+                    "event": "l2_load"
+                }]
+            }
+        }
+    }
+}
+{ "execute": "human-monitor-command",
+    "arguments": {
+        "command-line": 'qemu-io drive0-debug "read 0 512"'
+    }
+}
+{ "execute": "quit" }
+EOF
+
+echo
+echo "=== Testing blkverify on existing block device ==="
+echo
+
+run_qemu -drive "file=$TEST_IMG,format=$IMGFMT,if=none,id=drive0" <<EOF
+{ "execute": "qmp_capabilities" }
+{ "execute": "blockdev-add",
+    "arguments": {
+        "options": {
+            "driver": "blkverify",
+            "id": "drive0-verify",
+            "test": "drive0",
+            "raw": {
+                "driver": "raw",
+                "file": {
+                    "driver": "file",
+                    "filename": "$TEST_IMG.base"
+                }
+            }
+        }
+    }
+}
+{ "execute": "human-monitor-command",
+    "arguments": {
+        "command-line": 'qemu-io drive0-verify "read 0 512"'
+    }
+}
+{ "execute": "quit" }
+EOF
+
+echo
+echo "=== Testing blkverify on existing raw block device ==="
+echo
+
+run_qemu -drive "file=$TEST_IMG.base,if=none,id=drive0" <<EOF
+{ "execute": "qmp_capabilities" }
+{ "execute": "blockdev-add",
+    "arguments": {
+        "options": {
+            "driver": "blkverify",
+            "id": "drive0-verify",
+            "test": {
+                "driver": "$IMGFMT",
+                "file": {
+                    "driver": "file",
+                    "filename": "$TEST_IMG"
+                }
+            },
+            "raw": "drive0"
+        }
+    }
+}
+{ "execute": "human-monitor-command",
+    "arguments": {
+        "command-line": 'qemu-io drive0-verify "read 0 512"'
+    }
+}
+{ "execute": "quit" }
+EOF
+
+echo
+echo "=== Testing blkdebug's set-state through QMP ==="
+echo
+
+run_qemu -drive "file=$TEST_IMG,format=raw,if=none,id=drive0" <<EOF
+{ "execute": "qmp_capabilities" }
+{ "execute": "blockdev-add",
+    "arguments": {
+        "options": {
+            "driver": "$IMGFMT",
+            "id": "drive0-debug",
+            "file": {
+                "driver": "blkdebug",
+                "image": "drive0",
+                "inject-error": [{
+                    "event": "read_aio",
+                    "state": 42
+                }],
+                "set-state": [{
+                    "event": "write_aio",
+                    "new_state": 42
+                }]
+            }
+        }
+    }
+}
+{ "execute": "human-monitor-command",
+    "arguments": {
+        "command-line": 'qemu-io drive0-debug "read 0 512"'
+    }
+}
+{ "execute": "human-monitor-command",
+    "arguments": {
+        "command-line": 'qemu-io drive0-debug "write 0 512"'
+    }
+}
+{ "execute": "human-monitor-command",
+    "arguments": {
+        "command-line": 'qemu-io drive0-debug "read 0 512"'
+    }
+}
+{ "execute": "quit" }
+EOF
+
+# success, all done
+echo "*** done"
+rm -f $seq.full
+status=0
diff --git a/tests/qemu-iotests/071.out b/tests/qemu-iotests/071.out
new file mode 100644
index 0000000000..5f840a9980
--- /dev/null
+++ b/tests/qemu-iotests/071.out
@@ -0,0 +1,90 @@
+QA output created by 071
+
+=== Testing blkverify through filename ===
+
+Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864 
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 
+read 512/512 bytes at offset 0
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 512/512 bytes at offset 229376
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 229376
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 512/512 bytes at offset 0
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+blkverify: read sector_num=0 nb_sectors=4 contents mismatch in sector 0
+
+=== Testing blkverify through file blockref ===
+
+Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864 
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 
+read 512/512 bytes at offset 0
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 512/512 bytes at offset 229376
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 229376
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 512/512 bytes at offset 0
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+blkverify: read sector_num=0 nb_sectors=4 contents mismatch in sector 0
+
+=== Testing blkdebug through filename ===
+
+read failed: Input/output error
+
+=== Testing blkdebug through file blockref ===
+
+read failed: Input/output error
+
+=== Testing blkdebug on existing block device ===
+
+Testing: -drive file=TEST_DIR/t.IMGFMT,format=raw,if=none,id=drive0
+QMP_VERSION
+{"return": {}}
+{"return": {}}
+read failed: Input/output error
+{"return": ""}
+{"return": {}}
+{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN"}
+{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "DEVICE_TRAY_MOVED", "data": {"device": "ide1-cd0", "tray-open": true}}
+{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "DEVICE_TRAY_MOVED", "data": {"device": "floppy0", "tray-open": true}}
+
+
+=== Testing blkverify on existing block device ===
+
+Testing: -drive file=TEST_DIR/t.IMGFMT,format=IMGFMT,if=none,id=drive0
+QMP_VERSION
+{"return": {}}
+{"return": {}}
+blkverify: read sector_num=0 nb_sectors=1 contents mismatch in sector 0
+
+
+=== Testing blkverify on existing raw block device ===
+
+Testing: -drive file=TEST_DIR/t.IMGFMT.base,if=none,id=drive0
+QMP_VERSION
+{"return": {}}
+{"return": {}}
+blkverify: read sector_num=0 nb_sectors=1 contents mismatch in sector 0
+
+
+=== Testing blkdebug's set-state through QMP ===
+
+Testing: -drive file=TEST_DIR/t.IMGFMT,format=raw,if=none,id=drive0
+QMP_VERSION
+{"return": {}}
+{"return": {}}
+read 512/512 bytes at offset 0
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+{"return": ""}
+wrote 512/512 bytes at offset 0
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+{"return": ""}
+read failed: Input/output error
+{"return": ""}
+{"return": {}}
+{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN"}
+{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "DEVICE_TRAY_MOVED", "data": {"device": "ide1-cd0", "tray-open": true}}
+{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "DEVICE_TRAY_MOVED", "data": {"device": "floppy0", "tray-open": true}}
+
+*** done
diff --git a/tests/qemu-iotests/072 b/tests/qemu-iotests/072
new file mode 100755
index 0000000000..a3876c2161
--- /dev/null
+++ b/tests/qemu-iotests/072
@@ -0,0 +1,69 @@
+#!/bin/bash
+#
+# Test case for nested image formats
+#
+# Copyright (C) 2013 Red Hat, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+# creator
+owner=mreitz@redhat.com
+
+seq="$(basename $0)"
+echo "QA output created by $seq"
+
+here="$PWD"
+tmp=/tmp/$$
+status=1	# failure is the default!
+
+_cleanup()
+{
+	_cleanup_test_img
+}
+trap "_cleanup; exit \$status" 0 1 2 3 15
+
+# get standard environment, filters and checks
+. ./common.rc
+. ./common.filter
+
+_supported_fmt vpc vmdk vhdx vdi qed qcow2 qcow cow
+_supported_proto generic
+_supported_os Linux
+
+IMG_SIZE=64M
+
+echo
+echo "=== Testing nested image formats ==="
+echo
+
+TEST_IMG="$TEST_IMG.base" _make_test_img $IMG_SIZE
+
+$QEMU_IO -c 'write -P 42 0 512' -c 'write -P 23 512 512' \
+         -c 'write -P 66 1024 512' "$TEST_IMG.base" | _filter_qemu_io
+
+$QEMU_IMG convert -f raw -O $IMGFMT "$TEST_IMG.base" "$TEST_IMG"
+
+$QEMU_IO -c "open -o driver=$IMGFMT,file.driver=$IMGFMT,file.file.filename=$TEST_IMG" \
+         -c 'read -P 42 0 512' -c 'read -P 23 512 512' \
+         -c 'read -P 66 1024 512' | _filter_qemu_io
+
+# When not giving any format, qemu should open only one "layer". Therefore, this
+# should not work for any image formats with a header.
+$QEMU_IO -c 'read -P 42 0 512' "$TEST_IMG" | _filter_qemu_io
+
+# success, all done
+echo "*** done"
+rm -f $seq.full
+status=0
diff --git a/tests/qemu-iotests/072.out b/tests/qemu-iotests/072.out
new file mode 100644
index 0000000000..efe577c1c0
--- /dev/null
+++ b/tests/qemu-iotests/072.out
@@ -0,0 +1,21 @@
+QA output created by 072
+
+=== Testing nested image formats ===
+
+Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864 
+wrote 512/512 bytes at offset 0
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 512/512 bytes at offset 512
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 512/512 bytes at offset 1024
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 0
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 512
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 1024
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Pattern verification failed at offset 0, 512 bytes
+read 512/512 bytes at offset 0
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+*** done
diff --git a/tests/qemu-iotests/077 b/tests/qemu-iotests/077
new file mode 100755
index 0000000000..bbf7b5145a
--- /dev/null
+++ b/tests/qemu-iotests/077
@@ -0,0 +1,278 @@
+#!/bin/bash
+#
+# Test concurrent pread/pwrite
+#
+# Copyright (C) 2014 Red Hat, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+# creator
+owner=kwolf@redhat.com
+
+seq=`basename $0`
+echo "QA output created by $seq"
+
+here=`pwd`
+tmp=/tmp/$$
+status=1	# failure is the default!
+
+_cleanup()
+{
+	_cleanup_test_img
+}
+trap "_cleanup; exit \$status" 0 1 2 3 15
+
+# get standard environment, filters and checks
+. ./common.rc
+. ./common.filter
+
+_supported_fmt generic
+_supported_proto generic
+_supported_os Linux
+
+CLUSTER_SIZE=4k
+size=128M
+
+_make_test_img $size
+
+echo
+echo "== Some concurrent requests involving RMW =="
+
+function test_io()
+{
+echo "open -o file.align=4k blkdebug::$TEST_IMG"
+# A simple RMW request
+cat  <<EOF
+aio_write -P 10 0x200 0x200
+aio_flush
+EOF
+
+# Sequential RMW requests on the same physical sector
+off=0x1000
+for ev in "head" "after_head" "tail" "after_tail"; do
+cat  <<EOF
+break pwritev_rmw.$ev A
+aio_write -P 10 $((off + 0x200)) 0x200
+wait_break A
+aio_write -P 11 $((off + 0x400)) 0x200
+sleep 100
+resume A
+aio_flush
+EOF
+off=$((off + 0x1000))
+done
+
+# Chained dependencies
+cat  <<EOF
+break pwritev_rmw.after_tail A
+aio_write -P 10 0x5000 0x200
+wait_break A
+aio_write -P 11 0x5200 0x200
+aio_write -P 12 0x5400 0x200
+aio_write -P 13 0x5600 0x200
+aio_write -P 14 0x5800 0x200
+aio_write -P 15 0x5a00 0x200
+aio_write -P 16 0x5c00 0x200
+aio_write -P 17 0x5e00 0x200
+sleep 100
+resume A
+aio_flush
+EOF
+
+# Overlapping multiple requests
+cat  <<EOF
+break pwritev_rmw.after_tail A
+aio_write -P 10 0x6000 0x200
+wait_break A
+break pwritev_rmw.after_head B
+aio_write -P 10 0x7e00 0x200
+wait_break B
+aio_write -P 11 0x6800 0x1000
+resume A
+sleep 100
+resume B
+aio_flush
+EOF
+
+cat  <<EOF
+break pwritev_rmw.after_tail A
+aio_write -P 10 0x8000 0x200
+wait_break A
+break pwritev_rmw.after_head B
+aio_write -P 10 0x9e00 0x200
+wait_break B
+aio_write -P 11 0x8800 0x1000
+resume B
+sleep 100
+resume A
+aio_flush
+EOF
+
+cat  <<EOF
+break pwritev_rmw.after_tail A
+aio_write -P 10 0xa000 0x200
+wait_break A
+aio_write -P 11 0xa800 0x1000
+break pwritev_rmw.after_head B
+aio_write -P 10 0xbe00 0x200
+wait_break B
+resume A
+sleep 100
+resume B
+aio_flush
+EOF
+
+cat  <<EOF
+break pwritev_rmw.after_tail A
+aio_write -P 10 0xc000 0x200
+wait_break A
+aio_write -P 11 0xc800 0x1000
+break pwritev_rmw.after_head B
+aio_write -P 10 0xde00 0x200
+wait_break B
+resume B
+sleep 100
+resume A
+aio_flush
+EOF
+
+# Only RMW for the tail part
+cat  <<EOF
+break pwritev_rmw.after_tail A
+aio_write -P 10 0xe000 0x1800
+wait_break A
+aio_write -P 11 0xf000 0xc00
+sleep 100
+resume A
+aio_flush
+EOF
+
+cat  <<EOF
+break pwritev A
+aio_write -P 10 0x10000 0x800
+wait_break A
+break pwritev_rmw.after_tail B
+aio_write -P 11 0x10000 0x400
+break pwritev_done C
+resume A
+wait_break C
+resume C
+sleep 100
+wait_break B
+resume B
+aio_flush
+EOF
+
+cat  <<EOF
+break pwritev A
+aio_write -P 10 0x11000 0x800
+wait_break A
+aio_write -P 11 0x11000 0x1000
+sleep 100
+resume A
+aio_flush
+EOF
+}
+
+test_io | $QEMU_IO  | _filter_qemu_io | \
+    sed -e 's,[0-9/]* bytes at offset [0-9]*,XXX/XXX bytes at offset XXX,g' \
+        -e 's/^[0-9]* \(bytes\|KiB\)/XXX bytes/' \
+        -e '/Suspended/d'
+
+echo
+echo "== Verify image content =="
+
+function verify_io()
+{
+    # A simple RMW request
+    echo read -P 0       0 0x200
+    echo read -P 10  0x200 0x200
+    echo read -P 0   0x400 0xc00
+
+    # Sequential RMW requests on the same physical sector
+    echo read -P 0  0x1000 0x200
+    echo read -P 10 0x1200 0x200
+    echo read -P 11 0x1400 0x200
+    echo read -P 0  0x1600 0xa00
+
+    echo read -P 0  0x2000 0x200
+    echo read -P 10 0x2200 0x200
+    echo read -P 11 0x2400 0x200
+    echo read -P 0  0x2600 0xa00
+
+    echo read -P 0  0x3000 0x200
+    echo read -P 10 0x3200 0x200
+    echo read -P 11 0x3400 0x200
+    echo read -P 0  0x3600 0xa00
+
+    echo read -P 0  0x4000 0x200
+    echo read -P 10 0x4200 0x200
+    echo read -P 11 0x4400 0x200
+    echo read -P 0  0x4600 0xa00
+
+    # Chained dependencies
+    echo read -P 10 0x5000 0x200
+    echo read -P 11 0x5200 0x200
+    echo read -P 12 0x5400 0x200
+    echo read -P 13 0x5600 0x200
+    echo read -P 14 0x5800 0x200
+    echo read -P 15 0x5a00 0x200
+    echo read -P 16 0x5c00 0x200
+    echo read -P 17 0x5e00 0x200
+
+    # Overlapping multiple requests
+    echo read -P 10 0x6000 0x200
+    echo read -P  0 0x6200 0x600
+    echo read -P 11 0x6800 0x1000
+    echo read -P  0 0x7800 0x600
+    echo read -P 10 0x7e00 0x200
+
+    echo read -P 10 0x8000 0x200
+    echo read -P  0 0x8200 0x600
+    echo read -P 11 0x8800 0x1000
+    echo read -P  0 0x9800 0x600
+    echo read -P 10 0x9e00 0x200
+
+    echo read -P 10 0xa000 0x200
+    echo read -P  0 0xa200 0x600
+    echo read -P 11 0xa800 0x1000
+    echo read -P  0 0xb800 0x600
+    echo read -P 10 0xbe00 0x200
+
+    echo read -P 10 0xc000 0x200
+    echo read -P  0 0xc200 0x600
+    echo read -P 11 0xc800 0x1000
+    echo read -P  0 0xd800 0x600
+    echo read -P 10 0xde00 0x200
+
+    # Only RMW for the tail part
+    echo read -P 10 0xe000 0x1000
+    echo read -P 11 0xf800 0x400
+    echo read -P  0 0xfc00 0x400
+
+    echo read -P 11 0x10000 0x400
+    echo read -P 10 0x10400 0x400
+
+    echo read -P 11 0x11800 0x800
+}
+
+verify_io | $QEMU_IO "$TEST_IMG" | _filter_qemu_io
+
+_check_test_img
+
+# success, all done
+echo "*** done"
+rm -f $seq.full
+status=0
diff --git a/tests/qemu-iotests/077.out b/tests/qemu-iotests/077.out
new file mode 100644
index 0000000000..ab612344d6
--- /dev/null
+++ b/tests/qemu-iotests/077.out
@@ -0,0 +1,202 @@
+QA output created by 077
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 
+
+== Some concurrent requests involving RMW ==
+wrote XXX/XXX bytes at offset XXX
+XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+blkdebug: Resuming request 'A'
+wrote XXX/XXX bytes at offset XXX
+XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote XXX/XXX bytes at offset XXX
+XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+blkdebug: Resuming request 'A'
+wrote XXX/XXX bytes at offset XXX
+XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote XXX/XXX bytes at offset XXX
+XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+blkdebug: Resuming request 'A'
+wrote XXX/XXX bytes at offset XXX
+XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote XXX/XXX bytes at offset XXX
+XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+blkdebug: Resuming request 'A'
+wrote XXX/XXX bytes at offset XXX
+XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote XXX/XXX bytes at offset XXX
+XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+blkdebug: Resuming request 'A'
+wrote XXX/XXX bytes at offset XXX
+XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote XXX/XXX bytes at offset XXX
+XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote XXX/XXX bytes at offset XXX
+XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote XXX/XXX bytes at offset XXX
+XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote XXX/XXX bytes at offset XXX
+XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote XXX/XXX bytes at offset XXX
+XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote XXX/XXX bytes at offset XXX
+XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote XXX/XXX bytes at offset XXX
+XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+blkdebug: Resuming request 'A'
+wrote XXX/XXX bytes at offset XXX
+XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+blkdebug: Resuming request 'B'
+wrote XXX/XXX bytes at offset XXX
+XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote XXX/XXX bytes at offset XXX
+XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+blkdebug: Resuming request 'B'
+wrote XXX/XXX bytes at offset XXX
+XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+blkdebug: Resuming request 'A'
+wrote XXX/XXX bytes at offset XXX
+XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote XXX/XXX bytes at offset XXX
+XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+blkdebug: Resuming request 'A'
+wrote XXX/XXX bytes at offset XXX
+XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+blkdebug: Resuming request 'B'
+wrote XXX/XXX bytes at offset XXX
+XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote XXX/XXX bytes at offset XXX
+XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+blkdebug: Resuming request 'B'
+wrote XXX/XXX bytes at offset XXX
+XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+blkdebug: Resuming request 'A'
+wrote XXX/XXX bytes at offset XXX
+XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote XXX/XXX bytes at offset XXX
+XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+blkdebug: Resuming request 'A'
+wrote XXX/XXX bytes at offset XXX
+XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote XXX/XXX bytes at offset XXX
+XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+blkdebug: Resuming request 'A'
+blkdebug: Resuming request 'C'
+wrote XXX/XXX bytes at offset XXX
+XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+blkdebug: Resuming request 'B'
+wrote XXX/XXX bytes at offset XXX
+XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+blkdebug: Resuming request 'A'
+wrote XXX/XXX bytes at offset XXX
+XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote XXX/XXX bytes at offset XXX
+XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+== Verify image content ==
+read 512/512 bytes at offset 0
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 512
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 3072/3072 bytes at offset 1024
+3 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 4096
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 4608
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 5120
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 2560/2560 bytes at offset 5632
+2.500 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 8192
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 8704
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 9216
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 2560/2560 bytes at offset 9728
+2.500 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 12288
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 12800
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 13312
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 2560/2560 bytes at offset 13824
+2.500 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 16384
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 16896
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 17408
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 2560/2560 bytes at offset 17920
+2.500 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 20480
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 20992
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 21504
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 22016
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 22528
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 23040
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 23552
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 24064
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 24576
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 1536/1536 bytes at offset 25088
+1.500 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 4096/4096 bytes at offset 26624
+4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 1536/1536 bytes at offset 30720
+1.500 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 32256
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 32768
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 1536/1536 bytes at offset 33280
+1.500 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 4096/4096 bytes at offset 34816
+4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 1536/1536 bytes at offset 38912
+1.500 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 40448
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 40960
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 1536/1536 bytes at offset 41472
+1.500 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 4096/4096 bytes at offset 43008
+4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 1536/1536 bytes at offset 47104
+1.500 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 48640
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 49152
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 1536/1536 bytes at offset 49664
+1.500 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 4096/4096 bytes at offset 51200
+4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 1536/1536 bytes at offset 55296
+1.500 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 56832
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 4096/4096 bytes at offset 57344
+4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 1024/1024 bytes at offset 63488
+1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 1024/1024 bytes at offset 64512
+1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 1024/1024 bytes at offset 65536
+1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 1024/1024 bytes at offset 66560
+1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 2048/2048 bytes at offset 71680
+2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+No errors were found on the image.
+*** done
diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc
index 28ba0d9ad5..0f68156400 100644
--- a/tests/qemu-iotests/common.rc
+++ b/tests/qemu-iotests/common.rc
@@ -170,6 +170,17 @@ _make_test_img()
     fi
 }
 
+_rm_test_img()
+{
+    local img=$1
+    if [ "$IMGFMT" = "vmdk" ]; then
+        # Remove all the extents for vmdk
+        $QEMU_IMG info $img 2>/dev/null | grep 'filename:' | cut -f 2 -d: \
+            | xargs -I {} rm -f "{}"
+    fi
+    rm -f $img
+}
+
 _cleanup_test_img()
 {
     case "$IMGPROTO" in
@@ -179,9 +190,9 @@ _cleanup_test_img()
             rm -f "$TEST_IMG_FILE"
             ;;
         file)
-            rm -f "$TEST_DIR/t.$IMGFMT"
-            rm -f "$TEST_DIR/t.$IMGFMT.orig"
-            rm -f "$TEST_DIR/t.$IMGFMT.base"
+            _rm_test_img "$TEST_DIR/t.$IMGFMT"
+            _rm_test_img "$TEST_DIR/t.$IMGFMT.orig"
+            _rm_test_img "$TEST_DIR/t.$IMGFMT.base"
             if [ -n "$SAMPLE_IMG_FILE" ]
             then
                 rm -f "$TEST_DIR/$SAMPLE_IMG_FILE"
@@ -406,6 +417,17 @@ _default_cache_mode()
     fi
 }
 
+_unsupported_imgopts()
+{
+    for bad_opt
+    do
+        if echo "$IMGOPTS" | grep -q 2>/dev/null "$bad_opt"
+        then
+            _notrun "not suitable for image option: $bad_opt"
+        fi
+    done
+}
+
 # this test requires that a specified command (executable) exists
 #
 _require_command()
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
index cc750c986e..03c762fb4f 100644
--- a/tests/qemu-iotests/group
+++ b/tests/qemu-iotests/group
@@ -77,5 +77,8 @@
 068 rw auto
 069 rw auto
 070 rw auto
+071 rw auto
+072 rw auto
 073 rw auto
 074 rw auto
+077 rw auto
diff --git a/tests/qom-test.c b/tests/qom-test.c
index 499be40261..5e5af7a50f 100644
--- a/tests/qom-test.c
+++ b/tests/qom-test.c
@@ -70,6 +70,8 @@ static const char *arm_machines[] = {
     "xilinx-zynq-a9",
     "highbank",
     "midway",
+    "canon-a1100",
+    "cubieboard",
 };
 
 static const char *cris_machines[] = {
diff --git a/tests/test-vmstate.c b/tests/test-vmstate.c
new file mode 100644
index 0000000000..75cd1a1fd4
--- /dev/null
+++ b/tests/test-vmstate.c
@@ -0,0 +1,357 @@
+/*
+ *  Test code for VMState
+ *
+ *  Copyright (c) 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <glib.h>
+
+#include "qemu-common.h"
+#include "migration/migration.h"
+#include "migration/vmstate.h"
+#include "block/coroutine.h"
+
+char temp_file[] = "/tmp/vmst.test.XXXXXX";
+int temp_fd;
+
+/* Fake yield_until_fd_readable() implementation so we don't have to pull the
+ * coroutine code as dependency.
+ */
+void yield_until_fd_readable(int fd)
+{
+    fd_set fds;
+    FD_ZERO(&fds);
+    FD_SET(fd, &fds);
+    select(fd + 1, &fds, NULL, NULL, NULL);
+}
+
+/* Duplicate temp_fd and seek to the beginning of the file */
+static int dup_temp_fd(bool truncate)
+{
+    int fd = dup(temp_fd);
+    lseek(fd, 0, SEEK_SET);
+    if (truncate) {
+        g_assert_cmpint(ftruncate(fd, 0), ==, 0);
+    }
+    return fd;
+}
+
+typedef struct TestSruct {
+    uint32_t a, b, c, e;
+    uint64_t d, f;
+    bool skip_c_e;
+} TestStruct;
+
+
+static const VMStateDescription vmstate_simple = {
+    .name = "test",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_UINT32(a, TestStruct),
+        VMSTATE_UINT32(b, TestStruct),
+        VMSTATE_UINT32(c, TestStruct),
+        VMSTATE_UINT64(d, TestStruct),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void test_simple_save(void)
+{
+    QEMUFile *fsave = qemu_fdopen(dup_temp_fd(true), "wb");
+    TestStruct obj = { .a = 1, .b = 2, .c = 3, .d = 4 };
+    vmstate_save_state(fsave, &vmstate_simple, &obj);
+    g_assert(!qemu_file_get_error(fsave));
+    qemu_fclose(fsave);
+
+    QEMUFile *loading = qemu_fdopen(dup_temp_fd(false), "rb");
+    uint8_t expected[] = {
+        0, 0, 0, 1, /* a */
+        0, 0, 0, 2, /* b */
+        0, 0, 0, 3, /* c */
+        0, 0, 0, 0, 0, 0, 0, 4, /* d */
+    };
+    uint8_t result[sizeof(expected)];
+    g_assert_cmpint(qemu_get_buffer(loading, result, sizeof(result)), ==,
+                    sizeof(result));
+    g_assert(!qemu_file_get_error(loading));
+    g_assert_cmpint(memcmp(result, expected, sizeof(result)), ==, 0);
+
+    /* Must reach EOF */
+    qemu_get_byte(loading);
+    g_assert_cmpint(qemu_file_get_error(loading), ==, -EIO);
+
+    qemu_fclose(loading);
+}
+
+static void test_simple_load(void)
+{
+    QEMUFile *fsave = qemu_fdopen(dup_temp_fd(true), "wb");
+    uint8_t buf[] = {
+        0, 0, 0, 10,             /* a */
+        0, 0, 0, 20,             /* b */
+        0, 0, 0, 30,             /* c */
+        0, 0, 0, 0, 0, 0, 0, 40, /* d */
+        QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */
+    };
+    qemu_put_buffer(fsave, buf, sizeof(buf));
+    qemu_fclose(fsave);
+
+    QEMUFile *loading = qemu_fdopen(dup_temp_fd(false), "rb");
+    TestStruct obj;
+    vmstate_load_state(loading, &vmstate_simple, &obj, 1);
+    g_assert(!qemu_file_get_error(loading));
+    g_assert_cmpint(obj.a, ==, 10);
+    g_assert_cmpint(obj.b, ==, 20);
+    g_assert_cmpint(obj.c, ==, 30);
+    g_assert_cmpint(obj.d, ==, 40);
+    qemu_fclose(loading);
+}
+
+static const VMStateDescription vmstate_versioned = {
+    .name = "test",
+    .version_id = 2,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField []) {
+        VMSTATE_UINT32(a, TestStruct),
+        VMSTATE_UINT32_V(b, TestStruct, 2), /* Versioned field in the middle, so
+                                             * we catch bugs more easily.
+                                             */
+        VMSTATE_UINT32(c, TestStruct),
+        VMSTATE_UINT64(d, TestStruct),
+        VMSTATE_UINT32_V(e, TestStruct, 2),
+        VMSTATE_UINT64_V(f, TestStruct, 2),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void test_load_v1(void)
+{
+    QEMUFile *fsave = qemu_fdopen(dup_temp_fd(true), "wb");
+    uint8_t buf[] = {
+        0, 0, 0, 10,             /* a */
+        0, 0, 0, 30,             /* c */
+        0, 0, 0, 0, 0, 0, 0, 40, /* d */
+        QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */
+    };
+    qemu_put_buffer(fsave, buf, sizeof(buf));
+    qemu_fclose(fsave);
+
+    QEMUFile *loading = qemu_fdopen(dup_temp_fd(false), "rb");
+    TestStruct obj = { .b = 200, .e = 500, .f = 600 };
+    vmstate_load_state(loading, &vmstate_versioned, &obj, 1);
+    g_assert(!qemu_file_get_error(loading));
+    g_assert_cmpint(obj.a, ==, 10);
+    g_assert_cmpint(obj.b, ==, 200);
+    g_assert_cmpint(obj.c, ==, 30);
+    g_assert_cmpint(obj.d, ==, 40);
+    g_assert_cmpint(obj.e, ==, 500);
+    g_assert_cmpint(obj.f, ==, 600);
+    qemu_fclose(loading);
+}
+
+static void test_load_v2(void)
+{
+    QEMUFile *fsave = qemu_fdopen(dup_temp_fd(true), "wb");
+    uint8_t buf[] = {
+        0, 0, 0, 10,             /* a */
+        0, 0, 0, 20,             /* b */
+        0, 0, 0, 30,             /* c */
+        0, 0, 0, 0, 0, 0, 0, 40, /* d */
+        0, 0, 0, 50,             /* e */
+        0, 0, 0, 0, 0, 0, 0, 60, /* f */
+        QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */
+    };
+    qemu_put_buffer(fsave, buf, sizeof(buf));
+    qemu_fclose(fsave);
+
+    QEMUFile *loading = qemu_fdopen(dup_temp_fd(false), "rb");
+    TestStruct obj;
+    vmstate_load_state(loading, &vmstate_versioned, &obj, 2);
+    g_assert_cmpint(obj.a, ==, 10);
+    g_assert_cmpint(obj.b, ==, 20);
+    g_assert_cmpint(obj.c, ==, 30);
+    g_assert_cmpint(obj.d, ==, 40);
+    g_assert_cmpint(obj.e, ==, 50);
+    g_assert_cmpint(obj.f, ==, 60);
+    qemu_fclose(loading);
+}
+
+static bool test_skip(void *opaque, int version_id)
+{
+    TestStruct *t = (TestStruct *)opaque;
+    return !t->skip_c_e;
+}
+
+static const VMStateDescription vmstate_skipping = {
+    .name = "test",
+    .version_id = 2,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField []) {
+        VMSTATE_UINT32(a, TestStruct),
+        VMSTATE_UINT32(b, TestStruct),
+        VMSTATE_UINT32_TEST(c, TestStruct, test_skip),
+        VMSTATE_UINT64(d, TestStruct),
+        VMSTATE_UINT32_TEST(e, TestStruct, test_skip),
+        VMSTATE_UINT64_V(f, TestStruct, 2),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+
+static void test_save_noskip(void)
+{
+    QEMUFile *fsave = qemu_fdopen(dup_temp_fd(true), "wb");
+    TestStruct obj = { .a = 1, .b = 2, .c = 3, .d = 4, .e = 5, .f = 6,
+                       .skip_c_e = false };
+    vmstate_save_state(fsave, &vmstate_skipping, &obj);
+    g_assert(!qemu_file_get_error(fsave));
+    qemu_fclose(fsave);
+
+    QEMUFile *loading = qemu_fdopen(dup_temp_fd(false), "rb");
+    uint8_t expected[] = {
+        0, 0, 0, 1,             /* a */
+        0, 0, 0, 2,             /* b */
+        0, 0, 0, 3,             /* c */
+        0, 0, 0, 0, 0, 0, 0, 4, /* d */
+        0, 0, 0, 5,             /* e */
+        0, 0, 0, 0, 0, 0, 0, 6, /* f */
+    };
+    uint8_t result[sizeof(expected)];
+    g_assert_cmpint(qemu_get_buffer(loading, result, sizeof(result)), ==,
+                    sizeof(result));
+    g_assert(!qemu_file_get_error(loading));
+    g_assert_cmpint(memcmp(result, expected, sizeof(result)), ==, 0);
+
+    /* Must reach EOF */
+    qemu_get_byte(loading);
+    g_assert_cmpint(qemu_file_get_error(loading), ==, -EIO);
+
+    qemu_fclose(loading);
+}
+
+static void test_save_skip(void)
+{
+    QEMUFile *fsave = qemu_fdopen(dup_temp_fd(true), "wb");
+    TestStruct obj = { .a = 1, .b = 2, .c = 3, .d = 4, .e = 5, .f = 6,
+                       .skip_c_e = true };
+    vmstate_save_state(fsave, &vmstate_skipping, &obj);
+    g_assert(!qemu_file_get_error(fsave));
+    qemu_fclose(fsave);
+
+    QEMUFile *loading = qemu_fdopen(dup_temp_fd(false), "rb");
+    uint8_t expected[] = {
+        0, 0, 0, 1,             /* a */
+        0, 0, 0, 2,             /* b */
+        0, 0, 0, 0, 0, 0, 0, 4, /* d */
+        0, 0, 0, 0, 0, 0, 0, 6, /* f */
+    };
+    uint8_t result[sizeof(expected)];
+    g_assert_cmpint(qemu_get_buffer(loading, result, sizeof(result)), ==,
+                    sizeof(result));
+    g_assert(!qemu_file_get_error(loading));
+    g_assert_cmpint(memcmp(result, expected, sizeof(result)), ==, 0);
+
+
+    /* Must reach EOF */
+    qemu_get_byte(loading);
+    g_assert_cmpint(qemu_file_get_error(loading), ==, -EIO);
+
+    qemu_fclose(loading);
+}
+
+static void test_load_noskip(void)
+{
+    QEMUFile *fsave = qemu_fdopen(dup_temp_fd(true), "wb");
+    uint8_t buf[] = {
+        0, 0, 0, 10,             /* a */
+        0, 0, 0, 20,             /* b */
+        0, 0, 0, 30,             /* c */
+        0, 0, 0, 0, 0, 0, 0, 40, /* d */
+        0, 0, 0, 50,             /* e */
+        0, 0, 0, 0, 0, 0, 0, 60, /* f */
+        QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */
+    };
+    qemu_put_buffer(fsave, buf, sizeof(buf));
+    qemu_fclose(fsave);
+
+    QEMUFile *loading = qemu_fdopen(dup_temp_fd(false), "rb");
+    TestStruct obj = { .skip_c_e = false };
+    vmstate_load_state(loading, &vmstate_skipping, &obj, 2);
+    g_assert(!qemu_file_get_error(loading));
+    g_assert_cmpint(obj.a, ==, 10);
+    g_assert_cmpint(obj.b, ==, 20);
+    g_assert_cmpint(obj.c, ==, 30);
+    g_assert_cmpint(obj.d, ==, 40);
+    g_assert_cmpint(obj.e, ==, 50);
+    g_assert_cmpint(obj.f, ==, 60);
+    qemu_fclose(loading);
+}
+
+static void test_load_skip(void)
+{
+    QEMUFile *fsave = qemu_fdopen(dup_temp_fd(true), "wb");
+    uint8_t buf[] = {
+        0, 0, 0, 10,             /* a */
+        0, 0, 0, 20,             /* b */
+        0, 0, 0, 0, 0, 0, 0, 40, /* d */
+        0, 0, 0, 0, 0, 0, 0, 60, /* f */
+        QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */
+    };
+    qemu_put_buffer(fsave, buf, sizeof(buf));
+    qemu_fclose(fsave);
+
+    QEMUFile *loading = qemu_fdopen(dup_temp_fd(false), "rb");
+    TestStruct obj = { .skip_c_e = true, .c = 300, .e = 500 };
+    vmstate_load_state(loading, &vmstate_skipping, &obj, 2);
+    g_assert(!qemu_file_get_error(loading));
+    g_assert_cmpint(obj.a, ==, 10);
+    g_assert_cmpint(obj.b, ==, 20);
+    g_assert_cmpint(obj.c, ==, 300);
+    g_assert_cmpint(obj.d, ==, 40);
+    g_assert_cmpint(obj.e, ==, 500);
+    g_assert_cmpint(obj.f, ==, 60);
+    qemu_fclose(loading);
+}
+
+int main(int argc, char **argv)
+{
+    temp_fd = mkstemp(temp_file);
+
+    g_test_init(&argc, &argv, NULL);
+    g_test_add_func("/vmstate/simple/save", test_simple_save);
+    g_test_add_func("/vmstate/simple/load", test_simple_load);
+    g_test_add_func("/vmstate/versioned/load/v1", test_load_v1);
+    g_test_add_func("/vmstate/versioned/load/v2", test_load_v2);
+    g_test_add_func("/vmstate/field_exists/load/noskip", test_load_noskip);
+    g_test_add_func("/vmstate/field_exists/load/skip", test_load_skip);
+    g_test_add_func("/vmstate/field_exists/save/noskip", test_save_noskip);
+    g_test_add_func("/vmstate/field_exists/save/skip", test_save_skip);
+    g_test_run();
+
+    close(temp_fd);
+    unlink(temp_file);
+
+    return 0;
+}