summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/.gitignore3
-rw-r--r--tests/Makefile70
-rw-r--r--tests/acpi-test-data/pc/DSDTbin2807 -> 3592 bytes
-rw-r--r--tests/acpi-test-data/pc/SSDTbin3065 -> 2279 bytes
-rw-r--r--tests/acpi-test-data/q35/DSDTbin7397 -> 8182 bytes
-rw-r--r--tests/acpi-test-data/q35/SSDTbin1346 -> 560 bytes
-rwxr-xr-xtests/acpi-test-data/rebuild-expected-aml.sh6
-rw-r--r--tests/ahci-test.c1561
-rw-r--r--tests/bios-tables-test.c15
-rw-r--r--tests/blockdev-test.c59
-rw-r--r--tests/drive_del-test.c137
-rw-r--r--tests/ide-test.c101
-rw-r--r--tests/image-fuzzer/qcow2/__init__.py1
-rw-r--r--tests/image-fuzzer/qcow2/fuzz.py367
-rw-r--r--tests/image-fuzzer/qcow2/layout.py612
-rwxr-xr-xtests/image-fuzzer/runner.py437
-rw-r--r--tests/libqos/malloc-pc.c283
-rw-r--r--tests/libqos/malloc-pc.h9
-rw-r--r--tests/libqos/malloc.h2
-rw-r--r--tests/libqos/pci-pc.c61
-rw-r--r--tests/libqos/pci-pc.h1
-rw-r--r--tests/libqos/pci.c121
-rw-r--r--tests/libqos/pci.h17
-rw-r--r--tests/libqos/usb.c71
-rw-r--r--tests/libqos/usb.h17
-rw-r--r--tests/libqos/virtio-pci.c343
-rw-r--r--tests/libqos/virtio-pci.h61
-rw-r--r--tests/libqos/virtio.c281
-rw-r--r--tests/libqos/virtio.h187
-rw-r--r--tests/libqtest.c73
-rw-r--r--tests/libqtest.h32
-rw-r--r--tests/qapi-schema/qapi-schema-test.json10
-rw-r--r--tests/qapi-schema/qapi-schema-test.out3
-rw-r--r--tests/qdev-monitor-test.c77
-rwxr-xr-xtests/qemu-iotests-quick.sh2
-rwxr-xr-xtests/qemu-iotests/0252
-rwxr-xr-xtests/qemu-iotests/0281
-rw-r--r--tests/qemu-iotests/028.out3
-rw-r--r--tests/qemu-iotests/039.out10
-rwxr-xr-xtests/qemu-iotests/0404
-rwxr-xr-xtests/qemu-iotests/0415
-rw-r--r--tests/qemu-iotests/049.out2
-rwxr-xr-xtests/qemu-iotests/05123
-rw-r--r--tests/qemu-iotests/051.out47
-rwxr-xr-xtests/qemu-iotests/0525
-rwxr-xr-xtests/qemu-iotests/0594
-rw-r--r--tests/qemu-iotests/059.out202
-rwxr-xr-xtests/qemu-iotests/06064
-rw-r--r--tests/qemu-iotests/060.out86
-rwxr-xr-xtests/qemu-iotests/06125
-rw-r--r--tests/qemu-iotests/061.out48
-rwxr-xr-xtests/qemu-iotests/06512
-rw-r--r--tests/qemu-iotests/067.out10
-rwxr-xr-xtests/qemu-iotests/0692
-rwxr-xr-xtests/qemu-iotests/0702
-rw-r--r--tests/qemu-iotests/070.out5
-rwxr-xr-xtests/qemu-iotests/0722
-rwxr-xr-xtests/qemu-iotests/0752
-rwxr-xr-xtests/qemu-iotests/07617
-rw-r--r--tests/qemu-iotests/076.out12
-rwxr-xr-xtests/qemu-iotests/0782
-rwxr-xr-xtests/qemu-iotests/0792
-rwxr-xr-xtests/qemu-iotests/0802
-rwxr-xr-xtests/qemu-iotests/0812
-rwxr-xr-xtests/qemu-iotests/08214
-rw-r--r--tests/qemu-iotests/082.out116
-rwxr-xr-xtests/qemu-iotests/08432
-rw-r--r--tests/qemu-iotests/084.out27
-rwxr-xr-xtests/qemu-iotests/0862
-rwxr-xr-xtests/qemu-iotests/08717
-rw-r--r--tests/qemu-iotests/087.out15
-rwxr-xr-xtests/qemu-iotests/0882
-rw-r--r--tests/qemu-iotests/089.out2
-rwxr-xr-xtests/qemu-iotests/0902
-rwxr-xr-xtests/qemu-iotests/0922
-rwxr-xr-xtests/qemu-iotests/0954
-rw-r--r--tests/qemu-iotests/095.out16
-rwxr-xr-xtests/qemu-iotests/097122
-rw-r--r--tests/qemu-iotests/097.out119
-rwxr-xr-xtests/qemu-iotests/09882
-rw-r--r--tests/qemu-iotests/098.out52
-rwxr-xr-xtests/qemu-iotests/099116
-rw-r--r--tests/qemu-iotests/099.out20
-rwxr-xr-xtests/qemu-iotests/100134
-rw-r--r--tests/qemu-iotests/100.out89
-rwxr-xr-xtests/qemu-iotests/10158
-rw-r--r--tests/qemu-iotests/101.out10
-rwxr-xr-xtests/qemu-iotests/10281
-rw-r--r--tests/qemu-iotests/102.out21
-rwxr-xr-xtests/qemu-iotests/10399
-rw-r--r--tests/qemu-iotests/103.out29
-rwxr-xr-xtests/qemu-iotests/10457
-rw-r--r--tests/qemu-iotests/104.out12
-rwxr-xr-xtests/qemu-iotests/10570
-rw-r--r--tests/qemu-iotests/105.out21
-rwxr-xr-xtests/qemu-iotests/10761
-rw-r--r--tests/qemu-iotests/107.out10
-rwxr-xr-xtests/qemu-iotests/108141
-rw-r--r--tests/qemu-iotests/108.out110
-rwxr-xr-xtests/qemu-iotests/11153
-rw-r--r--tests/qemu-iotests/111.out3
-rw-r--r--tests/qemu-iotests/common22
-rw-r--r--tests/qemu-iotests/common.filter50
-rw-r--r--tests/qemu-iotests/common.rc24
-rw-r--r--tests/qemu-iotests/group14
-rw-r--r--tests/qemu-iotests/iotests.py3
-rw-r--r--tests/qemu-iotests/sample_images/fake.parallels.bz2bin141 -> 0 bytes
-rw-r--r--tests/qemu-iotests/sample_images/iotest-version3.vmdk.bz2bin414 -> 4764 bytes
-rw-r--r--tests/qemu-iotests/sample_images/parallels-v1.bz2bin0 -> 147 bytes
-rw-r--r--tests/qemu-iotests/sample_images/parallels-v2.bz2bin0 -> 150 bytes
-rw-r--r--tests/qemu-iotests/socket_scm_helper.c2
-rw-r--r--tests/tcg/xtensa/Makefile10
-rw-r--r--tests/tcg/xtensa/linker.ld112
-rw-r--r--tests/tcg/xtensa/linker.ld.S130
-rw-r--r--tests/tcg/xtensa/test_windowed.S51
-rw-r--r--tests/test-aio.c58
-rw-r--r--tests/test-bitops.c1
-rw-r--r--tests/test-coroutine.c54
-rw-r--r--tests/test-qdev-global-props.c159
-rw-r--r--tests/test-qmp-input-strict.c17
-rw-r--r--tests/test-thread-pool.c46
-rw-r--r--tests/test-throttle.c10
-rw-r--r--tests/test-vmstate.c74
-rw-r--r--tests/usb-hcd-ehci-test.c50
-rw-r--r--tests/usb-hcd-ohci-test.c41
-rw-r--r--tests/usb-hcd-uhci-test.c96
-rw-r--r--tests/usb-hcd-xhci-test.c99
-rw-r--r--tests/virtio-blk-test.c664
-rw-r--r--tests/virtio-net-test.c10
-rw-r--r--tests/virtio-rng-test.c10
-rw-r--r--tests/virtio-scsi-test.c29
-rw-r--r--tests/virtio-serial-test.c27
132 files changed, 8661 insertions, 646 deletions
diff --git a/tests/.gitignore b/tests/.gitignore
index c71c11020..e2e495733 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -14,11 +14,14 @@ test-int128
test-iov
test-mul64
test-opts-visitor
+test-qapi-event.[ch]
test-qapi-types.[ch]
test-qapi-visit.[ch]
test-qdev-global-props
+test-qemu-opts
test-qmp-commands
test-qmp-commands.h
+test-qmp-event
test-qmp-input-strict
test-qmp-input-visitor
test-qmp-marshal.c
diff --git a/tests/Makefile b/tests/Makefile
index 4b2e1bbea..16f0e4c80 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -58,7 +58,7 @@ check-unit-y += tests/test-int128$(EXESUF)
# all code tested by test-int128 is inside int128.h
gcov-files-test-int128-y =
check-unit-y += tests/test-bitops$(EXESUF)
-check-unit-y += tests/test-qdev-global-props$(EXESUF)
+check-unit-$(CONFIG_HAS_GLIB_SUBPROCESS_TESTS) += 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-$(CONFIG_POSIX) += tests/test-vmstate$(EXESUF)
@@ -132,6 +132,7 @@ check-qtest-i386-y = tests/endianness-test$(EXESUF)
check-qtest-i386-y += tests/fdc-test$(EXESUF)
gcov-files-i386-y = hw/block/fdc.c
check-qtest-i386-y += tests/ide-test$(EXESUF)
+check-qtest-i386-y += tests/ahci-test$(EXESUF)
check-qtest-i386-y += tests/hd-geo-test$(EXESUF)
gcov-files-i386-y += hw/block/hd-geometry.c
check-qtest-i386-y += tests/boot-order-test$(EXESUF)
@@ -139,8 +140,7 @@ check-qtest-i386-y += tests/bios-tables-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)
-check-qtest-i386-y += tests/blockdev-test$(EXESUF)
-check-qtest-i386-y += tests/qdev-monitor-test$(EXESUF)
+check-qtest-i386-y += tests/drive_del-test$(EXESUF)
check-qtest-i386-y += tests/wdt_ib700-test$(EXESUF)
gcov-files-i386-y += hw/watchdog/watchdog.c hw/watchdog/wdt_ib700.c
check-qtest-i386-y += $(check-qtest-pci-y)
@@ -155,11 +155,16 @@ check-qtest-i386-y += tests/i82801b11-test$(EXESUF)
gcov-files-i386-y += hw/pci-bridge/i82801b11.c
check-qtest-i386-y += tests/ioh3420-test$(EXESUF)
gcov-files-i386-y += hw/pci-bridge/ioh3420.c
+check-qtest-i386-y += tests/usb-hcd-ohci-test$(EXESUF)
+gcov-files-i386-y += hw/usb/hcd-ohci.c
+check-qtest-i386-y += tests/usb-hcd-uhci-test$(EXESUF)
+gcov-files-i386-y += hw/usb/hcd-uhci.c
check-qtest-i386-y += tests/usb-hcd-ehci-test$(EXESUF)
gcov-files-i386-y += hw/usb/hcd-ehci.c
-gcov-files-i386-y += hw/usb/hcd-uhci.c
gcov-files-i386-y += hw/usb/dev-hid.c
gcov-files-i386-y += hw/usb/dev-storage.c
+check-qtest-i386-y += tests/usb-hcd-xhci-test$(EXESUF)
+gcov-files-i386-y += hw/usb/hcd-xhci.c
check-qtest-i386-$(CONFIG_LINUX) += tests/vhost-user-test$(EXESUF)
check-qtest-x86_64-y = $(check-qtest-i386-y)
gcov-files-i386-y += i386-softmmu/hw/timer/mc146818rtc.c
@@ -187,26 +192,27 @@ check-qtest-xtensaeb-y = $(check-qtest-xtensa-y)
# qom-test works for all sysemu architectures:
$(foreach target,$(SYSEMU_TARGET_LIST), \
- $(eval check-qtest-$(target)-y += tests/qom-test$(EXESUF)))
+ $(if $(findstring tests/qom-test$(EXESUF), $(check-qtest-$(target)-y)),, \
+ $(eval check-qtest-$(target)-y += tests/qom-test$(EXESUF))))
check-qapi-schema-y := $(addprefix tests/qapi-schema/, \
- comments.json empty.json funny-char.json indented-expr.json \
- missing-colon.json missing-comma-list.json \
- missing-comma-object.json non-objects.json \
- qapi-schema-test.json quoted-structural-chars.json \
- trailing-comma-list.json trailing-comma-object.json \
- unclosed-list.json unclosed-object.json unclosed-string.json \
- duplicate-key.json union-invalid-base.json flat-union-no-base.json \
- flat-union-invalid-discriminator.json \
- flat-union-invalid-branch-key.json flat-union-reverse-define.json \
- flat-union-string-discriminator.json \
- include-simple.json include-relpath.json include-format-err.json \
- include-non-file.json include-no-file.json include-before-err.json \
- include-nested-err.json include-self-cycle.json include-cycle.json \
- include-repetition.json event-nest-struct.json)
+ comments.json empty.json funny-char.json indented-expr.json \
+ missing-colon.json missing-comma-list.json \
+ missing-comma-object.json non-objects.json \
+ qapi-schema-test.json quoted-structural-chars.json \
+ trailing-comma-list.json trailing-comma-object.json \
+ unclosed-list.json unclosed-object.json unclosed-string.json \
+ duplicate-key.json union-invalid-base.json flat-union-no-base.json \
+ flat-union-invalid-discriminator.json \
+ flat-union-invalid-branch-key.json flat-union-reverse-define.json \
+ flat-union-string-discriminator.json \
+ include-simple.json include-relpath.json include-format-err.json \
+ include-non-file.json include-no-file.json include-before-err.json \
+ include-nested-err.json include-self-cycle.json include-cycle.json \
+ include-repetition.json event-nest-struct.json)
GENERATED_HEADERS += tests/test-qapi-types.h tests/test-qapi-visit.h \
- tests/test-qmp-commands.h tests/test-qapi-event.h
+ tests/test-qmp-commands.h tests/test-qapi-event.h
test-obj-y = tests/check-qint.o tests/check-qstring.o tests/check-qdict.o \
tests/check-qlist.o tests/check-qfloat.o tests/check-qjson.o \
@@ -218,7 +224,7 @@ test-obj-y = tests/check-qint.o tests/check-qstring.o tests/check-qdict.o \
tests/test-opts-visitor.o tests/test-qmp-event.o
test-qapi-obj-y = tests/test-qapi-visit.o tests/test-qapi-types.o \
- tests/test-qapi-event.o
+ tests/test-qapi-event.o
$(test-obj-y): QEMU_INCLUDES += -Itests
QEMU_CFLAGS += -I$(SRC_PATH)/tests
@@ -252,8 +258,8 @@ tests/test-qdev-global-props$(EXESUF): tests/test-qdev-global-props.o \
$(test-qapi-obj-y) \
libqemuutil.a libqemustub.a
tests/test-vmstate$(EXESUF): tests/test-vmstate.o \
- vmstate.o qemu-file.o \
- libqemuutil.a
+ vmstate.o qemu-file.o qemu-file-unix.o \
+ libqemuutil.a libqemustub.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
@@ -282,7 +288,7 @@ tests/test-qmp-event$(EXESUF): tests/test-qmp-event.o $(test-qapi-obj-y) libqemu
tests/test-qmp-output-visitor$(EXESUF): tests/test-qmp-output-visitor.o $(test-qapi-obj-y) libqemuutil.a libqemustub.a
tests/test-qmp-input-visitor$(EXESUF): tests/test-qmp-input-visitor.o $(test-qapi-obj-y) libqemuutil.a libqemustub.a
tests/test-qmp-input-strict$(EXESUF): tests/test-qmp-input-strict.o $(test-qapi-obj-y) libqemuutil.a libqemustub.a
-tests/test-qmp-commands$(EXESUF): tests/test-qmp-commands.o tests/test-qmp-marshal.o $(test-qapi-obj-y) qapi-types.o qapi-visit.o libqemuutil.a libqemustub.a
+tests/test-qmp-commands$(EXESUF): tests/test-qmp-commands.o tests/test-qmp-marshal.o $(test-qapi-obj-y) libqemuutil.a libqemustub.a
tests/test-visitor-serialization$(EXESUF): tests/test-visitor-serialization.o $(test-qapi-obj-y) libqemuutil.a libqemustub.a
tests/test-opts-visitor$(EXESUF): tests/test-opts-visitor.o $(test-qapi-obj-y) libqemuutil.a libqemustub.a
@@ -294,6 +300,8 @@ libqos-obj-y += tests/libqos/i2c.o
libqos-pc-obj-y = $(libqos-obj-y) tests/libqos/pci-pc.o
libqos-pc-obj-y += tests/libqos/malloc-pc.o
libqos-omap-obj-y = $(libqos-obj-y) tests/libqos/i2c-omap.o
+libqos-virtio-obj-y = $(libqos-obj-y) $(libqos-pc-obj-y) tests/libqos/virtio.o tests/libqos/virtio-pci.o
+libqos-usb-obj-y = $(libqos-pc-obj-y) tests/libqos/usb.o
tests/rtc-test$(EXESUF): tests/rtc-test.o
tests/m48t59-test$(EXESUF): tests/m48t59-test.o
@@ -301,6 +309,7 @@ tests/endianness-test$(EXESUF): tests/endianness-test.o
tests/spapr-phb-test$(EXESUF): tests/spapr-phb-test.o $(libqos-obj-y)
tests/fdc-test$(EXESUF): tests/fdc-test.o
tests/ide-test$(EXESUF): tests/ide-test.o $(libqos-pc-obj-y)
+tests/ahci-test$(EXESUF): tests/ahci-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/bios-tables-test$(EXESUF): tests/bios-tables-test.o $(libqos-obj-y)
@@ -315,9 +324,9 @@ tests/vmxnet3-test$(EXESUF): tests/vmxnet3-test.o
tests/ne2000-test$(EXESUF): tests/ne2000-test.o
tests/wdt_ib700-test$(EXESUF): tests/wdt_ib700-test.o
tests/virtio-balloon-test$(EXESUF): tests/virtio-balloon-test.o
-tests/virtio-blk-test$(EXESUF): tests/virtio-blk-test.o
-tests/virtio-net-test$(EXESUF): tests/virtio-net-test.o
-tests/virtio-rng-test$(EXESUF): tests/virtio-rng-test.o
+tests/virtio-blk-test$(EXESUF): tests/virtio-blk-test.o $(libqos-virtio-obj-y)
+tests/virtio-net-test$(EXESUF): tests/virtio-net-test.o $(libqos-pc-obj-y)
+tests/virtio-rng-test$(EXESUF): tests/virtio-rng-test.o $(libqos-pc-obj-y)
tests/virtio-scsi-test$(EXESUF): tests/virtio-scsi-test.o
tests/virtio-9p-test$(EXESUF): tests/virtio-9p-test.o
tests/virtio-serial-test$(EXESUF): tests/virtio-serial-test.o
@@ -326,7 +335,7 @@ tests/tpci200-test$(EXESUF): tests/tpci200-test.o
tests/display-vga-test$(EXESUF): tests/display-vga-test.o
tests/ipoctal232-test$(EXESUF): tests/ipoctal232-test.o
tests/qom-test$(EXESUF): tests/qom-test.o
-tests/blockdev-test$(EXESUF): tests/blockdev-test.o $(libqos-pc-obj-y)
+tests/drive_del-test$(EXESUF): tests/drive_del-test.o $(libqos-pc-obj-y)
tests/qdev-monitor-test$(EXESUF): tests/qdev-monitor-test.o $(libqos-pc-obj-y)
tests/nvme-test$(EXESUF): tests/nvme-test.o
tests/pvpanic-test$(EXESUF): tests/pvpanic-test.o
@@ -335,7 +344,10 @@ tests/ac97-test$(EXESUF): tests/ac97-test.o
tests/es1370-test$(EXESUF): tests/es1370-test.o
tests/intel-hda-test$(EXESUF): tests/intel-hda-test.o
tests/ioh3420-test$(EXESUF): tests/ioh3420-test.o
-tests/usb-hcd-ehci-test$(EXESUF): tests/usb-hcd-ehci-test.o $(libqos-pc-obj-y)
+tests/usb-hcd-ohci-test$(EXESUF): tests/usb-hcd-ohci-test.o $(libqos-usb-obj-y)
+tests/usb-hcd-uhci-test$(EXESUF): tests/usb-hcd-uhci-test.o $(libqos-usb-obj-y)
+tests/usb-hcd-ehci-test$(EXESUF): tests/usb-hcd-ehci-test.o $(libqos-usb-obj-y)
+tests/usb-hcd-xhci-test$(EXESUF): tests/usb-hcd-xhci-test.o $(libqos-usb-obj-y)
tests/vhost-user-test$(EXESUF): tests/vhost-user-test.o qemu-char.o qemu-timer.o $(qtest-obj-y)
tests/qemu-iotests/socket_scm_helper$(EXESUF): tests/qemu-iotests/socket_scm_helper.o
tests/test-qemu-opts$(EXESUF): tests/test-qemu-opts.o libqemuutil.a libqemustub.a
diff --git a/tests/acpi-test-data/pc/DSDT b/tests/acpi-test-data/pc/DSDT
index d37ec3445..ee9cc6781 100644
--- a/tests/acpi-test-data/pc/DSDT
+++ b/tests/acpi-test-data/pc/DSDT
Binary files differ
diff --git a/tests/acpi-test-data/pc/SSDT b/tests/acpi-test-data/pc/SSDT
index eb2d8b698..558e4c85b 100644
--- a/tests/acpi-test-data/pc/SSDT
+++ b/tests/acpi-test-data/pc/SSDT
Binary files differ
diff --git a/tests/acpi-test-data/q35/DSDT b/tests/acpi-test-data/q35/DSDT
index 2d2bc4ada..ef0c75f42 100644
--- a/tests/acpi-test-data/q35/DSDT
+++ b/tests/acpi-test-data/q35/DSDT
Binary files differ
diff --git a/tests/acpi-test-data/q35/SSDT b/tests/acpi-test-data/q35/SSDT
index 778b79bf4..4e4551018 100644
--- a/tests/acpi-test-data/q35/SSDT
+++ b/tests/acpi-test-data/q35/SSDT
Binary files differ
diff --git a/tests/acpi-test-data/rebuild-expected-aml.sh b/tests/acpi-test-data/rebuild-expected-aml.sh
index ab9849888..11bf74391 100755
--- a/tests/acpi-test-data/rebuild-expected-aml.sh
+++ b/tests/acpi-test-data/rebuild-expected-aml.sh
@@ -23,13 +23,13 @@ else
exit 1;
fi
-if [ ! -e "tests/acpi-test" ]; then
- echo "Test: acpi-test is required! Run make check before this script."
+if [ ! -e "tests/bios-tables-test" ]; then
+ echo "Test: bios-tables-test is required! Run make check before this script."
echo "Run this script from the build directory."
exit 1;
fi
-TEST_ACPI_REBUILD_AML=y QTEST_QEMU_BINARY=$qemu tests/acpi-test
+TEST_ACPI_REBUILD_AML=y QTEST_QEMU_BINARY=$qemu tests/bios-tables-test
echo "The files were rebuilt and can be added to git."
echo "However, if new files were created, please copy them manually" \
diff --git a/tests/ahci-test.c b/tests/ahci-test.c
new file mode 100644
index 000000000..4c77ebec7
--- /dev/null
+++ b/tests/ahci-test.c
@@ -0,0 +1,1561 @@
+/*
+ * AHCI test cases
+ *
+ * Copyright (c) 2014 John Snow <jsnow@redhat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include <stdio.h>
+#include <getopt.h>
+#include <glib.h>
+
+#include "libqtest.h"
+#include "libqos/pci-pc.h"
+#include "libqos/malloc-pc.h"
+
+#include "qemu-common.h"
+#include "qemu/host-utils.h"
+
+#include "hw/pci/pci_ids.h"
+#include "hw/pci/pci_regs.h"
+
+/* Test-specific defines. */
+#define TEST_IMAGE_SIZE (64 * 1024 * 1024)
+
+/*** Supplementary PCI Config Space IDs & Masks ***/
+#define PCI_DEVICE_ID_INTEL_Q35_AHCI (0x2922)
+#define PCI_MSI_FLAGS_RESERVED (0xFF00)
+#define PCI_PM_CTRL_RESERVED (0xFC)
+#define PCI_BCC(REG32) ((REG32) >> 24)
+#define PCI_PI(REG32) (((REG32) >> 8) & 0xFF)
+#define PCI_SCC(REG32) (((REG32) >> 16) & 0xFF)
+
+/*** Recognized AHCI Device Types ***/
+#define AHCI_INTEL_ICH9 (PCI_DEVICE_ID_INTEL_Q35_AHCI << 16 | \
+ PCI_VENDOR_ID_INTEL)
+
+/*** AHCI/HBA Register Offsets and Bitmasks ***/
+#define AHCI_CAP (0)
+#define AHCI_CAP_NP (0x1F)
+#define AHCI_CAP_SXS (0x20)
+#define AHCI_CAP_EMS (0x40)
+#define AHCI_CAP_CCCS (0x80)
+#define AHCI_CAP_NCS (0x1F00)
+#define AHCI_CAP_PSC (0x2000)
+#define AHCI_CAP_SSC (0x4000)
+#define AHCI_CAP_PMD (0x8000)
+#define AHCI_CAP_FBSS (0x10000)
+#define AHCI_CAP_SPM (0x20000)
+#define AHCI_CAP_SAM (0x40000)
+#define AHCI_CAP_RESERVED (0x80000)
+#define AHCI_CAP_ISS (0xF00000)
+#define AHCI_CAP_SCLO (0x1000000)
+#define AHCI_CAP_SAL (0x2000000)
+#define AHCI_CAP_SALP (0x4000000)
+#define AHCI_CAP_SSS (0x8000000)
+#define AHCI_CAP_SMPS (0x10000000)
+#define AHCI_CAP_SSNTF (0x20000000)
+#define AHCI_CAP_SNCQ (0x40000000)
+#define AHCI_CAP_S64A (0x80000000)
+
+#define AHCI_GHC (1)
+#define AHCI_GHC_HR (0x01)
+#define AHCI_GHC_IE (0x02)
+#define AHCI_GHC_MRSM (0x04)
+#define AHCI_GHC_RESERVED (0x7FFFFFF8)
+#define AHCI_GHC_AE (0x80000000)
+
+#define AHCI_IS (2)
+#define AHCI_PI (3)
+#define AHCI_VS (4)
+
+#define AHCI_CCCCTL (5)
+#define AHCI_CCCCTL_EN (0x01)
+#define AHCI_CCCCTL_RESERVED (0x06)
+#define AHCI_CCCCTL_CC (0xFF00)
+#define AHCI_CCCCTL_TV (0xFFFF0000)
+
+#define AHCI_CCCPORTS (6)
+#define AHCI_EMLOC (7)
+
+#define AHCI_EMCTL (8)
+#define AHCI_EMCTL_STSMR (0x01)
+#define AHCI_EMCTL_CTLTM (0x100)
+#define AHCI_EMCTL_CTLRST (0x200)
+#define AHCI_EMCTL_RESERVED (0xF0F0FCFE)
+
+#define AHCI_CAP2 (9)
+#define AHCI_CAP2_BOH (0x01)
+#define AHCI_CAP2_NVMP (0x02)
+#define AHCI_CAP2_APST (0x04)
+#define AHCI_CAP2_RESERVED (0xFFFFFFF8)
+
+#define AHCI_BOHC (10)
+#define AHCI_RESERVED (11)
+#define AHCI_NVMHCI (24)
+#define AHCI_VENDOR (40)
+#define AHCI_PORTS (64)
+
+/*** Port Memory Offsets & Bitmasks ***/
+#define AHCI_PX_CLB (0)
+#define AHCI_PX_CLB_RESERVED (0x1FF)
+
+#define AHCI_PX_CLBU (1)
+
+#define AHCI_PX_FB (2)
+#define AHCI_PX_FB_RESERVED (0xFF)
+
+#define AHCI_PX_FBU (3)
+
+#define AHCI_PX_IS (4)
+#define AHCI_PX_IS_DHRS (0x1)
+#define AHCI_PX_IS_PSS (0x2)
+#define AHCI_PX_IS_DSS (0x4)
+#define AHCI_PX_IS_SDBS (0x8)
+#define AHCI_PX_IS_UFS (0x10)
+#define AHCI_PX_IS_DPS (0x20)
+#define AHCI_PX_IS_PCS (0x40)
+#define AHCI_PX_IS_DMPS (0x80)
+#define AHCI_PX_IS_RESERVED (0x23FFF00)
+#define AHCI_PX_IS_PRCS (0x400000)
+#define AHCI_PX_IS_IPMS (0x800000)
+#define AHCI_PX_IS_OFS (0x1000000)
+#define AHCI_PX_IS_INFS (0x4000000)
+#define AHCI_PX_IS_IFS (0x8000000)
+#define AHCI_PX_IS_HBDS (0x10000000)
+#define AHCI_PX_IS_HBFS (0x20000000)
+#define AHCI_PX_IS_TFES (0x40000000)
+#define AHCI_PX_IS_CPDS (0x80000000)
+
+#define AHCI_PX_IE (5)
+#define AHCI_PX_IE_DHRE (0x1)
+#define AHCI_PX_IE_PSE (0x2)
+#define AHCI_PX_IE_DSE (0x4)
+#define AHCI_PX_IE_SDBE (0x8)
+#define AHCI_PX_IE_UFE (0x10)
+#define AHCI_PX_IE_DPE (0x20)
+#define AHCI_PX_IE_PCE (0x40)
+#define AHCI_PX_IE_DMPE (0x80)
+#define AHCI_PX_IE_RESERVED (0x23FFF00)
+#define AHCI_PX_IE_PRCE (0x400000)
+#define AHCI_PX_IE_IPME (0x800000)
+#define AHCI_PX_IE_OFE (0x1000000)
+#define AHCI_PX_IE_INFE (0x4000000)
+#define AHCI_PX_IE_IFE (0x8000000)
+#define AHCI_PX_IE_HBDE (0x10000000)
+#define AHCI_PX_IE_HBFE (0x20000000)
+#define AHCI_PX_IE_TFEE (0x40000000)
+#define AHCI_PX_IE_CPDE (0x80000000)
+
+#define AHCI_PX_CMD (6)
+#define AHCI_PX_CMD_ST (0x1)
+#define AHCI_PX_CMD_SUD (0x2)
+#define AHCI_PX_CMD_POD (0x4)
+#define AHCI_PX_CMD_CLO (0x8)
+#define AHCI_PX_CMD_FRE (0x10)
+#define AHCI_PX_CMD_RESERVED (0xE0)
+#define AHCI_PX_CMD_CCS (0x1F00)
+#define AHCI_PX_CMD_MPSS (0x2000)
+#define AHCI_PX_CMD_FR (0x4000)
+#define AHCI_PX_CMD_CR (0x8000)
+#define AHCI_PX_CMD_CPS (0x10000)
+#define AHCI_PX_CMD_PMA (0x20000)
+#define AHCI_PX_CMD_HPCP (0x40000)
+#define AHCI_PX_CMD_MPSP (0x80000)
+#define AHCI_PX_CMD_CPD (0x100000)
+#define AHCI_PX_CMD_ESP (0x200000)
+#define AHCI_PX_CMD_FBSCP (0x400000)
+#define AHCI_PX_CMD_APSTE (0x800000)
+#define AHCI_PX_CMD_ATAPI (0x1000000)
+#define AHCI_PX_CMD_DLAE (0x2000000)
+#define AHCI_PX_CMD_ALPE (0x4000000)
+#define AHCI_PX_CMD_ASP (0x8000000)
+#define AHCI_PX_CMD_ICC (0xF0000000)
+
+#define AHCI_PX_RES1 (7)
+
+#define AHCI_PX_TFD (8)
+#define AHCI_PX_TFD_STS (0xFF)
+#define AHCI_PX_TFD_STS_ERR (0x01)
+#define AHCI_PX_TFD_STS_CS1 (0x06)
+#define AHCI_PX_TFD_STS_DRQ (0x08)
+#define AHCI_PX_TFD_STS_CS2 (0x70)
+#define AHCI_PX_TFD_STS_BSY (0x80)
+#define AHCI_PX_TFD_ERR (0xFF00)
+#define AHCI_PX_TFD_RESERVED (0xFFFF0000)
+
+#define AHCI_PX_SIG (9)
+#define AHCI_PX_SIG_SECTOR_COUNT (0xFF)
+#define AHCI_PX_SIG_LBA_LOW (0xFF00)
+#define AHCI_PX_SIG_LBA_MID (0xFF0000)
+#define AHCI_PX_SIG_LBA_HIGH (0xFF000000)
+
+#define AHCI_PX_SSTS (10)
+#define AHCI_PX_SSTS_DET (0x0F)
+#define AHCI_PX_SSTS_SPD (0xF0)
+#define AHCI_PX_SSTS_IPM (0xF00)
+#define AHCI_PX_SSTS_RESERVED (0xFFFFF000)
+#define SSTS_DET_NO_DEVICE (0x00)
+#define SSTS_DET_PRESENT (0x01)
+#define SSTS_DET_ESTABLISHED (0x03)
+#define SSTS_DET_OFFLINE (0x04)
+
+#define AHCI_PX_SCTL (11)
+
+#define AHCI_PX_SERR (12)
+#define AHCI_PX_SERR_ERR (0xFFFF)
+#define AHCI_PX_SERR_DIAG (0xFFFF0000)
+#define AHCI_PX_SERR_DIAG_X (0x04000000)
+
+#define AHCI_PX_SACT (13)
+#define AHCI_PX_CI (14)
+#define AHCI_PX_SNTF (15)
+
+#define AHCI_PX_FBS (16)
+#define AHCI_PX_FBS_EN (0x1)
+#define AHCI_PX_FBS_DEC (0x2)
+#define AHCI_PX_FBS_SDE (0x4)
+#define AHCI_PX_FBS_DEV (0xF00)
+#define AHCI_PX_FBS_ADO (0xF000)
+#define AHCI_PX_FBS_DWE (0xF0000)
+#define AHCI_PX_FBS_RESERVED (0xFFF000F8)
+
+#define AHCI_PX_RES2 (17)
+#define AHCI_PX_VS (28)
+
+#define HBA_DATA_REGION_SIZE (256)
+#define HBA_PORT_DATA_SIZE (128)
+#define HBA_PORT_NUM_REG (HBA_PORT_DATA_SIZE/4)
+
+#define AHCI_VERSION_0_95 (0x00000905)
+#define AHCI_VERSION_1_0 (0x00010000)
+#define AHCI_VERSION_1_1 (0x00010100)
+#define AHCI_VERSION_1_2 (0x00010200)
+#define AHCI_VERSION_1_3 (0x00010300)
+
+/*** Structures ***/
+
+/**
+ * Generic FIS structure.
+ */
+typedef struct FIS {
+ uint8_t fis_type;
+ uint8_t flags;
+ char data[0];
+} __attribute__((__packed__)) FIS;
+
+/**
+ * Register device-to-host FIS structure.
+ */
+typedef struct RegD2HFIS {
+ /* DW0 */
+ uint8_t fis_type;
+ uint8_t flags;
+ uint8_t status;
+ uint8_t error;
+ /* DW1 */
+ uint8_t lba_low;
+ uint8_t lba_mid;
+ uint8_t lba_high;
+ uint8_t device;
+ /* DW2 */
+ uint8_t lba3;
+ uint8_t lba4;
+ uint8_t lba5;
+ uint8_t res1;
+ /* DW3 */
+ uint16_t count;
+ uint8_t res2;
+ uint8_t res3;
+ /* DW4 */
+ uint16_t res4;
+ uint16_t res5;
+} __attribute__((__packed__)) RegD2HFIS;
+
+/**
+ * Register host-to-device FIS structure.
+ */
+typedef struct RegH2DFIS {
+ /* DW0 */
+ uint8_t fis_type;
+ uint8_t flags;
+ uint8_t command;
+ uint8_t feature_low;
+ /* DW1 */
+ uint8_t lba_low;
+ uint8_t lba_mid;
+ uint8_t lba_high;
+ uint8_t device;
+ /* DW2 */
+ uint8_t lba3;
+ uint8_t lba4;
+ uint8_t lba5;
+ uint8_t feature_high;
+ /* DW3 */
+ uint16_t count;
+ uint8_t icc;
+ uint8_t control;
+ /* DW4 */
+ uint32_t aux;
+} __attribute__((__packed__)) RegH2DFIS;
+
+/**
+ * Command List entry structure.
+ * The command list contains between 1-32 of these structures.
+ */
+typedef struct AHCICommand {
+ uint8_t b1;
+ uint8_t b2;
+ uint16_t prdtl; /* Phys Region Desc. Table Length */
+ uint32_t prdbc; /* Phys Region Desc. Byte Count */
+ uint32_t ctba; /* Command Table Descriptor Base Address */
+ uint32_t ctbau; /* '' Upper */
+ uint32_t res[4];
+} __attribute__((__packed__)) AHCICommand;
+
+/**
+ * Physical Region Descriptor; pointed to by the Command List Header,
+ * struct ahci_command.
+ */
+typedef struct PRD {
+ uint32_t dba; /* Data Base Address */
+ uint32_t dbau; /* Data Base Address Upper */
+ uint32_t res; /* Reserved */
+ uint32_t dbc; /* Data Byte Count (0-indexed) & Interrupt Flag (bit 2^31) */
+} PRD;
+
+typedef struct HBACap {
+ uint32_t cap;
+ uint32_t cap2;
+} HBACap;
+
+/*** Globals ***/
+static QGuestAllocator *guest_malloc;
+static QPCIBus *pcibus;
+static uint64_t barsize;
+static char tmp_path[] = "/tmp/qtest.XXXXXX";
+static bool ahci_pedantic;
+static uint32_t ahci_fingerprint;
+
+/*** Macro Utilities ***/
+#define BITANY(data, mask) (((data) & (mask)) != 0)
+#define BITSET(data, mask) (((data) & (mask)) == (mask))
+#define BITCLR(data, mask) (((data) & (mask)) == 0)
+#define ASSERT_BIT_SET(data, mask) g_assert_cmphex((data) & (mask), ==, (mask))
+#define ASSERT_BIT_CLEAR(data, mask) g_assert_cmphex((data) & (mask), ==, 0)
+
+/*** IO macros for the AHCI memory registers. ***/
+#define AHCI_READ(OFST) qpci_io_readl(ahci, hba_base + (OFST))
+#define AHCI_WRITE(OFST, VAL) qpci_io_writel(ahci, hba_base + (OFST), (VAL))
+#define AHCI_RREG(regno) AHCI_READ(4 * (regno))
+#define AHCI_WREG(regno, val) AHCI_WRITE(4 * (regno), (val))
+#define AHCI_SET(regno, mask) AHCI_WREG((regno), AHCI_RREG(regno) | (mask))
+#define AHCI_CLR(regno, mask) AHCI_WREG((regno), AHCI_RREG(regno) & ~(mask))
+
+/*** IO macros for port-specific offsets inside of AHCI memory. ***/
+#define PX_OFST(port, regno) (HBA_PORT_NUM_REG * (port) + AHCI_PORTS + (regno))
+#define PX_RREG(port, regno) AHCI_RREG(PX_OFST((port), (regno)))
+#define PX_WREG(port, regno, val) AHCI_WREG(PX_OFST((port), (regno)), (val))
+#define PX_SET(port, reg, mask) PX_WREG((port), (reg), \
+ PX_RREG((port), (reg)) | (mask));
+#define PX_CLR(port, reg, mask) PX_WREG((port), (reg), \
+ PX_RREG((port), (reg)) & ~(mask));
+
+/* For calculating how big the PRD table needs to be: */
+#define CMD_TBL_SIZ(n) ((0x80 + ((n) * sizeof(PRD)) + 0x7F) & ~0x7F)
+
+
+/*** Function Declarations ***/
+static QPCIDevice *get_ahci_device(void);
+static QPCIDevice *start_ahci_device(QPCIDevice *dev, void **hba_base);
+static void free_ahci_device(QPCIDevice *dev);
+static void ahci_test_port_spec(QPCIDevice *ahci, void *hba_base,
+ HBACap *hcap, uint8_t port);
+static void ahci_test_pci_spec(QPCIDevice *ahci);
+static void ahci_test_pci_caps(QPCIDevice *ahci, uint16_t header,
+ uint8_t offset);
+static void ahci_test_satacap(QPCIDevice *ahci, uint8_t offset);
+static void ahci_test_msicap(QPCIDevice *ahci, uint8_t offset);
+static void ahci_test_pmcap(QPCIDevice *ahci, uint8_t offset);
+
+/*** Utilities ***/
+
+static void string_bswap16(uint16_t *s, size_t bytes)
+{
+ g_assert_cmphex((bytes & 1), ==, 0);
+ bytes /= 2;
+
+ while (bytes--) {
+ *s = bswap16(*s);
+ s++;
+ }
+}
+
+/**
+ * Locate, verify, and return a handle to the AHCI device.
+ */
+static QPCIDevice *get_ahci_device(void)
+{
+ QPCIDevice *ahci;
+
+ pcibus = qpci_init_pc();
+
+ /* Find the AHCI PCI device and verify it's the right one. */
+ ahci = qpci_device_find(pcibus, QPCI_DEVFN(0x1F, 0x02));
+ g_assert(ahci != NULL);
+
+ ahci_fingerprint = qpci_config_readl(ahci, PCI_VENDOR_ID);
+
+ switch (ahci_fingerprint) {
+ case AHCI_INTEL_ICH9:
+ break;
+ default:
+ /* Unknown device. */
+ g_assert_not_reached();
+ }
+
+ return ahci;
+}
+
+static void free_ahci_device(QPCIDevice *ahci)
+{
+ /* libqos doesn't have a function for this, so free it manually */
+ g_free(ahci);
+
+ if (pcibus) {
+ qpci_free_pc(pcibus);
+ pcibus = NULL;
+ }
+
+ /* Clear our cached barsize information. */
+ barsize = 0;
+}
+
+/*** Test Setup & Teardown ***/
+
+/**
+ * Launch QEMU with the given command line,
+ * and then set up interrupts and our guest malloc interface.
+ */
+static void qtest_boot(const char *cmdline_fmt, ...)
+{
+ va_list ap;
+ char *cmdline;
+
+ va_start(ap, cmdline_fmt);
+ cmdline = g_strdup_vprintf(cmdline_fmt, ap);
+ va_end(ap);
+
+ qtest_start(cmdline);
+ qtest_irq_intercept_in(global_qtest, "ioapic");
+ guest_malloc = pc_alloc_init();
+
+ g_free(cmdline);
+}
+
+/**
+ * Tear down the QEMU instance.
+ */
+static void qtest_shutdown(void)
+{
+ g_free(guest_malloc);
+ guest_malloc = NULL;
+ qtest_end();
+}
+
+/**
+ * Start a Q35 machine and bookmark a handle to the AHCI device.
+ */
+static QPCIDevice *ahci_boot(void)
+{
+ qtest_boot("-drive if=none,id=drive0,file=%s,cache=writeback,serial=%s"
+ " -M q35 "
+ "-device ide-hd,drive=drive0 "
+ "-global ide-hd.ver=%s",
+ tmp_path, "testdisk", "version");
+
+ /* Verify that we have an AHCI device present. */
+ return get_ahci_device();
+}
+
+/**
+ * Clean up the PCI device, then terminate the QEMU instance.
+ */
+static void ahci_shutdown(QPCIDevice *ahci)
+{
+ free_ahci_device(ahci);
+ qtest_shutdown();
+}
+
+/*** Logical Device Initialization ***/
+
+/**
+ * Start the PCI device and sanity-check default operation.
+ */
+static void ahci_pci_enable(QPCIDevice *ahci, void **hba_base)
+{
+ uint8_t reg;
+
+ start_ahci_device(ahci, hba_base);
+
+ switch (ahci_fingerprint) {
+ case AHCI_INTEL_ICH9:
+ /* ICH9 has a register at PCI 0x92 that
+ * acts as a master port enabler mask. */
+ reg = qpci_config_readb(ahci, 0x92);
+ reg |= 0x3F;
+ qpci_config_writeb(ahci, 0x92, reg);
+ /* 0...0111111b -- bit significant, ports 0-5 enabled. */
+ ASSERT_BIT_SET(qpci_config_readb(ahci, 0x92), 0x3F);
+ break;
+ }
+
+}
+
+/**
+ * Map BAR5/ABAR, and engage the PCI device.
+ */
+static QPCIDevice *start_ahci_device(QPCIDevice *ahci, void **hba_base)
+{
+ /* Map AHCI's ABAR (BAR5) */
+ *hba_base = qpci_iomap(ahci, 5, &barsize);
+
+ /* turns on pci.cmd.iose, pci.cmd.mse and pci.cmd.bme */
+ qpci_device_enable(ahci);
+
+ return ahci;
+}
+
+/**
+ * Test and initialize the AHCI's HBA memory areas.
+ * Initialize and start any ports with devices attached.
+ * Bring the HBA into the idle state.
+ */
+static void ahci_hba_enable(QPCIDevice *ahci, void *hba_base)
+{
+ /* Bits of interest in this section:
+ * GHC.AE Global Host Control / AHCI Enable
+ * PxCMD.ST Port Command: Start
+ * PxCMD.SUD "Spin Up Device"
+ * PxCMD.POD "Power On Device"
+ * PxCMD.FRE "FIS Receive Enable"
+ * PxCMD.FR "FIS Receive Running"
+ * PxCMD.CR "Command List Running"
+ */
+
+ g_assert(ahci != NULL);
+ g_assert(hba_base != NULL);
+
+ uint32_t reg, ports_impl, clb, fb;
+ uint16_t i;
+ uint8_t num_cmd_slots;
+
+ g_assert(hba_base != 0);
+
+ /* Set GHC.AE to 1 */
+ AHCI_SET(AHCI_GHC, AHCI_GHC_AE);
+ reg = AHCI_RREG(AHCI_GHC);
+ ASSERT_BIT_SET(reg, AHCI_GHC_AE);
+
+ /* Read CAP.NCS, how many command slots do we have? */
+ reg = AHCI_RREG(AHCI_CAP);
+ num_cmd_slots = ((reg & AHCI_CAP_NCS) >> ctzl(AHCI_CAP_NCS)) + 1;
+ g_test_message("Number of Command Slots: %u", num_cmd_slots);
+
+ /* Determine which ports are implemented. */
+ ports_impl = AHCI_RREG(AHCI_PI);
+
+ for (i = 0; ports_impl; ports_impl >>= 1, ++i) {
+ if (!(ports_impl & 0x01)) {
+ continue;
+ }
+
+ g_test_message("Initializing port %u", i);
+
+ reg = PX_RREG(i, AHCI_PX_CMD);
+ if (BITCLR(reg, AHCI_PX_CMD_ST | AHCI_PX_CMD_CR |
+ AHCI_PX_CMD_FRE | AHCI_PX_CMD_FR)) {
+ g_test_message("port is idle");
+ } else {
+ g_test_message("port needs to be idled");
+ PX_CLR(i, AHCI_PX_CMD, (AHCI_PX_CMD_ST | AHCI_PX_CMD_FRE));
+ /* The port has 500ms to disengage. */
+ usleep(500000);
+ reg = PX_RREG(i, AHCI_PX_CMD);
+ ASSERT_BIT_CLEAR(reg, AHCI_PX_CMD_CR);
+ ASSERT_BIT_CLEAR(reg, AHCI_PX_CMD_FR);
+ g_test_message("port is now idle");
+ /* The spec does allow for possibly needing a PORT RESET
+ * or HBA reset if we fail to idle the port. */
+ }
+
+ /* Allocate Memory for the Command List Buffer & FIS Buffer */
+ /* PxCLB space ... 0x20 per command, as in 4.2.2 p 36 */
+ clb = guest_alloc(guest_malloc, num_cmd_slots * 0x20);
+ g_test_message("CLB: 0x%08x", clb);
+ PX_WREG(i, AHCI_PX_CLB, clb);
+ g_assert_cmphex(clb, ==, PX_RREG(i, AHCI_PX_CLB));
+
+ /* PxFB space ... 0x100, as in 4.2.1 p 35 */
+ fb = guest_alloc(guest_malloc, 0x100);
+ g_test_message("FB: 0x%08x", fb);
+ PX_WREG(i, AHCI_PX_FB, fb);
+ g_assert_cmphex(fb, ==, PX_RREG(i, AHCI_PX_FB));
+
+ /* Clear PxSERR, PxIS, then IS.IPS[x] by writing '1's. */
+ PX_WREG(i, AHCI_PX_SERR, 0xFFFFFFFF);
+ PX_WREG(i, AHCI_PX_IS, 0xFFFFFFFF);
+ AHCI_WREG(AHCI_IS, (1 << i));
+
+ /* Verify Interrupts Cleared */
+ reg = PX_RREG(i, AHCI_PX_SERR);
+ g_assert_cmphex(reg, ==, 0);
+
+ reg = PX_RREG(i, AHCI_PX_IS);
+ g_assert_cmphex(reg, ==, 0);
+
+ reg = AHCI_RREG(AHCI_IS);
+ ASSERT_BIT_CLEAR(reg, (1 << i));
+
+ /* Enable All Interrupts: */
+ PX_WREG(i, AHCI_PX_IE, 0xFFFFFFFF);
+ reg = PX_RREG(i, AHCI_PX_IE);
+ g_assert_cmphex(reg, ==, ~((uint32_t)AHCI_PX_IE_RESERVED));
+
+ /* Enable the FIS Receive Engine. */
+ PX_SET(i, AHCI_PX_CMD, AHCI_PX_CMD_FRE);
+ reg = PX_RREG(i, AHCI_PX_CMD);
+ ASSERT_BIT_SET(reg, AHCI_PX_CMD_FR);
+
+ /* AHCI 1.3 spec: if !STS.BSY, !STS.DRQ and PxSSTS.DET indicates
+ * physical presence, a device is present and may be started. However,
+ * PxSERR.DIAG.X /may/ need to be cleared a priori. */
+ reg = PX_RREG(i, AHCI_PX_SERR);
+ if (BITSET(reg, AHCI_PX_SERR_DIAG_X)) {
+ PX_SET(i, AHCI_PX_SERR, AHCI_PX_SERR_DIAG_X);
+ }
+
+ reg = PX_RREG(i, AHCI_PX_TFD);
+ if (BITCLR(reg, AHCI_PX_TFD_STS_BSY | AHCI_PX_TFD_STS_DRQ)) {
+ reg = PX_RREG(i, AHCI_PX_SSTS);
+ if ((reg & AHCI_PX_SSTS_DET) == SSTS_DET_ESTABLISHED) {
+ /* Device Found: set PxCMD.ST := 1 */
+ PX_SET(i, AHCI_PX_CMD, AHCI_PX_CMD_ST);
+ ASSERT_BIT_SET(PX_RREG(i, AHCI_PX_CMD), AHCI_PX_CMD_CR);
+ g_test_message("Started Device %u", i);
+ } else if ((reg & AHCI_PX_SSTS_DET)) {
+ /* Device present, but in some unknown state. */
+ g_assert_not_reached();
+ }
+ }
+ }
+
+ /* Enable GHC.IE */
+ AHCI_SET(AHCI_GHC, AHCI_GHC_IE);
+ reg = AHCI_RREG(AHCI_GHC);
+ ASSERT_BIT_SET(reg, AHCI_GHC_IE);
+
+ /* TODO: The device should now be idling and waiting for commands.
+ * In the future, a small test-case to inspect the Register D2H FIS
+ * and clear the initial interrupts might be good. */
+}
+
+/*** Specification Adherence Tests ***/
+
+/**
+ * Implementation for test_pci_spec. Ensures PCI configuration space is sane.
+ */
+static void ahci_test_pci_spec(QPCIDevice *ahci)
+{
+ uint8_t datab;
+ uint16_t data;
+ uint32_t datal;
+
+ /* Most of these bits should start cleared until we turn them on. */
+ data = qpci_config_readw(ahci, PCI_COMMAND);
+ ASSERT_BIT_CLEAR(data, PCI_COMMAND_MEMORY);
+ ASSERT_BIT_CLEAR(data, PCI_COMMAND_MASTER);
+ ASSERT_BIT_CLEAR(data, PCI_COMMAND_SPECIAL); /* Reserved */
+ ASSERT_BIT_CLEAR(data, PCI_COMMAND_VGA_PALETTE); /* Reserved */
+ ASSERT_BIT_CLEAR(data, PCI_COMMAND_PARITY);
+ ASSERT_BIT_CLEAR(data, PCI_COMMAND_WAIT); /* Reserved */
+ ASSERT_BIT_CLEAR(data, PCI_COMMAND_SERR);
+ ASSERT_BIT_CLEAR(data, PCI_COMMAND_FAST_BACK);
+ ASSERT_BIT_CLEAR(data, PCI_COMMAND_INTX_DISABLE);
+ ASSERT_BIT_CLEAR(data, 0xF800); /* Reserved */
+
+ data = qpci_config_readw(ahci, PCI_STATUS);
+ ASSERT_BIT_CLEAR(data, 0x01 | 0x02 | 0x04); /* Reserved */
+ ASSERT_BIT_CLEAR(data, PCI_STATUS_INTERRUPT);
+ ASSERT_BIT_SET(data, PCI_STATUS_CAP_LIST); /* must be set */
+ ASSERT_BIT_CLEAR(data, PCI_STATUS_UDF); /* Reserved */
+ ASSERT_BIT_CLEAR(data, PCI_STATUS_PARITY);
+ ASSERT_BIT_CLEAR(data, PCI_STATUS_SIG_TARGET_ABORT);
+ ASSERT_BIT_CLEAR(data, PCI_STATUS_REC_TARGET_ABORT);
+ ASSERT_BIT_CLEAR(data, PCI_STATUS_REC_MASTER_ABORT);
+ ASSERT_BIT_CLEAR(data, PCI_STATUS_SIG_SYSTEM_ERROR);
+ ASSERT_BIT_CLEAR(data, PCI_STATUS_DETECTED_PARITY);
+
+ /* RID occupies the low byte, CCs occupy the high three. */
+ datal = qpci_config_readl(ahci, PCI_CLASS_REVISION);
+ if (ahci_pedantic) {
+ /* AHCI 1.3 specifies that at-boot, the RID should reset to 0x00,
+ * Though in practice this is likely seldom true. */
+ ASSERT_BIT_CLEAR(datal, 0xFF);
+ }
+
+ /* BCC *must* equal 0x01. */
+ g_assert_cmphex(PCI_BCC(datal), ==, 0x01);
+ if (PCI_SCC(datal) == 0x01) {
+ /* IDE */
+ ASSERT_BIT_SET(0x80000000, datal);
+ ASSERT_BIT_CLEAR(0x60000000, datal);
+ } else if (PCI_SCC(datal) == 0x04) {
+ /* RAID */
+ g_assert_cmphex(PCI_PI(datal), ==, 0);
+ } else if (PCI_SCC(datal) == 0x06) {
+ /* AHCI */
+ g_assert_cmphex(PCI_PI(datal), ==, 0x01);
+ } else {
+ g_assert_not_reached();
+ }
+
+ datab = qpci_config_readb(ahci, PCI_CACHE_LINE_SIZE);
+ g_assert_cmphex(datab, ==, 0);
+
+ datab = qpci_config_readb(ahci, PCI_LATENCY_TIMER);
+ g_assert_cmphex(datab, ==, 0);
+
+ /* Only the bottom 7 bits must be off. */
+ datab = qpci_config_readb(ahci, PCI_HEADER_TYPE);
+ ASSERT_BIT_CLEAR(datab, 0x7F);
+
+ /* BIST is optional, but the low 7 bits must always start off regardless. */
+ datab = qpci_config_readb(ahci, PCI_BIST);
+ ASSERT_BIT_CLEAR(datab, 0x7F);
+
+ /* BARS 0-4 do not have a boot spec, but ABAR/BAR5 must be clean. */
+ datal = qpci_config_readl(ahci, PCI_BASE_ADDRESS_5);
+ g_assert_cmphex(datal, ==, 0);
+
+ qpci_config_writel(ahci, PCI_BASE_ADDRESS_5, 0xFFFFFFFF);
+ datal = qpci_config_readl(ahci, PCI_BASE_ADDRESS_5);
+ /* ABAR must be 32-bit, memory mapped, non-prefetchable and
+ * must be >= 512 bytes. To that end, bits 0-8 must be off. */
+ ASSERT_BIT_CLEAR(datal, 0xFF);
+
+ /* Capability list MUST be present, */
+ datal = qpci_config_readl(ahci, PCI_CAPABILITY_LIST);
+ /* But these bits are reserved. */
+ ASSERT_BIT_CLEAR(datal, ~0xFF);
+ g_assert_cmphex(datal, !=, 0);
+
+ /* Check specification adherence for capability extenstions. */
+ data = qpci_config_readw(ahci, datal);
+
+ switch (ahci_fingerprint) {
+ case AHCI_INTEL_ICH9:
+ /* Intel ICH9 Family Datasheet 14.1.19 p.550 */
+ g_assert_cmphex((data & 0xFF), ==, PCI_CAP_ID_MSI);
+ break;
+ default:
+ /* AHCI 1.3, Section 2.1.14 -- CAP must point to PMCAP. */
+ g_assert_cmphex((data & 0xFF), ==, PCI_CAP_ID_PM);
+ }
+
+ ahci_test_pci_caps(ahci, data, (uint8_t)datal);
+
+ /* Reserved. */
+ datal = qpci_config_readl(ahci, PCI_CAPABILITY_LIST + 4);
+ g_assert_cmphex(datal, ==, 0);
+
+ /* IPIN might vary, but ILINE must be off. */
+ datab = qpci_config_readb(ahci, PCI_INTERRUPT_LINE);
+ g_assert_cmphex(datab, ==, 0);
+}
+
+/**
+ * Test PCI capabilities for AHCI specification adherence.
+ */
+static void ahci_test_pci_caps(QPCIDevice *ahci, uint16_t header,
+ uint8_t offset)
+{
+ uint8_t cid = header & 0xFF;
+ uint8_t next = header >> 8;
+
+ g_test_message("CID: %02x; next: %02x", cid, next);
+
+ switch (cid) {
+ case PCI_CAP_ID_PM:
+ ahci_test_pmcap(ahci, offset);
+ break;
+ case PCI_CAP_ID_MSI:
+ ahci_test_msicap(ahci, offset);
+ break;
+ case PCI_CAP_ID_SATA:
+ ahci_test_satacap(ahci, offset);
+ break;
+
+ default:
+ g_test_message("Unknown CAP 0x%02x", cid);
+ }
+
+ if (next) {
+ ahci_test_pci_caps(ahci, qpci_config_readw(ahci, next), next);
+ }
+}
+
+/**
+ * Test SATA PCI capabilitity for AHCI specification adherence.
+ */
+static void ahci_test_satacap(QPCIDevice *ahci, uint8_t offset)
+{
+ uint16_t dataw;
+ uint32_t datal;
+
+ g_test_message("Verifying SATACAP");
+
+ /* Assert that the SATACAP version is 1.0, And reserved bits are empty. */
+ dataw = qpci_config_readw(ahci, offset + 2);
+ g_assert_cmphex(dataw, ==, 0x10);
+
+ /* Grab the SATACR1 register. */
+ datal = qpci_config_readw(ahci, offset + 4);
+
+ switch (datal & 0x0F) {
+ case 0x04: /* BAR0 */
+ case 0x05: /* BAR1 */
+ case 0x06:
+ case 0x07:
+ case 0x08:
+ case 0x09: /* BAR5 */
+ case 0x0F: /* Immediately following SATACR1 in PCI config space. */
+ break;
+ default:
+ /* Invalid BARLOC for the Index Data Pair. */
+ g_assert_not_reached();
+ }
+
+ /* Reserved. */
+ g_assert_cmphex((datal >> 24), ==, 0x00);
+}
+
+/**
+ * Test MSI PCI capability for AHCI specification adherence.
+ */
+static void ahci_test_msicap(QPCIDevice *ahci, uint8_t offset)
+{
+ uint16_t dataw;
+ uint32_t datal;
+
+ g_test_message("Verifying MSICAP");
+
+ dataw = qpci_config_readw(ahci, offset + PCI_MSI_FLAGS);
+ ASSERT_BIT_CLEAR(dataw, PCI_MSI_FLAGS_ENABLE);
+ ASSERT_BIT_CLEAR(dataw, PCI_MSI_FLAGS_QSIZE);
+ ASSERT_BIT_CLEAR(dataw, PCI_MSI_FLAGS_RESERVED);
+
+ datal = qpci_config_readl(ahci, offset + PCI_MSI_ADDRESS_LO);
+ g_assert_cmphex(datal, ==, 0);
+
+ if (dataw & PCI_MSI_FLAGS_64BIT) {
+ g_test_message("MSICAP is 64bit");
+ datal = qpci_config_readl(ahci, offset + PCI_MSI_ADDRESS_HI);
+ g_assert_cmphex(datal, ==, 0);
+ dataw = qpci_config_readw(ahci, offset + PCI_MSI_DATA_64);
+ g_assert_cmphex(dataw, ==, 0);
+ } else {
+ g_test_message("MSICAP is 32bit");
+ dataw = qpci_config_readw(ahci, offset + PCI_MSI_DATA_32);
+ g_assert_cmphex(dataw, ==, 0);
+ }
+}
+
+/**
+ * Test Power Management PCI capability for AHCI specification adherence.
+ */
+static void ahci_test_pmcap(QPCIDevice *ahci, uint8_t offset)
+{
+ uint16_t dataw;
+
+ g_test_message("Verifying PMCAP");
+
+ dataw = qpci_config_readw(ahci, offset + PCI_PM_PMC);
+ ASSERT_BIT_CLEAR(dataw, PCI_PM_CAP_PME_CLOCK);
+ ASSERT_BIT_CLEAR(dataw, PCI_PM_CAP_RESERVED);
+ ASSERT_BIT_CLEAR(dataw, PCI_PM_CAP_D1);
+ ASSERT_BIT_CLEAR(dataw, PCI_PM_CAP_D2);
+
+ dataw = qpci_config_readw(ahci, offset + PCI_PM_CTRL);
+ ASSERT_BIT_CLEAR(dataw, PCI_PM_CTRL_STATE_MASK);
+ ASSERT_BIT_CLEAR(dataw, PCI_PM_CTRL_RESERVED);
+ ASSERT_BIT_CLEAR(dataw, PCI_PM_CTRL_DATA_SEL_MASK);
+ ASSERT_BIT_CLEAR(dataw, PCI_PM_CTRL_DATA_SCALE_MASK);
+}
+
+static void ahci_test_hba_spec(QPCIDevice *ahci, void *hba_base)
+{
+ HBACap hcap;
+ unsigned i;
+ uint32_t cap, cap2, reg;
+ uint32_t ports;
+ uint8_t nports_impl;
+ uint8_t maxports;
+
+ g_assert(ahci != 0);
+ g_assert(hba_base != 0);
+
+ /*
+ * Note that the AHCI spec does expect the BIOS to set up a few things:
+ * CAP.SSS - Support for staggered spin-up (t/f)
+ * CAP.SMPS - Support for mechanical presence switches (t/f)
+ * PI - Ports Implemented (1-32)
+ * PxCMD.HPCP - Hot Plug Capable Port
+ * PxCMD.MPSP - Mechanical Presence Switch Present
+ * PxCMD.CPD - Cold Presence Detection support
+ *
+ * Additional items are touched if CAP.SSS is on, see AHCI 10.1.1 p.97:
+ * Foreach Port Implemented:
+ * -PxCMD.ST, PxCMD.CR, PxCMD.FRE, PxCMD.FR, PxSCTL.DET are 0
+ * -PxCLB/U and PxFB/U are set to valid regions in memory
+ * -PxSUD is set to 1.
+ * -PxSSTS.DET is polled for presence; if detected, we continue:
+ * -PxSERR is cleared with 1's.
+ * -If PxTFD.STS.BSY, PxTFD.STS.DRQ, and PxTFD.STS.ERR are all zero,
+ * the device is ready.
+ */
+
+ /* 1 CAP - Capabilities Register */
+ cap = AHCI_RREG(AHCI_CAP);
+ ASSERT_BIT_CLEAR(cap, AHCI_CAP_RESERVED);
+
+ /* 2 GHC - Global Host Control */
+ reg = AHCI_RREG(AHCI_GHC);
+ ASSERT_BIT_CLEAR(reg, AHCI_GHC_HR);
+ ASSERT_BIT_CLEAR(reg, AHCI_GHC_IE);
+ ASSERT_BIT_CLEAR(reg, AHCI_GHC_MRSM);
+ if (BITSET(cap, AHCI_CAP_SAM)) {
+ g_test_message("Supports AHCI-Only Mode: GHC_AE is Read-Only.");
+ ASSERT_BIT_SET(reg, AHCI_GHC_AE);
+ } else {
+ g_test_message("Supports AHCI/Legacy mix.");
+ ASSERT_BIT_CLEAR(reg, AHCI_GHC_AE);
+ }
+
+ /* 3 IS - Interrupt Status */
+ reg = AHCI_RREG(AHCI_IS);
+ g_assert_cmphex(reg, ==, 0);
+
+ /* 4 PI - Ports Implemented */
+ ports = AHCI_RREG(AHCI_PI);
+ /* Ports Implemented must be non-zero. */
+ g_assert_cmphex(ports, !=, 0);
+ /* Ports Implemented must be <= Number of Ports. */
+ nports_impl = ctpopl(ports);
+ g_assert_cmpuint(((AHCI_CAP_NP & cap) + 1), >=, nports_impl);
+
+ g_assert_cmphex(barsize, >, 0);
+ /* Ports must be within the proper range. Given a mapping of SIZE,
+ * 256 bytes are used for global HBA control, and the rest is used
+ * for ports data, at 0x80 bytes each. */
+ maxports = (barsize - HBA_DATA_REGION_SIZE) / HBA_PORT_DATA_SIZE;
+ /* e.g, 30 ports for 4K of memory. (4096 - 256) / 128 = 30 */
+ g_assert_cmphex((reg >> maxports), ==, 0);
+
+ /* 5 AHCI Version */
+ reg = AHCI_RREG(AHCI_VS);
+ switch (reg) {
+ case AHCI_VERSION_0_95:
+ case AHCI_VERSION_1_0:
+ case AHCI_VERSION_1_1:
+ case AHCI_VERSION_1_2:
+ case AHCI_VERSION_1_3:
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ /* 6 Command Completion Coalescing Control: depends on CAP.CCCS. */
+ reg = AHCI_RREG(AHCI_CCCCTL);
+ if (BITSET(cap, AHCI_CAP_CCCS)) {
+ ASSERT_BIT_CLEAR(reg, AHCI_CCCCTL_EN);
+ ASSERT_BIT_CLEAR(reg, AHCI_CCCCTL_RESERVED);
+ ASSERT_BIT_SET(reg, AHCI_CCCCTL_CC);
+ ASSERT_BIT_SET(reg, AHCI_CCCCTL_TV);
+ } else {
+ g_assert_cmphex(reg, ==, 0);
+ }
+
+ /* 7 CCC_PORTS */
+ reg = AHCI_RREG(AHCI_CCCPORTS);
+ /* Must be zeroes initially regardless of CAP.CCCS */
+ g_assert_cmphex(reg, ==, 0);
+
+ /* 8 EM_LOC */
+ reg = AHCI_RREG(AHCI_EMLOC);
+ if (BITCLR(cap, AHCI_CAP_EMS)) {
+ g_assert_cmphex(reg, ==, 0);
+ }
+
+ /* 9 EM_CTL */
+ reg = AHCI_RREG(AHCI_EMCTL);
+ if (BITSET(cap, AHCI_CAP_EMS)) {
+ ASSERT_BIT_CLEAR(reg, AHCI_EMCTL_STSMR);
+ ASSERT_BIT_CLEAR(reg, AHCI_EMCTL_CTLTM);
+ ASSERT_BIT_CLEAR(reg, AHCI_EMCTL_CTLRST);
+ ASSERT_BIT_CLEAR(reg, AHCI_EMCTL_RESERVED);
+ } else {
+ g_assert_cmphex(reg, ==, 0);
+ }
+
+ /* 10 CAP2 -- Capabilities Extended */
+ cap2 = AHCI_RREG(AHCI_CAP2);
+ ASSERT_BIT_CLEAR(cap2, AHCI_CAP2_RESERVED);
+
+ /* 11 BOHC -- Bios/OS Handoff Control */
+ reg = AHCI_RREG(AHCI_BOHC);
+ g_assert_cmphex(reg, ==, 0);
+
+ /* 12 -- 23: Reserved */
+ g_test_message("Verifying HBA reserved area is empty.");
+ for (i = AHCI_RESERVED; i < AHCI_NVMHCI; ++i) {
+ reg = AHCI_RREG(i);
+ g_assert_cmphex(reg, ==, 0);
+ }
+
+ /* 24 -- 39: NVMHCI */
+ if (BITCLR(cap2, AHCI_CAP2_NVMP)) {
+ g_test_message("Verifying HBA/NVMHCI area is empty.");
+ for (i = AHCI_NVMHCI; i < AHCI_VENDOR; ++i) {
+ reg = AHCI_RREG(i);
+ g_assert_cmphex(reg, ==, 0);
+ }
+ }
+
+ /* 40 -- 63: Vendor */
+ g_test_message("Verifying HBA/Vendor area is empty.");
+ for (i = AHCI_VENDOR; i < AHCI_PORTS; ++i) {
+ reg = AHCI_RREG(i);
+ g_assert_cmphex(reg, ==, 0);
+ }
+
+ /* 64 -- XX: Port Space */
+ hcap.cap = cap;
+ hcap.cap2 = cap2;
+ for (i = 0; ports || (i < maxports); ports >>= 1, ++i) {
+ if (BITSET(ports, 0x1)) {
+ g_test_message("Testing port %u for spec", i);
+ ahci_test_port_spec(ahci, hba_base, &hcap, i);
+ } else {
+ uint16_t j;
+ uint16_t low = AHCI_PORTS + (32 * i);
+ uint16_t high = AHCI_PORTS + (32 * (i + 1));
+ g_test_message("Asserting unimplemented port %u "
+ "(reg [%u-%u]) is empty.",
+ i, low, high - 1);
+ for (j = low; j < high; ++j) {
+ reg = AHCI_RREG(j);
+ g_assert_cmphex(reg, ==, 0);
+ }
+ }
+ }
+}
+
+/**
+ * Test the memory space for one port for specification adherence.
+ */
+static void ahci_test_port_spec(QPCIDevice *ahci, void *hba_base,
+ HBACap *hcap, uint8_t port)
+{
+ uint32_t reg;
+ unsigned i;
+
+ /* (0) CLB */
+ reg = PX_RREG(port, AHCI_PX_CLB);
+ ASSERT_BIT_CLEAR(reg, AHCI_PX_CLB_RESERVED);
+
+ /* (1) CLBU */
+ if (BITCLR(hcap->cap, AHCI_CAP_S64A)) {
+ reg = PX_RREG(port, AHCI_PX_CLBU);
+ g_assert_cmphex(reg, ==, 0);
+ }
+
+ /* (2) FB */
+ reg = PX_RREG(port, AHCI_PX_FB);
+ ASSERT_BIT_CLEAR(reg, AHCI_PX_FB_RESERVED);
+
+ /* (3) FBU */
+ if (BITCLR(hcap->cap, AHCI_CAP_S64A)) {
+ reg = PX_RREG(port, AHCI_PX_FBU);
+ g_assert_cmphex(reg, ==, 0);
+ }
+
+ /* (4) IS */
+ reg = PX_RREG(port, AHCI_PX_IS);
+ g_assert_cmphex(reg, ==, 0);
+
+ /* (5) IE */
+ reg = PX_RREG(port, AHCI_PX_IE);
+ g_assert_cmphex(reg, ==, 0);
+
+ /* (6) CMD */
+ reg = PX_RREG(port, AHCI_PX_CMD);
+ ASSERT_BIT_CLEAR(reg, AHCI_PX_CMD_FRE);
+ ASSERT_BIT_CLEAR(reg, AHCI_PX_CMD_RESERVED);
+ ASSERT_BIT_CLEAR(reg, AHCI_PX_CMD_CCS);
+ ASSERT_BIT_CLEAR(reg, AHCI_PX_CMD_FR);
+ ASSERT_BIT_CLEAR(reg, AHCI_PX_CMD_CR);
+ ASSERT_BIT_CLEAR(reg, AHCI_PX_CMD_PMA); /* And RW only if CAP.SPM */
+ ASSERT_BIT_CLEAR(reg, AHCI_PX_CMD_APSTE); /* RW only if CAP2.APST */
+ ASSERT_BIT_CLEAR(reg, AHCI_PX_CMD_ATAPI);
+ ASSERT_BIT_CLEAR(reg, AHCI_PX_CMD_DLAE);
+ ASSERT_BIT_CLEAR(reg, AHCI_PX_CMD_ALPE); /* RW only if CAP.SALP */
+ ASSERT_BIT_CLEAR(reg, AHCI_PX_CMD_ASP); /* RW only if CAP.SALP */
+ ASSERT_BIT_CLEAR(reg, AHCI_PX_CMD_ICC);
+ /* If CPDetect support does not exist, CPState must be off. */
+ if (BITCLR(reg, AHCI_PX_CMD_CPD)) {
+ ASSERT_BIT_CLEAR(reg, AHCI_PX_CMD_CPS);
+ }
+ /* If MPSPresence is not set, MPSState must be off. */
+ if (BITCLR(reg, AHCI_PX_CMD_MPSP)) {
+ ASSERT_BIT_CLEAR(reg, AHCI_PX_CMD_MPSS);
+ }
+ /* If we do not support MPS, MPSS and MPSP must be off. */
+ if (BITCLR(hcap->cap, AHCI_CAP_SMPS)) {
+ ASSERT_BIT_CLEAR(reg, AHCI_PX_CMD_MPSS);
+ ASSERT_BIT_CLEAR(reg, AHCI_PX_CMD_MPSP);
+ }
+ /* If, via CPD or MPSP we detect a drive, HPCP must be on. */
+ if (BITANY(reg, AHCI_PX_CMD_CPD || AHCI_PX_CMD_MPSP)) {
+ ASSERT_BIT_SET(reg, AHCI_PX_CMD_HPCP);
+ }
+ /* HPCP and ESP cannot both be active. */
+ g_assert(!BITSET(reg, AHCI_PX_CMD_HPCP | AHCI_PX_CMD_ESP));
+ /* If CAP.FBSS is not set, FBSCP must not be set. */
+ if (BITCLR(hcap->cap, AHCI_CAP_FBSS)) {
+ ASSERT_BIT_CLEAR(reg, AHCI_PX_CMD_FBSCP);
+ }
+
+ /* (7) RESERVED */
+ reg = PX_RREG(port, AHCI_PX_RES1);
+ g_assert_cmphex(reg, ==, 0);
+
+ /* (8) TFD */
+ reg = PX_RREG(port, AHCI_PX_TFD);
+ /* At boot, prior to an FIS being received, the TFD register should be 0x7F,
+ * which breaks down as follows, as seen in AHCI 1.3 sec 3.3.8, p. 27. */
+ ASSERT_BIT_SET(reg, AHCI_PX_TFD_STS_ERR);
+ ASSERT_BIT_SET(reg, AHCI_PX_TFD_STS_CS1);
+ ASSERT_BIT_SET(reg, AHCI_PX_TFD_STS_DRQ);
+ ASSERT_BIT_SET(reg, AHCI_PX_TFD_STS_CS2);
+ ASSERT_BIT_CLEAR(reg, AHCI_PX_TFD_STS_BSY);
+ ASSERT_BIT_CLEAR(reg, AHCI_PX_TFD_ERR);
+ ASSERT_BIT_CLEAR(reg, AHCI_PX_TFD_RESERVED);
+
+ /* (9) SIG */
+ /* Though AHCI specifies the boot value should be 0xFFFFFFFF,
+ * Even when GHC.ST is zero, the AHCI HBA may receive the initial
+ * D2H register FIS and update the signature asynchronously,
+ * so we cannot expect a value here. AHCI 1.3, sec 3.3.9, pp 27-28 */
+
+ /* (10) SSTS / SCR0: SStatus */
+ reg = PX_RREG(port, AHCI_PX_SSTS);
+ ASSERT_BIT_CLEAR(reg, AHCI_PX_SSTS_RESERVED);
+ /* Even though the register should be 0 at boot, it is asynchronous and
+ * prone to change, so we cannot test any well known value. */
+
+ /* (11) SCTL / SCR2: SControl */
+ reg = PX_RREG(port, AHCI_PX_SCTL);
+ g_assert_cmphex(reg, ==, 0);
+
+ /* (12) SERR / SCR1: SError */
+ reg = PX_RREG(port, AHCI_PX_SERR);
+ g_assert_cmphex(reg, ==, 0);
+
+ /* (13) SACT / SCR3: SActive */
+ reg = PX_RREG(port, AHCI_PX_SACT);
+ g_assert_cmphex(reg, ==, 0);
+
+ /* (14) CI */
+ reg = PX_RREG(port, AHCI_PX_CI);
+ g_assert_cmphex(reg, ==, 0);
+
+ /* (15) SNTF */
+ reg = PX_RREG(port, AHCI_PX_SNTF);
+ g_assert_cmphex(reg, ==, 0);
+
+ /* (16) FBS */
+ reg = PX_RREG(port, AHCI_PX_FBS);
+ ASSERT_BIT_CLEAR(reg, AHCI_PX_FBS_EN);
+ ASSERT_BIT_CLEAR(reg, AHCI_PX_FBS_DEC);
+ ASSERT_BIT_CLEAR(reg, AHCI_PX_FBS_SDE);
+ ASSERT_BIT_CLEAR(reg, AHCI_PX_FBS_DEV);
+ ASSERT_BIT_CLEAR(reg, AHCI_PX_FBS_DWE);
+ ASSERT_BIT_CLEAR(reg, AHCI_PX_FBS_RESERVED);
+ if (BITSET(hcap->cap, AHCI_CAP_FBSS)) {
+ /* if Port-Multiplier FIS-based switching avail, ADO must >= 2 */
+ g_assert((reg & AHCI_PX_FBS_ADO) >> ctzl(AHCI_PX_FBS_ADO) >= 2);
+ }
+
+ /* [17 -- 27] RESERVED */
+ for (i = AHCI_PX_RES2; i < AHCI_PX_VS; ++i) {
+ reg = PX_RREG(port, i);
+ g_assert_cmphex(reg, ==, 0);
+ }
+
+ /* [28 -- 31] Vendor-Specific */
+ for (i = AHCI_PX_VS; i < 32; ++i) {
+ reg = PX_RREG(port, i);
+ if (reg) {
+ g_test_message("INFO: Vendor register %u non-empty", i);
+ }
+ }
+}
+
+/**
+ * Utilizing an initialized AHCI HBA, issue an IDENTIFY command to the first
+ * device we see, then read and check the response.
+ */
+static void ahci_test_identify(QPCIDevice *ahci, void *hba_base)
+{
+ RegD2HFIS *d2h = g_malloc0(0x20);
+ RegD2HFIS *pio = g_malloc0(0x20);
+ RegH2DFIS fis;
+ AHCICommand cmd;
+ PRD prd;
+ uint32_t ports, reg, clb, table, fb, data_ptr;
+ uint16_t buff[256];
+ unsigned i;
+ int rc;
+
+ g_assert(ahci != NULL);
+ g_assert(hba_base != NULL);
+
+ /* We need to:
+ * (1) Create a Command Table Buffer and update the Command List Slot #0
+ * to point to this buffer.
+ * (2) Construct an FIS host-to-device command structure, and write it to
+ * the top of the command table buffer.
+ * (3) Create a data buffer for the IDENTIFY response to be sent to
+ * (4) Create a Physical Region Descriptor that points to the data buffer,
+ * and write it to the bottom (offset 0x80) of the command table.
+ * (5) Now, PxCLB points to the command list, command 0 points to
+ * our table, and our table contains an FIS instruction and a
+ * PRD that points to our rx buffer.
+ * (6) We inform the HBA via PxCI that there is a command ready in slot #0.
+ */
+
+ /* Pick the first implemented and running port */
+ ports = AHCI_RREG(AHCI_PI);
+ for (i = 0; i < 32; ports >>= 1, ++i) {
+ if (ports == 0) {
+ i = 32;
+ }
+
+ if (!(ports & 0x01)) {
+ continue;
+ }
+
+ reg = PX_RREG(i, AHCI_PX_CMD);
+ if (BITSET(reg, AHCI_PX_CMD_ST)) {
+ break;
+ }
+ }
+ g_assert_cmphex(i, <, 32);
+ g_test_message("Selected port %u for test", i);
+
+ /* Clear out this port's interrupts (ignore the init register d2h fis) */
+ reg = PX_RREG(i, AHCI_PX_IS);
+ PX_WREG(i, AHCI_PX_IS, reg);
+ g_assert_cmphex(PX_RREG(i, AHCI_PX_IS), ==, 0);
+
+ /* Wipe the FIS-Recieve Buffer */
+ fb = PX_RREG(i, AHCI_PX_FB);
+ g_assert_cmphex(fb, !=, 0);
+ qmemset(fb, 0x00, 0x100);
+
+ /* Create a Command Table buffer. 0x80 is the smallest with a PRDTL of 0. */
+ /* We need at least one PRD, so round up to the nearest 0x80 multiple. */
+ table = guest_alloc(guest_malloc, CMD_TBL_SIZ(1));
+ g_assert(table);
+ ASSERT_BIT_CLEAR(table, 0x7F);
+
+ /* Create a data buffer ... where we will dump the IDENTIFY data to. */
+ data_ptr = guest_alloc(guest_malloc, 512);
+ g_assert(data_ptr);
+
+ /* Grab the Command List Buffer pointer */
+ clb = PX_RREG(i, AHCI_PX_CLB);
+ g_assert(clb);
+
+ /* Copy the existing Command #0 structure from the CLB into local memory,
+ * and build a new command #0. */
+ memread(clb, &cmd, sizeof(cmd));
+ cmd.b1 = 5; /* reg_h2d_fis is 5 double-words long */
+ cmd.b2 = 0x04; /* clear PxTFD.STS.BSY when done */
+ cmd.prdtl = cpu_to_le16(1); /* One PRD table entry. */
+ cmd.prdbc = 0;
+ cmd.ctba = cpu_to_le32(table);
+ cmd.ctbau = 0;
+
+ /* Construct our PRD, noting that DBC is 0-indexed. */
+ prd.dba = cpu_to_le32(data_ptr);
+ prd.dbau = 0;
+ prd.res = 0;
+ /* 511+1 bytes, request DPS interrupt */
+ prd.dbc = cpu_to_le32(511 | 0x80000000);
+
+ /* Construct our Command FIS, Based on http://wiki.osdev.org/AHCI */
+ memset(&fis, 0x00, sizeof(fis));
+ fis.fis_type = 0x27; /* Register Host-to-Device FIS */
+ fis.command = 0xEC; /* IDENTIFY */
+ fis.device = 0;
+ fis.flags = 0x80; /* Indicate this is a command FIS */
+
+ /* We've committed nothing yet, no interrupts should be posted yet. */
+ g_assert_cmphex(PX_RREG(i, AHCI_PX_IS), ==, 0);
+
+ /* Commit the Command FIS to the Command Table */
+ memwrite(table, &fis, sizeof(fis));
+
+ /* Commit the PRD entry to the Command Table */
+ memwrite(table + 0x80, &prd, sizeof(prd));
+
+ /* Commit Command #0, pointing to the Table, to the Command List Buffer. */
+ memwrite(clb, &cmd, sizeof(cmd));
+
+ /* Everything is in place, but we haven't given the go-ahead yet. */
+ g_assert_cmphex(PX_RREG(i, AHCI_PX_IS), ==, 0);
+
+ /* Issue Command #0 via PxCI */
+ PX_WREG(i, AHCI_PX_CI, (1 << 0));
+ while (BITSET(PX_RREG(i, AHCI_PX_TFD), AHCI_PX_TFD_STS_BSY)) {
+ usleep(50);
+ }
+
+ /* Check for expected interrupts */
+ reg = PX_RREG(i, AHCI_PX_IS);
+ ASSERT_BIT_SET(reg, AHCI_PX_IS_DHRS);
+ ASSERT_BIT_SET(reg, AHCI_PX_IS_PSS);
+ /* BUG: we expect AHCI_PX_IS_DPS to be set. */
+ ASSERT_BIT_CLEAR(reg, AHCI_PX_IS_DPS);
+
+ /* Clear expected interrupts and assert all interrupts now cleared. */
+ PX_WREG(i, AHCI_PX_IS, AHCI_PX_IS_DHRS | AHCI_PX_IS_PSS | AHCI_PX_IS_DPS);
+ g_assert_cmphex(PX_RREG(i, AHCI_PX_IS), ==, 0);
+
+ /* Check for errors. */
+ reg = PX_RREG(i, AHCI_PX_SERR);
+ g_assert_cmphex(reg, ==, 0);
+ reg = PX_RREG(i, AHCI_PX_TFD);
+ ASSERT_BIT_CLEAR(reg, AHCI_PX_TFD_STS_ERR);
+ ASSERT_BIT_CLEAR(reg, AHCI_PX_TFD_ERR);
+
+ /* Investigate CMD #0, assert that we read 512 bytes */
+ memread(clb, &cmd, sizeof(cmd));
+ g_assert_cmphex(512, ==, le32_to_cpu(cmd.prdbc));
+
+ /* Investigate FIS responses */
+ memread(fb + 0x20, pio, 0x20);
+ memread(fb + 0x40, d2h, 0x20);
+ g_assert_cmphex(pio->fis_type, ==, 0x5f);
+ g_assert_cmphex(d2h->fis_type, ==, 0x34);
+ g_assert_cmphex(pio->flags, ==, d2h->flags);
+ g_assert_cmphex(pio->status, ==, d2h->status);
+ g_assert_cmphex(pio->error, ==, d2h->error);
+
+ reg = PX_RREG(i, AHCI_PX_TFD);
+ g_assert_cmphex((reg & AHCI_PX_TFD_ERR), ==, pio->error);
+ g_assert_cmphex((reg & AHCI_PX_TFD_STS), ==, pio->status);
+ /* The PIO Setup FIS contains a "bytes read" field, which is a
+ * 16-bit value. The Physical Region Descriptor Byte Count is
+ * 32-bit, but for small transfers using one PRD, it should match. */
+ g_assert_cmphex(le16_to_cpu(pio->res4), ==, le32_to_cpu(cmd.prdbc));
+
+ /* Last, but not least: Investigate the IDENTIFY response data. */
+ memread(data_ptr, &buff, 512);
+
+ /* Check serial number/version in the buffer */
+ /* NB: IDENTIFY strings are packed in 16bit little endian chunks.
+ * Since we copy byte-for-byte in ahci-test, on both LE and BE, we need to
+ * unchunk this data. By contrast, ide-test copies 2 bytes at a time, and
+ * as a consequence, only needs to unchunk the data on LE machines. */
+ string_bswap16(&buff[10], 20);
+ rc = memcmp(&buff[10], "testdisk ", 20);
+ g_assert_cmphex(rc, ==, 0);
+
+ string_bswap16(&buff[23], 8);
+ rc = memcmp(&buff[23], "version ", 8);
+ g_assert_cmphex(rc, ==, 0);
+
+ g_free(d2h);
+ g_free(pio);
+}
+
+/******************************************************************************/
+/* Test Interfaces */
+/******************************************************************************/
+
+/**
+ * Basic sanity test to boot a machine, find an AHCI device, and shutdown.
+ */
+static void test_sanity(void)
+{
+ QPCIDevice *ahci;
+ ahci = ahci_boot();
+ ahci_shutdown(ahci);
+}
+
+/**
+ * Ensure that the PCI configuration space for the AHCI device is in-line with
+ * the AHCI 1.3 specification for initial values.
+ */
+static void test_pci_spec(void)
+{
+ QPCIDevice *ahci;
+ ahci = ahci_boot();
+ ahci_test_pci_spec(ahci);
+ ahci_shutdown(ahci);
+}
+
+/**
+ * Engage the PCI AHCI device and sanity check the response.
+ * Perform additional PCI config space bringup for the HBA.
+ */
+static void test_pci_enable(void)
+{
+ QPCIDevice *ahci;
+ void *hba_base;
+ ahci = ahci_boot();
+ ahci_pci_enable(ahci, &hba_base);
+ ahci_shutdown(ahci);
+}
+
+/**
+ * Investigate the memory mapped regions of the HBA,
+ * and test them for AHCI specification adherence.
+ */
+static void test_hba_spec(void)
+{
+ QPCIDevice *ahci;
+ void *hba_base;
+
+ ahci = ahci_boot();
+ ahci_pci_enable(ahci, &hba_base);
+ ahci_test_hba_spec(ahci, hba_base);
+ ahci_shutdown(ahci);
+}
+
+/**
+ * Engage the HBA functionality of the AHCI PCI device,
+ * and bring it into a functional idle state.
+ */
+static void test_hba_enable(void)
+{
+ QPCIDevice *ahci;
+ void *hba_base;
+
+ ahci = ahci_boot();
+ ahci_pci_enable(ahci, &hba_base);
+ ahci_hba_enable(ahci, hba_base);
+ ahci_shutdown(ahci);
+}
+
+/**
+ * Bring up the device and issue an IDENTIFY command.
+ * Inspect the state of the HBA device and the data returned.
+ */
+static void test_identify(void)
+{
+ QPCIDevice *ahci;
+ void *hba_base;
+
+ ahci = ahci_boot();
+ ahci_pci_enable(ahci, &hba_base);
+ ahci_hba_enable(ahci, hba_base);
+ ahci_test_identify(ahci, hba_base);
+ ahci_shutdown(ahci);
+}
+
+/******************************************************************************/
+
+int main(int argc, char **argv)
+{
+ const char *arch;
+ int fd;
+ int ret;
+ int c;
+
+ static struct option long_options[] = {
+ {"pedantic", no_argument, 0, 'p' },
+ {0, 0, 0, 0},
+ };
+
+ /* Should be first to utilize g_test functionality, So we can see errors. */
+ g_test_init(&argc, &argv, NULL);
+
+ while (1) {
+ c = getopt_long(argc, argv, "", long_options, NULL);
+ if (c == -1) {
+ break;
+ }
+ switch (c) {
+ case -1:
+ break;
+ case 'p':
+ ahci_pedantic = 1;
+ break;
+ default:
+ fprintf(stderr, "Unrecognized ahci_test option.\n");
+ g_assert_not_reached();
+ }
+ }
+
+ /* Check architecture */
+ arch = qtest_get_arch();
+ if (strcmp(arch, "i386") && strcmp(arch, "x86_64")) {
+ g_test_message("Skipping test for non-x86");
+ return 0;
+ }
+
+ /* Create a temporary raw image */
+ fd = mkstemp(tmp_path);
+ g_assert(fd >= 0);
+ ret = ftruncate(fd, TEST_IMAGE_SIZE);
+ g_assert(ret == 0);
+ close(fd);
+
+ /* Run the tests */
+ qtest_add_func("/ahci/sanity", test_sanity);
+ qtest_add_func("/ahci/pci_spec", test_pci_spec);
+ qtest_add_func("/ahci/pci_enable", test_pci_enable);
+ qtest_add_func("/ahci/hba_spec", test_hba_spec);
+ qtest_add_func("/ahci/hba_enable", test_hba_enable);
+ qtest_add_func("/ahci/identify", test_identify);
+
+ ret = g_test_run();
+
+ /* Cleanup */
+ unlink(tmp_path);
+
+ return ret;
+}
diff --git a/tests/bios-tables-test.c b/tests/bios-tables-test.c
index 045eb2757..9e4d20592 100644
--- a/tests/bios-tables-test.c
+++ b/tests/bios-tables-test.c
@@ -714,14 +714,12 @@ static void test_acpi_one(const char *params, test_data *data)
uint8_t signature_high;
uint16_t signature;
int i;
- const char *device = "";
- if (!g_strcmp0(data->machine, MACHINE_Q35)) {
- device = ",id=hd -device ide-hd,drive=hd";
- }
+ args = g_strdup_printf("-net none -display none %s "
+ "-drive id=hd0,if=none,file=%s "
+ "-device ide-hd,drive=hd0 ",
+ params ? params : "", disk);
- args = g_strdup_printf("-net none -display none %s -drive file=%s%s,",
- params ? params : "", disk, device);
qtest_start(args);
/* Wait at most 1 minute */
@@ -790,6 +788,11 @@ int main(int argc, char *argv[])
const char *arch = qtest_get_arch();
FILE *f = fopen(disk, "w");
int ret;
+
+ if (!f) {
+ fprintf(stderr, "Couldn't open \"%s\": %s", disk, strerror(errno));
+ return 1;
+ }
fwrite(boot_sector, 1, sizeof boot_sector, f);
fclose(f);
diff --git a/tests/blockdev-test.c b/tests/blockdev-test.c
deleted file mode 100644
index c940e0069..000000000
--- a/tests/blockdev-test.c
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * blockdev.c test cases
- *
- * Copyright (C) 2013 Red Hat Inc.
- *
- * Authors:
- * Stefan Hajnoczi <stefanha@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 <string.h>
-#include "libqtest.h"
-
-static void test_drive_add_empty(void)
-{
- QDict *response;
- const char *response_return;
-
- /* Start with an empty drive */
- qtest_start("-drive if=none,id=drive0");
-
- /* Delete the drive */
- response = qmp("{\"execute\": \"human-monitor-command\","
- " \"arguments\": {"
- " \"command-line\": \"drive_del drive0\""
- "}}");
- g_assert(response);
- response_return = qdict_get_try_str(response, "return");
- g_assert(response_return);
- g_assert(strcmp(response_return, "") == 0);
- QDECREF(response);
-
- /* Ensure re-adding the drive works - there should be no duplicate ID error
- * because the old drive must be gone.
- */
- response = qmp("{\"execute\": \"human-monitor-command\","
- " \"arguments\": {"
- " \"command-line\": \"drive_add 0 if=none,id=drive0\""
- "}}");
- g_assert(response);
- response_return = qdict_get_try_str(response, "return");
- g_assert(response_return);
- g_assert(strcmp(response_return, "OK\r\n") == 0);
- QDECREF(response);
-
- qtest_end();
-}
-
-int main(int argc, char **argv)
-{
- g_test_init(&argc, &argv, NULL);
-
- qtest_add_func("/qmp/drive_add_empty", test_drive_add_empty);
-
- return g_test_run();
-}
diff --git a/tests/drive_del-test.c b/tests/drive_del-test.c
new file mode 100644
index 000000000..53fa96926
--- /dev/null
+++ b/tests/drive_del-test.c
@@ -0,0 +1,137 @@
+/*
+ * blockdev.c test cases
+ *
+ * Copyright (C) 2013-2014 Red Hat Inc.
+ *
+ * Authors:
+ * Stefan Hajnoczi <stefanha@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 <string.h>
+#include "libqtest.h"
+
+static void drive_add(void)
+{
+ QDict *response;
+
+ response = qmp("{'execute': 'human-monitor-command',"
+ " 'arguments': {"
+ " 'command-line': 'drive_add 0 if=none,id=drive0'"
+ "}}");
+ g_assert(response);
+ g_assert_cmpstr(qdict_get_try_str(response, "return"), ==, "OK\r\n");
+ QDECREF(response);
+}
+
+static void drive_del(void)
+{
+ QDict *response;
+
+ response = qmp("{'execute': 'human-monitor-command',"
+ " 'arguments': {"
+ " 'command-line': 'drive_del drive0'"
+ "}}");
+ g_assert(response);
+ g_assert_cmpstr(qdict_get_try_str(response, "return"), ==, "");
+ QDECREF(response);
+}
+
+static void device_del(void)
+{
+ QDict *response;
+
+ /* Complication: ignore DEVICE_DELETED event */
+ qmp_discard_response("{'execute': 'device_del',"
+ " 'arguments': { 'id': 'dev0' } }");
+ response = qmp_receive();
+ g_assert(response);
+ g_assert(qdict_haskey(response, "return"));
+ QDECREF(response);
+}
+
+static void test_drive_without_dev(void)
+{
+ /* Start with an empty drive */
+ qtest_start("-drive if=none,id=drive0");
+
+ /* Delete the drive */
+ drive_del();
+
+ /* Ensure re-adding the drive works - there should be no duplicate ID error
+ * because the old drive must be gone.
+ */
+ drive_add();
+
+ qtest_end();
+}
+
+static void test_after_failed_device_add(void)
+{
+ QDict *response;
+ QDict *error;
+
+ qtest_start("-drive if=none,id=drive0");
+
+ /* Make device_add fail. If this leaks the virtio-blk-pci device then a
+ * reference to drive0 will also be held (via qdev properties).
+ */
+ response = qmp("{'execute': 'device_add',"
+ " 'arguments': {"
+ " 'driver': 'virtio-blk-pci',"
+ " 'drive': 'drive0'"
+ "}}");
+ g_assert(response);
+ error = qdict_get_qdict(response, "error");
+ g_assert_cmpstr(qdict_get_try_str(error, "class"), ==, "GenericError");
+ QDECREF(response);
+
+ /* Delete the drive */
+ drive_del();
+
+ /* Try to re-add the drive. This fails with duplicate IDs if a leaked
+ * virtio-blk-pci exists that holds a reference to the old drive0.
+ */
+ drive_add();
+
+ qtest_end();
+}
+
+static void test_drive_del_device_del(void)
+{
+ /* Start with a drive used by a device that unplugs instantaneously */
+ qtest_start("-drive if=none,id=drive0,file=/dev/null"
+ " -device virtio-scsi-pci"
+ " -device scsi-hd,drive=drive0,id=dev0");
+
+ /*
+ * Delete the drive, and then the device
+ * Doing it in this order takes notoriously tricky special paths
+ */
+ drive_del();
+ device_del();
+
+ qtest_end();
+}
+
+int main(int argc, char **argv)
+{
+ const char *arch = qtest_get_arch();
+
+ g_test_init(&argc, &argv, NULL);
+
+ qtest_add_func("/drive_del/without-dev", test_drive_without_dev);
+
+ /* TODO I guess any arch with PCI would do */
+ if (!strcmp(arch, "i386") || !strcmp(arch, "x86_64")) {
+ qtest_add_func("/drive_del/after_failed_device_add",
+ test_after_failed_device_add);
+ qtest_add_func("/blockdev/drive_del_device_del",
+ test_drive_del_device_del);
+ }
+
+ return g_test_run();
+}
diff --git a/tests/ide-test.c b/tests/ide-test.c
index 4a0d97f19..b7a97e936 100644
--- a/tests/ide-test.c
+++ b/tests/ide-test.c
@@ -106,6 +106,7 @@ static QPCIBus *pcibus = NULL;
static QGuestAllocator *guest_malloc;
static char tmp_path[] = "/tmp/qtest.XXXXXX";
+static char debug_path[] = "/tmp/qtest-blkdebug.XXXXXX";
static void ide_test_start(const char *cmdline_fmt, ...)
{
@@ -119,10 +120,14 @@ static void ide_test_start(const char *cmdline_fmt, ...)
qtest_start(cmdline);
qtest_irq_intercept_in(global_qtest, "ioapic");
guest_malloc = pc_alloc_init();
+
+ g_free(cmdline);
}
static void ide_test_quit(void)
{
+ pc_alloc_uninit(guest_malloc);
+ guest_malloc = NULL;
qtest_end();
}
@@ -145,7 +150,7 @@ static QPCIDevice *get_pci_device(uint16_t *bmdma_base)
g_assert(device_id == PCI_DEVICE_ID_INTEL_82371SB_1);
/* Map bmdma BAR */
- *bmdma_base = (uint16_t)(uintptr_t) qpci_iomap(dev, 4);
+ *bmdma_base = (uint16_t)(uintptr_t) qpci_iomap(dev, 4, NULL);
qpci_device_enable(dev);
@@ -489,6 +494,91 @@ static void test_flush(void)
ide_test_quit();
}
+static void prepare_blkdebug_script(const char *debug_fn, const char *event)
+{
+ FILE *debug_file = fopen(debug_fn, "w");
+ int ret;
+
+ fprintf(debug_file, "[inject-error]\n");
+ fprintf(debug_file, "event = \"%s\"\n", event);
+ fprintf(debug_file, "errno = \"5\"\n");
+ fprintf(debug_file, "state = \"1\"\n");
+ fprintf(debug_file, "immediately = \"off\"\n");
+ fprintf(debug_file, "once = \"on\"\n");
+
+ fprintf(debug_file, "[set-state]\n");
+ fprintf(debug_file, "event = \"%s\"\n", event);
+ fprintf(debug_file, "new_state = \"2\"\n");
+ fflush(debug_file);
+ g_assert(!ferror(debug_file));
+
+ ret = fclose(debug_file);
+ g_assert(ret == 0);
+}
+
+static void test_retry_flush(void)
+{
+ uint8_t data;
+ const char *s;
+ QDict *response;
+
+ prepare_blkdebug_script(debug_path, "flush_to_disk");
+
+ ide_test_start(
+ "-vnc none "
+ "-drive file=blkdebug:%s:%s,if=ide,cache=writeback,rerror=stop,werror=stop",
+ debug_path, tmp_path);
+
+ /* FLUSH CACHE command on device 0*/
+ outb(IDE_BASE + reg_device, 0);
+ outb(IDE_BASE + reg_command, CMD_FLUSH_CACHE);
+
+ /* Check status while request is in flight*/
+ data = inb(IDE_BASE + reg_status);
+ assert_bit_set(data, BSY | DRDY);
+ assert_bit_clear(data, DF | ERR | DRQ);
+
+ for (;; response = NULL) {
+ response = qmp_receive();
+ if ((qdict_haskey(response, "event")) &&
+ (strcmp(qdict_get_str(response, "event"), "STOP") == 0)) {
+ QDECREF(response);
+ break;
+ }
+ QDECREF(response);
+ }
+
+ /* Complete the command */
+ s = "{'execute':'cont' }";
+ qmp_discard_response(s);
+
+ /* Check registers */
+ data = inb(IDE_BASE + reg_device);
+ g_assert_cmpint(data & DEV, ==, 0);
+
+ do {
+ data = inb(IDE_BASE + reg_status);
+ } while (data & BSY);
+
+ assert_bit_set(data, DRDY);
+ assert_bit_clear(data, BSY | DF | ERR | DRQ);
+
+ ide_test_quit();
+}
+
+static void test_flush_nodev(void)
+{
+ ide_test_start("");
+
+ /* FLUSH CACHE command on device 0*/
+ outb(IDE_BASE + reg_device, 0);
+ outb(IDE_BASE + reg_command, CMD_FLUSH_CACHE);
+
+ /* Just testing that qemu doesn't crash... */
+
+ ide_test_quit();
+}
+
int main(int argc, char **argv)
{
const char *arch = qtest_get_arch();
@@ -501,6 +591,11 @@ int main(int argc, char **argv)
return 0;
}
+ /* Create temporary blkdebug instructions */
+ fd = mkstemp(debug_path);
+ g_assert(fd >= 0);
+ close(fd);
+
/* Create a temporary raw image */
fd = mkstemp(tmp_path);
g_assert(fd >= 0);
@@ -521,11 +616,15 @@ int main(int argc, char **argv)
qtest_add_func("/ide/bmdma/teardown", test_bmdma_teardown);
qtest_add_func("/ide/flush", test_flush);
+ qtest_add_func("/ide/flush_nodev", test_flush_nodev);
+
+ qtest_add_func("/ide/retry/flush", test_retry_flush);
ret = g_test_run();
/* Cleanup */
unlink(tmp_path);
+ unlink(debug_path);
return ret;
}
diff --git a/tests/image-fuzzer/qcow2/__init__.py b/tests/image-fuzzer/qcow2/__init__.py
new file mode 100644
index 000000000..e2ebe1931
--- /dev/null
+++ b/tests/image-fuzzer/qcow2/__init__.py
@@ -0,0 +1 @@
+from layout import create_image
diff --git a/tests/image-fuzzer/qcow2/fuzz.py b/tests/image-fuzzer/qcow2/fuzz.py
new file mode 100644
index 000000000..20eba6bc1
--- /dev/null
+++ b/tests/image-fuzzer/qcow2/fuzz.py
@@ -0,0 +1,367 @@
+# Fuzzing functions for qcow2 fields
+#
+# Copyright (C) 2014 Maria Kustova <maria.k@catit.be>
+#
+# 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/>.
+#
+
+import random
+
+UINT8 = 0xff
+UINT16 = 0xffff
+UINT32 = 0xffffffff
+UINT64 = 0xffffffffffffffff
+# Most significant bit orders
+UINT32_M = 31
+UINT64_M = 63
+# Fuzz vectors
+UINT8_V = [0, 0x10, UINT8/4, UINT8/2 - 1, UINT8/2, UINT8/2 + 1, UINT8 - 1,
+ UINT8]
+UINT16_V = [0, 0x100, 0x1000, UINT16/4, UINT16/2 - 1, UINT16/2, UINT16/2 + 1,
+ UINT16 - 1, UINT16]
+UINT32_V = [0, 0x100, 0x1000, 0x10000, 0x100000, UINT32/4, UINT32/2 - 1,
+ UINT32/2, UINT32/2 + 1, UINT32 - 1, UINT32]
+UINT64_V = UINT32_V + [0x1000000, 0x10000000, 0x100000000, UINT64/4,
+ UINT64/2 - 1, UINT64/2, UINT64/2 + 1, UINT64 - 1,
+ UINT64]
+STRING_V = ['%s%p%x%d', '.1024d', '%.2049d', '%p%p%p%p', '%x%x%x%x',
+ '%d%d%d%d', '%s%s%s%s', '%99999999999s', '%08x', '%%20d', '%%20n',
+ '%%20x', '%%20s', '%s%s%s%s%s%s%s%s%s%s', '%p%p%p%p%p%p%p%p%p%p',
+ '%#0123456x%08x%x%s%p%d%n%o%u%c%h%l%q%j%z%Z%t%i%e%g%f%a%C%S%08x%%',
+ '%s x 129', '%x x 257']
+
+
+def random_from_intervals(intervals):
+ """Select a random integer number from the list of specified intervals.
+
+ Each interval is a tuple of lower and upper limits of the interval. The
+ limits are included. Intervals in a list should not overlap.
+ """
+ total = reduce(lambda x, y: x + y[1] - y[0] + 1, intervals, 0)
+ r = random.randint(0, total - 1) + intervals[0][0]
+ for x in zip(intervals, intervals[1:]):
+ r = r + (r > x[0][1]) * (x[1][0] - x[0][1] - 1)
+ return r
+
+
+def random_bits(bit_ranges):
+ """Generate random binary mask with ones in the specified bit ranges.
+
+ Each bit_ranges is a list of tuples of lower and upper limits of bit
+ positions will be fuzzed. The limits are included. Random amount of bits
+ in range limits will be set to ones. The mask is returned in decimal
+ integer format.
+ """
+ bit_numbers = []
+ # Select random amount of random positions in bit_ranges
+ for rng in bit_ranges:
+ bit_numbers += random.sample(range(rng[0], rng[1] + 1),
+ random.randint(0, rng[1] - rng[0] + 1))
+ val = 0
+ # Set bits on selected positions to ones
+ for bit in bit_numbers:
+ val |= 1 << bit
+ return val
+
+
+def truncate_string(strings, length):
+ """Return strings truncated to specified length."""
+ if type(strings) == list:
+ return [s[:length] for s in strings]
+ else:
+ return strings[:length]
+
+
+def validator(current, pick, choices):
+ """Return a value not equal to the current selected by the pick
+ function from choices.
+ """
+ while True:
+ val = pick(choices)
+ if not val == current:
+ return val
+
+
+def int_validator(current, intervals):
+ """Return a random value from intervals not equal to the current.
+
+ This function is useful for selection from valid values except current one.
+ """
+ return validator(current, random_from_intervals, intervals)
+
+
+def bit_validator(current, bit_ranges):
+ """Return a random bit mask not equal to the current.
+
+ This function is useful for selection from valid values except current one.
+ """
+ return validator(current, random_bits, bit_ranges)
+
+
+def string_validator(current, strings):
+ """Return a random string value from the list not equal to the current.
+
+ This function is useful for selection from valid values except current one.
+ """
+ return validator(current, random.choice, strings)
+
+
+def selector(current, constraints, validate=int_validator):
+ """Select one value from all defined by constraints.
+
+ Each constraint produces one random value satisfying to it. The function
+ randomly selects one value satisfying at least one constraint (depending on
+ constraints overlaps).
+ """
+ def iter_validate(c):
+ """Apply validate() only to constraints represented as lists.
+
+ This auxiliary function replaces short circuit conditions not supported
+ in Python 2.4
+ """
+ if type(c) == list:
+ return validate(current, c)
+ else:
+ return c
+
+ fuzz_values = [iter_validate(c) for c in constraints]
+ # Remove current for cases it's implicitly specified in constraints
+ # Duplicate validator functionality to prevent decreasing of probability
+ # to get one of allowable values
+ # TODO: remove validators after implementation of intelligent selection
+ # of fields will be fuzzed
+ try:
+ fuzz_values.remove(current)
+ except ValueError:
+ pass
+ return random.choice(fuzz_values)
+
+
+def magic(current):
+ """Fuzz magic header field.
+
+ The function just returns the current magic value and provides uniformity
+ of calls for all fuzzing functions.
+ """
+ return current
+
+
+def version(current):
+ """Fuzz version header field."""
+ constraints = UINT32_V + [
+ [(2, 3)], # correct values
+ [(0, 1), (4, UINT32)]
+ ]
+ return selector(current, constraints)
+
+
+def backing_file_offset(current):
+ """Fuzz backing file offset header field."""
+ constraints = UINT64_V
+ return selector(current, constraints)
+
+
+def backing_file_size(current):
+ """Fuzz backing file size header field."""
+ constraints = UINT32_V
+ return selector(current, constraints)
+
+
+def cluster_bits(current):
+ """Fuzz cluster bits header field."""
+ constraints = UINT32_V + [
+ [(9, 20)], # correct values
+ [(0, 9), (20, UINT32)]
+ ]
+ return selector(current, constraints)
+
+
+def size(current):
+ """Fuzz image size header field."""
+ constraints = UINT64_V
+ return selector(current, constraints)
+
+
+def crypt_method(current):
+ """Fuzz crypt method header field."""
+ constraints = UINT32_V + [
+ 1,
+ [(2, UINT32)]
+ ]
+ return selector(current, constraints)
+
+
+def l1_size(current):
+ """Fuzz L1 table size header field."""
+ constraints = UINT32_V
+ return selector(current, constraints)
+
+
+def l1_table_offset(current):
+ """Fuzz L1 table offset header field."""
+ constraints = UINT64_V
+ return selector(current, constraints)
+
+
+def refcount_table_offset(current):
+ """Fuzz refcount table offset header field."""
+ constraints = UINT64_V
+ return selector(current, constraints)
+
+
+def refcount_table_clusters(current):
+ """Fuzz refcount table clusters header field."""
+ constraints = UINT32_V
+ return selector(current, constraints)
+
+
+def nb_snapshots(current):
+ """Fuzz number of snapshots header field."""
+ constraints = UINT32_V
+ return selector(current, constraints)
+
+
+def snapshots_offset(current):
+ """Fuzz snapshots offset header field."""
+ constraints = UINT64_V
+ return selector(current, constraints)
+
+
+def incompatible_features(current):
+ """Fuzz incompatible features header field."""
+ constraints = [
+ [(0, 1)], # allowable values
+ [(0, UINT64_M)]
+ ]
+ return selector(current, constraints, bit_validator)
+
+
+def compatible_features(current):
+ """Fuzz compatible features header field."""
+ constraints = [
+ [(0, UINT64_M)]
+ ]
+ return selector(current, constraints, bit_validator)
+
+
+def autoclear_features(current):
+ """Fuzz autoclear features header field."""
+ constraints = [
+ [(0, UINT64_M)]
+ ]
+ return selector(current, constraints, bit_validator)
+
+
+def refcount_order(current):
+ """Fuzz number of refcount order header field."""
+ constraints = UINT32_V
+ return selector(current, constraints)
+
+
+def header_length(current):
+ """Fuzz number of refcount order header field."""
+ constraints = UINT32_V + [
+ 72,
+ 104,
+ [(0, UINT32)]
+ ]
+ return selector(current, constraints)
+
+
+def bf_name(current):
+ """Fuzz the backing file name."""
+ constraints = [
+ truncate_string(STRING_V, len(current))
+ ]
+ return selector(current, constraints, string_validator)
+
+
+def ext_magic(current):
+ """Fuzz magic field of a header extension."""
+ constraints = UINT32_V
+ return selector(current, constraints)
+
+
+def ext_length(current):
+ """Fuzz length field of a header extension."""
+ constraints = UINT32_V
+ return selector(current, constraints)
+
+
+def bf_format(current):
+ """Fuzz backing file format in the corresponding header extension."""
+ constraints = [
+ truncate_string(STRING_V, len(current)),
+ truncate_string(STRING_V, (len(current) + 7) & ~7) # Fuzz padding
+ ]
+ return selector(current, constraints, string_validator)
+
+
+def feature_type(current):
+ """Fuzz feature type field of a feature name table header extension."""
+ constraints = UINT8_V
+ return selector(current, constraints)
+
+
+def feature_bit_number(current):
+ """Fuzz bit number field of a feature name table header extension."""
+ constraints = UINT8_V
+ return selector(current, constraints)
+
+
+def feature_name(current):
+ """Fuzz feature name field of a feature name table header extension."""
+ constraints = [
+ truncate_string(STRING_V, len(current)),
+ truncate_string(STRING_V, 46) # Fuzz padding (field length = 46)
+ ]
+ return selector(current, constraints, string_validator)
+
+
+def l1_entry(current):
+ """Fuzz an entry of the L1 table."""
+ constraints = UINT64_V
+ # Reserved bits are ignored
+ # Added a possibility when only flags are fuzzed
+ offset = 0x7fffffffffffffff & \
+ random.choice([selector(current, constraints), current])
+ is_cow = random.randint(0, 1)
+ return offset + (is_cow << UINT64_M)
+
+
+def l2_entry(current):
+ """Fuzz an entry of an L2 table."""
+ constraints = UINT64_V
+ # Reserved bits are ignored
+ # Add a possibility when only flags are fuzzed
+ offset = 0x3ffffffffffffffe & \
+ random.choice([selector(current, constraints), current])
+ is_compressed = random.randint(0, 1)
+ is_cow = random.randint(0, 1)
+ is_zero = random.randint(0, 1)
+ value = offset + (is_cow << UINT64_M) + \
+ (is_compressed << UINT64_M - 1) + is_zero
+ return value
+
+
+def refcount_table_entry(current):
+ """Fuzz an entry of the refcount table."""
+ constraints = UINT64_V
+ return selector(current, constraints)
+
+
+def refcount_block_entry(current):
+ """Fuzz an entry of a refcount block."""
+ constraints = UINT16_V
+ return selector(current, constraints)
diff --git a/tests/image-fuzzer/qcow2/layout.py b/tests/image-fuzzer/qcow2/layout.py
new file mode 100644
index 000000000..63e801f4e
--- /dev/null
+++ b/tests/image-fuzzer/qcow2/layout.py
@@ -0,0 +1,612 @@
+# Generator of fuzzed qcow2 images
+#
+# Copyright (C) 2014 Maria Kustova <maria.k@catit.be>
+#
+# 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/>.
+#
+
+import random
+import struct
+import fuzz
+from math import ceil
+from os import urandom
+from itertools import chain
+
+MAX_IMAGE_SIZE = 10 * (1 << 20)
+# Standard sizes
+UINT32_S = 4
+UINT64_S = 8
+
+
+class Field(object):
+
+ """Atomic image element (field).
+
+ The class represents an image field as quadruple of a data format
+ of value necessary for its packing to binary form, an offset from
+ the beginning of the image, a value and a name.
+
+ The field can be iterated as a list [format, offset, value, name].
+ """
+
+ __slots__ = ('fmt', 'offset', 'value', 'name')
+
+ def __init__(self, fmt, offset, val, name):
+ self.fmt = fmt
+ self.offset = offset
+ self.value = val
+ self.name = name
+
+ def __iter__(self):
+ return iter([self.fmt, self.offset, self.value, self.name])
+
+ def __repr__(self):
+ return "Field(fmt='%s', offset=%d, value=%s, name=%s)" % \
+ (self.fmt, self.offset, str(self.value), self.name)
+
+
+class FieldsList(object):
+
+ """List of fields.
+
+ The class allows access to a field in the list by its name.
+ """
+
+ def __init__(self, meta_data=None):
+ if meta_data is None:
+ self.data = []
+ else:
+ self.data = [Field(*f)
+ for f in meta_data]
+
+ def __getitem__(self, name):
+ return [x for x in self.data if x.name == name]
+
+ def __iter__(self):
+ return iter(self.data)
+
+ def __len__(self):
+ return len(self.data)
+
+
+class Image(object):
+
+ """ Qcow2 image object.
+
+ This class allows to create qcow2 images with random valid structures and
+ values, fuzz them via external qcow2.fuzz module and write the result to
+ a file.
+ """
+
+ def __init__(self, backing_file_name=None):
+ """Create a random valid qcow2 image with the correct header and stored
+ backing file name.
+ """
+ cluster_bits, self.image_size = self._size_params()
+ self.cluster_size = 1 << cluster_bits
+ self.header = FieldsList()
+ self.backing_file_name = FieldsList()
+ self.backing_file_format = FieldsList()
+ self.feature_name_table = FieldsList()
+ self.end_of_extension_area = FieldsList()
+ self.l2_tables = FieldsList()
+ self.l1_table = FieldsList()
+ self.refcount_table = FieldsList()
+ self.refcount_blocks = FieldsList()
+ self.ext_offset = 0
+ self.create_header(cluster_bits, backing_file_name)
+ self.set_backing_file_name(backing_file_name)
+ self.data_clusters = self._alloc_data(self.image_size,
+ self.cluster_size)
+ # Percentage of fields will be fuzzed
+ self.bias = random.uniform(0.2, 0.5)
+
+ def __iter__(self):
+ return chain(self.header, self.backing_file_format,
+ self.feature_name_table, self.end_of_extension_area,
+ self.backing_file_name, self.l1_table, self.l2_tables,
+ self.refcount_table, self.refcount_blocks)
+
+ def create_header(self, cluster_bits, backing_file_name=None):
+ """Generate a random valid header."""
+ meta_header = [
+ ['>4s', 0, "QFI\xfb", 'magic'],
+ ['>I', 4, random.randint(2, 3), 'version'],
+ ['>Q', 8, 0, 'backing_file_offset'],
+ ['>I', 16, 0, 'backing_file_size'],
+ ['>I', 20, cluster_bits, 'cluster_bits'],
+ ['>Q', 24, self.image_size, 'size'],
+ ['>I', 32, 0, 'crypt_method'],
+ ['>I', 36, 0, 'l1_size'],
+ ['>Q', 40, 0, 'l1_table_offset'],
+ ['>Q', 48, 0, 'refcount_table_offset'],
+ ['>I', 56, 0, 'refcount_table_clusters'],
+ ['>I', 60, 0, 'nb_snapshots'],
+ ['>Q', 64, 0, 'snapshots_offset'],
+ ['>Q', 72, 0, 'incompatible_features'],
+ ['>Q', 80, 0, 'compatible_features'],
+ ['>Q', 88, 0, 'autoclear_features'],
+ # Only refcount_order = 4 is supported by current (07.2014)
+ # implementation of QEMU
+ ['>I', 96, 4, 'refcount_order'],
+ ['>I', 100, 0, 'header_length']
+ ]
+ self.header = FieldsList(meta_header)
+
+ if self.header['version'][0].value == 2:
+ self.header['header_length'][0].value = 72
+ else:
+ self.header['incompatible_features'][0].value = \
+ random.getrandbits(2)
+ self.header['compatible_features'][0].value = random.getrandbits(1)
+ self.header['header_length'][0].value = 104
+ # Extensions start at the header last field offset and the field size
+ self.ext_offset = struct.calcsize(
+ self.header['header_length'][0].fmt) + \
+ self.header['header_length'][0].offset
+ end_of_extension_area_len = 2 * UINT32_S
+ free_space = self.cluster_size - self.ext_offset - \
+ end_of_extension_area_len
+ # If the backing file name specified and there is enough space for it
+ # in the first cluster, then it's placed in the very end of the first
+ # cluster.
+ if (backing_file_name is not None) and \
+ (free_space >= len(backing_file_name)):
+ self.header['backing_file_size'][0].value = len(backing_file_name)
+ self.header['backing_file_offset'][0].value = \
+ self.cluster_size - len(backing_file_name)
+
+ def set_backing_file_name(self, backing_file_name=None):
+ """Add the name of the backing file at the offset specified
+ in the header.
+ """
+ if (backing_file_name is not None) and \
+ (not self.header['backing_file_offset'][0].value == 0):
+ data_len = len(backing_file_name)
+ data_fmt = '>' + str(data_len) + 's'
+ self.backing_file_name = FieldsList([
+ [data_fmt, self.header['backing_file_offset'][0].value,
+ backing_file_name, 'bf_name']
+ ])
+
+ def set_backing_file_format(self, backing_file_fmt=None):
+ """Generate the header extension for the backing file format."""
+ if backing_file_fmt is not None:
+ # Calculation of the free space available in the first cluster
+ end_of_extension_area_len = 2 * UINT32_S
+ high_border = (self.header['backing_file_offset'][0].value or
+ (self.cluster_size - 1)) - \
+ end_of_extension_area_len
+ free_space = high_border - self.ext_offset
+ ext_size = 2 * UINT32_S + ((len(backing_file_fmt) + 7) & ~7)
+
+ if free_space >= ext_size:
+ ext_data_len = len(backing_file_fmt)
+ ext_data_fmt = '>' + str(ext_data_len) + 's'
+ ext_padding_len = 7 - (ext_data_len - 1) % 8
+ self.backing_file_format = FieldsList([
+ ['>I', self.ext_offset, 0xE2792ACA, 'ext_magic'],
+ ['>I', self.ext_offset + UINT32_S, ext_data_len,
+ 'ext_length'],
+ [ext_data_fmt, self.ext_offset + UINT32_S * 2,
+ backing_file_fmt, 'bf_format']
+ ])
+ self.ext_offset = \
+ struct.calcsize(
+ self.backing_file_format['bf_format'][0].fmt) + \
+ ext_padding_len + \
+ self.backing_file_format['bf_format'][0].offset
+
+ def create_feature_name_table(self):
+ """Generate a random header extension for names of features used in
+ the image.
+ """
+ def gen_feat_ids():
+ """Return random feature type and feature bit."""
+ return (random.randint(0, 2), random.randint(0, 63))
+
+ end_of_extension_area_len = 2 * UINT32_S
+ high_border = (self.header['backing_file_offset'][0].value or
+ (self.cluster_size - 1)) - \
+ end_of_extension_area_len
+ free_space = high_border - self.ext_offset
+ # Sum of sizes of 'magic' and 'length' header extension fields
+ ext_header_len = 2 * UINT32_S
+ fnt_entry_size = 6 * UINT64_S
+ num_fnt_entries = min(10, (free_space - ext_header_len) /
+ fnt_entry_size)
+ if not num_fnt_entries == 0:
+ feature_tables = []
+ feature_ids = []
+ inner_offset = self.ext_offset + ext_header_len
+ feat_name = 'some cool feature'
+ while len(feature_tables) < num_fnt_entries * 3:
+ feat_type, feat_bit = gen_feat_ids()
+ # Remove duplicates
+ while (feat_type, feat_bit) in feature_ids:
+ feat_type, feat_bit = gen_feat_ids()
+ feature_ids.append((feat_type, feat_bit))
+ feat_fmt = '>' + str(len(feat_name)) + 's'
+ feature_tables += [['B', inner_offset,
+ feat_type, 'feature_type'],
+ ['B', inner_offset + 1, feat_bit,
+ 'feature_bit_number'],
+ [feat_fmt, inner_offset + 2,
+ feat_name, 'feature_name']
+ ]
+ inner_offset += fnt_entry_size
+ # No padding for the extension is necessary, because
+ # the extension length is multiple of 8
+ self.feature_name_table = FieldsList([
+ ['>I', self.ext_offset, 0x6803f857, 'ext_magic'],
+ # One feature table contains 3 fields and takes 48 bytes
+ ['>I', self.ext_offset + UINT32_S,
+ len(feature_tables) / 3 * 48, 'ext_length']
+ ] + feature_tables)
+ self.ext_offset = inner_offset
+
+ def set_end_of_extension_area(self):
+ """Generate a mandatory header extension marking end of header
+ extensions.
+ """
+ self.end_of_extension_area = FieldsList([
+ ['>I', self.ext_offset, 0, 'ext_magic'],
+ ['>I', self.ext_offset + UINT32_S, 0, 'ext_length']
+ ])
+
+ def create_l_structures(self):
+ """Generate random valid L1 and L2 tables."""
+ def create_l2_entry(host, guest, l2_cluster):
+ """Generate one L2 entry."""
+ offset = l2_cluster * self.cluster_size
+ l2_size = self.cluster_size / UINT64_S
+ entry_offset = offset + UINT64_S * (guest % l2_size)
+ cluster_descriptor = host * self.cluster_size
+ if not self.header['version'][0].value == 2:
+ cluster_descriptor += random.randint(0, 1)
+ # While snapshots are not supported, bit #63 = 1
+ # Compressed clusters are not supported => bit #62 = 0
+ entry_val = (1 << 63) + cluster_descriptor
+ return ['>Q', entry_offset, entry_val, 'l2_entry']
+
+ def create_l1_entry(l2_cluster, l1_offset, guest):
+ """Generate one L1 entry."""
+ l2_size = self.cluster_size / UINT64_S
+ entry_offset = l1_offset + UINT64_S * (guest / l2_size)
+ # While snapshots are not supported bit #63 = 1
+ entry_val = (1 << 63) + l2_cluster * self.cluster_size
+ return ['>Q', entry_offset, entry_val, 'l1_entry']
+
+ if len(self.data_clusters) == 0:
+ # All metadata for an empty guest image needs 4 clusters:
+ # header, rfc table, rfc block, L1 table.
+ # Header takes cluster #0, other clusters ##1-3 can be used
+ l1_offset = random.randint(1, 3) * self.cluster_size
+ l1 = [['>Q', l1_offset, 0, 'l1_entry']]
+ l2 = []
+ else:
+ meta_data = self._get_metadata()
+ guest_clusters = random.sample(range(self.image_size /
+ self.cluster_size),
+ len(self.data_clusters))
+ # Number of entries in a L1/L2 table
+ l_size = self.cluster_size / UINT64_S
+ # Number of clusters necessary for L1 table
+ l1_size = int(ceil((max(guest_clusters) + 1) / float(l_size**2)))
+ l1_start = self._get_adjacent_clusters(self.data_clusters |
+ meta_data, l1_size)
+ meta_data |= set(range(l1_start, l1_start + l1_size))
+ l1_offset = l1_start * self.cluster_size
+ # Indices of L2 tables
+ l2_ids = []
+ # Host clusters allocated for L2 tables
+ l2_clusters = []
+ # L1 entries
+ l1 = []
+ # L2 entries
+ l2 = []
+ for host, guest in zip(self.data_clusters, guest_clusters):
+ l2_id = guest / l_size
+ if l2_id not in l2_ids:
+ l2_ids.append(l2_id)
+ l2_clusters.append(self._get_adjacent_clusters(
+ self.data_clusters | meta_data | set(l2_clusters),
+ 1))
+ l1.append(create_l1_entry(l2_clusters[-1], l1_offset,
+ guest))
+ l2.append(create_l2_entry(host, guest,
+ l2_clusters[l2_ids.index(l2_id)]))
+ self.l2_tables = FieldsList(l2)
+ self.l1_table = FieldsList(l1)
+ self.header['l1_size'][0].value = int(ceil(UINT64_S * self.image_size /
+ float(self.cluster_size**2)))
+ self.header['l1_table_offset'][0].value = l1_offset
+
+ def create_refcount_structures(self):
+ """Generate random refcount blocks and refcount table."""
+ def allocate_rfc_blocks(data, size):
+ """Return indices of clusters allocated for refcount blocks."""
+ cluster_ids = set()
+ diff = block_ids = set([x / size for x in data])
+ while len(diff) != 0:
+ # Allocate all yet not allocated clusters
+ new = self._get_available_clusters(data | cluster_ids,
+ len(diff))
+ # Indices of new refcount blocks necessary to cover clusters
+ # in 'new'
+ diff = set([x / size for x in new]) - block_ids
+ cluster_ids |= new
+ block_ids |= diff
+ return cluster_ids, block_ids
+
+ def allocate_rfc_table(data, init_blocks, block_size):
+ """Return indices of clusters allocated for the refcount table
+ and updated indices of clusters allocated for blocks and indices
+ of blocks.
+ """
+ blocks = set(init_blocks)
+ clusters = set()
+ # Number of entries in one cluster of the refcount table
+ size = self.cluster_size / UINT64_S
+ # Number of clusters necessary for the refcount table based on
+ # the current number of refcount blocks
+ table_size = int(ceil((max(blocks) + 1) / float(size)))
+ # Index of the first cluster of the refcount table
+ table_start = self._get_adjacent_clusters(data, table_size + 1)
+ # Clusters allocated for the current length of the refcount table
+ table_clusters = set(range(table_start, table_start + table_size))
+ # Clusters allocated for the refcount table including
+ # last optional one for potential l1 growth
+ table_clusters_allocated = set(range(table_start, table_start +
+ table_size + 1))
+ # New refcount blocks necessary for clusters occupied by the
+ # refcount table
+ diff = set([c / block_size for c in table_clusters]) - blocks
+ blocks |= diff
+ while len(diff) != 0:
+ # Allocate clusters for new refcount blocks
+ new = self._get_available_clusters((data | clusters) |
+ table_clusters_allocated,
+ len(diff))
+ # Indices of new refcount blocks necessary to cover
+ # clusters in 'new'
+ diff = set([x / block_size for x in new]) - blocks
+ clusters |= new
+ blocks |= diff
+ # Check if the refcount table needs one more cluster
+ if int(ceil((max(blocks) + 1) / float(size))) > table_size:
+ new_block_id = (table_start + table_size) / block_size
+ # Check if the additional table cluster needs
+ # one more refcount block
+ if new_block_id not in blocks:
+ diff.add(new_block_id)
+ table_clusters.add(table_start + table_size)
+ table_size += 1
+ return table_clusters, blocks, clusters
+
+ def create_table_entry(table_offset, block_cluster, block_size,
+ cluster):
+ """Generate a refcount table entry."""
+ offset = table_offset + UINT64_S * (cluster / block_size)
+ return ['>Q', offset, block_cluster * self.cluster_size,
+ 'refcount_table_entry']
+
+ def create_block_entry(block_cluster, block_size, cluster):
+ """Generate a list of entries for the current block."""
+ entry_size = self.cluster_size / block_size
+ offset = block_cluster * self.cluster_size
+ entry_offset = offset + entry_size * (cluster % block_size)
+ # While snapshots are not supported all refcounts are set to 1
+ return ['>H', entry_offset, 1, 'refcount_block_entry']
+ # Size of a block entry in bits
+ refcount_bits = 1 << self.header['refcount_order'][0].value
+ # Number of refcount entries per refcount block
+ # Convert self.cluster_size from bytes to bits to have the same
+ # base for the numerator and denominator
+ block_size = self.cluster_size * 8 / refcount_bits
+ meta_data = self._get_metadata()
+ if len(self.data_clusters) == 0:
+ # All metadata for an empty guest image needs 4 clusters:
+ # header, rfc table, rfc block, L1 table.
+ # Header takes cluster #0, other clusters ##1-3 can be used
+ block_clusters = set([random.choice(list(set(range(1, 4)) -
+ meta_data))])
+ block_ids = set([0])
+ table_clusters = set([random.choice(list(set(range(1, 4)) -
+ meta_data -
+ block_clusters))])
+ else:
+ block_clusters, block_ids = \
+ allocate_rfc_blocks(self.data_clusters |
+ meta_data, block_size)
+ table_clusters, block_ids, new_clusters = \
+ allocate_rfc_table(self.data_clusters |
+ meta_data |
+ block_clusters,
+ block_ids,
+ block_size)
+ block_clusters |= new_clusters
+
+ meta_data |= block_clusters | table_clusters
+ table_offset = min(table_clusters) * self.cluster_size
+ block_id = None
+ # Clusters allocated for refcount blocks
+ block_clusters = list(block_clusters)
+ # Indices of refcount blocks
+ block_ids = list(block_ids)
+ # Refcount table entries
+ rfc_table = []
+ # Refcount entries
+ rfc_blocks = []
+
+ for cluster in sorted(self.data_clusters | meta_data):
+ if cluster / block_size != block_id:
+ block_id = cluster / block_size
+ block_cluster = block_clusters[block_ids.index(block_id)]
+ rfc_table.append(create_table_entry(table_offset,
+ block_cluster,
+ block_size, cluster))
+ rfc_blocks.append(create_block_entry(block_cluster, block_size,
+ cluster))
+ self.refcount_table = FieldsList(rfc_table)
+ self.refcount_blocks = FieldsList(rfc_blocks)
+
+ self.header['refcount_table_offset'][0].value = table_offset
+ self.header['refcount_table_clusters'][0].value = len(table_clusters)
+
+ def fuzz(self, fields_to_fuzz=None):
+ """Fuzz an image by corrupting values of a random subset of its fields.
+
+ Without parameters the method fuzzes an entire image.
+
+ If 'fields_to_fuzz' is specified then only fields in this list will be
+ fuzzed. 'fields_to_fuzz' can contain both individual fields and more
+ general image elements as a header or tables.
+
+ In the first case the field will be fuzzed always.
+ In the second a random subset of fields will be selected and fuzzed.
+ """
+ def coin():
+ """Return boolean value proportional to a portion of fields to be
+ fuzzed.
+ """
+ return random.random() < self.bias
+
+ if fields_to_fuzz is None:
+ for field in self:
+ if coin():
+ field.value = getattr(fuzz, field.name)(field.value)
+ else:
+ for item in fields_to_fuzz:
+ if len(item) == 1:
+ for field in getattr(self, item[0]):
+ if coin():
+ field.value = getattr(fuzz,
+ field.name)(field.value)
+ else:
+ # If fields with the requested name were not generated
+ # getattr(self, item[0])[item[1]] returns an empty list
+ for field in getattr(self, item[0])[item[1]]:
+ field.value = getattr(fuzz, field.name)(field.value)
+
+ def write(self, filename):
+ """Write an entire image to the file."""
+ image_file = open(filename, 'w')
+ for field in self:
+ image_file.seek(field.offset)
+ image_file.write(struct.pack(field.fmt, field.value))
+
+ for cluster in sorted(self.data_clusters):
+ image_file.seek(cluster * self.cluster_size)
+ image_file.write(urandom(self.cluster_size))
+
+ # Align the real image size to the cluster size
+ image_file.seek(0, 2)
+ size = image_file.tell()
+ rounded = (size + self.cluster_size - 1) & ~(self.cluster_size - 1)
+ if rounded > size:
+ image_file.seek(rounded - 1)
+ image_file.write("\0")
+ image_file.close()
+
+ @staticmethod
+ def _size_params():
+ """Generate a random image size aligned to a random correct
+ cluster size.
+ """
+ cluster_bits = random.randrange(9, 21)
+ cluster_size = 1 << cluster_bits
+ img_size = random.randrange(0, MAX_IMAGE_SIZE + 1, cluster_size)
+ return (cluster_bits, img_size)
+
+ @staticmethod
+ def _get_available_clusters(used, number):
+ """Return a set of indices of not allocated clusters.
+
+ 'used' contains indices of currently allocated clusters.
+ All clusters that cannot be allocated between 'used' clusters will have
+ indices appended to the end of 'used'.
+ """
+ append_id = max(used) + 1
+ free = set(range(1, append_id)) - used
+ if len(free) >= number:
+ return set(random.sample(free, number))
+ else:
+ return free | set(range(append_id, append_id + number - len(free)))
+
+ @staticmethod
+ def _get_adjacent_clusters(used, size):
+ """Return an index of the first cluster in the sequence of free ones.
+
+ 'used' contains indices of currently allocated clusters. 'size' is the
+ length of the sequence of free clusters.
+ If the sequence of 'size' is not available between 'used' clusters, its
+ first index will be append to the end of 'used'.
+ """
+ def get_cluster_id(lst, length):
+ """Return the first index of the sequence of the specified length
+ or None if the sequence cannot be inserted in the list.
+ """
+ if len(lst) != 0:
+ pairs = []
+ pair = (lst[0], 1)
+ for i in range(1, len(lst)):
+ if lst[i] == lst[i-1] + 1:
+ pair = (lst[i], pair[1] + 1)
+ else:
+ pairs.append(pair)
+ pair = (lst[i], 1)
+ pairs.append(pair)
+ random.shuffle(pairs)
+ for x, s in pairs:
+ if s >= length:
+ return x - length + 1
+ return None
+
+ append_id = max(used) + 1
+ free = list(set(range(1, append_id)) - used)
+ idx = get_cluster_id(free, size)
+ if idx is None:
+ return append_id
+ else:
+ return idx
+
+ @staticmethod
+ def _alloc_data(img_size, cluster_size):
+ """Return a set of random indices of clusters allocated for guest data.
+ """
+ num_of_cls = img_size/cluster_size
+ return set(random.sample(range(1, num_of_cls + 1),
+ random.randint(0, num_of_cls)))
+
+ def _get_metadata(self):
+ """Return indices of clusters allocated for image metadata."""
+ ids = set()
+ for x in self:
+ ids.add(x.offset/self.cluster_size)
+ return ids
+
+
+def create_image(test_img_path, backing_file_name=None, backing_file_fmt=None,
+ fields_to_fuzz=None):
+ """Create a fuzzed image and write it to the specified file."""
+ image = Image(backing_file_name)
+ image.set_backing_file_format(backing_file_fmt)
+ image.create_feature_name_table()
+ image.set_end_of_extension_area()
+ image.create_l_structures()
+ image.create_refcount_structures()
+ image.fuzz(fields_to_fuzz)
+ image.write(test_img_path)
+ return image.image_size
diff --git a/tests/image-fuzzer/runner.py b/tests/image-fuzzer/runner.py
new file mode 100755
index 000000000..0a8743ef4
--- /dev/null
+++ b/tests/image-fuzzer/runner.py
@@ -0,0 +1,437 @@
+#!/usr/bin/env python
+
+# Tool for running fuzz tests
+#
+# Copyright (C) 2014 Maria Kustova <maria.k@catit.be>
+#
+# 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/>.
+#
+
+import sys
+import os
+import signal
+import subprocess
+import random
+import shutil
+from itertools import count
+import time
+import getopt
+import StringIO
+import resource
+
+try:
+ import json
+except ImportError:
+ try:
+ import simplejson as json
+ except ImportError:
+ print >>sys.stderr, \
+ "Warning: Module for JSON processing is not found.\n" \
+ "'--config' and '--command' options are not supported."
+
+# Backing file sizes in MB
+MAX_BACKING_FILE_SIZE = 10
+MIN_BACKING_FILE_SIZE = 1
+
+
+def multilog(msg, *output):
+ """ Write an object to all of specified file descriptors."""
+ for fd in output:
+ fd.write(msg)
+ fd.flush()
+
+
+def str_signal(sig):
+ """ Convert a numeric value of a system signal to the string one
+ defined by the current operational system.
+ """
+ for k, v in signal.__dict__.items():
+ if v == sig:
+ return k
+
+
+def run_app(fd, q_args):
+ """Start an application with specified arguments and return its exit code
+ or kill signal depending on the result of execution.
+ """
+
+ class Alarm(Exception):
+ """Exception for signal.alarm events."""
+ pass
+
+ def handler(*args):
+ """Notify that an alarm event occurred."""
+ raise Alarm
+
+ signal.signal(signal.SIGALRM, handler)
+ signal.alarm(600)
+ term_signal = signal.SIGKILL
+ devnull = open('/dev/null', 'r+')
+ process = subprocess.Popen(q_args, stdin=devnull,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ try:
+ out, err = process.communicate()
+ signal.alarm(0)
+ fd.write(out)
+ fd.write(err)
+ fd.flush()
+ return process.returncode
+
+ except Alarm:
+ os.kill(process.pid, term_signal)
+ fd.write('The command was terminated by timeout.\n')
+ fd.flush()
+ return -term_signal
+
+
+class TestException(Exception):
+ """Exception for errors risen by TestEnv objects."""
+ pass
+
+
+class TestEnv(object):
+
+ """Test object.
+
+ The class sets up test environment, generates backing and test images
+ and executes application under tests with specified arguments and a test
+ image provided.
+
+ All logs are collected.
+
+ The summary log will contain short descriptions and statuses of tests in
+ a run.
+
+ The test log will include application (e.g. 'qemu-img') logs besides info
+ sent to the summary log.
+ """
+
+ def __init__(self, test_id, seed, work_dir, run_log,
+ cleanup=True, log_all=False):
+ """Set test environment in a specified work directory.
+
+ Path to qemu-img and qemu-io will be retrieved from 'QEMU_IMG' and
+ 'QEMU_IO' environment variables.
+ """
+ if seed is not None:
+ self.seed = seed
+ else:
+ self.seed = str(random.randint(0, sys.maxint))
+ random.seed(self.seed)
+
+ self.init_path = os.getcwd()
+ self.work_dir = work_dir
+ self.current_dir = os.path.join(work_dir, 'test-' + test_id)
+ self.qemu_img = \
+ os.environ.get('QEMU_IMG', 'qemu-img').strip().split(' ')
+ self.qemu_io = os.environ.get('QEMU_IO', 'qemu-io').strip().split(' ')
+ self.commands = [['qemu-img', 'check', '-f', 'qcow2', '$test_img'],
+ ['qemu-img', 'info', '-f', 'qcow2', '$test_img'],
+ ['qemu-io', '$test_img', '-c', 'read $off $len'],
+ ['qemu-io', '$test_img', '-c', 'write $off $len'],
+ ['qemu-io', '$test_img', '-c',
+ 'aio_read $off $len'],
+ ['qemu-io', '$test_img', '-c',
+ 'aio_write $off $len'],
+ ['qemu-io', '$test_img', '-c', 'flush'],
+ ['qemu-io', '$test_img', '-c',
+ 'discard $off $len'],
+ ['qemu-io', '$test_img', '-c',
+ 'truncate $off']]
+ for fmt in ['raw', 'vmdk', 'vdi', 'qcow2', 'file', 'qed', 'vpc']:
+ self.commands.append(
+ ['qemu-img', 'convert', '-f', 'qcow2', '-O', fmt,
+ '$test_img', 'converted_image.' + fmt])
+
+ try:
+ os.makedirs(self.current_dir)
+ except OSError, e:
+ print >>sys.stderr, \
+ "Error: The working directory '%s' cannot be used. Reason: %s"\
+ % (self.work_dir, e[1])
+ raise TestException
+ self.log = open(os.path.join(self.current_dir, "test.log"), "w")
+ self.parent_log = open(run_log, "a")
+ self.failed = False
+ self.cleanup = cleanup
+ self.log_all = log_all
+
+ def _create_backing_file(self):
+ """Create a backing file in the current directory.
+
+ Return a tuple of a backing file name and format.
+
+ Format of a backing file is randomly chosen from all formats supported
+ by 'qemu-img create'.
+ """
+ # All formats supported by the 'qemu-img create' command.
+ backing_file_fmt = random.choice(['raw', 'vmdk', 'vdi', 'qcow2',
+ 'file', 'qed', 'vpc'])
+ backing_file_name = 'backing_img.' + backing_file_fmt
+ backing_file_size = random.randint(MIN_BACKING_FILE_SIZE,
+ MAX_BACKING_FILE_SIZE) * (1 << 20)
+ cmd = self.qemu_img + ['create', '-f', backing_file_fmt,
+ backing_file_name, str(backing_file_size)]
+ temp_log = StringIO.StringIO()
+ retcode = run_app(temp_log, cmd)
+ if retcode == 0:
+ temp_log.close()
+ return (backing_file_name, backing_file_fmt)
+ else:
+ multilog("Warning: The %s backing file was not created.\n\n"
+ % backing_file_fmt, sys.stderr, self.log, self.parent_log)
+ self.log.write("Log for the failure:\n" + temp_log.getvalue() +
+ '\n\n')
+ temp_log.close()
+ return (None, None)
+
+ def execute(self, input_commands=None, fuzz_config=None):
+ """ Execute a test.
+
+ The method creates backing and test images, runs test app and analyzes
+ its exit status. If the application was killed by a signal, the test
+ is marked as failed.
+ """
+ if input_commands is None:
+ commands = self.commands
+ else:
+ commands = input_commands
+
+ os.chdir(self.current_dir)
+ backing_file_name, backing_file_fmt = self._create_backing_file()
+ img_size = image_generator.create_image(
+ 'test.img', backing_file_name, backing_file_fmt, fuzz_config)
+ for item in commands:
+ shutil.copy('test.img', 'copy.img')
+ # 'off' and 'len' are multiple of the sector size
+ sector_size = 512
+ start = random.randrange(0, img_size + 1, sector_size)
+ end = random.randrange(start, img_size + 1, sector_size)
+
+ if item[0] == 'qemu-img':
+ current_cmd = list(self.qemu_img)
+ elif item[0] == 'qemu-io':
+ current_cmd = list(self.qemu_io)
+ else:
+ multilog("Warning: test command '%s' is not defined.\n"
+ % item[0], sys.stderr, self.log, self.parent_log)
+ continue
+ # Replace all placeholders with their real values
+ for v in item[1:]:
+ c = (v
+ .replace('$test_img', 'copy.img')
+ .replace('$off', str(start))
+ .replace('$len', str(end - start)))
+ current_cmd.append(c)
+
+ # Log string with the test header
+ test_summary = "Seed: %s\nCommand: %s\nTest directory: %s\n" \
+ "Backing file: %s\n" \
+ % (self.seed, " ".join(current_cmd),
+ self.current_dir, backing_file_name)
+ temp_log = StringIO.StringIO()
+ try:
+ retcode = run_app(temp_log, current_cmd)
+ except OSError, e:
+ multilog("%sError: Start of '%s' failed. Reason: %s\n\n"
+ % (test_summary, os.path.basename(current_cmd[0]),
+ e[1]),
+ sys.stderr, self.log, self.parent_log)
+ raise TestException
+
+ if retcode < 0:
+ self.log.write(temp_log.getvalue())
+ multilog("%sFAIL: Test terminated by signal %s\n\n"
+ % (test_summary, str_signal(-retcode)),
+ sys.stderr, self.log, self.parent_log)
+ self.failed = True
+ else:
+ if self.log_all:
+ self.log.write(temp_log.getvalue())
+ multilog("%sPASS: Application exited with the code " \
+ "'%d'\n\n" % (test_summary, retcode),
+ sys.stdout, self.log, self.parent_log)
+ temp_log.close()
+ os.remove('copy.img')
+
+ def finish(self):
+ """Restore the test environment after a test execution."""
+ self.log.close()
+ self.parent_log.close()
+ os.chdir(self.init_path)
+ if self.cleanup and not self.failed:
+ shutil.rmtree(self.current_dir)
+
+if __name__ == '__main__':
+
+ def usage():
+ print """
+ Usage: runner.py [OPTION...] TEST_DIR IMG_GENERATOR
+
+ Set up test environment in TEST_DIR and run a test in it. A module for
+ test image generation should be specified via IMG_GENERATOR.
+
+ Example:
+ runner.py -c '[["qemu-img", "info", "$test_img"]]' /tmp/test qcow2
+
+ Optional arguments:
+ -h, --help display this help and exit
+ -d, --duration=NUMBER finish tests after NUMBER of seconds
+ -c, --command=JSON run tests for all commands specified in
+ the JSON array
+ -s, --seed=STRING seed for a test image generation,
+ by default will be generated randomly
+ --config=JSON take fuzzer configuration from the JSON
+ array
+ -k, --keep_passed don't remove folders of passed tests
+ -v, --verbose log information about passed tests
+
+ JSON:
+
+ '--command' accepts a JSON array of commands. Each command presents
+ an application under test with all its paramaters as a list of strings,
+ e.g. ["qemu-io", "$test_img", "-c", "write $off $len"].
+
+ Supported application aliases: 'qemu-img' and 'qemu-io'.
+
+ Supported argument aliases: $test_img for the fuzzed image, $off
+ for an offset, $len for length.
+
+ Values for $off and $len will be generated based on the virtual disk
+ size of the fuzzed image.
+
+ Paths to 'qemu-img' and 'qemu-io' are retrevied from 'QEMU_IMG' and
+ 'QEMU_IO' environment variables.
+
+ '--config' accepts a JSON array of fields to be fuzzed, e.g.
+ '[["header"], ["header", "version"]]'.
+
+ Each of the list elements can consist of a complex image element only
+ as ["header"] or ["feature_name_table"] or an exact field as
+ ["header", "version"]. In the first case random portion of the element
+ fields will be fuzzed, in the second one the specified field will be
+ fuzzed always.
+
+ If '--config' argument is specified, fields not listed in
+ the configuration array will not be fuzzed.
+ """
+
+ def run_test(test_id, seed, work_dir, run_log, cleanup, log_all,
+ command, fuzz_config):
+ """Setup environment for one test and execute this test."""
+ try:
+ test = TestEnv(test_id, seed, work_dir, run_log, cleanup,
+ log_all)
+ except TestException:
+ sys.exit(1)
+
+ # Python 2.4 doesn't support 'finally' and 'except' in the same 'try'
+ # block
+ try:
+ try:
+ test.execute(command, fuzz_config)
+ except TestException:
+ sys.exit(1)
+ finally:
+ test.finish()
+
+ def should_continue(duration, start_time):
+ """Return True if a new test can be started and False otherwise."""
+ current_time = int(time.time())
+ return (duration is None) or (current_time - start_time < duration)
+
+ try:
+ opts, args = getopt.gnu_getopt(sys.argv[1:], 'c:hs:kvd:',
+ ['command=', 'help', 'seed=', 'config=',
+ 'keep_passed', 'verbose', 'duration='])
+ except getopt.error, e:
+ print >>sys.stderr, \
+ "Error: %s\n\nTry 'runner.py --help' for more information" % e
+ sys.exit(1)
+
+ command = None
+ cleanup = True
+ log_all = False
+ seed = None
+ config = None
+ duration = None
+ for opt, arg in opts:
+ if opt in ('-h', '--help'):
+ usage()
+ sys.exit()
+ elif opt in ('-c', '--command'):
+ try:
+ command = json.loads(arg)
+ except (TypeError, ValueError, NameError), e:
+ print >>sys.stderr, \
+ "Error: JSON array of test commands cannot be loaded.\n" \
+ "Reason: %s" % e
+ sys.exit(1)
+ elif opt in ('-k', '--keep_passed'):
+ cleanup = False
+ elif opt in ('-v', '--verbose'):
+ log_all = True
+ elif opt in ('-s', '--seed'):
+ seed = arg
+ elif opt in ('-d', '--duration'):
+ duration = int(arg)
+ elif opt == '--config':
+ try:
+ config = json.loads(arg)
+ except (TypeError, ValueError, NameError), e:
+ print >>sys.stderr, \
+ "Error: JSON array with the fuzzer configuration cannot" \
+ " be loaded\nReason: %s" % e
+ sys.exit(1)
+
+ if not len(args) == 2:
+ print >>sys.stderr, \
+ "Expected two parameters\nTry 'runner.py --help'" \
+ " for more information."
+ sys.exit(1)
+
+ work_dir = os.path.realpath(args[0])
+ # run_log is created in 'main', because multiple tests are expected to
+ # log in it
+ run_log = os.path.join(work_dir, 'run.log')
+
+ # Add the path to the image generator module to sys.path
+ sys.path.append(os.path.realpath(os.path.dirname(args[1])))
+ # Remove a script extension from image generator module if any
+ generator_name = os.path.splitext(os.path.basename(args[1]))[0]
+
+ try:
+ image_generator = __import__(generator_name)
+ except ImportError, e:
+ print >>sys.stderr, \
+ "Error: The image generator '%s' cannot be imported.\n" \
+ "Reason: %s" % (generator_name, e)
+ sys.exit(1)
+
+ # Enable core dumps
+ resource.setrlimit(resource.RLIMIT_CORE, (-1, -1))
+ # If a seed is specified, only one test will be executed.
+ # Otherwise runner will terminate after a keyboard interruption
+ start_time = int(time.time())
+ test_id = count(1)
+ while should_continue(duration, start_time):
+ try:
+ run_test(str(test_id.next()), seed, work_dir, run_log, cleanup,
+ log_all, command, config)
+ except (KeyboardInterrupt, SystemExit):
+ sys.exit(1)
+
+ if seed is not None:
+ break
diff --git a/tests/libqos/malloc-pc.c b/tests/libqos/malloc-pc.c
index db1496c66..f4218c645 100644
--- a/tests/libqos/malloc-pc.c
+++ b/tests/libqos/malloc-pc.c
@@ -17,45 +17,294 @@
#include "hw/nvram/fw_cfg.h"
#include "qemu-common.h"
+#include "qemu/queue.h"
#include <glib.h>
#define PAGE_SIZE (4096)
+#define MLIST_ENTNAME entries
+typedef QTAILQ_HEAD(MemList, MemBlock) MemList;
+typedef struct MemBlock {
+ QTAILQ_ENTRY(MemBlock) MLIST_ENTNAME;
+ uint64_t size;
+ uint64_t addr;
+} MemBlock;
+
typedef struct PCAlloc
{
QGuestAllocator alloc;
-
+ PCAllocOpts opts;
uint64_t start;
uint64_t end;
+
+ MemList used;
+ MemList free;
} PCAlloc;
-static uint64_t pc_alloc(QGuestAllocator *allocator, size_t size)
+static MemBlock *mlist_new(uint64_t addr, uint64_t size)
{
- PCAlloc *s = container_of(allocator, PCAlloc, alloc);
- uint64_t addr;
+ MemBlock *block;
+
+ if (!size) {
+ return NULL;
+ }
+ block = g_malloc0(sizeof(MemBlock));
+ block->addr = addr;
+ block->size = size;
- size += (PAGE_SIZE - 1);
- size &= PAGE_SIZE;
+ return block;
+}
+
+static void mlist_delete(MemList *list, MemBlock *node)
+{
+ g_assert(list && node);
+ QTAILQ_REMOVE(list, node, MLIST_ENTNAME);
+ g_free(node);
+}
+
+static MemBlock *mlist_find_key(MemList *head, uint64_t addr)
+{
+ MemBlock *node;
+ QTAILQ_FOREACH(node, head, MLIST_ENTNAME) {
+ if (node->addr == addr) {
+ return node;
+ }
+ }
+ return NULL;
+}
+
+static MemBlock *mlist_find_space(MemList *head, uint64_t size)
+{
+ MemBlock *node;
+
+ QTAILQ_FOREACH(node, head, MLIST_ENTNAME) {
+ if (node->size >= size) {
+ return node;
+ }
+ }
+ return NULL;
+}
+
+static MemBlock *mlist_sort_insert(MemList *head, MemBlock *insr)
+{
+ MemBlock *node;
+ g_assert(head && insr);
+
+ QTAILQ_FOREACH(node, head, MLIST_ENTNAME) {
+ if (insr->addr < node->addr) {
+ QTAILQ_INSERT_BEFORE(node, insr, MLIST_ENTNAME);
+ return insr;
+ }
+ }
+
+ QTAILQ_INSERT_TAIL(head, insr, MLIST_ENTNAME);
+ return insr;
+}
+
+static inline uint64_t mlist_boundary(MemBlock *node)
+{
+ return node->size + node->addr;
+}
+
+static MemBlock *mlist_join(MemList *head, MemBlock *left, MemBlock *right)
+{
+ g_assert(head && left && right);
+
+ left->size += right->size;
+ mlist_delete(head, right);
+ return left;
+}
+
+static void mlist_coalesce(MemList *head, MemBlock *node)
+{
+ g_assert(node);
+ MemBlock *left;
+ MemBlock *right;
+ char merge;
+
+ do {
+ merge = 0;
+ left = QTAILQ_PREV(node, MemList, MLIST_ENTNAME);
+ right = QTAILQ_NEXT(node, MLIST_ENTNAME);
+
+ /* clowns to the left of me */
+ if (left && mlist_boundary(left) == node->addr) {
+ node = mlist_join(head, left, node);
+ merge = 1;
+ }
+
+ /* jokers to the right */
+ if (right && mlist_boundary(node) == right->addr) {
+ node = mlist_join(head, node, right);
+ merge = 1;
+ }
+
+ } while (merge);
+}
+
+static uint64_t pc_mlist_fulfill(PCAlloc *s, MemBlock *freenode, uint64_t size)
+{
+ uint64_t addr;
+ MemBlock *usednode;
- g_assert_cmpint((s->start + size), <=, s->end);
+ g_assert(freenode);
+ g_assert_cmpint(freenode->size, >=, size);
- addr = s->start;
- s->start += size;
+ addr = freenode->addr;
+ if (freenode->size == size) {
+ /* re-use this freenode as our used node */
+ QTAILQ_REMOVE(&s->free, freenode, MLIST_ENTNAME);
+ usednode = freenode;
+ } else {
+ /* adjust the free node and create a new used node */
+ freenode->addr += size;
+ freenode->size -= size;
+ usednode = mlist_new(addr, size);
+ }
+ mlist_sort_insert(&s->used, usednode);
return addr;
}
+/* To assert the correctness of the list.
+ * Used only if PC_ALLOC_PARANOID is set. */
+static void pc_mlist_check(PCAlloc *s)
+{
+ MemBlock *node;
+ uint64_t addr = s->start > 0 ? s->start - 1 : 0;
+ uint64_t next = s->start;
+
+ QTAILQ_FOREACH(node, &s->free, MLIST_ENTNAME) {
+ g_assert_cmpint(node->addr, >, addr);
+ g_assert_cmpint(node->addr, >=, next);
+ addr = node->addr;
+ next = node->addr + node->size;
+ }
+
+ addr = s->start > 0 ? s->start - 1 : 0;
+ next = s->start;
+ QTAILQ_FOREACH(node, &s->used, MLIST_ENTNAME) {
+ g_assert_cmpint(node->addr, >, addr);
+ g_assert_cmpint(node->addr, >=, next);
+ addr = node->addr;
+ next = node->addr + node->size;
+ }
+}
+
+static uint64_t pc_mlist_alloc(PCAlloc *s, uint64_t size)
+{
+ MemBlock *node;
+
+ node = mlist_find_space(&s->free, size);
+ if (!node) {
+ fprintf(stderr, "Out of guest memory.\n");
+ g_assert_not_reached();
+ }
+ return pc_mlist_fulfill(s, node, size);
+}
+
+static void pc_mlist_free(PCAlloc *s, uint64_t addr)
+{
+ MemBlock *node;
+
+ if (addr == 0) {
+ return;
+ }
+
+ node = mlist_find_key(&s->used, addr);
+ if (!node) {
+ fprintf(stderr, "Error: no record found for an allocation at "
+ "0x%016" PRIx64 ".\n",
+ addr);
+ g_assert_not_reached();
+ }
+
+ /* Rip it out of the used list and re-insert back into the free list. */
+ QTAILQ_REMOVE(&s->used, node, MLIST_ENTNAME);
+ mlist_sort_insert(&s->free, node);
+ mlist_coalesce(&s->free, node);
+}
+
+static uint64_t pc_alloc(QGuestAllocator *allocator, size_t size)
+{
+ PCAlloc *s = container_of(allocator, PCAlloc, alloc);
+ uint64_t rsize = size;
+ uint64_t naddr;
+
+ rsize += (PAGE_SIZE - 1);
+ rsize &= -PAGE_SIZE;
+ g_assert_cmpint((s->start + rsize), <=, s->end);
+ g_assert_cmpint(rsize, >=, size);
+
+ naddr = pc_mlist_alloc(s, rsize);
+ if (s->opts & PC_ALLOC_PARANOID) {
+ pc_mlist_check(s);
+ }
+
+ return naddr;
+}
+
static void pc_free(QGuestAllocator *allocator, uint64_t addr)
{
+ PCAlloc *s = container_of(allocator, PCAlloc, alloc);
+
+ pc_mlist_free(s, addr);
+ if (s->opts & PC_ALLOC_PARANOID) {
+ pc_mlist_check(s);
+ }
+}
+
+/*
+ * Mostly for valgrind happiness, but it does offer
+ * a chokepoint for debugging guest memory leaks, too.
+ */
+void pc_alloc_uninit(QGuestAllocator *allocator)
+{
+ PCAlloc *s = container_of(allocator, PCAlloc, alloc);
+ MemBlock *node;
+ MemBlock *tmp;
+ PCAllocOpts mask;
+
+ /* Check for guest leaks, and destroy the list. */
+ QTAILQ_FOREACH_SAFE(node, &s->used, MLIST_ENTNAME, tmp) {
+ if (s->opts & (PC_ALLOC_LEAK_WARN | PC_ALLOC_LEAK_ASSERT)) {
+ fprintf(stderr, "guest malloc leak @ 0x%016" PRIx64 "; "
+ "size 0x%016" PRIx64 ".\n",
+ node->addr, node->size);
+ }
+ if (s->opts & (PC_ALLOC_LEAK_ASSERT)) {
+ g_assert_not_reached();
+ }
+ g_free(node);
+ }
+
+ /* If we have previously asserted that there are no leaks, then there
+ * should be only one node here with a specific address and size. */
+ mask = PC_ALLOC_LEAK_ASSERT | PC_ALLOC_PARANOID;
+ QTAILQ_FOREACH_SAFE(node, &s->free, MLIST_ENTNAME, tmp) {
+ if ((s->opts & mask) == mask) {
+ if ((node->addr != s->start) ||
+ (node->size != s->end - s->start)) {
+ fprintf(stderr, "Free list is corrupted.\n");
+ g_assert_not_reached();
+ }
+ }
+
+ g_free(node);
+ }
+
+ g_free(s);
}
-QGuestAllocator *pc_alloc_init(void)
+QGuestAllocator *pc_alloc_init_flags(PCAllocOpts flags)
{
PCAlloc *s = g_malloc0(sizeof(*s));
uint64_t ram_size;
QFWCFG *fw_cfg = pc_fw_cfg_init();
+ MemBlock *node;
+ s->opts = flags;
s->alloc.alloc = pc_alloc;
s->alloc.free = pc_free;
@@ -67,5 +316,19 @@ QGuestAllocator *pc_alloc_init(void)
/* Respect PCI hole */
s->end = MIN(ram_size, 0xE0000000);
+ /* clean-up */
+ g_free(fw_cfg);
+
+ QTAILQ_INIT(&s->used);
+ QTAILQ_INIT(&s->free);
+
+ node = mlist_new(s->start, s->end - s->start);
+ QTAILQ_INSERT_HEAD(&s->free, node, MLIST_ENTNAME);
+
return &s->alloc;
}
+
+inline QGuestAllocator *pc_alloc_init(void)
+{
+ return pc_alloc_init_flags(PC_ALLOC_NO_FLAGS);
+}
diff --git a/tests/libqos/malloc-pc.h b/tests/libqos/malloc-pc.h
index ff964abe5..9f525e3b9 100644
--- a/tests/libqos/malloc-pc.h
+++ b/tests/libqos/malloc-pc.h
@@ -15,6 +15,15 @@
#include "libqos/malloc.h"
+typedef enum {
+ PC_ALLOC_NO_FLAGS = 0x00,
+ PC_ALLOC_LEAK_WARN = 0x01,
+ PC_ALLOC_LEAK_ASSERT = 0x02,
+ PC_ALLOC_PARANOID = 0x04
+} PCAllocOpts;
+
QGuestAllocator *pc_alloc_init(void);
+QGuestAllocator *pc_alloc_init_flags(PCAllocOpts flags);
+void pc_alloc_uninit(QGuestAllocator *allocator);
#endif
diff --git a/tests/libqos/malloc.h b/tests/libqos/malloc.h
index 46f600076..556538121 100644
--- a/tests/libqos/malloc.h
+++ b/tests/libqos/malloc.h
@@ -32,7 +32,7 @@ static inline uint64_t guest_alloc(QGuestAllocator *allocator, size_t size)
static inline void guest_free(QGuestAllocator *allocator, uint64_t addr)
{
- allocator->alloc(allocator, addr);
+ allocator->free(allocator, addr);
}
#endif
diff --git a/tests/libqos/pci-pc.c b/tests/libqos/pci-pc.c
index 4adf4006a..6dba0db00 100644
--- a/tests/libqos/pci-pc.c
+++ b/tests/libqos/pci-pc.c
@@ -20,6 +20,9 @@
#include <glib.h>
+#define ACPI_PCIHP_ADDR 0xae00
+#define PCI_EJ_BASE 0x0008
+
typedef struct QPCIBusPC
{
QPCIBus bus;
@@ -144,7 +147,7 @@ static void qpci_pc_config_writel(QPCIBus *bus, int devfn, uint8_t offset, uint3
outl(0xcfc, value);
}
-static void *qpci_pc_iomap(QPCIBus *bus, QPCIDevice *dev, int barno)
+static void *qpci_pc_iomap(QPCIBus *bus, QPCIDevice *dev, int barno, uint64_t *sizeptr)
{
QPCIBusPC *s = container_of(bus, QPCIBusPC, bus);
static const int bar_reg_map[] = {
@@ -173,6 +176,9 @@ static void *qpci_pc_iomap(QPCIBus *bus, QPCIDevice *dev, int barno)
if (size == 0) {
return NULL;
}
+ if (sizeptr) {
+ *sizeptr = size;
+ }
if (io_type == PCI_BASE_ADDRESS_SPACE_IO) {
uint16_t loc;
@@ -237,3 +243,56 @@ QPCIBus *qpci_init_pc(void)
return &ret->bus;
}
+
+void qpci_free_pc(QPCIBus *bus)
+{
+ QPCIBusPC *s = container_of(bus, QPCIBusPC, bus);
+
+ g_free(s);
+}
+
+void qpci_plug_device_test(const char *driver, const char *id,
+ uint8_t slot, const char *opts)
+{
+ QDict *response;
+ char *cmd;
+
+ cmd = g_strdup_printf("{'execute': 'device_add',"
+ " 'arguments': {"
+ " 'driver': '%s',"
+ " 'addr': '%d',"
+ " %s%s"
+ " 'id': '%s'"
+ "}}", driver, slot,
+ opts ? opts : "", opts ? "," : "",
+ id);
+ response = qmp(cmd);
+ g_free(cmd);
+ g_assert(response);
+ g_assert(!qdict_haskey(response, "error"));
+ QDECREF(response);
+}
+
+void qpci_unplug_acpi_device_test(const char *id, uint8_t slot)
+{
+ QDict *response;
+ char *cmd;
+
+ cmd = g_strdup_printf("{'execute': 'device_del',"
+ " 'arguments': {"
+ " 'id': '%s'"
+ "}}", id);
+ response = qmp(cmd);
+ g_free(cmd);
+ g_assert(response);
+ g_assert(!qdict_haskey(response, "error"));
+ QDECREF(response);
+
+ outb(ACPI_PCIHP_ADDR + PCI_EJ_BASE, 1 << slot);
+
+ response = qmp("");
+ g_assert(response);
+ g_assert(qdict_haskey(response, "event"));
+ g_assert(!strcmp(qdict_get_str(response, "event"), "DEVICE_DELETED"));
+ QDECREF(response);
+}
diff --git a/tests/libqos/pci-pc.h b/tests/libqos/pci-pc.h
index 4f7475f6f..26211790c 100644
--- a/tests/libqos/pci-pc.h
+++ b/tests/libqos/pci-pc.h
@@ -16,5 +16,6 @@
#include "libqos/pci.h"
QPCIBus *qpci_init_pc(void);
+void qpci_free_pc(QPCIBus *bus);
#endif
diff --git a/tests/libqos/pci.c b/tests/libqos/pci.c
index c9a0b9134..4e630c250 100644
--- a/tests/libqos/pci.c
+++ b/tests/libqos/pci.c
@@ -15,8 +15,6 @@
#include "hw/pci/pci_regs.h"
#include <glib.h>
-#include <stdio.h>
-
void qpci_device_foreach(QPCIBus *bus, int vendor_id, int device_id,
void (*func)(QPCIDevice *dev, int devfn, void *data),
void *data)
@@ -73,6 +71,121 @@ void qpci_device_enable(QPCIDevice *dev)
cmd = qpci_config_readw(dev, PCI_COMMAND);
cmd |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER;
qpci_config_writew(dev, PCI_COMMAND, cmd);
+
+ /* Verify the bits are now set. */
+ cmd = qpci_config_readw(dev, PCI_COMMAND);
+ g_assert_cmphex(cmd & PCI_COMMAND_IO, ==, PCI_COMMAND_IO);
+ g_assert_cmphex(cmd & PCI_COMMAND_MEMORY, ==, PCI_COMMAND_MEMORY);
+ g_assert_cmphex(cmd & PCI_COMMAND_MASTER, ==, PCI_COMMAND_MASTER);
+}
+
+uint8_t qpci_find_capability(QPCIDevice *dev, uint8_t id)
+{
+ uint8_t cap;
+ uint8_t addr = qpci_config_readb(dev, PCI_CAPABILITY_LIST);
+
+ do {
+ cap = qpci_config_readb(dev, addr);
+ if (cap != id) {
+ addr = qpci_config_readb(dev, addr + PCI_CAP_LIST_NEXT);
+ }
+ } while (cap != id && addr != 0);
+
+ return addr;
+}
+
+void qpci_msix_enable(QPCIDevice *dev)
+{
+ uint8_t addr;
+ uint16_t val;
+ uint32_t table;
+ uint8_t bir_table;
+ uint8_t bir_pba;
+ void *offset;
+
+ addr = qpci_find_capability(dev, PCI_CAP_ID_MSIX);
+ g_assert_cmphex(addr, !=, 0);
+
+ val = qpci_config_readw(dev, addr + PCI_MSIX_FLAGS);
+ qpci_config_writew(dev, addr + PCI_MSIX_FLAGS, val | PCI_MSIX_FLAGS_ENABLE);
+
+ table = qpci_config_readl(dev, addr + PCI_MSIX_TABLE);
+ bir_table = table & PCI_MSIX_FLAGS_BIRMASK;
+ offset = qpci_iomap(dev, bir_table, NULL);
+ dev->msix_table = offset + (table & ~PCI_MSIX_FLAGS_BIRMASK);
+
+ table = qpci_config_readl(dev, addr + PCI_MSIX_PBA);
+ bir_pba = table & PCI_MSIX_FLAGS_BIRMASK;
+ if (bir_pba != bir_table) {
+ offset = qpci_iomap(dev, bir_pba, NULL);
+ }
+ dev->msix_pba = offset + (table & ~PCI_MSIX_FLAGS_BIRMASK);
+
+ g_assert(dev->msix_table != NULL);
+ g_assert(dev->msix_pba != NULL);
+ dev->msix_enabled = true;
+}
+
+void qpci_msix_disable(QPCIDevice *dev)
+{
+ uint8_t addr;
+ uint16_t val;
+
+ g_assert(dev->msix_enabled);
+ addr = qpci_find_capability(dev, PCI_CAP_ID_MSIX);
+ g_assert_cmphex(addr, !=, 0);
+ val = qpci_config_readw(dev, addr + PCI_MSIX_FLAGS);
+ qpci_config_writew(dev, addr + PCI_MSIX_FLAGS,
+ val & ~PCI_MSIX_FLAGS_ENABLE);
+
+ qpci_iounmap(dev, dev->msix_table);
+ qpci_iounmap(dev, dev->msix_pba);
+ dev->msix_enabled = 0;
+ dev->msix_table = NULL;
+ dev->msix_pba = NULL;
+}
+
+bool qpci_msix_pending(QPCIDevice *dev, uint16_t entry)
+{
+ uint32_t pba_entry;
+ uint8_t bit_n = entry % 32;
+ void *addr = dev->msix_pba + (entry / 32) * PCI_MSIX_ENTRY_SIZE / 4;
+
+ g_assert(dev->msix_enabled);
+ pba_entry = qpci_io_readl(dev, addr);
+ qpci_io_writel(dev, addr, pba_entry & ~(1 << bit_n));
+ return (pba_entry & (1 << bit_n)) != 0;
+}
+
+bool qpci_msix_masked(QPCIDevice *dev, uint16_t entry)
+{
+ uint8_t addr;
+ uint16_t val;
+ void *vector_addr = dev->msix_table + (entry * PCI_MSIX_ENTRY_SIZE);
+
+ g_assert(dev->msix_enabled);
+ addr = qpci_find_capability(dev, PCI_CAP_ID_MSIX);
+ g_assert_cmphex(addr, !=, 0);
+ val = qpci_config_readw(dev, addr + PCI_MSIX_FLAGS);
+
+ if (val & PCI_MSIX_FLAGS_MASKALL) {
+ return true;
+ } else {
+ return (qpci_io_readl(dev, vector_addr + PCI_MSIX_ENTRY_VECTOR_CTRL)
+ & PCI_MSIX_ENTRY_CTRL_MASKBIT) != 0;
+ }
+}
+
+uint16_t qpci_msix_table_size(QPCIDevice *dev)
+{
+ uint8_t addr;
+ uint16_t control;
+
+ addr = qpci_find_capability(dev, PCI_CAP_ID_MSIX);
+ g_assert_cmphex(addr, !=, 0);
+
+ control = qpci_config_readw(dev, addr + PCI_MSIX_FLAGS);
+ return (control & PCI_MSIX_FLAGS_QSIZE) + 1;
}
uint8_t qpci_config_readb(QPCIDevice *dev, uint8_t offset)
@@ -138,9 +251,9 @@ void qpci_io_writel(QPCIDevice *dev, void *data, uint32_t value)
dev->bus->io_writel(dev->bus, data, value);
}
-void *qpci_iomap(QPCIDevice *dev, int barno)
+void *qpci_iomap(QPCIDevice *dev, int barno, uint64_t *sizeptr)
{
- return dev->bus->iomap(dev->bus, dev, barno);
+ return dev->bus->iomap(dev->bus, dev, barno, sizeptr);
}
void qpci_iounmap(QPCIDevice *dev, void *data)
diff --git a/tests/libqos/pci.h b/tests/libqos/pci.h
index 343943154..dfaee9ec3 100644
--- a/tests/libqos/pci.h
+++ b/tests/libqos/pci.h
@@ -14,6 +14,7 @@
#define LIBQOS_PCI_H
#include <stdint.h>
+#include "libqtest.h"
#define QPCI_DEVFN(dev, fn) (((dev) << 3) | (fn))
@@ -41,7 +42,7 @@ struct QPCIBus
void (*config_writel)(QPCIBus *bus, int devfn,
uint8_t offset, uint32_t value);
- void *(*iomap)(QPCIBus *bus, QPCIDevice *dev, int barno);
+ void *(*iomap)(QPCIBus *bus, QPCIDevice *dev, int barno, uint64_t *sizeptr);
void (*iounmap)(QPCIBus *bus, void *data);
};
@@ -49,6 +50,9 @@ struct QPCIDevice
{
QPCIBus *bus;
int devfn;
+ bool msix_enabled;
+ void *msix_table;
+ void *msix_pba;
};
void qpci_device_foreach(QPCIBus *bus, int vendor_id, int device_id,
@@ -57,6 +61,12 @@ void qpci_device_foreach(QPCIBus *bus, int vendor_id, int device_id,
QPCIDevice *qpci_device_find(QPCIBus *bus, int devfn);
void qpci_device_enable(QPCIDevice *dev);
+uint8_t qpci_find_capability(QPCIDevice *dev, uint8_t id);
+void qpci_msix_enable(QPCIDevice *dev);
+void qpci_msix_disable(QPCIDevice *dev);
+bool qpci_msix_pending(QPCIDevice *dev, uint16_t entry);
+bool qpci_msix_masked(QPCIDevice *dev, uint16_t entry);
+uint16_t qpci_msix_table_size(QPCIDevice *dev);
uint8_t qpci_config_readb(QPCIDevice *dev, uint8_t offset);
uint16_t qpci_config_readw(QPCIDevice *dev, uint8_t offset);
@@ -74,7 +84,10 @@ void qpci_io_writeb(QPCIDevice *dev, void *data, uint8_t value);
void qpci_io_writew(QPCIDevice *dev, void *data, uint16_t value);
void qpci_io_writel(QPCIDevice *dev, void *data, uint32_t value);
-void *qpci_iomap(QPCIDevice *dev, int barno);
+void *qpci_iomap(QPCIDevice *dev, int barno, uint64_t *sizeptr);
void qpci_iounmap(QPCIDevice *dev, void *data);
+void qpci_plug_device_test(const char *driver, const char *id,
+ uint8_t slot, const char *opts);
+void qpci_unplug_acpi_device_test(const char *id, uint8_t slot);
#endif
diff --git a/tests/libqos/usb.c b/tests/libqos/usb.c
new file mode 100644
index 000000000..41d89b848
--- /dev/null
+++ b/tests/libqos/usb.c
@@ -0,0 +1,71 @@
+/*
+ * common code shared by usb tests
+ *
+ * Copyright (c) 2014 Red Hat, Inc
+ *
+ * Authors:
+ * Gerd Hoffmann <kraxel@redhat.com>
+ * John Snow <jsnow@redhat.com>
+ * Igor Mammedov <imammedo@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 <glib.h>
+#include <string.h>
+#include "libqtest.h"
+#include "qemu/osdep.h"
+#include "hw/usb/uhci-regs.h"
+#include "libqos/usb.h"
+
+void qusb_pci_init_one(QPCIBus *pcibus, struct qhc *hc, uint32_t devfn, int bar)
+{
+ hc->dev = qpci_device_find(pcibus, devfn);
+ g_assert(hc->dev != NULL);
+ qpci_device_enable(hc->dev);
+ hc->base = qpci_iomap(hc->dev, bar, NULL);
+ g_assert(hc->base != NULL);
+}
+
+void uhci_port_test(struct qhc *hc, int port, uint16_t expect)
+{
+ void *addr = hc->base + 0x10 + 2 * port;
+ uint16_t value = qpci_io_readw(hc->dev, addr);
+ uint16_t mask = ~(UHCI_PORT_WRITE_CLEAR | UHCI_PORT_RSVD1);
+
+ g_assert((value & mask) == (expect & mask));
+}
+
+void usb_test_hotplug(const char *hcd_id, const int port,
+ void (*port_check)(void))
+{
+ QDict *response;
+ char *cmd;
+
+ cmd = g_strdup_printf("{'execute': 'device_add',"
+ " 'arguments': {"
+ " 'driver': 'usb-tablet',"
+ " 'port': '%d',"
+ " 'bus': '%s.0',"
+ " 'id': 'usbdev%d'"
+ "}}", port, hcd_id, port);
+ response = qmp(cmd);
+ g_free(cmd);
+ g_assert(response);
+ g_assert(!qdict_haskey(response, "error"));
+ QDECREF(response);
+
+ if (port_check) {
+ port_check();
+ }
+
+ cmd = g_strdup_printf("{'execute': 'device_del',"
+ " 'arguments': {"
+ " 'id': 'usbdev%d'"
+ "}}", port);
+ response = qmp(cmd);
+ g_free(cmd);
+ g_assert(response);
+ g_assert(qdict_haskey(response, "event"));
+ g_assert(!strcmp(qdict_get_str(response, "event"), "DEVICE_DELETED"));
+}
diff --git a/tests/libqos/usb.h b/tests/libqos/usb.h
new file mode 100644
index 000000000..8fe56872b
--- /dev/null
+++ b/tests/libqos/usb.h
@@ -0,0 +1,17 @@
+#ifndef LIBQOS_USB_H
+#define LIBQOS_USB_H
+
+#include "libqos/pci-pc.h"
+
+struct qhc {
+ QPCIDevice *dev;
+ void *base;
+};
+
+void qusb_pci_init_one(QPCIBus *pcibus, struct qhc *hc,
+ uint32_t devfn, int bar);
+void uhci_port_test(struct qhc *hc, int port, uint16_t expect);
+
+void usb_test_hotplug(const char *bus_name, const int port,
+ void (*port_check)(void));
+#endif
diff --git a/tests/libqos/virtio-pci.c b/tests/libqos/virtio-pci.c
new file mode 100644
index 000000000..788ebaff4
--- /dev/null
+++ b/tests/libqos/virtio-pci.c
@@ -0,0 +1,343 @@
+/*
+ * libqos virtio PCI driver
+ *
+ * Copyright (c) 2014 Marc MarĂ­
+ *
+ * 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 <glib.h>
+#include <stdio.h>
+#include "libqtest.h"
+#include "libqos/virtio.h"
+#include "libqos/virtio-pci.h"
+#include "libqos/pci.h"
+#include "libqos/pci-pc.h"
+#include "libqos/malloc.h"
+#include "libqos/malloc-pc.h"
+
+#include "hw/pci/pci_regs.h"
+
+typedef struct QVirtioPCIForeachData {
+ void (*func)(QVirtioDevice *d, void *data);
+ uint16_t device_type;
+ void *user_data;
+} QVirtioPCIForeachData;
+
+static QVirtioPCIDevice *qpcidevice_to_qvirtiodevice(QPCIDevice *pdev)
+{
+ QVirtioPCIDevice *vpcidev;
+ vpcidev = g_malloc0(sizeof(*vpcidev));
+
+ if (pdev) {
+ vpcidev->pdev = pdev;
+ vpcidev->vdev.device_type =
+ qpci_config_readw(vpcidev->pdev, PCI_SUBSYSTEM_ID);
+ }
+
+ vpcidev->config_msix_entry = -1;
+
+ return vpcidev;
+}
+
+static void qvirtio_pci_foreach_callback(
+ QPCIDevice *dev, int devfn, void *data)
+{
+ QVirtioPCIForeachData *d = data;
+ QVirtioPCIDevice *vpcidev = qpcidevice_to_qvirtiodevice(dev);
+
+ if (vpcidev->vdev.device_type == d->device_type) {
+ d->func(&vpcidev->vdev, d->user_data);
+ } else {
+ g_free(vpcidev);
+ }
+}
+
+static void qvirtio_pci_assign_device(QVirtioDevice *d, void *data)
+{
+ QVirtioPCIDevice **vpcidev = data;
+ *vpcidev = (QVirtioPCIDevice *)d;
+}
+
+static uint8_t qvirtio_pci_config_readb(QVirtioDevice *d, void *addr)
+{
+ QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
+ return qpci_io_readb(dev->pdev, addr);
+}
+
+static uint16_t qvirtio_pci_config_readw(QVirtioDevice *d, void *addr)
+{
+ QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
+ return qpci_io_readw(dev->pdev, addr);
+}
+
+static uint32_t qvirtio_pci_config_readl(QVirtioDevice *d, void *addr)
+{
+ QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
+ return qpci_io_readl(dev->pdev, addr);
+}
+
+static uint64_t qvirtio_pci_config_readq(QVirtioDevice *d, void *addr)
+{
+ QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
+ int i;
+ uint64_t u64 = 0;
+
+ if (qtest_big_endian()) {
+ for (i = 0; i < 8; ++i) {
+ u64 |= (uint64_t)qpci_io_readb(dev->pdev, addr + i) << (7 - i) * 8;
+ }
+ } else {
+ for (i = 0; i < 8; ++i) {
+ u64 |= (uint64_t)qpci_io_readb(dev->pdev, addr + i) << i * 8;
+ }
+ }
+
+ return u64;
+}
+
+static uint32_t qvirtio_pci_get_features(QVirtioDevice *d)
+{
+ QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
+ return qpci_io_readl(dev->pdev, dev->addr + QVIRTIO_DEVICE_FEATURES);
+}
+
+static void qvirtio_pci_set_features(QVirtioDevice *d, uint32_t features)
+{
+ QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
+ qpci_io_writel(dev->pdev, dev->addr + QVIRTIO_GUEST_FEATURES, features);
+}
+
+static uint32_t qvirtio_pci_get_guest_features(QVirtioDevice *d)
+{
+ QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
+ return qpci_io_readl(dev->pdev, dev->addr + QVIRTIO_GUEST_FEATURES);
+}
+
+static uint8_t qvirtio_pci_get_status(QVirtioDevice *d)
+{
+ QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
+ return qpci_io_readb(dev->pdev, dev->addr + QVIRTIO_DEVICE_STATUS);
+}
+
+static void qvirtio_pci_set_status(QVirtioDevice *d, uint8_t status)
+{
+ QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
+ qpci_io_writeb(dev->pdev, dev->addr + QVIRTIO_DEVICE_STATUS, status);
+}
+
+static bool qvirtio_pci_get_queue_isr_status(QVirtioDevice *d, QVirtQueue *vq)
+{
+ QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
+ QVirtQueuePCI *vqpci = (QVirtQueuePCI *)vq;
+ uint32_t data;
+
+ if (dev->pdev->msix_enabled) {
+ g_assert_cmpint(vqpci->msix_entry, !=, -1);
+ if (qpci_msix_masked(dev->pdev, vqpci->msix_entry)) {
+ /* No ISR checking should be done if masked, but read anyway */
+ return qpci_msix_pending(dev->pdev, vqpci->msix_entry);
+ } else {
+ data = readl(vqpci->msix_addr);
+ writel(vqpci->msix_addr, 0);
+ return data == vqpci->msix_data;
+ }
+ } else {
+ return qpci_io_readb(dev->pdev, dev->addr + QVIRTIO_ISR_STATUS) & 1;
+ }
+}
+
+static bool qvirtio_pci_get_config_isr_status(QVirtioDevice *d)
+{
+ QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
+ uint32_t data;
+
+ if (dev->pdev->msix_enabled) {
+ g_assert_cmpint(dev->config_msix_entry, !=, -1);
+ if (qpci_msix_masked(dev->pdev, dev->config_msix_entry)) {
+ /* No ISR checking should be done if masked, but read anyway */
+ return qpci_msix_pending(dev->pdev, dev->config_msix_entry);
+ } else {
+ data = readl(dev->config_msix_addr);
+ writel(dev->config_msix_addr, 0);
+ return data == dev->config_msix_data;
+ }
+ } else {
+ return qpci_io_readb(dev->pdev, dev->addr + QVIRTIO_ISR_STATUS) & 2;
+ }
+}
+
+static void qvirtio_pci_queue_select(QVirtioDevice *d, uint16_t index)
+{
+ QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
+ qpci_io_writeb(dev->pdev, dev->addr + QVIRTIO_QUEUE_SELECT, index);
+}
+
+static uint16_t qvirtio_pci_get_queue_size(QVirtioDevice *d)
+{
+ QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
+ return qpci_io_readw(dev->pdev, dev->addr + QVIRTIO_QUEUE_SIZE);
+}
+
+static void qvirtio_pci_set_queue_address(QVirtioDevice *d, uint32_t pfn)
+{
+ QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
+ qpci_io_writel(dev->pdev, dev->addr + QVIRTIO_QUEUE_ADDRESS, pfn);
+}
+
+static QVirtQueue *qvirtio_pci_virtqueue_setup(QVirtioDevice *d,
+ QGuestAllocator *alloc, uint16_t index)
+{
+ uint32_t feat;
+ uint64_t addr;
+ QVirtQueuePCI *vqpci;
+
+ vqpci = g_malloc0(sizeof(*vqpci));
+ feat = qvirtio_pci_get_guest_features(d);
+
+ qvirtio_pci_queue_select(d, index);
+ vqpci->vq.index = index;
+ vqpci->vq.size = qvirtio_pci_get_queue_size(d);
+ vqpci->vq.free_head = 0;
+ vqpci->vq.num_free = vqpci->vq.size;
+ vqpci->vq.align = QVIRTIO_PCI_ALIGN;
+ vqpci->vq.indirect = (feat & QVIRTIO_F_RING_INDIRECT_DESC) != 0;
+ vqpci->vq.event = (feat & QVIRTIO_F_RING_EVENT_IDX) != 0;
+
+ vqpci->msix_entry = -1;
+ vqpci->msix_addr = 0;
+ vqpci->msix_data = 0x12345678;
+
+ /* Check different than 0 */
+ g_assert_cmpint(vqpci->vq.size, !=, 0);
+
+ /* Check power of 2 */
+ g_assert_cmpint(vqpci->vq.size & (vqpci->vq.size - 1), ==, 0);
+
+ addr = guest_alloc(alloc, qvring_size(vqpci->vq.size, QVIRTIO_PCI_ALIGN));
+ qvring_init(alloc, &vqpci->vq, addr);
+ qvirtio_pci_set_queue_address(d, vqpci->vq.desc / QVIRTIO_PCI_ALIGN);
+
+ return &vqpci->vq;
+}
+
+static void qvirtio_pci_virtqueue_kick(QVirtioDevice *d, QVirtQueue *vq)
+{
+ QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
+ qpci_io_writew(dev->pdev, dev->addr + QVIRTIO_QUEUE_NOTIFY, vq->index);
+}
+
+const QVirtioBus qvirtio_pci = {
+ .config_readb = qvirtio_pci_config_readb,
+ .config_readw = qvirtio_pci_config_readw,
+ .config_readl = qvirtio_pci_config_readl,
+ .config_readq = qvirtio_pci_config_readq,
+ .get_features = qvirtio_pci_get_features,
+ .set_features = qvirtio_pci_set_features,
+ .get_guest_features = qvirtio_pci_get_guest_features,
+ .get_status = qvirtio_pci_get_status,
+ .set_status = qvirtio_pci_set_status,
+ .get_queue_isr_status = qvirtio_pci_get_queue_isr_status,
+ .get_config_isr_status = qvirtio_pci_get_config_isr_status,
+ .queue_select = qvirtio_pci_queue_select,
+ .get_queue_size = qvirtio_pci_get_queue_size,
+ .set_queue_address = qvirtio_pci_set_queue_address,
+ .virtqueue_setup = qvirtio_pci_virtqueue_setup,
+ .virtqueue_kick = qvirtio_pci_virtqueue_kick,
+};
+
+void qvirtio_pci_foreach(QPCIBus *bus, uint16_t device_type,
+ void (*func)(QVirtioDevice *d, void *data), void *data)
+{
+ QVirtioPCIForeachData d = { .func = func,
+ .device_type = device_type,
+ .user_data = data };
+
+ qpci_device_foreach(bus, QVIRTIO_VENDOR_ID, -1,
+ qvirtio_pci_foreach_callback, &d);
+}
+
+QVirtioPCIDevice *qvirtio_pci_device_find(QPCIBus *bus, uint16_t device_type)
+{
+ QVirtioPCIDevice *dev = NULL;
+ qvirtio_pci_foreach(bus, device_type, qvirtio_pci_assign_device, &dev);
+
+ return dev;
+}
+
+void qvirtio_pci_device_enable(QVirtioPCIDevice *d)
+{
+ qpci_device_enable(d->pdev);
+ d->addr = qpci_iomap(d->pdev, 0, NULL);
+ g_assert(d->addr != NULL);
+}
+
+void qvirtio_pci_device_disable(QVirtioPCIDevice *d)
+{
+ qpci_iounmap(d->pdev, d->addr);
+ d->addr = NULL;
+}
+
+void qvirtqueue_pci_msix_setup(QVirtioPCIDevice *d, QVirtQueuePCI *vqpci,
+ QGuestAllocator *alloc, uint16_t entry)
+{
+ uint16_t vector;
+ uint32_t control;
+ void *addr;
+
+ g_assert(d->pdev->msix_enabled);
+ addr = d->pdev->msix_table + (entry * 16);
+
+ g_assert_cmpint(entry, >=, 0);
+ g_assert_cmpint(entry, <, qpci_msix_table_size(d->pdev));
+ vqpci->msix_entry = entry;
+
+ vqpci->msix_addr = guest_alloc(alloc, 4);
+ qpci_io_writel(d->pdev, addr + PCI_MSIX_ENTRY_LOWER_ADDR,
+ vqpci->msix_addr & ~0UL);
+ qpci_io_writel(d->pdev, addr + PCI_MSIX_ENTRY_UPPER_ADDR,
+ (vqpci->msix_addr >> 32) & ~0UL);
+ qpci_io_writel(d->pdev, addr + PCI_MSIX_ENTRY_DATA, vqpci->msix_data);
+
+ control = qpci_io_readl(d->pdev, addr + PCI_MSIX_ENTRY_VECTOR_CTRL);
+ qpci_io_writel(d->pdev, addr + PCI_MSIX_ENTRY_VECTOR_CTRL,
+ control & ~PCI_MSIX_ENTRY_CTRL_MASKBIT);
+
+ qvirtio_pci_queue_select(&d->vdev, vqpci->vq.index);
+ qpci_io_writew(d->pdev, d->addr + QVIRTIO_MSIX_QUEUE_VECTOR, entry);
+ vector = qpci_io_readw(d->pdev, d->addr + QVIRTIO_MSIX_QUEUE_VECTOR);
+ g_assert_cmphex(vector, !=, QVIRTIO_MSI_NO_VECTOR);
+}
+
+void qvirtio_pci_set_msix_configuration_vector(QVirtioPCIDevice *d,
+ QGuestAllocator *alloc, uint16_t entry)
+{
+ uint16_t vector;
+ uint32_t control;
+ void *addr;
+
+ g_assert(d->pdev->msix_enabled);
+ addr = d->pdev->msix_table + (entry * 16);
+
+ g_assert_cmpint(entry, >=, 0);
+ g_assert_cmpint(entry, <, qpci_msix_table_size(d->pdev));
+ d->config_msix_entry = entry;
+
+ d->config_msix_data = 0x12345678;
+ d->config_msix_addr = guest_alloc(alloc, 4);
+
+ qpci_io_writel(d->pdev, addr + PCI_MSIX_ENTRY_LOWER_ADDR,
+ d->config_msix_addr & ~0UL);
+ qpci_io_writel(d->pdev, addr + PCI_MSIX_ENTRY_UPPER_ADDR,
+ (d->config_msix_addr >> 32) & ~0UL);
+ qpci_io_writel(d->pdev, addr + PCI_MSIX_ENTRY_DATA, d->config_msix_data);
+
+ control = qpci_io_readl(d->pdev, addr + PCI_MSIX_ENTRY_VECTOR_CTRL);
+ qpci_io_writel(d->pdev, addr + PCI_MSIX_ENTRY_VECTOR_CTRL,
+ control & ~PCI_MSIX_ENTRY_CTRL_MASKBIT);
+
+ qpci_io_writew(d->pdev, d->addr + QVIRTIO_MSIX_CONF_VECTOR, entry);
+ vector = qpci_io_readw(d->pdev, d->addr + QVIRTIO_MSIX_CONF_VECTOR);
+ g_assert_cmphex(vector, !=, QVIRTIO_MSI_NO_VECTOR);
+}
diff --git a/tests/libqos/virtio-pci.h b/tests/libqos/virtio-pci.h
new file mode 100644
index 000000000..883f7ff26
--- /dev/null
+++ b/tests/libqos/virtio-pci.h
@@ -0,0 +1,61 @@
+/*
+ * libqos virtio PCI definitions
+ *
+ * Copyright (c) 2014 Marc MarĂ­
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef LIBQOS_VIRTIO_PCI_H
+#define LIBQOS_VIRTIO_PCI_H
+
+#include "libqos/virtio.h"
+#include "libqos/pci.h"
+
+#define QVIRTIO_DEVICE_FEATURES 0x00
+#define QVIRTIO_GUEST_FEATURES 0x04
+#define QVIRTIO_QUEUE_ADDRESS 0x08
+#define QVIRTIO_QUEUE_SIZE 0x0C
+#define QVIRTIO_QUEUE_SELECT 0x0E
+#define QVIRTIO_QUEUE_NOTIFY 0x10
+#define QVIRTIO_DEVICE_STATUS 0x12
+#define QVIRTIO_ISR_STATUS 0x13
+#define QVIRTIO_MSIX_CONF_VECTOR 0x14
+#define QVIRTIO_MSIX_QUEUE_VECTOR 0x16
+#define QVIRTIO_DEVICE_SPECIFIC_MSIX 0x18
+#define QVIRTIO_DEVICE_SPECIFIC_NO_MSIX 0x14
+
+#define QVIRTIO_PCI_ALIGN 4096
+
+#define QVIRTIO_MSI_NO_VECTOR 0xFFFF
+
+typedef struct QVirtioPCIDevice {
+ QVirtioDevice vdev;
+ QPCIDevice *pdev;
+ void *addr;
+ uint16_t config_msix_entry;
+ uint64_t config_msix_addr;
+ uint32_t config_msix_data;
+} QVirtioPCIDevice;
+
+typedef struct QVirtQueuePCI {
+ QVirtQueue vq;
+ uint16_t msix_entry;
+ uint64_t msix_addr;
+ uint32_t msix_data;
+} QVirtQueuePCI;
+
+extern const QVirtioBus qvirtio_pci;
+
+void qvirtio_pci_foreach(QPCIBus *bus, uint16_t device_type,
+ void (*func)(QVirtioDevice *d, void *data), void *data);
+QVirtioPCIDevice *qvirtio_pci_device_find(QPCIBus *bus, uint16_t device_type);
+void qvirtio_pci_device_enable(QVirtioPCIDevice *d);
+void qvirtio_pci_device_disable(QVirtioPCIDevice *d);
+
+void qvirtio_pci_set_msix_configuration_vector(QVirtioPCIDevice *d,
+ QGuestAllocator *alloc, uint16_t entry);
+void qvirtqueue_pci_msix_setup(QVirtioPCIDevice *d, QVirtQueuePCI *vqpci,
+ QGuestAllocator *alloc, uint16_t entry);
+#endif
diff --git a/tests/libqos/virtio.c b/tests/libqos/virtio.c
new file mode 100644
index 000000000..a06128924
--- /dev/null
+++ b/tests/libqos/virtio.c
@@ -0,0 +1,281 @@
+/*
+ * libqos virtio driver
+ *
+ * Copyright (c) 2014 Marc MarĂ­
+ *
+ * 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 <glib.h>
+#include "libqtest.h"
+#include "libqos/virtio.h"
+
+uint8_t qvirtio_config_readb(const QVirtioBus *bus, QVirtioDevice *d,
+ void *addr)
+{
+ return bus->config_readb(d, addr);
+}
+
+uint16_t qvirtio_config_readw(const QVirtioBus *bus, QVirtioDevice *d,
+ void *addr)
+{
+ return bus->config_readw(d, addr);
+}
+
+uint32_t qvirtio_config_readl(const QVirtioBus *bus, QVirtioDevice *d,
+ void *addr)
+{
+ return bus->config_readl(d, addr);
+}
+
+uint64_t qvirtio_config_readq(const QVirtioBus *bus, QVirtioDevice *d,
+ void *addr)
+{
+ return bus->config_readq(d, addr);
+}
+
+uint32_t qvirtio_get_features(const QVirtioBus *bus, QVirtioDevice *d)
+{
+ return bus->get_features(d);
+}
+
+void qvirtio_set_features(const QVirtioBus *bus, QVirtioDevice *d,
+ uint32_t features)
+{
+ bus->set_features(d, features);
+}
+
+QVirtQueue *qvirtqueue_setup(const QVirtioBus *bus, QVirtioDevice *d,
+ QGuestAllocator *alloc, uint16_t index)
+{
+ return bus->virtqueue_setup(d, alloc, index);
+}
+
+void qvirtio_reset(const QVirtioBus *bus, QVirtioDevice *d)
+{
+ bus->set_status(d, QVIRTIO_RESET);
+ g_assert_cmphex(bus->get_status(d), ==, QVIRTIO_RESET);
+}
+
+void qvirtio_set_acknowledge(const QVirtioBus *bus, QVirtioDevice *d)
+{
+ bus->set_status(d, bus->get_status(d) | QVIRTIO_ACKNOWLEDGE);
+ g_assert_cmphex(bus->get_status(d), ==, QVIRTIO_ACKNOWLEDGE);
+}
+
+void qvirtio_set_driver(const QVirtioBus *bus, QVirtioDevice *d)
+{
+ bus->set_status(d, bus->get_status(d) | QVIRTIO_DRIVER);
+ g_assert_cmphex(bus->get_status(d), ==,
+ QVIRTIO_DRIVER | QVIRTIO_ACKNOWLEDGE);
+}
+
+void qvirtio_set_driver_ok(const QVirtioBus *bus, QVirtioDevice *d)
+{
+ bus->set_status(d, bus->get_status(d) | QVIRTIO_DRIVER_OK);
+ g_assert_cmphex(bus->get_status(d), ==,
+ QVIRTIO_DRIVER_OK | QVIRTIO_DRIVER | QVIRTIO_ACKNOWLEDGE);
+}
+
+void qvirtio_wait_queue_isr(const QVirtioBus *bus, QVirtioDevice *d,
+ QVirtQueue *vq, gint64 timeout_us)
+{
+ gint64 start_time = g_get_monotonic_time();
+
+ for (;;) {
+ clock_step(100);
+ if (bus->get_queue_isr_status(d, vq)) {
+ return;
+ }
+ g_assert(g_get_monotonic_time() - start_time <= timeout_us);
+ }
+}
+
+/* Wait for the status byte at given guest memory address to be set
+ *
+ * The virtqueue interrupt must not be raised, making this useful for testing
+ * event_index functionality.
+ */
+uint8_t qvirtio_wait_status_byte_no_isr(const QVirtioBus *bus,
+ QVirtioDevice *d,
+ QVirtQueue *vq,
+ uint64_t addr,
+ gint64 timeout_us)
+{
+ gint64 start_time = g_get_monotonic_time();
+ uint8_t val;
+
+ while ((val = readb(addr)) == 0xff) {
+ clock_step(100);
+ g_assert(!bus->get_queue_isr_status(d, vq));
+ g_assert(g_get_monotonic_time() - start_time <= timeout_us);
+ }
+ return val;
+}
+
+void qvirtio_wait_config_isr(const QVirtioBus *bus, QVirtioDevice *d,
+ gint64 timeout_us)
+{
+ gint64 start_time = g_get_monotonic_time();
+
+ for (;;) {
+ clock_step(100);
+ if (bus->get_config_isr_status(d)) {
+ return;
+ }
+ g_assert(g_get_monotonic_time() - start_time <= timeout_us);
+ }
+}
+
+void qvring_init(const QGuestAllocator *alloc, QVirtQueue *vq, uint64_t addr)
+{
+ int i;
+
+ vq->desc = addr;
+ vq->avail = vq->desc + vq->size*sizeof(QVRingDesc);
+ vq->used = (uint64_t)((vq->avail + sizeof(uint16_t) * (3 + vq->size)
+ + vq->align - 1) & ~(vq->align - 1));
+
+ for (i = 0; i < vq->size - 1; i++) {
+ /* vq->desc[i].addr */
+ writew(vq->desc + (16 * i), 0);
+ /* vq->desc[i].next */
+ writew(vq->desc + (16 * i) + 14, i + 1);
+ }
+
+ /* vq->avail->flags */
+ writew(vq->avail, 0);
+ /* vq->avail->idx */
+ writew(vq->avail + 2, 0);
+ /* vq->avail->used_event */
+ writew(vq->avail + 4 + (2 * vq->size), 0);
+
+ /* vq->used->flags */
+ writew(vq->used, 0);
+ /* vq->used->avail_event */
+ writew(vq->used+2+(sizeof(struct QVRingUsedElem)*vq->size), 0);
+}
+
+QVRingIndirectDesc *qvring_indirect_desc_setup(QVirtioDevice *d,
+ QGuestAllocator *alloc, uint16_t elem)
+{
+ int i;
+ QVRingIndirectDesc *indirect = g_malloc(sizeof(*indirect));
+
+ indirect->index = 0;
+ indirect->elem = elem;
+ indirect->desc = guest_alloc(alloc, sizeof(QVRingDesc)*elem);
+
+ for (i = 0; i < elem - 1; ++i) {
+ /* indirect->desc[i].addr */
+ writeq(indirect->desc + (16 * i), 0);
+ /* indirect->desc[i].flags */
+ writew(indirect->desc + (16 * i) + 12, QVRING_DESC_F_NEXT);
+ /* indirect->desc[i].next */
+ writew(indirect->desc + (16 * i) + 14, i + 1);
+ }
+
+ return indirect;
+}
+
+void qvring_indirect_desc_add(QVRingIndirectDesc *indirect, uint64_t data,
+ uint32_t len, bool write)
+{
+ uint16_t flags;
+
+ g_assert_cmpint(indirect->index, <, indirect->elem);
+
+ flags = readw(indirect->desc + (16 * indirect->index) + 12);
+
+ if (write) {
+ flags |= QVRING_DESC_F_WRITE;
+ }
+
+ /* indirect->desc[indirect->index].addr */
+ writeq(indirect->desc + (16 * indirect->index), data);
+ /* indirect->desc[indirect->index].len */
+ writel(indirect->desc + (16 * indirect->index) + 8, len);
+ /* indirect->desc[indirect->index].flags */
+ writew(indirect->desc + (16 * indirect->index) + 12, flags);
+
+ indirect->index++;
+}
+
+uint32_t qvirtqueue_add(QVirtQueue *vq, uint64_t data, uint32_t len, bool write,
+ bool next)
+{
+ uint16_t flags = 0;
+ vq->num_free--;
+
+ if (write) {
+ flags |= QVRING_DESC_F_WRITE;
+ }
+
+ if (next) {
+ flags |= QVRING_DESC_F_NEXT;
+ }
+
+ /* vq->desc[vq->free_head].addr */
+ writeq(vq->desc + (16 * vq->free_head), data);
+ /* vq->desc[vq->free_head].len */
+ writel(vq->desc + (16 * vq->free_head) + 8, len);
+ /* vq->desc[vq->free_head].flags */
+ writew(vq->desc + (16 * vq->free_head) + 12, flags);
+
+ return vq->free_head++; /* Return and increase, in this order */
+}
+
+uint32_t qvirtqueue_add_indirect(QVirtQueue *vq, QVRingIndirectDesc *indirect)
+{
+ g_assert(vq->indirect);
+ g_assert_cmpint(vq->size, >=, indirect->elem);
+ g_assert_cmpint(indirect->index, ==, indirect->elem);
+
+ vq->num_free--;
+
+ /* vq->desc[vq->free_head].addr */
+ writeq(vq->desc + (16 * vq->free_head), indirect->desc);
+ /* vq->desc[vq->free_head].len */
+ writel(vq->desc + (16 * vq->free_head) + 8,
+ sizeof(QVRingDesc) * indirect->elem);
+ /* vq->desc[vq->free_head].flags */
+ writew(vq->desc + (16 * vq->free_head) + 12, QVRING_DESC_F_INDIRECT);
+
+ return vq->free_head++; /* Return and increase, in this order */
+}
+
+void qvirtqueue_kick(const QVirtioBus *bus, QVirtioDevice *d, QVirtQueue *vq,
+ uint32_t free_head)
+{
+ /* vq->avail->idx */
+ uint16_t idx = readl(vq->avail + 2);
+ /* vq->used->flags */
+ uint16_t flags;
+ /* vq->used->avail_event */
+ uint16_t avail_event;
+
+ /* vq->avail->ring[idx % vq->size] */
+ writel(vq->avail + 4 + (2 * (idx % vq->size)), free_head);
+ /* vq->avail->idx */
+ writel(vq->avail + 2, idx + 1);
+
+ /* Must read after idx is updated */
+ flags = readw(vq->avail);
+ avail_event = readw(vq->used + 4 +
+ (sizeof(struct QVRingUsedElem) * vq->size));
+
+ /* < 1 because we add elements to avail queue one by one */
+ if ((flags & QVRING_USED_F_NO_NOTIFY) == 0 &&
+ (!vq->event || (uint16_t)(idx-avail_event) < 1)) {
+ bus->virtqueue_kick(d, vq);
+ }
+}
+
+void qvirtqueue_set_used_event(QVirtQueue *vq, uint16_t idx)
+{
+ g_assert(vq->event);
+
+ /* vq->avail->used_event */
+ writew(vq->avail + 4 + (2 * vq->size), idx);
+}
diff --git a/tests/libqos/virtio.h b/tests/libqos/virtio.h
new file mode 100644
index 000000000..29fbacbc9
--- /dev/null
+++ b/tests/libqos/virtio.h
@@ -0,0 +1,187 @@
+/*
+ * libqos virtio definitions
+ *
+ * Copyright (c) 2014 Marc MarĂ­
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef LIBQOS_VIRTIO_H
+#define LIBQOS_VIRTIO_H
+
+#include "libqos/malloc.h"
+
+#define QVIRTIO_VENDOR_ID 0x1AF4
+
+#define QVIRTIO_RESET 0x0
+#define QVIRTIO_ACKNOWLEDGE 0x1
+#define QVIRTIO_DRIVER 0x2
+#define QVIRTIO_DRIVER_OK 0x4
+
+#define QVIRTIO_NET_DEVICE_ID 0x1
+#define QVIRTIO_BLK_DEVICE_ID 0x2
+
+#define QVIRTIO_F_NOTIFY_ON_EMPTY 0x01000000
+#define QVIRTIO_F_ANY_LAYOUT 0x08000000
+#define QVIRTIO_F_RING_INDIRECT_DESC 0x10000000
+#define QVIRTIO_F_RING_EVENT_IDX 0x20000000
+#define QVIRTIO_F_BAD_FEATURE 0x40000000
+
+#define QVRING_DESC_F_NEXT 0x1
+#define QVRING_DESC_F_WRITE 0x2
+#define QVRING_DESC_F_INDIRECT 0x4
+
+#define QVIRTIO_F_NOTIFY_ON_EMPTY 0x01000000
+#define QVIRTIO_F_ANY_LAYOUT 0x08000000
+#define QVIRTIO_F_RING_INDIRECT_DESC 0x10000000
+#define QVIRTIO_F_RING_EVENT_IDX 0x20000000
+#define QVIRTIO_F_BAD_FEATURE 0x40000000
+
+#define QVRING_AVAIL_F_NO_INTERRUPT 1
+
+#define QVRING_USED_F_NO_NOTIFY 1
+
+typedef struct QVirtioDevice {
+ /* Device type */
+ uint16_t device_type;
+} QVirtioDevice;
+
+typedef struct QVRingDesc {
+ uint64_t addr;
+ uint32_t len;
+ uint16_t flags;
+ uint16_t next;
+} QVRingDesc;
+
+typedef struct QVRingAvail {
+ uint16_t flags;
+ uint16_t idx;
+ uint16_t ring[0]; /* This is an array of uint16_t */
+ uint16_t used_event;
+} QVRingAvail;
+
+typedef struct QVRingUsedElem {
+ uint32_t id;
+ uint32_t len;
+} QVRingUsedElem;
+
+typedef struct QVRingUsed {
+ uint16_t flags;
+ uint16_t idx;
+ QVRingUsedElem ring[0]; /* This is an array of QVRingUsedElem structs */
+ uint16_t avail_event;
+} QVRingUsed;
+
+typedef struct QVirtQueue {
+ uint64_t desc; /* This points to an array of QVRingDesc */
+ uint64_t avail; /* This points to a QVRingAvail */
+ uint64_t used; /* This points to a QVRingDesc */
+ uint16_t index;
+ uint32_t size;
+ uint32_t free_head;
+ uint32_t num_free;
+ uint32_t align;
+ bool indirect;
+ bool event;
+} QVirtQueue;
+
+typedef struct QVRingIndirectDesc {
+ uint64_t desc; /* This points to an array fo QVRingDesc */
+ uint16_t index;
+ uint16_t elem;
+} QVRingIndirectDesc;
+
+typedef struct QVirtioBus {
+ uint8_t (*config_readb)(QVirtioDevice *d, void *addr);
+ uint16_t (*config_readw)(QVirtioDevice *d, void *addr);
+ uint32_t (*config_readl)(QVirtioDevice *d, void *addr);
+ uint64_t (*config_readq)(QVirtioDevice *d, void *addr);
+
+ /* Get features of the device */
+ uint32_t (*get_features)(QVirtioDevice *d);
+
+ /* Set features of the device */
+ void (*set_features)(QVirtioDevice *d, uint32_t features);
+
+ /* Get features of the guest */
+ uint32_t (*get_guest_features)(QVirtioDevice *d);
+
+ /* Get status of the device */
+ uint8_t (*get_status)(QVirtioDevice *d);
+
+ /* Set status of the device */
+ void (*set_status)(QVirtioDevice *d, uint8_t status);
+
+ /* Get the queue ISR status of the device */
+ bool (*get_queue_isr_status)(QVirtioDevice *d, QVirtQueue *vq);
+
+ /* Get the configuration ISR status of the device */
+ bool (*get_config_isr_status)(QVirtioDevice *d);
+
+ /* Select a queue to work on */
+ void (*queue_select)(QVirtioDevice *d, uint16_t index);
+
+ /* Get the size of the selected queue */
+ uint16_t (*get_queue_size)(QVirtioDevice *d);
+
+ /* Set the address of the selected queue */
+ void (*set_queue_address)(QVirtioDevice *d, uint32_t pfn);
+
+ /* Setup the virtqueue specified by index */
+ QVirtQueue *(*virtqueue_setup)(QVirtioDevice *d, QGuestAllocator *alloc,
+ uint16_t index);
+
+ /* Notify changes in virtqueue */
+ void (*virtqueue_kick)(QVirtioDevice *d, QVirtQueue *vq);
+} QVirtioBus;
+
+static inline uint32_t qvring_size(uint32_t num, uint32_t align)
+{
+ return ((sizeof(struct QVRingDesc) * num + sizeof(uint16_t) * (3 + num)
+ + align - 1) & ~(align - 1))
+ + sizeof(uint16_t) * 3 + sizeof(struct QVRingUsedElem) * num;
+}
+
+uint8_t qvirtio_config_readb(const QVirtioBus *bus, QVirtioDevice *d,
+ void *addr);
+uint16_t qvirtio_config_readw(const QVirtioBus *bus, QVirtioDevice *d,
+ void *addr);
+uint32_t qvirtio_config_readl(const QVirtioBus *bus, QVirtioDevice *d,
+ void *addr);
+uint64_t qvirtio_config_readq(const QVirtioBus *bus, QVirtioDevice *d,
+ void *addr);
+uint32_t qvirtio_get_features(const QVirtioBus *bus, QVirtioDevice *d);
+void qvirtio_set_features(const QVirtioBus *bus, QVirtioDevice *d,
+ uint32_t features);
+
+void qvirtio_reset(const QVirtioBus *bus, QVirtioDevice *d);
+void qvirtio_set_acknowledge(const QVirtioBus *bus, QVirtioDevice *d);
+void qvirtio_set_driver(const QVirtioBus *bus, QVirtioDevice *d);
+void qvirtio_set_driver_ok(const QVirtioBus *bus, QVirtioDevice *d);
+
+void qvirtio_wait_queue_isr(const QVirtioBus *bus, QVirtioDevice *d,
+ QVirtQueue *vq, gint64 timeout_us);
+uint8_t qvirtio_wait_status_byte_no_isr(const QVirtioBus *bus,
+ QVirtioDevice *d,
+ QVirtQueue *vq,
+ uint64_t addr,
+ gint64 timeout_us);
+void qvirtio_wait_config_isr(const QVirtioBus *bus, QVirtioDevice *d,
+ gint64 timeout_us);
+QVirtQueue *qvirtqueue_setup(const QVirtioBus *bus, QVirtioDevice *d,
+ QGuestAllocator *alloc, uint16_t index);
+
+void qvring_init(const QGuestAllocator *alloc, QVirtQueue *vq, uint64_t addr);
+QVRingIndirectDesc *qvring_indirect_desc_setup(QVirtioDevice *d,
+ QGuestAllocator *alloc, uint16_t elem);
+void qvring_indirect_desc_add(QVRingIndirectDesc *indirect, uint64_t data,
+ uint32_t len, bool write);
+uint32_t qvirtqueue_add(QVirtQueue *vq, uint64_t data, uint32_t len, bool write,
+ bool next);
+uint32_t qvirtqueue_add_indirect(QVirtQueue *vq, QVRingIndirectDesc *indirect);
+void qvirtqueue_kick(const QVirtioBus *bus, QVirtioDevice *d, QVirtQueue *vq,
+ uint32_t free_head);
+
+void qvirtqueue_set_used_event(QVirtQueue *vq, uint16_t idx);
+#endif
diff --git a/tests/libqtest.c b/tests/libqtest.c
index 98e8f4b64..9a92aa70e 100644
--- a/tests/libqtest.c
+++ b/tests/libqtest.c
@@ -165,13 +165,15 @@ QTestState *qtest_init(const char *extra_args)
s->qemu_pid = fork();
if (s->qemu_pid == 0) {
+ setenv("QEMU_AUDIO_DRV", "none", true);
command = g_strdup_printf("exec %s "
"-qtest unix:%s,nowait "
- "-qtest-log /dev/null "
+ "-qtest-log %s "
"-qmp unix:%s,nowait "
"-machine accel=qtest "
"-display none "
"%s", qemu_binary, socket_path,
+ getenv("QTEST_LOG") ? "/dev/fd/2" : "/dev/null",
qmp_socket_path,
extra_args ?: "");
execlp("/bin/sh", "sh", "-c", command, NULL);
@@ -358,6 +360,7 @@ static void qmp_response(JSONMessageParser *parser, QList *tokens)
QDict *qtest_qmp_receive(QTestState *s)
{
QMPResponseParser qmp;
+ bool log = getenv("QTEST_LOG") != NULL;
qmp.response = NULL;
json_message_parser_init(&qmp.parser, qmp_response);
@@ -375,6 +378,9 @@ QDict *qtest_qmp_receive(QTestState *s)
exit(1);
}
+ if (log) {
+ len = write(2, &c, 1);
+ }
json_message_parser_feed(&qmp.parser, &c, 1);
}
json_message_parser_destroy(&qmp.parser);
@@ -397,10 +403,14 @@ QDict *qtest_qmpv(QTestState *s, const char *fmt, va_list ap)
/* No need to send anything for an empty QObject. */
if (qobj) {
+ int log = getenv("QTEST_LOG") != NULL;
QString *qstr = qobject_to_json(qobj);
const char *str = qstring_get_str(qstr);
size_t size = qstring_get_length(qstr);
+ if (log) {
+ fprintf(stderr, "%s", str);
+ }
/* Send QMP request */
socket_send(s->qmp_fd, str, size);
@@ -639,6 +649,7 @@ void qtest_add_func(const char *str, void (*fn))
{
gchar *path = g_strdup_printf("/%s/%s", qtest_get_arch(), str);
g_test_add_func(path, fn);
+ g_free(path);
}
void qtest_memwrite(QTestState *s, uint64_t addr, const void *data, size_t size)
@@ -654,6 +665,18 @@ void qtest_memwrite(QTestState *s, uint64_t addr, const void *data, size_t size)
qtest_rsp(s, 0);
}
+void qtest_memset(QTestState *s, uint64_t addr, uint8_t pattern, size_t size)
+{
+ size_t i;
+
+ qtest_sendf(s, "write 0x%" PRIx64 " 0x%zx 0x", addr, size);
+ for (i = 0; i < size; i++) {
+ qtest_sendf(s, "%02x", pattern);
+ }
+ qtest_sendf(s, "\n");
+ qtest_rsp(s, 0);
+}
+
QDict *qmp(const char *fmt, ...)
{
va_list ap;
@@ -673,3 +696,51 @@ void qmp_discard_response(const char *fmt, ...)
qtest_qmpv_discard_response(global_qtest, fmt, ap);
va_end(ap);
}
+
+bool qtest_big_endian(void)
+{
+ const char *arch = qtest_get_arch();
+ int i;
+
+ static const struct {
+ const char *arch;
+ bool big_endian;
+ } endianness[] = {
+ { "aarch64", false },
+ { "alpha", false },
+ { "arm", false },
+ { "cris", false },
+ { "i386", false },
+ { "lm32", true },
+ { "m68k", true },
+ { "microblaze", true },
+ { "microblazeel", false },
+ { "mips", true },
+ { "mips64", true },
+ { "mips64el", false },
+ { "mipsel", false },
+ { "moxie", true },
+ { "or32", true },
+ { "ppc", true },
+ { "ppc64", true },
+ { "ppcemb", true },
+ { "s390x", true },
+ { "sh4", false },
+ { "sh4eb", true },
+ { "sparc", true },
+ { "sparc64", true },
+ { "unicore32", false },
+ { "x86_64", false },
+ { "xtensa", false },
+ { "xtensaeb", true },
+ {},
+ };
+
+ for (i = 0; endianness[i].arch; i++) {
+ if (strcmp(endianness[i].arch, arch) == 0) {
+ return endianness[i].big_endian;
+ }
+ }
+
+ return false;
+}
diff --git a/tests/libqtest.h b/tests/libqtest.h
index 8f323c703..e7413d52d 100644
--- a/tests/libqtest.h
+++ b/tests/libqtest.h
@@ -23,6 +23,7 @@
#include <stdarg.h>
#include <sys/types.h>
#include "qapi/qmp/qdict.h"
+#include "glib-compat.h"
typedef struct QTestState QTestState;
@@ -283,6 +284,17 @@ void qtest_memread(QTestState *s, uint64_t addr, void *data, size_t size);
void qtest_memwrite(QTestState *s, uint64_t addr, const void *data, size_t size);
/**
+ * qtest_memset:
+ * @s: #QTestState instance to operate on.
+ * @addr: Guest address to write to.
+ * @patt: Byte pattern to fill the guest memory region with.
+ * @size: Number of bytes to write.
+ *
+ * Write a pattern to guest memory.
+ */
+void qtest_memset(QTestState *s, uint64_t addr, uint8_t patt, size_t size);
+
+/**
* qtest_clock_step_next:
* @s: #QTestState instance to operate on.
*
@@ -621,6 +633,19 @@ static inline void memwrite(uint64_t addr, const void *data, size_t size)
}
/**
+ * qmemset:
+ * @addr: Guest address to write to.
+ * @patt: Byte pattern to fill the guest memory region with.
+ * @size: Number of bytes to write.
+ *
+ * Write a pattern to guest memory.
+ */
+static inline void qmemset(uint64_t addr, uint8_t patt, size_t size)
+{
+ qtest_memset(global_qtest, addr, patt, size);
+}
+
+/**
* clock_step_next:
*
* Advance the QEMU_CLOCK_VIRTUAL to the next deadline.
@@ -658,4 +683,11 @@ static inline int64_t clock_set(int64_t val)
return qtest_clock_set(global_qtest, val);
}
+/**
+ * qtest_big_endian:
+ *
+ * Returns: True if the architecture under test has a big endian configuration.
+ */
+bool qtest_big_endian(void);
+
#endif
diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json
index ab4d3d96b..d43b5fd2e 100644
--- a/tests/qapi-schema/qapi-schema-test.json
+++ b/tests/qapi-schema/qapi-schema-test.json
@@ -33,6 +33,9 @@
{ 'type': 'UserDefB',
'data': { 'integer': 'int' } }
+{ 'type': 'UserDefC',
+ 'data': { 'string1': 'str', 'string2': 'str' } }
+
{ 'union': 'UserDefUnion',
'base': 'UserDefZero',
'data': { 'a' : 'UserDefA', 'b' : 'UserDefB' } }
@@ -47,6 +50,13 @@
# FIXME generated struct UserDefFlatUnion has members for direct base
# UserDefOne, but lacks members for indirect base UserDefZero
+# this variant of UserDefFlatUnion defaults to a union that uses fields with
+# allocated types to test corner cases in the cleanup/dealloc visitor
+{ 'union': 'UserDefFlatUnion2',
+ 'base': 'UserDefUnionBase',
+ 'discriminator': 'enum1',
+ 'data': { 'value1' : 'UserDefC', 'value2' : 'UserDefB', 'value3' : 'UserDefA' } }
+
{ 'union': 'UserDefAnonUnion',
'discriminator': {},
'data': { 'uda': 'UserDefA', 's': 'str', 'i': 'int' } }
diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out
index 95e989925..08d7304df 100644
--- a/tests/qapi-schema/qapi-schema-test.out
+++ b/tests/qapi-schema/qapi-schema-test.out
@@ -6,9 +6,11 @@
OrderedDict([('type', 'UserDefNested'), ('data', OrderedDict([('string0', 'str'), ('dict1', OrderedDict([('string1', 'str'), ('dict2', OrderedDict([('userdef1', 'UserDefOne'), ('string2', 'str')])), ('*dict3', OrderedDict([('userdef2', 'UserDefOne'), ('string3', 'str')]))]))]))]),
OrderedDict([('type', 'UserDefA'), ('data', OrderedDict([('boolean', 'bool')]))]),
OrderedDict([('type', 'UserDefB'), ('data', OrderedDict([('integer', 'int')]))]),
+ OrderedDict([('type', 'UserDefC'), ('data', OrderedDict([('string1', 'str'), ('string2', 'str')]))]),
OrderedDict([('union', 'UserDefUnion'), ('base', 'UserDefZero'), ('data', OrderedDict([('a', 'UserDefA'), ('b', 'UserDefB')]))]),
OrderedDict([('type', 'UserDefUnionBase'), ('data', OrderedDict([('string', 'str'), ('enum1', 'EnumOne')]))]),
OrderedDict([('union', 'UserDefFlatUnion'), ('base', 'UserDefUnionBase'), ('discriminator', 'enum1'), ('data', OrderedDict([('value1', 'UserDefA'), ('value2', 'UserDefB'), ('value3', 'UserDefB')]))]),
+ OrderedDict([('union', 'UserDefFlatUnion2'), ('base', 'UserDefUnionBase'), ('discriminator', 'enum1'), ('data', OrderedDict([('value1', 'UserDefC'), ('value2', 'UserDefB'), ('value3', 'UserDefA')]))]),
OrderedDict([('union', 'UserDefAnonUnion'), ('discriminator', OrderedDict()), ('data', OrderedDict([('uda', 'UserDefA'), ('s', 'str'), ('i', 'int')]))]),
OrderedDict([('union', 'UserDefNativeListUnion'), ('data', OrderedDict([('integer', ['int']), ('s8', ['int8']), ('s16', ['int16']), ('s32', ['int32']), ('s64', ['int64']), ('u8', ['uint8']), ('u16', ['uint16']), ('u32', ['uint32']), ('u64', ['uint64']), ('number', ['number']), ('boolean', ['bool']), ('string', ['str'])]))]),
OrderedDict([('command', 'user_def_cmd'), ('data', OrderedDict())]),
@@ -32,6 +34,7 @@
OrderedDict([('type', 'UserDefNested'), ('data', OrderedDict([('string0', 'str'), ('dict1', OrderedDict([('string1', 'str'), ('dict2', OrderedDict([('userdef1', 'UserDefOne'), ('string2', 'str')])), ('*dict3', OrderedDict([('userdef2', 'UserDefOne'), ('string3', 'str')]))]))]))]),
OrderedDict([('type', 'UserDefA'), ('data', OrderedDict([('boolean', 'bool')]))]),
OrderedDict([('type', 'UserDefB'), ('data', OrderedDict([('integer', 'int')]))]),
+ OrderedDict([('type', 'UserDefC'), ('data', OrderedDict([('string1', 'str'), ('string2', 'str')]))]),
OrderedDict([('type', 'UserDefUnionBase'), ('data', OrderedDict([('string', 'str'), ('enum1', 'EnumOne')]))]),
OrderedDict([('type', 'UserDefOptions'), ('data', OrderedDict([('*i64', ['int']), ('*u64', ['uint64']), ('*u16', ['uint16']), ('*i64x', 'int'), ('*u64x', 'uint64')]))]),
OrderedDict([('type', 'EventStructOne'), ('data', OrderedDict([('struct1', 'UserDefOne'), ('string', 'str'), ('*enum2', 'EnumOne')]))])]
diff --git a/tests/qdev-monitor-test.c b/tests/qdev-monitor-test.c
deleted file mode 100644
index e20ffd67a..000000000
--- a/tests/qdev-monitor-test.c
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * qdev-monitor.c test cases
- *
- * Copyright (C) 2013 Red Hat Inc.
- *
- * Authors:
- * Stefan Hajnoczi <stefanha@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 <string.h>
-#include <glib.h>
-#include "libqtest.h"
-#include "qapi/qmp/qjson.h"
-
-static void test_device_add(void)
-{
- QDict *response;
- QDict *error;
-
- qtest_start("-drive if=none,id=drive0");
-
- /* Make device_add fail. If this leaks the virtio-blk-pci device then a
- * reference to drive0 will also be held (via qdev properties).
- */
- response = qmp("{\"execute\": \"device_add\","
- " \"arguments\": {"
- " \"driver\": \"virtio-blk-pci\","
- " \"drive\": \"drive0\""
- "}}");
- g_assert(response);
- error = qdict_get_qdict(response, "error");
- g_assert_cmpstr(qdict_get_try_str(error, "class"), ==, "GenericError");
- QDECREF(response);
-
- /* Delete the drive */
- response = qmp("{\"execute\": \"human-monitor-command\","
- " \"arguments\": {"
- " \"command-line\": \"drive_del drive0\""
- "}}");
- g_assert(response);
- g_assert_cmpstr(qdict_get_try_str(response, "return"), ==, "");
- QDECREF(response);
-
- /* Try to re-add the drive. This fails with duplicate IDs if a leaked
- * virtio-blk-pci exists that holds a reference to the old drive0.
- */
- response = qmp("{\"execute\": \"human-monitor-command\","
- " \"arguments\": {"
- " \"command-line\": \"drive_add pci-addr=auto if=none,id=drive0\""
- "}}");
- g_assert(response);
- g_assert_cmpstr(qdict_get_try_str(response, "return"), ==, "OK\r\n");
- QDECREF(response);
-
- qtest_end();
-}
-
-int main(int argc, char **argv)
-{
- const char *arch = qtest_get_arch();
-
- /* Check architecture */
- if (strcmp(arch, "i386") && strcmp(arch, "x86_64")) {
- g_test_message("Skipping test for non-x86\n");
- return 0;
- }
-
- /* Run the tests */
- g_test_init(&argc, &argv, NULL);
-
- qtest_add_func("/qmp/device_add", test_device_add);
-
- return g_test_run();
-}
diff --git a/tests/qemu-iotests-quick.sh b/tests/qemu-iotests-quick.sh
index 8a9a4c68e..12af731c6 100755
--- a/tests/qemu-iotests-quick.sh
+++ b/tests/qemu-iotests-quick.sh
@@ -3,6 +3,6 @@
cd tests/qemu-iotests
ret=0
-./check -T -nocache -qcow2 -g quick || ret=1
+./check -T -qcow2 -g quick || ret=1
exit $ret
diff --git a/tests/qemu-iotests/025 b/tests/qemu-iotests/025
index a5f45b454..467a4b709 100755
--- a/tests/qemu-iotests/025
+++ b/tests/qemu-iotests/025
@@ -40,7 +40,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
. ./common.pattern
_supported_fmt raw qcow2 qed
-_supported_proto file sheepdog rbd nfs
+_supported_proto file sheepdog rbd nfs archipelago
_supported_os Linux
echo "=== Creating image"
diff --git a/tests/qemu-iotests/028 b/tests/qemu-iotests/028
index 9e701e1c2..a1f4423d4 100755
--- a/tests/qemu-iotests/028
+++ b/tests/qemu-iotests/028
@@ -113,6 +113,7 @@ QEMU_COMM_TIMEOUT=1
# Silence output since it contains the disk image path and QEMU's readline
# character echoing makes it very hard to filter the output
_send_qemu_cmd $h "drive_backup disk ${TEST_IMG}.copy" "(qemu)" >/dev/null
+_send_qemu_cmd $h "" "Formatting" | _filter_img_create
qemu_cmd_repeat=20 _send_qemu_cmd $h "info block-jobs" "No active jobs"
_send_qemu_cmd $h 'quit' ""
diff --git a/tests/qemu-iotests/028.out b/tests/qemu-iotests/028.out
index 0e1a5ae65..e8d02459b 100644
--- a/tests/qemu-iotests/028.out
+++ b/tests/qemu-iotests/028.out
@@ -468,7 +468,8 @@ No errors were found on the image.
block-backup
-Formatting 'TEST_DIR/t.qcow2.copy', fmt=qcow2 size=4294968832 backing_file='TEST_DIR/t.qcow2.base' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off
+Formatting 'TEST_DIR/t.IMGFMT.copy', fmt=IMGFMT size=4294968832 backing_file='TEST_DIR/t.IMGFMT.base' backing_fmt='IMGFMT'
+(qemu)
(qemu) iininfinfoinfo info binfo blinfo bloinfo blocinfo blockinfo block-info block-jinfo block-joinfo block-jobinfo block-jobs
Type backup, device disk: Completed 0 of 4294968832 bytes, speed limit 0 bytes/s
iininfinfoinfo info binfo blinfo bloinfo blocinfo blockinfo block-info block-jinfo block-joinfo block-jobinfo block-jobs
diff --git a/tests/qemu-iotests/039.out b/tests/qemu-iotests/039.out
index 67e774430..0adf1535e 100644
--- a/tests/qemu-iotests/039.out
+++ b/tests/qemu-iotests/039.out
@@ -25,7 +25,10 @@ read 512/512 bytes at offset 0
incompatible_features 0x1
== Repairing the image file must succeed ==
-Repairing cluster 5 refcount=0 reference=1
+ERROR cluster 5 refcount=0 reference=1
+Rebuilding refcount structure
+Repairing cluster 1 refcount=1 reference=0
+Repairing cluster 2 refcount=1 reference=0
The following inconsistencies were found and repaired:
0 leaked clusters
@@ -45,7 +48,10 @@ wrote 512/512 bytes at offset 0
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
./039: Aborted ( ulimit -c 0; exec "$@" )
incompatible_features 0x1
-Repairing cluster 5 refcount=0 reference=1
+ERROR cluster 5 refcount=0 reference=1
+Rebuilding refcount structure
+Repairing cluster 1 refcount=1 reference=0
+Repairing cluster 2 refcount=1 reference=0
wrote 512/512 bytes at offset 0
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
incompatible_features 0x0
diff --git a/tests/qemu-iotests/040 b/tests/qemu-iotests/040
index f1e16c11c..2b432ad7a 100755
--- a/tests/qemu-iotests/040
+++ b/tests/qemu-iotests/040
@@ -43,8 +43,7 @@ class ImageCommitTestCase(iotests.QMPTestCase):
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)
+ self.assert_qmp(event, 'data/offset', event['data']['len'])
if need_ready:
self.assertTrue(ready, "Expecting BLOCK_JOB_COMPLETED event")
completed = True
@@ -52,7 +51,6 @@ class ImageCommitTestCase(iotests.QMPTestCase):
ready = True
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_block_jobs()
diff --git a/tests/qemu-iotests/041 b/tests/qemu-iotests/041
index 5dbd4ee91..59a8f733f 100755
--- a/tests/qemu-iotests/041
+++ b/tests/qemu-iotests/041
@@ -52,8 +52,7 @@ class ImageMirroringTestCase(iotests.QMPTestCase):
event = self.cancel_and_wait(drive=drive)
self.assertEquals(event['event'], 'BLOCK_JOB_COMPLETED')
self.assert_qmp(event, 'data/type', 'mirror')
- self.assert_qmp(event, 'data/offset', self.image_len)
- self.assert_qmp(event, 'data/len', self.image_len)
+ self.assert_qmp(event, 'data/offset', event['data']['len'])
def complete_and_wait(self, drive='drive0', wait_ready=True):
'''Complete a block job and wait for it to finish'''
@@ -417,7 +416,6 @@ new_state = "1"
self.assert_qmp(event, 'data/type', 'mirror')
self.assert_qmp(event, 'data/device', 'drive0')
self.assert_qmp(event, 'data/error', 'Input/output error')
- self.assert_qmp(event, 'data/len', self.image_len)
completed = True
self.assert_no_active_block_jobs()
@@ -568,7 +566,6 @@ new_state = "1"
self.assert_qmp(event, 'data/type', 'mirror')
self.assert_qmp(event, 'data/device', 'drive0')
self.assert_qmp(event, 'data/error', 'Input/output error')
- self.assert_qmp(event, 'data/len', self.image_len)
completed = True
self.assert_no_active_block_jobs()
diff --git a/tests/qemu-iotests/049.out b/tests/qemu-iotests/049.out
index 71ca44d76..09ca0aed4 100644
--- a/tests/qemu-iotests/049.out
+++ b/tests/qemu-iotests/049.out
@@ -179,7 +179,7 @@ qemu-img create -f qcow2 -o preallocation=metadata TEST_DIR/t.qcow2 64M
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=65536 preallocation='metadata' lazy_refcounts=off
qemu-img create -f qcow2 -o preallocation=1234 TEST_DIR/t.qcow2 64M
-qemu-img: TEST_DIR/t.qcow2: Invalid preallocation mode: '1234'
+qemu-img: TEST_DIR/t.qcow2: invalid parameter value: 1234
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=65536 preallocation='1234' lazy_refcounts=off
== Check encryption option ==
diff --git a/tests/qemu-iotests/051 b/tests/qemu-iotests/051
index a41334e02..11c858f27 100755
--- a/tests/qemu-iotests/051
+++ b/tests/qemu-iotests/051
@@ -199,6 +199,29 @@ run_qemu -drive file.driver=raw
run_qemu -drive foo=bar
echo
+echo === Specifying both an option and its legacy alias ===
+echo
+
+run_qemu -drive file="$TEST_IMG",iops=1234,throttling.iops-total=5678
+run_qemu -drive file="$TEST_IMG",iops_rd=1234,throttling.iops-read=5678
+run_qemu -drive file="$TEST_IMG",iops_wr=1234,throttling.iops-write=5678
+
+run_qemu -drive file="$TEST_IMG",bps=1234,throttling.bps-total=5678
+run_qemu -drive file="$TEST_IMG",bps_rd=1234,throttling.bps-read=5678
+run_qemu -drive file="$TEST_IMG",bps_wr=1234,throttling.bps-write=5678
+
+run_qemu -drive file="$TEST_IMG",iops_max=1234,throttling.iops-total-max=5678
+run_qemu -drive file="$TEST_IMG",iops_rd_max=1234,throttling.iops-read-max=5678
+run_qemu -drive file="$TEST_IMG",iops_wr_max=1234,throttling.iops-write-max=5678
+
+run_qemu -drive file="$TEST_IMG",bps_max=1234,throttling.bps-total-max=5678
+run_qemu -drive file="$TEST_IMG",bps_rd_max=1234,throttling.bps-read-max=5678
+run_qemu -drive file="$TEST_IMG",bps_wr_max=1234,throttling.bps-write-max=5678
+
+run_qemu -drive file="$TEST_IMG",iops_size=1234,throttling.iops-size=5678
+run_qemu -drive file="$TEST_IMG",readonly=on,read-only=off
+
+echo
echo === Parsing protocol from file name ===
echo
diff --git a/tests/qemu-iotests/051.out b/tests/qemu-iotests/051.out
index d7b0f503a..2c7e80876 100644
--- a/tests/qemu-iotests/051.out
+++ b/tests/qemu-iotests/051.out
@@ -149,13 +149,11 @@ QEMU_PROG: -device ide-hd,drive=disk: Device 'ide-hd' could not be initialized
Testing: -drive if=none,id=disk -device lsi53c895a -device scsi-disk,drive=disk
QEMU X.Y.Z monitor - type 'help' for more information
(qemu) QEMU_PROG: -device scsi-disk,drive=disk: Device needs media, but drive is empty
-QEMU_PROG: -device scsi-disk,drive=disk: Device initialization failed.
QEMU_PROG: -device scsi-disk,drive=disk: Device 'scsi-disk' could not be initialized
Testing: -drive if=none,id=disk -device lsi53c895a -device scsi-hd,drive=disk
QEMU X.Y.Z monitor - type 'help' for more information
(qemu) QEMU_PROG: -device scsi-hd,drive=disk: Device needs media, but drive is empty
-QEMU_PROG: -device scsi-hd,drive=disk: Device initialization failed.
QEMU_PROG: -device scsi-hd,drive=disk: Device 'scsi-hd' could not be initialized
@@ -276,6 +274,51 @@ Testing: -drive foo=bar
QEMU_PROG: -drive foo=bar: could not open disk image ide0-hd0: Must specify either driver or file
+=== Specifying both an option and its legacy alias ===
+
+Testing: -drive file=TEST_DIR/t.qcow2,iops=1234,throttling.iops-total=5678
+QEMU_PROG: -drive file=TEST_DIR/t.qcow2,iops=1234,throttling.iops-total=5678: 'throttling.iops-total' and its alias 'iops' can't be used at the same time
+
+Testing: -drive file=TEST_DIR/t.qcow2,iops_rd=1234,throttling.iops-read=5678
+QEMU_PROG: -drive file=TEST_DIR/t.qcow2,iops_rd=1234,throttling.iops-read=5678: 'throttling.iops-read' and its alias 'iops_rd' can't be used at the same time
+
+Testing: -drive file=TEST_DIR/t.qcow2,iops_wr=1234,throttling.iops-write=5678
+QEMU_PROG: -drive file=TEST_DIR/t.qcow2,iops_wr=1234,throttling.iops-write=5678: 'throttling.iops-write' and its alias 'iops_wr' can't be used at the same time
+
+Testing: -drive file=TEST_DIR/t.qcow2,bps=1234,throttling.bps-total=5678
+QEMU_PROG: -drive file=TEST_DIR/t.qcow2,bps=1234,throttling.bps-total=5678: 'throttling.bps-total' and its alias 'bps' can't be used at the same time
+
+Testing: -drive file=TEST_DIR/t.qcow2,bps_rd=1234,throttling.bps-read=5678
+QEMU_PROG: -drive file=TEST_DIR/t.qcow2,bps_rd=1234,throttling.bps-read=5678: 'throttling.bps-read' and its alias 'bps_rd' can't be used at the same time
+
+Testing: -drive file=TEST_DIR/t.qcow2,bps_wr=1234,throttling.bps-write=5678
+QEMU_PROG: -drive file=TEST_DIR/t.qcow2,bps_wr=1234,throttling.bps-write=5678: 'throttling.bps-write' and its alias 'bps_wr' can't be used at the same time
+
+Testing: -drive file=TEST_DIR/t.qcow2,iops_max=1234,throttling.iops-total-max=5678
+QEMU_PROG: -drive file=TEST_DIR/t.qcow2,iops_max=1234,throttling.iops-total-max=5678: 'throttling.iops-total-max' and its alias 'iops_max' can't be used at the same time
+
+Testing: -drive file=TEST_DIR/t.qcow2,iops_rd_max=1234,throttling.iops-read-max=5678
+QEMU_PROG: -drive file=TEST_DIR/t.qcow2,iops_rd_max=1234,throttling.iops-read-max=5678: 'throttling.iops-read-max' and its alias 'iops_rd_max' can't be used at the same time
+
+Testing: -drive file=TEST_DIR/t.qcow2,iops_wr_max=1234,throttling.iops-write-max=5678
+QEMU_PROG: -drive file=TEST_DIR/t.qcow2,iops_wr_max=1234,throttling.iops-write-max=5678: 'throttling.iops-write-max' and its alias 'iops_wr_max' can't be used at the same time
+
+Testing: -drive file=TEST_DIR/t.qcow2,bps_max=1234,throttling.bps-total-max=5678
+QEMU_PROG: -drive file=TEST_DIR/t.qcow2,bps_max=1234,throttling.bps-total-max=5678: 'throttling.bps-total-max' and its alias 'bps_max' can't be used at the same time
+
+Testing: -drive file=TEST_DIR/t.qcow2,bps_rd_max=1234,throttling.bps-read-max=5678
+QEMU_PROG: -drive file=TEST_DIR/t.qcow2,bps_rd_max=1234,throttling.bps-read-max=5678: 'throttling.bps-read-max' and its alias 'bps_rd_max' can't be used at the same time
+
+Testing: -drive file=TEST_DIR/t.qcow2,bps_wr_max=1234,throttling.bps-write-max=5678
+QEMU_PROG: -drive file=TEST_DIR/t.qcow2,bps_wr_max=1234,throttling.bps-write-max=5678: 'throttling.bps-write-max' and its alias 'bps_wr_max' can't be used at the same time
+
+Testing: -drive file=TEST_DIR/t.qcow2,iops_size=1234,throttling.iops-size=5678
+QEMU_PROG: -drive file=TEST_DIR/t.qcow2,iops_size=1234,throttling.iops-size=5678: 'throttling.iops-size' and its alias 'iops_size' can't be used at the same time
+
+Testing: -drive file=TEST_DIR/t.qcow2,readonly=on,read-only=off
+QEMU_PROG: -drive file=TEST_DIR/t.qcow2,readonly=on,read-only=off: 'read-only' and its alias 'readonly' can't be used at the same time
+
+
=== Parsing protocol from file name ===
Testing: -hda foo:bar
diff --git a/tests/qemu-iotests/052 b/tests/qemu-iotests/052
index 6bdae9278..61959e286 100755
--- a/tests/qemu-iotests/052
+++ b/tests/qemu-iotests/052
@@ -41,8 +41,9 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
_supported_fmt generic
_supported_proto file
_supported_os Linux
-_default_cache_mode "writethrough"
-_supported_cache_modes "writethrough"
+
+# Don't do O_DIRECT on tmpfs
+_supported_cache_modes "writeback" "writethrough" "unsafe"
size=128M
_make_test_img $size
diff --git a/tests/qemu-iotests/059 b/tests/qemu-iotests/059
index 26a2fd3e0..3c053c29b 100755
--- a/tests/qemu-iotests/059
+++ b/tests/qemu-iotests/059
@@ -114,6 +114,10 @@ echo
echo "=== Testing version 3 ==="
_use_sample_img iotest-version3.vmdk.bz2
_img_info
+for i in {0..99}; do
+ $QEMU_IO -r -c "read -P $(( i % 10 + 0x30 )) $(( i * 64 * 1024 * 10 + i * 512 )) 512" $TEST_IMG \
+ | _filter_qemu_io
+done
echo
echo "=== Testing 4TB monolithicFlat creation and IO ==="
diff --git a/tests/qemu-iotests/059.out b/tests/qemu-iotests/059.out
index eba0dedda..0dadba658 100644
--- a/tests/qemu-iotests/059.out
+++ b/tests/qemu-iotests/059.out
@@ -2056,8 +2056,208 @@ wrote 512/512 bytes at offset 10240
=== Testing version 3 ===
image: TEST_DIR/iotest-version3.IMGFMT
file format: IMGFMT
-virtual size: 1.0G (1073741824 bytes)
+virtual size: 16G (17179869184 bytes)
cluster_size: 65536
+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 655872
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 1311744
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 1967616
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 2623488
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 3279360
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 3935232
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 4591104
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 5246976
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 5902848
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 6558720
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 7214592
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 7870464
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 8526336
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 9182208
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 9838080
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 10493952
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 11149824
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 11805696
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 12461568
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 13117440
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 13773312
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 14429184
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 15085056
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 15740928
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 16396800
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 17052672
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 17708544
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 18364416
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 19020288
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 19676160
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 20332032
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 20987904
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 21643776
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 22299648
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 22955520
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 23611392
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 24267264
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 24923136
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 25579008
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 26234880
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 26890752
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 27546624
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 28202496
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 28858368
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 29514240
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 30170112
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 30825984
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 31481856
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 32137728
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 32793600
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 33449472
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 34105344
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 34761216
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 35417088
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 36072960
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 36728832
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 37384704
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 38040576
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 38696448
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 39352320
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 40008192
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 40664064
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 41319936
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 41975808
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 42631680
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 43287552
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 43943424
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 44599296
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 45255168
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 45911040
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 46566912
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 47222784
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 47878656
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 48534528
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 49190400
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 49846272
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 50502144
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 51158016
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 51813888
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 52469760
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 53125632
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 53781504
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 54437376
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 55093248
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 55749120
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 56404992
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 57060864
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 57716736
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 58372608
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 59028480
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 59684352
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 60340224
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 60996096
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 61651968
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 62307840
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 62963712
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 63619584
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 64275456
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 64931328
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
=== Testing 4TB monolithicFlat creation and IO ===
Formatting 'TEST_DIR/iotest-version3.IMGFMT', fmt=IMGFMT size=4398046511104
diff --git a/tests/qemu-iotests/060 b/tests/qemu-iotests/060
index 3cffc12fe..9772d365a 100755
--- a/tests/qemu-iotests/060
+++ b/tests/qemu-iotests/060
@@ -76,6 +76,9 @@ $QEMU_IO -c "$OPEN_RW" -c "write -P 0x2a 0 512" | _filter_qemu_io
# The corrupt bit must now be set
$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
+# This information should be available through qemu-img info
+$QEMU_IMG info "$TEST_IMG" | _filter_testdir
+
# Try to open the image R/W (which should fail)
$QEMU_IO -c "$OPEN_RW" -c "read 0 512" 2>&1 | _filter_qemu_io \
| _filter_testdir \
@@ -164,6 +167,67 @@ wait_break 0
write 64k 64k
resume 0" | $QEMU_IO | _filter_qemu_io
+echo
+echo "=== Testing unallocated image header ==="
+echo
+_make_test_img 64M
+# Create L1/L2
+$QEMU_IO -c "write 0 64k" "$TEST_IMG" | _filter_qemu_io
+poke_file "$TEST_IMG" "$rb_offset" "\x00\x00"
+$QEMU_IO -c "write 64k 64k" "$TEST_IMG" | _filter_qemu_io
+
+echo
+echo "=== Testing unaligned L1 entry ==="
+echo
+_make_test_img 64M
+$QEMU_IO -c "write 0 64k" "$TEST_IMG" | _filter_qemu_io
+# This will be masked with ~(512 - 1) = ~0x1ff, so whether the lower 9 bits are
+# aligned or not does not matter
+poke_file "$TEST_IMG" "$l1_offset" "\x80\x00\x00\x00\x00\x04\x2a\x00"
+$QEMU_IO -c "read 0 64k" "$TEST_IMG" | _filter_qemu_io
+
+echo
+echo "=== Testing unaligned L2 entry ==="
+echo
+_make_test_img 64M
+$QEMU_IO -c "write 0 64k" "$TEST_IMG" | _filter_qemu_io
+poke_file "$TEST_IMG" "$l2_offset" "\x80\x00\x00\x00\x00\x05\x2a\x00"
+$QEMU_IO -c "read 0 64k" "$TEST_IMG" | _filter_qemu_io
+
+echo
+echo "=== Testing unaligned reftable entry ==="
+echo
+_make_test_img 64M
+poke_file "$TEST_IMG" "$rt_offset" "\x00\x00\x00\x00\x00\x02\x2a\x00"
+$QEMU_IO -c "write 0 64k" "$TEST_IMG" | _filter_qemu_io
+
+echo
+echo "=== Testing non-fatal corruption on freeing ==="
+echo
+_make_test_img 64M
+$QEMU_IO -c "write 0 64k" "$TEST_IMG" | _filter_qemu_io
+poke_file "$TEST_IMG" "$l2_offset" "\x80\x00\x00\x00\x00\x05\x2a\x00"
+$QEMU_IO -c "discard 0 64k" "$TEST_IMG" | _filter_qemu_io
+
+echo
+echo "=== Testing read-only corruption report ==="
+echo
+_make_test_img 64M
+$QEMU_IO -c "write 0 64k" "$TEST_IMG" | _filter_qemu_io
+poke_file "$TEST_IMG" "$l2_offset" "\x80\x00\x00\x00\x00\x05\x2a\x00"
+# Should only emit a single error message
+$QEMU_IO -c "$OPEN_RO" -c "read 0 64k" -c "read 0 64k" | _filter_qemu_io
+
+echo
+echo "=== Testing non-fatal and then fatal corruption report ==="
+echo
+_make_test_img 64M
+$QEMU_IO -c "write 0 128k" "$TEST_IMG" | _filter_qemu_io
+poke_file "$TEST_IMG" "$l2_offset" "\x80\x00\x00\x00\x00\x05\x2a\x00"
+poke_file "$TEST_IMG" "$(($l2_offset+8))" "\x80\x00\x00\x00\x00\x06\x2a\x00"
+# Should emit two error messages
+$QEMU_IO -c "discard 0 64k" -c "read 64k 64k" "$TEST_IMG" | _filter_qemu_io
+
# success, all done
echo "*** done"
rm -f $seq.full
diff --git a/tests/qemu-iotests/060.out b/tests/qemu-iotests/060.out
index a51794803..9419da1b4 100644
--- a/tests/qemu-iotests/060.out
+++ b/tests/qemu-iotests/060.out
@@ -8,9 +8,18 @@ ERROR cluster 3 refcount=1 reference=3
1 errors were found on the image.
Data may be corrupted, or further writes to the image may corrupt it.
incompatible_features 0x0
-qcow2: Preventing invalid write on metadata (overlaps with active L1 table); image marked as corrupt.
+qcow2: Marking image as corrupt: Preventing invalid write on metadata (overlaps with active L1 table); further corruption events will be suppressed
write failed: Input/output error
incompatible_features 0x2
+image: TEST_DIR/t.qcow2
+file format: qcow2
+virtual size: 64M (67108864 bytes)
+disk size: 196K
+cluster_size: 65536
+Format specific information:
+ compat: 1.1
+ lazy refcounts: false
+ corrupt: true
qemu-io: can't open device TEST_DIR/t.IMGFMT: IMGFMT: Image is corrupt; cannot be opened read/write
read 512/512 bytes at offset 0
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
@@ -24,14 +33,18 @@ ERROR cluster 2 refcount=1 reference=2
2 errors were found on the image.
Data may be corrupted, or further writes to the image may corrupt it.
incompatible_features 0x0
-qcow2: Preventing invalid write on metadata (overlaps with refcount block); image marked as corrupt.
+qcow2: Marking image as corrupt: Preventing invalid write on metadata (overlaps with refcount block); further corruption events will be suppressed
write failed: Input/output error
incompatible_features 0x2
-Repairing refcount block 0 refcount=2
+ERROR refcount block 0 refcount=2
+ERROR cluster 2 refcount=1 reference=2
+Rebuilding refcount structure
+Repairing cluster 1 refcount=1 reference=0
+Repairing cluster 2 refcount=2 reference=1
The following inconsistencies were found and repaired:
0 leaked clusters
- 1 corruptions
+ 2 corruptions
Double checking the fixed image now...
No errors were found on the image.
@@ -56,9 +69,11 @@ Data may be corrupted, or further writes to the image may corrupt it.
1 leaked clusters were found on the image.
This means waste of disk space, but no harm to data.
incompatible_features 0x0
-qcow2: Preventing invalid write on metadata (overlaps with inactive L2 table); image marked as corrupt.
+qcow2: Marking image as corrupt: Preventing invalid write on metadata (overlaps with inactive L2 table); further corruption events will be suppressed
write failed: Input/output error
incompatible_features 0x2
+ERROR cluster 4 refcount=1 reference=2
+Leaked cluster 9 refcount=1 reference=0
Repairing cluster 4 refcount=1 reference=2
Repairing cluster 9 refcount=1 reference=0
Repairing OFLAG_COPIED data cluster: l2_entry=8000000000040000 refcount=2
@@ -88,9 +103,68 @@ wrote 65536/65536 bytes at offset 536870912
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
discard 65536/65536 bytes at offset 0
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
-qcow2: Preventing invalid write on metadata (overlaps with active L2 table); image marked as corrupt.
+qcow2: Marking image as corrupt: Preventing invalid write on metadata (overlaps with active L2 table); further corruption events will be suppressed
blkdebug: Suspended request '0'
write failed: Input/output error
blkdebug: Resuming request '0'
aio_write failed: No medium found
+
+=== Testing unallocated image header ===
+
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
+wrote 65536/65536 bytes at offset 0
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+qcow2: Marking image as corrupt: Preventing invalid write on metadata (overlaps with qcow2_header); further corruption events will be suppressed
+write failed: Input/output error
+
+=== Testing unaligned L1 entry ===
+
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
+wrote 65536/65536 bytes at offset 0
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+qcow2: Marking image as corrupt: L2 table offset 0x42a00 unaligned (L1 index: 0); further corruption events will be suppressed
+read failed: Input/output error
+
+=== Testing unaligned L2 entry ===
+
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
+wrote 65536/65536 bytes at offset 0
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+qcow2: Marking image as corrupt: Data cluster offset 0x52a00 unaligned (L2 offset: 0x40000, L2 index: 0); further corruption events will be suppressed
+read failed: Input/output error
+
+=== Testing unaligned reftable entry ===
+
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
+qcow2: Marking image as corrupt: Refblock offset 0x22a00 unaligned (reftable index: 0); further corruption events will be suppressed
+write failed: Input/output error
+
+=== Testing non-fatal corruption on freeing ===
+
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
+wrote 65536/65536 bytes at offset 0
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+qcow2: Image is corrupt: Cannot free unaligned cluster 0x52a00; further non-fatal corruption events will be suppressed
+discard 65536/65536 bytes at offset 0
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+=== Testing read-only corruption report ===
+
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
+wrote 65536/65536 bytes at offset 0
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+qcow2: Image is corrupt: Data cluster offset 0x52a00 unaligned (L2 offset: 0x40000, L2 index: 0); further non-fatal corruption events will be suppressed
+read failed: Input/output error
+read failed: Input/output error
+
+=== Testing non-fatal and then fatal corruption report ===
+
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
+wrote 131072/131072 bytes at offset 0
+128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+qcow2: Image is corrupt: Cannot free unaligned cluster 0x52a00; further non-fatal corruption events will be suppressed
+qcow2: Marking image as corrupt: Data cluster offset 0x62a00 unaligned (L2 offset: 0x40000, L2 index: 0x1); further corruption events will be suppressed
+discard 65536/65536 bytes at offset 0
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read failed: Input/output error
*** done
diff --git a/tests/qemu-iotests/061 b/tests/qemu-iotests/061
index ab98def6d..8d37f8a65 100755
--- a/tests/qemu-iotests/061
+++ b/tests/qemu-iotests/061
@@ -209,6 +209,31 @@ $QEMU_IMG amend -o "compat=0.10" "$TEST_IMG"
_check_test_img
$QEMU_IO -c "read -P 0 0 64M" "$TEST_IMG" | _filter_qemu_io
+echo
+echo "=== Testing progress report without snapshot ==="
+echo
+IMGOPTS="compat=1.1" TEST_IMG="$TEST_IMG.base" _make_test_img 4G
+IMGOPTS="compat=1.1" _make_test_img -b "$TEST_IMG.base" 4G
+$QEMU_IO -c "write -z 0 64k" \
+ -c "write -z 1G 64k" \
+ -c "write -z 2G 64k" \
+ -c "write -z 3G 64k" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IMG amend -p -o "compat=0.10" "$TEST_IMG"
+_check_test_img
+
+echo
+echo "=== Testing progress report with snapshot ==="
+echo
+IMGOPTS="compat=1.1" TEST_IMG="$TEST_IMG.base" _make_test_img 4G
+IMGOPTS="compat=1.1" _make_test_img -b "$TEST_IMG.base" 4G
+$QEMU_IO -c "write -z 0 64k" \
+ -c "write -z 1G 64k" \
+ -c "write -z 2G 64k" \
+ -c "write -z 3G 64k" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IMG snapshot -c foo "$TEST_IMG"
+$QEMU_IMG amend -p -o "compat=0.10" "$TEST_IMG"
+_check_test_img
+
# success, all done
echo "*** done"
rm -f $seq.full
diff --git a/tests/qemu-iotests/061.out b/tests/qemu-iotests/061.out
index e3724700b..9045544df 100644
--- a/tests/qemu-iotests/061.out
+++ b/tests/qemu-iotests/061.out
@@ -76,8 +76,11 @@ autoclear_features 0x0
refcount_order 4
header_length 104
-Repairing cluster 5 refcount=0 reference=1
-Repairing cluster 6 refcount=0 reference=1
+ERROR cluster 5 refcount=0 reference=1
+ERROR cluster 6 refcount=0 reference=1
+Rebuilding refcount structure
+Repairing cluster 1 refcount=1 reference=0
+Repairing cluster 2 refcount=1 reference=0
magic 0x514649fb
version 2
backing_file_offset 0x0
@@ -87,7 +90,7 @@ size 67108864
crypt_method 0
l1_size 1
l1_table_offset 0x30000
-refcount_table_offset 0x10000
+refcount_table_offset 0x80000
refcount_table_clusters 1
nb_snapshots 0
snapshot_offset 0x0
@@ -230,8 +233,11 @@ autoclear_features 0x0
refcount_order 4
header_length 104
-Repairing cluster 5 refcount=0 reference=1
-Repairing cluster 6 refcount=0 reference=1
+ERROR cluster 5 refcount=0 reference=1
+ERROR cluster 6 refcount=0 reference=1
+Rebuilding refcount structure
+Repairing cluster 1 refcount=1 reference=0
+Repairing cluster 2 refcount=1 reference=0
magic 0x514649fb
version 3
backing_file_offset 0x0
@@ -241,7 +247,7 @@ size 67108864
crypt_method 0
l1_size 1
l1_table_offset 0x30000
-refcount_table_offset 0x10000
+refcount_table_offset 0x80000
refcount_table_clusters 1
nb_snapshots 0
snapshot_offset 0x0
@@ -384,4 +390,34 @@ wrote 67108864/67108864 bytes at offset 0
No errors were found on the image.
read 67108864/67108864 bytes at offset 0
64 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+=== Testing progress report without snapshot ===
+
+Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=4294967296
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294967296 backing_file='TEST_DIR/t.IMGFMT.base'
+wrote 65536/65536 bytes at offset 0
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 65536/65536 bytes at offset 1073741824
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 65536/65536 bytes at offset 2147483648
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 65536/65536 bytes at offset 3221225472
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+ (0.00/100%) (12.50/100%) (25.00/100%) (37.50/100%) (50.00/100%) (62.50/100%) (75.00/100%) (87.50/100%) (100.00/100%) (100.00/100%)
+No errors were found on the image.
+
+=== Testing progress report with snapshot ===
+
+Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=4294967296
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294967296 backing_file='TEST_DIR/t.IMGFMT.base'
+wrote 65536/65536 bytes at offset 0
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 65536/65536 bytes at offset 1073741824
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 65536/65536 bytes at offset 2147483648
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 65536/65536 bytes at offset 3221225472
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+ (0.00/100%) (6.25/100%) (12.50/100%) (18.75/100%) (25.00/100%) (31.25/100%) (37.50/100%) (43.75/100%) (50.00/100%) (56.25/100%) (62.50/100%) (68.75/100%) (75.00/100%) (81.25/100%) (87.50/100%) (93.75/100%) (100.00/100%) (100.00/100%)
+No errors were found on the image.
*** done
diff --git a/tests/qemu-iotests/065 b/tests/qemu-iotests/065
index e89b61d70..8d3a9c9af 100755
--- a/tests/qemu-iotests/065
+++ b/tests/qemu-iotests/065
@@ -94,28 +94,28 @@ class TestQCow2(TestQemuImgInfo):
class TestQCow3NotLazy(TestQemuImgInfo):
'''Testing a qcow2 version 3 image with lazy refcounts disabled'''
img_options = 'compat=1.1,lazy_refcounts=off'
- json_compare = { 'compat': '1.1', 'lazy-refcounts': False }
- human_compare = [ 'compat: 1.1', 'lazy refcounts: false' ]
+ json_compare = { 'compat': '1.1', 'lazy-refcounts': False, 'corrupt': False }
+ human_compare = [ 'compat: 1.1', 'lazy refcounts: false', 'corrupt: false' ]
class TestQCow3Lazy(TestQemuImgInfo):
'''Testing a qcow2 version 3 image with lazy refcounts enabled'''
img_options = 'compat=1.1,lazy_refcounts=on'
- json_compare = { 'compat': '1.1', 'lazy-refcounts': True }
- human_compare = [ 'compat: 1.1', 'lazy refcounts: true' ]
+ json_compare = { 'compat': '1.1', 'lazy-refcounts': True, 'corrupt': False }
+ human_compare = [ 'compat: 1.1', 'lazy refcounts: true', 'corrupt: false' ]
class TestQCow3NotLazyQMP(TestQMP):
'''Testing a qcow2 version 3 image with lazy refcounts disabled, opening
with lazy refcounts enabled'''
img_options = 'compat=1.1,lazy_refcounts=off'
qemu_options = 'lazy-refcounts=on'
- compare = { 'compat': '1.1', 'lazy-refcounts': False }
+ compare = { 'compat': '1.1', 'lazy-refcounts': False, 'corrupt': False }
class TestQCow3LazyQMP(TestQMP):
'''Testing a qcow2 version 3 image with lazy refcounts enabled, opening
with lazy refcounts disabled'''
img_options = 'compat=1.1,lazy_refcounts=on'
qemu_options = 'lazy-refcounts=off'
- compare = { 'compat': '1.1', 'lazy-refcounts': True }
+ compare = { 'compat': '1.1', 'lazy-refcounts': True, 'corrupt': False }
TestImageInfoSpecific = None
TestQemuImgInfo = None
diff --git a/tests/qemu-iotests/067.out b/tests/qemu-iotests/067.out
index 7e090b95a..0f72dcf63 100644
--- a/tests/qemu-iotests/067.out
+++ b/tests/qemu-iotests/067.out
@@ -6,7 +6,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728
Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,if=none,id=disk -device virtio-blk-pci,drive=disk,id=virtio0
QMP_VERSION
{"return": {}}
-{"return": [{"io-status": "ok", "device": "disk", "locked": false, "removable": false, "inserted": {"iops_rd": 0, "detect_zeroes": "off", "image": {"virtual-size": 134217728, "filename": "TEST_DIR/t.qcow2", "cluster-size": 65536, "format": "qcow2", "actual-size": SIZE, "format-specific": {"type": "qcow2", "data": {"compat": "1.1", "lazy-refcounts": false}}, "dirty-flag": false}, "iops_wr": 0, "ro": false, "backing_file_depth": 0, "drv": "qcow2", "iops": 0, "bps_wr": 0, "encrypted": false, "bps": 0, "bps_rd": 0, "file": "TEST_DIR/t.qcow2", "encryption_key_missing": false}, "type": "unknown"}, {"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}]}
+{"return": [{"io-status": "ok", "device": "disk", "locked": false, "removable": false, "inserted": {"iops_rd": 0, "detect_zeroes": "off", "image": {"virtual-size": 134217728, "filename": "TEST_DIR/t.qcow2", "cluster-size": 65536, "format": "qcow2", "actual-size": SIZE, "format-specific": {"type": "qcow2", "data": {"compat": "1.1", "lazy-refcounts": false, "corrupt": false}}, "dirty-flag": false}, "iops_wr": 0, "ro": false, "backing_file_depth": 0, "drv": "qcow2", "iops": 0, "bps_wr": 0, "encrypted": false, "bps": 0, "bps_rd": 0, "file": "TEST_DIR/t.qcow2", "encryption_key_missing": false}, "type": "unknown"}, {"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}]}
{"return": {}}
{"return": {}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_DELETED", "data": {"path": "/machine/peripheral/virtio0/virtio-backend"}}
@@ -24,7 +24,7 @@ QMP_VERSION
Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,if=none,id=disk
QMP_VERSION
{"return": {}}
-{"return": [{"device": "disk", "locked": false, "removable": true, "inserted": {"iops_rd": 0, "detect_zeroes": "off", "image": {"virtual-size": 134217728, "filename": "TEST_DIR/t.qcow2", "cluster-size": 65536, "format": "qcow2", "actual-size": SIZE, "format-specific": {"type": "qcow2", "data": {"compat": "1.1", "lazy-refcounts": false}}, "dirty-flag": false}, "iops_wr": 0, "ro": false, "backing_file_depth": 0, "drv": "qcow2", "iops": 0, "bps_wr": 0, "encrypted": false, "bps": 0, "bps_rd": 0, "file": "TEST_DIR/t.qcow2", "encryption_key_missing": false}, "tray_open": false, "type": "unknown"}, {"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}]}
+{"return": [{"device": "disk", "locked": false, "removable": true, "inserted": {"iops_rd": 0, "detect_zeroes": "off", "image": {"virtual-size": 134217728, "filename": "TEST_DIR/t.qcow2", "cluster-size": 65536, "format": "qcow2", "actual-size": SIZE, "format-specific": {"type": "qcow2", "data": {"compat": "1.1", "lazy-refcounts": false, "corrupt": false}}, "dirty-flag": false}, "iops_wr": 0, "ro": false, "backing_file_depth": 0, "drv": "qcow2", "iops": 0, "bps_wr": 0, "encrypted": false, "bps": 0, "bps_rd": 0, "file": "TEST_DIR/t.qcow2", "encryption_key_missing": false}, "tray_open": false, "type": "unknown"}, {"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}]}
{"return": {}}
{"return": {}}
{"return": {}}
@@ -44,7 +44,7 @@ Testing:
QMP_VERSION
{"return": {}}
{"return": "OK\r\n"}
-{"return": [{"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "disk", "locked": false, "removable": true, "inserted": {"iops_rd": 0, "detect_zeroes": "off", "image": {"virtual-size": 134217728, "filename": "TEST_DIR/t.qcow2", "cluster-size": 65536, "format": "qcow2", "actual-size": SIZE, "format-specific": {"type": "qcow2", "data": {"compat": "1.1", "lazy-refcounts": false}}, "dirty-flag": false}, "iops_wr": 0, "ro": false, "backing_file_depth": 0, "drv": "qcow2", "iops": 0, "bps_wr": 0, "encrypted": false, "bps": 0, "bps_rd": 0, "file": "TEST_DIR/t.qcow2", "encryption_key_missing": false}, "tray_open": false, "type": "unknown"}]}
+{"return": [{"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "disk", "locked": false, "removable": true, "inserted": {"iops_rd": 0, "detect_zeroes": "off", "image": {"virtual-size": 134217728, "filename": "TEST_DIR/t.qcow2", "cluster-size": 65536, "format": "qcow2", "actual-size": SIZE, "format-specific": {"type": "qcow2", "data": {"compat": "1.1", "lazy-refcounts": false, "corrupt": false}}, "dirty-flag": false}, "iops_wr": 0, "ro": false, "backing_file_depth": 0, "drv": "qcow2", "iops": 0, "bps_wr": 0, "encrypted": false, "bps": 0, "bps_rd": 0, "file": "TEST_DIR/t.qcow2", "encryption_key_missing": false}, "tray_open": false, "type": "unknown"}]}
{"return": {}}
{"return": {}}
{"return": {}}
@@ -64,14 +64,14 @@ Testing:
QMP_VERSION
{"return": {}}
{"return": {}}
-{"return": [{"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "disk", "locked": false, "removable": true, "inserted": {"iops_rd": 0, "detect_zeroes": "off", "image": {"virtual-size": 134217728, "filename": "TEST_DIR/t.qcow2", "cluster-size": 65536, "format": "qcow2", "actual-size": SIZE, "format-specific": {"type": "qcow2", "data": {"compat": "1.1", "lazy-refcounts": false}}, "dirty-flag": false}, "iops_wr": 0, "ro": false, "backing_file_depth": 0, "drv": "qcow2", "iops": 0, "bps_wr": 0, "encrypted": false, "bps": 0, "bps_rd": 0, "file": "TEST_DIR/t.qcow2", "encryption_key_missing": false}, "tray_open": false, "type": "unknown"}]}
+{"return": [{"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "disk", "locked": false, "removable": true, "inserted": {"iops_rd": 0, "detect_zeroes": "off", "image": {"virtual-size": 134217728, "filename": "TEST_DIR/t.qcow2", "cluster-size": 65536, "format": "qcow2", "actual-size": SIZE, "format-specific": {"type": "qcow2", "data": {"compat": "1.1", "lazy-refcounts": false, "corrupt": false}}, "dirty-flag": false}, "iops_wr": 0, "ro": false, "backing_file_depth": 0, "drv": "qcow2", "iops": 0, "bps_wr": 0, "encrypted": false, "bps": 0, "bps_rd": 0, "file": "TEST_DIR/t.qcow2", "encryption_key_missing": false}, "tray_open": false, "type": "unknown"}]}
{"return": {}}
{"return": {}}
{"return": {}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_DELETED", "data": {"path": "/machine/peripheral/virtio0/virtio-backend"}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_DELETED", "data": {"device": "virtio0", "path": "/machine/peripheral/virtio0"}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "RESET"}
-{"return": [{"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"io-status": "ok", "device": "disk", "locked": false, "removable": true, "inserted": {"iops_rd": 0, "detect_zeroes": "off", "image": {"virtual-size": 134217728, "filename": "TEST_DIR/t.qcow2", "cluster-size": 65536, "format": "qcow2", "actual-size": SIZE, "format-specific": {"type": "qcow2", "data": {"compat": "1.1", "lazy-refcounts": false}}, "dirty-flag": false}, "iops_wr": 0, "ro": false, "backing_file_depth": 0, "drv": "qcow2", "iops": 0, "bps_wr": 0, "encrypted": false, "bps": 0, "bps_rd": 0, "file": "TEST_DIR/t.qcow2", "encryption_key_missing": false}, "tray_open": false, "type": "unknown"}]}
+{"return": [{"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"io-status": "ok", "device": "disk", "locked": false, "removable": true, "inserted": {"iops_rd": 0, "detect_zeroes": "off", "image": {"virtual-size": 134217728, "filename": "TEST_DIR/t.qcow2", "cluster-size": 65536, "format": "qcow2", "actual-size": SIZE, "format-specific": {"type": "qcow2", "data": {"compat": "1.1", "lazy-refcounts": false, "corrupt": false}}, "dirty-flag": false}, "iops_wr": 0, "ro": false, "backing_file_depth": 0, "drv": "qcow2", "iops": 0, "bps_wr": 0, "encrypted": false, "bps": 0, "bps_rd": 0, "file": "TEST_DIR/t.qcow2", "encryption_key_missing": false}, "tray_open": false, "type": "unknown"}]}
{"return": {}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN"}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_TRAY_MOVED", "data": {"device": "ide1-cd0", "tray-open": true}}
diff --git a/tests/qemu-iotests/069 b/tests/qemu-iotests/069
index e661598c4..ce9e0541b 100755
--- a/tests/qemu-iotests/069
+++ b/tests/qemu-iotests/069
@@ -38,7 +38,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
. ./common.rc
. ./common.filter
-_supported_fmt cow qed qcow qcow2 vmdk
+_supported_fmt qed qcow qcow2 vmdk
_supported_proto file
_supported_os Linux
_unsupported_imgopts "subformat=monolithicFlat" "subformat=twoGbMaxExtentFlat"
diff --git a/tests/qemu-iotests/070 b/tests/qemu-iotests/070
index ea0dae7e9..d649ddf9b 100755
--- a/tests/qemu-iotests/070
+++ b/tests/qemu-iotests/070
@@ -77,7 +77,7 @@ _use_sample_img test-disk2vhd.vhdx.bz2
echo
echo "=== Verify image created by Disk2VHD can be opened ==="
-$QEMU_IMG info "$TEST_IMG" 2>&1 | _filter_testdir | _filter_qemu
+_img_info
# success, all done
echo "*** done"
diff --git a/tests/qemu-iotests/070.out b/tests/qemu-iotests/070.out
index 15f1fc147..ca743831c 100644
--- a/tests/qemu-iotests/070.out
+++ b/tests/qemu-iotests/070.out
@@ -20,9 +20,8 @@ read 18874368/18874368 bytes at offset 0
18 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
=== Verify image created by Disk2VHD can be opened ===
-image: TEST_DIR/test-disk2vhd.vhdx
-file format: vhdx
+image: TEST_DIR/test-disk2vhd.IMGFMT
+file format: IMGFMT
virtual size: 256M (268435456 bytes)
-disk size: 260M
cluster_size: 2097152
*** done
diff --git a/tests/qemu-iotests/072 b/tests/qemu-iotests/072
index 58faa8b5a..e4a723d73 100755
--- a/tests/qemu-iotests/072
+++ b/tests/qemu-iotests/072
@@ -38,7 +38,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
. ./common.rc
. ./common.filter
-_supported_fmt vpc vmdk vhdx vdi qed qcow2 qcow cow
+_supported_fmt vpc vmdk vhdx vdi qed qcow2 qcow
_supported_proto file
_supported_os Linux
diff --git a/tests/qemu-iotests/075 b/tests/qemu-iotests/075
index 40032c563..6117660c5 100755
--- a/tests/qemu-iotests/075
+++ b/tests/qemu-iotests/075
@@ -39,7 +39,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
. ./common.filter
_supported_fmt cloop
-_supported_proto generic
+_supported_proto file
_supported_os Linux
block_size_offset=128
diff --git a/tests/qemu-iotests/076 b/tests/qemu-iotests/076
index b614a7dd6..ed2be3581 100755
--- a/tests/qemu-iotests/076
+++ b/tests/qemu-iotests/076
@@ -39,7 +39,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
. ./common.filter
_supported_fmt parallels
-_supported_proto generic
+_supported_proto file
_supported_os Linux
tracks_offset=$((0x1c))
@@ -47,29 +47,34 @@ catalog_entries_offset=$((0x20))
nb_sectors_offset=$((0x24))
echo
-echo "== Read from a valid (enough) image =="
-_use_sample_img fake.parallels.bz2
+echo "== Read from a valid v1 image =="
+_use_sample_img parallels-v1.bz2
{ $QEMU_IO -c "read -P 0x11 0 64k" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir
echo
echo "== Negative catalog size =="
-_use_sample_img fake.parallels.bz2
+_use_sample_img parallels-v1.bz2
poke_file "$TEST_IMG" "$catalog_entries_offset" "\xff\xff\xff\xff"
{ $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir
echo
echo "== Overflow in catalog allocation =="
-_use_sample_img fake.parallels.bz2
+_use_sample_img parallels-v1.bz2
poke_file "$TEST_IMG" "$nb_sectors_offset" "\xff\xff\xff\xff"
poke_file "$TEST_IMG" "$catalog_entries_offset" "\x01\x00\x00\x40"
{ $QEMU_IO -c "read 64M 64M" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir
echo
echo "== Zero sectors per track =="
-_use_sample_img fake.parallels.bz2
+_use_sample_img parallels-v1.bz2
poke_file "$TEST_IMG" "$tracks_offset" "\x00\x00\x00\x00"
{ $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir
+echo
+echo "== Read from a valid v2 image =="
+_use_sample_img parallels-v2.bz2
+{ $QEMU_IO -c "read -P 0x11 0 64k" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir
+
# success, all done
echo "*** done"
rm -f $seq.full
diff --git a/tests/qemu-iotests/076.out b/tests/qemu-iotests/076.out
index f7745d8b0..32ade0856 100644
--- a/tests/qemu-iotests/076.out
+++ b/tests/qemu-iotests/076.out
@@ -1,18 +1,22 @@
QA output created by 076
-== Read from a valid (enough) image ==
+== Read from a valid v1 image ==
read 65536/65536 bytes at offset 0
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
== Negative catalog size ==
-qemu-io: can't open device TEST_DIR/fake.parallels: Catalog too large
+qemu-io: can't open device TEST_DIR/parallels-v1: Catalog too large
no file open, try 'help open'
== Overflow in catalog allocation ==
-qemu-io: can't open device TEST_DIR/fake.parallels: Catalog too large
+qemu-io: can't open device TEST_DIR/parallels-v1: Catalog too large
no file open, try 'help open'
== Zero sectors per track ==
-qemu-io: can't open device TEST_DIR/fake.parallels: Invalid image: Zero sectors per track
+qemu-io: can't open device TEST_DIR/parallels-v1: Invalid image: Zero sectors per track
no file open, try 'help open'
+
+== Read from a valid v2 image ==
+read 65536/65536 bytes at offset 0
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
*** done
diff --git a/tests/qemu-iotests/078 b/tests/qemu-iotests/078
index d4d6da7b0..7be2c3f69 100755
--- a/tests/qemu-iotests/078
+++ b/tests/qemu-iotests/078
@@ -39,7 +39,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
. ./common.filter
_supported_fmt bochs
-_supported_proto generic
+_supported_proto file
_supported_os Linux
catalog_size_offset=$((0x48))
diff --git a/tests/qemu-iotests/079 b/tests/qemu-iotests/079
index 2142bbb37..6613cfb18 100755
--- a/tests/qemu-iotests/079
+++ b/tests/qemu-iotests/079
@@ -39,7 +39,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
. ./common.filter
_supported_fmt qcow2
-_supported_proto file
+_supported_proto file nfs
_supported_os Linux
function test_qemu_img()
diff --git a/tests/qemu-iotests/080 b/tests/qemu-iotests/080
index 6b3a3e77a..9de337c40 100755
--- a/tests/qemu-iotests/080
+++ b/tests/qemu-iotests/080
@@ -40,7 +40,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
. ./common.filter
_supported_fmt qcow2
-_supported_proto generic
+_supported_proto file
_supported_os Linux
header_size=104
diff --git a/tests/qemu-iotests/081 b/tests/qemu-iotests/081
index 7ae4be205..ed3c29e13 100755
--- a/tests/qemu-iotests/081
+++ b/tests/qemu-iotests/081
@@ -41,7 +41,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
. ./common.filter
_supported_fmt raw
-_supported_proto generic
+_supported_proto file
_supported_os Linux
function do_run_qemu()
diff --git a/tests/qemu-iotests/082 b/tests/qemu-iotests/082
index f6eb75f62..e64de2773 100755
--- a/tests/qemu-iotests/082
+++ b/tests/qemu-iotests/082
@@ -39,7 +39,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
. ./common.filter
_supported_fmt qcow2
-_supported_proto file
+_supported_proto file nfs
_supported_os Linux
function run_qemu_img()
@@ -56,7 +56,7 @@ echo === create: Options specified more than once ===
# Last -f should win
run_qemu_img create -f foo -f $IMGFMT "$TEST_IMG" $size
-run_qemu_img info "$TEST_IMG"
+_img_info
# Multiple -o should be merged
run_qemu_img create -f $IMGFMT -o cluster_size=4k -o lazy_refcounts=on "$TEST_IMG" $size
@@ -66,7 +66,7 @@ run_qemu_img info "$TEST_IMG"
run_qemu_img create -f $IMGFMT -o cluster_size=4k -o lazy_refcounts=on -o cluster_size=8k "$TEST_IMG" $size
run_qemu_img info "$TEST_IMG"
run_qemu_img create -f $IMGFMT -o cluster_size=4k,cluster_size=8k "$TEST_IMG" $size
-run_qemu_img info "$TEST_IMG"
+_img_info
echo
echo === create: help for -o ===
@@ -106,11 +106,11 @@ run_qemu_img create -f $IMGFMT "$TEST_IMG" $size
# Last -f should win
run_qemu_img convert -f foo -f $IMGFMT "$TEST_IMG" "$TEST_IMG".base
-run_qemu_img info "$TEST_IMG".base
+TEST_IMG="${TEST_IMG}.base" _img_info
# Last -O should win
run_qemu_img convert -O foo -O $IMGFMT "$TEST_IMG" "$TEST_IMG".base
-run_qemu_img info "$TEST_IMG".base
+TEST_IMG="${TEST_IMG}.base" _img_info
# Multiple -o should be merged
run_qemu_img convert -O $IMGFMT -o cluster_size=4k -o lazy_refcounts=on "$TEST_IMG" "$TEST_IMG".base
@@ -120,7 +120,7 @@ run_qemu_img info "$TEST_IMG".base
run_qemu_img convert -O $IMGFMT -o cluster_size=4k -o lazy_refcounts=on -o cluster_size=8k "$TEST_IMG" "$TEST_IMG".base
run_qemu_img info "$TEST_IMG".base
run_qemu_img convert -O $IMGFMT -o cluster_size=4k,cluster_size=8k "$TEST_IMG" "$TEST_IMG".base
-run_qemu_img info "$TEST_IMG".base
+TEST_IMG="${TEST_IMG}.base" _img_info
echo
echo === convert: help for -o ===
@@ -167,7 +167,7 @@ run_qemu_img info "$TEST_IMG"
run_qemu_img amend -f $IMGFMT -o size=8M -o lazy_refcounts=on -o size=132M "$TEST_IMG"
run_qemu_img info "$TEST_IMG"
run_qemu_img amend -f $IMGFMT -o size=4M,size=148M "$TEST_IMG"
-run_qemu_img info "$TEST_IMG"
+_img_info
echo
echo === amend: help for -o ===
diff --git a/tests/qemu-iotests/082.out b/tests/qemu-iotests/082.out
index 413e7ef39..0a3ab5ac9 100644
--- a/tests/qemu-iotests/082.out
+++ b/tests/qemu-iotests/082.out
@@ -4,16 +4,10 @@ QA output created by 082
Testing: create -f foo -f qcow2 TEST_DIR/t.qcow2 128M
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 encryption=off cluster_size=65536 lazy_refcounts=off
-
-Testing: info TEST_DIR/t.qcow2
-image: TEST_DIR/t.qcow2
-file format: qcow2
+image: TEST_DIR/t.IMGFMT
+file format: IMGFMT
virtual size: 128M (134217728 bytes)
-disk size: 196K
cluster_size: 65536
-Format specific information:
- compat: 1.1
- lazy refcounts: false
Testing: create -f qcow2 -o cluster_size=4k -o lazy_refcounts=on TEST_DIR/t.qcow2 128M
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 encryption=off cluster_size=4096 lazy_refcounts=on
@@ -27,6 +21,7 @@ cluster_size: 4096
Format specific information:
compat: 1.1
lazy refcounts: true
+ corrupt: false
Testing: create -f qcow2 -o cluster_size=4k -o lazy_refcounts=on -o cluster_size=8k TEST_DIR/t.qcow2 128M
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 encryption=off cluster_size=8192 lazy_refcounts=on
@@ -40,19 +35,14 @@ cluster_size: 8192
Format specific information:
compat: 1.1
lazy refcounts: true
+ corrupt: false
Testing: create -f qcow2 -o cluster_size=4k,cluster_size=8k TEST_DIR/t.qcow2 128M
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 encryption=off cluster_size=8192 lazy_refcounts=off
-
-Testing: info TEST_DIR/t.qcow2
-image: TEST_DIR/t.qcow2
-file format: qcow2
+image: TEST_DIR/t.IMGFMT
+file format: IMGFMT
virtual size: 128M (134217728 bytes)
-disk size: 28K
cluster_size: 8192
-Format specific information:
- compat: 1.1
- lazy refcounts: false
=== create: help for -o ===
@@ -64,7 +54,7 @@ backing_file File name of a base image
backing_fmt Image format of the base image
encryption Encrypt the image
cluster_size qcow2 cluster size
-preallocation Preallocation mode (allowed values: off, metadata)
+preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
lazy_refcounts Postpone refcount updates
nocow Turn off copy-on-write (valid only on btrfs)
@@ -76,7 +66,7 @@ backing_file File name of a base image
backing_fmt Image format of the base image
encryption Encrypt the image
cluster_size qcow2 cluster size
-preallocation Preallocation mode (allowed values: off, metadata)
+preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
lazy_refcounts Postpone refcount updates
nocow Turn off copy-on-write (valid only on btrfs)
@@ -88,7 +78,7 @@ backing_file File name of a base image
backing_fmt Image format of the base image
encryption Encrypt the image
cluster_size qcow2 cluster size
-preallocation Preallocation mode (allowed values: off, metadata)
+preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
lazy_refcounts Postpone refcount updates
nocow Turn off copy-on-write (valid only on btrfs)
@@ -100,7 +90,7 @@ backing_file File name of a base image
backing_fmt Image format of the base image
encryption Encrypt the image
cluster_size qcow2 cluster size
-preallocation Preallocation mode (allowed values: off, metadata)
+preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
lazy_refcounts Postpone refcount updates
nocow Turn off copy-on-write (valid only on btrfs)
@@ -112,7 +102,7 @@ backing_file File name of a base image
backing_fmt Image format of the base image
encryption Encrypt the image
cluster_size qcow2 cluster size
-preallocation Preallocation mode (allowed values: off, metadata)
+preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
lazy_refcounts Postpone refcount updates
nocow Turn off copy-on-write (valid only on btrfs)
@@ -124,7 +114,7 @@ backing_file File name of a base image
backing_fmt Image format of the base image
encryption Encrypt the image
cluster_size qcow2 cluster size
-preallocation Preallocation mode (allowed values: off, metadata)
+preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
lazy_refcounts Postpone refcount updates
nocow Turn off copy-on-write (valid only on btrfs)
@@ -136,7 +126,7 @@ backing_file File name of a base image
backing_fmt Image format of the base image
encryption Encrypt the image
cluster_size qcow2 cluster size
-preallocation Preallocation mode (allowed values: off, metadata)
+preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
lazy_refcounts Postpone refcount updates
nocow Turn off copy-on-write (valid only on btrfs)
@@ -148,7 +138,7 @@ backing_file File name of a base image
backing_fmt Image format of the base image
encryption Encrypt the image
cluster_size qcow2 cluster size
-preallocation Preallocation mode (allowed values: off, metadata)
+preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
lazy_refcounts Postpone refcount updates
nocow Turn off copy-on-write (valid only on btrfs)
@@ -175,7 +165,7 @@ backing_file File name of a base image
backing_fmt Image format of the base image
encryption Encrypt the image
cluster_size qcow2 cluster size
-preallocation Preallocation mode (allowed values: off, metadata)
+preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
lazy_refcounts Postpone refcount updates
Testing: create -o help
@@ -188,24 +178,15 @@ Testing: create -f qcow2 TEST_DIR/t.qcow2 128M
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 encryption=off cluster_size=65536 lazy_refcounts=off
Testing: convert -f foo -f qcow2 TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base
-
-Testing: info TEST_DIR/t.qcow2.base
-image: TEST_DIR/t.qcow2.base
+image: TEST_DIR/t.IMGFMT.base
file format: raw
virtual size: 128M (134217728 bytes)
-disk size: 0
Testing: convert -O foo -O qcow2 TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base
-
-Testing: info TEST_DIR/t.qcow2.base
-image: TEST_DIR/t.qcow2.base
-file format: qcow2
+image: TEST_DIR/t.IMGFMT.base
+file format: IMGFMT
virtual size: 128M (134217728 bytes)
-disk size: 196K
cluster_size: 65536
-Format specific information:
- compat: 1.1
- lazy refcounts: false
Testing: convert -O qcow2 -o cluster_size=4k -o lazy_refcounts=on TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base
@@ -218,6 +199,7 @@ cluster_size: 4096
Format specific information:
compat: 1.1
lazy refcounts: true
+ corrupt: false
Testing: convert -O qcow2 -o cluster_size=4k -o lazy_refcounts=on -o cluster_size=8k TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base
@@ -230,18 +212,13 @@ cluster_size: 8192
Format specific information:
compat: 1.1
lazy refcounts: true
+ corrupt: false
Testing: convert -O qcow2 -o cluster_size=4k,cluster_size=8k TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base
-
-Testing: info TEST_DIR/t.qcow2.base
-image: TEST_DIR/t.qcow2.base
-file format: qcow2
+image: TEST_DIR/t.IMGFMT.base
+file format: IMGFMT
virtual size: 128M (134217728 bytes)
-disk size: 28K
cluster_size: 8192
-Format specific information:
- compat: 1.1
- lazy refcounts: false
=== convert: help for -o ===
@@ -253,7 +230,7 @@ backing_file File name of a base image
backing_fmt Image format of the base image
encryption Encrypt the image
cluster_size qcow2 cluster size
-preallocation Preallocation mode (allowed values: off, metadata)
+preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
lazy_refcounts Postpone refcount updates
nocow Turn off copy-on-write (valid only on btrfs)
@@ -265,7 +242,7 @@ backing_file File name of a base image
backing_fmt Image format of the base image
encryption Encrypt the image
cluster_size qcow2 cluster size
-preallocation Preallocation mode (allowed values: off, metadata)
+preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
lazy_refcounts Postpone refcount updates
nocow Turn off copy-on-write (valid only on btrfs)
@@ -277,7 +254,7 @@ backing_file File name of a base image
backing_fmt Image format of the base image
encryption Encrypt the image
cluster_size qcow2 cluster size
-preallocation Preallocation mode (allowed values: off, metadata)
+preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
lazy_refcounts Postpone refcount updates
nocow Turn off copy-on-write (valid only on btrfs)
@@ -289,7 +266,7 @@ backing_file File name of a base image
backing_fmt Image format of the base image
encryption Encrypt the image
cluster_size qcow2 cluster size
-preallocation Preallocation mode (allowed values: off, metadata)
+preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
lazy_refcounts Postpone refcount updates
nocow Turn off copy-on-write (valid only on btrfs)
@@ -301,7 +278,7 @@ backing_file File name of a base image
backing_fmt Image format of the base image
encryption Encrypt the image
cluster_size qcow2 cluster size
-preallocation Preallocation mode (allowed values: off, metadata)
+preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
lazy_refcounts Postpone refcount updates
nocow Turn off copy-on-write (valid only on btrfs)
@@ -313,7 +290,7 @@ backing_file File name of a base image
backing_fmt Image format of the base image
encryption Encrypt the image
cluster_size qcow2 cluster size
-preallocation Preallocation mode (allowed values: off, metadata)
+preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
lazy_refcounts Postpone refcount updates
nocow Turn off copy-on-write (valid only on btrfs)
@@ -325,7 +302,7 @@ backing_file File name of a base image
backing_fmt Image format of the base image
encryption Encrypt the image
cluster_size qcow2 cluster size
-preallocation Preallocation mode (allowed values: off, metadata)
+preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
lazy_refcounts Postpone refcount updates
nocow Turn off copy-on-write (valid only on btrfs)
@@ -337,7 +314,7 @@ backing_file File name of a base image
backing_fmt Image format of the base image
encryption Encrypt the image
cluster_size qcow2 cluster size
-preallocation Preallocation mode (allowed values: off, metadata)
+preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
lazy_refcounts Postpone refcount updates
nocow Turn off copy-on-write (valid only on btrfs)
@@ -364,7 +341,7 @@ backing_file File name of a base image
backing_fmt Image format of the base image
encryption Encrypt the image
cluster_size qcow2 cluster size
-preallocation Preallocation mode (allowed values: off, metadata)
+preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
lazy_refcounts Postpone refcount updates
Testing: convert -o help
@@ -384,6 +361,7 @@ cluster_size: 65536
Format specific information:
compat: 1.1
lazy refcounts: true
+ corrupt: false
Testing: amend -f qcow2 -o size=130M -o lazy_refcounts=off TEST_DIR/t.qcow2
@@ -396,6 +374,7 @@ cluster_size: 65536
Format specific information:
compat: 1.1
lazy refcounts: false
+ corrupt: false
Testing: amend -f qcow2 -o size=8M -o lazy_refcounts=on -o size=132M TEST_DIR/t.qcow2
@@ -408,18 +387,13 @@ cluster_size: 65536
Format specific information:
compat: 1.1
lazy refcounts: true
+ corrupt: false
Testing: amend -f qcow2 -o size=4M,size=148M TEST_DIR/t.qcow2
-
-Testing: info TEST_DIR/t.qcow2
-image: TEST_DIR/t.qcow2
-file format: qcow2
+image: TEST_DIR/t.IMGFMT
+file format: IMGFMT
virtual size: 148M (155189248 bytes)
-disk size: 196K
cluster_size: 65536
-Format specific information:
- compat: 1.1
- lazy refcounts: true
=== amend: help for -o ===
@@ -431,7 +405,7 @@ backing_file File name of a base image
backing_fmt Image format of the base image
encryption Encrypt the image
cluster_size qcow2 cluster size
-preallocation Preallocation mode (allowed values: off, metadata)
+preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
lazy_refcounts Postpone refcount updates
nocow Turn off copy-on-write (valid only on btrfs)
@@ -443,7 +417,7 @@ backing_file File name of a base image
backing_fmt Image format of the base image
encryption Encrypt the image
cluster_size qcow2 cluster size
-preallocation Preallocation mode (allowed values: off, metadata)
+preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
lazy_refcounts Postpone refcount updates
nocow Turn off copy-on-write (valid only on btrfs)
@@ -455,7 +429,7 @@ backing_file File name of a base image
backing_fmt Image format of the base image
encryption Encrypt the image
cluster_size qcow2 cluster size
-preallocation Preallocation mode (allowed values: off, metadata)
+preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
lazy_refcounts Postpone refcount updates
nocow Turn off copy-on-write (valid only on btrfs)
@@ -467,7 +441,7 @@ backing_file File name of a base image
backing_fmt Image format of the base image
encryption Encrypt the image
cluster_size qcow2 cluster size
-preallocation Preallocation mode (allowed values: off, metadata)
+preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
lazy_refcounts Postpone refcount updates
nocow Turn off copy-on-write (valid only on btrfs)
@@ -479,7 +453,7 @@ backing_file File name of a base image
backing_fmt Image format of the base image
encryption Encrypt the image
cluster_size qcow2 cluster size
-preallocation Preallocation mode (allowed values: off, metadata)
+preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
lazy_refcounts Postpone refcount updates
nocow Turn off copy-on-write (valid only on btrfs)
@@ -491,7 +465,7 @@ backing_file File name of a base image
backing_fmt Image format of the base image
encryption Encrypt the image
cluster_size qcow2 cluster size
-preallocation Preallocation mode (allowed values: off, metadata)
+preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
lazy_refcounts Postpone refcount updates
nocow Turn off copy-on-write (valid only on btrfs)
@@ -503,7 +477,7 @@ backing_file File name of a base image
backing_fmt Image format of the base image
encryption Encrypt the image
cluster_size qcow2 cluster size
-preallocation Preallocation mode (allowed values: off, metadata)
+preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
lazy_refcounts Postpone refcount updates
nocow Turn off copy-on-write (valid only on btrfs)
@@ -515,7 +489,7 @@ backing_file File name of a base image
backing_fmt Image format of the base image
encryption Encrypt the image
cluster_size qcow2 cluster size
-preallocation Preallocation mode (allowed values: off, metadata)
+preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
lazy_refcounts Postpone refcount updates
nocow Turn off copy-on-write (valid only on btrfs)
@@ -544,7 +518,7 @@ backing_file File name of a base image
backing_fmt Image format of the base image
encryption Encrypt the image
cluster_size qcow2 cluster size
-preallocation Preallocation mode (allowed values: off, metadata)
+preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
lazy_refcounts Postpone refcount updates
Testing: convert -o help
diff --git a/tests/qemu-iotests/084 b/tests/qemu-iotests/084
index cb4d7b729..733018d4a 100755
--- a/tests/qemu-iotests/084
+++ b/tests/qemu-iotests/084
@@ -1,6 +1,7 @@
#!/bin/bash
#
-# Test case for VDI header corruption; image too large, and too many blocks
+# Test case for VDI header corruption; image too large, and too many blocks.
+# Also simple test for creating dynamic and static VDI images.
#
# Copyright (C) 2013 Red Hat, Inc.
#
@@ -40,29 +41,40 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
# This tests vdi-specific header fields
_supported_fmt vdi
-_supported_proto generic
+_supported_proto file
_supported_os Linux
+size=64M
ds_offset=368 # disk image size field offset
bs_offset=376 # block size field offset
bii_offset=384 # block in image field offset
echo
+echo "=== Statically allocated image creation ==="
+echo
+_make_test_img $size -o static
+_img_info
+stat -c"disk image file size in bytes: %s" "${TEST_IMG}"
+_cleanup_test_img
+
+echo
echo "=== Testing image size bounds ==="
echo
-_make_test_img 64M
+_make_test_img $size
+_img_info
+stat -c"disk image file size in bytes: %s" "${TEST_IMG}"
# check for image size too large
# poke max image size, and appropriate blocks_in_image value
-echo "Test 1: Maximum size (1024 TB):"
-poke_file "$TEST_IMG" "$ds_offset" "\x00\x00\xf0\xff\xff\xff\x03\x00"
-poke_file "$TEST_IMG" "$bii_offset" "\xff\xff\xff\x3f"
+echo "Test 1: Maximum size (512 TB - 128 MB):"
+poke_file "$TEST_IMG" "$ds_offset" "\x00\x00\x00\xf8\xff\xff\x01\x00"
+poke_file "$TEST_IMG" "$bii_offset" "\x80\xff\xff\x1f"
_img_info
echo
-echo "Test 2: Size too large (1024TB + 1)"
+echo "Test 2: Size too large (512 TB - 128 MB + 64 kB)"
# This should be too large (-EINVAL):
-poke_file "$TEST_IMG" "$ds_offset" "\x00\x00\xf1\xff\xff\xff\x03\x00"
+poke_file "$TEST_IMG" "$ds_offset" "\x00\x00\x01\xf8\xff\xff\x01\x00"
_img_info
echo
@@ -77,9 +89,9 @@ _img_info
echo
echo "Test 4: Size valid (64M), but Blocks In Image exceeds max allowed"
-# Now check the bounds of blocks_in_image - 0x3fffffff should be the max
+# Now check the bounds of blocks_in_image - 0x1fffff80 should be the max
# value here, and we should get -ENOTSUP
-poke_file "$TEST_IMG" "$bii_offset" "\x00\x00\x00\x40"
+poke_file "$TEST_IMG" "$bii_offset" "\x81\xff\xff\x1f"
_img_info
# Finally, 1MB is the only block size supported. Verify that
diff --git a/tests/qemu-iotests/084.out b/tests/qemu-iotests/084.out
index c7120d9b0..5ece8299c 100644
--- a/tests/qemu-iotests/084.out
+++ b/tests/qemu-iotests/084.out
@@ -1,19 +1,36 @@
QA output created by 084
+=== Statically allocated image creation ===
+
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
+image: TEST_DIR/t.IMGFMT
+file format: IMGFMT
+virtual size: 64M (67108864 bytes)
+cluster_size: 1048576
+disk image file size in bytes: 67109888
+
=== Testing image size bounds ===
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
-Test 1: Maximum size (1024 TB):
-qemu-img: Could not open 'TEST_DIR/t.IMGFMT': Could not open 'TEST_DIR/t.IMGFMT': Invalid argument
+image: TEST_DIR/t.IMGFMT
+file format: IMGFMT
+virtual size: 64M (67108864 bytes)
+cluster_size: 1048576
+disk image file size in bytes: 1024
+Test 1: Maximum size (512 TB - 128 MB):
+image: TEST_DIR/t.IMGFMT
+file format: IMGFMT
+virtual size: 512T (562949819203584 bytes)
+cluster_size: 1048576
-Test 2: Size too large (1024TB + 1)
-qemu-img: Could not open 'TEST_DIR/t.IMGFMT': Unsupported VDI image size (size is 0x3fffffff10000, max supported is 0x3fffffff00000)
+Test 2: Size too large (512 TB - 128 MB + 64 kB)
+qemu-img: Could not open 'TEST_DIR/t.IMGFMT': Unsupported VDI image size (size is 0x1fffff8010000, max supported is 0x1fffff8000000)
Test 3: Size valid (64M), but Blocks In Image too small (63)
qemu-img: Could not open 'TEST_DIR/t.IMGFMT': unsupported VDI image (disk size 67108864, image bitmap has room for 66060288)
Test 4: Size valid (64M), but Blocks In Image exceeds max allowed
-qemu-img: Could not open 'TEST_DIR/t.IMGFMT': unsupported VDI image (too many blocks 1073741824, max is 1073741823)
+qemu-img: Could not open 'TEST_DIR/t.IMGFMT': unsupported VDI image (too many blocks 536870785, max is 536870784)
Test 5: Valid Image: 64MB, Blocks In Image 64, Block Size 1MB
image: TEST_DIR/t.IMGFMT
diff --git a/tests/qemu-iotests/086 b/tests/qemu-iotests/086
index d9a80cf86..234eb9a91 100755
--- a/tests/qemu-iotests/086
+++ b/tests/qemu-iotests/086
@@ -39,7 +39,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
. ./common.filter
_supported_fmt qcow2
-_supported_proto file
+_supported_proto file nfs
_supported_os Linux
function run_qemu_img()
diff --git a/tests/qemu-iotests/087 b/tests/qemu-iotests/087
index 82c56b139..d7454d13d 100755
--- a/tests/qemu-iotests/087
+++ b/tests/qemu-iotests/087
@@ -218,6 +218,23 @@ run_qemu <<EOF
{ "execute": "quit" }
EOF
+echo
+echo === Missing driver ===
+echo
+
+_make_test_img -o encryption=on $size
+run_qemu -S <<EOF
+{ "execute": "qmp_capabilities" }
+{ "execute": "blockdev-add",
+ "arguments": {
+ "options": {
+ "id": "disk"
+ }
+ }
+ }
+{ "execute": "quit" }
+EOF
+
# success, all done
echo "*** done"
rm -f $seq.full
diff --git a/tests/qemu-iotests/087.out b/tests/qemu-iotests/087.out
index 7fbee3ff5..e8795b3a1 100644
--- a/tests/qemu-iotests/087.out
+++ b/tests/qemu-iotests/087.out
@@ -20,7 +20,7 @@ QMP_VERSION
{"return": {}}
{"return": {}}
{"error": {"class": "GenericError", "desc": "Device with id 'disk' already exists"}}
-{"error": {"class": "GenericError", "desc": "Device with node-name 'test-node' already exists"}}
+{"error": {"class": "GenericError", "desc": "Device name 'test-node' conflicts with an existing node name"}}
main-loop: WARNING: I/O thread spun for 1000 iterations
{"error": {"class": "GenericError", "desc": "could not open disk image disk2: node-name=disk is conflicting with a device id"}}
{"error": {"class": "GenericError", "desc": "could not open disk image disk2: Duplicate node name"}}
@@ -64,4 +64,17 @@ QMP_VERSION
{"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}}
+
+=== Missing driver ===
+
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 encryption=on
+Testing: -S
+QMP_VERSION
+{"return": {}}
+{"error": {"class": "GenericError", "desc": "Invalid parameter type for 'driver', expected: string"}}
+{"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/088 b/tests/qemu-iotests/088
index c09adf802..f9c312918 100755
--- a/tests/qemu-iotests/088
+++ b/tests/qemu-iotests/088
@@ -40,7 +40,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
. ./common.filter
_supported_fmt vpc
-_supported_proto generic
+_supported_proto file
_supported_os Linux
offset_block_size=$((512 + 32))
diff --git a/tests/qemu-iotests/089.out b/tests/qemu-iotests/089.out
index 4ca2f88e6..b2b039081 100644
--- a/tests/qemu-iotests/089.out
+++ b/tests/qemu-iotests/089.out
@@ -41,10 +41,12 @@ vm state offset: 512 MiB
Format specific information:
compat: 1.1
lazy refcounts: false
+ corrupt: false
format name: IMGFMT
cluster size: 64 KiB
vm state offset: 512 MiB
Format specific information:
compat: 1.1
lazy refcounts: false
+ corrupt: false
*** done
diff --git a/tests/qemu-iotests/090 b/tests/qemu-iotests/090
index 8d032f811..70b5a6fd7 100755
--- a/tests/qemu-iotests/090
+++ b/tests/qemu-iotests/090
@@ -39,7 +39,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
. ./common.filter
_supported_fmt qcow2
-_supported_proto file
+_supported_proto file nfs
_supported_os Linux
IMG_SIZE=128K
diff --git a/tests/qemu-iotests/092 b/tests/qemu-iotests/092
index a8c0c9ca2..52c529bae 100755
--- a/tests/qemu-iotests/092
+++ b/tests/qemu-iotests/092
@@ -40,7 +40,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
. ./common.filter
_supported_fmt qcow
-_supported_proto generic
+_supported_proto file
_supported_os Linux
offset_backing_file_offset=8
diff --git a/tests/qemu-iotests/095 b/tests/qemu-iotests/095
index acc7dbf18..6630181a7 100755
--- a/tests/qemu-iotests/095
+++ b/tests/qemu-iotests/095
@@ -60,7 +60,7 @@ _make_test_img -b "${TEST_IMG}.snp1" $size_larger
echo
echo "=== Base image info before commit and resize ==="
-$QEMU_IMG info "${TEST_IMG}.base" | _filter_testdir
+TEST_IMG="${TEST_IMG}.base" _img_info
echo
echo === Running QEMU Live Commit Test ===
@@ -78,7 +78,7 @@ _send_qemu_cmd $h "{ 'execute': 'block-commit',
echo
echo "=== Base image info after commit and resize ==="
-$QEMU_IMG info "${TEST_IMG}.base" | _filter_testdir
+TEST_IMG="${TEST_IMG}.base" _img_info
# success, all done
echo "*** done"
diff --git a/tests/qemu-iotests/095.out b/tests/qemu-iotests/095.out
index 5864ddac2..cc86efacc 100644
--- a/tests/qemu-iotests/095.out
+++ b/tests/qemu-iotests/095.out
@@ -4,14 +4,10 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=104857600 backing_file='TEST_DIR
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=104857600 backing_file='TEST_DIR/t.IMGFMT.snp1'
=== Base image info before commit and resize ===
-image: TEST_DIR/t.qcow2.base
-file format: qcow2
+image: TEST_DIR/t.IMGFMT.base
+file format: IMGFMT
virtual size: 5.0M (5242880 bytes)
-disk size: 196K
cluster_size: 65536
-Format specific information:
- compat: 1.1
- lazy refcounts: false
=== Running QEMU Live Commit Test ===
@@ -20,12 +16,8 @@ Format specific information:
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "test", "len": 104857600, "offset": 104857600, "speed": 0, "type": "commit"}}
=== Base image info after commit and resize ===
-image: TEST_DIR/t.qcow2.base
-file format: qcow2
+image: TEST_DIR/t.IMGFMT.base
+file format: IMGFMT
virtual size: 100M (104857600 bytes)
-disk size: 196K
cluster_size: 65536
-Format specific information:
- compat: 1.1
- lazy refcounts: false
*** done
diff --git a/tests/qemu-iotests/097 b/tests/qemu-iotests/097
new file mode 100755
index 000000000..c7a613b7e
--- /dev/null
+++ b/tests/qemu-iotests/097
@@ -0,0 +1,122 @@
+#!/bin/bash
+#
+# Commit changes into backing chains and empty the top image if the
+# backing image is not explicitly specified
+#
+# 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=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
+ _rm_test_img "$TEST_IMG.itmd"
+}
+trap "_cleanup; exit \$status" 0 1 2 3 15
+
+# get standard environment, filters and checks
+. ./common.rc
+. ./common.filter
+. ./common.pattern
+
+# Any format supporting backing files and bdrv_make_empty
+_supported_fmt qcow qcow2
+_supported_proto file
+_supported_os Linux
+
+
+# Four passes:
+# 0: Two-layer backing chain, commit to upper backing file (implicitly)
+# (in this case, the top image will be emptied)
+# 1: Two-layer backing chain, commit to upper backing file (explicitly)
+# (in this case, the top image will implicitly stay unchanged)
+# 2: Two-layer backing chain, commit to upper backing file (implicitly with -d)
+# (in this case, the top image will explicitly stay unchanged)
+# 3: Two-layer backing chain, commit to lower backing file
+# (in this case, the top image will implicitly stay unchanged)
+#
+# 020 already tests committing, so this only tests whether image chains are
+# working properly and that all images above the base are emptied; therefore,
+# no complicated patterns are necessary
+for i in 0 1 2 3; do
+
+echo
+echo "=== Test pass $i ==="
+echo
+
+TEST_IMG="$TEST_IMG.base" _make_test_img 64M
+TEST_IMG="$TEST_IMG.itmd" _make_test_img -b "$TEST_IMG.base" 64M
+_make_test_img -b "$TEST_IMG.itmd" 64M
+
+$QEMU_IO -c 'write -P 1 0 192k' "$TEST_IMG.base" | _filter_qemu_io
+$QEMU_IO -c 'write -P 2 64k 128k' "$TEST_IMG.itmd" | _filter_qemu_io
+$QEMU_IO -c 'write -P 3 128k 64k' "$TEST_IMG" | _filter_qemu_io
+
+if [ $i -lt 3 ]; then
+ if [ $i == 0 ]; then
+ # -b "$TEST_IMG.itmd" should be the default (that is, committing to the
+ # first backing file in the chain)
+ $QEMU_IMG commit "$TEST_IMG"
+ elif [ $i == 1 ]; then
+ # explicitly specify the commit target (this should imply -d)
+ $QEMU_IMG commit -b "$TEST_IMG.itmd" "$TEST_IMG"
+ else
+ # do not explicitly specify the commit target, but use -d to leave the
+ # top image unchanged
+ $QEMU_IMG commit -d "$TEST_IMG"
+ fi
+
+ # Bottom should be unchanged
+ $QEMU_IO -c 'read -P 1 0 192k' "$TEST_IMG.base" | _filter_qemu_io
+
+ # Intermediate should contain changes from top
+ $QEMU_IO -c 'read -P 1 0 64k' "$TEST_IMG.itmd" | _filter_qemu_io
+ $QEMU_IO -c 'read -P 2 64k 64k' "$TEST_IMG.itmd" | _filter_qemu_io
+ $QEMU_IO -c 'read -P 3 128k 64k' "$TEST_IMG.itmd" | _filter_qemu_io
+
+ # And in pass 0, the top image should be empty, whereas in both other passes
+ # it should be unchanged (which is both checked by qemu-img map)
+else
+ $QEMU_IMG commit -b "$TEST_IMG.base" "$TEST_IMG"
+
+ # Bottom should contain all changes
+ $QEMU_IO -c 'read -P 1 0 64k' "$TEST_IMG.base" | _filter_qemu_io
+ $QEMU_IO -c 'read -P 2 64k 64k' "$TEST_IMG.base" | _filter_qemu_io
+ $QEMU_IO -c 'read -P 3 128k 64k' "$TEST_IMG.base" | _filter_qemu_io
+
+ # Both top and intermediate should be unchanged
+fi
+
+$QEMU_IMG map "$TEST_IMG.base" | _filter_qemu_img_map
+$QEMU_IMG map "$TEST_IMG.itmd" | _filter_qemu_img_map
+$QEMU_IMG map "$TEST_IMG" | _filter_qemu_img_map
+
+done
+
+
+# success, all done
+echo "*** done"
+rm -f $seq.full
+status=0
diff --git a/tests/qemu-iotests/097.out b/tests/qemu-iotests/097.out
new file mode 100644
index 000000000..1cb7764fe
--- /dev/null
+++ b/tests/qemu-iotests/097.out
@@ -0,0 +1,119 @@
+QA output created by 097
+
+=== Test pass 0 ===
+
+Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864
+Formatting 'TEST_DIR/t.IMGFMT.itmd', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.base'
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.itmd'
+wrote 196608/196608 bytes at offset 0
+192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 131072/131072 bytes at offset 65536
+128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 65536/65536 bytes at offset 131072
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Image committed.
+read 196608/196608 bytes at offset 0
+192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 65536/65536 bytes at offset 0
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 65536/65536 bytes at offset 65536
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 65536/65536 bytes at offset 131072
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Offset Length File
+0 0x30000 TEST_DIR/t.IMGFMT.base
+Offset Length File
+0 0x10000 TEST_DIR/t.IMGFMT.base
+0x10000 0x20000 TEST_DIR/t.IMGFMT.itmd
+Offset Length File
+0 0x10000 TEST_DIR/t.IMGFMT.base
+0x10000 0x20000 TEST_DIR/t.IMGFMT.itmd
+
+=== Test pass 1 ===
+
+Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864
+Formatting 'TEST_DIR/t.IMGFMT.itmd', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.base'
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.itmd'
+wrote 196608/196608 bytes at offset 0
+192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 131072/131072 bytes at offset 65536
+128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 65536/65536 bytes at offset 131072
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Image committed.
+read 196608/196608 bytes at offset 0
+192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 65536/65536 bytes at offset 0
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 65536/65536 bytes at offset 65536
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 65536/65536 bytes at offset 131072
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Offset Length File
+0 0x30000 TEST_DIR/t.IMGFMT.base
+Offset Length File
+0 0x10000 TEST_DIR/t.IMGFMT.base
+0x10000 0x20000 TEST_DIR/t.IMGFMT.itmd
+Offset Length File
+0 0x10000 TEST_DIR/t.IMGFMT.base
+0x10000 0x10000 TEST_DIR/t.IMGFMT.itmd
+0x20000 0x10000 TEST_DIR/t.IMGFMT
+
+=== Test pass 2 ===
+
+Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864
+Formatting 'TEST_DIR/t.IMGFMT.itmd', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.base'
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.itmd'
+wrote 196608/196608 bytes at offset 0
+192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 131072/131072 bytes at offset 65536
+128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 65536/65536 bytes at offset 131072
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Image committed.
+read 196608/196608 bytes at offset 0
+192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 65536/65536 bytes at offset 0
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 65536/65536 bytes at offset 65536
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 65536/65536 bytes at offset 131072
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Offset Length File
+0 0x30000 TEST_DIR/t.IMGFMT.base
+Offset Length File
+0 0x10000 TEST_DIR/t.IMGFMT.base
+0x10000 0x20000 TEST_DIR/t.IMGFMT.itmd
+Offset Length File
+0 0x10000 TEST_DIR/t.IMGFMT.base
+0x10000 0x10000 TEST_DIR/t.IMGFMT.itmd
+0x20000 0x10000 TEST_DIR/t.IMGFMT
+
+=== Test pass 3 ===
+
+Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864
+Formatting 'TEST_DIR/t.IMGFMT.itmd', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.base'
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.itmd'
+wrote 196608/196608 bytes at offset 0
+192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 131072/131072 bytes at offset 65536
+128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 65536/65536 bytes at offset 131072
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Image committed.
+read 65536/65536 bytes at offset 0
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 65536/65536 bytes at offset 65536
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 65536/65536 bytes at offset 131072
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Offset Length File
+0 0x30000 TEST_DIR/t.IMGFMT.base
+Offset Length File
+0 0x10000 TEST_DIR/t.IMGFMT.base
+0x10000 0x20000 TEST_DIR/t.IMGFMT.itmd
+Offset Length File
+0 0x10000 TEST_DIR/t.IMGFMT.base
+0x10000 0x10000 TEST_DIR/t.IMGFMT.itmd
+0x20000 0x10000 TEST_DIR/t.IMGFMT
+*** done
diff --git a/tests/qemu-iotests/098 b/tests/qemu-iotests/098
new file mode 100755
index 000000000..e2230ad60
--- /dev/null
+++ b/tests/qemu-iotests/098
@@ -0,0 +1,82 @@
+#!/bin/bash
+#
+# Test qcow2's bdrv_make_empty for images without internal snapshots
+#
+# 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=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
+ rm -f "$TEST_DIR/blkdebug.conf"
+}
+trap "_cleanup; exit \$status" 0 1 2 3 15
+
+# get standard environment, filters and checks
+. ./common.rc
+. ./common.filter
+. ./common.pattern
+
+_supported_fmt qcow2
+_supported_proto file
+_supported_os Linux
+
+IMGOPTS="compat=1.1"
+
+for event in l1_update empty_image_prepare reftable_update refblock_alloc; do
+
+echo
+echo "=== $event ==="
+echo
+
+TEST_IMG="$TEST_IMG.base" _make_test_img 64M
+_make_test_img -b "$TEST_IMG.base" 64M
+
+# Some data that can be leaked when emptying the top image
+$QEMU_IO -c 'write 0 64k' "$TEST_IMG" | _filter_qemu_io
+
+cat > "$TEST_DIR/blkdebug.conf" <<EOF
+[inject-error]
+event = "$event"
+EOF
+
+$QEMU_IMG commit "blkdebug:$TEST_DIR/blkdebug.conf:$TEST_IMG" 2>&1 \
+ | _filter_testdir | _filter_imgfmt
+
+# There may be errors, but they should be fixed by opening the image
+$QEMU_IO -c close "$TEST_IMG"
+
+_check_test_img
+
+done
+
+
+rm -f "$TEST_DIR/blkdebug.conf"
+
+# success, all done
+echo "*** done"
+rm -f $seq.full
+status=0
diff --git a/tests/qemu-iotests/098.out b/tests/qemu-iotests/098.out
new file mode 100644
index 000000000..586dfc809
--- /dev/null
+++ b/tests/qemu-iotests/098.out
@@ -0,0 +1,52 @@
+QA output created by 098
+
+=== l1_update ===
+
+Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.base'
+wrote 65536/65536 bytes at offset 0
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+qemu-img: Could not empty blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.IMGFMT: Input/output error
+No errors were found on the image.
+
+=== empty_image_prepare ===
+
+Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.base'
+wrote 65536/65536 bytes at offset 0
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+qemu-img: Could not empty blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.IMGFMT: Input/output error
+Leaked cluster 4 refcount=1 reference=0
+Leaked cluster 5 refcount=1 reference=0
+Repairing cluster 4 refcount=1 reference=0
+Repairing cluster 5 refcount=1 reference=0
+No errors were found on the image.
+
+=== reftable_update ===
+
+Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.base'
+wrote 65536/65536 bytes at offset 0
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+qemu-img: Could not empty blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.IMGFMT: Input/output error
+ERROR cluster 0 refcount=0 reference=1
+ERROR cluster 1 refcount=0 reference=1
+ERROR cluster 3 refcount=0 reference=1
+Rebuilding refcount structure
+Repairing cluster 1 refcount=1 reference=0
+No errors were found on the image.
+
+=== refblock_alloc ===
+
+Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.base'
+wrote 65536/65536 bytes at offset 0
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+qemu-img: Could not empty blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.IMGFMT: Input/output error
+ERROR cluster 0 refcount=0 reference=1
+ERROR cluster 1 refcount=0 reference=1
+ERROR cluster 3 refcount=0 reference=1
+Rebuilding refcount structure
+Repairing cluster 1 refcount=1 reference=0
+No errors were found on the image.
+*** done
diff --git a/tests/qemu-iotests/099 b/tests/qemu-iotests/099
new file mode 100755
index 000000000..ffc7ea795
--- /dev/null
+++ b/tests/qemu-iotests/099
@@ -0,0 +1,116 @@
+#!/bin/bash
+#
+# Test valid filenames for blkdebug and blkverify representatively for
+# other protocols (such as NBD) when queried
+#
+# 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=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
+
+# Basically all formats, but "raw" has issues with _filter_imgfmt regarding the
+# raw comparison image for blkverify; also, all images have to support creation
+_supported_fmt qcow qcow2 qed vdi vhdx vmdk vpc
+_supported_proto file
+_supported_os Linux
+
+
+function do_run_qemu()
+{
+ $QEMU -nographic -qmp stdio -serial none "$@"
+}
+
+function run_qemu()
+{
+ # Get the "file": "foo" entry ($foo may only contain escaped double quotes,
+ # which is how we can extract it)
+ do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_imgfmt | _filter_qmp \
+ | grep "drv0" \
+ | sed -e 's/^.*"file": "\(\(\\"\|[^"]\)*\)".*$/\1/' -e 's/\\"/"/g'
+}
+
+function test_qemu()
+{
+ run_qemu -drive "if=none,id=drv0,$1" <<EOF
+ { 'execute': 'qmp_capabilities' }
+ { 'execute': 'query-block' }
+ { 'execute': 'quit' }
+EOF
+}
+
+
+
+IMG_SIZE=128K
+
+_make_test_img $IMG_SIZE
+$QEMU_IMG create -f raw "$TEST_IMG.compare" $IMG_SIZE \
+ | _filter_testdir | _filter_imgfmt
+
+echo
+echo '=== Testing simple filename for blkverify ==='
+echo
+
+# This should return simply the filename itself
+test_qemu "file=blkverify:$TEST_IMG.compare:$TEST_IMG"
+
+echo
+echo '=== Testing filename reconstruction for blkverify ==='
+echo
+
+# This should return the same filename as above
+test_qemu "file.driver=blkverify,file.raw.filename=$TEST_IMG.compare,file.test.file.filename=$TEST_IMG"
+
+echo
+echo '=== Testing JSON filename for blkdebug ==='
+echo
+
+# blkdebug cannot create a configuration file, therefore it is unable to
+# generate a plain filename here; thus this should return a JSON filename
+test_qemu "file.driver=blkdebug,file.image.filename=$TEST_IMG,file.inject-error.0.event=l1_update"
+
+echo
+echo '=== Testing indirectly enforced JSON filename ==='
+echo
+
+# Because blkdebug cannot return a plain filename, blkverify is forced to
+# generate a JSON object here as well
+test_qemu "file.driver=blkverify,file.raw.filename=$TEST_IMG.compare,file.test.file.driver=blkdebug,file.test.file.image.filename=$TEST_IMG,file.test.file.inject-error.0.event=l1_update"
+
+
+rm -f "$TEST_IMG.compare"
+
+# success, all done
+echo "*** done"
+rm -f $seq.full
+status=0
diff --git a/tests/qemu-iotests/099.out b/tests/qemu-iotests/099.out
new file mode 100644
index 000000000..55be4d4c5
--- /dev/null
+++ b/tests/qemu-iotests/099.out
@@ -0,0 +1,20 @@
+QA output created by 099
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=131072
+Formatting 'TEST_DIR/t.IMGFMT.compare', fmt=raw size=131072
+
+=== Testing simple filename for blkverify ===
+
+blkverify:TEST_DIR/t.IMGFMT.compare:TEST_DIR/t.IMGFMT
+
+=== Testing filename reconstruction for blkverify ===
+
+blkverify:TEST_DIR/t.IMGFMT.compare:TEST_DIR/t.IMGFMT
+
+=== Testing JSON filename for blkdebug ===
+
+json:{"driver": "IMGFMT", "file": {"inject-error": [{"immediately": false, "once": false, "state": 0, "sector": -1, "event": "l1_update", "errno": 5}], "image": {"driver": "file", "filename": "TEST_DIR/t.IMGFMT"}, "driver": "blkdebug"}}
+
+=== Testing indirectly enforced JSON filename ===
+
+json:{"driver": "raw", "file": {"test": {"driver": "IMGFMT", "file": {"inject-error": [{"immediately": false, "once": false, "state": 0, "sector": -1, "event": "l1_update", "errno": 5}], "image": {"driver": "file", "filename": "TEST_DIR/t.IMGFMT"}, "driver": "blkdebug"}}, "driver": "blkverify", "raw": {"driver": "file", "filename": "TEST_DIR/t.IMGFMT.compare"}}}
+*** done
diff --git a/tests/qemu-iotests/100 b/tests/qemu-iotests/100
new file mode 100755
index 000000000..9124aba76
--- /dev/null
+++ b/tests/qemu-iotests/100
@@ -0,0 +1,134 @@
+#!/bin/bash
+#
+# Test simple read/write using plain bdrv_read/bdrv_write
+#
+# 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=stefanha@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
+
+
+size=128M
+
+echo
+echo "== Single request =="
+_make_test_img $size
+$QEMU_IO -c "multiwrite 0 4k" "$TEST_IMG" | _filter_qemu_io
+
+echo
+echo "== verify pattern =="
+$QEMU_IO -c "read -P 0xcd 0 4k" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IO -c "read -P 0 4k 4k" "$TEST_IMG" | _filter_qemu_io
+
+echo
+echo "== Sequential requests =="
+_make_test_img $size
+$QEMU_IO -c "multiwrite 0 4k ; 4k 4k" "$TEST_IMG" | _filter_qemu_io
+
+echo
+echo "== verify pattern =="
+$QEMU_IO -c "read -P 0xcd 0 4k" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IO -c "read -P 0xce 4k 4k" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IO -c "read -P 0 8k 4k" "$TEST_IMG" | _filter_qemu_io
+
+echo
+echo "== Superset overlapping requests =="
+_make_test_img $size
+$QEMU_IO -c "multiwrite 0 4k ; 1k 2k" "$TEST_IMG" | _filter_qemu_io
+
+echo
+echo "== verify pattern =="
+# Order of overlapping in-flight requests is not guaranteed so we cannot verify
+# [1k, 3k) since it could have either pattern 0xcd or 0xce.
+$QEMU_IO -c "read -P 0xcd 0 1k" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IO -c "read -P 0xcd 3k 1k" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IO -c "read -P 0 4k 4k" "$TEST_IMG" | _filter_qemu_io
+
+echo
+echo "== Subset overlapping requests =="
+_make_test_img $size
+$QEMU_IO -c "multiwrite 1k 2k ; 0k 4k" "$TEST_IMG" | _filter_qemu_io
+
+echo
+echo "== verify pattern =="
+# Order of overlapping in-flight requests is not guaranteed so we cannot verify
+# [1k, 3k) since it could have either pattern 0xcd or 0xce.
+$QEMU_IO -c "read -P 0xce 0 1k" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IO -c "read -P 0xce 3k 1k" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IO -c "read -P 0 4k 4k" "$TEST_IMG" | _filter_qemu_io
+
+echo
+echo "== Head overlapping requests =="
+_make_test_img $size
+$QEMU_IO -c "multiwrite 0k 2k ; 0k 4k" "$TEST_IMG" | _filter_qemu_io
+
+echo
+echo "== verify pattern =="
+# Order of overlapping in-flight requests is not guaranteed so we cannot verify
+# [0k, 2k) since it could have either pattern 0xcd or 0xce.
+$QEMU_IO -c "read -P 0xce 2k 2k" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IO -c "read -P 0 4k 4k" "$TEST_IMG" | _filter_qemu_io
+
+echo
+echo "== Tail overlapping requests =="
+_make_test_img $size
+$QEMU_IO -c "multiwrite 2k 2k ; 0k 4k" "$TEST_IMG" | _filter_qemu_io
+
+echo
+echo "== verify pattern =="
+# Order of overlapping in-flight requests is not guaranteed so we cannot verify
+# [2k, 4k) since it could have either pattern 0xcd or 0xce.
+$QEMU_IO -c "read -P 0xce 0k 2k" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IO -c "read -P 0 4k 4k" "$TEST_IMG" | _filter_qemu_io
+
+echo
+echo "== Disjoint requests =="
+_make_test_img $size
+$QEMU_IO -c "multiwrite 0 4k ; 64k 4k" "$TEST_IMG" | _filter_qemu_io
+
+echo
+echo "== verify pattern =="
+$QEMU_IO -c "read -P 0xcd 0 4k" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IO -c "read -P 0 4k 60k" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IO -c "read -P 0xce 64k 4k" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IO -c "read -P 0 68k 4k" "$TEST_IMG" | _filter_qemu_io
+
+# success, all done
+echo "*** done"
+rm -f $seq.full
+status=0
diff --git a/tests/qemu-iotests/100.out b/tests/qemu-iotests/100.out
new file mode 100644
index 000000000..2d6e9f0a7
--- /dev/null
+++ b/tests/qemu-iotests/100.out
@@ -0,0 +1,89 @@
+QA output created by 100
+
+== Single request ==
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728
+wrote 4096/4096 bytes at offset 0
+4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+== verify pattern ==
+read 4096/4096 bytes at offset 0
+4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 4096/4096 bytes at offset 4096
+4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+== Sequential requests ==
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728
+wrote 8192/8192 bytes at offset 0
+8 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+== verify pattern ==
+read 4096/4096 bytes at offset 0
+4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 4096/4096 bytes at offset 4096
+4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 4096/4096 bytes at offset 8192
+4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+== Superset overlapping requests ==
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728
+wrote 6144/6144 bytes at offset 0
+6 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+== verify pattern ==
+read 1024/1024 bytes at offset 0
+1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 1024/1024 bytes at offset 3072
+1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 4096/4096 bytes at offset 4096
+4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+== Subset overlapping requests ==
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728
+wrote 6144/6144 bytes at offset 1024
+6 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+== verify pattern ==
+read 1024/1024 bytes at offset 0
+1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 1024/1024 bytes at offset 3072
+1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 4096/4096 bytes at offset 4096
+4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+== Head overlapping requests ==
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728
+wrote 6144/6144 bytes at offset 0
+6 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+== verify pattern ==
+read 2048/2048 bytes at offset 2048
+2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 4096/4096 bytes at offset 4096
+4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+== Tail overlapping requests ==
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728
+wrote 6144/6144 bytes at offset 2048
+6 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+== verify pattern ==
+read 2048/2048 bytes at offset 0
+2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 4096/4096 bytes at offset 4096
+4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+== Disjoint requests ==
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728
+wrote 8192/8192 bytes at offset 0
+8 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+== verify pattern ==
+read 4096/4096 bytes at offset 0
+4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 61440/61440 bytes at offset 4096
+60 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 4096/4096 bytes at offset 65536
+4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 4096/4096 bytes at offset 69632
+4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+*** done
diff --git a/tests/qemu-iotests/101 b/tests/qemu-iotests/101
new file mode 100755
index 000000000..70fbf25f6
--- /dev/null
+++ b/tests/qemu-iotests/101
@@ -0,0 +1,58 @@
+#!/bin/bash
+#
+# Test short file I/O
+#
+# 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=stefanha@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 raw
+_supported_proto file
+_supported_os Linux
+
+
+echo
+echo "== creating short image file =="
+dd if=/dev/zero of="$TEST_IMG" bs=1 count=320
+
+echo
+echo "== reading bytes beyond EOF gives zeroes =="
+$QEMU_IO -c "read -P 0 0 512" "$TEST_IMG" | _filter_qemu_io
+
+
+# success, all done
+echo "*** done"
+rm -f $seq.full
+status=0
diff --git a/tests/qemu-iotests/101.out b/tests/qemu-iotests/101.out
new file mode 100644
index 000000000..9a996e8fc
--- /dev/null
+++ b/tests/qemu-iotests/101.out
@@ -0,0 +1,10 @@
+QA output created by 101
+
+== creating short image file ==
+320+0 records in
+320+0 records out
+
+== reading bytes beyond EOF gives zeroes ==
+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/102 b/tests/qemu-iotests/102
new file mode 100755
index 000000000..161b1974c
--- /dev/null
+++ b/tests/qemu-iotests/102
@@ -0,0 +1,81 @@
+#!/bin/bash
+#
+# Test case for qemu-io -c map and qemu-img map
+#
+# 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=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 qemu instance handling
+. ./common.rc
+. ./common.filter
+. ./common.qemu
+
+_supported_fmt qcow2
+_supported_proto file
+_supported_os Linux
+
+IMG_SIZE=64K
+
+echo
+echo '=== Testing map command on truncated image ==='
+echo
+
+_make_test_img $IMG_SIZE
+# Create cluster
+$QEMU_IO -c 'write 0 64k' "$TEST_IMG" | _filter_qemu_io
+# Remove data cluster from image (first cluster: image header, second: reftable,
+# third: refblock, fourth: L1 table, fifth: L2 table)
+$QEMU_IMG resize -f raw "$TEST_IMG" $((5 * 64 * 1024))
+
+$QEMU_IO -c map "$TEST_IMG"
+$QEMU_IMG map "$TEST_IMG"
+
+echo
+echo '=== Testing map on an image file truncated outside of qemu ==='
+echo
+
+# Same as above, only now we concurrently truncate and map the image
+_make_test_img $IMG_SIZE
+$QEMU_IO -c 'write 0 64k' "$TEST_IMG" | _filter_qemu_io
+
+qemu_comm_method=monitor _launch_qemu -drive if=none,file="$TEST_IMG",id=drv0
+
+$QEMU_IMG resize -f raw "$TEST_IMG" $((5 * 64 * 1024))
+
+_send_qemu_cmd $QEMU_HANDLE 'qemu-io drv0 map' 'allocated' \
+ | sed -e 's/^(qemu).*qemu-io drv0 map...$/(qemu) qemu-io drv0 map/'
+_send_qemu_cmd $QEMU_HANDLE 'quit' ''
+
+# success, all done
+echo '*** done'
+rm -f $seq.full
+status=0
diff --git a/tests/qemu-iotests/102.out b/tests/qemu-iotests/102.out
new file mode 100644
index 000000000..eecde16ad
--- /dev/null
+++ b/tests/qemu-iotests/102.out
@@ -0,0 +1,21 @@
+QA output created by 102
+
+=== Testing map command on truncated image ===
+
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=65536
+wrote 65536/65536 bytes at offset 0
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Image resized.
+[ 0] 128/ 128 sectors allocated at offset 0 bytes (1)
+Offset Length Mapped to File
+
+=== Testing map on an image file truncated outside of qemu ===
+
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=65536
+wrote 65536/65536 bytes at offset 0
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Image resized.
+QEMU X.Y.Z monitor - type 'help' for more information
+(qemu) qemu-io drv0 map
+[ 0] 128/ 128 sectors allocated at offset 0 bytes (1)
+*** done
diff --git a/tests/qemu-iotests/103 b/tests/qemu-iotests/103
new file mode 100755
index 000000000..ccab551f6
--- /dev/null
+++ b/tests/qemu-iotests/103
@@ -0,0 +1,99 @@
+#!/bin/bash
+#
+# Test case for qcow2 metadata cache size specification
+#
+# 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=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 qcow2
+_supported_proto file nfs
+_supported_os Linux
+
+IMG_SIZE=64K
+
+_make_test_img $IMG_SIZE
+$QEMU_IO -c 'write -P 42 0 64k' "$TEST_IMG" | _filter_qemu_io
+
+echo
+echo '=== Testing invalid option combinations ==='
+echo
+
+# all sizes set at the same time
+$QEMU_IO -c "open -o cache-size=1.25M,l2-cache-size=1M,refcount-cache-size=0.25M $TEST_IMG" \
+ 2>&1 | _filter_testdir | _filter_imgfmt
+# l2-cache-size may not exceed cache-size
+$QEMU_IO -c "open -o cache-size=1M,l2-cache-size=2M $TEST_IMG" 2>&1 \
+ | _filter_testdir | _filter_imgfmt
+# refcount-cache-size may not exceed cache-size
+$QEMU_IO -c "open -o cache-size=1M,refcount-cache-size=2M $TEST_IMG" 2>&1 \
+ | _filter_testdir | _filter_imgfmt
+# 0 should be a valid size (e.g. for enforcing the minimum), so this should not
+# work
+$QEMU_IO -c "open -o cache-size=0,l2-cache-size=0,refcount-cache-size=0 $TEST_IMG" \
+ 2>&1 | _filter_testdir | _filter_imgfmt
+
+echo
+echo '=== Testing valid option combinations ==='
+echo
+
+# There should be a reasonable and working minimum
+$QEMU_IO -c "open -o cache-size=0 $TEST_IMG" -c 'read -P 42 0 64k' \
+ | _filter_qemu_io
+$QEMU_IO -c "open -o l2-cache-size=0 $TEST_IMG" -c 'read -P 42 0 64k' \
+ | _filter_qemu_io
+$QEMU_IO -c "open -o refcount-cache-size=0 $TEST_IMG" -c 'read -P 42 0 64k' \
+ | _filter_qemu_io
+
+# Derive cache sizes from combined size (with a reasonable ratio, but we cannot
+# test that)
+$QEMU_IO -c "open -o cache-size=2M $TEST_IMG" -c 'read -P 42 0 64k' \
+ | _filter_qemu_io
+# Fix one cache, derive the other
+$QEMU_IO -c "open -o cache-size=2M,l2-cache-size=1M $TEST_IMG" \
+ -c 'read -P 42 0 64k' \
+ | _filter_qemu_io
+$QEMU_IO -c "open -o cache-size=2M,refcount-cache-size=1M $TEST_IMG" \
+ -c 'read -P 42 0 64k' \
+ | _filter_qemu_io
+# Directly set both caches
+$QEMU_IO -c "open -o l2-cache-size=1M,refcount-cache-size=0.25M $TEST_IMG" \
+ -c 'read -P 42 0 64k' \
+ | _filter_qemu_io
+
+# success, all done
+echo '*** done'
+rm -f $seq.full
+status=0
diff --git a/tests/qemu-iotests/103.out b/tests/qemu-iotests/103.out
new file mode 100644
index 000000000..ddf6b5a07
--- /dev/null
+++ b/tests/qemu-iotests/103.out
@@ -0,0 +1,29 @@
+QA output created by 103
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=65536
+wrote 65536/65536 bytes at offset 0
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+=== Testing invalid option combinations ===
+
+qemu-io: can't open device TEST_DIR/t.IMGFMT: cache-size, l2-cache-size and refcount-cache-size may not be set the same time
+qemu-io: can't open device TEST_DIR/t.IMGFMT: l2-cache-size may not exceed cache-size
+qemu-io: can't open device TEST_DIR/t.IMGFMT: refcount-cache-size may not exceed cache-size
+qemu-io: can't open device TEST_DIR/t.IMGFMT: cache-size, l2-cache-size and refcount-cache-size may not be set the same time
+
+=== Testing valid option combinations ===
+
+read 65536/65536 bytes at offset 0
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 65536/65536 bytes at offset 0
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 65536/65536 bytes at offset 0
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 65536/65536 bytes at offset 0
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 65536/65536 bytes at offset 0
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 65536/65536 bytes at offset 0
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 65536/65536 bytes at offset 0
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+*** done
diff --git a/tests/qemu-iotests/104 b/tests/qemu-iotests/104
new file mode 100755
index 000000000..b471aa5bb
--- /dev/null
+++ b/tests/qemu-iotests/104
@@ -0,0 +1,57 @@
+#!/bin/bash
+#
+# Test image creation with aligned and unaligned sizes
+#
+# Copyright (C) 2014 Fujitsu.
+#
+# 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=hutao@cn.fujitsu.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
+
+echo "=== Check qemu-img info output ==="
+echo
+image_sizes="1024 1234"
+
+for s in $image_sizes; do
+ _make_test_img $s | _filter_img_create
+ _img_info | _filter_img_info
+done
+
+# success, all done
+echo "*** done"
+rm -f $seq.full
+status=0
diff --git a/tests/qemu-iotests/104.out b/tests/qemu-iotests/104.out
new file mode 100644
index 000000000..de27852e3
--- /dev/null
+++ b/tests/qemu-iotests/104.out
@@ -0,0 +1,12 @@
+QA output created by 104
+=== Check qemu-img info output ===
+
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1024
+image: TEST_DIR/t.IMGFMT
+file format: IMGFMT
+virtual size: 1.0K (1024 bytes)
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1234
+image: TEST_DIR/t.IMGFMT
+file format: IMGFMT
+virtual size: 1.5K (1536 bytes)
+***done
diff --git a/tests/qemu-iotests/105 b/tests/qemu-iotests/105
new file mode 100755
index 000000000..9bae49e32
--- /dev/null
+++ b/tests/qemu-iotests/105
@@ -0,0 +1,70 @@
+#!/bin/bash
+#
+# Create, read, write big image
+#
+# 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=famz@redhat.com
+
+seq=`basename $0`
+echo "QA output created by $seq"
+
+here=`pwd`
+tmp=/tmp/$$
+status=1 # failure is the default!
+
+_cleanup()
+{
+ _cleanup_test_img
+}
+trap "_cleanup; exit \$status" 0 1 2 3 15
+
+# get standard environment, filters and checks
+. ./common.rc
+. ./common.filter
+
+_supported_fmt qcow2 vmdk vhdx qed
+_supported_proto generic
+_supported_os Linux
+_unsupported_imgopts "subformat=twoGbMaxExtentFlat" \
+ "subformat=twoGbMaxExtentSparse"
+
+echo
+echo "creating large image"
+_make_test_img 16T
+
+echo
+echo "small read"
+$QEMU_IO -c "read 1024 4096" "$TEST_IMG" | _filter_qemu_io
+
+echo
+echo "small write"
+$QEMU_IO -c "write 8192 4096" "$TEST_IMG" | _filter_qemu_io
+
+echo
+echo "small read at high offset"
+$QEMU_IO -c "read 14T 4096" "$TEST_IMG" | _filter_qemu_io
+
+echo
+echo "small write at high offset"
+$QEMU_IO -c "write 14T 4096" "$TEST_IMG" | _filter_qemu_io
+
+# success, all done
+echo "*** done"
+rm -f $seq.full
+status=0
diff --git a/tests/qemu-iotests/105.out b/tests/qemu-iotests/105.out
new file mode 100644
index 000000000..13ffcb593
--- /dev/null
+++ b/tests/qemu-iotests/105.out
@@ -0,0 +1,21 @@
+QA output created by 105
+
+creating large image
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=17592186044416
+
+small read
+read 4096/4096 bytes at offset 1024
+4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+small write
+wrote 4096/4096 bytes at offset 8192
+4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+small read at high offset
+read 4096/4096 bytes at offset 15393162788864
+4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+small write at high offset
+wrote 4096/4096 bytes at offset 15393162788864
+4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+*** done
diff --git a/tests/qemu-iotests/107 b/tests/qemu-iotests/107
new file mode 100755
index 000000000..986203046
--- /dev/null
+++ b/tests/qemu-iotests/107
@@ -0,0 +1,61 @@
+#!/bin/bash
+#
+# Tests updates of the qcow2 L1 table
+#
+# 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=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 qcow2
+_supported_proto file nfs
+_supported_os Linux
+
+
+IMG_SIZE=64K
+
+echo
+echo '=== Updates should not write random data ==='
+echo
+
+_make_test_img $IMG_SIZE
+$QEMU_IO -c 'write 0 64k' "$TEST_IMG" | _filter_qemu_io
+$QEMU_IO -c "open -o driver=raw $TEST_IMG" -c 'read -p -P 0 196616 65528' \
+ | _filter_qemu_io
+
+# success, all done
+echo "*** done"
+rm -f $seq.full
+status=0
+
diff --git a/tests/qemu-iotests/107.out b/tests/qemu-iotests/107.out
new file mode 100644
index 000000000..93445b731
--- /dev/null
+++ b/tests/qemu-iotests/107.out
@@ -0,0 +1,10 @@
+QA output created by 107
+
+=== Updates should not write random data ===
+
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=65536
+wrote 65536/65536 bytes at offset 0
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 65528/65528 bytes at offset 196616
+63.992 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+*** done
diff --git a/tests/qemu-iotests/108 b/tests/qemu-iotests/108
new file mode 100755
index 000000000..12fc92a63
--- /dev/null
+++ b/tests/qemu-iotests/108
@@ -0,0 +1,141 @@
+#!/bin/bash
+#
+# Test case for repairing qcow2 images which cannot be repaired using
+# the on-disk refcount structures
+#
+# 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=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
+
+# This tests qocw2-specific low-level functionality
+_supported_fmt qcow2
+_supported_proto file
+_supported_os Linux
+
+echo
+echo '=== Repairing an image without any refcount table ==='
+echo
+
+_make_test_img 64M
+# just write some data
+$QEMU_IO -c 'write -P 42 0 64k' "$TEST_IMG" | _filter_qemu_io
+
+# refcount_table_offset
+poke_file "$TEST_IMG" $((0x30)) "\x00\x00\x00\x00\x00\x00\x00\x00"
+# refcount_table_clusters
+poke_file "$TEST_IMG" $((0x38)) "\x00\x00\x00\x00"
+
+_check_test_img -r all
+
+$QEMU_IO -c 'read -P 42 0 64k' "$TEST_IMG" | _filter_qemu_io
+
+echo
+echo '=== Repairing unreferenced data cluster in new refblock area ==='
+echo
+
+IMGOPTS='cluster_size=512' _make_test_img 64M
+# Allocate the first 128 kB in the image (first refblock)
+$QEMU_IO -c 'write 0 0x1b200' "$TEST_IMG" | _filter_qemu_io
+# should be 131072 == 0x20000
+stat -c '%s' "$TEST_IMG"
+
+# Enter a cluster at 128 kB (0x20000)
+# XXX: This should be the first free entry in the last L2 table, but we cannot
+# be certain
+poke_file "$TEST_IMG" $((0x1ccc8)) "\x80\x00\x00\x00\x00\x02\x00\x00"
+
+# Fill the cluster
+truncate -s $((0x20200)) "$TEST_IMG"
+$QEMU_IO -c "open -o driver=raw $TEST_IMG" -c 'write -P 42 128k 512' \
+ | _filter_qemu_io
+
+# The data should now appear at this guest offset
+$QEMU_IO -c 'read -P 42 0x1b200 512' "$TEST_IMG" | _filter_qemu_io
+
+# This cluster is unallocated; fix it
+_check_test_img -r all
+
+# This repair operation must have allocated a new refblock; and that refblock
+# should not overlap with the unallocated data cluster. If it does, the data
+# will be damaged, so check it.
+$QEMU_IO -c 'read -P 42 0x1b200 512' "$TEST_IMG" | _filter_qemu_io
+
+echo
+echo '=== Repairing refblock beyond the image end ==='
+echo
+
+echo
+echo '--- Otherwise clean ---'
+echo
+
+_make_test_img 64M
+# Normally, qemu doesn't create empty refblocks, so we just have to do it by
+# hand
+# XXX: This should be the entry for the second refblock
+poke_file "$TEST_IMG" $((0x10008)) "\x00\x00\x00\x00\x00\x10\x00\x00"
+# Mark that refblock as used
+# XXX: This should be the 17th entry (cluster 16) of the first
+# refblock
+poke_file "$TEST_IMG" $((0x20020)) "\x00\x01"
+_check_test_img -r all
+
+echo
+echo '--- Refblock is unallocated ---'
+echo
+
+_make_test_img 64M
+poke_file "$TEST_IMG" $((0x10008)) "\x00\x00\x00\x00\x00\x10\x00\x00"
+_check_test_img -r all
+
+echo
+echo '--- Signed overflow after the refblock ---'
+echo
+
+_make_test_img 64M
+poke_file "$TEST_IMG" $((0x10008)) "\x7f\xff\xff\xff\xff\xff\x00\x00"
+_check_test_img -r all
+
+echo
+echo '--- Unsigned overflow after the refblock ---'
+echo
+
+_make_test_img 64M
+poke_file "$TEST_IMG" $((0x10008)) "\xff\xff\xff\xff\xff\xff\x00\x00"
+_check_test_img -r all
+
+# success, all done
+echo '*** done'
+rm -f $seq.full
+status=0
diff --git a/tests/qemu-iotests/108.out b/tests/qemu-iotests/108.out
new file mode 100644
index 000000000..824d5cf13
--- /dev/null
+++ b/tests/qemu-iotests/108.out
@@ -0,0 +1,110 @@
+QA output created by 108
+
+=== Repairing an image without any refcount table ===
+
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
+wrote 65536/65536 bytes at offset 0
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+ERROR cluster 0 refcount=0 reference=1
+ERROR cluster 3 refcount=0 reference=1
+ERROR cluster 4 refcount=0 reference=1
+ERROR cluster 5 refcount=0 reference=1
+Rebuilding refcount structure
+The following inconsistencies were found and repaired:
+
+ 0 leaked clusters
+ 4 corruptions
+
+Double checking the fixed image now...
+No errors were found on the image.
+read 65536/65536 bytes at offset 0
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+=== Repairing unreferenced data cluster in new refblock area ===
+
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
+wrote 111104/111104 bytes at offset 0
+108.500 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+131072
+wrote 512/512 bytes at offset 131072
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 111104
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+ERROR cluster 256 refcount=0 reference=1
+Rebuilding refcount structure
+Repairing cluster 1 refcount=1 reference=0
+Repairing cluster 2 refcount=1 reference=0
+The following inconsistencies were found and repaired:
+
+ 0 leaked clusters
+ 1 corruptions
+
+Double checking the fixed image now...
+No errors were found on the image.
+read 512/512 bytes at offset 111104
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+=== Repairing refblock beyond the image end ===
+
+
+--- Otherwise clean ---
+
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
+Repairing refcount block 1 is outside image
+The following inconsistencies were found and repaired:
+
+ 0 leaked clusters
+ 1 corruptions
+
+Double checking the fixed image now...
+No errors were found on the image.
+
+--- Refblock is unallocated ---
+
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
+Repairing refcount block 1 is outside image
+ERROR cluster 16 refcount=0 reference=1
+Rebuilding refcount structure
+Repairing cluster 1 refcount=1 reference=0
+Repairing cluster 2 refcount=1 reference=0
+Repairing cluster 16 refcount=1 reference=0
+The following inconsistencies were found and repaired:
+
+ 0 leaked clusters
+ 2 corruptions
+
+Double checking the fixed image now...
+No errors were found on the image.
+
+--- Signed overflow after the refblock ---
+
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
+Repairing refcount block 1 is outside image
+ERROR could not resize image: Invalid argument
+Rebuilding refcount structure
+Repairing cluster 1 refcount=1 reference=0
+Repairing cluster 2 refcount=1 reference=0
+The following inconsistencies were found and repaired:
+
+ 0 leaked clusters
+ 1 corruptions
+
+Double checking the fixed image now...
+No errors were found on the image.
+
+--- Unsigned overflow after the refblock ---
+
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
+Repairing refcount block 1 is outside image
+ERROR could not resize image: Invalid argument
+Rebuilding refcount structure
+Repairing cluster 1 refcount=1 reference=0
+Repairing cluster 2 refcount=1 reference=0
+The following inconsistencies were found and repaired:
+
+ 0 leaked clusters
+ 1 corruptions
+
+Double checking the fixed image now...
+No errors were found on the image.
+*** done
diff --git a/tests/qemu-iotests/111 b/tests/qemu-iotests/111
new file mode 100755
index 000000000..6011c94b7
--- /dev/null
+++ b/tests/qemu-iotests/111
@@ -0,0 +1,53 @@
+#!/bin/bash
+#
+# Test case for non-existing backing file when creating a qcow2 image
+# and not specifying the size
+#
+# 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=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 qed qcow qcow2 vmdk
+_supported_proto file
+_supported_os Linux
+_unsupported_imgopts "subformat=monolithicFlat" "subformat=twoGbMaxExtentFlat"
+
+$QEMU_IMG create -f $IMGFMT -b "$TEST_IMG.inexistent" "$TEST_IMG" 2>&1 \
+ | _filter_testdir | _filter_imgfmt
+
+# success, all done
+echo '*** done'
+rm -f $seq.full
+status=0
diff --git a/tests/qemu-iotests/111.out b/tests/qemu-iotests/111.out
new file mode 100644
index 000000000..683c01a67
--- /dev/null
+++ b/tests/qemu-iotests/111.out
@@ -0,0 +1,3 @@
+QA output created by 111
+qemu-img: TEST_DIR/t.IMGFMT: Could not open 'TEST_DIR/t.IMGFMT.inexistent': No such file or directory
+*** done
diff --git a/tests/qemu-iotests/common b/tests/qemu-iotests/common
index e4083f421..9e12bec2b 100644
--- a/tests/qemu-iotests/common
+++ b/tests/qemu-iotests/common
@@ -136,7 +136,6 @@ common options
check options
-raw test raw (default)
-bochs test bochs
- -cow test cow
-cloop test cloop
-parallels test parallels
-qcow test qcow
@@ -152,6 +151,7 @@ check options
-nbd test nbd
-ssh test ssh
-nfs test nfs
+ -archipelago test archipelago
-xdiff graphical mode diff
-nocache use O_DIRECT on backing file
-misalign misalign memory allocations
@@ -181,11 +181,6 @@ testlist options
xpand=false
;;
- -cow)
- IMGFMT=cow
- xpand=false
- ;;
-
-cloop)
IMGFMT=cloop
IMGFMT_GENERIC=false
@@ -263,6 +258,11 @@ testlist options
xpand=false
;;
+ -archipelago)
+ IMGPROTO=archipelago
+ xpand=false
+ ;;
+
-nocache)
CACHEMODE="none"
CACHEMODE_IS_DEFAULT=false
@@ -376,10 +376,16 @@ BEGIN { for (t='$start'; t<='$end'; t++) printf "%03d\n",t }' \
echo $id >>$tmp.list
else
# oops
- echo "$id - unknown test, ignored"
+ if [ "$start" == "$end" -a "$id" == "$end" ]
+ then
+ echo "$id - unknown test"
+ exit 1
+ else
+ echo "$id - unknown test, ignored"
+ fi
fi
fi
- done
+ done || exit 1
fi
done
diff --git a/tests/qemu-iotests/common.filter b/tests/qemu-iotests/common.filter
index a04df7f6d..3acdb307f 100644
--- a/tests/qemu-iotests/common.filter
+++ b/tests/qemu-iotests/common.filter
@@ -170,5 +170,55 @@ _filter_qmp()
-e 's#^{"QMP":.*}$#QMP_VERSION#'
}
+# replace driver-specific options in the "Formatting..." line
+_filter_img_create()
+{
+ sed -e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" \
+ -e "s#$TEST_DIR#TEST_DIR#g" \
+ -e "s#$IMGFMT#IMGFMT#g" \
+ -e "s# encryption=off##g" \
+ -e "s# cluster_size=[0-9]\\+##g" \
+ -e "s# table_size=[0-9]\\+##g" \
+ -e "s# compat='[^']*'##g" \
+ -e "s# compat6=\\(on\\|off\\)##g" \
+ -e "s# static=\\(on\\|off\\)##g" \
+ -e "s# zeroed_grain=\\(on\\|off\\)##g" \
+ -e "s# subformat='[^']*'##g" \
+ -e "s# adapter_type='[^']*'##g" \
+ -e "s# lazy_refcounts=\\(on\\|off\\)##g" \
+ -e "s# block_size=[0-9]\\+##g" \
+ -e "s# block_state_zero=\\(on\\|off\\)##g" \
+ -e "s# log_size=[0-9]\\+##g" \
+ -e "s/archipelago:a/TEST_DIR\//g"
+}
+
+_filter_img_info()
+{
+ sed -e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" \
+ -e "s#$TEST_DIR#TEST_DIR#g" \
+ -e "s#$IMGFMT#IMGFMT#g" \
+ -e "/encrypted: yes/d" \
+ -e "/cluster_size: [0-9]\\+/d" \
+ -e "/table_size: [0-9]\\+/d" \
+ -e "/compat: '[^']*'/d" \
+ -e "/compat6: \\(on\\|off\\)/d" \
+ -e "/static: \\(on\\|off\\)/d" \
+ -e "/zeroed_grain: \\(on\\|off\\)/d" \
+ -e "/subformat: '[^']*'/d" \
+ -e "/adapter_type: '[^']*'/d" \
+ -e "/lazy_refcounts: \\(on\\|off\\)/d" \
+ -e "/block_size: [0-9]\\+/d" \
+ -e "/block_state_zero: \\(on\\|off\\)/d" \
+ -e "/log_size: [0-9]\\+/d" \
+ -e "s/archipelago:a/TEST_DIR\//g"
+}
+
+# filter out offsets and file names from qemu-img map
+_filter_qemu_img_map()
+{
+ sed -e 's/\([0-9a-fx]* *[0-9a-fx]* *\)[0-9a-fx]* */\1/g' \
+ -e 's/Mapped to *//' | _filter_testdir | _filter_imgfmt
+}
+
# make sure this script returns success
/bin/true
diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc
index e0ea7e3a7..9c49deb3d 100644
--- a/tests/qemu-iotests/common.rc
+++ b/tests/qemu-iotests/common.rc
@@ -64,6 +64,8 @@ elif [ "$IMGPROTO" = "ssh" ]; then
elif [ "$IMGPROTO" = "nfs" ]; then
TEST_DIR="nfs://127.0.0.1/$TEST_DIR"
TEST_IMG=$TEST_DIR/t.$IMGFMT
+elif [ "$IMGPROTO" = "archipelago" ]; then
+ TEST_IMG="archipelago:at.$IMGFMT"
else
TEST_IMG=$IMGPROTO:$TEST_DIR/t.$IMGFMT
fi
@@ -147,23 +149,7 @@ _make_test_img()
else
$QEMU_IMG create -f $IMGFMT $extra_img_options "$img_name" $image_size 2>&1
fi
- ) | \
- sed -e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" \
- -e "s#$TEST_DIR#TEST_DIR#g" \
- -e "s#$IMGFMT#IMGFMT#g" \
- -e "s# encryption=off##g" \
- -e "s# cluster_size=[0-9]\\+##g" \
- -e "s# table_size=[0-9]\\+##g" \
- -e "s# compat='[^']*'##g" \
- -e "s# compat6=\\(on\\|off\\)##g" \
- -e "s# static=\\(on\\|off\\)##g" \
- -e "s# zeroed_grain=\\(on\\|off\\)##g" \
- -e "s# subformat='[^']*'##g" \
- -e "s# adapter_type='[^']*'##g" \
- -e "s# lazy_refcounts=\\(on\\|off\\)##g" \
- -e "s# block_size=[0-9]\\+##g" \
- -e "s# block_state_zero=\\(on\\|off\\)##g" \
- -e "s# log_size=[0-9]\\+##g"
+ ) | _filter_img_create
# Start an NBD server on the image file, which is what we'll be talking to
if [ $IMGPROTO = "nbd" ]; then
@@ -206,6 +192,10 @@ _cleanup_test_img()
rbd --no-progress rm "$TEST_DIR/t.$IMGFMT" > /dev/null
;;
+ archipelago)
+ vlmc remove "at.$IMGFMT" > /dev/null
+ ;;
+
sheepdog)
collie vdi delete "$TEST_DIR/t.$IMGFMT"
;;
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
index 6e67f6126..7dfe46940 100644
--- a/tests/qemu-iotests/group
+++ b/tests/qemu-iotests/group
@@ -67,7 +67,7 @@
058 rw auto quick
059 rw auto quick
060 rw auto quick
-061 rw auto quick
+061 rw auto
062 rw auto quick
063 rw auto quick
064 rw auto quick
@@ -100,3 +100,15 @@
091 rw auto quick
092 rw auto quick
095 rw auto quick
+097 rw auto backing
+098 rw auto backing quick
+099 rw auto quick
+100 rw auto quick
+101 rw auto quick
+102 rw auto quick
+103 rw auto quick
+104 rw auto
+105 rw auto quick
+107 rw auto quick
+108 rw auto quick
+111 rw auto quick
diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
index 39a4cfcf4..f57f1548a 100644
--- a/tests/qemu-iotests/iotests.py
+++ b/tests/qemu-iotests/iotests.py
@@ -267,8 +267,7 @@ class QMPTestCase(unittest.TestCase):
self.assert_qmp(event, 'data/device', drive)
self.assert_qmp_absent(event, 'data/error')
if check_offset:
- self.assert_qmp(event, 'data/offset', self.image_len)
- self.assert_qmp(event, 'data/len', self.image_len)
+ self.assert_qmp(event, 'data/offset', event['data']['len'])
completed = True
self.assert_no_active_block_jobs()
diff --git a/tests/qemu-iotests/sample_images/fake.parallels.bz2 b/tests/qemu-iotests/sample_images/fake.parallels.bz2
deleted file mode 100644
index ffb5f13ba..000000000
--- a/tests/qemu-iotests/sample_images/fake.parallels.bz2
+++ /dev/null
Binary files differ
diff --git a/tests/qemu-iotests/sample_images/iotest-version3.vmdk.bz2 b/tests/qemu-iotests/sample_images/iotest-version3.vmdk.bz2
index 30abf217e..619329a24 100644
--- a/tests/qemu-iotests/sample_images/iotest-version3.vmdk.bz2
+++ b/tests/qemu-iotests/sample_images/iotest-version3.vmdk.bz2
Binary files differ
diff --git a/tests/qemu-iotests/sample_images/parallels-v1.bz2 b/tests/qemu-iotests/sample_images/parallels-v1.bz2
new file mode 100644
index 000000000..d1ef14205
--- /dev/null
+++ b/tests/qemu-iotests/sample_images/parallels-v1.bz2
Binary files differ
diff --git a/tests/qemu-iotests/sample_images/parallels-v2.bz2 b/tests/qemu-iotests/sample_images/parallels-v2.bz2
new file mode 100644
index 000000000..fd8614d06
--- /dev/null
+++ b/tests/qemu-iotests/sample_images/parallels-v2.bz2
Binary files differ
diff --git a/tests/qemu-iotests/socket_scm_helper.c b/tests/qemu-iotests/socket_scm_helper.c
index 0e2b2859a..81959835e 100644
--- a/tests/qemu-iotests/socket_scm_helper.c
+++ b/tests/qemu-iotests/socket_scm_helper.c
@@ -52,7 +52,7 @@ static int send_fd(int fd, int fd_to_send)
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
- memcpy(CMSG_DATA(cmsg), &fd, sizeof(int));
+ memcpy(CMSG_DATA(cmsg), &fd_to_send, sizeof(int));
do {
ret = sendmsg(fd, &msg, 0);
diff --git a/tests/tcg/xtensa/Makefile b/tests/tcg/xtensa/Makefile
index a70c92be7..522a63e36 100644
--- a/tests/tcg/xtensa/Makefile
+++ b/tests/tcg/xtensa/Makefile
@@ -13,6 +13,7 @@ SIMFLAGS = --xtensa-core=DC_B_232L --exit_with_target_code $(EXTFLAGS)
SIMDEBUG = --gdbserve=0
endif
+HOST_CC = gcc
CC = $(CROSS)gcc
AS = $(CROSS)gcc -x assembler-with-cpp
LD = $(CROSS)ld
@@ -21,7 +22,7 @@ XTENSA_SRC_PATH = $(SRC_PATH)/tests/tcg/xtensa
INCLUDE_DIRS = $(XTENSA_SRC_PATH) $(SRC_PATH)/target-xtensa/core-$(CORE)
XTENSA_INC = $(addprefix -I,$(INCLUDE_DIRS))
-LDFLAGS = -T$(XTENSA_SRC_PATH)/linker.ld
+LDFLAGS = -Tlinker.ld
CRT = crt.o vectors.o
@@ -59,13 +60,16 @@ TESTCASES += test_windowed.tst
all: build
+linker.ld: $(XTENSA_SRC_PATH)/linker.ld.S
+ $(HOST_CC) $(XTENSA_INC) -E -P $< -o $@
+
%.o: $(XTENSA_SRC_PATH)/%.c
$(CC) $(XTENSA_INC) $(CFLAGS) -c $< -o $@
%.o: $(XTENSA_SRC_PATH)/%.S
$(CC) $(XTENSA_INC) $(ASFLAGS) -c $< -o $@
-%.tst: %.o $(XTENSA_SRC_PATH)/macros.inc $(CRT) Makefile
+%.tst: %.o linker.ld $(XTENSA_SRC_PATH)/macros.inc $(CRT) Makefile
$(LD) $(LDFLAGS) $(NOSTDFLAGS) $(CRT) $< -o $@
build: $(TESTCASES)
@@ -85,4 +89,4 @@ host-debug-%.tst: %.tst
gdb --args $(SIM) $(SIMFLAGS) ./$<
clean:
- $(RM) -fr $(TESTCASES) $(CRT)
+ $(RM) -fr $(TESTCASES) $(CRT) linker.ld
diff --git a/tests/tcg/xtensa/linker.ld b/tests/tcg/xtensa/linker.ld
deleted file mode 100644
index 4d0b307fd..000000000
--- a/tests/tcg/xtensa/linker.ld
+++ /dev/null
@@ -1,112 +0,0 @@
-OUTPUT_FORMAT("elf32-xtensa-le")
-ENTRY(_start)
-
-__DYNAMIC = 0;
-
-MEMORY {
- ram : ORIGIN = 0xd0000000, LENGTH = 0x08000000 /* 128M */
- rom : ORIGIN = 0xfe000000, LENGTH = 0x00001000 /* 4k */
-}
-
-SECTIONS
-{
- .init :
- {
- *(.init)
- *(.init.*)
- } > rom
-
- .vector :
- {
- . = 0x00000000;
- *(.vector.window_overflow_4)
- *(.vector.window_overflow_4.*)
- . = 0x00000040;
- *(.vector.window_underflow_4)
- *(.vector.window_underflow_4.*)
- . = 0x00000080;
- *(.vector.window_overflow_8)
- *(.vector.window_overflow_8.*)
- . = 0x000000c0;
- *(.vector.window_underflow_8)
- *(.vector.window_underflow_8.*)
- . = 0x00000100;
- *(.vector.window_overflow_12)
- *(.vector.window_overflow_12.*)
- . = 0x00000140;
- *(.vector.window_underflow_12)
- *(.vector.window_underflow_12.*)
-
- . = 0x00000180;
- *(.vector.level2)
- *(.vector.level2.*)
- . = 0x000001c0;
- *(.vector.level3)
- *(.vector.level3.*)
- . = 0x00000200;
- *(.vector.level4)
- *(.vector.level4.*)
- . = 0x00000240;
- *(.vector.level5)
- *(.vector.level5.*)
- . = 0x00000280;
- *(.vector.level6)
- *(.vector.level6.*)
- . = 0x000002c0;
- *(.vector.level7)
- *(.vector.level7.*)
-
- . = 0x00000300;
- *(.vector.kernel)
- *(.vector.kernel.*)
- . = 0x00000340;
- *(.vector.user)
- *(.vector.user.*)
- . = 0x000003c0;
- *(.vector.double)
- *(.vector.double.*)
- } > ram
-
- .text :
- {
- _ftext = .;
- *(.text .stub .text.* .gnu.linkonce.t.* .literal .literal.*)
- _etext = .;
- } > ram
-
- .rodata :
- {
- . = ALIGN(4);
- _frodata = .;
- *(.rodata .rodata.* .gnu.linkonce.r.*)
- *(.rodata1)
- _erodata = .;
- } > ram
-
- .data :
- {
- . = ALIGN(4);
- _fdata = .;
- *(.data .data.* .gnu.linkonce.d.*)
- *(.data1)
- _gp = ALIGN(16);
- *(.sdata .sdata.* .gnu.linkonce.s.*)
- _edata = .;
- } > ram
-
- .bss :
- {
- . = ALIGN(4);
- _fbss = .;
- *(.dynsbss)
- *(.sbss .sbss.* .gnu.linkonce.sb.*)
- *(.scommon)
- *(.dynbss)
- *(.bss .bss.* .gnu.linkonce.b.*)
- *(COMMON)
- _ebss = .;
- _end = .;
- } > ram
-}
-
-PROVIDE(_fstack = ORIGIN(ram) + LENGTH(ram) - 4);
diff --git a/tests/tcg/xtensa/linker.ld.S b/tests/tcg/xtensa/linker.ld.S
new file mode 100644
index 000000000..f1e7fa9f8
--- /dev/null
+++ b/tests/tcg/xtensa/linker.ld.S
@@ -0,0 +1,130 @@
+#include <core-isa.h>
+
+#if XTENSA_HAVE_BE
+OUTPUT_FORMAT("elf32-xtensa-be")
+#else
+OUTPUT_FORMAT("elf32-xtensa-le")
+#endif
+ENTRY(_start)
+
+__DYNAMIC = 0;
+
+MEMORY {
+ ram : ORIGIN = XCHAL_VECBASE_RESET_VADDR, LENGTH = 0x08000000 /* 128M */
+ rom : ORIGIN = XCHAL_RESET_VECTOR_VADDR, LENGTH = 0x00001000 /* 4k */
+}
+
+SECTIONS
+{
+ .init :
+ {
+ *(.init)
+ *(.init.*)
+ } > rom
+
+ .vector :
+ {
+ . = XCHAL_WINDOW_OF4_VECOFS;
+ *(.vector.window_overflow_4)
+ *(.vector.window_overflow_4.*)
+ . = XCHAL_WINDOW_UF4_VECOFS;
+ *(.vector.window_underflow_4)
+ *(.vector.window_underflow_4.*)
+ . = XCHAL_WINDOW_OF8_VECOFS;
+ *(.vector.window_overflow_8)
+ *(.vector.window_overflow_8.*)
+ . = XCHAL_WINDOW_UF8_VECOFS;
+ *(.vector.window_underflow_8)
+ *(.vector.window_underflow_8.*)
+ . = XCHAL_WINDOW_OF12_VECOFS;
+ *(.vector.window_overflow_12)
+ *(.vector.window_overflow_12.*)
+ . = XCHAL_WINDOW_UF12_VECOFS;
+ *(.vector.window_underflow_12)
+ *(.vector.window_underflow_12.*)
+
+#if XCHAL_NUM_INTLEVELS + XCHAL_HAVE_NMI >= 2
+ . = XCHAL_INTLEVEL2_VECOFS;
+ *(.vector.level2)
+ *(.vector.level2.*)
+#endif
+#if XCHAL_NUM_INTLEVELS + XCHAL_HAVE_NMI >= 3
+ . = XCHAL_INTLEVEL3_VECOFS;
+ *(.vector.level3)
+ *(.vector.level3.*)
+#endif
+#if XCHAL_NUM_INTLEVELS + XCHAL_HAVE_NMI >= 4
+ . = XCHAL_INTLEVEL4_VECOFS;
+ *(.vector.level4)
+ *(.vector.level4.*)
+#endif
+#if XCHAL_NUM_INTLEVELS + XCHAL_HAVE_NMI >= 5
+ . = XCHAL_INTLEVEL5_VECOFS;
+ *(.vector.level5)
+ *(.vector.level5.*)
+#endif
+#if XCHAL_NUM_INTLEVELS + XCHAL_HAVE_NMI >= 6
+ . = XCHAL_INTLEVEL6_VECOFS;
+ *(.vector.level6)
+ *(.vector.level6.*)
+#endif
+#if XCHAL_NUM_INTLEVELS + XCHAL_HAVE_NMI >= 7
+ . = XCHAL_INTLEVEL7_VECOFS;
+ *(.vector.level7)
+ *(.vector.level7.*)
+#endif
+
+ . = XCHAL_KERNEL_VECOFS;
+ *(.vector.kernel)
+ *(.vector.kernel.*)
+ . = XCHAL_USER_VECOFS;
+ *(.vector.user)
+ *(.vector.user.*)
+ . = XCHAL_DOUBLEEXC_VECOFS;
+ *(.vector.double)
+ *(.vector.double.*)
+ } > ram
+
+ .text :
+ {
+ _ftext = .;
+ *(.text .stub .text.* .gnu.linkonce.t.* .literal .literal.*)
+ _etext = .;
+ } > ram
+
+ .rodata :
+ {
+ . = ALIGN(4);
+ _frodata = .;
+ *(.rodata .rodata.* .gnu.linkonce.r.*)
+ *(.rodata1)
+ _erodata = .;
+ } > ram
+
+ .data :
+ {
+ . = ALIGN(4);
+ _fdata = .;
+ *(.data .data.* .gnu.linkonce.d.*)
+ *(.data1)
+ _gp = ALIGN(16);
+ *(.sdata .sdata.* .gnu.linkonce.s.*)
+ _edata = .;
+ } > ram
+
+ .bss :
+ {
+ . = ALIGN(4);
+ _fbss = .;
+ *(.dynsbss)
+ *(.sbss .sbss.* .gnu.linkonce.sb.*)
+ *(.scommon)
+ *(.dynbss)
+ *(.bss .bss.* .gnu.linkonce.b.*)
+ *(COMMON)
+ _ebss = .;
+ _end = .;
+ } > ram
+}
+
+PROVIDE(_fstack = (ORIGIN(ram) & 0xf0000000) + LENGTH(ram) - 16);
diff --git a/tests/tcg/xtensa/test_windowed.S b/tests/tcg/xtensa/test_windowed.S
index 3de6d3763..d851e8f43 100644
--- a/tests/tcg/xtensa/test_windowed.S
+++ b/tests/tcg/xtensa/test_windowed.S
@@ -299,4 +299,55 @@ test entry
entry_test 12
test_end
+.macro entry_overflow_test window, free, next_window
+ set_vector window_overflow_4, 0
+ set_vector window_overflow_8, 0
+ set_vector window_overflow_12, 0
+ set_vector window_overflow_\next_window, 10f
+
+ movi a2, \window
+ movi a2, \free
+ movi a2, \next_window
+ reset_window %(1 | ((1 | (1 << ((\next_window) / 4))) << ((\free) / 4)))
+ reset_ps
+ movi a2, 0x4000f | ((\window) << 14)
+ wsr a2, ps
+ isync
+ movi a3, 0x12345678
+ j 1f
+ .align 4
+1:
+ entry a3, 0x5678
+ test_fail
+ .align 4
+10:
+ rsr a2, epc1
+ movi a3, 1b
+ assert eq, a2, a3
+ movi a2, 2f
+ wsr a2, epc1
+
+ rsr a2, windowbase
+ movi a3, (\free) / 4
+ assert eq, a2, a3
+ rfwo
+2:
+.endm
+
+.macro all_entry_overflow_tests
+ .irp window, 4, 8, 12
+ .irp next_window, 4, 8, 12
+ .irp free, 4, 8, 12
+ .if \free <= \window
+ entry_overflow_test \window, \free, \next_window
+ .endif
+ .endr
+ .endr
+ .endr
+.endm
+
+test entry_overflow
+ all_entry_overflow_tests
+test_end
+
test_suite_end
diff --git a/tests/test-aio.c b/tests/test-aio.c
index f12b6e0ae..a7cb5c991 100644
--- a/tests/test-aio.c
+++ b/tests/test-aio.c
@@ -14,6 +14,7 @@
#include "block/aio.h"
#include "qemu/timer.h"
#include "qemu/sockets.h"
+#include "qemu/error-report.h"
static AioContext *ctx;
@@ -57,8 +58,6 @@ static void bh_test_cb(void *opaque)
}
}
-#if !defined(_WIN32)
-
static void timer_test_cb(void *opaque)
{
TimerTestData *data = opaque;
@@ -68,12 +67,10 @@ static void timer_test_cb(void *opaque)
}
}
-static void dummy_io_handler_read(void *opaque)
+static void dummy_io_handler_read(EventNotifier *e)
{
}
-#endif /* !_WIN32 */
-
static void bh_delete_cb(void *opaque)
{
BHTestData *data = opaque;
@@ -428,24 +425,18 @@ static void test_wait_event_notifier_noflush(void)
event_notifier_cleanup(&data.e);
}
-#if !defined(_WIN32)
-
static void test_timer_schedule(void)
{
TimerTestData data = { .n = 0, .ctx = ctx, .ns = SCALE_MS * 750LL,
.max = 2,
.clock_type = QEMU_CLOCK_VIRTUAL };
- int pipefd[2];
+ EventNotifier e;
/* aio_poll will not block to wait for timers to complete unless it has
* an fd to wait on. Fixing this breaks other tests. So create a dummy one.
*/
- g_assert(!qemu_pipe(pipefd));
- qemu_set_nonblock(pipefd[0]);
- qemu_set_nonblock(pipefd[1]);
-
- aio_set_fd_handler(ctx, pipefd[0],
- dummy_io_handler_read, NULL, NULL);
+ event_notifier_init(&e, false);
+ aio_set_event_notifier(ctx, &e, dummy_io_handler_read);
aio_poll(ctx, false);
aio_timer_init(ctx, &data.timer, data.clock_type,
@@ -484,15 +475,12 @@ static void test_timer_schedule(void)
g_assert(!aio_poll(ctx, false));
g_assert_cmpint(data.n, ==, 2);
- aio_set_fd_handler(ctx, pipefd[0], NULL, NULL, NULL);
- close(pipefd[0]);
- close(pipefd[1]);
+ aio_set_event_notifier(ctx, &e, NULL);
+ event_notifier_cleanup(&e);
timer_del(&data.timer);
}
-#endif /* !_WIN32 */
-
/* Now the same tests, using the context as a GSource. They are
* very similar to the ones above, with g_main_context_iteration
* replacing aio_poll. However:
@@ -775,25 +763,19 @@ static void test_source_wait_event_notifier_noflush(void)
event_notifier_cleanup(&data.e);
}
-#if !defined(_WIN32)
-
static void test_source_timer_schedule(void)
{
TimerTestData data = { .n = 0, .ctx = ctx, .ns = SCALE_MS * 750LL,
.max = 2,
.clock_type = QEMU_CLOCK_VIRTUAL };
- int pipefd[2];
+ EventNotifier e;
int64_t expiry;
/* aio_poll will not block to wait for timers to complete unless it has
* an fd to wait on. Fixing this breaks other tests. So create a dummy one.
*/
- g_assert(!qemu_pipe(pipefd));
- qemu_set_nonblock(pipefd[0]);
- qemu_set_nonblock(pipefd[1]);
-
- aio_set_fd_handler(ctx, pipefd[0],
- dummy_io_handler_read, NULL, NULL);
+ event_notifier_init(&e, false);
+ aio_set_event_notifier(ctx, &e, dummy_io_handler_read);
do {} while (g_main_context_iteration(NULL, false));
aio_timer_init(ctx, &data.timer, data.clock_type,
@@ -818,25 +800,29 @@ static void test_source_timer_schedule(void)
g_assert_cmpint(data.n, ==, 2);
g_assert(qemu_clock_get_ns(data.clock_type) > expiry);
- aio_set_fd_handler(ctx, pipefd[0], NULL, NULL, NULL);
- close(pipefd[0]);
- close(pipefd[1]);
+ aio_set_event_notifier(ctx, &e, NULL);
+ event_notifier_cleanup(&e);
timer_del(&data.timer);
}
-#endif /* !_WIN32 */
-
/* End of tests. */
int main(int argc, char **argv)
{
+ Error *local_error = NULL;
GSource *src;
init_clocks();
- ctx = aio_context_new();
+ ctx = aio_context_new(&local_error);
+ if (!ctx) {
+ error_report("Failed to create AIO Context: '%s'",
+ error_get_pretty(local_error));
+ error_free(local_error);
+ exit(1);
+ }
src = aio_get_g_source(ctx);
g_source_attach(src, NULL);
g_source_unref(src);
@@ -857,9 +843,7 @@ int main(int argc, char **argv)
g_test_add_func("/aio/event/wait", test_wait_event_notifier);
g_test_add_func("/aio/event/wait/no-flush-cb", test_wait_event_notifier_noflush);
g_test_add_func("/aio/event/flush", test_flush_event_notifier);
-#if !defined(_WIN32)
g_test_add_func("/aio/timer/schedule", test_timer_schedule);
-#endif
g_test_add_func("/aio-gsource/notify", test_source_notify);
g_test_add_func("/aio-gsource/flush", test_source_flush);
@@ -874,8 +858,6 @@ int main(int argc, char **argv)
g_test_add_func("/aio-gsource/event/wait", test_source_wait_event_notifier);
g_test_add_func("/aio-gsource/event/wait/no-flush-cb", test_source_wait_event_notifier_noflush);
g_test_add_func("/aio-gsource/event/flush", test_source_flush_event_notifier);
-#if !defined(_WIN32)
g_test_add_func("/aio-gsource/timer/schedule", test_source_timer_schedule);
-#endif
return g_test_run();
}
diff --git a/tests/test-bitops.c b/tests/test-bitops.c
index 8238eb5f6..47b5d3ed9 100644
--- a/tests/test-bitops.c
+++ b/tests/test-bitops.c
@@ -8,6 +8,7 @@
#include <glib.h>
#include <stdint.h>
+#include "qemu/osdep.h"
#include "qemu/bitops.h"
typedef struct {
diff --git a/tests/test-coroutine.c b/tests/test-coroutine.c
index 760636ddc..e22fae170 100644
--- a/tests/test-coroutine.c
+++ b/tests/test-coroutine.c
@@ -288,6 +288,58 @@ static void perf_yield(void)
maxcycles, duration);
}
+static __attribute__((noinline)) void dummy(unsigned *i)
+{
+ (*i)--;
+}
+
+static void perf_baseline(void)
+{
+ unsigned int i, maxcycles;
+ double duration;
+
+ maxcycles = 100000000;
+ i = maxcycles;
+
+ g_test_timer_start();
+ while (i > 0) {
+ dummy(&i);
+ }
+ duration = g_test_timer_elapsed();
+
+ g_test_message("Function call %u iterations: %f s\n",
+ maxcycles, duration);
+}
+
+static __attribute__((noinline)) void perf_cost_func(void *opaque)
+{
+ qemu_coroutine_yield();
+}
+
+static void perf_cost(void)
+{
+ const unsigned long maxcycles = 40000000;
+ unsigned long i = 0;
+ double duration;
+ unsigned long ops;
+ Coroutine *co;
+
+ g_test_timer_start();
+ while (i++ < maxcycles) {
+ co = qemu_coroutine_create(perf_cost_func);
+ qemu_coroutine_enter(co, &i);
+ qemu_coroutine_enter(co, NULL);
+ }
+ duration = g_test_timer_elapsed();
+ ops = (long)(maxcycles / (duration * 1000));
+
+ g_test_message("Run operation %lu iterations %f s, %luK operations/s, "
+ "%luns per coroutine",
+ maxcycles,
+ duration, ops,
+ (unsigned long)(1000000000 * duration) / maxcycles);
+}
+
int main(int argc, char **argv)
{
g_test_init(&argc, &argv, NULL);
@@ -301,6 +353,8 @@ int main(int argc, char **argv)
g_test_add_func("/perf/lifecycle", perf_lifecycle);
g_test_add_func("/perf/nesting", perf_nesting);
g_test_add_func("/perf/yield", perf_yield);
+ g_test_add_func("/perf/function-call", perf_baseline);
+ g_test_add_func("/perf/cost", perf_cost);
}
return g_test_run();
}
diff --git a/tests/test-qdev-global-props.c b/tests/test-qdev-global-props.c
index 2bef04c76..0be98355c 100644
--- a/tests/test-qdev-global-props.c
+++ b/tests/test-qdev-global-props.c
@@ -65,7 +65,7 @@ static const TypeInfo static_prop_type = {
};
/* Test simple static property setting to default value */
-static void test_static_prop(void)
+static void test_static_prop_subprocess(void)
{
MyType *mt;
@@ -75,8 +75,16 @@ static void test_static_prop(void)
g_assert_cmpuint(mt->prop1, ==, PROP_DEFAULT);
}
+static void test_static_prop(void)
+{
+ g_test_trap_subprocess("/qdev/properties/static/default/subprocess", 0, 0);
+ g_test_trap_assert_passed();
+ g_test_trap_assert_stderr("");
+ g_test_trap_assert_stdout("");
+}
+
/* Test setting of static property using global properties */
-static void test_static_globalprop(void)
+static void test_static_globalprop_subprocess(void)
{
MyType *mt;
static GlobalProperty props[] = {
@@ -93,10 +101,21 @@ static void test_static_globalprop(void)
g_assert_cmpuint(mt->prop2, ==, PROP_DEFAULT);
}
+static void test_static_globalprop(void)
+{
+ g_test_trap_subprocess("/qdev/properties/static/global/subprocess", 0, 0);
+ g_test_trap_assert_passed();
+ g_test_trap_assert_stderr("");
+ g_test_trap_assert_stdout("");
+}
+
#define TYPE_DYNAMIC_PROPS "dynamic-prop-type"
#define DYNAMIC_TYPE(obj) \
OBJECT_CHECK(MyType, (obj), TYPE_DYNAMIC_PROPS)
+#define TYPE_UNUSED_HOTPLUG "hotplug-type"
+#define TYPE_UNUSED_NOHOTPLUG "nohotplug-type"
+
static void prop1_accessor(Object *obj,
Visitor *v,
void *opaque,
@@ -143,14 +162,101 @@ static const TypeInfo dynamic_prop_type = {
.class_init = dynamic_class_init,
};
-/* Test setting of static property using global properties */
+static void hotplug_class_init(ObjectClass *oc, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(oc);
+
+ dc->realize = NULL;
+ dc->hotpluggable = true;
+}
+
+static const TypeInfo hotplug_type = {
+ .name = TYPE_UNUSED_HOTPLUG,
+ .parent = TYPE_DEVICE,
+ .instance_size = sizeof(MyType),
+ .instance_init = dynamic_instance_init,
+ .class_init = hotplug_class_init,
+};
+
+static void nohotplug_class_init(ObjectClass *oc, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(oc);
+
+ dc->realize = NULL;
+ dc->hotpluggable = false;
+}
+
+static const TypeInfo nohotplug_type = {
+ .name = TYPE_UNUSED_NOHOTPLUG,
+ .parent = TYPE_DEVICE,
+ .instance_size = sizeof(MyType),
+ .instance_init = dynamic_instance_init,
+ .class_init = nohotplug_class_init,
+};
+
+#define TYPE_NONDEVICE "nondevice-type"
+
+static const TypeInfo nondevice_type = {
+ .name = TYPE_NONDEVICE,
+ .parent = TYPE_OBJECT,
+};
+
+/* Test setting of dynamic properties using global properties */
+static void test_dynamic_globalprop_subprocess(void)
+{
+ MyType *mt;
+ static GlobalProperty props[] = {
+ { TYPE_DYNAMIC_PROPS, "prop1", "101", true },
+ { TYPE_DYNAMIC_PROPS, "prop2", "102", true },
+ { TYPE_DYNAMIC_PROPS"-bad", "prop3", "103", true },
+ { TYPE_UNUSED_HOTPLUG, "prop4", "104", true },
+ { TYPE_UNUSED_NOHOTPLUG, "prop5", "105", true },
+ { TYPE_NONDEVICE, "prop6", "106", true },
+ {}
+ };
+ int all_used;
+
+ qdev_prop_register_global_list(props);
+
+ mt = DYNAMIC_TYPE(object_new(TYPE_DYNAMIC_PROPS));
+ qdev_init_nofail(DEVICE(mt));
+
+ g_assert_cmpuint(mt->prop1, ==, 101);
+ g_assert_cmpuint(mt->prop2, ==, 102);
+ all_used = qdev_prop_check_globals();
+ g_assert_cmpuint(all_used, ==, 1);
+ g_assert(props[0].used);
+ g_assert(props[1].used);
+ g_assert(!props[2].used);
+ g_assert(!props[3].used);
+ g_assert(!props[4].used);
+ g_assert(!props[5].used);
+}
+
static void test_dynamic_globalprop(void)
{
+ g_test_trap_subprocess("/qdev/properties/dynamic/global/subprocess", 0, 0);
+ g_test_trap_assert_passed();
+ g_test_trap_assert_stderr_unmatched("*prop1*");
+ g_test_trap_assert_stderr_unmatched("*prop2*");
+ g_test_trap_assert_stderr("*Warning: global dynamic-prop-type-bad.prop3 has invalid class name\n*");
+ g_test_trap_assert_stderr_unmatched("*prop4*");
+ g_test_trap_assert_stderr("*Warning: global nohotplug-type.prop5=105 not used\n*");
+ g_test_trap_assert_stderr("*Warning: global nondevice-type.prop6 has invalid class name\n*");
+ g_test_trap_assert_stdout("");
+}
+
+/* Test setting of dynamic properties using user_provided=false properties */
+static void test_dynamic_globalprop_nouser_subprocess(void)
+{
MyType *mt;
static GlobalProperty props[] = {
{ TYPE_DYNAMIC_PROPS, "prop1", "101" },
{ TYPE_DYNAMIC_PROPS, "prop2", "102" },
- { TYPE_DYNAMIC_PROPS"-bad", "prop3", "103", true },
+ { TYPE_DYNAMIC_PROPS"-bad", "prop3", "103" },
+ { TYPE_UNUSED_HOTPLUG, "prop4", "104" },
+ { TYPE_UNUSED_NOHOTPLUG, "prop5", "105" },
+ { TYPE_NONDEVICE, "prop6", "106" },
{}
};
int all_used;
@@ -162,8 +268,22 @@ static void test_dynamic_globalprop(void)
g_assert_cmpuint(mt->prop1, ==, 101);
g_assert_cmpuint(mt->prop2, ==, 102);
- all_used = qdev_prop_check_global();
- g_assert_cmpuint(all_used, ==, 1);
+ all_used = qdev_prop_check_globals();
+ g_assert_cmpuint(all_used, ==, 0);
+ g_assert(props[0].used);
+ g_assert(props[1].used);
+ g_assert(!props[2].used);
+ g_assert(!props[3].used);
+ g_assert(!props[4].used);
+ g_assert(!props[5].used);
+}
+
+static void test_dynamic_globalprop_nouser(void)
+{
+ g_test_trap_subprocess("/qdev/properties/dynamic/global/nouser/subprocess", 0, 0);
+ g_test_trap_assert_passed();
+ g_test_trap_assert_stderr("");
+ g_test_trap_assert_stdout("");
}
int main(int argc, char **argv)
@@ -173,10 +293,29 @@ int main(int argc, char **argv)
module_call_init(MODULE_INIT_QOM);
type_register_static(&static_prop_type);
type_register_static(&dynamic_prop_type);
-
- g_test_add_func("/qdev/properties/static/default", test_static_prop);
- g_test_add_func("/qdev/properties/static/global", test_static_globalprop);
- g_test_add_func("/qdev/properties/dynamic/global", test_dynamic_globalprop);
+ type_register_static(&hotplug_type);
+ type_register_static(&nohotplug_type);
+ type_register_static(&nondevice_type);
+
+ g_test_add_func("/qdev/properties/static/default/subprocess",
+ test_static_prop_subprocess);
+ g_test_add_func("/qdev/properties/static/default",
+ test_static_prop);
+
+ g_test_add_func("/qdev/properties/static/global/subprocess",
+ test_static_globalprop_subprocess);
+ g_test_add_func("/qdev/properties/static/global",
+ test_static_globalprop);
+
+ g_test_add_func("/qdev/properties/dynamic/global/subprocess",
+ test_dynamic_globalprop_subprocess);
+ g_test_add_func("/qdev/properties/dynamic/global",
+ test_dynamic_globalprop);
+
+ g_test_add_func("/qdev/properties/dynamic/global/nouser/subprocess",
+ test_dynamic_globalprop_nouser_subprocess);
+ g_test_add_func("/qdev/properties/dynamic/global/nouser",
+ test_dynamic_globalprop_nouser);
g_test_run();
diff --git a/tests/test-qmp-input-strict.c b/tests/test-qmp-input-strict.c
index 0f770034b..d5360c6a8 100644
--- a/tests/test-qmp-input-strict.c
+++ b/tests/test-qmp-input-strict.c
@@ -260,6 +260,21 @@ static void test_validate_fail_union_flat(TestInputVisitorData *data,
qapi_free_UserDefFlatUnion(tmp);
}
+static void test_validate_fail_union_flat_no_discrim(TestInputVisitorData *data,
+ const void *unused)
+{
+ UserDefFlatUnion2 *tmp = NULL;
+ Error *err = NULL;
+ Visitor *v;
+
+ /* test situation where discriminator field ('enum1' here) is missing */
+ v = validate_test_init(data, "{ 'string': 'c', 'string1': 'd', 'string2': 'e' }");
+
+ visit_type_UserDefFlatUnion2(v, &tmp, NULL, &err);
+ g_assert(err);
+ qapi_free_UserDefFlatUnion2(tmp);
+}
+
static void test_validate_fail_union_anon(TestInputVisitorData *data,
const void *unused)
{
@@ -310,6 +325,8 @@ int main(int argc, char **argv)
&testdata, test_validate_fail_union);
validate_test_add("/visitor/input-strict/fail/union-flat",
&testdata, test_validate_fail_union_flat);
+ validate_test_add("/visitor/input-strict/fail/union-flat-no-discriminator",
+ &testdata, test_validate_fail_union_flat_no_discrim);
validate_test_add("/visitor/input-strict/fail/union-anon",
&testdata, test_validate_fail_union_anon);
diff --git a/tests/test-thread-pool.c b/tests/test-thread-pool.c
index f40b7fc17..6a0b9813f 100644
--- a/tests/test-thread-pool.c
+++ b/tests/test-thread-pool.c
@@ -4,13 +4,14 @@
#include "block/thread-pool.h"
#include "block/block.h"
#include "qemu/timer.h"
+#include "qemu/error-report.h"
static AioContext *ctx;
static ThreadPool *pool;
static int active;
typedef struct {
- BlockDriverAIOCB *aiocb;
+ BlockAIOCB *aiocb;
int n;
int ret;
} WorkerTestData;
@@ -33,7 +34,7 @@ static int long_cb(void *opaque)
static void done_cb(void *opaque, int ret)
{
WorkerTestData *data = opaque;
- g_assert_cmpint(data->ret, ==, -EINPROGRESS);
+ g_assert(data->ret == -EINPROGRESS || data->ret == -ECANCELED);
data->ret = ret;
data->aiocb = NULL;
@@ -132,7 +133,7 @@ static void test_submit_many(void)
}
}
-static void test_cancel(void)
+static void do_test_cancel(bool sync)
{
WorkerTestData data[100];
int num_canceled;
@@ -170,18 +171,25 @@ static void test_cancel(void)
for (i = 0; i < 100; i++) {
if (atomic_cmpxchg(&data[i].n, 0, 3) == 0) {
data[i].ret = -ECANCELED;
- bdrv_aio_cancel(data[i].aiocb);
- active--;
+ if (sync) {
+ bdrv_aio_cancel(data[i].aiocb);
+ } else {
+ bdrv_aio_cancel_async(data[i].aiocb);
+ }
num_canceled++;
}
}
g_assert_cmpint(active, >, 0);
g_assert_cmpint(num_canceled, <, 100);
- /* Canceling the others will be a blocking operation. */
for (i = 0; i < 100; i++) {
if (data[i].aiocb && data[i].n != 3) {
- bdrv_aio_cancel(data[i].aiocb);
+ if (sync) {
+ /* Canceling the others will be a blocking operation. */
+ bdrv_aio_cancel(data[i].aiocb);
+ } else {
+ bdrv_aio_cancel_async(data[i].aiocb);
+ }
}
}
@@ -193,22 +201,39 @@ static void test_cancel(void)
for (i = 0; i < 100; i++) {
if (data[i].n == 3) {
g_assert_cmpint(data[i].ret, ==, -ECANCELED);
- g_assert(data[i].aiocb != NULL);
+ g_assert(data[i].aiocb == NULL);
} else {
g_assert_cmpint(data[i].n, ==, 2);
- g_assert_cmpint(data[i].ret, ==, 0);
+ g_assert(data[i].ret == 0 || data[i].ret == -ECANCELED);
g_assert(data[i].aiocb == NULL);
}
}
}
+static void test_cancel(void)
+{
+ do_test_cancel(true);
+}
+
+static void test_cancel_async(void)
+{
+ do_test_cancel(false);
+}
+
int main(int argc, char **argv)
{
int ret;
+ Error *local_error = NULL;
init_clocks();
- ctx = aio_context_new();
+ ctx = aio_context_new(&local_error);
+ if (!ctx) {
+ error_report("Failed to create AIO Context: '%s'",
+ error_get_pretty(local_error));
+ error_free(local_error);
+ exit(1);
+ }
pool = aio_get_thread_pool(ctx);
g_test_init(&argc, &argv, NULL);
@@ -217,6 +242,7 @@ int main(int argc, char **argv)
g_test_add_func("/thread-pool/submit-co", test_submit_co);
g_test_add_func("/thread-pool/submit-many", test_submit_many);
g_test_add_func("/thread-pool/cancel", test_cancel);
+ g_test_add_func("/thread-pool/cancel-async", test_cancel_async);
ret = g_test_run();
diff --git a/tests/test-throttle.c b/tests/test-throttle.c
index 000ae31af..d8ba415e4 100644
--- a/tests/test-throttle.c
+++ b/tests/test-throttle.c
@@ -14,6 +14,7 @@
#include <math.h>
#include "block/aio.h"
#include "qemu/throttle.h"
+#include "qemu/error-report.h"
static AioContext *ctx;
static LeakyBucket bkt;
@@ -492,10 +493,17 @@ static void test_accounting(void)
int main(int argc, char **argv)
{
GSource *src;
+ Error *local_error = NULL;
init_clocks();
- ctx = aio_context_new();
+ ctx = aio_context_new(&local_error);
+ if (!ctx) {
+ error_report("Failed to create AIO Context: '%s'",
+ error_get_pretty(local_error));
+ error_free(local_error);
+ exit(1);
+ }
src = aio_get_g_source(ctx);
g_source_attach(src, NULL);
g_source_unref(src);
diff --git a/tests/test-vmstate.c b/tests/test-vmstate.c
index d72c64c90..5e0fd13cc 100644
--- a/tests/test-vmstate.c
+++ b/tests/test-vmstate.c
@@ -43,6 +43,12 @@ void yield_until_fd_readable(int fd)
select(fd + 1, &fds, NULL, NULL, NULL);
}
+/*
+ * Some tests use 'open_test_file' to work on a real fd, some use
+ * an in memory file (QEMUSizedBuffer+qemu_bufopen); we could pick one
+ * but this way we test both.
+ */
+
/* Duplicate temp_fd and seek to the beginning of the file */
static QEMUFile *open_test_file(bool write)
{
@@ -54,6 +60,30 @@ static QEMUFile *open_test_file(bool write)
return qemu_fdopen(fd, write ? "wb" : "rb");
}
+/* Open a read-only qemu-file from an existing memory block */
+static QEMUFile *open_mem_file_read(const void *data, size_t len)
+{
+ /* The qsb gets freed by qemu_fclose */
+ QEMUSizedBuffer *qsb = qsb_create(data, len);
+ g_assert(qsb);
+
+ return qemu_bufopen("r", qsb);
+}
+
+/*
+ * Check that the contents of the memory-buffered file f match
+ * the given size/data.
+ */
+static void check_mem_file(QEMUFile *f, void *data, size_t size)
+{
+ uint8_t *result = g_malloc(size);
+ const QEMUSizedBuffer *qsb = qemu_buf_get(f);
+ g_assert_cmpint(qsb_get_length(qsb), ==, size);
+ g_assert_cmpint(qsb_get_buffer(qsb, 0, size, result), ==, size);
+ g_assert_cmpint(memcmp(result, data, size), ==, 0);
+ g_free(result);
+}
+
#define SUCCESS(val) \
g_assert_cmpint((val), ==, 0)
@@ -371,14 +401,12 @@ static const VMStateDescription vmstate_skipping = {
static void test_save_noskip(void)
{
- QEMUFile *fsave = open_test_file(true);
+ QEMUFile *fsave = qemu_bufopen("w", NULL);
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 = open_test_file(false);
uint8_t expected[] = {
0, 0, 0, 1, /* a */
0, 0, 0, 2, /* b */
@@ -387,52 +415,31 @@ static void test_save_noskip(void)
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);
+ check_mem_file(fsave, expected, sizeof(expected));
+ qemu_fclose(fsave);
}
static void test_save_skip(void)
{
- QEMUFile *fsave = open_test_file(true);
+ QEMUFile *fsave = qemu_bufopen("w", NULL);
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 = open_test_file(false);
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);
+ check_mem_file(fsave, expected, sizeof(expected));
- qemu_fclose(loading);
+ qemu_fclose(fsave);
}
static void test_load_noskip(void)
{
- QEMUFile *fsave = open_test_file(true);
uint8_t buf[] = {
0, 0, 0, 10, /* a */
0, 0, 0, 20, /* b */
@@ -442,10 +449,8 @@ static void test_load_noskip(void)
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 = open_test_file(false);
+ QEMUFile *loading = open_mem_file_read(buf, sizeof(buf));
TestStruct obj = { .skip_c_e = false };
vmstate_load_state(loading, &vmstate_skipping, &obj, 2);
g_assert(!qemu_file_get_error(loading));
@@ -460,7 +465,6 @@ static void test_load_noskip(void)
static void test_load_skip(void)
{
- QEMUFile *fsave = open_test_file(true);
uint8_t buf[] = {
0, 0, 0, 10, /* a */
0, 0, 0, 20, /* b */
@@ -468,10 +472,8 @@ static void test_load_skip(void)
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 = open_test_file(false);
+ QEMUFile *loading = open_mem_file_read(buf, sizeof(buf));
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));
diff --git a/tests/usb-hcd-ehci-test.c b/tests/usb-hcd-ehci-test.c
index bcdf62fc3..75073bf24 100644
--- a/tests/usb-hcd-ehci-test.c
+++ b/tests/usb-hcd-ehci-test.c
@@ -15,11 +15,7 @@
#include "qemu/osdep.h"
#include "hw/usb/uhci-regs.h"
#include "hw/usb/ehci-regs.h"
-
-struct qhc {
- QPCIDevice *dev;
- void *base;
-};
+#include "libqos/usb.h"
static QPCIBus *pcibus;
static struct qhc uhci1;
@@ -29,15 +25,6 @@ static struct qhc ehci1;
/* helpers */
-static void pci_init_one(struct qhc *hc, uint32_t devfn, int bar)
-{
- hc->dev = qpci_device_find(pcibus, devfn);
- g_assert(hc->dev != NULL);
- qpci_device_enable(hc->dev);
- hc->base = qpci_iomap(hc->dev, bar);
- g_assert(hc->base != NULL);
-}
-
#if 0
static void uhci_port_update(struct qhc *hc, int port,
uint16_t set, uint16_t clear)
@@ -52,19 +39,6 @@ static void uhci_port_update(struct qhc *hc, int port,
}
#endif
-static void uhci_port_test(struct qhc *hc, int port, uint16_t expect)
-{
- void *addr = hc->base + 0x10 + 2 * port;
- uint16_t value = qpci_io_readw(hc->dev, addr);
- uint16_t mask = ~(UHCI_PORT_WRITE_CLEAR | UHCI_PORT_RSVD1);
-
-#if 0
- fprintf(stderr, "%s: %d, have 0x%04x, want 0x%04x\n",
- __func__, port, value & mask, expect & mask);
-#endif
- g_assert((value & mask) == (expect & mask));
-}
-
static void ehci_port_test(struct qhc *hc, int port, uint32_t expect)
{
void *addr = hc->base + 0x64 + 4 * port;
@@ -88,10 +62,10 @@ static void pci_init(void)
pcibus = qpci_init_pc();
g_assert(pcibus != NULL);
- pci_init_one(&uhci1, QPCI_DEVFN(0x1d, 0), 4);
- pci_init_one(&uhci2, QPCI_DEVFN(0x1d, 1), 4);
- pci_init_one(&uhci3, QPCI_DEVFN(0x1d, 2), 4);
- pci_init_one(&ehci1, QPCI_DEVFN(0x1d, 7), 0);
+ qusb_pci_init_one(pcibus, &uhci1, QPCI_DEVFN(0x1d, 0), 4);
+ qusb_pci_init_one(pcibus, &uhci2, QPCI_DEVFN(0x1d, 1), 4);
+ qusb_pci_init_one(pcibus, &uhci3, QPCI_DEVFN(0x1d, 2), 4);
+ qusb_pci_init_one(pcibus, &ehci1, QPCI_DEVFN(0x1d, 7), 0);
}
static void pci_uhci_port_1(void)
@@ -154,6 +128,19 @@ static void pci_ehci_port_2(void)
}
}
+static void pci_ehci_port_3_hotplug(void)
+{
+ /* check for presence of hotplugged usb-tablet */
+ g_assert(pcibus != NULL);
+ ehci_port_test(&ehci1, 2, PORTSC_PPOWER | PORTSC_CONNECT);
+}
+
+static void pci_ehci_port_hotplug(void)
+{
+ usb_test_hotplug("ich9-ehci-1", 3, pci_ehci_port_3_hotplug);
+}
+
+
int main(int argc, char **argv)
{
int ret;
@@ -165,6 +152,7 @@ int main(int argc, char **argv)
qtest_add_func("/ehci/pci/ehci-config", pci_ehci_config);
qtest_add_func("/ehci/pci/uhci-port-2", pci_uhci_port_2);
qtest_add_func("/ehci/pci/ehci-port-2", pci_ehci_port_2);
+ qtest_add_func("/ehci/pci/ehci-port-3-hotplug", pci_ehci_port_hotplug);
qtest_start("-machine q35 -device ich9-usb-ehci1,bus=pcie.0,addr=1d.7,"
"multifunction=on,id=ich9-ehci-1 "
diff --git a/tests/usb-hcd-ohci-test.c b/tests/usb-hcd-ohci-test.c
new file mode 100644
index 000000000..1160bde84
--- /dev/null
+++ b/tests/usb-hcd-ohci-test.c
@@ -0,0 +1,41 @@
+/*
+ * QTest testcase for USB OHCI controller
+ *
+ * Copyright (c) 2014 HUAWEI TECHNOLOGIES CO.,LTD.
+ *
+ * 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 <glib.h>
+#include <string.h>
+#include "libqtest.h"
+#include "qemu/osdep.h"
+#include "libqos/usb.h"
+
+
+static void test_ohci_init(void)
+{
+
+}
+
+static void test_ohci_hotplug(void)
+{
+ usb_test_hotplug("ohci", 1, NULL);
+}
+
+int main(int argc, char **argv)
+{
+ int ret;
+
+ g_test_init(&argc, &argv, NULL);
+
+ qtest_add_func("/ohci/pci/init", test_ohci_init);
+ qtest_add_func("/ohci/pci/hotplug", test_ohci_hotplug);
+
+ qtest_start("-device pci-ohci,id=ohci");
+ ret = g_test_run();
+ qtest_end();
+
+ return ret;
+}
diff --git a/tests/usb-hcd-uhci-test.c b/tests/usb-hcd-uhci-test.c
new file mode 100644
index 000000000..8cf2c5bca
--- /dev/null
+++ b/tests/usb-hcd-uhci-test.c
@@ -0,0 +1,96 @@
+/*
+ * QTest testcase for USB UHCI controller
+ *
+ * Copyright (c) 2014 HUAWEI TECHNOLOGIES CO.,LTD.
+ *
+ * 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 <glib.h>
+#include <string.h>
+#include "libqtest.h"
+#include "qemu/osdep.h"
+#include "libqos/usb.h"
+#include "hw/usb/uhci-regs.h"
+
+
+static void test_uhci_init(void)
+{
+}
+
+static void test_port(int port)
+{
+ QPCIBus *pcibus;
+ struct qhc uhci;
+
+ g_assert(port > 0);
+ pcibus = qpci_init_pc();
+ g_assert(pcibus != NULL);
+ qusb_pci_init_one(pcibus, &uhci, QPCI_DEVFN(0x1d, 0), 4);
+ uhci_port_test(&uhci, port - 1, UHCI_PORT_CCS);
+}
+
+static void test_port_1(void)
+{
+ test_port(1);
+}
+
+static void test_port_2(void)
+{
+ test_port(2);
+}
+
+static void test_uhci_hotplug(void)
+{
+ usb_test_hotplug("uhci", 2, test_port_2);
+}
+
+static void test_usb_storage_hotplug(void)
+{
+ QDict *response;
+
+ response = qmp("{'execute': 'device_add',"
+ " 'arguments': {"
+ " 'driver': 'usb-storage',"
+ " 'drive': 'drive0',"
+ " 'id': 'usbdev0'"
+ "}}");
+ g_assert(response);
+ g_assert(!qdict_haskey(response, "error"));
+ QDECREF(response);
+
+ response = qmp("{'execute': 'device_del',"
+ " 'arguments': {"
+ " 'id': 'usbdev0'"
+ "}}");
+ g_assert(response);
+ g_assert(!qdict_haskey(response, "error"));
+ QDECREF(response);
+
+ response = qmp("");
+ g_assert(response);
+ g_assert(qdict_haskey(response, "event"));
+ g_assert(!strcmp(qdict_get_str(response, "event"), "DEVICE_DELETED"));
+ QDECREF(response);
+}
+
+int main(int argc, char **argv)
+{
+ int ret;
+
+ g_test_init(&argc, &argv, NULL);
+
+ qtest_add_func("/uhci/pci/init", test_uhci_init);
+ qtest_add_func("/uhci/pci/port1", test_port_1);
+ qtest_add_func("/uhci/pci/hotplug", test_uhci_hotplug);
+ qtest_add_func("/uhci/pci/hotplug/usb-storage", test_usb_storage_hotplug);
+
+ qtest_start("-device piix3-usb-uhci,id=uhci,addr=1d.0"
+ " -drive id=drive0,if=none,file=/dev/null"
+ " -device usb-tablet,bus=uhci.0,port=1");
+ ret = g_test_run();
+ qtest_end();
+
+ return ret;
+}
diff --git a/tests/usb-hcd-xhci-test.c b/tests/usb-hcd-xhci-test.c
new file mode 100644
index 000000000..b1a7dec5b
--- /dev/null
+++ b/tests/usb-hcd-xhci-test.c
@@ -0,0 +1,99 @@
+/*
+ * QTest testcase for USB xHCI controller
+ *
+ * Copyright (c) 2014 HUAWEI TECHNOLOGIES CO.,LTD.
+ *
+ * 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 <glib.h>
+#include <string.h>
+#include "libqtest.h"
+#include "qemu/osdep.h"
+#include "libqos/usb.h"
+
+
+static void test_xhci_init(void)
+{
+}
+
+static void test_xhci_hotplug(void)
+{
+ usb_test_hotplug("xhci", 1, NULL);
+}
+
+static void test_usb_uas_hotplug(void)
+{
+ QDict *response;
+
+ response = qmp("{'execute': 'device_add',"
+ " 'arguments': {"
+ " 'driver': 'usb-uas',"
+ " 'id': 'uas'"
+ "}}");
+ g_assert(response);
+ g_assert(!qdict_haskey(response, "error"));
+ QDECREF(response);
+
+ response = qmp("{'execute': 'device_add',"
+ " 'arguments': {"
+ " 'driver': 'scsi-hd',"
+ " 'drive': 'drive0',"
+ " 'id': 'scsi-hd'"
+ "}}");
+ g_assert(response);
+ g_assert(!qdict_haskey(response, "error"));
+ QDECREF(response);
+
+ /* TODO:
+ UAS HBA driver in libqos, to check that
+ added disk is visible after BUS rescan
+ */
+
+ response = qmp("{'execute': 'device_del',"
+ " 'arguments': {"
+ " 'id': 'scsi-hd'"
+ "}}");
+ g_assert(response);
+ g_assert(!qdict_haskey(response, "error"));
+ QDECREF(response);
+
+ response = qmp("");
+ g_assert(qdict_haskey(response, "event"));
+ g_assert(!strcmp(qdict_get_str(response, "event"), "DEVICE_DELETED"));
+ QDECREF(response);
+
+
+ response = qmp("{'execute': 'device_del',"
+ " 'arguments': {"
+ " 'id': 'uas'"
+ "}}");
+ g_assert(response);
+ g_assert(!qdict_haskey(response, "error"));
+ QDECREF(response);
+
+ response = qmp("");
+ g_assert(response);
+ g_assert(qdict_haskey(response, "event"));
+ g_assert(!strcmp(qdict_get_str(response, "event"), "DEVICE_DELETED"));
+ QDECREF(response);
+}
+
+int main(int argc, char **argv)
+{
+ int ret;
+
+ g_test_init(&argc, &argv, NULL);
+
+ qtest_add_func("/xhci/pci/init", test_xhci_init);
+ qtest_add_func("/xhci/pci/hotplug", test_xhci_hotplug);
+ qtest_add_func("/xhci/pci/hotplug/usb-uas", test_usb_uas_hotplug);
+
+ qtest_start("-device nec-usb-xhci,id=xhci"
+ " -drive id=drive0,if=none,file=/dev/null");
+ ret = g_test_run();
+ qtest_end();
+
+ return ret;
+}
diff --git a/tests/virtio-blk-test.c b/tests/virtio-blk-test.c
index d53f875b8..ead3911e2 100644
--- a/tests/virtio-blk-test.c
+++ b/tests/virtio-blk-test.c
@@ -2,6 +2,7 @@
* QTest testcase for VirtIO Block Device
*
* Copyright (c) 2014 SUSE LINUX Products GmbH
+ * Copyright (c) 2014 Marc MarĂ­
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
@@ -9,12 +10,657 @@
#include <glib.h>
#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
#include "libqtest.h"
-#include "qemu/osdep.h"
+#include "libqos/virtio.h"
+#include "libqos/virtio-pci.h"
+#include "libqos/pci-pc.h"
+#include "libqos/malloc.h"
+#include "libqos/malloc-pc.h"
+#include "qemu/bswap.h"
-/* Tests only initialization so far. TODO: Replace with functional tests */
-static void pci_nop(void)
+#define QVIRTIO_BLK_F_BARRIER 0x00000001
+#define QVIRTIO_BLK_F_SIZE_MAX 0x00000002
+#define QVIRTIO_BLK_F_SEG_MAX 0x00000004
+#define QVIRTIO_BLK_F_GEOMETRY 0x00000010
+#define QVIRTIO_BLK_F_RO 0x00000020
+#define QVIRTIO_BLK_F_BLK_SIZE 0x00000040
+#define QVIRTIO_BLK_F_SCSI 0x00000080
+#define QVIRTIO_BLK_F_WCE 0x00000200
+#define QVIRTIO_BLK_F_TOPOLOGY 0x00000400
+#define QVIRTIO_BLK_F_CONFIG_WCE 0x00000800
+
+#define QVIRTIO_BLK_T_IN 0
+#define QVIRTIO_BLK_T_OUT 1
+#define QVIRTIO_BLK_T_SCSI_CMD 2
+#define QVIRTIO_BLK_T_SCSI_CMD_OUT 3
+#define QVIRTIO_BLK_T_FLUSH 4
+#define QVIRTIO_BLK_T_FLUSH_OUT 5
+#define QVIRTIO_BLK_T_GET_ID 8
+
+#define TEST_IMAGE_SIZE (64 * 1024 * 1024)
+#define QVIRTIO_BLK_TIMEOUT_US (30 * 1000 * 1000)
+#define PCI_SLOT 0x04
+#define PCI_FN 0x00
+
+#define PCI_SLOT_HP 0x06
+
+typedef struct QVirtioBlkReq {
+ uint32_t type;
+ uint32_t ioprio;
+ uint64_t sector;
+ char *data;
+ uint8_t status;
+} QVirtioBlkReq;
+
+static QPCIBus *test_start(void)
+{
+ char *cmdline;
+ char tmp_path[] = "/tmp/qtest.XXXXXX";
+ int fd, ret;
+
+ /* Create a temporary raw image */
+ fd = mkstemp(tmp_path);
+ g_assert_cmpint(fd, >=, 0);
+ ret = ftruncate(fd, TEST_IMAGE_SIZE);
+ g_assert_cmpint(ret, ==, 0);
+ close(fd);
+
+ cmdline = g_strdup_printf("-drive if=none,id=drive0,file=%s "
+ "-drive if=none,id=drive1,file=/dev/null "
+ "-device virtio-blk-pci,id=drv0,drive=drive0,"
+ "addr=%x.%x",
+ tmp_path, PCI_SLOT, PCI_FN);
+ qtest_start(cmdline);
+ unlink(tmp_path);
+ g_free(cmdline);
+
+ return qpci_init_pc();
+}
+
+static void test_end(void)
{
+ qtest_end();
+}
+
+static QVirtioPCIDevice *virtio_blk_init(QPCIBus *bus, int slot)
+{
+ QVirtioPCIDevice *dev;
+
+ dev = qvirtio_pci_device_find(bus, QVIRTIO_BLK_DEVICE_ID);
+ g_assert(dev != NULL);
+ g_assert_cmphex(dev->vdev.device_type, ==, QVIRTIO_BLK_DEVICE_ID);
+ g_assert_cmphex(dev->pdev->devfn, ==, ((slot << 3) | PCI_FN));
+
+ qvirtio_pci_device_enable(dev);
+ qvirtio_reset(&qvirtio_pci, &dev->vdev);
+ qvirtio_set_acknowledge(&qvirtio_pci, &dev->vdev);
+ qvirtio_set_driver(&qvirtio_pci, &dev->vdev);
+
+ return dev;
+}
+
+static inline void virtio_blk_fix_request(QVirtioBlkReq *req)
+{
+#ifdef HOST_WORDS_BIGENDIAN
+ bool host_endian = true;
+#else
+ bool host_endian = false;
+#endif
+
+ if (qtest_big_endian() != host_endian) {
+ req->type = bswap32(req->type);
+ req->ioprio = bswap32(req->ioprio);
+ req->sector = bswap64(req->sector);
+ }
+}
+
+static uint64_t virtio_blk_request(QGuestAllocator *alloc, QVirtioBlkReq *req,
+ uint64_t data_size)
+{
+ uint64_t addr;
+ uint8_t status = 0xFF;
+
+ g_assert_cmpuint(data_size % 512, ==, 0);
+ addr = guest_alloc(alloc, sizeof(*req) + data_size);
+
+ virtio_blk_fix_request(req);
+
+ memwrite(addr, req, 16);
+ memwrite(addr + 16, req->data, data_size);
+ memwrite(addr + 16 + data_size, &status, sizeof(status));
+
+ return addr;
+}
+
+static void pci_basic(void)
+{
+ QVirtioPCIDevice *dev;
+ QPCIBus *bus;
+ QVirtQueuePCI *vqpci;
+ QGuestAllocator *alloc;
+ QVirtioBlkReq req;
+ void *addr;
+ uint64_t req_addr;
+ uint64_t capacity;
+ uint32_t features;
+ uint32_t free_head;
+ uint8_t status;
+ char *data;
+
+ bus = test_start();
+
+ dev = virtio_blk_init(bus, PCI_SLOT);
+
+ /* MSI-X is not enabled */
+ addr = dev->addr + QVIRTIO_DEVICE_SPECIFIC_NO_MSIX;
+
+ capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev, addr);
+ g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512);
+
+ features = qvirtio_get_features(&qvirtio_pci, &dev->vdev);
+ features = features & ~(QVIRTIO_F_BAD_FEATURE |
+ QVIRTIO_F_RING_INDIRECT_DESC | QVIRTIO_F_RING_EVENT_IDX |
+ QVIRTIO_BLK_F_SCSI);
+ qvirtio_set_features(&qvirtio_pci, &dev->vdev, features);
+
+ alloc = pc_alloc_init();
+ vqpci = (QVirtQueuePCI *)qvirtqueue_setup(&qvirtio_pci, &dev->vdev,
+ alloc, 0);
+
+ qvirtio_set_driver_ok(&qvirtio_pci, &dev->vdev);
+
+ /* Write and read with 2 descriptor layout */
+ /* Write request */
+ req.type = QVIRTIO_BLK_T_OUT;
+ req.ioprio = 1;
+ req.sector = 0;
+ req.data = g_malloc0(512);
+ strcpy(req.data, "TEST");
+
+ req_addr = virtio_blk_request(alloc, &req, 512);
+
+ g_free(req.data);
+
+ free_head = qvirtqueue_add(&vqpci->vq, req_addr, 528, false, true);
+ qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false);
+ qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head);
+
+ qvirtio_wait_queue_isr(&qvirtio_pci, &dev->vdev, &vqpci->vq,
+ QVIRTIO_BLK_TIMEOUT_US);
+ status = readb(req_addr + 528);
+ g_assert_cmpint(status, ==, 0);
+
+ guest_free(alloc, req_addr);
+
+ /* Read request */
+ req.type = QVIRTIO_BLK_T_IN;
+ req.ioprio = 1;
+ req.sector = 0;
+ req.data = g_malloc0(512);
+
+ req_addr = virtio_blk_request(alloc, &req, 512);
+
+ g_free(req.data);
+
+ free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true);
+ qvirtqueue_add(&vqpci->vq, req_addr + 16, 513, true, false);
+
+ qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head);
+
+ qvirtio_wait_queue_isr(&qvirtio_pci, &dev->vdev, &vqpci->vq,
+ QVIRTIO_BLK_TIMEOUT_US);
+ status = readb(req_addr + 528);
+ g_assert_cmpint(status, ==, 0);
+
+ data = g_malloc0(512);
+ memread(req_addr + 16, data, 512);
+ g_assert_cmpstr(data, ==, "TEST");
+ g_free(data);
+
+ guest_free(alloc, req_addr);
+
+ /* Write and read with 3 descriptor layout */
+ /* Write request */
+ req.type = QVIRTIO_BLK_T_OUT;
+ req.ioprio = 1;
+ req.sector = 1;
+ req.data = g_malloc0(512);
+ strcpy(req.data, "TEST");
+
+ req_addr = virtio_blk_request(alloc, &req, 512);
+
+ free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true);
+ qvirtqueue_add(&vqpci->vq, req_addr + 16, 512, false, true);
+ qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false);
+
+ qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head);
+
+ qvirtio_wait_queue_isr(&qvirtio_pci, &dev->vdev, &vqpci->vq,
+ QVIRTIO_BLK_TIMEOUT_US);
+ status = readb(req_addr + 528);
+ g_assert_cmpint(status, ==, 0);
+
+ guest_free(alloc, req_addr);
+
+ /* Read request */
+ req.type = QVIRTIO_BLK_T_IN;
+ req.ioprio = 1;
+ req.sector = 1;
+ req.data = g_malloc0(512);
+
+ req_addr = virtio_blk_request(alloc, &req, 512);
+
+ g_free(req.data);
+
+ free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true);
+ qvirtqueue_add(&vqpci->vq, req_addr + 16, 512, true, true);
+ qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false);
+
+ qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head);
+
+ qvirtio_wait_queue_isr(&qvirtio_pci, &dev->vdev, &vqpci->vq,
+ QVIRTIO_BLK_TIMEOUT_US);
+ status = readb(req_addr + 528);
+ g_assert_cmpint(status, ==, 0);
+
+ data = g_malloc0(512);
+ memread(req_addr + 16, data, 512);
+ g_assert_cmpstr(data, ==, "TEST");
+ g_free(data);
+
+ guest_free(alloc, req_addr);
+
+ /* End test */
+ guest_free(alloc, vqpci->vq.desc);
+ qvirtio_pci_device_disable(dev);
+ g_free(dev);
+ test_end();
+}
+
+static void pci_indirect(void)
+{
+ QVirtioPCIDevice *dev;
+ QPCIBus *bus;
+ QVirtQueuePCI *vqpci;
+ QGuestAllocator *alloc;
+ QVirtioBlkReq req;
+ QVRingIndirectDesc *indirect;
+ void *addr;
+ uint64_t req_addr;
+ uint64_t capacity;
+ uint32_t features;
+ uint32_t free_head;
+ uint8_t status;
+ char *data;
+
+ bus = test_start();
+
+ dev = virtio_blk_init(bus, PCI_SLOT);
+
+ /* MSI-X is not enabled */
+ addr = dev->addr + QVIRTIO_DEVICE_SPECIFIC_NO_MSIX;
+
+ capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev, addr);
+ g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512);
+
+ features = qvirtio_get_features(&qvirtio_pci, &dev->vdev);
+ g_assert_cmphex(features & QVIRTIO_F_RING_INDIRECT_DESC, !=, 0);
+ features = features & ~(QVIRTIO_F_BAD_FEATURE | QVIRTIO_F_RING_EVENT_IDX |
+ QVIRTIO_BLK_F_SCSI);
+ qvirtio_set_features(&qvirtio_pci, &dev->vdev, features);
+
+ alloc = pc_alloc_init();
+ vqpci = (QVirtQueuePCI *)qvirtqueue_setup(&qvirtio_pci, &dev->vdev,
+ alloc, 0);
+ qvirtio_set_driver_ok(&qvirtio_pci, &dev->vdev);
+
+ /* Write request */
+ req.type = QVIRTIO_BLK_T_OUT;
+ req.ioprio = 1;
+ req.sector = 0;
+ req.data = g_malloc0(512);
+ strcpy(req.data, "TEST");
+
+ req_addr = virtio_blk_request(alloc, &req, 512);
+
+ g_free(req.data);
+
+ indirect = qvring_indirect_desc_setup(&dev->vdev, alloc, 2);
+ qvring_indirect_desc_add(indirect, req_addr, 528, false);
+ qvring_indirect_desc_add(indirect, req_addr + 528, 1, true);
+ free_head = qvirtqueue_add_indirect(&vqpci->vq, indirect);
+ qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head);
+
+ qvirtio_wait_queue_isr(&qvirtio_pci, &dev->vdev, &vqpci->vq,
+ QVIRTIO_BLK_TIMEOUT_US);
+ status = readb(req_addr + 528);
+ g_assert_cmpint(status, ==, 0);
+
+ g_free(indirect);
+ guest_free(alloc, req_addr);
+
+ /* Read request */
+ req.type = QVIRTIO_BLK_T_IN;
+ req.ioprio = 1;
+ req.sector = 0;
+ req.data = g_malloc0(512);
+ strcpy(req.data, "TEST");
+
+ req_addr = virtio_blk_request(alloc, &req, 512);
+
+ g_free(req.data);
+
+ indirect = qvring_indirect_desc_setup(&dev->vdev, alloc, 2);
+ qvring_indirect_desc_add(indirect, req_addr, 16, false);
+ qvring_indirect_desc_add(indirect, req_addr + 16, 513, true);
+ free_head = qvirtqueue_add_indirect(&vqpci->vq, indirect);
+ qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head);
+
+ qvirtio_wait_queue_isr(&qvirtio_pci, &dev->vdev, &vqpci->vq,
+ QVIRTIO_BLK_TIMEOUT_US);
+ status = readb(req_addr + 528);
+ g_assert_cmpint(status, ==, 0);
+
+ data = g_malloc0(512);
+ memread(req_addr + 16, data, 512);
+ g_assert_cmpstr(data, ==, "TEST");
+ g_free(data);
+
+ g_free(indirect);
+ guest_free(alloc, req_addr);
+
+ /* End test */
+ guest_free(alloc, vqpci->vq.desc);
+ qvirtio_pci_device_disable(dev);
+ g_free(dev);
+ test_end();
+}
+
+static void pci_config(void)
+{
+ QVirtioPCIDevice *dev;
+ QPCIBus *bus;
+ int n_size = TEST_IMAGE_SIZE / 2;
+ void *addr;
+ uint64_t capacity;
+
+ bus = test_start();
+
+ dev = virtio_blk_init(bus, PCI_SLOT);
+
+ /* MSI-X is not enabled */
+ addr = dev->addr + QVIRTIO_DEVICE_SPECIFIC_NO_MSIX;
+
+ capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev, addr);
+ g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512);
+
+ qvirtio_set_driver_ok(&qvirtio_pci, &dev->vdev);
+
+ qmp("{ 'execute': 'block_resize', 'arguments': { 'device': 'drive0', "
+ " 'size': %d } }", n_size);
+ qvirtio_wait_config_isr(&qvirtio_pci, &dev->vdev, QVIRTIO_BLK_TIMEOUT_US);
+
+ capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev, addr);
+ g_assert_cmpint(capacity, ==, n_size / 512);
+
+ qvirtio_pci_device_disable(dev);
+ g_free(dev);
+ test_end();
+}
+
+static void pci_msix(void)
+{
+ QVirtioPCIDevice *dev;
+ QPCIBus *bus;
+ QVirtQueuePCI *vqpci;
+ QGuestAllocator *alloc;
+ QVirtioBlkReq req;
+ int n_size = TEST_IMAGE_SIZE / 2;
+ void *addr;
+ uint64_t req_addr;
+ uint64_t capacity;
+ uint32_t features;
+ uint32_t free_head;
+ uint8_t status;
+ char *data;
+
+ bus = test_start();
+ alloc = pc_alloc_init();
+
+ dev = virtio_blk_init(bus, PCI_SLOT);
+ qpci_msix_enable(dev->pdev);
+
+ qvirtio_pci_set_msix_configuration_vector(dev, alloc, 0);
+
+ /* MSI-X is enabled */
+ addr = dev->addr + QVIRTIO_DEVICE_SPECIFIC_MSIX;
+
+ capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev, addr);
+ g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512);
+
+ features = qvirtio_get_features(&qvirtio_pci, &dev->vdev);
+ features = features & ~(QVIRTIO_F_BAD_FEATURE |
+ QVIRTIO_F_RING_INDIRECT_DESC |
+ QVIRTIO_F_RING_EVENT_IDX | QVIRTIO_BLK_F_SCSI);
+ qvirtio_set_features(&qvirtio_pci, &dev->vdev, features);
+
+ vqpci = (QVirtQueuePCI *)qvirtqueue_setup(&qvirtio_pci, &dev->vdev,
+ alloc, 0);
+ qvirtqueue_pci_msix_setup(dev, vqpci, alloc, 1);
+
+ qvirtio_set_driver_ok(&qvirtio_pci, &dev->vdev);
+
+ qmp("{ 'execute': 'block_resize', 'arguments': { 'device': 'drive0', "
+ " 'size': %d } }", n_size);
+
+ qvirtio_wait_config_isr(&qvirtio_pci, &dev->vdev, QVIRTIO_BLK_TIMEOUT_US);
+
+ capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev, addr);
+ g_assert_cmpint(capacity, ==, n_size / 512);
+
+ /* Write request */
+ req.type = QVIRTIO_BLK_T_OUT;
+ req.ioprio = 1;
+ req.sector = 0;
+ req.data = g_malloc0(512);
+ strcpy(req.data, "TEST");
+
+ req_addr = virtio_blk_request(alloc, &req, 512);
+
+ g_free(req.data);
+
+ free_head = qvirtqueue_add(&vqpci->vq, req_addr, 528, false, true);
+ qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false);
+ qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head);
+
+ qvirtio_wait_queue_isr(&qvirtio_pci, &dev->vdev, &vqpci->vq,
+ QVIRTIO_BLK_TIMEOUT_US);
+
+ status = readb(req_addr + 528);
+ g_assert_cmpint(status, ==, 0);
+
+ guest_free(alloc, req_addr);
+
+ /* Read request */
+ req.type = QVIRTIO_BLK_T_IN;
+ req.ioprio = 1;
+ req.sector = 0;
+ req.data = g_malloc0(512);
+
+ req_addr = virtio_blk_request(alloc, &req, 512);
+
+ g_free(req.data);
+
+ free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true);
+ qvirtqueue_add(&vqpci->vq, req_addr + 16, 513, true, false);
+
+ qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head);
+
+
+ qvirtio_wait_queue_isr(&qvirtio_pci, &dev->vdev, &vqpci->vq,
+ QVIRTIO_BLK_TIMEOUT_US);
+
+ status = readb(req_addr + 528);
+ g_assert_cmpint(status, ==, 0);
+
+ data = g_malloc0(512);
+ memread(req_addr + 16, data, 512);
+ g_assert_cmpstr(data, ==, "TEST");
+ g_free(data);
+
+ guest_free(alloc, req_addr);
+
+ /* End test */
+ guest_free(alloc, (uint64_t)vqpci->vq.desc);
+ qpci_msix_disable(dev->pdev);
+ qvirtio_pci_device_disable(dev);
+ g_free(dev);
+ test_end();
+}
+
+static void pci_idx(void)
+{
+ QVirtioPCIDevice *dev;
+ QPCIBus *bus;
+ QVirtQueuePCI *vqpci;
+ QGuestAllocator *alloc;
+ QVirtioBlkReq req;
+ void *addr;
+ uint64_t req_addr;
+ uint64_t capacity;
+ uint32_t features;
+ uint32_t free_head;
+ uint8_t status;
+ char *data;
+
+ bus = test_start();
+ alloc = pc_alloc_init();
+
+ dev = virtio_blk_init(bus, PCI_SLOT);
+ qpci_msix_enable(dev->pdev);
+
+ qvirtio_pci_set_msix_configuration_vector(dev, alloc, 0);
+
+ /* MSI-X is enabled */
+ addr = dev->addr + QVIRTIO_DEVICE_SPECIFIC_MSIX;
+
+ capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev, addr);
+ g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512);
+
+ features = qvirtio_get_features(&qvirtio_pci, &dev->vdev);
+ features = features & ~(QVIRTIO_F_BAD_FEATURE |
+ QVIRTIO_F_RING_INDIRECT_DESC |
+ QVIRTIO_F_NOTIFY_ON_EMPTY | QVIRTIO_BLK_F_SCSI);
+ qvirtio_set_features(&qvirtio_pci, &dev->vdev, features);
+
+ vqpci = (QVirtQueuePCI *)qvirtqueue_setup(&qvirtio_pci, &dev->vdev,
+ alloc, 0);
+ qvirtqueue_pci_msix_setup(dev, vqpci, alloc, 1);
+
+ qvirtio_set_driver_ok(&qvirtio_pci, &dev->vdev);
+
+ /* Write request */
+ req.type = QVIRTIO_BLK_T_OUT;
+ req.ioprio = 1;
+ req.sector = 0;
+ req.data = g_malloc0(512);
+ strcpy(req.data, "TEST");
+
+ req_addr = virtio_blk_request(alloc, &req, 512);
+
+ g_free(req.data);
+
+ free_head = qvirtqueue_add(&vqpci->vq, req_addr, 528, false, true);
+ qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false);
+ qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head);
+
+ qvirtio_wait_queue_isr(&qvirtio_pci, &dev->vdev, &vqpci->vq,
+ QVIRTIO_BLK_TIMEOUT_US);
+
+ /* Write request */
+ req.type = QVIRTIO_BLK_T_OUT;
+ req.ioprio = 1;
+ req.sector = 1;
+ req.data = g_malloc0(512);
+ strcpy(req.data, "TEST");
+
+ req_addr = virtio_blk_request(alloc, &req, 512);
+
+ g_free(req.data);
+
+ /* Notify after processing the third request */
+ qvirtqueue_set_used_event(&vqpci->vq, 2);
+ free_head = qvirtqueue_add(&vqpci->vq, req_addr, 528, false, true);
+ qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false);
+ qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head);
+
+ /* No notification expected */
+ status = qvirtio_wait_status_byte_no_isr(&qvirtio_pci, &dev->vdev,
+ &vqpci->vq, req_addr + 528,
+ QVIRTIO_BLK_TIMEOUT_US);
+ g_assert_cmpint(status, ==, 0);
+
+ guest_free(alloc, req_addr);
+
+ /* Read request */
+ req.type = QVIRTIO_BLK_T_IN;
+ req.ioprio = 1;
+ req.sector = 1;
+ req.data = g_malloc0(512);
+
+ req_addr = virtio_blk_request(alloc, &req, 512);
+
+ g_free(req.data);
+
+ free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true);
+ qvirtqueue_add(&vqpci->vq, req_addr + 16, 513, true, false);
+
+ qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head);
+
+
+ qvirtio_wait_queue_isr(&qvirtio_pci, &dev->vdev, &vqpci->vq,
+ QVIRTIO_BLK_TIMEOUT_US);
+
+ status = readb(req_addr + 528);
+ g_assert_cmpint(status, ==, 0);
+
+ data = g_malloc0(512);
+ memread(req_addr + 16, data, 512);
+ g_assert_cmpstr(data, ==, "TEST");
+ g_free(data);
+
+ guest_free(alloc, req_addr);
+
+ /* End test */
+ guest_free(alloc, vqpci->vq.desc);
+ qpci_msix_disable(dev->pdev);
+ qvirtio_pci_device_disable(dev);
+ g_free(dev);
+ test_end();
+}
+
+static void hotplug(void)
+{
+ QPCIBus *bus;
+ QVirtioPCIDevice *dev;
+
+ bus = test_start();
+
+ /* plug secondary disk */
+ qpci_plug_device_test("virtio-blk-pci", "drv1", PCI_SLOT_HP,
+ "'drive': 'drive1'");
+
+ dev = virtio_blk_init(bus, PCI_SLOT_HP);
+ g_assert(dev);
+ qvirtio_pci_device_disable(dev);
+ g_free(dev);
+
+ /* unplug secondary disk */
+ qpci_unplug_acpi_device_test("drv1", PCI_SLOT_HP);
+ test_end();
}
int main(int argc, char **argv)
@@ -22,13 +668,15 @@ int main(int argc, char **argv)
int ret;
g_test_init(&argc, &argv, NULL);
- qtest_add_func("/virtio/blk/pci/nop", pci_nop);
- qtest_start("-drive id=drv0,if=none,file=/dev/null "
- "-device virtio-blk-pci,drive=drv0");
- ret = g_test_run();
+ g_test_add_func("/virtio/blk/pci/basic", pci_basic);
+ g_test_add_func("/virtio/blk/pci/indirect", pci_indirect);
+ g_test_add_func("/virtio/blk/pci/config", pci_config);
+ g_test_add_func("/virtio/blk/pci/msix", pci_msix);
+ g_test_add_func("/virtio/blk/pci/idx", pci_idx);
+ g_test_add_func("/virtio/blk/pci/hotplug", hotplug);
- qtest_end();
+ ret = g_test_run();
return ret;
}
diff --git a/tests/virtio-net-test.c b/tests/virtio-net-test.c
index df9934323..ea7478c27 100644
--- a/tests/virtio-net-test.c
+++ b/tests/virtio-net-test.c
@@ -11,18 +11,28 @@
#include <string.h>
#include "libqtest.h"
#include "qemu/osdep.h"
+#include "libqos/pci.h"
+
+#define PCI_SLOT_HP 0x06
/* Tests only initialization so far. TODO: Replace with functional tests */
static void pci_nop(void)
{
}
+static void hotplug(void)
+{
+ qpci_plug_device_test("virtio-net-pci", "net1", PCI_SLOT_HP, NULL);
+ qpci_unplug_acpi_device_test("net1", PCI_SLOT_HP);
+}
+
int main(int argc, char **argv)
{
int ret;
g_test_init(&argc, &argv, NULL);
qtest_add_func("/virtio/net/pci/nop", pci_nop);
+ qtest_add_func("/virtio/net/pci/hotplug", hotplug);
qtest_start("-device virtio-net-pci");
ret = g_test_run();
diff --git a/tests/virtio-rng-test.c b/tests/virtio-rng-test.c
index 402c2060d..41c1cdb1a 100644
--- a/tests/virtio-rng-test.c
+++ b/tests/virtio-rng-test.c
@@ -11,18 +11,28 @@
#include <string.h>
#include "libqtest.h"
#include "qemu/osdep.h"
+#include "libqos/pci.h"
+
+#define PCI_SLOT_HP 0x06
/* Tests only initialization so far. TODO: Replace with functional tests */
static void pci_nop(void)
{
}
+static void hotplug(void)
+{
+ qpci_plug_device_test("virtio-rng-pci", "rng1", PCI_SLOT_HP, NULL);
+ qpci_unplug_acpi_device_test("rng1", PCI_SLOT_HP);
+}
+
int main(int argc, char **argv)
{
int ret;
g_test_init(&argc, &argv, NULL);
qtest_add_func("/virtio/rng/pci/nop", pci_nop);
+ qtest_add_func("/virtio/rng/pci/hotplug", hotplug);
qtest_start("-device virtio-rng-pci");
ret = g_test_run();
diff --git a/tests/virtio-scsi-test.c b/tests/virtio-scsi-test.c
index 3230908b9..41f9602e4 100644
--- a/tests/virtio-scsi-test.c
+++ b/tests/virtio-scsi-test.c
@@ -17,14 +17,43 @@ static void pci_nop(void)
{
}
+static void hotplug(void)
+{
+ QDict *response;
+
+ response = qmp("{\"execute\": \"device_add\","
+ " \"arguments\": {"
+ " \"driver\": \"scsi-hd\","
+ " \"id\": \"scsi-hd\","
+ " \"drive\": \"drv1\""
+ "}}");
+
+ g_assert(response);
+ g_assert(!qdict_haskey(response, "error"));
+ QDECREF(response);
+
+ response = qmp("{\"execute\": \"device_del\","
+ " \"arguments\": {"
+ " \"id\": \"scsi-hd\""
+ "}}");
+
+ g_assert(response);
+ g_assert(!qdict_haskey(response, "error"));
+ g_assert(qdict_haskey(response, "event"));
+ g_assert(!strcmp(qdict_get_str(response, "event"), "DEVICE_DELETED"));
+ QDECREF(response);
+}
+
int main(int argc, char **argv)
{
int ret;
g_test_init(&argc, &argv, NULL);
qtest_add_func("/virtio/scsi/pci/nop", pci_nop);
+ qtest_add_func("/virtio/scsi/pci/hotplug", hotplug);
qtest_start("-drive id=drv0,if=none,file=/dev/null "
+ "-drive id=drv1,if=none,file=/dev/null "
"-device virtio-scsi-pci,id=vscsi0 "
"-device scsi-hd,bus=vscsi0.0,drive=drv0");
ret = g_test_run();
diff --git a/tests/virtio-serial-test.c b/tests/virtio-serial-test.c
index e7438751e..bf030a616 100644
--- a/tests/virtio-serial-test.c
+++ b/tests/virtio-serial-test.c
@@ -17,12 +17,39 @@ static void pci_nop(void)
{
}
+static void hotplug(void)
+{
+ QDict *response;
+
+ response = qmp("{\"execute\": \"device_add\","
+ " \"arguments\": {"
+ " \"driver\": \"virtserialport\","
+ " \"id\": \"hp-port\""
+ "}}");
+
+ g_assert(response);
+ g_assert(!qdict_haskey(response, "error"));
+ QDECREF(response);
+
+ response = qmp("{\"execute\": \"device_del\","
+ " \"arguments\": {"
+ " \"id\": \"hp-port\""
+ "}}");
+
+ g_assert(response);
+ g_assert(!qdict_haskey(response, "error"));
+ g_assert(qdict_haskey(response, "event"));
+ g_assert(!strcmp(qdict_get_str(response, "event"), "DEVICE_DELETED"));
+ QDECREF(response);
+}
+
int main(int argc, char **argv)
{
int ret;
g_test_init(&argc, &argv, NULL);
qtest_add_func("/virtio/serial/pci/nop", pci_nop);
+ qtest_add_func("/virtio/serial/pci/hotplug", hotplug);
qtest_start("-device virtio-serial-pci");
ret = g_test_run();