aboutsummaryrefslogtreecommitdiff
path: root/tests/qtest
diff options
context:
space:
mode:
Diffstat (limited to 'tests/qtest')
-rw-r--r--tests/qtest/acpi-utils.c1
-rw-r--r--tests/qtest/adm1266-test.c4
-rw-r--r--tests/qtest/adm1272-test.c4
-rw-r--r--tests/qtest/ahci-test.c3
-rw-r--r--tests/qtest/arm-cpu-features.c32
-rw-r--r--tests/qtest/aspeed-hace-utils.c646
-rw-r--r--tests/qtest/aspeed-hace-utils.h84
-rw-r--r--tests/qtest/aspeed-smc-utils.c686
-rw-r--r--tests/qtest/aspeed-smc-utils.h95
-rw-r--r--tests/qtest/aspeed_gpio-test.c2
-rw-r--r--tests/qtest/aspeed_hace-test.c577
-rw-r--r--tests/qtest/aspeed_smc-test.c778
-rw-r--r--tests/qtest/ast2700-gpio-test.c95
-rw-r--r--tests/qtest/ast2700-hace-test.c98
-rw-r--r--tests/qtest/ast2700-smc-test.c72
-rw-r--r--tests/qtest/bcm2835-i2c-test.c2
-rw-r--r--tests/qtest/bios-tables-test.c322
-rw-r--r--tests/qtest/boot-order-test.c13
-rw-r--r--tests/qtest/boot-serial-test.c25
-rw-r--r--tests/qtest/cdrom-test.c103
-rw-r--r--tests/qtest/cmsdk-apb-watchdog-test.c328
-rw-r--r--tests/qtest/cpu-plug-test.c28
-rw-r--r--tests/qtest/dbus-display-test.c72
-rw-r--r--tests/qtest/device-introspect-test.c13
-rw-r--r--tests/qtest/device-plug-test.c15
-rw-r--r--tests/qtest/dm163-test.c2
-rw-r--r--tests/qtest/drive_del-test.c11
-rw-r--r--tests/qtest/emc141x-test.c2
-rw-r--r--tests/qtest/fdc-test.c4
-rw-r--r--tests/qtest/fuzz/fuzz.c7
-rw-r--r--tests/qtest/fuzz/generic_fuzz.c8
-rw-r--r--tests/qtest/fuzz/qos_fuzz.c2
-rw-r--r--tests/qtest/fuzz/qtest_wrappers.c2
-rw-r--r--tests/qtest/fw_cfg-test.c6
-rw-r--r--tests/qtest/hd-geo-test.c82
-rw-r--r--tests/qtest/ide-test.c2
-rw-r--r--tests/qtest/intel-iommu-test.c64
-rw-r--r--tests/qtest/ipmi-bt-test.c4
-rw-r--r--tests/qtest/ipmi-kcs-test.c4
-rw-r--r--tests/qtest/isl_pmbus_vr-test.c4
-rw-r--r--tests/qtest/libqmp.c4
-rw-r--r--tests/qtest/libqmp.h2
-rw-r--r--tests/qtest/libqos/arm-imx25-pdk-machine.c5
-rw-r--r--tests/qtest/libqos/arm-n800-machine.c92
-rw-r--r--tests/qtest/libqos/fw_cfg.c202
-rw-r--r--tests/qtest/libqos/fw_cfg.h6
-rw-r--r--tests/qtest/libqos/generic-pcihost.c2
-rw-r--r--tests/qtest/libqos/i2c-imx.c4
-rw-r--r--tests/qtest/libqos/igb.c4
-rw-r--r--tests/qtest/libqos/libqos-malloc.c1
-rw-r--r--tests/qtest/libqos/libqos.c5
-rw-r--r--tests/qtest/libqos/meson.build12
-rw-r--r--tests/qtest/libqos/pci-pc.c2
-rw-r--r--tests/qtest/libqos/pci.c2
-rw-r--r--tests/qtest/libqos/qgraph.h2
-rw-r--r--tests/qtest/libqos/qos_external.c8
-rw-r--r--tests/qtest/libqos/riscv-iommu.c76
-rw-r--r--tests/qtest/libqos/riscv-iommu.h101
-rw-r--r--tests/qtest/libqos/sdhci-cmd.h2
-rw-r--r--tests/qtest/libqos/virtio-9p-client.c52
-rw-r--r--tests/qtest/libqos/virtio-9p-client.h34
-rw-r--r--tests/qtest/libqos/virtio-pci-modern.c6
-rw-r--r--tests/qtest/libqos/virtio-pci.c6
-rw-r--r--tests/qtest/libqos/virtio-scmi.c2
-rw-r--r--tests/qtest/libqos/virtio.c48
-rw-r--r--tests/qtest/libqtest.c281
-rw-r--r--tests/qtest/libqtest.h78
-rw-r--r--tests/qtest/lsm303dlhc-mag-test.c2
-rw-r--r--tests/qtest/m48t59-test.c5
-rw-r--r--tests/qtest/machine-none-test.c3
-rw-r--r--tests/qtest/max34451-test.c4
-rw-r--r--tests/qtest/meson.build121
-rw-r--r--tests/qtest/migration-helpers.c533
-rw-r--r--tests/qtest/migration-test.c4046
-rw-r--r--tests/qtest/migration/Makefile40
-rw-r--r--tests/qtest/migration/aarch64/Makefile18
-rw-r--r--tests/qtest/migration/aarch64/a-b-kernel.S74
-rw-r--r--tests/qtest/migration/aarch64/a-b-kernel.h18
-rw-r--r--tests/qtest/migration/bootfile.c70
-rw-r--r--tests/qtest/migration/bootfile.h39
-rw-r--r--tests/qtest/migration/compression-tests.c226
-rw-r--r--tests/qtest/migration/cpr-tests.c136
-rw-r--r--tests/qtest/migration/file-tests.c341
-rw-r--r--tests/qtest/migration/framework.c1066
-rw-r--r--tests/qtest/migration/framework.h251
-rw-r--r--tests/qtest/migration/i386/Makefile23
-rw-r--r--tests/qtest/migration/i386/a-b-bootblock.S145
-rw-r--r--tests/qtest/migration/i386/a-b-bootblock.h61
-rw-r--r--tests/qtest/migration/migration-qmp.c520
-rw-r--r--tests/qtest/migration/migration-qmp.h48
-rw-r--r--tests/qtest/migration/migration-util.c398
-rw-r--r--tests/qtest/migration/migration-util.h (renamed from tests/qtest/migration-helpers.h)29
-rw-r--r--tests/qtest/migration/misc-tests.c297
-rw-r--r--tests/qtest/migration/postcopy-tests.c149
-rw-r--r--tests/qtest/migration/ppc64/Makefile15
-rw-r--r--tests/qtest/migration/ppc64/a-b-kernel.S66
-rw-r--r--tests/qtest/migration/ppc64/a-b-kernel.h42
-rw-r--r--tests/qtest/migration/precopy-tests.c1337
-rw-r--r--tests/qtest/migration/s390x/Makefile24
-rw-r--r--tests/qtest/migration/s390x/a-b-bios.c44
-rw-r--r--tests/qtest/migration/s390x/a-b-bios.h279
-rw-r--r--tests/qtest/migration/tls-tests.c871
-rw-r--r--tests/qtest/netdev-socket.c4
-rw-r--r--tests/qtest/npcm7xx_adc-test.c2
-rw-r--r--tests/qtest/npcm7xx_emc-test.c4
-rw-r--r--tests/qtest/npcm7xx_pwm-test.c4
-rw-r--r--tests/qtest/npcm7xx_sdhci-test.c8
-rw-r--r--tests/qtest/npcm7xx_timer-test.c1
-rw-r--r--tests/qtest/npcm7xx_watchdog_timer-test.c2
-rw-r--r--tests/qtest/npcm_gmac-test.c85
-rw-r--r--tests/qtest/numa-test.c14
-rw-r--r--tests/qtest/pnv-host-i2c-test.c4
-rw-r--r--tests/qtest/pnv-spi-seeprom-test.c110
-rw-r--r--tests/qtest/pnv-xive2-common.c190
-rw-r--r--tests/qtest/pnv-xive2-common.h112
-rw-r--r--tests/qtest/pnv-xive2-flush-sync.c205
-rw-r--r--tests/qtest/pnv-xive2-nvpg_bar.c152
-rw-r--r--tests/qtest/pnv-xive2-test.c585
-rw-r--r--tests/qtest/pnv-xscom.h2
-rw-r--r--tests/qtest/pvpanic-pci-test.c46
-rw-r--r--tests/qtest/pvpanic-test.c36
-rw-r--r--tests/qtest/q35-test.c51
-rw-r--r--tests/qtest/qmp-cmd-test.c3
-rw-r--r--tests/qtest/qmp-test.c6
-rw-r--r--tests/qtest/qom-test.c15
-rw-r--r--tests/qtest/qos-test.c21
-rw-r--r--tests/qtest/readconfig-test.c6
-rw-r--r--tests/qtest/riscv-csr-test.c56
-rw-r--r--tests/qtest/riscv-iommu-test.c210
-rw-r--r--tests/qtest/rs5c372-test.c43
-rw-r--r--tests/qtest/rtl8139-test.c2
-rw-r--r--tests/qtest/stm32l4x5.h42
-rw-r--r--tests/qtest/stm32l4x5_exti-test.c8
-rw-r--r--tests/qtest/stm32l4x5_gpio-test.c33
-rw-r--r--tests/qtest/stm32l4x5_syscfg-test.c48
-rw-r--r--tests/qtest/stm32l4x5_usart-test.c72
-rw-r--r--tests/qtest/tco-test.c2
-rw-r--r--tests/qtest/test-filter-mirror.c2
-rw-r--r--tests/qtest/test-filter-redirector.c2
-rw-r--r--tests/qtest/test-netfilter.c2
-rw-r--r--tests/qtest/test-x86-cpuid-compat.c41
-rw-r--r--tests/qtest/tmp105-test.c6
-rw-r--r--tests/qtest/tpm-emu.c4
-rw-r--r--tests/qtest/tpm-emu.h2
-rw-r--r--tests/qtest/tpm-tests.c2
-rw-r--r--tests/qtest/tpm-util.c2
-rw-r--r--tests/qtest/ufs-test.c760
-rw-r--r--tests/qtest/vhost-user-blk-test.c2
-rw-r--r--tests/qtest/vhost-user-test.c43
-rw-r--r--tests/qtest/virtio-9p-test.c61
-rw-r--r--tests/qtest/virtio-balloon-test.c57
-rw-r--r--tests/qtest/virtio-iommu-test.c4
-rw-r--r--tests/qtest/virtio-net-failover.c17
-rw-r--r--tests/qtest/virtio-net-test.c2
-rw-r--r--tests/qtest/vmcoreinfo-test.c90
-rw-r--r--tests/qtest/vmgenid-test.c2
-rw-r--r--tests/qtest/wdt_ib700-test.c2
157 files changed, 13446 insertions, 6523 deletions
diff --git a/tests/qtest/acpi-utils.c b/tests/qtest/acpi-utils.c
index 673fc97..9dc24fb 100644
--- a/tests/qtest/acpi-utils.c
+++ b/tests/qtest/acpi-utils.c
@@ -156,5 +156,4 @@ uint64_t acpi_find_rsdp_address_uefi(QTestState *qts, uint64_t start,
g_usleep(TEST_DELAY);
}
g_assert_not_reached();
- return 0;
}
diff --git a/tests/qtest/adm1266-test.c b/tests/qtest/adm1266-test.c
index 6c312c4..5ae8206 100644
--- a/tests/qtest/adm1266-test.c
+++ b/tests/qtest/adm1266-test.c
@@ -13,8 +13,8 @@
#include "libqtest-single.h"
#include "libqos/qgraph.h"
#include "libqos/i2c.h"
-#include "qapi/qmp/qdict.h"
-#include "qapi/qmp/qnum.h"
+#include "qobject/qdict.h"
+#include "qobject/qnum.h"
#include "qemu/bitops.h"
#define TEST_ID "adm1266-test"
diff --git a/tests/qtest/adm1272-test.c b/tests/qtest/adm1272-test.c
index 63f8514..2abda8d 100644
--- a/tests/qtest/adm1272-test.c
+++ b/tests/qtest/adm1272-test.c
@@ -12,8 +12,8 @@
#include "libqtest-single.h"
#include "libqos/qgraph.h"
#include "libqos/i2c.h"
-#include "qapi/qmp/qdict.h"
-#include "qapi/qmp/qnum.h"
+#include "qobject/qdict.h"
+#include "qobject/qnum.h"
#include "qemu/bitops.h"
#define TEST_ID "adm1272-test"
diff --git a/tests/qtest/ahci-test.c b/tests/qtest/ahci-test.c
index 5a1923f..e8aabfc 100644
--- a/tests/qtest/ahci-test.c
+++ b/tests/qtest/ahci-test.c
@@ -30,7 +30,7 @@
#include "libqos/ahci.h"
#include "libqos/pci-pc.h"
-#include "qapi/qmp/qdict.h"
+#include "qobject/qdict.h"
#include "qemu/host-utils.h"
#include "hw/pci/pci_ids.h"
@@ -1881,7 +1881,6 @@ static void test_io_interface(gconstpointer opaque)
sector = offset_sector(opts->offset, opts->address_type, bufsize);
test_io_rw_interface(opts->address_type, opts->io_type, bufsize, sector);
g_free(opts);
- return;
}
static void create_ahci_io_test(enum IOMode type, enum AddrMode addr,
diff --git a/tests/qtest/arm-cpu-features.c b/tests/qtest/arm-cpu-features.c
index 966c65d..eb8ddeb 100644
--- a/tests/qtest/arm-cpu-features.c
+++ b/tests/qtest/arm-cpu-features.c
@@ -11,8 +11,8 @@
#include "qemu/osdep.h"
#include "qemu/bitops.h"
#include "libqtest.h"
-#include "qapi/qmp/qdict.h"
-#include "qapi/qmp/qjson.h"
+#include "qobject/qdict.h"
+#include "qobject/qjson.h"
/*
* We expect the SVE max-vq to be 16. Also it must be <= 64
@@ -419,21 +419,28 @@ static void pauth_tests_default(QTestState *qts, const char *cpu_type)
assert_has_feature_enabled(qts, cpu_type, "pauth");
assert_has_feature_disabled(qts, cpu_type, "pauth-impdef");
assert_has_feature_disabled(qts, cpu_type, "pauth-qarma3");
+ assert_has_feature_disabled(qts, cpu_type, "pauth-qarma5");
assert_set_feature(qts, cpu_type, "pauth", false);
assert_set_feature(qts, cpu_type, "pauth", true);
assert_set_feature(qts, cpu_type, "pauth-impdef", true);
assert_set_feature(qts, cpu_type, "pauth-impdef", false);
assert_set_feature(qts, cpu_type, "pauth-qarma3", true);
assert_set_feature(qts, cpu_type, "pauth-qarma3", false);
+ assert_set_feature(qts, cpu_type, "pauth-qarma5", true);
+ assert_set_feature(qts, cpu_type, "pauth-qarma5", false);
assert_error(qts, cpu_type,
- "cannot enable pauth-impdef or pauth-qarma3 without pauth",
+ "cannot enable pauth-impdef, pauth-qarma3 or pauth-qarma5 without pauth",
"{ 'pauth': false, 'pauth-impdef': true }");
assert_error(qts, cpu_type,
- "cannot enable pauth-impdef or pauth-qarma3 without pauth",
+ "cannot enable pauth-impdef, pauth-qarma3 or pauth-qarma5 without pauth",
"{ 'pauth': false, 'pauth-qarma3': true }");
assert_error(qts, cpu_type,
- "cannot enable both pauth-impdef and pauth-qarma3",
- "{ 'pauth': true, 'pauth-impdef': true, 'pauth-qarma3': true }");
+ "cannot enable pauth-impdef, pauth-qarma3 or pauth-qarma5 without pauth",
+ "{ 'pauth': false, 'pauth-qarma5': true }");
+ assert_error(qts, cpu_type,
+ "cannot enable pauth-impdef, pauth-qarma3 and pauth-qarma5 at the same time",
+ "{ 'pauth': true, 'pauth-impdef': true, 'pauth-qarma3': true,"
+ " 'pauth-qarma5': true }");
}
static void test_query_cpu_model_expansion(const void *data)
@@ -509,6 +516,7 @@ static void test_query_cpu_model_expansion_kvm(const void *data)
assert_set_feature(qts, "host", "kvm-no-adjvtime", false);
if (g_str_equal(qtest_get_arch(), "aarch64")) {
+ bool kvm_supports_pmu;
bool kvm_supports_steal_time;
bool kvm_supports_sve;
char max_name[8], name[8];
@@ -537,11 +545,6 @@ static void test_query_cpu_model_expansion_kvm(const void *data)
assert_has_feature_enabled(qts, "host", "aarch64");
- /* Enabling and disabling pmu should always work. */
- assert_has_feature_enabled(qts, "host", "pmu");
- assert_set_feature(qts, "host", "pmu", false);
- assert_set_feature(qts, "host", "pmu", true);
-
/*
* Some features would be enabled by default, but they're disabled
* because this instance of KVM doesn't support them. Test that the
@@ -551,11 +554,18 @@ static void test_query_cpu_model_expansion_kvm(const void *data)
assert_has_feature(qts, "host", "sve");
resp = do_query_no_props(qts, "host");
+ kvm_supports_pmu = resp_get_feature(resp, "pmu");
kvm_supports_steal_time = resp_get_feature(resp, "kvm-steal-time");
kvm_supports_sve = resp_get_feature(resp, "sve");
vls = resp_get_sve_vls(resp);
qobject_unref(resp);
+ if (kvm_supports_pmu) {
+ /* If we have pmu then we should be able to toggle it. */
+ assert_set_feature(qts, "host", "pmu", false);
+ assert_set_feature(qts, "host", "pmu", true);
+ }
+
if (kvm_supports_steal_time) {
/* If we have steal-time then we should be able to toggle it. */
assert_set_feature(qts, "host", "kvm-steal-time", false);
diff --git a/tests/qtest/aspeed-hace-utils.c b/tests/qtest/aspeed-hace-utils.c
new file mode 100644
index 0000000..0f7f911
--- /dev/null
+++ b/tests/qtest/aspeed-hace-utils.c
@@ -0,0 +1,646 @@
+/*
+ * QTest testcase for the ASPEED Hash and Crypto Engine
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2021 IBM Corp.
+ */
+
+#include "qemu/osdep.h"
+#include "libqtest.h"
+#include "qemu/bitops.h"
+#include "aspeed-hace-utils.h"
+
+/*
+ * Test vector is the ascii "abc"
+ *
+ * Expected results were generated using command line utitiles:
+ *
+ * echo -n -e 'abc' | dd of=/tmp/test
+ * for hash in sha512sum sha384sum sha256sum md5sum; do $hash /tmp/test; done
+ *
+ */
+static const uint8_t test_vector[3] = {0x61, 0x62, 0x63};
+
+static const uint8_t test_result_sha512[64] = {
+ 0xdd, 0xaf, 0x35, 0xa1, 0x93, 0x61, 0x7a, 0xba, 0xcc, 0x41, 0x73, 0x49,
+ 0xae, 0x20, 0x41, 0x31, 0x12, 0xe6, 0xfa, 0x4e, 0x89, 0xa9, 0x7e, 0xa2,
+ 0x0a, 0x9e, 0xee, 0xe6, 0x4b, 0x55, 0xd3, 0x9a, 0x21, 0x92, 0x99, 0x2a,
+ 0x27, 0x4f, 0xc1, 0xa8, 0x36, 0xba, 0x3c, 0x23, 0xa3, 0xfe, 0xeb, 0xbd,
+ 0x45, 0x4d, 0x44, 0x23, 0x64, 0x3c, 0xe8, 0x0e, 0x2a, 0x9a, 0xc9, 0x4f,
+ 0xa5, 0x4c, 0xa4, 0x9f};
+
+static const uint8_t test_result_sha384[48] = {
+ 0xcb, 0x00, 0x75, 0x3f, 0x45, 0xa3, 0x5e, 0x8b, 0xb5, 0xa0, 0x3d, 0x69,
+ 0x9a, 0xc6, 0x50, 0x07, 0x27, 0x2c, 0x32, 0xab, 0x0e, 0xde, 0xd1, 0x63,
+ 0x1a, 0x8b, 0x60, 0x5a, 0x43, 0xff, 0x5b, 0xed, 0x80, 0x86, 0x07, 0x2b,
+ 0xa1, 0xe7, 0xcc, 0x23, 0x58, 0xba, 0xec, 0xa1, 0x34, 0xc8, 0x25, 0xa7};
+
+static const uint8_t test_result_sha256[32] = {
+ 0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, 0x41, 0x41, 0x40, 0xde,
+ 0x5d, 0xae, 0x22, 0x23, 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c,
+ 0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad};
+
+static const uint8_t test_result_md5[16] = {
+ 0x90, 0x01, 0x50, 0x98, 0x3c, 0xd2, 0x4f, 0xb0, 0xd6, 0x96, 0x3f, 0x7d,
+ 0x28, 0xe1, 0x7f, 0x72};
+
+/*
+ * The Scatter-Gather Test vector is the ascii "abc" "def" "ghi", broken
+ * into blocks of 3 characters as shown
+ *
+ * Expected results were generated using command line utitiles:
+ *
+ * echo -n -e 'abcdefghijkl' | dd of=/tmp/test
+ * for hash in sha512sum sha384sum sha256sum; do $hash /tmp/test; done
+ *
+ */
+static const uint8_t test_vector_sg1[6] = {0x61, 0x62, 0x63, 0x64, 0x65, 0x66};
+static const uint8_t test_vector_sg2[3] = {0x67, 0x68, 0x69};
+static const uint8_t test_vector_sg3[3] = {0x6a, 0x6b, 0x6c};
+
+static const uint8_t test_result_sg_sha512[64] = {
+ 0x17, 0x80, 0x7c, 0x72, 0x8e, 0xe3, 0xba, 0x35, 0xe7, 0xcf, 0x7a, 0xf8,
+ 0x23, 0x11, 0x6d, 0x26, 0xe4, 0x1e, 0x5d, 0x4d, 0x6c, 0x2f, 0xf1, 0xf3,
+ 0x72, 0x0d, 0x3d, 0x96, 0xaa, 0xcb, 0x6f, 0x69, 0xde, 0x64, 0x2e, 0x63,
+ 0xd5, 0xb7, 0x3f, 0xc3, 0x96, 0xc1, 0x2b, 0xe3, 0x8b, 0x2b, 0xd5, 0xd8,
+ 0x84, 0x25, 0x7c, 0x32, 0xc8, 0xf6, 0xd0, 0x85, 0x4a, 0xe6, 0xb5, 0x40,
+ 0xf8, 0x6d, 0xda, 0x2e};
+
+static const uint8_t test_result_sg_sha384[48] = {
+ 0x10, 0x3c, 0xa9, 0x6c, 0x06, 0xa1, 0xce, 0x79, 0x8f, 0x08, 0xf8, 0xef,
+ 0xf0, 0xdf, 0xb0, 0xcc, 0xdb, 0x56, 0x7d, 0x48, 0xb2, 0x85, 0xb2, 0x3d,
+ 0x0c, 0xd7, 0x73, 0x45, 0x46, 0x67, 0xa3, 0xc2, 0xfa, 0x5f, 0x1b, 0x58,
+ 0xd9, 0xcd, 0xf2, 0x32, 0x9b, 0xd9, 0x97, 0x97, 0x30, 0xbf, 0xaa, 0xff};
+
+static const uint8_t test_result_sg_sha256[32] = {
+ 0xd6, 0x82, 0xed, 0x4c, 0xa4, 0xd9, 0x89, 0xc1, 0x34, 0xec, 0x94, 0xf1,
+ 0x55, 0x1e, 0x1e, 0xc5, 0x80, 0xdd, 0x6d, 0x5a, 0x6e, 0xcd, 0xe9, 0xf3,
+ 0xd3, 0x5e, 0x6e, 0x4a, 0x71, 0x7f, 0xbd, 0xe4};
+
+/*
+ * The accumulative mode requires firmware to provide internal initial state
+ * and message padding (including length L at the end of padding).
+ *
+ * This test vector is a ascii text "abc" with padding message.
+ *
+ * Expected results were generated using command line utitiles:
+ *
+ * echo -n -e 'abc' | dd of=/tmp/test
+ * for hash in sha512sum sha384sum sha256sum; do $hash /tmp/test; done
+ */
+static const uint8_t test_vector_accum_512[128] = {
+ 0x61, 0x62, 0x63, 0x80, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18};
+
+static const uint8_t test_vector_accum_384[128] = {
+ 0x61, 0x62, 0x63, 0x80, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18};
+
+static const uint8_t test_vector_accum_256[64] = {
+ 0x61, 0x62, 0x63, 0x80, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18};
+
+static const uint8_t test_result_accum_sha512[64] = {
+ 0xdd, 0xaf, 0x35, 0xa1, 0x93, 0x61, 0x7a, 0xba, 0xcc, 0x41, 0x73, 0x49,
+ 0xae, 0x20, 0x41, 0x31, 0x12, 0xe6, 0xfa, 0x4e, 0x89, 0xa9, 0x7e, 0xa2,
+ 0x0a, 0x9e, 0xee, 0xe6, 0x4b, 0x55, 0xd3, 0x9a, 0x21, 0x92, 0x99, 0x2a,
+ 0x27, 0x4f, 0xc1, 0xa8, 0x36, 0xba, 0x3c, 0x23, 0xa3, 0xfe, 0xeb, 0xbd,
+ 0x45, 0x4d, 0x44, 0x23, 0x64, 0x3c, 0xe8, 0x0e, 0x2a, 0x9a, 0xc9, 0x4f,
+ 0xa5, 0x4c, 0xa4, 0x9f};
+
+static const uint8_t test_result_accum_sha384[48] = {
+ 0xcb, 0x00, 0x75, 0x3f, 0x45, 0xa3, 0x5e, 0x8b, 0xb5, 0xa0, 0x3d, 0x69,
+ 0x9a, 0xc6, 0x50, 0x07, 0x27, 0x2c, 0x32, 0xab, 0x0e, 0xde, 0xd1, 0x63,
+ 0x1a, 0x8b, 0x60, 0x5a, 0x43, 0xff, 0x5b, 0xed, 0x80, 0x86, 0x07, 0x2b,
+ 0xa1, 0xe7, 0xcc, 0x23, 0x58, 0xba, 0xec, 0xa1, 0x34, 0xc8, 0x25, 0xa7};
+
+static const uint8_t test_result_accum_sha256[32] = {
+ 0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, 0x41, 0x41, 0x40, 0xde,
+ 0x5d, 0xae, 0x22, 0x23, 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c,
+ 0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad};
+
+static void write_regs(QTestState *s, uint32_t base, uint64_t src,
+ uint32_t length, uint64_t out, uint32_t method)
+{
+ qtest_writel(s, base + HACE_HASH_SRC, extract64(src, 0, 32));
+ qtest_writel(s, base + HACE_HASH_SRC_HI, extract64(src, 32, 32));
+ qtest_writel(s, base + HACE_HASH_DIGEST, extract64(out, 0, 32));
+ qtest_writel(s, base + HACE_HASH_DIGEST_HI, extract64(out, 32, 32));
+ qtest_writel(s, base + HACE_HASH_DATA_LEN, length);
+ qtest_writel(s, base + HACE_HASH_CMD, HACE_SHA_BE_EN | method);
+}
+
+void aspeed_test_md5(const char *machine, const uint32_t base,
+ const uint64_t src_addr)
+
+{
+ QTestState *s = qtest_init(machine);
+
+ uint64_t digest_addr = src_addr + 0x010000;
+ uint8_t digest[16] = {0};
+
+ /* Check engine is idle, no busy or irq bits set */
+ g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0);
+
+ /* Write test vector into memory */
+ qtest_memwrite(s, src_addr, test_vector, sizeof(test_vector));
+
+ write_regs(s, base, src_addr, sizeof(test_vector),
+ digest_addr, HACE_ALGO_MD5);
+
+ /* Check hash IRQ status is asserted */
+ g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0x00000200);
+
+ /* Clear IRQ status and check status is deasserted */
+ qtest_writel(s, base + HACE_STS, 0x00000200);
+ g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0);
+
+ /* Read computed digest from memory */
+ qtest_memread(s, digest_addr, digest, sizeof(digest));
+
+ /* Check result of computation */
+ g_assert_cmpmem(digest, sizeof(digest),
+ test_result_md5, sizeof(digest));
+
+ qtest_quit(s);
+}
+
+void aspeed_test_sha256(const char *machine, const uint32_t base,
+ const uint64_t src_addr)
+{
+ QTestState *s = qtest_init(machine);
+
+ const uint64_t digest_addr = src_addr + 0x10000;
+ uint8_t digest[32] = {0};
+
+ /* Check engine is idle, no busy or irq bits set */
+ g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0);
+
+ /* Write test vector into memory */
+ qtest_memwrite(s, src_addr, test_vector, sizeof(test_vector));
+
+ write_regs(s, base, src_addr, sizeof(test_vector), digest_addr,
+ HACE_ALGO_SHA256);
+
+ /* Check hash IRQ status is asserted */
+ g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0x00000200);
+
+ /* Clear IRQ status and check status is deasserted */
+ qtest_writel(s, base + HACE_STS, 0x00000200);
+ g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0);
+
+ /* Read computed digest from memory */
+ qtest_memread(s, digest_addr, digest, sizeof(digest));
+
+ /* Check result of computation */
+ g_assert_cmpmem(digest, sizeof(digest),
+ test_result_sha256, sizeof(digest));
+
+ qtest_quit(s);
+}
+
+void aspeed_test_sha384(const char *machine, const uint32_t base,
+ const uint64_t src_addr)
+{
+ QTestState *s = qtest_init(machine);
+
+ const uint64_t digest_addr = src_addr + 0x10000;
+ uint8_t digest[48] = {0};
+
+ /* Check engine is idle, no busy or irq bits set */
+ g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0);
+
+ /* Write test vector into memory */
+ qtest_memwrite(s, src_addr, test_vector, sizeof(test_vector));
+
+ write_regs(s, base, src_addr, sizeof(test_vector), digest_addr,
+ HACE_ALGO_SHA384);
+
+ /* Check hash IRQ status is asserted */
+ g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0x00000200);
+
+ /* Clear IRQ status and check status is deasserted */
+ qtest_writel(s, base + HACE_STS, 0x00000200);
+ g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0);
+
+ /* Read computed digest from memory */
+ qtest_memread(s, digest_addr, digest, sizeof(digest));
+
+ /* Check result of computation */
+ g_assert_cmpmem(digest, sizeof(digest),
+ test_result_sha384, sizeof(digest));
+
+ qtest_quit(s);
+}
+
+void aspeed_test_sha512(const char *machine, const uint32_t base,
+ const uint64_t src_addr)
+{
+ QTestState *s = qtest_init(machine);
+
+ const uint64_t digest_addr = src_addr + 0x10000;
+ uint8_t digest[64] = {0};
+
+ /* Check engine is idle, no busy or irq bits set */
+ g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0);
+
+ /* Write test vector into memory */
+ qtest_memwrite(s, src_addr, test_vector, sizeof(test_vector));
+
+ write_regs(s, base, src_addr, sizeof(test_vector), digest_addr,
+ HACE_ALGO_SHA512);
+
+ /* Check hash IRQ status is asserted */
+ g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0x00000200);
+
+ /* Clear IRQ status and check status is deasserted */
+ qtest_writel(s, base + HACE_STS, 0x00000200);
+ g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0);
+
+ /* Read computed digest from memory */
+ qtest_memread(s, digest_addr, digest, sizeof(digest));
+
+ /* Check result of computation */
+ g_assert_cmpmem(digest, sizeof(digest),
+ test_result_sha512, sizeof(digest));
+
+ qtest_quit(s);
+}
+
+void aspeed_test_sha256_sg(const char *machine, const uint32_t base,
+ const uint64_t src_addr)
+{
+ QTestState *s = qtest_init(machine);
+
+ const uint64_t src_addr_1 = src_addr + 0x10000;
+ const uint64_t src_addr_2 = src_addr + 0x20000;
+ const uint64_t src_addr_3 = src_addr + 0x30000;
+ const uint64_t digest_addr = src_addr + 0x40000;
+ uint8_t digest[32] = {0};
+ struct AspeedSgList array[] = {
+ { cpu_to_le32(sizeof(test_vector_sg1)),
+ cpu_to_le32(src_addr_1) },
+ { cpu_to_le32(sizeof(test_vector_sg2)),
+ cpu_to_le32(src_addr_2) },
+ { cpu_to_le32(sizeof(test_vector_sg3) | SG_LIST_LEN_LAST),
+ cpu_to_le32(src_addr_3) },
+ };
+
+ /* Check engine is idle, no busy or irq bits set */
+ g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0);
+
+ /* Write test vector into memory */
+ qtest_memwrite(s, src_addr_1, test_vector_sg1, sizeof(test_vector_sg1));
+ qtest_memwrite(s, src_addr_2, test_vector_sg2, sizeof(test_vector_sg2));
+ qtest_memwrite(s, src_addr_3, test_vector_sg3, sizeof(test_vector_sg3));
+ qtest_memwrite(s, src_addr, array, sizeof(array));
+
+ write_regs(s, base, src_addr,
+ (sizeof(test_vector_sg1)
+ + sizeof(test_vector_sg2)
+ + sizeof(test_vector_sg3)),
+ digest_addr, HACE_ALGO_SHA256 | HACE_SG_EN);
+
+ /* Check hash IRQ status is asserted */
+ g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0x00000200);
+
+ /* Clear IRQ status and check status is deasserted */
+ qtest_writel(s, base + HACE_STS, 0x00000200);
+ g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0);
+
+ /* Read computed digest from memory */
+ qtest_memread(s, digest_addr, digest, sizeof(digest));
+
+ /* Check result of computation */
+ g_assert_cmpmem(digest, sizeof(digest),
+ test_result_sg_sha256, sizeof(digest));
+
+ qtest_quit(s);
+}
+
+void aspeed_test_sha384_sg(const char *machine, const uint32_t base,
+ const uint64_t src_addr)
+{
+ QTestState *s = qtest_init(machine);
+
+ const uint64_t src_addr_1 = src_addr + 0x10000;
+ const uint64_t src_addr_2 = src_addr + 0x20000;
+ const uint64_t src_addr_3 = src_addr + 0x30000;
+ const uint64_t digest_addr = src_addr + 0x40000;
+ uint8_t digest[48] = {0};
+ struct AspeedSgList array[] = {
+ { cpu_to_le32(sizeof(test_vector_sg1)),
+ cpu_to_le32(src_addr_1) },
+ { cpu_to_le32(sizeof(test_vector_sg2)),
+ cpu_to_le32(src_addr_2) },
+ { cpu_to_le32(sizeof(test_vector_sg3) | SG_LIST_LEN_LAST),
+ cpu_to_le32(src_addr_3) },
+ };
+
+ /* Check engine is idle, no busy or irq bits set */
+ g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0);
+
+ /* Write test vector into memory */
+ qtest_memwrite(s, src_addr_1, test_vector_sg1, sizeof(test_vector_sg1));
+ qtest_memwrite(s, src_addr_2, test_vector_sg2, sizeof(test_vector_sg2));
+ qtest_memwrite(s, src_addr_3, test_vector_sg3, sizeof(test_vector_sg3));
+ qtest_memwrite(s, src_addr, array, sizeof(array));
+
+ write_regs(s, base, src_addr,
+ (sizeof(test_vector_sg1)
+ + sizeof(test_vector_sg2)
+ + sizeof(test_vector_sg3)),
+ digest_addr, HACE_ALGO_SHA384 | HACE_SG_EN);
+
+ /* Check hash IRQ status is asserted */
+ g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0x00000200);
+
+ /* Clear IRQ status and check status is deasserted */
+ qtest_writel(s, base + HACE_STS, 0x00000200);
+ g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0);
+
+ /* Read computed digest from memory */
+ qtest_memread(s, digest_addr, digest, sizeof(digest));
+
+ /* Check result of computation */
+ g_assert_cmpmem(digest, sizeof(digest),
+ test_result_sg_sha384, sizeof(digest));
+
+ qtest_quit(s);
+}
+
+void aspeed_test_sha512_sg(const char *machine, const uint32_t base,
+ const uint64_t src_addr)
+{
+ QTestState *s = qtest_init(machine);
+
+ const uint64_t src_addr_1 = src_addr + 0x10000;
+ const uint64_t src_addr_2 = src_addr + 0x20000;
+ const uint64_t src_addr_3 = src_addr + 0x30000;
+ const uint64_t digest_addr = src_addr + 0x40000;
+ uint8_t digest[64] = {0};
+ struct AspeedSgList array[] = {
+ { cpu_to_le32(sizeof(test_vector_sg1)),
+ cpu_to_le32(src_addr_1) },
+ { cpu_to_le32(sizeof(test_vector_sg2)),
+ cpu_to_le32(src_addr_2) },
+ { cpu_to_le32(sizeof(test_vector_sg3) | SG_LIST_LEN_LAST),
+ cpu_to_le32(src_addr_3) },
+ };
+
+ /* Check engine is idle, no busy or irq bits set */
+ g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0);
+
+ /* Write test vector into memory */
+ qtest_memwrite(s, src_addr_1, test_vector_sg1, sizeof(test_vector_sg1));
+ qtest_memwrite(s, src_addr_2, test_vector_sg2, sizeof(test_vector_sg2));
+ qtest_memwrite(s, src_addr_3, test_vector_sg3, sizeof(test_vector_sg3));
+ qtest_memwrite(s, src_addr, array, sizeof(array));
+
+ write_regs(s, base, src_addr,
+ (sizeof(test_vector_sg1)
+ + sizeof(test_vector_sg2)
+ + sizeof(test_vector_sg3)),
+ digest_addr, HACE_ALGO_SHA512 | HACE_SG_EN);
+
+ /* Check hash IRQ status is asserted */
+ g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0x00000200);
+
+ /* Clear IRQ status and check status is deasserted */
+ qtest_writel(s, base + HACE_STS, 0x00000200);
+ g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0);
+
+ /* Read computed digest from memory */
+ qtest_memread(s, digest_addr, digest, sizeof(digest));
+
+ /* Check result of computation */
+ g_assert_cmpmem(digest, sizeof(digest),
+ test_result_sg_sha512, sizeof(digest));
+
+ qtest_quit(s);
+}
+
+void aspeed_test_sha256_accum(const char *machine, const uint32_t base,
+ const uint64_t src_addr)
+{
+ QTestState *s = qtest_init(machine);
+
+ const uint64_t buffer_addr = src_addr + 0x10000;
+ const uint64_t digest_addr = src_addr + 0x40000;
+ uint8_t digest[32] = {0};
+ struct AspeedSgList array[] = {
+ { cpu_to_le32(sizeof(test_vector_accum_256) | SG_LIST_LEN_LAST),
+ cpu_to_le32(buffer_addr) },
+ };
+
+ /* Check engine is idle, no busy or irq bits set */
+ g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0);
+
+ /* Write test vector into memory */
+ qtest_memwrite(s, buffer_addr, test_vector_accum_256,
+ sizeof(test_vector_accum_256));
+ qtest_memwrite(s, src_addr, array, sizeof(array));
+
+ write_regs(s, base, src_addr, sizeof(test_vector_accum_256),
+ digest_addr, HACE_ALGO_SHA256 | HACE_SG_EN | HACE_ACCUM_EN);
+
+ /* Check hash IRQ status is asserted */
+ g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0x00000200);
+
+ /* Clear IRQ status and check status is deasserted */
+ qtest_writel(s, base + HACE_STS, 0x00000200);
+ g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0);
+
+ /* Read computed digest from memory */
+ qtest_memread(s, digest_addr, digest, sizeof(digest));
+
+ /* Check result of computation */
+ g_assert_cmpmem(digest, sizeof(digest),
+ test_result_accum_sha256, sizeof(digest));
+
+ qtest_quit(s);
+}
+
+void aspeed_test_sha384_accum(const char *machine, const uint32_t base,
+ const uint64_t src_addr)
+{
+ QTestState *s = qtest_init(machine);
+
+ const uint64_t buffer_addr = src_addr + 0x10000;
+ const uint64_t digest_addr = src_addr + 0x40000;
+ uint8_t digest[48] = {0};
+ struct AspeedSgList array[] = {
+ { cpu_to_le32(sizeof(test_vector_accum_384) | SG_LIST_LEN_LAST),
+ cpu_to_le32(buffer_addr) },
+ };
+
+ /* Check engine is idle, no busy or irq bits set */
+ g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0);
+
+ /* Write test vector into memory */
+ qtest_memwrite(s, buffer_addr, test_vector_accum_384,
+ sizeof(test_vector_accum_384));
+ qtest_memwrite(s, src_addr, array, sizeof(array));
+
+ write_regs(s, base, src_addr, sizeof(test_vector_accum_384),
+ digest_addr, HACE_ALGO_SHA384 | HACE_SG_EN | HACE_ACCUM_EN);
+
+ /* Check hash IRQ status is asserted */
+ g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0x00000200);
+
+ /* Clear IRQ status and check status is deasserted */
+ qtest_writel(s, base + HACE_STS, 0x00000200);
+ g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0);
+
+ /* Read computed digest from memory */
+ qtest_memread(s, digest_addr, digest, sizeof(digest));
+
+ /* Check result of computation */
+ g_assert_cmpmem(digest, sizeof(digest),
+ test_result_accum_sha384, sizeof(digest));
+
+ qtest_quit(s);
+}
+
+void aspeed_test_sha512_accum(const char *machine, const uint32_t base,
+ const uint64_t src_addr)
+{
+ QTestState *s = qtest_init(machine);
+
+ const uint64_t buffer_addr = src_addr + 0x10000;
+ const uint64_t digest_addr = src_addr + 0x40000;
+ uint8_t digest[64] = {0};
+ struct AspeedSgList array[] = {
+ { cpu_to_le32(sizeof(test_vector_accum_512) | SG_LIST_LEN_LAST),
+ cpu_to_le32(buffer_addr) },
+ };
+
+ /* Check engine is idle, no busy or irq bits set */
+ g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0);
+
+ /* Write test vector into memory */
+ qtest_memwrite(s, buffer_addr, test_vector_accum_512,
+ sizeof(test_vector_accum_512));
+ qtest_memwrite(s, src_addr, array, sizeof(array));
+
+ write_regs(s, base, src_addr, sizeof(test_vector_accum_512),
+ digest_addr, HACE_ALGO_SHA512 | HACE_SG_EN | HACE_ACCUM_EN);
+
+ /* Check hash IRQ status is asserted */
+ g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0x00000200);
+
+ /* Clear IRQ status and check status is deasserted */
+ qtest_writel(s, base + HACE_STS, 0x00000200);
+ g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0);
+
+ /* Read computed digest from memory */
+ qtest_memread(s, digest_addr, digest, sizeof(digest));
+
+ /* Check result of computation */
+ g_assert_cmpmem(digest, sizeof(digest),
+ test_result_accum_sha512, sizeof(digest));
+
+ qtest_quit(s);
+}
+
+void aspeed_test_addresses(const char *machine, const uint32_t base,
+ const struct AspeedMasks *expected)
+{
+ QTestState *s = qtest_init(machine);
+
+ /*
+ * Check command mode is zero, meaning engine is in direct access mode,
+ * as this affects the masking behavior of the HASH_SRC register.
+ */
+ g_assert_cmphex(qtest_readl(s, base + HACE_CMD), ==, 0);
+ g_assert_cmphex(qtest_readl(s, base + HACE_HASH_SRC), ==, 0);
+ g_assert_cmphex(qtest_readl(s, base + HACE_HASH_SRC_HI), ==, 0);
+ g_assert_cmphex(qtest_readl(s, base + HACE_HASH_DIGEST), ==, 0);
+ g_assert_cmphex(qtest_readl(s, base + HACE_HASH_DIGEST_HI), ==, 0);
+ g_assert_cmphex(qtest_readl(s, base + HACE_HASH_KEY_BUFF), ==, 0);
+ g_assert_cmphex(qtest_readl(s, base + HACE_HASH_KEY_BUFF_HI), ==, 0);
+ g_assert_cmphex(qtest_readl(s, base + HACE_HASH_DATA_LEN), ==, 0);
+
+ /* Check that the address masking is correct */
+ qtest_writel(s, base + HACE_HASH_SRC, 0xffffffff);
+ g_assert_cmphex(qtest_readl(s, base + HACE_HASH_SRC), ==, expected->src);
+
+ qtest_writel(s, base + HACE_HASH_SRC_HI, 0xffffffff);
+ g_assert_cmphex(qtest_readl(s, base + HACE_HASH_SRC_HI),
+ ==, expected->src_hi);
+
+ qtest_writel(s, base + HACE_HASH_DIGEST, 0xffffffff);
+ g_assert_cmphex(qtest_readl(s, base + HACE_HASH_DIGEST), ==,
+ expected->dest);
+
+ qtest_writel(s, base + HACE_HASH_DIGEST_HI, 0xffffffff);
+ g_assert_cmphex(qtest_readl(s, base + HACE_HASH_DIGEST_HI), ==,
+ expected->dest_hi);
+
+ qtest_writel(s, base + HACE_HASH_KEY_BUFF, 0xffffffff);
+ g_assert_cmphex(qtest_readl(s, base + HACE_HASH_KEY_BUFF), ==,
+ expected->key);
+
+ qtest_writel(s, base + HACE_HASH_KEY_BUFF_HI, 0xffffffff);
+ g_assert_cmphex(qtest_readl(s, base + HACE_HASH_KEY_BUFF_HI), ==,
+ expected->key_hi);
+
+ qtest_writel(s, base + HACE_HASH_DATA_LEN, 0xffffffff);
+ g_assert_cmphex(qtest_readl(s, base + HACE_HASH_DATA_LEN), ==,
+ expected->len);
+
+ /* Reset to zero */
+ qtest_writel(s, base + HACE_HASH_SRC, 0);
+ qtest_writel(s, base + HACE_HASH_SRC_HI, 0);
+ qtest_writel(s, base + HACE_HASH_DIGEST, 0);
+ qtest_writel(s, base + HACE_HASH_DIGEST_HI, 0);
+ qtest_writel(s, base + HACE_HASH_KEY_BUFF, 0);
+ qtest_writel(s, base + HACE_HASH_KEY_BUFF_HI, 0);
+ qtest_writel(s, base + HACE_HASH_DATA_LEN, 0);
+
+ /* Check that all bits are now zero */
+ g_assert_cmphex(qtest_readl(s, base + HACE_HASH_SRC), ==, 0);
+ g_assert_cmphex(qtest_readl(s, base + HACE_HASH_SRC_HI), ==, 0);
+ g_assert_cmphex(qtest_readl(s, base + HACE_HASH_DIGEST), ==, 0);
+ g_assert_cmphex(qtest_readl(s, base + HACE_HASH_DIGEST_HI), ==, 0);
+ g_assert_cmphex(qtest_readl(s, base + HACE_HASH_KEY_BUFF), ==, 0);
+ g_assert_cmphex(qtest_readl(s, base + HACE_HASH_KEY_BUFF_HI), ==, 0);
+ g_assert_cmphex(qtest_readl(s, base + HACE_HASH_DATA_LEN), ==, 0);
+
+ qtest_quit(s);
+}
+
diff --git a/tests/qtest/aspeed-hace-utils.h b/tests/qtest/aspeed-hace-utils.h
new file mode 100644
index 0000000..c8b2ec4
--- /dev/null
+++ b/tests/qtest/aspeed-hace-utils.h
@@ -0,0 +1,84 @@
+/*
+ * QTest testcase for the ASPEED Hash and Crypto Engine
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2021 IBM Corp.
+ */
+
+#ifndef TESTS_ASPEED_HACE_UTILS_H
+#define TESTS_ASPEED_HACE_UTILS_H
+
+#include "qemu/osdep.h"
+#include "libqtest.h"
+#include "qemu/bitops.h"
+
+#define HACE_CMD 0x10
+#define HACE_SHA_BE_EN BIT(3)
+#define HACE_MD5_LE_EN BIT(2)
+#define HACE_ALGO_MD5 0
+#define HACE_ALGO_SHA1 BIT(5)
+#define HACE_ALGO_SHA224 BIT(6)
+#define HACE_ALGO_SHA256 (BIT(4) | BIT(6))
+#define HACE_ALGO_SHA512 (BIT(5) | BIT(6))
+#define HACE_ALGO_SHA384 (BIT(5) | BIT(6) | BIT(10))
+#define HACE_SG_EN BIT(18)
+#define HACE_ACCUM_EN BIT(8)
+
+#define HACE_STS 0x1c
+#define HACE_RSA_ISR BIT(13)
+#define HACE_CRYPTO_ISR BIT(12)
+#define HACE_HASH_ISR BIT(9)
+#define HACE_RSA_BUSY BIT(2)
+#define HACE_CRYPTO_BUSY BIT(1)
+#define HACE_HASH_BUSY BIT(0)
+#define HACE_HASH_SRC 0x20
+#define HACE_HASH_DIGEST 0x24
+#define HACE_HASH_KEY_BUFF 0x28
+#define HACE_HASH_DATA_LEN 0x2c
+#define HACE_HASH_CMD 0x30
+#define HACE_HASH_SRC_HI 0x90
+#define HACE_HASH_DIGEST_HI 0x94
+#define HACE_HASH_KEY_BUFF_HI 0x98
+
+/* Scatter-Gather Hash */
+#define SG_LIST_LEN_LAST BIT(31)
+struct AspeedSgList {
+ uint32_t len;
+ uint32_t addr;
+} __attribute__ ((__packed__));
+
+struct AspeedMasks {
+ uint32_t src;
+ uint32_t dest;
+ uint32_t key;
+ uint32_t len;
+ uint32_t src_hi;
+ uint32_t dest_hi;
+ uint32_t key_hi;
+};
+
+void aspeed_test_md5(const char *machine, const uint32_t base,
+ const uint64_t src_addr);
+void aspeed_test_sha256(const char *machine, const uint32_t base,
+ const uint64_t src_addr);
+void aspeed_test_sha384(const char *machine, const uint32_t base,
+ const uint64_t src_addr);
+void aspeed_test_sha512(const char *machine, const uint32_t base,
+ const uint64_t src_addr);
+void aspeed_test_sha256_sg(const char *machine, const uint32_t base,
+ const uint64_t src_addr);
+void aspeed_test_sha384_sg(const char *machine, const uint32_t base,
+ const uint64_t src_addr);
+void aspeed_test_sha512_sg(const char *machine, const uint32_t base,
+ const uint64_t src_addr);
+void aspeed_test_sha256_accum(const char *machine, const uint32_t base,
+ const uint64_t src_addr);
+void aspeed_test_sha384_accum(const char *machine, const uint32_t base,
+ const uint64_t src_addr);
+void aspeed_test_sha512_accum(const char *machine, const uint32_t base,
+ const uint64_t src_addr);
+void aspeed_test_addresses(const char *machine, const uint32_t base,
+ const struct AspeedMasks *expected);
+
+#endif /* TESTS_ASPEED_HACE_UTILS_H */
+
diff --git a/tests/qtest/aspeed-smc-utils.c b/tests/qtest/aspeed-smc-utils.c
new file mode 100644
index 0000000..c27d09e
--- /dev/null
+++ b/tests/qtest/aspeed-smc-utils.c
@@ -0,0 +1,686 @@
+/*
+ * QTest testcase for the M25P80 Flash (Using the Aspeed SPI
+ * Controller)
+ *
+ * Copyright (C) 2016 IBM Corp.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/bswap.h"
+#include "libqtest-single.h"
+#include "qemu/bitops.h"
+#include "aspeed-smc-utils.h"
+
+/*
+ * Use an explicit bswap for the values read/wrote to the flash region
+ * as they are BE and the Aspeed CPU is LE.
+ */
+static inline uint32_t make_be32(uint32_t data)
+{
+ return bswap32(data);
+}
+
+static inline void spi_writel(const AspeedSMCTestData *data, uint64_t offset,
+ uint32_t value)
+{
+ qtest_writel(data->s, data->spi_base + offset, value);
+}
+
+static inline uint32_t spi_readl(const AspeedSMCTestData *data, uint64_t offset)
+{
+ return qtest_readl(data->s, data->spi_base + offset);
+}
+
+static inline void flash_writeb(const AspeedSMCTestData *data, uint64_t offset,
+ uint8_t value)
+{
+ qtest_writeb(data->s, data->flash_base + offset, value);
+}
+
+static inline void flash_writel(const AspeedSMCTestData *data, uint64_t offset,
+ uint32_t value)
+{
+ qtest_writel(data->s, data->flash_base + offset, value);
+}
+
+static inline uint8_t flash_readb(const AspeedSMCTestData *data,
+ uint64_t offset)
+{
+ return qtest_readb(data->s, data->flash_base + offset);
+}
+
+static inline uint32_t flash_readl(const AspeedSMCTestData *data,
+ uint64_t offset)
+{
+ return qtest_readl(data->s, data->flash_base + offset);
+}
+
+static void spi_conf(const AspeedSMCTestData *data, uint32_t value)
+{
+ uint32_t conf = spi_readl(data, R_CONF);
+
+ conf |= value;
+ spi_writel(data, R_CONF, conf);
+}
+
+static void spi_conf_remove(const AspeedSMCTestData *data, uint32_t value)
+{
+ uint32_t conf = spi_readl(data, R_CONF);
+
+ conf &= ~value;
+ spi_writel(data, R_CONF, conf);
+}
+
+static void spi_ce_ctrl(const AspeedSMCTestData *data, uint32_t value)
+{
+ uint32_t conf = spi_readl(data, R_CE_CTRL);
+
+ conf |= value;
+ spi_writel(data, R_CE_CTRL, conf);
+}
+
+static void spi_ctrl_setmode(const AspeedSMCTestData *data, uint8_t mode,
+ uint8_t cmd)
+{
+ uint32_t ctrl_reg = R_CTRL0 + data->cs * 4;
+ uint32_t ctrl = spi_readl(data, ctrl_reg);
+ ctrl &= ~(CTRL_USERMODE | 0xff << 16);
+ ctrl |= mode | (cmd << 16);
+ spi_writel(data, ctrl_reg, ctrl);
+}
+
+static void spi_ctrl_start_user(const AspeedSMCTestData *data)
+{
+ uint32_t ctrl_reg = R_CTRL0 + data->cs * 4;
+ uint32_t ctrl = spi_readl(data, ctrl_reg);
+
+ ctrl |= CTRL_USERMODE | CTRL_CE_STOP_ACTIVE;
+ spi_writel(data, ctrl_reg, ctrl);
+
+ ctrl &= ~CTRL_CE_STOP_ACTIVE;
+ spi_writel(data, ctrl_reg, ctrl);
+}
+
+static void spi_ctrl_stop_user(const AspeedSMCTestData *data)
+{
+ uint32_t ctrl_reg = R_CTRL0 + data->cs * 4;
+ uint32_t ctrl = spi_readl(data, ctrl_reg);
+
+ ctrl |= CTRL_USERMODE | CTRL_CE_STOP_ACTIVE;
+ spi_writel(data, ctrl_reg, ctrl);
+}
+
+static void spi_ctrl_set_io_mode(const AspeedSMCTestData *data, uint32_t value)
+{
+ uint32_t ctrl_reg = R_CTRL0 + data->cs * 4;
+ uint32_t ctrl = spi_readl(data, ctrl_reg);
+ uint32_t mode;
+
+ mode = value & CTRL_IO_MODE_MASK;
+ ctrl &= ~CTRL_IO_MODE_MASK;
+ ctrl |= mode;
+ spi_writel(data, ctrl_reg, ctrl);
+}
+
+static void flash_reset(const AspeedSMCTestData *data)
+{
+ spi_conf(data, 1 << (CONF_ENABLE_W0 + data->cs));
+
+ spi_ctrl_start_user(data);
+ flash_writeb(data, 0, RESET_ENABLE);
+ flash_writeb(data, 0, RESET_MEMORY);
+ flash_writeb(data, 0, WREN);
+ flash_writeb(data, 0, BULK_ERASE);
+ flash_writeb(data, 0, WRDI);
+ spi_ctrl_stop_user(data);
+
+ spi_conf_remove(data, 1 << (CONF_ENABLE_W0 + data->cs));
+}
+
+static void read_page(const AspeedSMCTestData *data, uint32_t addr,
+ uint32_t *page)
+{
+ int i;
+
+ spi_ctrl_start_user(data);
+
+ flash_writeb(data, 0, EN_4BYTE_ADDR);
+ flash_writeb(data, 0, READ);
+ flash_writel(data, 0, make_be32(addr));
+
+ /* Continuous read are supported */
+ for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) {
+ page[i] = make_be32(flash_readl(data, 0));
+ }
+ spi_ctrl_stop_user(data);
+}
+
+static void read_page_mem(const AspeedSMCTestData *data, uint32_t addr,
+ uint32_t *page)
+{
+ int i;
+
+ /* move out USER mode to use direct reads from the AHB bus */
+ spi_ctrl_setmode(data, CTRL_READMODE, READ);
+
+ for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) {
+ page[i] = make_be32(flash_readl(data, addr + i * 4));
+ }
+}
+
+static void write_page_mem(const AspeedSMCTestData *data, uint32_t addr,
+ uint32_t write_value)
+{
+ spi_ctrl_setmode(data, CTRL_WRITEMODE, PP);
+
+ for (int i = 0; i < FLASH_PAGE_SIZE / 4; i++) {
+ flash_writel(data, addr + i * 4, write_value);
+ }
+}
+
+static void assert_page_mem(const AspeedSMCTestData *data, uint32_t addr,
+ uint32_t expected_value)
+{
+ uint32_t page[FLASH_PAGE_SIZE / 4];
+ read_page_mem(data, addr, page);
+ for (int i = 0; i < FLASH_PAGE_SIZE / 4; i++) {
+ g_assert_cmphex(page[i], ==, expected_value);
+ }
+}
+
+void aspeed_smc_test_read_jedec(const void *data)
+{
+ const AspeedSMCTestData *test_data = (const AspeedSMCTestData *)data;
+ uint32_t jedec = 0x0;
+
+ spi_conf(test_data, 1 << (CONF_ENABLE_W0 + test_data->cs));
+
+ spi_ctrl_start_user(test_data);
+ flash_writeb(test_data, 0, JEDEC_READ);
+ jedec |= flash_readb(test_data, 0) << 16;
+ jedec |= flash_readb(test_data, 0) << 8;
+ jedec |= flash_readb(test_data, 0);
+ spi_ctrl_stop_user(test_data);
+
+ flash_reset(test_data);
+
+ g_assert_cmphex(jedec, ==, test_data->jedec_id);
+}
+
+void aspeed_smc_test_erase_sector(const void *data)
+{
+ const AspeedSMCTestData *test_data = (const AspeedSMCTestData *)data;
+ uint32_t some_page_addr = test_data->page_addr;
+ uint32_t page[FLASH_PAGE_SIZE / 4];
+ int i;
+
+ spi_conf(test_data, 1 << (CONF_ENABLE_W0 + test_data->cs));
+
+ /*
+ * Previous page should be full of 0xffs after backend is
+ * initialized
+ */
+ read_page(test_data, some_page_addr - FLASH_PAGE_SIZE, page);
+ for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) {
+ g_assert_cmphex(page[i], ==, 0xffffffff);
+ }
+
+ spi_ctrl_start_user(test_data);
+ flash_writeb(test_data, 0, EN_4BYTE_ADDR);
+ flash_writeb(test_data, 0, WREN);
+ flash_writeb(test_data, 0, PP);
+ flash_writel(test_data, 0, make_be32(some_page_addr));
+
+ /* Fill the page with its own addresses */
+ for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) {
+ flash_writel(test_data, 0, make_be32(some_page_addr + i * 4));
+ }
+ spi_ctrl_stop_user(test_data);
+
+ /* Check the page is correctly written */
+ read_page(test_data, some_page_addr, page);
+ for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) {
+ g_assert_cmphex(page[i], ==, some_page_addr + i * 4);
+ }
+
+ spi_ctrl_start_user(test_data);
+ flash_writeb(test_data, 0, WREN);
+ flash_writeb(test_data, 0, EN_4BYTE_ADDR);
+ flash_writeb(test_data, 0, ERASE_SECTOR);
+ flash_writel(test_data, 0, make_be32(some_page_addr));
+ spi_ctrl_stop_user(test_data);
+
+ /* Check the page is erased */
+ read_page(test_data, some_page_addr, page);
+ for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) {
+ g_assert_cmphex(page[i], ==, 0xffffffff);
+ }
+
+ flash_reset(test_data);
+}
+
+void aspeed_smc_test_erase_all(const void *data)
+{
+ const AspeedSMCTestData *test_data = (const AspeedSMCTestData *)data;
+ uint32_t some_page_addr = test_data->page_addr;
+ uint32_t page[FLASH_PAGE_SIZE / 4];
+ int i;
+
+ spi_conf(test_data, 1 << (CONF_ENABLE_W0 + test_data->cs));
+
+ /*
+ * Previous page should be full of 0xffs after backend is
+ * initialized
+ */
+ read_page(test_data, some_page_addr - FLASH_PAGE_SIZE, page);
+ for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) {
+ g_assert_cmphex(page[i], ==, 0xffffffff);
+ }
+
+ spi_ctrl_start_user(test_data);
+ flash_writeb(test_data, 0, EN_4BYTE_ADDR);
+ flash_writeb(test_data, 0, WREN);
+ flash_writeb(test_data, 0, PP);
+ flash_writel(test_data, 0, make_be32(some_page_addr));
+
+ /* Fill the page with its own addresses */
+ for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) {
+ flash_writel(test_data, 0, make_be32(some_page_addr + i * 4));
+ }
+ spi_ctrl_stop_user(test_data);
+
+ /* Check the page is correctly written */
+ read_page(test_data, some_page_addr, page);
+ for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) {
+ g_assert_cmphex(page[i], ==, some_page_addr + i * 4);
+ }
+
+ spi_ctrl_start_user(test_data);
+ flash_writeb(test_data, 0, WREN);
+ flash_writeb(test_data, 0, BULK_ERASE);
+ spi_ctrl_stop_user(test_data);
+
+ /* Check the page is erased */
+ read_page(test_data, some_page_addr, page);
+ for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) {
+ g_assert_cmphex(page[i], ==, 0xffffffff);
+ }
+
+ flash_reset(test_data);
+}
+
+void aspeed_smc_test_write_page(const void *data)
+{
+ const AspeedSMCTestData *test_data = (const AspeedSMCTestData *)data;
+ uint32_t my_page_addr = test_data->page_addr;
+ uint32_t some_page_addr = my_page_addr + FLASH_PAGE_SIZE;
+ uint32_t page[FLASH_PAGE_SIZE / 4];
+ int i;
+
+ spi_conf(test_data, 1 << (CONF_ENABLE_W0 + test_data->cs));
+
+ spi_ctrl_start_user(test_data);
+ flash_writeb(test_data, 0, EN_4BYTE_ADDR);
+ flash_writeb(test_data, 0, WREN);
+ flash_writeb(test_data, 0, PP);
+ flash_writel(test_data, 0, make_be32(my_page_addr));
+
+ /* Fill the page with its own addresses */
+ for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) {
+ flash_writel(test_data, 0, make_be32(my_page_addr + i * 4));
+ }
+ spi_ctrl_stop_user(test_data);
+
+ /* Check what was written */
+ read_page(test_data, my_page_addr, page);
+ for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) {
+ g_assert_cmphex(page[i], ==, my_page_addr + i * 4);
+ }
+
+ /* Check some other page. It should be full of 0xff */
+ read_page(test_data, some_page_addr, page);
+ for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) {
+ g_assert_cmphex(page[i], ==, 0xffffffff);
+ }
+
+ flash_reset(test_data);
+}
+
+void aspeed_smc_test_read_page_mem(const void *data)
+{
+ const AspeedSMCTestData *test_data = (const AspeedSMCTestData *)data;
+ uint32_t my_page_addr = test_data->page_addr;
+ uint32_t some_page_addr = my_page_addr + FLASH_PAGE_SIZE;
+ uint32_t page[FLASH_PAGE_SIZE / 4];
+ int i;
+
+ /*
+ * Enable 4BYTE mode for controller.
+ */
+ spi_ce_ctrl(test_data, 1 << (CRTL_EXTENDED0 + test_data->cs));
+
+ /* Enable 4BYTE mode for flash. */
+ spi_conf(test_data, 1 << (CONF_ENABLE_W0 + test_data->cs));
+ spi_ctrl_start_user(test_data);
+ flash_writeb(test_data, 0, EN_4BYTE_ADDR);
+ flash_writeb(test_data, 0, WREN);
+ flash_writeb(test_data, 0, PP);
+ flash_writel(test_data, 0, make_be32(my_page_addr));
+
+ /* Fill the page with its own addresses */
+ for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) {
+ flash_writel(test_data, 0, make_be32(my_page_addr + i * 4));
+ }
+ spi_ctrl_stop_user(test_data);
+ spi_conf_remove(test_data, 1 << (CONF_ENABLE_W0 + test_data->cs));
+
+ /* Check what was written */
+ read_page_mem(test_data, my_page_addr, page);
+ for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) {
+ g_assert_cmphex(page[i], ==, my_page_addr + i * 4);
+ }
+
+ /* Check some other page. It should be full of 0xff */
+ read_page_mem(test_data, some_page_addr, page);
+ for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) {
+ g_assert_cmphex(page[i], ==, 0xffffffff);
+ }
+
+ flash_reset(test_data);
+}
+
+void aspeed_smc_test_write_page_mem(const void *data)
+{
+ const AspeedSMCTestData *test_data = (const AspeedSMCTestData *)data;
+ uint32_t my_page_addr = test_data->page_addr;
+ uint32_t page[FLASH_PAGE_SIZE / 4];
+ int i;
+
+ /*
+ * Enable 4BYTE mode for controller.
+ */
+ spi_ce_ctrl(test_data, 1 << (CRTL_EXTENDED0 + test_data->cs));
+
+ /* Enable 4BYTE mode for flash. */
+ spi_conf(test_data, 1 << (CONF_ENABLE_W0 + test_data->cs));
+ spi_ctrl_start_user(test_data);
+ flash_writeb(test_data, 0, EN_4BYTE_ADDR);
+ flash_writeb(test_data, 0, WREN);
+ spi_ctrl_stop_user(test_data);
+
+ /* move out USER mode to use direct writes to the AHB bus */
+ spi_ctrl_setmode(test_data, CTRL_WRITEMODE, PP);
+
+ for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) {
+ flash_writel(test_data, my_page_addr + i * 4,
+ make_be32(my_page_addr + i * 4));
+ }
+
+ /* Check what was written */
+ read_page_mem(test_data, my_page_addr, page);
+ for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) {
+ g_assert_cmphex(page[i], ==, my_page_addr + i * 4);
+ }
+
+ flash_reset(test_data);
+}
+
+void aspeed_smc_test_read_status_reg(const void *data)
+{
+ const AspeedSMCTestData *test_data = (const AspeedSMCTestData *)data;
+ uint8_t r;
+
+ spi_conf(test_data, 1 << (CONF_ENABLE_W0 + test_data->cs));
+
+ spi_ctrl_start_user(test_data);
+ flash_writeb(test_data, 0, RDSR);
+ r = flash_readb(test_data, 0);
+ spi_ctrl_stop_user(test_data);
+
+ g_assert_cmphex(r & SR_WEL, ==, 0);
+ g_assert(!qtest_qom_get_bool
+ (test_data->s, test_data->node, "write-enable"));
+
+ spi_ctrl_start_user(test_data);
+ flash_writeb(test_data, 0, WREN);
+ flash_writeb(test_data, 0, RDSR);
+ r = flash_readb(test_data, 0);
+ spi_ctrl_stop_user(test_data);
+
+ g_assert_cmphex(r & SR_WEL, ==, SR_WEL);
+ g_assert(qtest_qom_get_bool
+ (test_data->s, test_data->node, "write-enable"));
+
+ spi_ctrl_start_user(test_data);
+ flash_writeb(test_data, 0, WRDI);
+ flash_writeb(test_data, 0, RDSR);
+ r = flash_readb(test_data, 0);
+ spi_ctrl_stop_user(test_data);
+
+ g_assert_cmphex(r & SR_WEL, ==, 0);
+ g_assert(!qtest_qom_get_bool
+ (test_data->s, test_data->node, "write-enable"));
+
+ flash_reset(test_data);
+}
+
+void aspeed_smc_test_status_reg_write_protection(const void *data)
+{
+ const AspeedSMCTestData *test_data = (const AspeedSMCTestData *)data;
+ uint8_t r;
+
+ spi_conf(test_data, 1 << (CONF_ENABLE_W0 + test_data->cs));
+
+ /* default case: WP# is high and SRWD is low -> status register writable */
+ spi_ctrl_start_user(test_data);
+ flash_writeb(test_data, 0, WREN);
+ /* test ability to write SRWD */
+ flash_writeb(test_data, 0, WRSR);
+ flash_writeb(test_data, 0, SRWD);
+ flash_writeb(test_data, 0, RDSR);
+ r = flash_readb(test_data, 0);
+ spi_ctrl_stop_user(test_data);
+ g_assert_cmphex(r & SRWD, ==, SRWD);
+
+ /* WP# high and SRWD high -> status register writable */
+ spi_ctrl_start_user(test_data);
+ flash_writeb(test_data, 0, WREN);
+ /* test ability to write SRWD */
+ flash_writeb(test_data, 0, WRSR);
+ flash_writeb(test_data, 0, 0);
+ flash_writeb(test_data, 0, RDSR);
+ r = flash_readb(test_data, 0);
+ spi_ctrl_stop_user(test_data);
+ g_assert_cmphex(r & SRWD, ==, 0);
+
+ /* WP# low and SRWD low -> status register writable */
+ qtest_set_irq_in(test_data->s, test_data->node, "WP#", 0, 0);
+ spi_ctrl_start_user(test_data);
+ flash_writeb(test_data, 0, WREN);
+ /* test ability to write SRWD */
+ flash_writeb(test_data, 0, WRSR);
+ flash_writeb(test_data, 0, SRWD);
+ flash_writeb(test_data, 0, RDSR);
+ r = flash_readb(test_data, 0);
+ spi_ctrl_stop_user(test_data);
+ g_assert_cmphex(r & SRWD, ==, SRWD);
+
+ /* WP# low and SRWD high -> status register NOT writable */
+ spi_ctrl_start_user(test_data);
+ flash_writeb(test_data, 0 , WREN);
+ /* test ability to write SRWD */
+ flash_writeb(test_data, 0, WRSR);
+ flash_writeb(test_data, 0, 0);
+ flash_writeb(test_data, 0, RDSR);
+ r = flash_readb(test_data, 0);
+ spi_ctrl_stop_user(test_data);
+ /* write is not successful */
+ g_assert_cmphex(r & SRWD, ==, SRWD);
+
+ qtest_set_irq_in(test_data->s, test_data->node, "WP#", 0, 1);
+ flash_reset(test_data);
+}
+
+void aspeed_smc_test_write_block_protect(const void *data)
+{
+ const AspeedSMCTestData *test_data = (const AspeedSMCTestData *)data;
+ uint32_t sector_size = 65536;
+ uint32_t n_sectors = 512;
+
+ spi_ce_ctrl(test_data, 1 << (CRTL_EXTENDED0 + test_data->cs));
+ spi_conf(test_data, 1 << (CONF_ENABLE_W0 + test_data->cs));
+
+ uint32_t bp_bits = 0b0;
+
+ for (int i = 0; i < 16; i++) {
+ bp_bits = ((i & 0b1000) << 3) | ((i & 0b0111) << 2);
+
+ spi_ctrl_start_user(test_data);
+ flash_writeb(test_data, 0, WREN);
+ flash_writeb(test_data, 0, BULK_ERASE);
+ flash_writeb(test_data, 0, WREN);
+ flash_writeb(test_data, 0, WRSR);
+ flash_writeb(test_data, 0, bp_bits);
+ flash_writeb(test_data, 0, EN_4BYTE_ADDR);
+ flash_writeb(test_data, 0, WREN);
+ spi_ctrl_stop_user(test_data);
+
+ uint32_t num_protected_sectors = i ? MIN(1 << (i - 1), n_sectors) : 0;
+ uint32_t protection_start = n_sectors - num_protected_sectors;
+ uint32_t protection_end = n_sectors;
+
+ for (int sector = 0; sector < n_sectors; sector++) {
+ uint32_t addr = sector * sector_size;
+
+ assert_page_mem(test_data, addr, 0xffffffff);
+ write_page_mem(test_data, addr, make_be32(0xabcdef12));
+
+ uint32_t expected_value = protection_start <= sector
+ && sector < protection_end
+ ? 0xffffffff : 0xabcdef12;
+
+ assert_page_mem(test_data, addr, expected_value);
+ }
+ }
+
+ flash_reset(test_data);
+}
+
+void aspeed_smc_test_write_block_protect_bottom_bit(const void *data)
+{
+ const AspeedSMCTestData *test_data = (const AspeedSMCTestData *)data;
+ uint32_t sector_size = 65536;
+ uint32_t n_sectors = 512;
+
+ spi_ce_ctrl(test_data, 1 << (CRTL_EXTENDED0 + test_data->cs));
+ spi_conf(test_data, 1 << (CONF_ENABLE_W0 + test_data->cs));
+
+ /* top bottom bit is enabled */
+ uint32_t bp_bits = 0b00100 << 3;
+
+ for (int i = 0; i < 16; i++) {
+ bp_bits = (((i & 0b1000) | 0b0100) << 3) | ((i & 0b0111) << 2);
+
+ spi_ctrl_start_user(test_data);
+ flash_writeb(test_data, 0, WREN);
+ flash_writeb(test_data, 0, BULK_ERASE);
+ flash_writeb(test_data, 0, WREN);
+ flash_writeb(test_data, 0, WRSR);
+ flash_writeb(test_data, 0, bp_bits);
+ flash_writeb(test_data, 0, EN_4BYTE_ADDR);
+ flash_writeb(test_data, 0, WREN);
+ spi_ctrl_stop_user(test_data);
+
+ uint32_t num_protected_sectors = i ? MIN(1 << (i - 1), n_sectors) : 0;
+ uint32_t protection_start = 0;
+ uint32_t protection_end = num_protected_sectors;
+
+ for (int sector = 0; sector < n_sectors; sector++) {
+ uint32_t addr = sector * sector_size;
+
+ assert_page_mem(test_data, addr, 0xffffffff);
+ write_page_mem(test_data, addr, make_be32(0xabcdef12));
+
+ uint32_t expected_value = protection_start <= sector
+ && sector < protection_end
+ ? 0xffffffff : 0xabcdef12;
+
+ assert_page_mem(test_data, addr, expected_value);
+ }
+ }
+
+ flash_reset(test_data);
+}
+
+void aspeed_smc_test_write_page_qpi(const void *data)
+{
+ const AspeedSMCTestData *test_data = (const AspeedSMCTestData *)data;
+ uint32_t my_page_addr = test_data->page_addr;
+ uint32_t some_page_addr = my_page_addr + FLASH_PAGE_SIZE;
+ uint32_t page[FLASH_PAGE_SIZE / 4];
+ uint32_t page_pattern[] = {
+ 0xebd8c134, 0x5da196bc, 0xae15e729, 0x5085ccdf
+ };
+ int i;
+
+ spi_conf(test_data, 1 << (CONF_ENABLE_W0 + test_data->cs));
+
+ spi_ctrl_start_user(test_data);
+ flash_writeb(test_data, 0, EN_4BYTE_ADDR);
+ flash_writeb(test_data, 0, WREN);
+ flash_writeb(test_data, 0, PP);
+ flash_writel(test_data, 0, make_be32(my_page_addr));
+
+ /* Set QPI mode */
+ spi_ctrl_set_io_mode(test_data, CTRL_IO_QUAD_IO);
+
+ /* Fill the page pattern */
+ for (i = 0; i < ARRAY_SIZE(page_pattern); i++) {
+ flash_writel(test_data, 0, make_be32(page_pattern[i]));
+ }
+
+ /* Fill the page with its own addresses */
+ for (; i < FLASH_PAGE_SIZE / 4; i++) {
+ flash_writel(test_data, 0, make_be32(my_page_addr + i * 4));
+ }
+
+ /* Restore io mode */
+ spi_ctrl_set_io_mode(test_data, 0);
+ spi_ctrl_stop_user(test_data);
+
+ /* Check what was written */
+ read_page(test_data, my_page_addr, page);
+ for (i = 0; i < ARRAY_SIZE(page_pattern); i++) {
+ g_assert_cmphex(page[i], ==, page_pattern[i]);
+ }
+ for (; i < FLASH_PAGE_SIZE / 4; i++) {
+ g_assert_cmphex(page[i], ==, my_page_addr + i * 4);
+ }
+
+ /* Check some other page. It should be full of 0xff */
+ read_page(test_data, some_page_addr, page);
+ for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) {
+ g_assert_cmphex(page[i], ==, 0xffffffff);
+ }
+
+ flash_reset(test_data);
+}
+
diff --git a/tests/qtest/aspeed-smc-utils.h b/tests/qtest/aspeed-smc-utils.h
new file mode 100644
index 0000000..b07870f
--- /dev/null
+++ b/tests/qtest/aspeed-smc-utils.h
@@ -0,0 +1,95 @@
+/*
+ * QTest testcase for the M25P80 Flash (Using the Aspeed SPI
+ * Controller)
+ *
+ * Copyright (C) 2016 IBM Corp.
+ *
+ * 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.
+ */
+
+#ifndef TESTS_ASPEED_SMC_UTILS_H
+#define TESTS_ASPEED_SMC_UTILS_H
+
+#include "qemu/osdep.h"
+#include "qemu/bswap.h"
+#include "libqtest-single.h"
+#include "qemu/bitops.h"
+
+/*
+ * ASPEED SPI Controller registers
+ */
+#define R_CONF 0x00
+#define CONF_ENABLE_W0 16
+#define R_CE_CTRL 0x04
+#define CRTL_EXTENDED0 0 /* 32 bit addressing for SPI */
+#define R_CTRL0 0x10
+#define CTRL_IO_QUAD_IO BIT(31)
+#define CTRL_CE_STOP_ACTIVE BIT(2)
+#define CTRL_READMODE 0x0
+#define CTRL_FREADMODE 0x1
+#define CTRL_WRITEMODE 0x2
+#define CTRL_USERMODE 0x3
+#define SR_WEL BIT(1)
+
+/*
+ * Flash commands
+ */
+enum {
+ JEDEC_READ = 0x9f,
+ RDSR = 0x5,
+ WRDI = 0x4,
+ BULK_ERASE = 0xc7,
+ READ = 0x03,
+ PP = 0x02,
+ WRSR = 0x1,
+ WREN = 0x6,
+ SRWD = 0x80,
+ RESET_ENABLE = 0x66,
+ RESET_MEMORY = 0x99,
+ EN_4BYTE_ADDR = 0xB7,
+ ERASE_SECTOR = 0xd8,
+};
+
+#define CTRL_IO_MODE_MASK (BIT(31) | BIT(30) | BIT(29) | BIT(28))
+#define FLASH_PAGE_SIZE 256
+
+typedef struct AspeedSMCTestData {
+ QTestState *s;
+ uint64_t spi_base;
+ uint64_t flash_base;
+ uint32_t jedec_id;
+ char *tmp_path;
+ uint8_t cs;
+ const char *node;
+ uint32_t page_addr;
+} AspeedSMCTestData;
+
+void aspeed_smc_test_read_jedec(const void *data);
+void aspeed_smc_test_erase_sector(const void *data);
+void aspeed_smc_test_erase_all(const void *data);
+void aspeed_smc_test_write_page(const void *data);
+void aspeed_smc_test_read_page_mem(const void *data);
+void aspeed_smc_test_write_page_mem(const void *data);
+void aspeed_smc_test_read_status_reg(const void *data);
+void aspeed_smc_test_status_reg_write_protection(const void *data);
+void aspeed_smc_test_write_block_protect(const void *data);
+void aspeed_smc_test_write_block_protect_bottom_bit(const void *data);
+void aspeed_smc_test_write_page_qpi(const void *data);
+
+#endif /* TESTS_ASPEED_SMC_UTILS_H */
diff --git a/tests/qtest/aspeed_gpio-test.c b/tests/qtest/aspeed_gpio-test.c
index d38f51d..12675d4 100644
--- a/tests/qtest/aspeed_gpio-test.c
+++ b/tests/qtest/aspeed_gpio-test.c
@@ -25,7 +25,7 @@
#include "qemu/osdep.h"
#include "qemu/bitops.h"
#include "qemu/timer.h"
-#include "qapi/qmp/qdict.h"
+#include "qobject/qdict.h"
#include "libqtest-single.h"
#define AST2600_GPIO_BASE 0x1E780000
diff --git a/tests/qtest/aspeed_hace-test.c b/tests/qtest/aspeed_hace-test.c
index ce86a44..3877702 100644
--- a/tests/qtest/aspeed_hace-test.c
+++ b/tests/qtest/aspeed_hace-test.c
@@ -6,599 +6,222 @@
*/
#include "qemu/osdep.h"
-
#include "libqtest.h"
#include "qemu/bitops.h"
+#include "aspeed-hace-utils.h"
-#define HACE_CMD 0x10
-#define HACE_SHA_BE_EN BIT(3)
-#define HACE_MD5_LE_EN BIT(2)
-#define HACE_ALGO_MD5 0
-#define HACE_ALGO_SHA1 BIT(5)
-#define HACE_ALGO_SHA224 BIT(6)
-#define HACE_ALGO_SHA256 (BIT(4) | BIT(6))
-#define HACE_ALGO_SHA512 (BIT(5) | BIT(6))
-#define HACE_ALGO_SHA384 (BIT(5) | BIT(6) | BIT(10))
-#define HACE_SG_EN BIT(18)
-#define HACE_ACCUM_EN BIT(8)
-
-#define HACE_STS 0x1c
-#define HACE_RSA_ISR BIT(13)
-#define HACE_CRYPTO_ISR BIT(12)
-#define HACE_HASH_ISR BIT(9)
-#define HACE_RSA_BUSY BIT(2)
-#define HACE_CRYPTO_BUSY BIT(1)
-#define HACE_HASH_BUSY BIT(0)
-#define HACE_HASH_SRC 0x20
-#define HACE_HASH_DIGEST 0x24
-#define HACE_HASH_KEY_BUFF 0x28
-#define HACE_HASH_DATA_LEN 0x2c
-#define HACE_HASH_CMD 0x30
-/* Scatter-Gather Hash */
-#define SG_LIST_LEN_LAST BIT(31)
-struct AspeedSgList {
- uint32_t len;
- uint32_t addr;
-} __attribute__ ((__packed__));
-
-/*
- * Test vector is the ascii "abc"
- *
- * Expected results were generated using command line utitiles:
- *
- * echo -n -e 'abc' | dd of=/tmp/test
- * for hash in sha512sum sha256sum md5sum; do $hash /tmp/test; done
- *
- */
-static const uint8_t test_vector[] = {0x61, 0x62, 0x63};
-
-static const uint8_t test_result_sha512[] = {
- 0xdd, 0xaf, 0x35, 0xa1, 0x93, 0x61, 0x7a, 0xba, 0xcc, 0x41, 0x73, 0x49,
- 0xae, 0x20, 0x41, 0x31, 0x12, 0xe6, 0xfa, 0x4e, 0x89, 0xa9, 0x7e, 0xa2,
- 0x0a, 0x9e, 0xee, 0xe6, 0x4b, 0x55, 0xd3, 0x9a, 0x21, 0x92, 0x99, 0x2a,
- 0x27, 0x4f, 0xc1, 0xa8, 0x36, 0xba, 0x3c, 0x23, 0xa3, 0xfe, 0xeb, 0xbd,
- 0x45, 0x4d, 0x44, 0x23, 0x64, 0x3c, 0xe8, 0x0e, 0x2a, 0x9a, 0xc9, 0x4f,
- 0xa5, 0x4c, 0xa4, 0x9f};
+static const struct AspeedMasks ast1030_masks = {
+ .src = 0x7fffffff,
+ .dest = 0x7ffffff8,
+ .key = 0x7ffffff8,
+ .len = 0x0fffffff,
+};
-static const uint8_t test_result_sha256[] = {
- 0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, 0x41, 0x41, 0x40, 0xde,
- 0x5d, 0xae, 0x22, 0x23, 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c,
- 0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad};
+static const struct AspeedMasks ast2600_masks = {
+ .src = 0x7fffffff,
+ .dest = 0x7ffffff8,
+ .key = 0x7ffffff8,
+ .len = 0x0fffffff,
+};
-static const uint8_t test_result_md5[] = {
- 0x90, 0x01, 0x50, 0x98, 0x3c, 0xd2, 0x4f, 0xb0, 0xd6, 0x96, 0x3f, 0x7d,
- 0x28, 0xe1, 0x7f, 0x72};
+static const struct AspeedMasks ast2500_masks = {
+ .src = 0x3fffffff,
+ .dest = 0x3ffffff8,
+ .key = 0x3fffffc0,
+ .len = 0x0fffffff,
+};
-/*
- * The Scatter-Gather Test vector is the ascii "abc" "def" "ghi", broken
- * into blocks of 3 characters as shown
- *
- * Expected results were generated using command line utitiles:
- *
- * echo -n -e 'abcdefghijkl' | dd of=/tmp/test
- * for hash in sha512sum sha256sum; do $hash /tmp/test; done
- *
- */
-static const uint8_t test_vector_sg1[] = {0x61, 0x62, 0x63, 0x64, 0x65, 0x66};
-static const uint8_t test_vector_sg2[] = {0x67, 0x68, 0x69};
-static const uint8_t test_vector_sg3[] = {0x6a, 0x6b, 0x6c};
-
-static const uint8_t test_result_sg_sha512[] = {
- 0x17, 0x80, 0x7c, 0x72, 0x8e, 0xe3, 0xba, 0x35, 0xe7, 0xcf, 0x7a, 0xf8,
- 0x23, 0x11, 0x6d, 0x26, 0xe4, 0x1e, 0x5d, 0x4d, 0x6c, 0x2f, 0xf1, 0xf3,
- 0x72, 0x0d, 0x3d, 0x96, 0xaa, 0xcb, 0x6f, 0x69, 0xde, 0x64, 0x2e, 0x63,
- 0xd5, 0xb7, 0x3f, 0xc3, 0x96, 0xc1, 0x2b, 0xe3, 0x8b, 0x2b, 0xd5, 0xd8,
- 0x84, 0x25, 0x7c, 0x32, 0xc8, 0xf6, 0xd0, 0x85, 0x4a, 0xe6, 0xb5, 0x40,
- 0xf8, 0x6d, 0xda, 0x2e};
-
-static const uint8_t test_result_sg_sha256[] = {
- 0xd6, 0x82, 0xed, 0x4c, 0xa4, 0xd9, 0x89, 0xc1, 0x34, 0xec, 0x94, 0xf1,
- 0x55, 0x1e, 0x1e, 0xc5, 0x80, 0xdd, 0x6d, 0x5a, 0x6e, 0xcd, 0xe9, 0xf3,
- 0xd3, 0x5e, 0x6e, 0x4a, 0x71, 0x7f, 0xbd, 0xe4};
+static const struct AspeedMasks ast2400_masks = {
+ .src = 0x0fffffff,
+ .dest = 0x0ffffff8,
+ .key = 0x0fffffc0,
+ .len = 0x0fffffff,
+};
-/*
- * The accumulative mode requires firmware to provide internal initial state
- * and message padding (including length L at the end of padding).
- *
- * This test vector is a ascii text "abc" with padding message.
- *
- * Expected results were generated using command line utitiles:
- *
- * echo -n -e 'abc' | dd of=/tmp/test
- * for hash in sha512sum sha256sum; do $hash /tmp/test; done
- */
-static const uint8_t test_vector_accum_512[] = {
- 0x61, 0x62, 0x63, 0x80, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18};
-
-static const uint8_t test_vector_accum_256[] = {
- 0x61, 0x62, 0x63, 0x80, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18};
-
-static const uint8_t test_result_accum_sha512[] = {
- 0xdd, 0xaf, 0x35, 0xa1, 0x93, 0x61, 0x7a, 0xba, 0xcc, 0x41, 0x73, 0x49,
- 0xae, 0x20, 0x41, 0x31, 0x12, 0xe6, 0xfa, 0x4e, 0x89, 0xa9, 0x7e, 0xa2,
- 0x0a, 0x9e, 0xee, 0xe6, 0x4b, 0x55, 0xd3, 0x9a, 0x21, 0x92, 0x99, 0x2a,
- 0x27, 0x4f, 0xc1, 0xa8, 0x36, 0xba, 0x3c, 0x23, 0xa3, 0xfe, 0xeb, 0xbd,
- 0x45, 0x4d, 0x44, 0x23, 0x64, 0x3c, 0xe8, 0x0e, 0x2a, 0x9a, 0xc9, 0x4f,
- 0xa5, 0x4c, 0xa4, 0x9f};
-
-static const uint8_t test_result_accum_sha256[] = {
- 0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, 0x41, 0x41, 0x40, 0xde,
- 0x5d, 0xae, 0x22, 0x23, 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c,
- 0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad};
-
-static void write_regs(QTestState *s, uint32_t base, uint32_t src,
- uint32_t length, uint32_t out, uint32_t method)
+/* ast1030 */
+static void test_md5_ast1030(void)
{
- qtest_writel(s, base + HACE_HASH_SRC, src);
- qtest_writel(s, base + HACE_HASH_DIGEST, out);
- qtest_writel(s, base + HACE_HASH_DATA_LEN, length);
- qtest_writel(s, base + HACE_HASH_CMD, HACE_SHA_BE_EN | method);
+ aspeed_test_md5("-machine ast1030-evb", 0x7e6d0000, 0x00000000);
}
-static void test_md5(const char *machine, const uint32_t base,
- const uint32_t src_addr)
-
+static void test_sha256_ast1030(void)
{
- QTestState *s = qtest_init(machine);
-
- uint32_t digest_addr = src_addr + 0x01000000;
- uint8_t digest[16] = {0};
-
- /* Check engine is idle, no busy or irq bits set */
- g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0);
-
- /* Write test vector into memory */
- qtest_memwrite(s, src_addr, test_vector, sizeof(test_vector));
-
- write_regs(s, base, src_addr, sizeof(test_vector), digest_addr, HACE_ALGO_MD5);
-
- /* Check hash IRQ status is asserted */
- g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0x00000200);
-
- /* Clear IRQ status and check status is deasserted */
- qtest_writel(s, base + HACE_STS, 0x00000200);
- g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0);
-
- /* Read computed digest from memory */
- qtest_memread(s, digest_addr, digest, sizeof(digest));
-
- /* Check result of computation */
- g_assert_cmpmem(digest, sizeof(digest),
- test_result_md5, sizeof(digest));
-
- qtest_quit(s);
+ aspeed_test_sha256("-machine ast1030-evb", 0x7e6d0000, 0x00000000);
}
-static void test_sha256(const char *machine, const uint32_t base,
- const uint32_t src_addr)
+static void test_sha256_sg_ast1030(void)
{
- QTestState *s = qtest_init(machine);
-
- const uint32_t digest_addr = src_addr + 0x1000000;
- uint8_t digest[32] = {0};
-
- /* Check engine is idle, no busy or irq bits set */
- g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0);
-
- /* Write test vector into memory */
- qtest_memwrite(s, src_addr, test_vector, sizeof(test_vector));
-
- write_regs(s, base, src_addr, sizeof(test_vector), digest_addr, HACE_ALGO_SHA256);
-
- /* Check hash IRQ status is asserted */
- g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0x00000200);
-
- /* Clear IRQ status and check status is deasserted */
- qtest_writel(s, base + HACE_STS, 0x00000200);
- g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0);
-
- /* Read computed digest from memory */
- qtest_memread(s, digest_addr, digest, sizeof(digest));
-
- /* Check result of computation */
- g_assert_cmpmem(digest, sizeof(digest),
- test_result_sha256, sizeof(digest));
-
- qtest_quit(s);
+ aspeed_test_sha256_sg("-machine ast1030-evb", 0x7e6d0000, 0x00000000);
}
-static void test_sha512(const char *machine, const uint32_t base,
- const uint32_t src_addr)
+static void test_sha384_ast1030(void)
{
- QTestState *s = qtest_init(machine);
-
- const uint32_t digest_addr = src_addr + 0x1000000;
- uint8_t digest[64] = {0};
-
- /* Check engine is idle, no busy or irq bits set */
- g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0);
-
- /* Write test vector into memory */
- qtest_memwrite(s, src_addr, test_vector, sizeof(test_vector));
-
- write_regs(s, base, src_addr, sizeof(test_vector), digest_addr, HACE_ALGO_SHA512);
-
- /* Check hash IRQ status is asserted */
- g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0x00000200);
-
- /* Clear IRQ status and check status is deasserted */
- qtest_writel(s, base + HACE_STS, 0x00000200);
- g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0);
-
- /* Read computed digest from memory */
- qtest_memread(s, digest_addr, digest, sizeof(digest));
-
- /* Check result of computation */
- g_assert_cmpmem(digest, sizeof(digest),
- test_result_sha512, sizeof(digest));
-
- qtest_quit(s);
+ aspeed_test_sha384("-machine ast1030-evb", 0x7e6d0000, 0x00000000);
}
-static void test_sha256_sg(const char *machine, const uint32_t base,
- const uint32_t src_addr)
+static void test_sha384_sg_ast1030(void)
{
- QTestState *s = qtest_init(machine);
-
- const uint32_t src_addr_1 = src_addr + 0x1000000;
- const uint32_t src_addr_2 = src_addr + 0x2000000;
- const uint32_t src_addr_3 = src_addr + 0x3000000;
- const uint32_t digest_addr = src_addr + 0x4000000;
- uint8_t digest[32] = {0};
- struct AspeedSgList array[] = {
- { cpu_to_le32(sizeof(test_vector_sg1)),
- cpu_to_le32(src_addr_1) },
- { cpu_to_le32(sizeof(test_vector_sg2)),
- cpu_to_le32(src_addr_2) },
- { cpu_to_le32(sizeof(test_vector_sg3) | SG_LIST_LEN_LAST),
- cpu_to_le32(src_addr_3) },
- };
-
- /* Check engine is idle, no busy or irq bits set */
- g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0);
-
- /* Write test vector into memory */
- qtest_memwrite(s, src_addr_1, test_vector_sg1, sizeof(test_vector_sg1));
- qtest_memwrite(s, src_addr_2, test_vector_sg2, sizeof(test_vector_sg2));
- qtest_memwrite(s, src_addr_3, test_vector_sg3, sizeof(test_vector_sg3));
- qtest_memwrite(s, src_addr, array, sizeof(array));
-
- write_regs(s, base, src_addr,
- (sizeof(test_vector_sg1)
- + sizeof(test_vector_sg2)
- + sizeof(test_vector_sg3)),
- digest_addr, HACE_ALGO_SHA256 | HACE_SG_EN);
-
- /* Check hash IRQ status is asserted */
- g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0x00000200);
-
- /* Clear IRQ status and check status is deasserted */
- qtest_writel(s, base + HACE_STS, 0x00000200);
- g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0);
-
- /* Read computed digest from memory */
- qtest_memread(s, digest_addr, digest, sizeof(digest));
-
- /* Check result of computation */
- g_assert_cmpmem(digest, sizeof(digest),
- test_result_sg_sha256, sizeof(digest));
-
- qtest_quit(s);
+ aspeed_test_sha384_sg("-machine ast1030-evb", 0x7e6d0000, 0x00000000);
}
-static void test_sha512_sg(const char *machine, const uint32_t base,
- const uint32_t src_addr)
+static void test_sha512_ast1030(void)
{
- QTestState *s = qtest_init(machine);
-
- const uint32_t src_addr_1 = src_addr + 0x1000000;
- const uint32_t src_addr_2 = src_addr + 0x2000000;
- const uint32_t src_addr_3 = src_addr + 0x3000000;
- const uint32_t digest_addr = src_addr + 0x4000000;
- uint8_t digest[64] = {0};
- struct AspeedSgList array[] = {
- { cpu_to_le32(sizeof(test_vector_sg1)),
- cpu_to_le32(src_addr_1) },
- { cpu_to_le32(sizeof(test_vector_sg2)),
- cpu_to_le32(src_addr_2) },
- { cpu_to_le32(sizeof(test_vector_sg3) | SG_LIST_LEN_LAST),
- cpu_to_le32(src_addr_3) },
- };
-
- /* Check engine is idle, no busy or irq bits set */
- g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0);
-
- /* Write test vector into memory */
- qtest_memwrite(s, src_addr_1, test_vector_sg1, sizeof(test_vector_sg1));
- qtest_memwrite(s, src_addr_2, test_vector_sg2, sizeof(test_vector_sg2));
- qtest_memwrite(s, src_addr_3, test_vector_sg3, sizeof(test_vector_sg3));
- qtest_memwrite(s, src_addr, array, sizeof(array));
-
- write_regs(s, base, src_addr,
- (sizeof(test_vector_sg1)
- + sizeof(test_vector_sg2)
- + sizeof(test_vector_sg3)),
- digest_addr, HACE_ALGO_SHA512 | HACE_SG_EN);
-
- /* Check hash IRQ status is asserted */
- g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0x00000200);
-
- /* Clear IRQ status and check status is deasserted */
- qtest_writel(s, base + HACE_STS, 0x00000200);
- g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0);
-
- /* Read computed digest from memory */
- qtest_memread(s, digest_addr, digest, sizeof(digest));
-
- /* Check result of computation */
- g_assert_cmpmem(digest, sizeof(digest),
- test_result_sg_sha512, sizeof(digest));
-
- qtest_quit(s);
+ aspeed_test_sha512("-machine ast1030-evb", 0x7e6d0000, 0x00000000);
}
-static void test_sha256_accum(const char *machine, const uint32_t base,
- const uint32_t src_addr)
+static void test_sha512_sg_ast1030(void)
{
- QTestState *s = qtest_init(machine);
-
- const uint32_t buffer_addr = src_addr + 0x1000000;
- const uint32_t digest_addr = src_addr + 0x4000000;
- uint8_t digest[32] = {0};
- struct AspeedSgList array[] = {
- { cpu_to_le32(sizeof(test_vector_accum_256) | SG_LIST_LEN_LAST),
- cpu_to_le32(buffer_addr) },
- };
-
- /* Check engine is idle, no busy or irq bits set */
- g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0);
-
- /* Write test vector into memory */
- qtest_memwrite(s, buffer_addr, test_vector_accum_256,
- sizeof(test_vector_accum_256));
- qtest_memwrite(s, src_addr, array, sizeof(array));
-
- write_regs(s, base, src_addr, sizeof(test_vector_accum_256),
- digest_addr, HACE_ALGO_SHA256 | HACE_SG_EN | HACE_ACCUM_EN);
-
- /* Check hash IRQ status is asserted */
- g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0x00000200);
-
- /* Clear IRQ status and check status is deasserted */
- qtest_writel(s, base + HACE_STS, 0x00000200);
- g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0);
-
- /* Read computed digest from memory */
- qtest_memread(s, digest_addr, digest, sizeof(digest));
-
- /* Check result of computation */
- g_assert_cmpmem(digest, sizeof(digest),
- test_result_accum_sha256, sizeof(digest));
-
- qtest_quit(s);
+ aspeed_test_sha512_sg("-machine ast1030-evb", 0x7e6d0000, 0x00000000);
}
-static void test_sha512_accum(const char *machine, const uint32_t base,
- const uint32_t src_addr)
+static void test_sha256_accum_ast1030(void)
{
- QTestState *s = qtest_init(machine);
-
- const uint32_t buffer_addr = src_addr + 0x1000000;
- const uint32_t digest_addr = src_addr + 0x4000000;
- uint8_t digest[64] = {0};
- struct AspeedSgList array[] = {
- { cpu_to_le32(sizeof(test_vector_accum_512) | SG_LIST_LEN_LAST),
- cpu_to_le32(buffer_addr) },
- };
-
- /* Check engine is idle, no busy or irq bits set */
- g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0);
-
- /* Write test vector into memory */
- qtest_memwrite(s, buffer_addr, test_vector_accum_512,
- sizeof(test_vector_accum_512));
- qtest_memwrite(s, src_addr, array, sizeof(array));
-
- write_regs(s, base, src_addr, sizeof(test_vector_accum_512),
- digest_addr, HACE_ALGO_SHA512 | HACE_SG_EN | HACE_ACCUM_EN);
-
- /* Check hash IRQ status is asserted */
- g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0x00000200);
-
- /* Clear IRQ status and check status is deasserted */
- qtest_writel(s, base + HACE_STS, 0x00000200);
- g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0);
-
- /* Read computed digest from memory */
- qtest_memread(s, digest_addr, digest, sizeof(digest));
-
- /* Check result of computation */
- g_assert_cmpmem(digest, sizeof(digest),
- test_result_accum_sha512, sizeof(digest));
-
- qtest_quit(s);
+ aspeed_test_sha256_accum("-machine ast1030-evb", 0x7e6d0000, 0x00000000);
}
-struct masks {
- uint32_t src;
- uint32_t dest;
- uint32_t len;
-};
-
-static const struct masks ast2600_masks = {
- .src = 0x7fffffff,
- .dest = 0x7ffffff8,
- .len = 0x0fffffff,
-};
-
-static const struct masks ast2500_masks = {
- .src = 0x3fffffff,
- .dest = 0x3ffffff8,
- .len = 0x0fffffff,
-};
-
-static const struct masks ast2400_masks = {
- .src = 0x0fffffff,
- .dest = 0x0ffffff8,
- .len = 0x0fffffff,
-};
-
-static void test_addresses(const char *machine, const uint32_t base,
- const struct masks *expected)
+static void test_sha384_accum_ast1030(void)
{
- QTestState *s = qtest_init(machine);
-
- /*
- * Check command mode is zero, meaning engine is in direct access mode,
- * as this affects the masking behavior of the HASH_SRC register.
- */
- g_assert_cmphex(qtest_readl(s, base + HACE_CMD), ==, 0);
- g_assert_cmphex(qtest_readl(s, base + HACE_HASH_SRC), ==, 0);
- g_assert_cmphex(qtest_readl(s, base + HACE_HASH_DIGEST), ==, 0);
- g_assert_cmphex(qtest_readl(s, base + HACE_HASH_DATA_LEN), ==, 0);
-
-
- /* Check that the address masking is correct */
- qtest_writel(s, base + HACE_HASH_SRC, 0xffffffff);
- g_assert_cmphex(qtest_readl(s, base + HACE_HASH_SRC), ==, expected->src);
-
- qtest_writel(s, base + HACE_HASH_DIGEST, 0xffffffff);
- g_assert_cmphex(qtest_readl(s, base + HACE_HASH_DIGEST), ==, expected->dest);
-
- qtest_writel(s, base + HACE_HASH_DATA_LEN, 0xffffffff);
- g_assert_cmphex(qtest_readl(s, base + HACE_HASH_DATA_LEN), ==, expected->len);
-
- /* Reset to zero */
- qtest_writel(s, base + HACE_HASH_SRC, 0);
- qtest_writel(s, base + HACE_HASH_DIGEST, 0);
- qtest_writel(s, base + HACE_HASH_DATA_LEN, 0);
+ aspeed_test_sha384_accum("-machine ast1030-evb", 0x7e6d0000, 0x00000000);
+}
- /* Check that all bits are now zero */
- g_assert_cmphex(qtest_readl(s, base + HACE_HASH_SRC), ==, 0);
- g_assert_cmphex(qtest_readl(s, base + HACE_HASH_DIGEST), ==, 0);
- g_assert_cmphex(qtest_readl(s, base + HACE_HASH_DATA_LEN), ==, 0);
+static void test_sha512_accum_ast1030(void)
+{
+ aspeed_test_sha512_accum("-machine ast1030-evb", 0x7e6d0000, 0x00000000);
+}
- qtest_quit(s);
+static void test_addresses_ast1030(void)
+{
+ aspeed_test_addresses("-machine ast1030-evb", 0x7e6d0000, &ast1030_masks);
}
/* ast2600 */
static void test_md5_ast2600(void)
{
- test_md5("-machine ast2600-evb", 0x1e6d0000, 0x80000000);
+ aspeed_test_md5("-machine ast2600-evb", 0x1e6d0000, 0x80000000);
}
static void test_sha256_ast2600(void)
{
- test_sha256("-machine ast2600-evb", 0x1e6d0000, 0x80000000);
+ aspeed_test_sha256("-machine ast2600-evb", 0x1e6d0000, 0x80000000);
}
static void test_sha256_sg_ast2600(void)
{
- test_sha256_sg("-machine ast2600-evb", 0x1e6d0000, 0x80000000);
+ aspeed_test_sha256_sg("-machine ast2600-evb", 0x1e6d0000, 0x80000000);
+}
+
+static void test_sha384_ast2600(void)
+{
+ aspeed_test_sha384("-machine ast2600-evb", 0x1e6d0000, 0x80000000);
+}
+
+static void test_sha384_sg_ast2600(void)
+{
+ aspeed_test_sha384_sg("-machine ast2600-evb", 0x1e6d0000, 0x80000000);
}
static void test_sha512_ast2600(void)
{
- test_sha512("-machine ast2600-evb", 0x1e6d0000, 0x80000000);
+ aspeed_test_sha512("-machine ast2600-evb", 0x1e6d0000, 0x80000000);
}
static void test_sha512_sg_ast2600(void)
{
- test_sha512_sg("-machine ast2600-evb", 0x1e6d0000, 0x80000000);
+ aspeed_test_sha512_sg("-machine ast2600-evb", 0x1e6d0000, 0x80000000);
}
static void test_sha256_accum_ast2600(void)
{
- test_sha256_accum("-machine ast2600-evb", 0x1e6d0000, 0x80000000);
+ aspeed_test_sha256_accum("-machine ast2600-evb", 0x1e6d0000, 0x80000000);
+}
+
+static void test_sha384_accum_ast2600(void)
+{
+ aspeed_test_sha384_accum("-machine ast2600-evb", 0x1e6d0000, 0x80000000);
}
static void test_sha512_accum_ast2600(void)
{
- test_sha512_accum("-machine ast2600-evb", 0x1e6d0000, 0x80000000);
+ aspeed_test_sha512_accum("-machine ast2600-evb", 0x1e6d0000, 0x80000000);
}
static void test_addresses_ast2600(void)
{
- test_addresses("-machine ast2600-evb", 0x1e6d0000, &ast2600_masks);
+ aspeed_test_addresses("-machine ast2600-evb", 0x1e6d0000, &ast2600_masks);
}
/* ast2500 */
static void test_md5_ast2500(void)
{
- test_md5("-machine ast2500-evb", 0x1e6e3000, 0x80000000);
+ aspeed_test_md5("-machine ast2500-evb", 0x1e6e3000, 0x80000000);
}
static void test_sha256_ast2500(void)
{
- test_sha256("-machine ast2500-evb", 0x1e6e3000, 0x80000000);
+ aspeed_test_sha256("-machine ast2500-evb", 0x1e6e3000, 0x80000000);
}
static void test_sha512_ast2500(void)
{
- test_sha512("-machine ast2500-evb", 0x1e6e3000, 0x80000000);
+ aspeed_test_sha512("-machine ast2500-evb", 0x1e6e3000, 0x80000000);
}
static void test_addresses_ast2500(void)
{
- test_addresses("-machine ast2500-evb", 0x1e6e3000, &ast2500_masks);
+ aspeed_test_addresses("-machine ast2500-evb", 0x1e6e3000, &ast2500_masks);
}
/* ast2400 */
static void test_md5_ast2400(void)
{
- test_md5("-machine palmetto-bmc", 0x1e6e3000, 0x40000000);
+ aspeed_test_md5("-machine palmetto-bmc", 0x1e6e3000, 0x40000000);
}
static void test_sha256_ast2400(void)
{
- test_sha256("-machine palmetto-bmc", 0x1e6e3000, 0x40000000);
+ aspeed_test_sha256("-machine palmetto-bmc", 0x1e6e3000, 0x40000000);
}
static void test_sha512_ast2400(void)
{
- test_sha512("-machine palmetto-bmc", 0x1e6e3000, 0x40000000);
+ aspeed_test_sha512("-machine palmetto-bmc", 0x1e6e3000, 0x40000000);
}
static void test_addresses_ast2400(void)
{
- test_addresses("-machine palmetto-bmc", 0x1e6e3000, &ast2400_masks);
+ aspeed_test_addresses("-machine palmetto-bmc", 0x1e6e3000, &ast2400_masks);
}
int main(int argc, char **argv)
{
g_test_init(&argc, &argv, NULL);
+ qtest_add_func("ast1030/hace/addresses", test_addresses_ast1030);
+ qtest_add_func("ast1030/hace/sha512", test_sha512_ast1030);
+ qtest_add_func("ast1030/hace/sha384", test_sha384_ast1030);
+ qtest_add_func("ast1030/hace/sha256", test_sha256_ast1030);
+ qtest_add_func("ast1030/hace/md5", test_md5_ast1030);
+
+ qtest_add_func("ast1030/hace/sha512_sg", test_sha512_sg_ast1030);
+ qtest_add_func("ast1030/hace/sha384_sg", test_sha384_sg_ast1030);
+ qtest_add_func("ast1030/hace/sha256_sg", test_sha256_sg_ast1030);
+
+ qtest_add_func("ast1030/hace/sha512_accum", test_sha512_accum_ast1030);
+ qtest_add_func("ast1030/hace/sha384_accum", test_sha384_accum_ast1030);
+ qtest_add_func("ast1030/hace/sha256_accum", test_sha256_accum_ast1030);
+
qtest_add_func("ast2600/hace/addresses", test_addresses_ast2600);
qtest_add_func("ast2600/hace/sha512", test_sha512_ast2600);
+ qtest_add_func("ast2600/hace/sha384", test_sha384_ast2600);
qtest_add_func("ast2600/hace/sha256", test_sha256_ast2600);
qtest_add_func("ast2600/hace/md5", test_md5_ast2600);
qtest_add_func("ast2600/hace/sha512_sg", test_sha512_sg_ast2600);
+ qtest_add_func("ast2600/hace/sha384_sg", test_sha384_sg_ast2600);
qtest_add_func("ast2600/hace/sha256_sg", test_sha256_sg_ast2600);
qtest_add_func("ast2600/hace/sha512_accum", test_sha512_accum_ast2600);
+ qtest_add_func("ast2600/hace/sha384_accum", test_sha384_accum_ast2600);
qtest_add_func("ast2600/hace/sha256_accum", test_sha256_accum_ast2600);
qtest_add_func("ast2500/hace/addresses", test_addresses_ast2500);
diff --git a/tests/qtest/aspeed_smc-test.c b/tests/qtest/aspeed_smc-test.c
index c713a37..52a00e6 100644
--- a/tests/qtest/aspeed_smc-test.c
+++ b/tests/qtest/aspeed_smc-test.c
@@ -27,623 +27,211 @@
#include "qemu/bswap.h"
#include "libqtest-single.h"
#include "qemu/bitops.h"
+#include "aspeed-smc-utils.h"
-/*
- * ASPEED SPI Controller registers
- */
-#define R_CONF 0x00
-#define CONF_ENABLE_W0 (1 << 16)
-#define R_CE_CTRL 0x04
-#define CRTL_EXTENDED0 0 /* 32 bit addressing for SPI */
-#define R_CTRL0 0x10
-#define CTRL_CE_STOP_ACTIVE (1 << 2)
-#define CTRL_READMODE 0x0
-#define CTRL_FREADMODE 0x1
-#define CTRL_WRITEMODE 0x2
-#define CTRL_USERMODE 0x3
-#define SR_WEL BIT(1)
-
-#define ASPEED_FMC_BASE 0x1E620000
-#define ASPEED_FLASH_BASE 0x20000000
-
-/*
- * Flash commands
- */
-enum {
- JEDEC_READ = 0x9f,
- RDSR = 0x5,
- WRDI = 0x4,
- BULK_ERASE = 0xc7,
- READ = 0x03,
- PP = 0x02,
- WRSR = 0x1,
- WREN = 0x6,
- SRWD = 0x80,
- RESET_ENABLE = 0x66,
- RESET_MEMORY = 0x99,
- EN_4BYTE_ADDR = 0xB7,
- ERASE_SECTOR = 0xd8,
-};
-
-#define FLASH_JEDEC 0x20ba19 /* n25q256a */
-#define FLASH_SIZE (32 * 1024 * 1024)
-
-#define FLASH_PAGE_SIZE 256
-
-/*
- * Use an explicit bswap for the values read/wrote to the flash region
- * as they are BE and the Aspeed CPU is LE.
- */
-static inline uint32_t make_be32(uint32_t data)
+static void test_palmetto_bmc(AspeedSMCTestData *data)
{
- return bswap32(data);
-}
-
-static void spi_conf(uint32_t value)
-{
- uint32_t conf = readl(ASPEED_FMC_BASE + R_CONF);
-
- conf |= value;
- writel(ASPEED_FMC_BASE + R_CONF, conf);
-}
-
-static void spi_conf_remove(uint32_t value)
-{
- uint32_t conf = readl(ASPEED_FMC_BASE + R_CONF);
-
- conf &= ~value;
- writel(ASPEED_FMC_BASE + R_CONF, conf);
-}
-
-static void spi_ce_ctrl(uint32_t value)
-{
- uint32_t conf = readl(ASPEED_FMC_BASE + R_CE_CTRL);
-
- conf |= value;
- writel(ASPEED_FMC_BASE + R_CE_CTRL, conf);
-}
-
-static void spi_ctrl_setmode(uint8_t mode, uint8_t cmd)
-{
- uint32_t ctrl = readl(ASPEED_FMC_BASE + R_CTRL0);
- ctrl &= ~(CTRL_USERMODE | 0xff << 16);
- ctrl |= mode | (cmd << 16);
- writel(ASPEED_FMC_BASE + R_CTRL0, ctrl);
-}
-
-static void spi_ctrl_start_user(void)
-{
- uint32_t ctrl = readl(ASPEED_FMC_BASE + R_CTRL0);
-
- ctrl |= CTRL_USERMODE | CTRL_CE_STOP_ACTIVE;
- writel(ASPEED_FMC_BASE + R_CTRL0, ctrl);
-
- ctrl &= ~CTRL_CE_STOP_ACTIVE;
- writel(ASPEED_FMC_BASE + R_CTRL0, ctrl);
-}
-
-static void spi_ctrl_stop_user(void)
-{
- uint32_t ctrl = readl(ASPEED_FMC_BASE + R_CTRL0);
-
- ctrl |= CTRL_USERMODE | CTRL_CE_STOP_ACTIVE;
- writel(ASPEED_FMC_BASE + R_CTRL0, ctrl);
-}
-
-static void flash_reset(void)
-{
- spi_conf(CONF_ENABLE_W0);
-
- spi_ctrl_start_user();
- writeb(ASPEED_FLASH_BASE, RESET_ENABLE);
- writeb(ASPEED_FLASH_BASE, RESET_MEMORY);
- writeb(ASPEED_FLASH_BASE, WREN);
- writeb(ASPEED_FLASH_BASE, BULK_ERASE);
- writeb(ASPEED_FLASH_BASE, WRDI);
- spi_ctrl_stop_user();
-
- spi_conf_remove(CONF_ENABLE_W0);
-}
-
-static void test_read_jedec(void)
-{
- uint32_t jedec = 0x0;
-
- spi_conf(CONF_ENABLE_W0);
-
- spi_ctrl_start_user();
- writeb(ASPEED_FLASH_BASE, JEDEC_READ);
- jedec |= readb(ASPEED_FLASH_BASE) << 16;
- jedec |= readb(ASPEED_FLASH_BASE) << 8;
- jedec |= readb(ASPEED_FLASH_BASE);
- spi_ctrl_stop_user();
-
- flash_reset();
-
- g_assert_cmphex(jedec, ==, FLASH_JEDEC);
-}
-
-static void read_page(uint32_t addr, uint32_t *page)
-{
- int i;
-
- spi_ctrl_start_user();
-
- writeb(ASPEED_FLASH_BASE, EN_4BYTE_ADDR);
- writeb(ASPEED_FLASH_BASE, READ);
- writel(ASPEED_FLASH_BASE, make_be32(addr));
-
- /* Continuous read are supported */
- for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) {
- page[i] = make_be32(readl(ASPEED_FLASH_BASE));
- }
- spi_ctrl_stop_user();
-}
-
-static void read_page_mem(uint32_t addr, uint32_t *page)
-{
- int i;
-
- /* move out USER mode to use direct reads from the AHB bus */
- spi_ctrl_setmode(CTRL_READMODE, READ);
-
- for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) {
- page[i] = make_be32(readl(ASPEED_FLASH_BASE + addr + i * 4));
- }
-}
-
-static void write_page_mem(uint32_t addr, uint32_t write_value)
-{
- spi_ctrl_setmode(CTRL_WRITEMODE, PP);
-
- for (int i = 0; i < FLASH_PAGE_SIZE / 4; i++) {
- writel(ASPEED_FLASH_BASE + addr + i * 4, write_value);
- }
-}
-
-static void assert_page_mem(uint32_t addr, uint32_t expected_value)
-{
- uint32_t page[FLASH_PAGE_SIZE / 4];
- read_page_mem(addr, page);
- for (int i = 0; i < FLASH_PAGE_SIZE / 4; i++) {
- g_assert_cmphex(page[i], ==, expected_value);
- }
-}
-
-static void test_erase_sector(void)
-{
- uint32_t some_page_addr = 0x600 * FLASH_PAGE_SIZE;
- uint32_t page[FLASH_PAGE_SIZE / 4];
- int i;
-
- spi_conf(CONF_ENABLE_W0);
-
- /*
- * Previous page should be full of 0xffs after backend is
- * initialized
- */
- read_page(some_page_addr - FLASH_PAGE_SIZE, page);
- for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) {
- g_assert_cmphex(page[i], ==, 0xffffffff);
- }
-
- spi_ctrl_start_user();
- writeb(ASPEED_FLASH_BASE, EN_4BYTE_ADDR);
- writeb(ASPEED_FLASH_BASE, WREN);
- writeb(ASPEED_FLASH_BASE, PP);
- writel(ASPEED_FLASH_BASE, make_be32(some_page_addr));
-
- /* Fill the page with its own addresses */
- for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) {
- writel(ASPEED_FLASH_BASE, make_be32(some_page_addr + i * 4));
- }
- spi_ctrl_stop_user();
-
- /* Check the page is correctly written */
- read_page(some_page_addr, page);
- for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) {
- g_assert_cmphex(page[i], ==, some_page_addr + i * 4);
- }
-
- spi_ctrl_start_user();
- writeb(ASPEED_FLASH_BASE, WREN);
- writeb(ASPEED_FLASH_BASE, EN_4BYTE_ADDR);
- writeb(ASPEED_FLASH_BASE, ERASE_SECTOR);
- writel(ASPEED_FLASH_BASE, make_be32(some_page_addr));
- spi_ctrl_stop_user();
-
- /* Check the page is erased */
- read_page(some_page_addr, page);
- for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) {
- g_assert_cmphex(page[i], ==, 0xffffffff);
- }
-
- flash_reset();
-}
-
-static void test_erase_all(void)
-{
- uint32_t some_page_addr = 0x15000 * FLASH_PAGE_SIZE;
- uint32_t page[FLASH_PAGE_SIZE / 4];
- int i;
-
- spi_conf(CONF_ENABLE_W0);
-
- /*
- * Previous page should be full of 0xffs after backend is
- * initialized
- */
- read_page(some_page_addr - FLASH_PAGE_SIZE, page);
- for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) {
- g_assert_cmphex(page[i], ==, 0xffffffff);
- }
-
- spi_ctrl_start_user();
- writeb(ASPEED_FLASH_BASE, EN_4BYTE_ADDR);
- writeb(ASPEED_FLASH_BASE, WREN);
- writeb(ASPEED_FLASH_BASE, PP);
- writel(ASPEED_FLASH_BASE, make_be32(some_page_addr));
-
- /* Fill the page with its own addresses */
- for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) {
- writel(ASPEED_FLASH_BASE, make_be32(some_page_addr + i * 4));
- }
- spi_ctrl_stop_user();
-
- /* Check the page is correctly written */
- read_page(some_page_addr, page);
- for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) {
- g_assert_cmphex(page[i], ==, some_page_addr + i * 4);
- }
-
- spi_ctrl_start_user();
- writeb(ASPEED_FLASH_BASE, WREN);
- writeb(ASPEED_FLASH_BASE, BULK_ERASE);
- spi_ctrl_stop_user();
-
- /* Check the page is erased */
- read_page(some_page_addr, page);
- for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) {
- g_assert_cmphex(page[i], ==, 0xffffffff);
- }
-
- flash_reset();
-}
-
-static void test_write_page(void)
-{
- uint32_t my_page_addr = 0x14000 * FLASH_PAGE_SIZE; /* beyond 16MB */
- uint32_t some_page_addr = 0x15000 * FLASH_PAGE_SIZE;
- uint32_t page[FLASH_PAGE_SIZE / 4];
- int i;
-
- spi_conf(CONF_ENABLE_W0);
-
- spi_ctrl_start_user();
- writeb(ASPEED_FLASH_BASE, EN_4BYTE_ADDR);
- writeb(ASPEED_FLASH_BASE, WREN);
- writeb(ASPEED_FLASH_BASE, PP);
- writel(ASPEED_FLASH_BASE, make_be32(my_page_addr));
-
- /* Fill the page with its own addresses */
- for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) {
- writel(ASPEED_FLASH_BASE, make_be32(my_page_addr + i * 4));
- }
- spi_ctrl_stop_user();
-
- /* Check what was written */
- read_page(my_page_addr, page);
- for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) {
- g_assert_cmphex(page[i], ==, my_page_addr + i * 4);
- }
-
- /* Check some other page. It should be full of 0xff */
- read_page(some_page_addr, page);
- for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) {
- g_assert_cmphex(page[i], ==, 0xffffffff);
- }
-
- flash_reset();
-}
-
-static void test_read_page_mem(void)
-{
- uint32_t my_page_addr = 0x14000 * FLASH_PAGE_SIZE; /* beyond 16MB */
- uint32_t some_page_addr = 0x15000 * FLASH_PAGE_SIZE;
- uint32_t page[FLASH_PAGE_SIZE / 4];
- int i;
-
- /* Enable 4BYTE mode for controller. This is should be strapped by
- * HW for CE0 anyhow.
- */
- spi_ce_ctrl(1 << CRTL_EXTENDED0);
-
- /* Enable 4BYTE mode for flash. */
- spi_conf(CONF_ENABLE_W0);
- spi_ctrl_start_user();
- writeb(ASPEED_FLASH_BASE, EN_4BYTE_ADDR);
- writeb(ASPEED_FLASH_BASE, WREN);
- writeb(ASPEED_FLASH_BASE, PP);
- writel(ASPEED_FLASH_BASE, make_be32(my_page_addr));
-
- /* Fill the page with its own addresses */
- for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) {
- writel(ASPEED_FLASH_BASE, make_be32(my_page_addr + i * 4));
- }
- spi_ctrl_stop_user();
- spi_conf_remove(CONF_ENABLE_W0);
-
- /* Check what was written */
- read_page_mem(my_page_addr, page);
- for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) {
- g_assert_cmphex(page[i], ==, my_page_addr + i * 4);
- }
-
- /* Check some other page. It should be full of 0xff */
- read_page_mem(some_page_addr, page);
- for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) {
- g_assert_cmphex(page[i], ==, 0xffffffff);
- }
-
- flash_reset();
-}
-
-static void test_write_page_mem(void)
-{
- uint32_t my_page_addr = 0x15000 * FLASH_PAGE_SIZE;
- uint32_t page[FLASH_PAGE_SIZE / 4];
- int i;
-
- /* Enable 4BYTE mode for controller. This is should be strapped by
- * HW for CE0 anyhow.
- */
- spi_ce_ctrl(1 << CRTL_EXTENDED0);
-
- /* Enable 4BYTE mode for flash. */
- spi_conf(CONF_ENABLE_W0);
- spi_ctrl_start_user();
- writeb(ASPEED_FLASH_BASE, EN_4BYTE_ADDR);
- writeb(ASPEED_FLASH_BASE, WREN);
- spi_ctrl_stop_user();
-
- /* move out USER mode to use direct writes to the AHB bus */
- spi_ctrl_setmode(CTRL_WRITEMODE, PP);
-
- for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) {
- writel(ASPEED_FLASH_BASE + my_page_addr + i * 4,
- make_be32(my_page_addr + i * 4));
- }
-
- /* Check what was written */
- read_page_mem(my_page_addr, page);
- for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) {
- g_assert_cmphex(page[i], ==, my_page_addr + i * 4);
- }
-
- flash_reset();
-}
-
-static void test_read_status_reg(void)
-{
- uint8_t r;
-
- spi_conf(CONF_ENABLE_W0);
-
- spi_ctrl_start_user();
- writeb(ASPEED_FLASH_BASE, RDSR);
- r = readb(ASPEED_FLASH_BASE);
- spi_ctrl_stop_user();
-
- g_assert_cmphex(r & SR_WEL, ==, 0);
- g_assert(!qtest_qom_get_bool
- (global_qtest, "/machine/soc/fmc/ssi.0/child[0]", "write-enable"));
-
- spi_ctrl_start_user();
- writeb(ASPEED_FLASH_BASE, WREN);
- writeb(ASPEED_FLASH_BASE, RDSR);
- r = readb(ASPEED_FLASH_BASE);
- spi_ctrl_stop_user();
-
- g_assert_cmphex(r & SR_WEL, ==, SR_WEL);
- g_assert(qtest_qom_get_bool
- (global_qtest, "/machine/soc/fmc/ssi.0/child[0]", "write-enable"));
-
- spi_ctrl_start_user();
- writeb(ASPEED_FLASH_BASE, WRDI);
- writeb(ASPEED_FLASH_BASE, RDSR);
- r = readb(ASPEED_FLASH_BASE);
- spi_ctrl_stop_user();
-
- g_assert_cmphex(r & SR_WEL, ==, 0);
- g_assert(!qtest_qom_get_bool
- (global_qtest, "/machine/soc/fmc/ssi.0/child[0]", "write-enable"));
+ int ret;
+ int fd;
- flash_reset();
-}
+ fd = g_file_open_tmp("qtest.m25p80.n25q256a.XXXXXX", &data->tmp_path, NULL);
+ g_assert(fd >= 0);
+ ret = ftruncate(fd, 32 * 1024 * 1024);
+ g_assert(ret == 0);
+ close(fd);
-static void test_status_reg_write_protection(void)
+ data->s = qtest_initf("-m 256 -machine palmetto-bmc "
+ "-drive file=%s,format=raw,if=mtd",
+ data->tmp_path);
+
+ /* fmc cs0 with n25q256a flash */
+ data->flash_base = 0x20000000;
+ data->spi_base = 0x1E620000;
+ data->jedec_id = 0x20ba19;
+ data->cs = 0;
+ data->node = "/machine/soc/fmc/ssi.0/child[0]";
+ /* beyond 16MB */
+ data->page_addr = 0x14000 * FLASH_PAGE_SIZE;
+
+ qtest_add_data_func("/ast2400/smc/read_jedec",
+ data, aspeed_smc_test_read_jedec);
+ qtest_add_data_func("/ast2400/smc/erase_sector",
+ data, aspeed_smc_test_erase_sector);
+ qtest_add_data_func("/ast2400/smc/erase_all",
+ data, aspeed_smc_test_erase_all);
+ qtest_add_data_func("/ast2400/smc/write_page",
+ data, aspeed_smc_test_write_page);
+ qtest_add_data_func("/ast2400/smc/read_page_mem",
+ data, aspeed_smc_test_read_page_mem);
+ qtest_add_data_func("/ast2400/smc/write_page_mem",
+ data, aspeed_smc_test_write_page_mem);
+ qtest_add_data_func("/ast2400/smc/read_status_reg",
+ data, aspeed_smc_test_read_status_reg);
+ qtest_add_data_func("/ast2400/smc/status_reg_write_protection",
+ data, aspeed_smc_test_status_reg_write_protection);
+ qtest_add_data_func("/ast2400/smc/write_block_protect",
+ data, aspeed_smc_test_write_block_protect);
+ qtest_add_data_func("/ast2400/smc/write_block_protect_bottom_bit",
+ data, aspeed_smc_test_write_block_protect_bottom_bit);
+}
+
+static void test_ast2500_evb(AspeedSMCTestData *data)
{
- uint8_t r;
-
- spi_conf(CONF_ENABLE_W0);
-
- /* default case: WP# is high and SRWD is low -> status register writable */
- spi_ctrl_start_user();
- writeb(ASPEED_FLASH_BASE, WREN);
- /* test ability to write SRWD */
- writeb(ASPEED_FLASH_BASE, WRSR);
- writeb(ASPEED_FLASH_BASE, SRWD);
- writeb(ASPEED_FLASH_BASE, RDSR);
- r = readb(ASPEED_FLASH_BASE);
- spi_ctrl_stop_user();
- g_assert_cmphex(r & SRWD, ==, SRWD);
-
- /* WP# high and SRWD high -> status register writable */
- spi_ctrl_start_user();
- writeb(ASPEED_FLASH_BASE, WREN);
- /* test ability to write SRWD */
- writeb(ASPEED_FLASH_BASE, WRSR);
- writeb(ASPEED_FLASH_BASE, 0);
- writeb(ASPEED_FLASH_BASE, RDSR);
- r = readb(ASPEED_FLASH_BASE);
- spi_ctrl_stop_user();
- g_assert_cmphex(r & SRWD, ==, 0);
-
- /* WP# low and SRWD low -> status register writable */
- qtest_set_irq_in(global_qtest,
- "/machine/soc/fmc/ssi.0/child[0]", "WP#", 0, 0);
- spi_ctrl_start_user();
- writeb(ASPEED_FLASH_BASE, WREN);
- /* test ability to write SRWD */
- writeb(ASPEED_FLASH_BASE, WRSR);
- writeb(ASPEED_FLASH_BASE, SRWD);
- writeb(ASPEED_FLASH_BASE, RDSR);
- r = readb(ASPEED_FLASH_BASE);
- spi_ctrl_stop_user();
- g_assert_cmphex(r & SRWD, ==, SRWD);
-
- /* WP# low and SRWD high -> status register NOT writable */
- spi_ctrl_start_user();
- writeb(ASPEED_FLASH_BASE, WREN);
- /* test ability to write SRWD */
- writeb(ASPEED_FLASH_BASE, WRSR);
- writeb(ASPEED_FLASH_BASE, 0);
- writeb(ASPEED_FLASH_BASE, RDSR);
- r = readb(ASPEED_FLASH_BASE);
- spi_ctrl_stop_user();
- /* write is not successful */
- g_assert_cmphex(r & SRWD, ==, SRWD);
+ int ret;
+ int fd;
- qtest_set_irq_in(global_qtest,
- "/machine/soc/fmc/ssi.0/child[0]", "WP#", 0, 1);
- flash_reset();
-}
+ fd = g_file_open_tmp("qtest.m25p80.mx25l25635e.XXXXXX",
+ &data->tmp_path, NULL);
+ g_assert(fd >= 0);
+ ret = ftruncate(fd, 32 * 1024 * 1024);
+ g_assert(ret == 0);
+ close(fd);
-static void test_write_block_protect(void)
+ data->s = qtest_initf("-machine ast2500-evb "
+ "-drive file=%s,format=raw,if=mtd",
+ data->tmp_path);
+
+ /* fmc cs0 with mx25l25635e flash */
+ data->flash_base = 0x20000000;
+ data->spi_base = 0x1E620000;
+ data->jedec_id = 0xc22019;
+ data->cs = 0;
+ data->node = "/machine/soc/fmc/ssi.0/child[0]";
+ /* beyond 16MB */
+ data->page_addr = 0x14000 * FLASH_PAGE_SIZE;
+
+ qtest_add_data_func("/ast2500/smc/read_jedec",
+ data, aspeed_smc_test_read_jedec);
+ qtest_add_data_func("/ast2500/smc/erase_sector",
+ data, aspeed_smc_test_erase_sector);
+ qtest_add_data_func("/ast2500/smc/erase_all",
+ data, aspeed_smc_test_erase_all);
+ qtest_add_data_func("/ast2500/smc/write_page",
+ data, aspeed_smc_test_write_page);
+ qtest_add_data_func("/ast2500/smc/read_page_mem",
+ data, aspeed_smc_test_read_page_mem);
+ qtest_add_data_func("/ast2500/smc/write_page_mem",
+ data, aspeed_smc_test_write_page_mem);
+ qtest_add_data_func("/ast2500/smc/read_status_reg",
+ data, aspeed_smc_test_read_status_reg);
+ qtest_add_data_func("/ast2500/smc/write_page_qpi",
+ data, aspeed_smc_test_write_page_qpi);
+}
+
+static void test_ast2600_evb(AspeedSMCTestData *data)
{
- uint32_t sector_size = 65536;
- uint32_t n_sectors = 512;
-
- spi_ce_ctrl(1 << CRTL_EXTENDED0);
- spi_conf(CONF_ENABLE_W0);
-
- uint32_t bp_bits = 0b0;
-
- for (int i = 0; i < 16; i++) {
- bp_bits = ((i & 0b1000) << 3) | ((i & 0b0111) << 2);
-
- spi_ctrl_start_user();
- writeb(ASPEED_FLASH_BASE, WREN);
- writeb(ASPEED_FLASH_BASE, BULK_ERASE);
- writeb(ASPEED_FLASH_BASE, WREN);
- writeb(ASPEED_FLASH_BASE, WRSR);
- writeb(ASPEED_FLASH_BASE, bp_bits);
- writeb(ASPEED_FLASH_BASE, EN_4BYTE_ADDR);
- writeb(ASPEED_FLASH_BASE, WREN);
- spi_ctrl_stop_user();
-
- uint32_t num_protected_sectors = i ? MIN(1 << (i - 1), n_sectors) : 0;
- uint32_t protection_start = n_sectors - num_protected_sectors;
- uint32_t protection_end = n_sectors;
-
- for (int sector = 0; sector < n_sectors; sector++) {
- uint32_t addr = sector * sector_size;
-
- assert_page_mem(addr, 0xffffffff);
- write_page_mem(addr, make_be32(0xabcdef12));
-
- uint32_t expected_value = protection_start <= sector
- && sector < protection_end
- ? 0xffffffff : 0xabcdef12;
-
- assert_page_mem(addr, expected_value);
- }
- }
+ int ret;
+ int fd;
- flash_reset();
-}
+ fd = g_file_open_tmp("qtest.m25p80.mx66u51235f.XXXXXX",
+ &data->tmp_path, NULL);
+ g_assert(fd >= 0);
+ ret = ftruncate(fd, 64 * 1024 * 1024);
+ g_assert(ret == 0);
+ close(fd);
-static void test_write_block_protect_bottom_bit(void)
+ data->s = qtest_initf("-machine ast2600-evb "
+ "-drive file=%s,format=raw,if=mtd",
+ data->tmp_path);
+
+ /* fmc cs0 with mx66u51235f flash */
+ data->flash_base = 0x20000000;
+ data->spi_base = 0x1E620000;
+ data->jedec_id = 0xc2253a;
+ data->cs = 0;
+ data->node = "/machine/soc/fmc/ssi.0/child[0]";
+ /* beyond 16MB */
+ data->page_addr = 0x14000 * FLASH_PAGE_SIZE;
+
+ qtest_add_data_func("/ast2600/smc/read_jedec",
+ data, aspeed_smc_test_read_jedec);
+ qtest_add_data_func("/ast2600/smc/erase_sector",
+ data, aspeed_smc_test_erase_sector);
+ qtest_add_data_func("/ast2600/smc/erase_all",
+ data, aspeed_smc_test_erase_all);
+ qtest_add_data_func("/ast2600/smc/write_page",
+ data, aspeed_smc_test_write_page);
+ qtest_add_data_func("/ast2600/smc/read_page_mem",
+ data, aspeed_smc_test_read_page_mem);
+ qtest_add_data_func("/ast2600/smc/write_page_mem",
+ data, aspeed_smc_test_write_page_mem);
+ qtest_add_data_func("/ast2600/smc/read_status_reg",
+ data, aspeed_smc_test_read_status_reg);
+ qtest_add_data_func("/ast2600/smc/write_page_qpi",
+ data, aspeed_smc_test_write_page_qpi);
+}
+
+static void test_ast1030_evb(AspeedSMCTestData *data)
{
- uint32_t sector_size = 65536;
- uint32_t n_sectors = 512;
-
- spi_ce_ctrl(1 << CRTL_EXTENDED0);
- spi_conf(CONF_ENABLE_W0);
-
- /* top bottom bit is enabled */
- uint32_t bp_bits = 0b00100 << 3;
-
- for (int i = 0; i < 16; i++) {
- bp_bits = (((i & 0b1000) | 0b0100) << 3) | ((i & 0b0111) << 2);
-
- spi_ctrl_start_user();
- writeb(ASPEED_FLASH_BASE, WREN);
- writeb(ASPEED_FLASH_BASE, BULK_ERASE);
- writeb(ASPEED_FLASH_BASE, WREN);
- writeb(ASPEED_FLASH_BASE, WRSR);
- writeb(ASPEED_FLASH_BASE, bp_bits);
- writeb(ASPEED_FLASH_BASE, EN_4BYTE_ADDR);
- writeb(ASPEED_FLASH_BASE, WREN);
- spi_ctrl_stop_user();
-
- uint32_t num_protected_sectors = i ? MIN(1 << (i - 1), n_sectors) : 0;
- uint32_t protection_start = 0;
- uint32_t protection_end = num_protected_sectors;
-
- for (int sector = 0; sector < n_sectors; sector++) {
- uint32_t addr = sector * sector_size;
-
- assert_page_mem(addr, 0xffffffff);
- write_page_mem(addr, make_be32(0xabcdef12));
-
- uint32_t expected_value = protection_start <= sector
- && sector < protection_end
- ? 0xffffffff : 0xabcdef12;
+ int ret;
+ int fd;
- assert_page_mem(addr, expected_value);
- }
- }
+ fd = g_file_open_tmp("qtest.m25p80.w25q80bl.XXXXXX",
+ &data->tmp_path, NULL);
+ g_assert(fd >= 0);
+ ret = ftruncate(fd, 1 * 1024 * 1024);
+ g_assert(ret == 0);
+ close(fd);
- flash_reset();
+ data->s = qtest_initf("-machine ast1030-evb "
+ "-drive file=%s,format=raw,if=mtd",
+ data->tmp_path);
+
+ /* fmc cs0 with w25q80bl flash */
+ data->flash_base = 0x80000000;
+ data->spi_base = 0x7E620000;
+ data->jedec_id = 0xef4014;
+ data->cs = 0;
+ data->node = "/machine/soc/fmc/ssi.0/child[0]";
+ /* beyond 512KB */
+ data->page_addr = 0x800 * FLASH_PAGE_SIZE;
+
+ qtest_add_data_func("/ast1030/smc/read_jedec",
+ data, aspeed_smc_test_read_jedec);
+ qtest_add_data_func("/ast1030/smc/erase_sector",
+ data, aspeed_smc_test_erase_sector);
+ qtest_add_data_func("/ast1030/smc/erase_all",
+ data, aspeed_smc_test_erase_all);
+ qtest_add_data_func("/ast1030/smc/write_page",
+ data, aspeed_smc_test_write_page);
+ qtest_add_data_func("/ast1030/smc/read_page_mem",
+ data, aspeed_smc_test_read_page_mem);
+ qtest_add_data_func("/ast1030/smc/write_page_mem",
+ data, aspeed_smc_test_write_page_mem);
+ qtest_add_data_func("/ast1030/smc/read_status_reg",
+ data, aspeed_smc_test_read_status_reg);
+ qtest_add_data_func("/ast1030/smc/write_page_qpi",
+ data, aspeed_smc_test_write_page_qpi);
}
int main(int argc, char **argv)
{
- g_autofree char *tmp_path = NULL;
+ AspeedSMCTestData palmetto_data;
+ AspeedSMCTestData ast2500_evb_data;
+ AspeedSMCTestData ast2600_evb_data;
+ AspeedSMCTestData ast1030_evb_data;
int ret;
- int fd;
g_test_init(&argc, &argv, NULL);
- fd = g_file_open_tmp("qtest.m25p80.XXXXXX", &tmp_path, NULL);
- g_assert(fd >= 0);
- ret = ftruncate(fd, FLASH_SIZE);
- g_assert(ret == 0);
- close(fd);
-
- global_qtest = qtest_initf("-m 256 -machine palmetto-bmc "
- "-drive file=%s,format=raw,if=mtd",
- tmp_path);
-
- qtest_add_func("/ast2400/smc/read_jedec", test_read_jedec);
- qtest_add_func("/ast2400/smc/erase_sector", test_erase_sector);
- qtest_add_func("/ast2400/smc/erase_all", test_erase_all);
- qtest_add_func("/ast2400/smc/write_page", test_write_page);
- qtest_add_func("/ast2400/smc/read_page_mem", test_read_page_mem);
- qtest_add_func("/ast2400/smc/write_page_mem", test_write_page_mem);
- qtest_add_func("/ast2400/smc/read_status_reg", test_read_status_reg);
- qtest_add_func("/ast2400/smc/status_reg_write_protection",
- test_status_reg_write_protection);
- qtest_add_func("/ast2400/smc/write_block_protect",
- test_write_block_protect);
- qtest_add_func("/ast2400/smc/write_block_protect_bottom_bit",
- test_write_block_protect_bottom_bit);
-
- flash_reset();
+ test_palmetto_bmc(&palmetto_data);
+ test_ast2500_evb(&ast2500_evb_data);
+ test_ast2600_evb(&ast2600_evb_data);
+ test_ast1030_evb(&ast1030_evb_data);
ret = g_test_run();
- qtest_quit(global_qtest);
- unlink(tmp_path);
+ qtest_quit(palmetto_data.s);
+ qtest_quit(ast2500_evb_data.s);
+ qtest_quit(ast2600_evb_data.s);
+ qtest_quit(ast1030_evb_data.s);
+ unlink(palmetto_data.tmp_path);
+ unlink(ast2500_evb_data.tmp_path);
+ unlink(ast2600_evb_data.tmp_path);
+ unlink(ast1030_evb_data.tmp_path);
+ g_free(palmetto_data.tmp_path);
+ g_free(ast2500_evb_data.tmp_path);
+ g_free(ast2600_evb_data.tmp_path);
+ g_free(ast1030_evb_data.tmp_path);
+
return ret;
}
diff --git a/tests/qtest/ast2700-gpio-test.c b/tests/qtest/ast2700-gpio-test.c
new file mode 100644
index 0000000..eeae9bf
--- /dev/null
+++ b/tests/qtest/ast2700-gpio-test.c
@@ -0,0 +1,95 @@
+/*
+ * QTest testcase for the ASPEED AST2700 GPIO Controller.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright (C) 2024 ASPEED Technology Inc.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/bitops.h"
+#include "qemu/timer.h"
+#include "qobject/qdict.h"
+#include "libqtest-single.h"
+
+#define AST2700_GPIO_BASE 0x14C0B000
+#define GPIOA0_CONTROL 0x180
+
+static void test_output_pins(const char *machine, const uint32_t base)
+{
+ QTestState *s = qtest_init(machine);
+ uint32_t offset = 0;
+ uint32_t value = 0;
+ uint32_t pin = 0;
+
+ for (char c = 'A'; c <= 'D'; c++) {
+ for (int i = 0; i < 8; i++) {
+ offset = base + (pin * 4);
+
+ /* output direction and output hi */
+ qtest_writel(s, offset, 0x00000003);
+ value = qtest_readl(s, offset);
+ g_assert_cmphex(value, ==, 0x00000003);
+
+ /* output direction and output low */
+ qtest_writel(s, offset, 0x00000002);
+ value = qtest_readl(s, offset);
+ g_assert_cmphex(value, ==, 0x00000002);
+ pin++;
+ }
+ }
+
+ qtest_quit(s);
+}
+
+static void test_input_pins(const char *machine, const uint32_t base)
+{
+ QTestState *s = qtest_init(machine);
+ char name[16];
+ uint32_t offset = 0;
+ uint32_t value = 0;
+ uint32_t pin = 0;
+
+ for (char c = 'A'; c <= 'D'; c++) {
+ for (int i = 0; i < 8; i++) {
+ sprintf(name, "gpio%c%d", c, i);
+ offset = base + (pin * 4);
+ /* input direction */
+ qtest_writel(s, offset, 0);
+
+ /* set input */
+ qtest_qom_set_bool(s, "/machine/soc/gpio", name, true);
+ value = qtest_readl(s, offset);
+ g_assert_cmphex(value, ==, 0x00002000);
+
+ /* clear input */
+ qtest_qom_set_bool(s, "/machine/soc/gpio", name, false);
+ value = qtest_readl(s, offset);
+ g_assert_cmphex(value, ==, 0);
+ pin++;
+ }
+ }
+
+ qtest_quit(s);
+}
+
+static void test_2700_input_pins(void)
+{
+ test_input_pins("-machine ast2700-evb",
+ AST2700_GPIO_BASE + GPIOA0_CONTROL);
+}
+
+static void test_2700_output_pins(void)
+{
+ test_output_pins("-machine ast2700-evb",
+ AST2700_GPIO_BASE + GPIOA0_CONTROL);
+}
+
+int main(int argc, char **argv)
+{
+ g_test_init(&argc, &argv, NULL);
+
+ qtest_add_func("/ast2700/gpio/input_pins", test_2700_input_pins);
+ qtest_add_func("/ast2700/gpio/output_pins", test_2700_output_pins);
+
+ return g_test_run();
+}
diff --git a/tests/qtest/ast2700-hace-test.c b/tests/qtest/ast2700-hace-test.c
new file mode 100644
index 0000000..a400e29
--- /dev/null
+++ b/tests/qtest/ast2700-hace-test.c
@@ -0,0 +1,98 @@
+/*
+ * QTest testcase for the ASPEED Hash and Crypto Engine
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright (C) 2025 ASPEED Technology Inc.
+ */
+
+#include "qemu/osdep.h"
+#include "libqtest.h"
+#include "qemu/bitops.h"
+#include "aspeed-hace-utils.h"
+
+static const struct AspeedMasks as2700_masks = {
+ .src = 0x7fffffff,
+ .dest = 0x7ffffff8,
+ .key = 0x7ffffff8,
+ .len = 0x0fffffff,
+ .src_hi = 0x00000003,
+ .dest_hi = 0x00000003,
+ .key_hi = 0x00000003,
+};
+
+/* ast2700 */
+static void test_md5_ast2700(void)
+{
+ aspeed_test_md5("-machine ast2700a1-evb", 0x12070000, 0x400000000);
+}
+
+static void test_sha256_ast2700(void)
+{
+ aspeed_test_sha256("-machine ast2700a1-evb", 0x12070000, 0x400000000);
+}
+
+static void test_sha256_sg_ast2700(void)
+{
+ aspeed_test_sha256_sg("-machine ast2700a1-evb", 0x12070000, 0x400000000);
+}
+
+static void test_sha384_ast2700(void)
+{
+ aspeed_test_sha384("-machine ast2700a1-evb", 0x12070000, 0x400000000);
+}
+
+static void test_sha384_sg_ast2700(void)
+{
+ aspeed_test_sha384_sg("-machine ast2700a1-evb", 0x12070000, 0x400000000);
+}
+
+static void test_sha512_ast2700(void)
+{
+ aspeed_test_sha512("-machine ast2700a1-evb", 0x12070000, 0x400000000);
+}
+
+static void test_sha512_sg_ast2700(void)
+{
+ aspeed_test_sha512_sg("-machine ast2700a1-evb", 0x12070000, 0x400000000);
+}
+
+static void test_sha256_accum_ast2700(void)
+{
+ aspeed_test_sha256_accum("-machine ast2700a1-evb", 0x12070000, 0x400000000);
+}
+
+static void test_sha384_accum_ast2700(void)
+{
+ aspeed_test_sha384_accum("-machine ast2700a1-evb", 0x12070000, 0x400000000);
+}
+
+static void test_sha512_accum_ast2700(void)
+{
+ aspeed_test_sha512_accum("-machine ast2700a1-evb", 0x12070000, 0x400000000);
+}
+
+static void test_addresses_ast2700(void)
+{
+ aspeed_test_addresses("-machine ast2700a1-evb", 0x12070000, &as2700_masks);
+}
+
+int main(int argc, char **argv)
+{
+ g_test_init(&argc, &argv, NULL);
+
+ qtest_add_func("ast2700/hace/addresses", test_addresses_ast2700);
+ qtest_add_func("ast2700/hace/sha512", test_sha512_ast2700);
+ qtest_add_func("ast2700/hace/sha384", test_sha384_ast2700);
+ qtest_add_func("ast2700/hace/sha256", test_sha256_ast2700);
+ qtest_add_func("ast2700/hace/md5", test_md5_ast2700);
+
+ qtest_add_func("ast2700/hace/sha512_sg", test_sha512_sg_ast2700);
+ qtest_add_func("ast2700/hace/sha384_sg", test_sha384_sg_ast2700);
+ qtest_add_func("ast2700/hace/sha256_sg", test_sha256_sg_ast2700);
+
+ qtest_add_func("ast2700/hace/sha512_accum", test_sha512_accum_ast2700);
+ qtest_add_func("ast2700/hace/sha384_accum", test_sha384_accum_ast2700);
+ qtest_add_func("ast2700/hace/sha256_accum", test_sha256_accum_ast2700);
+
+ return g_test_run();
+}
diff --git a/tests/qtest/ast2700-smc-test.c b/tests/qtest/ast2700-smc-test.c
new file mode 100644
index 0000000..62d538d
--- /dev/null
+++ b/tests/qtest/ast2700-smc-test.c
@@ -0,0 +1,72 @@
+/*
+ * QTest testcase for the M25P80 Flash using the ASPEED SPI Controller since
+ * AST2700.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright (C) 2024 ASPEED Technology Inc.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/bswap.h"
+#include "libqtest-single.h"
+#include "qemu/bitops.h"
+#include "aspeed-smc-utils.h"
+
+static void test_ast2700_evb(AspeedSMCTestData *data)
+{
+ int ret;
+ int fd;
+
+ fd = g_file_open_tmp("qtest.m25p80.w25q01jvq.XXXXXX",
+ &data->tmp_path, NULL);
+ g_assert(fd >= 0);
+ ret = ftruncate(fd, 128 * 1024 * 1024);
+ g_assert(ret == 0);
+ close(fd);
+
+ data->s = qtest_initf("-machine ast2700-evb "
+ "-drive file=%s,format=raw,if=mtd",
+ data->tmp_path);
+
+ /* fmc cs0 with w25q01jvq flash */
+ data->flash_base = 0x100000000;
+ data->spi_base = 0x14000000;
+ data->jedec_id = 0xef4021;
+ data->cs = 0;
+ data->node = "/machine/soc/fmc/ssi.0/child[0]";
+ /* beyond 64MB */
+ data->page_addr = 0x40000 * FLASH_PAGE_SIZE;
+
+ qtest_add_data_func("/ast2700/smc/read_jedec",
+ data, aspeed_smc_test_read_jedec);
+ qtest_add_data_func("/ast2700/smc/erase_sector",
+ data, aspeed_smc_test_erase_sector);
+ qtest_add_data_func("/ast2700/smc/erase_all",
+ data, aspeed_smc_test_erase_all);
+ qtest_add_data_func("/ast2700/smc/write_page",
+ data, aspeed_smc_test_write_page);
+ qtest_add_data_func("/ast2700/smc/read_page_mem",
+ data, aspeed_smc_test_read_page_mem);
+ qtest_add_data_func("/ast2700/smc/write_page_mem",
+ data, aspeed_smc_test_write_page_mem);
+ qtest_add_data_func("/ast2700/smc/read_status_reg",
+ data, aspeed_smc_test_read_status_reg);
+ qtest_add_data_func("/ast2700/smc/write_page_qpi",
+ data, aspeed_smc_test_write_page_qpi);
+}
+
+int main(int argc, char **argv)
+{
+ AspeedSMCTestData ast2700_evb_data;
+ int ret;
+
+ g_test_init(&argc, &argv, NULL);
+
+ test_ast2700_evb(&ast2700_evb_data);
+ ret = g_test_run();
+
+ qtest_quit(ast2700_evb_data.s);
+ unlink(ast2700_evb_data.tmp_path);
+ g_free(ast2700_evb_data.tmp_path);
+ return ret;
+}
diff --git a/tests/qtest/bcm2835-i2c-test.c b/tests/qtest/bcm2835-i2c-test.c
index 513ecce..1599194 100644
--- a/tests/qtest/bcm2835-i2c-test.c
+++ b/tests/qtest/bcm2835-i2c-test.c
@@ -81,7 +81,7 @@ static void test_i2c_read_write(gconstpointer data)
g_assert_cmpint(i2cdata, ==, 0xde);
i2cdata = readl(base_addr + BCM2835_I2C_FIFO);
- g_assert_cmpint(i2cdata, ==, 0xad);
+ g_assert_cmpint(i2cdata, ==, 0xa0);
/* Clear flags */
writel(base_addr + BCM2835_I2C_S, BCM2835_I2C_S_DONE | BCM2835_I2C_S_ERR |
diff --git a/tests/qtest/bios-tables-test.c b/tests/qtest/bios-tables-test.c
index d1ff4db..4dbc07e 100644
--- a/tests/qtest/bios-tables-test.c
+++ b/tests/qtest/bios-tables-test.c
@@ -78,6 +78,7 @@
typedef struct {
bool tcg_only;
const char *machine;
+ const char *arch;
const char *machine_param;
const char *variant;
const char *uefi_fl1;
@@ -262,8 +263,10 @@ static void dump_aml_files(test_data *data, bool rebuild)
g_assert(exp_sdt->aml);
if (rebuild) {
- aml_file = g_strdup_printf("%s/%s/%.4s%s", data_dir, data->machine,
+ aml_file = g_strdup_printf("%s/%s/%s/%.4s%s", data_dir,
+ data->arch, data->machine,
sdt->aml, ext);
+
if (!g_file_test(aml_file, G_FILE_TEST_EXISTS) &&
sdt->aml_len == exp_sdt->aml_len &&
!memcmp(sdt->aml, exp_sdt->aml, sdt->aml_len)) {
@@ -289,6 +292,7 @@ static void dump_aml_files(test_data *data, bool rebuild)
g_free(aml_file);
}
+ free_test_data(&exp_data);
}
static bool create_tmp_asl(AcpiSdtTable *sdt)
@@ -398,8 +402,8 @@ static GArray *load_expected_aml(test_data *data)
memset(&exp_sdt, 0, sizeof(exp_sdt));
try_again:
- aml_file = g_strdup_printf("%s/%s/%.4s%s", data_dir, data->machine,
- sdt->aml, ext);
+ aml_file = g_strdup_printf("%s/%s/%s/%.4s%s", data_dir, data->arch,
+ data->machine, sdt->aml, ext);
if (verbosity_level >= 2) {
fprintf(stderr, "Looking for expected file '%s'\n", aml_file);
}
@@ -916,6 +920,7 @@ static void test_acpi_piix4_tcg(void)
* This is to make guest actually run.
*/
data.machine = MACHINE_PC;
+ data.arch = "x86";
data.required_struct_types = base_required_struct_types;
data.required_struct_types_len = ARRAY_SIZE(base_required_struct_types);
test_acpi_one(NULL, &data);
@@ -927,6 +932,7 @@ static void test_acpi_piix4_tcg_bridge(void)
test_data data = {};
data.machine = MACHINE_PC;
+ data.arch = "x86";
data.variant = ".bridge";
data.required_struct_types = base_required_struct_types;
data.required_struct_types_len = ARRAY_SIZE(base_required_struct_types);
@@ -954,7 +960,7 @@ static void test_acpi_piix4_tcg_bridge(void)
free_test_data(&data);
/* check that reboot/reset doesn't change any ACPI tables */
- qtest_qmp_send(data.qts, "{'execute':'system_reset' }");
+ qtest_system_reset(data.qts);
process_acpi_tables(&data);
free_test_data(&data);
}
@@ -964,6 +970,7 @@ static void test_acpi_piix4_no_root_hotplug(void)
test_data data = {};
data.machine = MACHINE_PC;
+ data.arch = "x86";
data.variant = ".roothp";
data.required_struct_types = base_required_struct_types;
data.required_struct_types_len = ARRAY_SIZE(base_required_struct_types);
@@ -980,6 +987,7 @@ static void test_acpi_piix4_no_bridge_hotplug(void)
test_data data = {};
data.machine = MACHINE_PC;
+ data.arch = "x86";
data.variant = ".hpbridge";
data.required_struct_types = base_required_struct_types;
data.required_struct_types_len = ARRAY_SIZE(base_required_struct_types);
@@ -996,6 +1004,7 @@ static void test_acpi_piix4_no_acpi_pci_hotplug(void)
test_data data = {};
data.machine = MACHINE_PC;
+ data.arch = "x86";
data.variant = ".hpbrroot";
data.required_struct_types = base_required_struct_types;
data.required_struct_types_len = ARRAY_SIZE(base_required_struct_types);
@@ -1017,6 +1026,7 @@ static void test_acpi_q35_tcg(void)
test_data data = {};
data.machine = MACHINE_Q35;
+ data.arch = "x86";
data.required_struct_types = base_required_struct_types;
data.required_struct_types_len = ARRAY_SIZE(base_required_struct_types);
test_acpi_one(NULL, &data);
@@ -1032,6 +1042,7 @@ static void test_acpi_q35_kvm_type4_count(void)
{
test_data data = {
.machine = MACHINE_Q35,
+ .arch = "x86",
.variant = ".type4-count",
.required_struct_types = base_required_struct_types,
.required_struct_types_len = ARRAY_SIZE(base_required_struct_types),
@@ -1048,6 +1059,7 @@ static void test_acpi_q35_kvm_core_count(void)
{
test_data data = {
.machine = MACHINE_Q35,
+ .arch = "x86",
.variant = ".core-count",
.required_struct_types = base_required_struct_types,
.required_struct_types_len = ARRAY_SIZE(base_required_struct_types),
@@ -1065,6 +1077,7 @@ static void test_acpi_q35_kvm_core_count2(void)
{
test_data data = {
.machine = MACHINE_Q35,
+ .arch = "x86",
.variant = ".core-count2",
.required_struct_types = base_required_struct_types,
.required_struct_types_len = ARRAY_SIZE(base_required_struct_types),
@@ -1082,6 +1095,7 @@ static void test_acpi_q35_kvm_thread_count(void)
{
test_data data = {
.machine = MACHINE_Q35,
+ .arch = "x86",
.variant = ".thread-count",
.required_struct_types = base_required_struct_types,
.required_struct_types_len = ARRAY_SIZE(base_required_struct_types),
@@ -1099,6 +1113,7 @@ static void test_acpi_q35_kvm_thread_count2(void)
{
test_data data = {
.machine = MACHINE_Q35,
+ .arch = "x86",
.variant = ".thread-count2",
.required_struct_types = base_required_struct_types,
.required_struct_types_len = ARRAY_SIZE(base_required_struct_types),
@@ -1117,6 +1132,7 @@ static void test_acpi_q35_tcg_bridge(void)
test_data data = {};
data.machine = MACHINE_Q35;
+ data.arch = "x86",
data.variant = ".bridge";
data.required_struct_types = base_required_struct_types;
data.required_struct_types_len = ARRAY_SIZE(base_required_struct_types);
@@ -1131,6 +1147,7 @@ static void test_acpi_q35_tcg_no_acpi_hotplug(void)
test_data data = {};
data.machine = MACHINE_Q35;
+ data.arch = "x86",
data.variant = ".noacpihp";
data.required_struct_types = base_required_struct_types;
data.required_struct_types_len = ARRAY_SIZE(base_required_struct_types);
@@ -1159,6 +1176,7 @@ static void test_acpi_q35_multif_bridge(void)
{
test_data data = {
.machine = MACHINE_Q35,
+ .arch = "x86",
.variant = ".multi-bridge",
};
test_vm_prepare("-S"
@@ -1199,7 +1217,7 @@ static void test_acpi_q35_multif_bridge(void)
free_test_data(&data);
/* check that reboot/reset doesn't change any ACPI tables */
- qtest_qmp_send(data.qts, "{'execute':'system_reset' }");
+ qtest_system_reset(data.qts);
process_acpi_tables(&data);
free_test_data(&data);
}
@@ -1208,6 +1226,7 @@ static void test_acpi_q35_tcg_mmio64(void)
{
test_data data = {
.machine = MACHINE_Q35,
+ .arch = "x86",
.variant = ".mmio64",
.tcg_only = true,
.required_struct_types = base_required_struct_types,
@@ -1228,6 +1247,7 @@ static void test_acpi_piix4_tcg_cphp(void)
test_data data = {};
data.machine = MACHINE_PC;
+ data.arch = "x86";
data.variant = ".cphp";
test_acpi_one("-smp 2,cores=3,sockets=2,maxcpus=6"
" -object memory-backend-ram,id=ram0,size=64M"
@@ -1243,6 +1263,7 @@ static void test_acpi_q35_tcg_cphp(void)
test_data data = {};
data.machine = MACHINE_Q35;
+ data.arch = "x86",
data.variant = ".cphp";
test_acpi_one(" -smp 2,cores=3,sockets=2,maxcpus=6"
" -object memory-backend-ram,id=ram0,size=64M"
@@ -1262,6 +1283,7 @@ static void test_acpi_q35_tcg_ipmi(void)
test_data data = {};
data.machine = MACHINE_Q35;
+ data.arch = "x86",
data.variant = ".ipmibt";
data.required_struct_types = ipmi_required_struct_types;
data.required_struct_types_len = ARRAY_SIZE(ipmi_required_struct_types);
@@ -1276,6 +1298,7 @@ static void test_acpi_q35_tcg_smbus_ipmi(void)
test_data data = {};
data.machine = MACHINE_Q35;
+ data.arch = "x86",
data.variant = ".ipmismbus";
data.required_struct_types = ipmi_required_struct_types;
data.required_struct_types_len = ARRAY_SIZE(ipmi_required_struct_types);
@@ -1293,6 +1316,7 @@ static void test_acpi_piix4_tcg_ipmi(void)
* This is to make guest actually run.
*/
data.machine = MACHINE_PC;
+ data.arch = "x86";
data.variant = ".ipmikcs";
data.required_struct_types = ipmi_required_struct_types;
data.required_struct_types_len = ARRAY_SIZE(ipmi_required_struct_types);
@@ -1307,6 +1331,7 @@ static void test_acpi_q35_tcg_memhp(void)
test_data data = {};
data.machine = MACHINE_Q35;
+ data.arch = "x86",
data.variant = ".memhp";
test_acpi_one(" -m 128,slots=3,maxmem=1G"
" -object memory-backend-ram,id=ram0,size=64M"
@@ -1322,6 +1347,7 @@ static void test_acpi_piix4_tcg_memhp(void)
test_data data = {};
data.machine = MACHINE_PC;
+ data.arch = "x86";
data.variant = ".memhp";
test_acpi_one(" -m 128,slots=3,maxmem=1G"
" -object memory-backend-ram,id=ram0,size=64M"
@@ -1337,6 +1363,7 @@ static void test_acpi_piix4_tcg_nosmm(void)
test_data data = {};
data.machine = MACHINE_PC;
+ data.arch = "x86";
data.variant = ".nosmm";
test_acpi_one("-machine smm=off", &data);
free_test_data(&data);
@@ -1347,6 +1374,7 @@ static void test_acpi_piix4_tcg_smm_compat(void)
test_data data = {};
data.machine = MACHINE_PC;
+ data.arch = "x86";
data.variant = ".smm-compat";
test_acpi_one("-global PIIX4_PM.smm-compat=on", &data);
free_test_data(&data);
@@ -1357,6 +1385,7 @@ static void test_acpi_piix4_tcg_smm_compat_nosmm(void)
test_data data = {};
data.machine = MACHINE_PC;
+ data.arch = "x86";
data.variant = ".smm-compat-nosmm";
test_acpi_one("-global PIIX4_PM.smm-compat=on -machine smm=off", &data);
free_test_data(&data);
@@ -1367,6 +1396,7 @@ static void test_acpi_piix4_tcg_nohpet(void)
test_data data = {};
data.machine = MACHINE_PC;
+ data.arch = "x86";
data.machine_param = ",hpet=off";
data.variant = ".nohpet";
test_acpi_one(NULL, &data);
@@ -1378,6 +1408,7 @@ static void test_acpi_q35_tcg_numamem(void)
test_data data = {};
data.machine = MACHINE_Q35;
+ data.arch = "x86",
data.variant = ".numamem";
test_acpi_one(" -object memory-backend-ram,id=ram0,size=128M"
" -numa node -numa node,memdev=ram0", &data);
@@ -1389,6 +1420,7 @@ static void test_acpi_q35_kvm_xapic(void)
test_data data = {};
data.machine = MACHINE_Q35;
+ data.arch = "x86",
data.variant = ".xapic";
test_acpi_one(" -object memory-backend-ram,id=ram0,size=128M"
" -numa node -numa node,memdev=ram0"
@@ -1401,6 +1433,7 @@ static void test_acpi_q35_tcg_nosmm(void)
test_data data = {};
data.machine = MACHINE_Q35;
+ data.arch = "x86",
data.variant = ".nosmm";
test_acpi_one("-machine smm=off", &data);
free_test_data(&data);
@@ -1411,6 +1444,7 @@ static void test_acpi_q35_tcg_smm_compat(void)
test_data data = {};
data.machine = MACHINE_Q35;
+ data.arch = "x86",
data.variant = ".smm-compat";
test_acpi_one("-global ICH9-LPC.smm-compat=on", &data);
free_test_data(&data);
@@ -1421,6 +1455,7 @@ static void test_acpi_q35_tcg_smm_compat_nosmm(void)
test_data data = {};
data.machine = MACHINE_Q35;
+ data.arch = "x86",
data.variant = ".smm-compat-nosmm";
test_acpi_one("-global ICH9-LPC.smm-compat=on -machine smm=off", &data);
free_test_data(&data);
@@ -1431,6 +1466,7 @@ static void test_acpi_q35_tcg_nohpet(void)
test_data data = {};
data.machine = MACHINE_Q35;
+ data.arch = "x86",
data.machine_param = ",hpet=off";
data.variant = ".nohpet";
test_acpi_one(NULL, &data);
@@ -1442,6 +1478,7 @@ static void test_acpi_q35_kvm_dmar(void)
test_data data = {};
data.machine = MACHINE_Q35;
+ data.arch = "x86",
data.variant = ".dmar";
test_acpi_one("-machine kernel-irqchip=split -accel kvm"
" -device intel-iommu,intremap=on,device-iotlb=on", &data);
@@ -1453,6 +1490,7 @@ static void test_acpi_q35_tcg_ivrs(void)
test_data data = {};
data.machine = MACHINE_Q35;
+ data.arch = "x86",
data.variant = ".ivrs";
data.tcg_only = true,
test_acpi_one(" -device amd-iommu", &data);
@@ -1464,6 +1502,7 @@ static void test_acpi_piix4_tcg_numamem(void)
test_data data = {};
data.machine = MACHINE_PC;
+ data.arch = "x86";
data.variant = ".numamem";
test_acpi_one(" -object memory-backend-ram,id=ram0,size=128M"
" -numa node -numa node,memdev=ram0", &data);
@@ -1472,8 +1511,9 @@ static void test_acpi_piix4_tcg_numamem(void)
uint64_t tpm_tis_base_addr;
-static void test_acpi_tcg_tpm(const char *machine, const char *tpm_if,
- uint64_t base, enum TPMVersion tpm_version)
+static void test_acpi_tcg_tpm(const char *machine, const char *arch,
+ const char *tpm_if, uint64_t base,
+ enum TPMVersion tpm_version)
{
gchar *tmp_dir_name = g_strdup_printf("qemu-test_acpi_%s_tcg_%s.XXXXXX",
machine, tpm_if);
@@ -1500,6 +1540,7 @@ static void test_acpi_tcg_tpm(const char *machine, const char *tpm_if,
tpm_emu_test_wait_cond(&test);
data.machine = machine;
+ data.arch = arch;
data.variant = variant;
args = g_strdup_printf(
@@ -1523,19 +1564,20 @@ static void test_acpi_tcg_tpm(const char *machine, const char *tpm_if,
static void test_acpi_q35_tcg_tpm2_tis(void)
{
- test_acpi_tcg_tpm("q35", "tis", 0xFED40000, TPM_VERSION_2_0);
+ test_acpi_tcg_tpm("q35", "x86", "tis", 0xFED40000, TPM_VERSION_2_0);
}
static void test_acpi_q35_tcg_tpm12_tis(void)
{
- test_acpi_tcg_tpm("q35", "tis", 0xFED40000, TPM_VERSION_1_2);
+ test_acpi_tcg_tpm("q35", "x86", "tis", 0xFED40000, TPM_VERSION_1_2);
}
-static void test_acpi_tcg_dimm_pxm(const char *machine)
+static void test_acpi_tcg_dimm_pxm(const char *machine, const char *arch)
{
test_data data = {};
data.machine = machine;
+ data.arch = arch;
data.variant = ".dimmpxm";
test_acpi_one(" -machine nvdimm=on,nvdimm-persistence=cpu"
" -smp 4,sockets=4"
@@ -1562,24 +1604,25 @@ static void test_acpi_tcg_dimm_pxm(const char *machine)
static void test_acpi_q35_tcg_dimm_pxm(void)
{
- test_acpi_tcg_dimm_pxm(MACHINE_Q35);
+ test_acpi_tcg_dimm_pxm(MACHINE_Q35, "x86");
}
static void test_acpi_piix4_tcg_dimm_pxm(void)
{
- test_acpi_tcg_dimm_pxm(MACHINE_PC);
+ test_acpi_tcg_dimm_pxm(MACHINE_PC, "x86");
}
-static void test_acpi_virt_tcg_memhp(void)
+static void test_acpi_aarch64_virt_tcg_memhp(void)
{
test_data data = {
.machine = "virt",
+ .arch = "aarch64",
.tcg_only = true,
.uefi_fl1 = "pc-bios/edk2-aarch64-code.fd",
.uefi_fl2 = "pc-bios/edk2-arm-vars.fd",
.cd = "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2",
.ram_start = 0x40000000ULL,
- .scan_len = 256ULL * 1024 * 1024,
+ .scan_len = 256ULL * MiB,
};
data.variant = ".memhp";
@@ -1603,6 +1646,7 @@ static void test_acpi_virt_tcg_memhp(void)
static void test_acpi_microvm_prepare(test_data *data)
{
data->machine = "microvm";
+ data->arch = "x86";
data->required_struct_types = NULL; /* no smbios */
data->required_struct_types_len = 0;
data->blkdev = "virtio-blk-device";
@@ -1663,16 +1707,43 @@ static void test_acpi_microvm_ioapic2_tcg(void)
free_test_data(&data);
}
-static void test_acpi_virt_tcg_numamem(void)
+static void test_acpi_riscv64_virt_tcg_numamem(void)
{
test_data data = {
.machine = "virt",
+ .arch = "riscv64",
+ .tcg_only = true,
+ .uefi_fl1 = "pc-bios/edk2-riscv-code.fd",
+ .uefi_fl2 = "pc-bios/edk2-riscv-vars.fd",
+ .cd = "tests/data/uefi-boot-images/bios-tables-test.riscv64.iso.qcow2",
+ .ram_start = 0x80000000ULL,
+ .scan_len = 128ULL * MiB,
+ };
+
+ data.variant = ".numamem";
+ /*
+ * RHCT will have ISA string encoded. To reduce the effort
+ * of updating expected AML file for any new default ISA extension,
+ * use the profile rva22s64.
+ */
+ test_acpi_one(" -cpu rva22s64"
+ " -object memory-backend-ram,id=ram0,size=128M"
+ " -numa node,memdev=ram0",
+ &data);
+ free_test_data(&data);
+}
+
+static void test_acpi_aarch64_virt_tcg_numamem(void)
+{
+ test_data data = {
+ .machine = "virt",
+ .arch = "aarch64",
.tcg_only = true,
.uefi_fl1 = "pc-bios/edk2-aarch64-code.fd",
.uefi_fl2 = "pc-bios/edk2-arm-vars.fd",
.cd = "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2",
.ram_start = 0x40000000ULL,
- .scan_len = 128ULL * 1024 * 1024,
+ .scan_len = 128ULL * MiB,
};
data.variant = ".numamem";
@@ -1685,15 +1756,16 @@ static void test_acpi_virt_tcg_numamem(void)
}
-static void test_acpi_virt_tcg_pxb(void)
+static void test_acpi_aarch64_virt_tcg_pxb(void)
{
test_data data = {
.machine = "virt",
+ .arch = "aarch64",
.tcg_only = true,
.uefi_fl1 = "pc-bios/edk2-aarch64-code.fd",
.uefi_fl2 = "pc-bios/edk2-arm-vars.fd",
.ram_start = 0x40000000ULL,
- .scan_len = 128ULL * 1024 * 1024,
+ .scan_len = 128ULL * MiB,
};
/*
* While using -cdrom, the cdrom would auto plugged into pxb-pcie,
@@ -1717,11 +1789,12 @@ static void test_acpi_virt_tcg_pxb(void)
free_test_data(&data);
}
-static void test_acpi_tcg_acpi_hmat(const char *machine)
+static void test_acpi_tcg_acpi_hmat(const char *machine, const char *arch)
{
test_data data = {};
data.machine = machine;
+ data.arch = arch;
data.variant = ".acpihmat";
test_acpi_one(" -machine hmat=on"
" -smp 2,sockets=2"
@@ -1750,24 +1823,25 @@ static void test_acpi_tcg_acpi_hmat(const char *machine)
static void test_acpi_q35_tcg_acpi_hmat(void)
{
- test_acpi_tcg_acpi_hmat(MACHINE_Q35);
+ test_acpi_tcg_acpi_hmat(MACHINE_Q35, "x86");
}
static void test_acpi_piix4_tcg_acpi_hmat(void)
{
- test_acpi_tcg_acpi_hmat(MACHINE_PC);
+ test_acpi_tcg_acpi_hmat(MACHINE_PC, "x86");
}
-static void test_acpi_virt_tcg_acpi_hmat(void)
+static void test_acpi_aarch64_virt_tcg_acpi_hmat(void)
{
test_data data = {
.machine = "virt",
+ .arch = "aarch64",
.tcg_only = true,
.uefi_fl1 = "pc-bios/edk2-aarch64-code.fd",
.uefi_fl2 = "pc-bios/edk2-arm-vars.fd",
.cd = "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2",
.ram_start = 0x40000000ULL,
- .scan_len = 128ULL * 1024 * 1024,
+ .scan_len = 128ULL * MiB,
};
data.variant = ".acpihmatvirt";
@@ -1820,6 +1894,7 @@ static void test_acpi_q35_tcg_acpi_hmat_noinitiator(void)
test_data data = {};
data.machine = MACHINE_Q35;
+ data.arch = "x86";
data.variant = ".acpihmat-noinitiator";
test_acpi_one(" -machine hmat=on"
" -smp 4,sockets=2"
@@ -1862,14 +1937,110 @@ static void test_acpi_q35_tcg_acpi_hmat_noinitiator(void)
free_test_data(&data);
}
+/* Test intended to hit corner cases of SRAT and HMAT */
+static void test_acpi_q35_tcg_acpi_hmat_generic_x(void)
+{
+ test_data data = {};
+
+ data.machine = MACHINE_Q35;
+ data.arch = "x86";
+ data.variant = ".acpihmat-generic-x";
+ test_acpi_one(" -machine hmat=on,cxl=on"
+ " -smp 3,sockets=3"
+ " -m 128M,maxmem=384M,slots=2"
+ " -device pcie-root-port,chassis=1,id=pci.1"
+ " -device pci-testdev,bus=pci.1,"
+ "multifunction=on,addr=00.0"
+ " -device pci-testdev,bus=pci.1,addr=00.1"
+ " -device pci-testdev,bus=pci.1,id=gidev,addr=00.2"
+ " -device pxb-cxl,bus_nr=64,bus=pcie.0,id=cxl.1"
+ " -object memory-backend-ram,size=64M,id=ram0"
+ " -object memory-backend-ram,size=64M,id=ram1"
+ " -numa node,nodeid=0,cpus=0,memdev=ram0"
+ " -numa node,nodeid=1"
+ " -object acpi-generic-initiator,id=gi0,pci-dev=gidev,node=1"
+ " -numa node,nodeid=2"
+ " -object acpi-generic-port,id=gp0,pci-bus=cxl.1,node=2"
+ " -numa node,nodeid=3,cpus=1"
+ " -numa node,nodeid=4,memdev=ram1"
+ " -numa node,nodeid=5,cpus=2"
+ " -numa hmat-lb,initiator=0,target=0,hierarchy=memory,"
+ "data-type=access-latency,latency=10"
+ " -numa hmat-lb,initiator=0,target=0,hierarchy=memory,"
+ "data-type=access-bandwidth,bandwidth=800M"
+ " -numa hmat-lb,initiator=0,target=2,hierarchy=memory,"
+ "data-type=access-latency,latency=100"
+ " -numa hmat-lb,initiator=0,target=2,hierarchy=memory,"
+ "data-type=access-bandwidth,bandwidth=200M"
+ " -numa hmat-lb,initiator=0,target=4,hierarchy=memory,"
+ "data-type=access-latency,latency=100"
+ " -numa hmat-lb,initiator=0,target=4,hierarchy=memory,"
+ "data-type=access-bandwidth,bandwidth=200M"
+ " -numa hmat-lb,initiator=0,target=5,hierarchy=memory,"
+ "data-type=access-latency,latency=200"
+ " -numa hmat-lb,initiator=0,target=5,hierarchy=memory,"
+ "data-type=access-bandwidth,bandwidth=400M"
+ " -numa hmat-lb,initiator=1,target=0,hierarchy=memory,"
+ "data-type=access-latency,latency=500"
+ " -numa hmat-lb,initiator=1,target=0,hierarchy=memory,"
+ "data-type=access-bandwidth,bandwidth=100M"
+ " -numa hmat-lb,initiator=1,target=2,hierarchy=memory,"
+ "data-type=access-latency,latency=50"
+ " -numa hmat-lb,initiator=1,target=2,hierarchy=memory,"
+ "data-type=access-bandwidth,bandwidth=400M"
+ " -numa hmat-lb,initiator=1,target=4,hierarchy=memory,"
+ "data-type=access-latency,latency=50"
+ " -numa hmat-lb,initiator=1,target=4,hierarchy=memory,"
+ "data-type=access-bandwidth,bandwidth=800M"
+ " -numa hmat-lb,initiator=1,target=5,hierarchy=memory,"
+ "data-type=access-latency,latency=500"
+ " -numa hmat-lb,initiator=1,target=5,hierarchy=memory,"
+ "data-type=access-bandwidth,bandwidth=100M"
+ " -numa hmat-lb,initiator=3,target=0,hierarchy=memory,"
+ "data-type=access-latency,latency=20"
+ " -numa hmat-lb,initiator=3,target=0,hierarchy=memory,"
+ "data-type=access-bandwidth,bandwidth=400M"
+ " -numa hmat-lb,initiator=3,target=2,hierarchy=memory,"
+ "data-type=access-latency,latency=80"
+ " -numa hmat-lb,initiator=3,target=2,hierarchy=memory,"
+ "data-type=access-bandwidth,bandwidth=200M"
+ " -numa hmat-lb,initiator=3,target=4,hierarchy=memory,"
+ "data-type=access-latency,latency=80"
+ " -numa hmat-lb,initiator=3,target=4,hierarchy=memory,"
+ "data-type=access-bandwidth,bandwidth=200M"
+ " -numa hmat-lb,initiator=3,target=5,hierarchy=memory,"
+ "data-type=access-latency,latency=20"
+ " -numa hmat-lb,initiator=3,target=5,hierarchy=memory,"
+ "data-type=access-bandwidth,bandwidth=400M"
+ " -numa hmat-lb,initiator=5,target=0,hierarchy=memory,"
+ "data-type=access-latency,latency=20"
+ " -numa hmat-lb,initiator=5,target=0,hierarchy=memory,"
+ "data-type=access-bandwidth,bandwidth=400M"
+ " -numa hmat-lb,initiator=5,target=2,hierarchy=memory,"
+ "data-type=access-latency,latency=80"
+ " -numa hmat-lb,initiator=5,target=4,hierarchy=memory,"
+ "data-type=access-bandwidth,bandwidth=200M"
+ " -numa hmat-lb,initiator=5,target=4,hierarchy=memory,"
+ "data-type=access-latency,latency=80"
+ " -numa hmat-lb,initiator=5,target=2,hierarchy=memory,"
+ "data-type=access-bandwidth,bandwidth=200M"
+ " -numa hmat-lb,initiator=5,target=5,hierarchy=memory,"
+ "data-type=access-latency,latency=10"
+ " -numa hmat-lb,initiator=5,target=5,hierarchy=memory,"
+ "data-type=access-bandwidth,bandwidth=800M",
+ &data);
+ free_test_data(&data);
+}
+
#ifdef CONFIG_POSIX
-static void test_acpi_erst(const char *machine)
+static void test_acpi_erst(const char *machine, const char *arch)
{
gchar *tmp_path = g_dir_make_tmp("qemu-test-erst.XXXXXX", NULL);
gchar *params;
test_data data = {};
data.machine = machine;
+ data.arch = arch;
data.variant = ".acpierst";
params = g_strdup_printf(
" -object memory-backend-file,id=erstnvram,"
@@ -1884,12 +2055,12 @@ static void test_acpi_erst(const char *machine)
static void test_acpi_piix4_acpi_erst(void)
{
- test_acpi_erst(MACHINE_PC);
+ test_acpi_erst(MACHINE_PC, "x86");
}
static void test_acpi_q35_acpi_erst(void)
{
- test_acpi_erst(MACHINE_Q35);
+ test_acpi_erst(MACHINE_Q35, "x86");
}
static void test_acpi_microvm_acpi_erst(void)
@@ -1914,16 +2085,39 @@ static void test_acpi_microvm_acpi_erst(void)
}
#endif /* CONFIG_POSIX */
-static void test_acpi_virt_tcg(void)
+static void test_acpi_riscv64_virt_tcg(void)
+{
+ test_data data = {
+ .machine = "virt",
+ .arch = "riscv64",
+ .tcg_only = true,
+ .uefi_fl1 = "pc-bios/edk2-riscv-code.fd",
+ .uefi_fl2 = "pc-bios/edk2-riscv-vars.fd",
+ .cd = "tests/data/uefi-boot-images/bios-tables-test.riscv64.iso.qcow2",
+ .ram_start = 0x80000000ULL,
+ .scan_len = 128ULL * MiB,
+ };
+
+ /*
+ * RHCT will have ISA string encoded. To reduce the effort
+ * of updating expected AML file for any new default ISA extension,
+ * use the profile rva22s64.
+ */
+ test_acpi_one("-cpu rva22s64 ", &data);
+ free_test_data(&data);
+}
+
+static void test_acpi_aarch64_virt_tcg(void)
{
test_data data = {
.machine = "virt",
+ .arch = "aarch64",
.tcg_only = true,
.uefi_fl1 = "pc-bios/edk2-aarch64-code.fd",
.uefi_fl2 = "pc-bios/edk2-arm-vars.fd",
.cd = "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2",
.ram_start = 0x40000000ULL,
- .scan_len = 128ULL * 1024 * 1024,
+ .scan_len = 128ULL * MiB,
};
data.smbios_cpu_max_speed = 2900;
@@ -1933,17 +2127,18 @@ static void test_acpi_virt_tcg(void)
free_test_data(&data);
}
-static void test_acpi_virt_tcg_topology(void)
+static void test_acpi_aarch64_virt_tcg_topology(void)
{
test_data data = {
.machine = "virt",
+ .arch = "aarch64",
.variant = ".topology",
.tcg_only = true,
.uefi_fl1 = "pc-bios/edk2-aarch64-code.fd",
.uefi_fl2 = "pc-bios/edk2-arm-vars.fd",
.cd = "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2",
.ram_start = 0x40000000ULL,
- .scan_len = 128ULL * 1024 * 1024,
+ .scan_len = 128ULL * MiB,
};
test_acpi_one("-cpu cortex-a57 "
@@ -1951,10 +2146,30 @@ static void test_acpi_virt_tcg_topology(void)
free_test_data(&data);
}
+static void test_acpi_aarch64_virt_tcg_its_off(void)
+{
+ test_data data = {
+ .machine = "virt",
+ .arch = "aarch64",
+ .variant = ".its_off",
+ .tcg_only = true,
+ .uefi_fl1 = "pc-bios/edk2-aarch64-code.fd",
+ .uefi_fl2 = "pc-bios/edk2-arm-vars.fd",
+ .cd = "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2",
+ .ram_start = 0x40000000ULL,
+ .scan_len = 128ULL * 1024 * 1024,
+ };
+
+ test_acpi_one("-cpu cortex-a57 "
+ "-M gic-version=3,iommu=smmuv3,its=off", &data);
+ free_test_data(&data);
+}
+
static void test_acpi_q35_viot(void)
{
test_data data = {
.machine = MACHINE_Q35,
+ .arch = "x86",
.variant = ".viot",
};
@@ -1979,6 +2194,7 @@ static void test_acpi_q35_cxl(void)
test_data data = {
.machine = MACHINE_Q35,
+ .arch = "x86",
.variant = ".cxl",
};
/*
@@ -2016,16 +2232,17 @@ static void test_acpi_q35_cxl(void)
}
#endif /* CONFIG_POSIX */
-static void test_acpi_virt_viot(void)
+static void test_acpi_aarch64_virt_viot(void)
{
test_data data = {
.machine = "virt",
+ .arch = "aarch64",
.tcg_only = true,
.uefi_fl1 = "pc-bios/edk2-aarch64-code.fd",
.uefi_fl2 = "pc-bios/edk2-arm-vars.fd",
.cd = "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2",
.ram_start = 0x40000000ULL,
- .scan_len = 128ULL * 1024 * 1024,
+ .scan_len = 128ULL * MiB,
};
test_acpi_one("-cpu cortex-a57 "
@@ -2043,6 +2260,7 @@ static void test_acpi_q35_slic(void)
{
test_data data = {
.machine = MACHINE_Q35,
+ .arch = "x86",
.variant = ".slic",
};
@@ -2057,6 +2275,7 @@ static void test_acpi_q35_applesmc(void)
{
test_data data = {
.machine = MACHINE_Q35,
+ .arch = "x86",
.variant = ".applesmc",
};
@@ -2070,6 +2289,7 @@ static void test_acpi_q35_pvpanic_isa(void)
{
test_data data = {
.machine = MACHINE_Q35,
+ .arch = "x86",
.variant = ".pvpanic-isa",
};
@@ -2082,6 +2302,7 @@ static void test_acpi_pc_smbios_options(void)
uint8_t req_type11[] = { 11 };
test_data data = {
.machine = MACHINE_PC,
+ .arch = "x86",
.variant = ".pc_smbios_options",
.required_struct_types = req_type11,
.required_struct_types_len = ARRAY_SIZE(req_type11),
@@ -2096,6 +2317,7 @@ static void test_acpi_pc_smbios_blob(void)
uint8_t req_type11[] = { 11 };
test_data data = {
.machine = MACHINE_PC,
+ .arch = "x86",
.variant = ".pc_smbios_blob",
.required_struct_types = req_type11,
.required_struct_types_len = ARRAY_SIZE(req_type11),
@@ -2145,6 +2367,7 @@ static void test_acpi_piix4_oem_fields(void)
test_data data = {};
data.machine = MACHINE_PC;
+ data.arch = "x86";
data.required_struct_types = base_required_struct_types;
data.required_struct_types_len = ARRAY_SIZE(base_required_struct_types);
@@ -2163,6 +2386,7 @@ static void test_acpi_q35_oem_fields(void)
test_data data = {};
data.machine = MACHINE_Q35;
+ data.arch = "x86";
data.required_struct_types = base_required_struct_types;
data.required_struct_types_len = ARRAY_SIZE(base_required_struct_types);
@@ -2192,16 +2416,17 @@ static void test_acpi_microvm_oem_fields(void)
g_free(args);
}
-static void test_acpi_virt_oem_fields(void)
+static void test_acpi_aarch64_virt_oem_fields(void)
{
test_data data = {
.machine = "virt",
+ .arch = "aarch64",
.tcg_only = true,
.uefi_fl1 = "pc-bios/edk2-aarch64-code.fd",
.uefi_fl2 = "pc-bios/edk2-arm-vars.fd",
.cd = "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2",
.ram_start = 0x40000000ULL,
- .scan_len = 128ULL * 1024 * 1024,
+ .scan_len = 128ULL * MiB,
};
char *args;
@@ -2304,6 +2529,8 @@ int main(int argc, char *argv[])
qtest_add_func("acpi/q35/nohpet", test_acpi_q35_tcg_nohpet);
qtest_add_func("acpi/q35/acpihmat-noinitiator",
test_acpi_q35_tcg_acpi_hmat_noinitiator);
+ qtest_add_func("acpi/q35/acpihmat-genericx",
+ test_acpi_q35_tcg_acpi_hmat_generic_x);
/* i386 does not support memory hotplug */
if (strcmp(arch, "i386")) {
@@ -2364,18 +2591,29 @@ int main(int argc, char *argv[])
}
} else if (strcmp(arch, "aarch64") == 0) {
if (has_tcg && qtest_has_device("virtio-blk-pci")) {
- qtest_add_func("acpi/virt", test_acpi_virt_tcg);
+ qtest_add_func("acpi/virt", test_acpi_aarch64_virt_tcg);
qtest_add_func("acpi/virt/acpihmatvirt",
- test_acpi_virt_tcg_acpi_hmat);
- qtest_add_func("acpi/virt/topology", test_acpi_virt_tcg_topology);
- qtest_add_func("acpi/virt/numamem", test_acpi_virt_tcg_numamem);
- qtest_add_func("acpi/virt/memhp", test_acpi_virt_tcg_memhp);
- qtest_add_func("acpi/virt/pxb", test_acpi_virt_tcg_pxb);
- qtest_add_func("acpi/virt/oem-fields", test_acpi_virt_oem_fields);
+ test_acpi_aarch64_virt_tcg_acpi_hmat);
+ qtest_add_func("acpi/virt/topology",
+ test_acpi_aarch64_virt_tcg_topology);
+ qtest_add_func("acpi/virt/its_off",
+ test_acpi_aarch64_virt_tcg_its_off);
+ qtest_add_func("acpi/virt/numamem",
+ test_acpi_aarch64_virt_tcg_numamem);
+ qtest_add_func("acpi/virt/memhp", test_acpi_aarch64_virt_tcg_memhp);
+ qtest_add_func("acpi/virt/pxb", test_acpi_aarch64_virt_tcg_pxb);
+ qtest_add_func("acpi/virt/oem-fields",
+ test_acpi_aarch64_virt_oem_fields);
if (qtest_has_device("virtio-iommu-pci")) {
- qtest_add_func("acpi/virt/viot", test_acpi_virt_viot);
+ qtest_add_func("acpi/virt/viot", test_acpi_aarch64_virt_viot);
}
}
+ } else if (strcmp(arch, "riscv64") == 0) {
+ if (has_tcg && qtest_has_device("virtio-blk-pci")) {
+ qtest_add_func("acpi/virt", test_acpi_riscv64_virt_tcg);
+ qtest_add_func("acpi/virt/numamem",
+ test_acpi_riscv64_virt_tcg_numamem);
+ }
}
ret = g_test_run();
boot_sector_cleanup(disk);
diff --git a/tests/qtest/boot-order-test.c b/tests/qtest/boot-order-test.c
index 8f2b6ef..74d6b82 100644
--- a/tests/qtest/boot-order-test.c
+++ b/tests/qtest/boot-order-test.c
@@ -13,7 +13,7 @@
#include "qemu/osdep.h"
#include "libqos/fw_cfg.h"
#include "libqtest.h"
-#include "qapi/qmp/qdict.h"
+#include "qobject/qdict.h"
#include "standard-headers/linux/qemu_fw_cfg.h"
typedef struct {
@@ -31,7 +31,7 @@ static void test_a_boot_order(const char *machine,
uint64_t actual;
QTestState *qts;
- if (machine && !qtest_has_machine(machine)) {
+ if (!qtest_has_machine(machine)) {
g_test_skip("Machine is not available");
return;
}
@@ -40,12 +40,7 @@ static void test_a_boot_order(const char *machine,
machine ?: "", test_args);
actual = read_boot_order(qts);
g_assert_cmphex(actual, ==, expected_boot);
- qtest_qmp_assert_success(qts, "{ 'execute': 'system_reset' }");
- /*
- * system_reset only requests reset. We get a RESET event after
- * the actual reset completes. Need to wait for that.
- */
- qtest_qmp_eventwait(qts, "RESET");
+ qtest_system_reset(qts);
actual = read_boot_order(qts);
g_assert_cmphex(actual, ==, expected_reboot);
qtest_quit(qts);
@@ -107,7 +102,7 @@ static const boot_order_test test_cases_pc[] = {
static void test_pc_boot_order(void)
{
- test_boot_orders(NULL, read_boot_order_pc, test_cases_pc);
+ test_boot_orders("pc", read_boot_order_pc, test_cases_pc);
}
static uint64_t read_boot_order_pmac(QTestState *qts)
diff --git a/tests/qtest/boot-serial-test.c b/tests/qtest/boot-serial-test.c
index 3b92fa5..a05d26e 100644
--- a/tests/qtest/boot-serial-test.c
+++ b/tests/qtest/boot-serial-test.c
@@ -70,18 +70,23 @@ static const uint8_t kernel_plml605[] = {
};
static const uint8_t bios_raspi2[] = {
- 0x08, 0x30, 0x9f, 0xe5, /* ldr r3,[pc,#8] Get base */
- 0x54, 0x20, 0xa0, 0xe3, /* mov r2,#'T' */
- 0x00, 0x20, 0xc3, 0xe5, /* strb r2,[r3] */
- 0xfb, 0xff, 0xff, 0xea, /* b loop */
- 0x00, 0x10, 0x20, 0x3f, /* 0x3f201000 = UART0 base addr */
+ 0x10, 0x30, 0x9f, 0xe5, /* ldr r3, [pc, #16] Get &UART0 */
+ 0x10, 0x20, 0x9f, 0xe5, /* ldr r2, [pc, #16] Get &CR */
+ 0xb0, 0x23, 0xc3, 0xe1, /* strh r2, [r3, #48] Set CR */
+ 0x54, 0x20, 0xa0, 0xe3, /* mov r2, #'T' */
+ 0x00, 0x20, 0xc3, 0xe5, /* loop: strb r2, [r3] *TXDAT = 'T' */
+ 0xff, 0xff, 0xff, 0xea, /* b -4 (loop) */
+ 0x00, 0x10, 0x20, 0x3f, /* UART0: 0x3f201000 */
+ 0x01, 0x01, 0x00, 0x00, /* CR: 0x101 = UARTEN|TXE */
};
static const uint8_t kernel_aarch64[] = {
- 0x81, 0x0a, 0x80, 0x52, /* mov w1, #0x54 */
- 0x02, 0x20, 0xa1, 0xd2, /* mov x2, #0x9000000 */
- 0x41, 0x00, 0x00, 0x39, /* strb w1, [x2] */
- 0xfd, 0xff, 0xff, 0x17, /* b -12 (loop) */
+ 0x02, 0x20, 0xa1, 0xd2, /* mov x2, #0x9000000 Load UART0 */
+ 0x21, 0x20, 0x80, 0x52, /* mov w1, 0x101 CR = UARTEN|TXE */
+ 0x41, 0x60, 0x00, 0x79, /* strh w1, [x2, #48] Set CR */
+ 0x81, 0x0a, 0x80, 0x52, /* mov w1, #'T' */
+ 0x41, 0x00, 0x00, 0x39, /* loop: strb w1, [x2] *TXDAT = 'T' */
+ 0xff, 0xff, 0xff, 0x17, /* b -4 (loop) */
};
static const uint8_t kernel_nrf51[] = {
@@ -184,8 +189,6 @@ static const testdef_t tests[] = {
{ "microblazeel", "petalogix-ml605", "", "TT",
sizeof(kernel_plml605), kernel_plml605 },
{ "arm", "raspi2b", "", "TT", sizeof(bios_raspi2), 0, bios_raspi2 },
- /* For hppa, force bios to output to serial by disabling graphics. */
- { "hppa", "hppa", "-vga none", "SeaBIOS wants SYSTEM HALT" },
{ "aarch64", "virt", "-cpu max", "TT", sizeof(kernel_aarch64),
kernel_aarch64 },
{ "arm", "microbit", "", "T", sizeof(kernel_nrf51), kernel_nrf51 },
diff --git a/tests/qtest/cdrom-test.c b/tests/qtest/cdrom-test.c
index 5d89e62..56e2d28 100644
--- a/tests/qtest/cdrom-test.c
+++ b/tests/qtest/cdrom-test.c
@@ -13,7 +13,7 @@
#include "qemu/osdep.h"
#include "libqtest.h"
#include "boot-sector.h"
-#include "qapi/qmp/qdict.h"
+#include "qobject/qdict.h"
static char isoimage[] = "cdrom-boot-iso-XXXXXX";
@@ -135,13 +135,35 @@ static void add_x86_tests(void)
return;
}
- qtest_add_data_func("cdrom/boot/default", "-cdrom ", test_cdboot);
- if (qtest_has_device("virtio-scsi-ccw")) {
- qtest_add_data_func("cdrom/boot/virtio-scsi",
- "-device virtio-scsi -device scsi-cd,drive=cdr "
- "-blockdev file,node-name=cdr,filename=",
- test_cdboot);
+ if (qtest_has_machine("pc")) {
+ qtest_add_data_func("cdrom/boot/default", "-cdrom ", test_cdboot);
+ if (qtest_has_device("virtio-scsi-ccw")) {
+ qtest_add_data_func("cdrom/boot/virtio-scsi",
+ "-device virtio-scsi -device scsi-cd,drive=cdr "
+ "-blockdev file,node-name=cdr,filename=",
+ test_cdboot);
+ }
+
+ if (qtest_has_device("am53c974")) {
+ qtest_add_data_func("cdrom/boot/am53c974",
+ "-device am53c974 -device scsi-cd,drive=cd1 "
+ "-drive if=none,id=cd1,format=raw,file=",
+ test_cdboot);
+ }
+ if (qtest_has_device("dc390")) {
+ qtest_add_data_func("cdrom/boot/dc390",
+ "-device dc390 -device scsi-cd,drive=cd1 "
+ "-blockdev file,node-name=cd1,filename=",
+ test_cdboot);
+ }
+ if (qtest_has_device("lsi53c895a")) {
+ qtest_add_data_func("cdrom/boot/lsi53c895a",
+ "-device lsi53c895a -device scsi-cd,drive=cd1 "
+ "-blockdev file,node-name=cd1,filename=",
+ test_cdboot);
+ }
}
+
/*
* Unstable CI test under load
* See https://lists.gnu.org/archive/html/qemu-devel/2019-02/msg05509.html
@@ -150,35 +172,20 @@ static void add_x86_tests(void)
qtest_add_data_func("cdrom/boot/isapc", "-M isapc "
"-drive if=ide,media=cdrom,file=", test_cdboot);
}
- if (qtest_has_device("am53c974")) {
- qtest_add_data_func("cdrom/boot/am53c974",
- "-device am53c974 -device scsi-cd,drive=cd1 "
- "-drive if=none,id=cd1,format=raw,file=",
- test_cdboot);
- }
- if (qtest_has_device("dc390")) {
- qtest_add_data_func("cdrom/boot/dc390",
- "-device dc390 -device scsi-cd,drive=cd1 "
- "-blockdev file,node-name=cd1,filename=",
- test_cdboot);
- }
- if (qtest_has_device("lsi53c895a")) {
- qtest_add_data_func("cdrom/boot/lsi53c895a",
- "-device lsi53c895a -device scsi-cd,drive=cd1 "
- "-blockdev file,node-name=cd1,filename=",
- test_cdboot);
- }
- if (qtest_has_device("megasas")) {
- qtest_add_data_func("cdrom/boot/megasas", "-M q35 "
- "-device megasas -device scsi-cd,drive=cd1 "
- "-blockdev file,node-name=cd1,filename=",
- test_cdboot);
- }
- if (qtest_has_device("megasas-gen2")) {
- qtest_add_data_func("cdrom/boot/megasas-gen2", "-M q35 "
- "-device megasas-gen2 -device scsi-cd,drive=cd1 "
- "-blockdev file,node-name=cd1,filename=",
- test_cdboot);
+
+ if (qtest_has_machine("q35")) {
+ if (qtest_has_device("megasas")) {
+ qtest_add_data_func("cdrom/boot/megasas", "-M q35 "
+ "-device megasas -device scsi-cd,drive=cd1 "
+ "-blockdev file,node-name=cd1,filename=",
+ test_cdboot);
+ }
+ if (qtest_has_device("megasas-gen2")) {
+ qtest_add_data_func("cdrom/boot/megasas-gen2", "-M q35 "
+ "-device megasas-gen2 -device scsi-cd,drive=cd1 "
+ "-blockdev file,node-name=cd1,filename=",
+ test_cdboot);
+ }
}
}
@@ -206,6 +213,30 @@ static void add_s390x_tests(void)
"-drive driver=null-co,read-zeroes=on,if=none,id=d1 "
"-device virtio-blk,drive=d2,bootindex=1 "
"-drive if=none,id=d2,media=cdrom,file=", test_cdboot);
+ qtest_add_data_func("cdrom/boot/as-fallback-device",
+ "-device virtio-serial -device virtio-scsi "
+ "-device virtio-blk,drive=d1,bootindex=1 "
+ "-drive driver=null-co,read-zeroes=on,if=none,id=d1 "
+ "-device virtio-blk,drive=d2,bootindex=2 "
+ "-drive if=none,id=d2,media=cdrom,file=", test_cdboot);
+ qtest_add_data_func("cdrom/boot/as-last-option",
+ "-device virtio-serial -device virtio-scsi "
+ "-device virtio-blk,drive=d1,bootindex=1 "
+ "-drive driver=null-co,read-zeroes=on,if=none,id=d1 "
+ "-device virtio-blk,drive=d2,bootindex=2 "
+ "-drive driver=null-co,read-zeroes=on,if=none,id=d2 "
+ "-device virtio-blk,drive=d3,bootindex=3 "
+ "-drive driver=null-co,read-zeroes=on,if=none,id=d3 "
+ "-device scsi-hd,drive=d4,bootindex=4 "
+ "-drive driver=null-co,read-zeroes=on,if=none,id=d4 "
+ "-device scsi-hd,drive=d5,bootindex=5 "
+ "-drive driver=null-co,read-zeroes=on,if=none,id=d5 "
+ "-device virtio-blk,drive=d6,bootindex=6 "
+ "-drive driver=null-co,read-zeroes=on,if=none,id=d6 "
+ "-device scsi-hd,drive=d7,bootindex=7 "
+ "-drive driver=null-co,read-zeroes=on,if=none,id=d7 "
+ "-device scsi-cd,drive=d8,bootindex=8 "
+ "-drive if=none,id=d8,media=cdrom,file=", test_cdboot);
if (qtest_has_device("x-terminal3270")) {
qtest_add_data_func("cdrom/boot/without-bootindex",
"-device virtio-scsi -device virtio-serial "
diff --git a/tests/qtest/cmsdk-apb-watchdog-test.c b/tests/qtest/cmsdk-apb-watchdog-test.c
index 00b5dbb..cd0c602 100644
--- a/tests/qtest/cmsdk-apb-watchdog-test.c
+++ b/tests/qtest/cmsdk-apb-watchdog-test.c
@@ -15,14 +15,12 @@
*/
#include "qemu/osdep.h"
+#include "exec/hwaddr.h"
#include "qemu/bitops.h"
#include "libqtest-single.h"
-/*
- * lm3s811evb watchdog; at board startup this runs at 200MHz / 16 == 12.5MHz,
- * which is 80ns per tick.
- */
#define WDOG_BASE 0x40000000
+#define WDOG_BASE_MPS2 0x40008000
#define WDOGLOAD 0
#define WDOGVALUE 4
@@ -37,39 +35,97 @@
#define SYSDIV_SHIFT 23
#define SYSDIV_LENGTH 4
-static void test_watchdog(void)
+#define WDOGLOAD_DEFAULT 0xFFFFFFFF
+#define WDOGVALUE_DEFAULT 0xFFFFFFFF
+
+typedef struct CMSDKAPBWatchdogTestArgs {
+ int64_t tick;
+ hwaddr wdog_base;
+ const char *machine;
+} CMSDKAPBWatchdogTestArgs;
+
+enum {
+ MACHINE_LM3S811EVB,
+ MACHINE_MPS2_AN385,
+};
+
+/*
+ * lm3s811evb watchdog; at board startup this runs at 200MHz / 16 == 12.5MHz,
+ * which is 80ns per tick.
+ *
+ * IoTKit/ARMSSE dualtimer; driven at 25MHz in mps2-an385, so 40ns per tick
+ */
+static const CMSDKAPBWatchdogTestArgs machine_info[] = {
+ [MACHINE_LM3S811EVB] = {
+ .tick = 80,
+ .wdog_base = WDOG_BASE,
+ .machine = "lm3s811evb",
+ },
+ [MACHINE_MPS2_AN385] = {
+ .tick = 40,
+ .wdog_base = WDOG_BASE_MPS2,
+ .machine = "mps2-an385",
+ },
+};
+
+static void system_reset(QTestState *qtest)
{
- g_assert_cmpuint(readl(WDOG_BASE + WDOGRIS), ==, 0);
+ QDict *resp;
- writel(WDOG_BASE + WDOGCONTROL, 1);
- writel(WDOG_BASE + WDOGLOAD, 1000);
+ resp = qtest_qmp(qtest, "{'execute': 'system_reset'}");
+ g_assert(qdict_haskey(resp, "return"));
+ qobject_unref(resp);
+ qtest_qmp_eventwait(qtest, "RESET");
+}
+
+static void test_watchdog(const void *ptr)
+{
+ const CMSDKAPBWatchdogTestArgs *args = ptr;
+ hwaddr wdog_base = args->wdog_base;
+ int64_t tick = args->tick;
+ g_autofree gchar *cmdline = g_strdup_printf("-machine %s", args->machine);
+ qtest_start(cmdline);
+
+ g_assert_cmpuint(readl(wdog_base + WDOGRIS), ==, 0);
+
+ writel(wdog_base + WDOGCONTROL, 1);
+ writel(wdog_base + WDOGLOAD, 1000);
/* Step to just past the 500th tick */
- clock_step(500 * 80 + 1);
- g_assert_cmpuint(readl(WDOG_BASE + WDOGRIS), ==, 0);
- g_assert_cmpuint(readl(WDOG_BASE + WDOGVALUE), ==, 500);
+ clock_step(500 * tick + 1);
+ g_assert_cmpuint(readl(wdog_base + WDOGRIS), ==, 0);
+ g_assert_cmpuint(readl(wdog_base + WDOGVALUE), ==, 500);
/* Just past the 1000th tick: timer should have fired */
- clock_step(500 * 80);
- g_assert_cmpuint(readl(WDOG_BASE + WDOGRIS), ==, 1);
- g_assert_cmpuint(readl(WDOG_BASE + WDOGVALUE), ==, 0);
+ clock_step(500 * tick);
+ g_assert_cmpuint(readl(wdog_base + WDOGRIS), ==, 1);
+ g_assert_cmpuint(readl(wdog_base + WDOGVALUE), ==, 0);
/* VALUE reloads at following tick */
- clock_step(80);
- g_assert_cmpuint(readl(WDOG_BASE + WDOGVALUE), ==, 1000);
+ clock_step(tick);
+ g_assert_cmpuint(readl(wdog_base + WDOGVALUE), ==, 1000);
/* Writing any value to WDOGINTCLR clears the interrupt and reloads */
- clock_step(500 * 80);
- g_assert_cmpuint(readl(WDOG_BASE + WDOGVALUE), ==, 500);
- g_assert_cmpuint(readl(WDOG_BASE + WDOGRIS), ==, 1);
- writel(WDOG_BASE + WDOGINTCLR, 0);
- g_assert_cmpuint(readl(WDOG_BASE + WDOGVALUE), ==, 1000);
- g_assert_cmpuint(readl(WDOG_BASE + WDOGRIS), ==, 0);
+ clock_step(500 * tick);
+ g_assert_cmpuint(readl(wdog_base + WDOGVALUE), ==, 500);
+ g_assert_cmpuint(readl(wdog_base + WDOGRIS), ==, 1);
+ writel(wdog_base + WDOGINTCLR, 0);
+ g_assert_cmpuint(readl(wdog_base + WDOGVALUE), ==, 1000);
+ g_assert_cmpuint(readl(wdog_base + WDOGRIS), ==, 0);
+
+ qtest_end();
}
-static void test_clock_change(void)
+/*
+ * This test can only be executed in the stellaris board since it relies on a
+ * component of the board to change the clocking parameters of the watchdog.
+ */
+static void test_clock_change(const void *ptr)
{
uint32_t rcc;
+ const CMSDKAPBWatchdogTestArgs *args = ptr;
+ g_autofree gchar *cmdline = g_strdup_printf("-machine %s", args->machine);
+ qtest_start(cmdline);
/*
* Test that writing to the stellaris board's RCC register to
@@ -109,23 +165,231 @@ static void test_clock_change(void)
writel(WDOG_BASE + WDOGINTCLR, 0);
g_assert_cmpuint(readl(WDOG_BASE + WDOGVALUE), ==, 1000);
g_assert_cmpuint(readl(WDOG_BASE + WDOGRIS), ==, 0);
+
+ qtest_end();
}
-int main(int argc, char **argv)
+/* Tests the counter is not running after reset. */
+static void test_watchdog_reset(const void *ptr)
{
- int r;
+ const CMSDKAPBWatchdogTestArgs *args = ptr;
+ hwaddr wdog_base = args->wdog_base;
+ int64_t tick = args->tick;
+ g_autofree gchar *cmdline = g_strdup_printf("-machine %s", args->machine);
+ qtest_start(cmdline);
+ g_assert_cmpuint(readl(wdog_base + WDOGRIS), ==, 0);
- g_test_init(&argc, &argv, NULL);
+ g_assert_cmphex(readl(wdog_base + WDOGLOAD), ==, WDOGLOAD_DEFAULT);
+ g_assert_cmphex(readl(wdog_base + WDOGVALUE), ==, WDOGVALUE_DEFAULT);
+
+ g_assert_cmphex(readl(wdog_base + WDOGCONTROL), ==, 0);
+
+ /*
+ * The counter should not be running if WDOGCONTROL.INTEN has not been set,
+ * as it is the case after a cold reset.
+ */
+ clock_step(15 * tick + 1);
+ g_assert_cmphex(readl(wdog_base + WDOGLOAD), ==, WDOGLOAD_DEFAULT);
+ g_assert_cmphex(readl(wdog_base + WDOGVALUE), ==, WDOGVALUE_DEFAULT);
+
+ /* Let the counter run before reset */
+ writel(wdog_base + WDOGLOAD, 3000);
+ writel(wdog_base + WDOGCONTROL, 1);
+
+ /* Verify it is running */
+ clock_step(1000 * tick + 1);
+ g_assert_cmpuint(readl(wdog_base + WDOGLOAD), ==, 3000);
+ g_assert_cmpuint(readl(wdog_base + WDOGVALUE), ==, 2000);
+
+ system_reset(global_qtest);
- qtest_start("-machine lm3s811evb");
+ /* Check defaults after reset */
+ g_assert_cmphex(readl(wdog_base + WDOGLOAD), ==, WDOGLOAD_DEFAULT);
+ g_assert_cmphex(readl(wdog_base + WDOGVALUE), ==, WDOGVALUE_DEFAULT);
- qtest_add_func("/cmsdk-apb-watchdog/watchdog", test_watchdog);
- qtest_add_func("/cmsdk-apb-watchdog/watchdog_clock_change",
- test_clock_change);
+ /* The counter should not be running after reset. */
+ clock_step(1000 * tick + 1);
+ g_assert_cmphex(readl(wdog_base + WDOGLOAD), ==, WDOGLOAD_DEFAULT);
+ g_assert_cmphex(readl(wdog_base + WDOGVALUE), ==, WDOGVALUE_DEFAULT);
- r = g_test_run();
+ qtest_end();
+}
+
+/*
+ * Tests inten works as the counter enable based on this description:
+ *
+ * Enable the interrupt event, WDOGINT. Set HIGH to enable the counter and the
+ * interrupt, or LOW to disable the counter and interrupt. Reloads the counter
+ * from the value in WDOGLOAD when the interrupt is enabled, after previously
+ * being disabled.
+ */
+static void test_watchdog_inten(const void *ptr)
+{
+ const CMSDKAPBWatchdogTestArgs *args = ptr;
+ hwaddr wdog_base = args->wdog_base;
+ int64_t tick = args->tick;
+ g_autofree gchar *cmdline = g_strdup_printf("-machine %s", args->machine);
+ qtest_start(cmdline);
+ g_assert_cmpuint(readl(wdog_base + WDOGRIS), ==, 0);
+
+ g_assert_cmphex(readl(wdog_base + WDOGLOAD), ==, WDOGLOAD_DEFAULT);
+ g_assert_cmphex(readl(wdog_base + WDOGVALUE), ==, WDOGVALUE_DEFAULT);
+
+ /*
+ * When WDOGLOAD is written to, the count is immediately restarted from the
+ * new value.
+ *
+ * Note: the counter should not be running as long as WDOGCONTROL.INTEN is
+ * not set
+ */
+ writel(wdog_base + WDOGLOAD, 4000);
+ g_assert_cmpuint(readl(wdog_base + WDOGLOAD), ==, 4000);
+ g_assert_cmpuint(readl(wdog_base + WDOGVALUE), ==, 4000);
+ clock_step(500 * tick + 1);
+ g_assert_cmpuint(readl(wdog_base + WDOGLOAD), ==, 4000);
+ g_assert_cmpuint(readl(wdog_base + WDOGVALUE), ==, 4000);
+
+ /* Set HIGH WDOGCONTROL.INTEN to enable the counter and the interrupt */
+ writel(wdog_base + WDOGCONTROL, 1);
+ clock_step(500 * tick + 1);
+ g_assert_cmpuint(readl(wdog_base + WDOGLOAD), ==, 4000);
+ g_assert_cmpuint(readl(wdog_base + WDOGVALUE), ==, 3500);
+
+ /* or LOW to disable the counter and interrupt. */
+ writel(wdog_base + WDOGCONTROL, 0);
+ clock_step(100 * tick);
+ g_assert_cmpuint(readl(wdog_base + WDOGLOAD), ==, 4000);
+ g_assert_cmpuint(readl(wdog_base + WDOGVALUE), ==, 3500);
+
+ /*
+ * Reloads the counter from the value in WDOGLOAD when the interrupt is
+ * enabled, after previously being disabled.
+ */
+ writel(wdog_base + WDOGCONTROL, 1);
+ g_assert_cmpuint(readl(wdog_base + WDOGLOAD), ==, 4000);
+ g_assert_cmpuint(readl(wdog_base + WDOGVALUE), ==, 4000);
+
+ /* Test counter is still on */
+ clock_step(50 * tick + 1);
+ g_assert_cmpuint(readl(wdog_base + WDOGLOAD), ==, 4000);
+ g_assert_cmpuint(readl(wdog_base + WDOGVALUE), ==, 3950);
+
+ /*
+ * When WDOGLOAD is written to, the count is immediately restarted from the
+ * new value.
+ *
+ * Note: the counter should be running since WDOGCONTROL.INTEN is set
+ */
+ writel(wdog_base + WDOGLOAD, 5000);
+ g_assert_cmpuint(readl(wdog_base + WDOGLOAD), ==, 5000);
+ g_assert_cmpuint(readl(wdog_base + WDOGVALUE), ==, 5000);
+ clock_step(4999 * tick + 1);
+ g_assert_cmpuint(readl(wdog_base + WDOGLOAD), ==, 5000);
+ g_assert_cmpuint(readl(wdog_base + WDOGVALUE), ==, 1);
+ g_assert_cmpuint(readl(wdog_base + WDOGRIS), ==, 0);
+
+ /* Finally disable and check the conditions don't change */
+ writel(wdog_base + WDOGCONTROL, 0);
+ clock_step(10 * tick);
+ g_assert_cmpuint(readl(wdog_base + WDOGLOAD), ==, 5000);
+ g_assert_cmpuint(readl(wdog_base + WDOGVALUE), ==, 1);
+ g_assert_cmpuint(readl(wdog_base + WDOGRIS), ==, 0);
+
+ qtest_end();
+}
+
+/*
+ * Tests the following custom behavior:
+ *
+ * The Luminary version of this device ignores writes to this register after the
+ * guest has enabled interrupts (so they can only be disabled again via reset).
+ */
+static void test_watchdog_inten_luminary(const void *ptr)
+{
+ const CMSDKAPBWatchdogTestArgs *args = ptr;
+ hwaddr wdog_base = args->wdog_base;
+ int64_t tick = args->tick;
+ g_autofree gchar *cmdline = g_strdup_printf("-machine %s", args->machine);
+ qtest_start(cmdline);
+ g_assert_cmpuint(readl(wdog_base + WDOGRIS), ==, 0);
+
+ g_assert_cmphex(readl(wdog_base + WDOGLOAD), ==, WDOGLOAD_DEFAULT);
+ g_assert_cmphex(readl(wdog_base + WDOGVALUE), ==, WDOGVALUE_DEFAULT);
+
+ /*
+ * When WDOGLOAD is written to, the count is immediately restarted from the
+ * new value.
+ *
+ * Note: the counter should not be running as long as WDOGCONTROL.INTEN is
+ * not set
+ */
+ writel(wdog_base + WDOGLOAD, 4000);
+ g_assert_cmpuint(readl(wdog_base + WDOGLOAD), ==, 4000);
+ g_assert_cmpuint(readl(wdog_base + WDOGVALUE), ==, 4000);
+ clock_step(500 * tick + 1);
+ g_assert_cmpuint(readl(wdog_base + WDOGLOAD), ==, 4000);
+ g_assert_cmpuint(readl(wdog_base + WDOGVALUE), ==, 4000);
+
+ /* Set HIGH WDOGCONTROL.INTEN to enable the counter and the interrupt */
+ writel(wdog_base + WDOGCONTROL, 1);
+ clock_step(500 * tick + 1);
+ g_assert_cmpuint(readl(wdog_base + WDOGLOAD), ==, 4000);
+ g_assert_cmpuint(readl(wdog_base + WDOGVALUE), ==, 3500);
+
+ /*
+ * The Luminary version of this device ignores writes to this register after
+ * the guest has enabled interrupts
+ */
+ writel(wdog_base + WDOGCONTROL, 0);
+ clock_step(100 * tick);
+ g_assert_cmpuint(readl(wdog_base + WDOGLOAD), ==, 4000);
+ g_assert_cmpuint(readl(wdog_base + WDOGVALUE), ==, 3400);
+ g_assert_cmphex(readl(wdog_base + WDOGCONTROL), ==, 0x1);
+
+ /* They can only be disabled again via reset */
+ system_reset(global_qtest);
+
+ /* Check defaults after reset */
+ g_assert_cmphex(readl(wdog_base + WDOGLOAD), ==, WDOGLOAD_DEFAULT);
+ g_assert_cmphex(readl(wdog_base + WDOGVALUE), ==, WDOGVALUE_DEFAULT);
+ g_assert_cmphex(readl(wdog_base + WDOGCONTROL), ==, 0);
+
+ /* The counter should not be running after reset. */
+ clock_step(1000 * tick + 1);
+ g_assert_cmphex(readl(wdog_base + WDOGLOAD), ==, WDOGLOAD_DEFAULT);
+ g_assert_cmphex(readl(wdog_base + WDOGVALUE), ==, WDOGVALUE_DEFAULT);
qtest_end();
+}
+
+int main(int argc, char **argv)
+{
+ g_test_init(&argc, &argv, NULL);
+ g_test_set_nonfatal_assertions();
+
+ if (qtest_has_machine(machine_info[MACHINE_LM3S811EVB].machine)) {
+ qtest_add_data_func("/cmsdk-apb-watchdog/watchdog",
+ &machine_info[MACHINE_LM3S811EVB], test_watchdog);
+ qtest_add_data_func("/cmsdk-apb-watchdog/watchdog_clock_change",
+ &machine_info[MACHINE_LM3S811EVB],
+ test_clock_change);
+ qtest_add_data_func("/cmsdk-apb-watchdog/watchdog_reset",
+ &machine_info[MACHINE_LM3S811EVB],
+ test_watchdog_reset);
+ qtest_add_data_func("/cmsdk-apb-watchdog/watchdog_inten_luminary",
+ &machine_info[MACHINE_LM3S811EVB],
+ test_watchdog_inten_luminary);
+ }
+ if (qtest_has_machine(machine_info[MACHINE_MPS2_AN385].machine)) {
+ qtest_add_data_func("/cmsdk-apb-watchdog/watchdog_mps2",
+ &machine_info[MACHINE_MPS2_AN385], test_watchdog);
+ qtest_add_data_func("/cmsdk-apb-watchdog/watchdog_reset_mps2",
+ &machine_info[MACHINE_MPS2_AN385],
+ test_watchdog_reset);
+ qtest_add_data_func("/cmsdk-apb-watchdog/watchdog_inten",
+ &machine_info[MACHINE_MPS2_AN385],
+ test_watchdog_inten);
+ }
- return r;
+ return g_test_run();
}
diff --git a/tests/qtest/cpu-plug-test.c b/tests/qtest/cpu-plug-test.c
index 7f5dd5f..44d7046 100644
--- a/tests/qtest/cpu-plug-test.c
+++ b/tests/qtest/cpu-plug-test.c
@@ -10,8 +10,8 @@
#include "qemu/osdep.h"
#include "libqtest-single.h"
-#include "qapi/qmp/qdict.h"
-#include "qapi/qmp/qlist.h"
+#include "qobject/qdict.h"
+#include "qobject/qlist.h"
struct PlugTestData {
char *machine;
@@ -156,6 +156,28 @@ static void add_s390x_test_case(const char *mname)
g_free(path);
}
+static void add_loongarch_test_case(const char *mname)
+{
+ char *path;
+ PlugTestData *data;
+
+ data = g_new(PlugTestData, 1);
+ data->machine = g_strdup(mname);
+ data->cpu_model = "la464";
+ data->device_model = g_strdup("la464-loongarch-cpu");
+ data->sockets = 1;
+ data->cores = 3;
+ data->threads = 1;
+ data->maxcpus = data->sockets * data->cores * data->threads;
+
+ path = g_strdup_printf("cpu-plug/%s/device-add/%ux%ux%u&maxcpus=%u",
+ mname, data->sockets, data->cores,
+ data->threads, data->maxcpus);
+ qtest_add_data_func_full(path, data, test_plug_with_device_add,
+ test_data_free);
+ g_free(path);
+}
+
int main(int argc, char **argv)
{
const char *arch = qtest_get_arch();
@@ -168,6 +190,8 @@ int main(int argc, char **argv)
qtest_cb_for_every_machine(add_pseries_test_case, g_test_quick());
} else if (g_str_equal(arch, "s390x")) {
qtest_cb_for_every_machine(add_s390x_test_case, g_test_quick());
+ } else if (g_str_equal(arch, "loongarch64")) {
+ add_loongarch_test_case("virt");
}
return g_test_run();
diff --git a/tests/qtest/dbus-display-test.c b/tests/qtest/dbus-display-test.c
index 0390bdc..f7fc873 100644
--- a/tests/qtest/dbus-display-test.c
+++ b/tests/qtest/dbus-display-test.c
@@ -2,9 +2,14 @@
#include "qemu/sockets.h"
#include "qemu/dbus.h"
#include "qemu/sockets.h"
+#include "glib.h"
+#include "glibconfig.h"
#include <gio/gio.h>
#include <gio/gunixfdlist.h>
#include "libqtest.h"
+#ifndef WIN32
+#include <sys/mman.h>
+#endif
#include "ui/dbus-display1.h"
static GDBusConnection*
@@ -82,6 +87,7 @@ typedef struct TestDBusConsoleRegister {
GThread *thread;
GDBusConnection *listener_conn;
GDBusObjectManagerServer *server;
+ bool with_map;
} TestDBusConsoleRegister;
static gboolean listener_handle_scanout(
@@ -94,13 +100,49 @@ static gboolean listener_handle_scanout(
GVariant *arg_data,
TestDBusConsoleRegister *test)
{
+ if (!test->with_map) {
+ g_main_loop_quit(test->loop);
+ }
+
+ return DBUS_METHOD_INVOCATION_HANDLED;
+}
+
+#ifndef WIN32
+static gboolean listener_handle_scanout_map(
+ QemuDBusDisplay1ListenerUnixMap *object,
+ GDBusMethodInvocation *invocation,
+ GUnixFDList *fd_list,
+ GVariant *arg_handle,
+ guint arg_offset,
+ guint arg_width,
+ guint arg_height,
+ guint arg_stride,
+ guint arg_pixman_format,
+ TestDBusConsoleRegister *test)
+{
+ int fd = -1;
+ gint32 handle = g_variant_get_handle(arg_handle);
+ g_autoptr(GError) error = NULL;
+ void *addr = NULL;
+ size_t len = arg_height * arg_stride;
+
+ g_assert_cmpuint(g_unix_fd_list_get_length(fd_list), ==, 1);
+ fd = g_unix_fd_list_get(fd_list, handle, &error);
+ g_assert_no_error(error);
+
+ addr = mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, arg_offset);
+ g_assert_no_errno(addr == MAP_FAILED ? -1 : 0);
+ g_assert_no_errno(munmap(addr, len));
+
g_main_loop_quit(test->loop);
+ close(fd);
return DBUS_METHOD_INVOCATION_HANDLED;
}
+#endif
static void
-test_dbus_console_setup_listener(TestDBusConsoleRegister *test)
+test_dbus_console_setup_listener(TestDBusConsoleRegister *test, bool with_map)
{
g_autoptr(GDBusObjectSkeleton) listener = NULL;
g_autoptr(QemuDBusDisplay1ListenerSkeleton) iface = NULL;
@@ -114,6 +156,25 @@ test_dbus_console_setup_listener(TestDBusConsoleRegister *test)
NULL);
g_dbus_object_skeleton_add_interface(listener,
G_DBUS_INTERFACE_SKELETON(iface));
+ if (with_map) {
+#ifdef WIN32
+ g_test_skip("map test lacking on win32");
+ return;
+#else
+ g_autoptr(QemuDBusDisplay1ListenerUnixMapSkeleton) iface_map =
+ QEMU_DBUS_DISPLAY1_LISTENER_UNIX_MAP_SKELETON(
+ qemu_dbus_display1_listener_unix_map_skeleton_new());
+
+ g_object_connect(iface_map,
+ "signal::handle-scanout-map", listener_handle_scanout_map, test,
+ NULL);
+ g_dbus_object_skeleton_add_interface(listener,
+ G_DBUS_INTERFACE_SKELETON(iface_map));
+ g_object_set(iface, "interfaces",
+ (const gchar *[]) { "org.qemu.Display1.Listener.Unix.Map", NULL },
+ NULL);
+#endif
+ }
g_dbus_object_manager_server_export(test->server, listener);
g_dbus_object_manager_server_set_connection(test->server,
test->listener_conn);
@@ -145,7 +206,7 @@ test_dbus_console_registered(GObject *source_object,
g_assert_no_error(err);
test->listener_conn = g_thread_join(test->thread);
- test_dbus_console_setup_listener(test);
+ test_dbus_console_setup_listener(test, test->with_map);
}
static gpointer
@@ -155,7 +216,7 @@ test_dbus_p2p_server_setup_thread(gpointer data)
}
static void
-test_dbus_display_console(void)
+test_dbus_display_console(const void* data)
{
g_autoptr(GError) err = NULL;
g_autoptr(GDBusConnection) conn = NULL;
@@ -163,7 +224,7 @@ test_dbus_display_console(void)
g_autoptr(GMainLoop) loop = NULL;
QTestState *qts = NULL;
int pair[2];
- TestDBusConsoleRegister test = { 0, };
+ TestDBusConsoleRegister test = { 0, .with_map = GPOINTER_TO_INT(data) };
#ifdef WIN32
WSAPROTOCOL_INFOW info;
g_autoptr(GVariant) listener = NULL;
@@ -299,7 +360,8 @@ main(int argc, char **argv)
g_test_init(&argc, &argv, NULL);
qtest_add_func("/dbus-display/vm", test_dbus_display_vm);
- qtest_add_func("/dbus-display/console", test_dbus_display_console);
+ qtest_add_data_func("/dbus-display/console", GINT_TO_POINTER(false), test_dbus_display_console);
+ qtest_add_data_func("/dbus-display/console/map", GINT_TO_POINTER(true), test_dbus_display_console);
qtest_add_func("/dbus-display/keyboard", test_dbus_display_keyboard);
return g_test_run();
diff --git a/tests/qtest/device-introspect-test.c b/tests/qtest/device-introspect-test.c
index 5b0ffe4..f84cec5 100644
--- a/tests/qtest/device-introspect-test.c
+++ b/tests/qtest/device-introspect-test.c
@@ -18,9 +18,9 @@
*/
#include "qemu/osdep.h"
-#include "qapi/qmp/qstring.h"
-#include "qapi/qmp/qdict.h"
-#include "qapi/qmp/qlist.h"
+#include "qobject/qstring.h"
+#include "qobject/qdict.h"
+#include "qobject/qlist.h"
#include "libqtest.h"
const char common_args[] = "-nodefaults -machine none";
@@ -266,7 +266,6 @@ static void test_device_intro_concrete(const void *args)
qobject_unref(types);
qtest_quit(qts);
- g_free((void *)args);
}
static void test_abstract_interfaces(void)
@@ -310,12 +309,12 @@ static void add_machine_test_case(const char *mname)
path = g_strdup_printf("device/introspect/concrete/defaults/%s", mname);
args = g_strdup_printf("-M %s", mname);
- qtest_add_data_func(path, args, test_device_intro_concrete);
+ qtest_add_data_func_full(path, args, test_device_intro_concrete, g_free);
g_free(path);
path = g_strdup_printf("device/introspect/concrete/nodefaults/%s", mname);
args = g_strdup_printf("-nodefaults -M %s", mname);
- qtest_add_data_func(path, args, test_device_intro_concrete);
+ qtest_add_data_func_full(path, args, test_device_intro_concrete, g_free);
g_free(path);
}
@@ -330,7 +329,7 @@ int main(int argc, char **argv)
qtest_add_func("device/introspect/abstract-interfaces", test_abstract_interfaces);
if (g_test_quick()) {
qtest_add_data_func("device/introspect/concrete/defaults/none",
- g_strdup(common_args), test_device_intro_concrete);
+ common_args, test_device_intro_concrete);
} else {
qtest_cb_for_every_machine(add_machine_test_case, true);
}
diff --git a/tests/qtest/device-plug-test.c b/tests/qtest/device-plug-test.c
index c6f3315..2707ee5 100644
--- a/tests/qtest/device-plug-test.c
+++ b/tests/qtest/device-plug-test.c
@@ -12,17 +12,8 @@
#include "qemu/osdep.h"
#include "libqtest.h"
-#include "qapi/qmp/qdict.h"
-#include "qapi/qmp/qstring.h"
-
-static void system_reset(QTestState *qtest)
-{
- QDict *resp;
-
- resp = qtest_qmp(qtest, "{'execute': 'system_reset'}");
- g_assert(qdict_haskey(resp, "return"));
- qobject_unref(resp);
-}
+#include "qobject/qdict.h"
+#include "qobject/qstring.h"
static void wait_device_deleted_event(QTestState *qtest, const char *id)
{
@@ -58,7 +49,7 @@ static void process_device_remove(QTestState *qtest, const char *id)
* handled, removing the device.
*/
qtest_qmp_device_del_send(qtest, id);
- system_reset(qtest);
+ qtest_system_reset_nowait(qtest);
wait_device_deleted_event(qtest, id);
}
diff --git a/tests/qtest/dm163-test.c b/tests/qtest/dm163-test.c
index 3161c92..4c8e654 100644
--- a/tests/qtest/dm163-test.c
+++ b/tests/qtest/dm163-test.c
@@ -182,6 +182,8 @@ static void test_dm163_gpio_connection(void)
g_assert_false(qtest_get_irq(qts, LAT_B));
g_assert_false(qtest_get_irq(qts, SELBK));
g_assert_false(qtest_get_irq(qts, RST_B));
+
+ qtest_quit(qts);
}
int main(int argc, char **argv)
diff --git a/tests/qtest/drive_del-test.c b/tests/qtest/drive_del-test.c
index 7b67a4b..30d9451 100644
--- a/tests/qtest/drive_del-test.c
+++ b/tests/qtest/drive_del-test.c
@@ -13,8 +13,8 @@
#include "qemu/osdep.h"
#include "libqtest.h"
#include "libqos/virtio.h"
-#include "qapi/qmp/qdict.h"
-#include "qapi/qmp/qlist.h"
+#include "qobject/qdict.h"
+#include "qobject/qlist.h"
static const char *qvirtio_get_dev_type(void);
@@ -154,15 +154,10 @@ static void device_add(QTestState *qts)
static void device_del(QTestState *qts, bool and_reset)
{
- QDict *response;
-
qtest_qmp_device_del_send(qts, "dev0");
if (and_reset) {
- response = qtest_qmp(qts, "{'execute': 'system_reset' }");
- g_assert(response);
- g_assert(qdict_haskey(response, "return"));
- qobject_unref(response);
+ qtest_system_reset_nowait(qts);
}
qtest_qmp_eventwait(qts, "DEVICE_DELETED");
diff --git a/tests/qtest/emc141x-test.c b/tests/qtest/emc141x-test.c
index 8c86694..a24103e 100644
--- a/tests/qtest/emc141x-test.c
+++ b/tests/qtest/emc141x-test.c
@@ -10,7 +10,7 @@
#include "libqtest-single.h"
#include "libqos/qgraph.h"
#include "libqos/i2c.h"
-#include "qapi/qmp/qdict.h"
+#include "qobject/qdict.h"
#include "hw/sensor/emc141x_regs.h"
#define EMC1414_TEST_ID "emc1414-test"
diff --git a/tests/qtest/fdc-test.c b/tests/qtest/fdc-test.c
index 5e8fbda..1b37a8a 100644
--- a/tests/qtest/fdc-test.c
+++ b/tests/qtest/fdc-test.c
@@ -26,7 +26,7 @@
#include "libqtest-single.h"
-#include "qapi/qmp/qdict.h"
+#include "qobject/qdict.h"
#define DRIVE_FLOPPY_BLANK \
"-drive if=floppy,file=null-co://,file.read-zeroes=on,format=raw,size=1440k"
@@ -552,7 +552,7 @@ static bool qtest_check_clang_sanitizer(void)
#ifdef QEMU_SANITIZE_ADDRESS
return true;
#else
- g_test_skip("QEMU not configured using --enable-sanitizers");
+ g_test_skip("QEMU not configured using --enable-asan");
return false;
#endif
}
diff --git a/tests/qtest/fuzz/fuzz.c b/tests/qtest/fuzz/fuzz.c
index 9b9c9f9..ca248a5 100644
--- a/tests/qtest/fuzz/fuzz.c
+++ b/tests/qtest/fuzz/fuzz.c
@@ -17,9 +17,9 @@
#include "qemu/cutils.h"
#include "qemu/datadir.h"
-#include "sysemu/sysemu.h"
-#include "sysemu/qtest.h"
-#include "sysemu/runstate.h"
+#include "system/system.h"
+#include "system/qtest.h"
+#include "system/runstate.h"
#include "qemu/main-loop.h"
#include "qemu/rcu.h"
#include "tests/qtest/libqtest.h"
@@ -41,6 +41,7 @@ static FuzzTargetList *fuzz_target_list;
static FuzzTarget *fuzz_target;
static QTestState *fuzz_qts;
+int (*qemu_main)(void);
void flush_events(QTestState *s)
diff --git a/tests/qtest/fuzz/generic_fuzz.c b/tests/qtest/fuzz/generic_fuzz.c
index ec842e0..f12080e 100644
--- a/tests/qtest/fuzz/generic_fuzz.c
+++ b/tests/qtest/fuzz/generic_fuzz.c
@@ -11,6 +11,7 @@
*/
#include "qemu/osdep.h"
+#include "qemu/range.h"
#include <wordexp.h>
@@ -19,8 +20,8 @@
#include "tests/qtest/libqos/pci-pc.h"
#include "fuzz.h"
#include "string.h"
-#include "exec/memory.h"
-#include "exec/ramblock.h"
+#include "system/memory.h"
+#include "system/ramblock.h"
#include "hw/qdev-core.h"
#include "hw/pci/pci.h"
#include "hw/pci/pci_device.h"
@@ -211,7 +212,7 @@ void fuzz_dma_read_cb(size_t addr, size_t len, MemoryRegion *mr)
i < dma_regions->len && (avoid_double_fetches || qtest_log_enabled);
++i) {
region = g_array_index(dma_regions, address_range, i);
- if (addr < region.addr + region.size && addr + len > region.addr) {
+ if (ranges_overlap(addr, len, region.addr, region.size)) {
double_fetch = true;
if (addr < region.addr
&& avoid_double_fetches) {
@@ -571,7 +572,6 @@ static void op_add_dma_pattern(QTestState *s,
pattern p = {a.index, a.stride, len - sizeof(a), data + sizeof(a)};
p.index = a.index % p.len;
g_array_append_val(dma_patterns, p);
- return;
}
static void op_clear_dma_patterns(QTestState *s,
diff --git a/tests/qtest/fuzz/qos_fuzz.c b/tests/qtest/fuzz/qos_fuzz.c
index d3839bf..9afe8bf 100644
--- a/tests/qtest/fuzz/qos_fuzz.c
+++ b/tests/qtest/fuzz/qos_fuzz.c
@@ -19,7 +19,7 @@
#include "qemu/osdep.h"
#include "qemu/units.h"
#include "qapi/error.h"
-#include "exec/memory.h"
+#include "system/memory.h"
#include "qemu/main-loop.h"
#include "tests/qtest/libqtest.h"
diff --git a/tests/qtest/fuzz/qtest_wrappers.c b/tests/qtest/fuzz/qtest_wrappers.c
index 0580f8d..d7adcbe 100644
--- a/tests/qtest/fuzz/qtest_wrappers.c
+++ b/tests/qtest/fuzz/qtest_wrappers.c
@@ -13,7 +13,7 @@
#include "qemu/osdep.h"
#include "hw/core/cpu.h"
-#include "exec/ioport.h"
+#include "system/ioport.h"
#include "fuzz.h"
diff --git a/tests/qtest/fw_cfg-test.c b/tests/qtest/fw_cfg-test.c
index 5dc807b..e48b34a 100644
--- a/tests/qtest/fw_cfg-test.c
+++ b/tests/qtest/fw_cfg-test.c
@@ -243,12 +243,6 @@ int main(int argc, char **argv)
qtest_add_func("fw_cfg/ram_size", test_fw_cfg_ram_size);
qtest_add_func("fw_cfg/nographic", test_fw_cfg_nographic);
qtest_add_func("fw_cfg/nb_cpus", test_fw_cfg_nb_cpus);
-#if 0
- qtest_add_func("fw_cfg/machine_id", test_fw_cfg_machine_id);
- qtest_add_func("fw_cfg/kernel", test_fw_cfg_kernel);
- qtest_add_func("fw_cfg/initrd", test_fw_cfg_initrd);
- qtest_add_func("fw_cfg/boot_device", test_fw_cfg_boot_device);
-#endif
qtest_add_func("fw_cfg/max_cpus", test_fw_cfg_max_cpus);
qtest_add_func("fw_cfg/numa", test_fw_cfg_numa);
qtest_add_func("fw_cfg/boot_menu", test_fw_cfg_boot_menu);
diff --git a/tests/qtest/hd-geo-test.c b/tests/qtest/hd-geo-test.c
index d08bffa..41481a5 100644
--- a/tests/qtest/hd-geo-test.c
+++ b/tests/qtest/hd-geo-test.c
@@ -17,7 +17,7 @@
#include "qemu/osdep.h"
#include "qemu/bswap.h"
-#include "qapi/qmp/qlist.h"
+#include "qobject/qlist.h"
#include "libqtest.h"
#include "libqos/fw_cfg.h"
#include "libqos/libqos.h"
@@ -900,7 +900,6 @@ static void test_override_hot_unplug(TestArgs *args, const char *devid,
QTestState *qts;
char *joined_args;
QFWCFG *fw_cfg;
- QDict *response;
int i;
joined_args = g_strjoinv(" ", args->argv);
@@ -913,13 +912,7 @@ static void test_override_hot_unplug(TestArgs *args, const char *devid,
/* unplug device an restart */
qtest_qmp_device_del_send(qts, devid);
- response = qtest_qmp(qts,
- "{ 'execute': 'system_reset', 'arguments': { }}");
- g_assert(response);
- g_assert(!qdict_haskey(response, "error"));
- qobject_unref(response);
-
- qtest_qmp_eventwait(qts, "RESET");
+ qtest_system_reset(qts);
read_bootdevices(fw_cfg, expected2);
@@ -1074,17 +1067,26 @@ int main(int argc, char **argv)
}
}
- qtest_add_func("hd-geo/ide/none", test_ide_none);
- qtest_add_func("hd-geo/ide/drive/mbr/blank", test_ide_drive_mbr_blank);
- qtest_add_func("hd-geo/ide/drive/mbr/lba", test_ide_drive_mbr_lba);
- qtest_add_func("hd-geo/ide/drive/mbr/chs", test_ide_drive_mbr_chs);
- qtest_add_func("hd-geo/ide/drive/cd_0", test_ide_drive_cd_0);
- qtest_add_func("hd-geo/ide/device/mbr/blank", test_ide_device_mbr_blank);
- qtest_add_func("hd-geo/ide/device/mbr/lba", test_ide_device_mbr_lba);
- qtest_add_func("hd-geo/ide/device/mbr/chs", test_ide_device_mbr_chs);
- qtest_add_func("hd-geo/ide/device/user/chs", test_ide_device_user_chs);
- qtest_add_func("hd-geo/ide/device/user/chst", test_ide_device_user_chst);
- if (have_qemu_img()) {
+ if (qtest_has_machine("pc")) {
+ qtest_add_func("hd-geo/ide/none", test_ide_none);
+ qtest_add_func("hd-geo/ide/drive/mbr/blank", test_ide_drive_mbr_blank);
+ qtest_add_func("hd-geo/ide/drive/mbr/lba", test_ide_drive_mbr_lba);
+ qtest_add_func("hd-geo/ide/drive/mbr/chs", test_ide_drive_mbr_chs);
+ qtest_add_func("hd-geo/ide/drive/cd_0", test_ide_drive_cd_0);
+ qtest_add_func("hd-geo/ide/device/mbr/blank", test_ide_device_mbr_blank);
+ qtest_add_func("hd-geo/ide/device/mbr/lba", test_ide_device_mbr_lba);
+ qtest_add_func("hd-geo/ide/device/mbr/chs", test_ide_device_mbr_chs);
+ qtest_add_func("hd-geo/ide/device/user/chs", test_ide_device_user_chs);
+ qtest_add_func("hd-geo/ide/device/user/chst", test_ide_device_user_chst);
+ }
+
+ if (!have_qemu_img()) {
+ g_test_message("QTEST_QEMU_IMG not set or qemu-img missing; "
+ "skipping hd-geo/override/* tests");
+ goto test_add_done;
+ }
+
+ if (qtest_has_machine("pc")) {
qtest_add_func("hd-geo/override/ide", test_override_ide);
if (qtest_has_device("lsi53c895a")) {
qtest_add_func("hd-geo/override/scsi", test_override_scsi);
@@ -1104,30 +1106,26 @@ int main(int argc, char **argv)
qtest_add_func("hd-geo/override/virtio_blk",
test_override_virtio_blk);
}
+ }
- if (qtest_has_machine("q35")) {
- qtest_add_func("hd-geo/override/sata", test_override_sata);
- qtest_add_func("hd-geo/override/zero_chs_q35",
- test_override_zero_chs_q35);
- if (qtest_has_device("lsi53c895a")) {
- qtest_add_func("hd-geo/override/scsi_q35",
- test_override_scsi_q35);
- }
- if (qtest_has_device("virtio-scsi-pci")) {
- qtest_add_func("hd-geo/override/scsi_hot_unplug_q35",
- test_override_scsi_hot_unplug_q35);
- }
- if (qtest_has_device("virtio-blk-pci")) {
- qtest_add_func("hd-geo/override/virtio_hot_unplug_q35",
- test_override_virtio_hot_unplug_q35);
- qtest_add_func("hd-geo/override/virtio_blk_q35",
- test_override_virtio_blk_q35);
- }
-
+ if (qtest_has_machine("q35")) {
+ qtest_add_func("hd-geo/override/sata", test_override_sata);
+ qtest_add_func("hd-geo/override/zero_chs_q35",
+ test_override_zero_chs_q35);
+ if (qtest_has_device("lsi53c895a")) {
+ qtest_add_func("hd-geo/override/scsi_q35",
+ test_override_scsi_q35);
+ }
+ if (qtest_has_device("virtio-scsi-pci")) {
+ qtest_add_func("hd-geo/override/scsi_hot_unplug_q35",
+ test_override_scsi_hot_unplug_q35);
+ }
+ if (qtest_has_device("virtio-blk-pci")) {
+ qtest_add_func("hd-geo/override/virtio_hot_unplug_q35",
+ test_override_virtio_hot_unplug_q35);
+ qtest_add_func("hd-geo/override/virtio_blk_q35",
+ test_override_virtio_blk_q35);
}
- } else {
- g_test_message("QTEST_QEMU_IMG not set or qemu-img missing; "
- "skipping hd-geo/override/* tests");
}
test_add_done:
diff --git a/tests/qtest/ide-test.c b/tests/qtest/ide-test.c
index 90ba6b2..ceee444 100644
--- a/tests/qtest/ide-test.c
+++ b/tests/qtest/ide-test.c
@@ -29,7 +29,7 @@
#include "libqos/libqos.h"
#include "libqos/pci-pc.h"
#include "libqos/malloc-pc.h"
-#include "qapi/qmp/qdict.h"
+#include "qobject/qdict.h"
#include "qemu/bswap.h"
#include "hw/pci/pci_ids.h"
#include "hw/pci/pci_regs.h"
diff --git a/tests/qtest/intel-iommu-test.c b/tests/qtest/intel-iommu-test.c
new file mode 100644
index 0000000..c521b37
--- /dev/null
+++ b/tests/qtest/intel-iommu-test.c
@@ -0,0 +1,64 @@
+/*
+ * QTest testcase for intel-iommu
+ *
+ * Copyright (c) 2024 Intel, Inc.
+ *
+ * Author: Zhenzhong Duan <zhenzhong.duan@intel.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "libqtest.h"
+#include "hw/i386/intel_iommu_internal.h"
+
+#define CAP_STAGE_1_FIXED1 (VTD_CAP_FRO | VTD_CAP_NFR | VTD_CAP_ND | \
+ VTD_CAP_MAMV | VTD_CAP_PSI | VTD_CAP_SLLPS)
+#define ECAP_STAGE_1_FIXED1 (VTD_ECAP_QI | VTD_ECAP_IR | VTD_ECAP_IRO | \
+ VTD_ECAP_MHMV | VTD_ECAP_SMTS | VTD_ECAP_FLTS)
+
+static inline uint64_t vtd_reg_readq(QTestState *s, uint64_t offset)
+{
+ return qtest_readq(s, Q35_HOST_BRIDGE_IOMMU_ADDR + offset);
+}
+
+static void test_intel_iommu_stage_1(void)
+{
+ uint8_t init_csr[DMAR_REG_SIZE]; /* register values */
+ uint8_t post_reset_csr[DMAR_REG_SIZE]; /* register values */
+ uint64_t cap, ecap, tmp;
+ QTestState *s;
+
+ s = qtest_init("-M q35 -device intel-iommu,x-scalable-mode=on,x-flts=on");
+
+ cap = vtd_reg_readq(s, DMAR_CAP_REG);
+ g_assert((cap & CAP_STAGE_1_FIXED1) == CAP_STAGE_1_FIXED1);
+
+ tmp = cap & VTD_CAP_SAGAW_MASK;
+ g_assert(tmp == (VTD_CAP_SAGAW_39bit | VTD_CAP_SAGAW_48bit));
+
+ tmp = VTD_MGAW_FROM_CAP(cap);
+ g_assert(tmp == VTD_HOST_AW_48BIT - 1);
+
+ ecap = vtd_reg_readq(s, DMAR_ECAP_REG);
+ g_assert((ecap & ECAP_STAGE_1_FIXED1) == ECAP_STAGE_1_FIXED1);
+
+ qtest_memread(s, Q35_HOST_BRIDGE_IOMMU_ADDR, init_csr, DMAR_REG_SIZE);
+
+ qobject_unref(qtest_qmp(s, "{ 'execute': 'system_reset' }"));
+ qtest_qmp_eventwait(s, "RESET");
+
+ qtest_memread(s, Q35_HOST_BRIDGE_IOMMU_ADDR, post_reset_csr, DMAR_REG_SIZE);
+ /* Ensure registers are consistent after hard reset */
+ g_assert(!memcmp(init_csr, post_reset_csr, DMAR_REG_SIZE));
+
+ qtest_quit(s);
+}
+
+int main(int argc, char **argv)
+{
+ g_test_init(&argc, &argv, NULL);
+ qtest_add_func("/q35/intel-iommu/stage-1", test_intel_iommu_stage_1);
+
+ return g_test_run();
+}
diff --git a/tests/qtest/ipmi-bt-test.c b/tests/qtest/ipmi-bt-test.c
index 383239b..637732f 100644
--- a/tests/qtest/ipmi-bt-test.c
+++ b/tests/qtest/ipmi-bt-test.c
@@ -251,7 +251,7 @@ static void emu_msg_handler(void)
msg[msg_len++] = 0xa0;
write_emu_msg(msg, msg_len);
} else {
- g_assert(0);
+ g_assert_not_reached();
}
}
@@ -411,7 +411,7 @@ int main(int argc, char **argv)
g_test_init(&argc, &argv, NULL);
global_qtest = qtest_initf(
- " -chardev socket,id=ipmi0,host=127.0.0.1,port=%d,reconnect=10"
+ " -chardev socket,id=ipmi0,host=127.0.0.1,port=%d,reconnect-ms=10000"
" -device ipmi-bmc-extern,chardev=ipmi0,id=bmc0"
" -device isa-ipmi-bt,bmc=bmc0", emu_port);
qtest_irq_intercept_in(global_qtest, "ioapic");
diff --git a/tests/qtest/ipmi-kcs-test.c b/tests/qtest/ipmi-kcs-test.c
index afc24dd..3186c6a 100644
--- a/tests/qtest/ipmi-kcs-test.c
+++ b/tests/qtest/ipmi-kcs-test.c
@@ -145,7 +145,7 @@ static void kcs_cmd(uint8_t *cmd, unsigned int cmd_len,
break;
default:
- g_assert(0);
+ g_assert_not_reached();
}
*rsp_len = j;
}
@@ -184,7 +184,7 @@ static void kcs_abort(uint8_t *cmd, unsigned int cmd_len,
break;
default:
- g_assert(0);
+ g_assert_not_reached();
}
/* Start the abort here */
diff --git a/tests/qtest/isl_pmbus_vr-test.c b/tests/qtest/isl_pmbus_vr-test.c
index 5553ea4..1ff840c 100644
--- a/tests/qtest/isl_pmbus_vr-test.c
+++ b/tests/qtest/isl_pmbus_vr-test.c
@@ -21,8 +21,8 @@
#include "libqtest-single.h"
#include "libqos/qgraph.h"
#include "libqos/i2c.h"
-#include "qapi/qmp/qdict.h"
-#include "qapi/qmp/qnum.h"
+#include "qobject/qdict.h"
+#include "qobject/qnum.h"
#include "qemu/bitops.h"
#define TEST_ID "isl_pmbus_vr-test"
diff --git a/tests/qtest/libqmp.c b/tests/qtest/libqmp.c
index a89cab0..16fe546 100644
--- a/tests/qtest/libqmp.c
+++ b/tests/qtest/libqmp.c
@@ -25,8 +25,8 @@
#include "qemu/cutils.h"
#include "qemu/sockets.h"
#include "qapi/error.h"
-#include "qapi/qmp/json-parser.h"
-#include "qapi/qmp/qjson.h"
+#include "qobject/json-parser.h"
+#include "qobject/qjson.h"
#define SOCKET_MAX_FDS 16
diff --git a/tests/qtest/libqmp.h b/tests/qtest/libqmp.h
index 3445b75..4a931c9 100644
--- a/tests/qtest/libqmp.h
+++ b/tests/qtest/libqmp.h
@@ -18,7 +18,7 @@
#ifndef LIBQMP_H
#define LIBQMP_H
-#include "qapi/qmp/qdict.h"
+#include "qobject/qdict.h"
QDict *qmp_fd_receive(int fd);
#ifndef _WIN32
diff --git a/tests/qtest/libqos/arm-imx25-pdk-machine.c b/tests/qtest/libqos/arm-imx25-pdk-machine.c
index 8fe128f..2d8b754 100644
--- a/tests/qtest/libqos/arm-imx25-pdk-machine.c
+++ b/tests/qtest/libqos/arm-imx25-pdk-machine.c
@@ -23,6 +23,7 @@
#include "libqos-malloc.h"
#include "qgraph.h"
#include "i2c.h"
+#include "hw/i2c/imx_i2c.h"
#define ARM_PAGE_SIZE 4096
#define IMX25_PDK_RAM_START 0x80000000
@@ -50,7 +51,7 @@ static void *imx25_pdk_get_driver(void *object, const char *interface)
static QOSGraphObject *imx25_pdk_get_device(void *obj, const char *device)
{
QIMX25PDKMachine *machine = obj;
- if (!g_strcmp0(device, "imx.i2c")) {
+ if (!g_strcmp0(device, TYPE_IMX_I2C)) {
return &machine->i2c_1.obj;
}
@@ -86,7 +87,7 @@ static void imx25_pdk_register_nodes(void)
.extra_device_opts = "bus=i2c-bus.0"
};
qos_node_create_machine("arm/imx25-pdk", qos_create_machine_arm_imx25_pdk);
- qos_node_contains("arm/imx25-pdk", "imx.i2c", &edge, NULL);
+ qos_node_contains("arm/imx25-pdk", TYPE_IMX_I2C, &edge, NULL);
}
libqos_init(imx25_pdk_register_nodes);
diff --git a/tests/qtest/libqos/arm-n800-machine.c b/tests/qtest/libqos/arm-n800-machine.c
deleted file mode 100644
index 4e5afe0..0000000
--- a/tests/qtest/libqos/arm-n800-machine.c
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * libqos driver framework
- *
- * Copyright (c) 2019 Red Hat, Inc.
- *
- * Author: Paolo Bonzini <pbonzini@redhat.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License version 2.1 as published by the Free Software Foundation.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>
- */
-
-#include "qemu/osdep.h"
-#include "../libqtest.h"
-#include "libqos-malloc.h"
-#include "qgraph.h"
-#include "i2c.h"
-
-#define ARM_PAGE_SIZE 4096
-#define N800_RAM_START 0x80000000
-#define N800_RAM_END 0x88000000
-
-typedef struct QN800Machine QN800Machine;
-
-struct QN800Machine {
- QOSGraphObject obj;
- QGuestAllocator alloc;
- OMAPI2C i2c_1;
-};
-
-static void *n800_get_driver(void *object, const char *interface)
-{
- QN800Machine *machine = object;
- if (!g_strcmp0(interface, "memory")) {
- return &machine->alloc;
- }
-
- fprintf(stderr, "%s not present in arm/n800\n", interface);
- g_assert_not_reached();
-}
-
-static QOSGraphObject *n800_get_device(void *obj, const char *device)
-{
- QN800Machine *machine = obj;
- if (!g_strcmp0(device, "omap_i2c")) {
- return &machine->i2c_1.obj;
- }
-
- fprintf(stderr, "%s not present in arm/n800\n", device);
- g_assert_not_reached();
-}
-
-static void n800_destructor(QOSGraphObject *obj)
-{
- QN800Machine *machine = (QN800Machine *) obj;
- alloc_destroy(&machine->alloc);
-}
-
-static void *qos_create_machine_arm_n800(QTestState *qts)
-{
- QN800Machine *machine = g_new0(QN800Machine, 1);
-
- alloc_init(&machine->alloc, 0,
- N800_RAM_START,
- N800_RAM_END,
- ARM_PAGE_SIZE);
- machine->obj.get_device = n800_get_device;
- machine->obj.get_driver = n800_get_driver;
- machine->obj.destructor = n800_destructor;
-
- omap_i2c_init(&machine->i2c_1, qts, 0x48070000);
- return &machine->obj;
-}
-
-static void n800_register_nodes(void)
-{
- QOSGraphEdgeOptions edge = {
- .extra_device_opts = "bus=i2c-bus.0"
- };
- qos_node_create_machine("arm/n800", qos_create_machine_arm_n800);
- qos_node_contains("arm/n800", "omap_i2c", &edge, NULL);
-}
-
-libqos_init(n800_register_nodes);
diff --git a/tests/qtest/libqos/fw_cfg.c b/tests/qtest/libqos/fw_cfg.c
index 89f053c..0ab3959 100644
--- a/tests/qtest/libqos/fw_cfg.c
+++ b/tests/qtest/libqos/fw_cfg.c
@@ -14,6 +14,8 @@
#include "qemu/osdep.h"
#include "fw_cfg.h"
+#include "malloc-pc.h"
+#include "libqos-malloc.h"
#include "../libqtest.h"
#include "qemu/bswap.h"
#include "hw/nvram/fw_cfg.h"
@@ -60,6 +62,91 @@ static void mm_fw_cfg_select(QFWCFG *fw_cfg, uint16_t key)
qtest_writew(fw_cfg->qts, fw_cfg->base, key);
}
+static void qfw_cfg_dma_transfer(QFWCFG *fw_cfg, QOSState *qs, void *address,
+ uint32_t length, uint32_t control)
+{
+ FWCfgDmaAccess access;
+ uint32_t addr;
+ uint64_t guest_access_addr;
+ uint64_t gaddr;
+
+ /* create a data buffer in guest memory */
+ gaddr = guest_alloc(&qs->alloc, length);
+
+ if (control & FW_CFG_DMA_CTL_WRITE) {
+ qtest_bufwrite(fw_cfg->qts, gaddr, address, length);
+ }
+ access.address = cpu_to_be64(gaddr);
+ access.length = cpu_to_be32(length);
+ access.control = cpu_to_be32(control);
+
+ /* now create a separate buffer in guest memory for 'access' */
+ guest_access_addr = guest_alloc(&qs->alloc, sizeof(access));
+ qtest_bufwrite(fw_cfg->qts, guest_access_addr, &access, sizeof(access));
+
+ /* write lower 32 bits of address */
+ addr = cpu_to_be32((uint32_t)(uintptr_t)guest_access_addr);
+ qtest_outl(fw_cfg->qts, fw_cfg->base + 8, addr);
+
+ /* write upper 32 bits of address */
+ addr = cpu_to_be32((uint32_t)(uintptr_t)(guest_access_addr >> 32));
+ qtest_outl(fw_cfg->qts, fw_cfg->base + 4, addr);
+
+ g_assert(!(be32_to_cpu(access.control) & FW_CFG_DMA_CTL_ERROR));
+
+ if (control & FW_CFG_DMA_CTL_READ) {
+ qtest_bufread(fw_cfg->qts, gaddr, address, length);
+ }
+
+ guest_free(&qs->alloc, guest_access_addr);
+ guest_free(&qs->alloc, gaddr);
+}
+
+static void qfw_cfg_write_entry(QFWCFG *fw_cfg, QOSState *qs, uint16_t key,
+ void *buf, uint32_t len)
+{
+ qfw_cfg_select(fw_cfg, key);
+ qfw_cfg_dma_transfer(fw_cfg, qs, buf, len, FW_CFG_DMA_CTL_WRITE);
+}
+
+static void qfw_cfg_read_entry(QFWCFG *fw_cfg, QOSState *qs, uint16_t key,
+ void *buf, uint32_t len)
+{
+ qfw_cfg_select(fw_cfg, key);
+ qfw_cfg_dma_transfer(fw_cfg, qs, buf, len, FW_CFG_DMA_CTL_READ);
+}
+
+static bool find_pdir_entry(QFWCFG *fw_cfg, const char *filename,
+ uint16_t *sel, uint32_t *size)
+{
+ g_autofree unsigned char *filesbuf = NULL;
+ uint32_t count;
+ size_t dsize;
+ FWCfgFile *pdir_entry;
+ uint32_t i;
+ bool found = false;
+
+ *size = 0;
+ *sel = 0;
+
+ qfw_cfg_get(fw_cfg, FW_CFG_FILE_DIR, &count, sizeof(count));
+ count = be32_to_cpu(count);
+ dsize = sizeof(uint32_t) + count * sizeof(struct fw_cfg_file);
+ filesbuf = g_malloc(dsize);
+ qfw_cfg_get(fw_cfg, FW_CFG_FILE_DIR, filesbuf, dsize);
+ pdir_entry = (FWCfgFile *)(filesbuf + sizeof(uint32_t));
+ for (i = 0; i < count; ++i, ++pdir_entry) {
+ if (!strcmp(pdir_entry->name, filename)) {
+ *size = be32_to_cpu(pdir_entry->size);
+ *sel = be16_to_cpu(pdir_entry->select);
+ found = true;
+ break;
+ }
+ }
+
+ return found;
+}
+
/*
* The caller need check the return value. When the return value is
* nonzero, it means that some bytes have been transferred.
@@ -73,37 +160,106 @@ static void mm_fw_cfg_select(QFWCFG *fw_cfg, uint16_t key)
* populated, it has received only a starting slice of the fw_cfg file.
*/
size_t qfw_cfg_get_file(QFWCFG *fw_cfg, const char *filename,
- void *data, size_t buflen)
+ void *data, size_t buflen)
{
- uint32_t count;
- uint32_t i;
- unsigned char *filesbuf = NULL;
- size_t dsize;
- FWCfgFile *pdir_entry;
size_t filesize = 0;
+ uint32_t len;
+ uint16_t sel;
- qfw_cfg_get(fw_cfg, FW_CFG_FILE_DIR, &count, sizeof(count));
- count = be32_to_cpu(count);
- dsize = sizeof(uint32_t) + count * sizeof(struct fw_cfg_file);
- filesbuf = g_malloc(dsize);
- qfw_cfg_get(fw_cfg, FW_CFG_FILE_DIR, filesbuf, dsize);
- pdir_entry = (FWCfgFile *)(filesbuf + sizeof(uint32_t));
- for (i = 0; i < count; ++i, ++pdir_entry) {
- if (!strcmp(pdir_entry->name, filename)) {
- uint32_t len = be32_to_cpu(pdir_entry->size);
- uint16_t sel = be16_to_cpu(pdir_entry->select);
- filesize = len;
- if (len > buflen) {
- len = buflen;
- }
- qfw_cfg_get(fw_cfg, sel, data, len);
- break;
+ if (find_pdir_entry(fw_cfg, filename, &sel, &len)) {
+ filesize = len;
+ if (len > buflen) {
+ len = buflen;
}
+ qfw_cfg_get(fw_cfg, sel, data, len);
}
- g_free(filesbuf);
+
return filesize;
}
+/*
+ * The caller need check the return value. When the return value is
+ * nonzero, it means that some bytes have been transferred.
+ *
+ * If the fw_cfg file in question is smaller than the allocated & passed-in
+ * buffer, then the first len bytes were read.
+ *
+ * If the fw_cfg file in question is larger than the passed-in
+ * buffer, then the return value explains how much was actually read.
+ *
+ * It is illegal to call this function if fw_cfg does not support DMA
+ * interface. The caller should ensure that DMA is supported before
+ * calling this function.
+ *
+ * Passed QOSState pointer qs must be initialized. qs->alloc must also be
+ * properly initialized.
+ */
+size_t qfw_cfg_read_file(QFWCFG *fw_cfg, QOSState *qs, const char *filename,
+ void *data, size_t buflen)
+{
+ uint32_t len = 0;
+ uint16_t sel;
+ uint32_t id;
+
+ g_assert(qs);
+ g_assert(filename);
+ g_assert(data);
+ g_assert(buflen);
+ /* check if DMA is supported since we use DMA for read */
+ id = qfw_cfg_get_u32(fw_cfg, FW_CFG_ID);
+ g_assert(id & FW_CFG_VERSION_DMA);
+
+ if (find_pdir_entry(fw_cfg, filename, &sel, &len)) {
+ if (len > buflen) {
+ len = buflen;
+ }
+ qfw_cfg_read_entry(fw_cfg, qs, sel, data, len);
+ }
+
+ return len;
+}
+
+/*
+ * The caller need check the return value. When the return value is
+ * nonzero, it means that some bytes have been transferred.
+ *
+ * If the fw_cfg file in question is smaller than the allocated & passed-in
+ * buffer, then the buffer has been partially written.
+ *
+ * If the fw_cfg file in question is larger than the passed-in
+ * buffer, then the return value explains how much was actually written.
+ *
+ * It is illegal to call this function if fw_cfg does not support DMA
+ * interface. The caller should ensure that DMA is supported before
+ * calling this function.
+ *
+ * Passed QOSState pointer qs must be initialized. qs->alloc must also be
+ * properly initialized.
+ */
+size_t qfw_cfg_write_file(QFWCFG *fw_cfg, QOSState *qs, const char *filename,
+ void *data, size_t buflen)
+{
+ uint32_t len = 0;
+ uint16_t sel;
+ uint32_t id;
+
+ g_assert(qs);
+ g_assert(filename);
+ g_assert(data);
+ g_assert(buflen);
+ /* write operation is only valid if DMA is supported */
+ id = qfw_cfg_get_u32(fw_cfg, FW_CFG_ID);
+ g_assert(id & FW_CFG_VERSION_DMA);
+
+ if (find_pdir_entry(fw_cfg, filename, &sel, &len)) {
+ if (len > buflen) {
+ len = buflen;
+ }
+ qfw_cfg_write_entry(fw_cfg, qs, sel, data, len);
+ }
+ return len;
+}
+
static void mm_fw_cfg_read(QFWCFG *fw_cfg, void *data, size_t len)
{
uint8_t *ptr = data;
diff --git a/tests/qtest/libqos/fw_cfg.h b/tests/qtest/libqos/fw_cfg.h
index b0456a1..6d6ff09 100644
--- a/tests/qtest/libqos/fw_cfg.h
+++ b/tests/qtest/libqos/fw_cfg.h
@@ -14,6 +14,7 @@
#define LIBQOS_FW_CFG_H
#include "../libqtest.h"
+#include "libqos.h"
typedef struct QFWCFG QFWCFG;
@@ -33,7 +34,10 @@ uint32_t qfw_cfg_get_u32(QFWCFG *fw_cfg, uint16_t key);
uint64_t qfw_cfg_get_u64(QFWCFG *fw_cfg, uint16_t key);
size_t qfw_cfg_get_file(QFWCFG *fw_cfg, const char *filename,
void *data, size_t buflen);
-
+size_t qfw_cfg_write_file(QFWCFG *fw_cfg, QOSState *qs, const char *filename,
+ void *data, size_t buflen);
+size_t qfw_cfg_read_file(QFWCFG *fw_cfg, QOSState *qs, const char *filename,
+ void *data, size_t buflen);
QFWCFG *mm_fw_cfg_init(QTestState *qts, uint64_t base);
void mm_fw_cfg_uninit(QFWCFG *fw_cfg);
QFWCFG *io_fw_cfg_init(QTestState *qts, uint16_t base);
diff --git a/tests/qtest/libqos/generic-pcihost.c b/tests/qtest/libqos/generic-pcihost.c
index 3124b0e..4bbeb5f 100644
--- a/tests/qtest/libqos/generic-pcihost.c
+++ b/tests/qtest/libqos/generic-pcihost.c
@@ -13,7 +13,7 @@
#include "qemu/osdep.h"
#include "../libqtest.h"
#include "generic-pcihost.h"
-#include "qapi/qmp/qdict.h"
+#include "qobject/qdict.h"
#include "hw/pci/pci_regs.h"
#include "qemu/host-utils.h"
diff --git a/tests/qtest/libqos/i2c-imx.c b/tests/qtest/libqos/i2c-imx.c
index 710cb92..6d868e4 100644
--- a/tests/qtest/libqos/i2c-imx.c
+++ b/tests/qtest/libqos/i2c-imx.c
@@ -209,8 +209,8 @@ void imx_i2c_init(IMXI2C *s, QTestState *qts, uint64_t addr)
static void imx_i2c_register_nodes(void)
{
- qos_node_create_driver("imx.i2c", NULL);
- qos_node_produces("imx.i2c", "i2c-bus");
+ qos_node_create_driver(TYPE_IMX_I2C, NULL);
+ qos_node_produces(TYPE_IMX_I2C, "i2c-bus");
}
libqos_init(imx_i2c_register_nodes);
diff --git a/tests/qtest/libqos/igb.c b/tests/qtest/libqos/igb.c
index f40c4ec..ab3ef6f 100644
--- a/tests/qtest/libqos/igb.c
+++ b/tests/qtest/libqos/igb.c
@@ -104,10 +104,10 @@ static void igb_pci_start_hw(QOSGraphObject *obj)
e1000e_macreg_write(&d->e1000e, E1000_RDT(0), 0);
e1000e_macreg_write(&d->e1000e, E1000_RDH(0), 0);
e1000e_macreg_write(&d->e1000e, E1000_RA,
- le32_to_cpu(*(uint32_t *)address));
+ ldl_le_p(address));
e1000e_macreg_write(&d->e1000e, E1000_RA + 4,
E1000_RAH_AV | E1000_RAH_POOL_1 |
- le16_to_cpu(*(uint16_t *)(address + 4)));
+ lduw_le_p(address + 4));
/* Set supported receive descriptor mode */
e1000e_macreg_write(&d->e1000e,
diff --git a/tests/qtest/libqos/libqos-malloc.c b/tests/qtest/libqos/libqos-malloc.c
index d756697..c90f8f0 100644
--- a/tests/qtest/libqos/libqos-malloc.c
+++ b/tests/qtest/libqos/libqos-malloc.c
@@ -342,5 +342,4 @@ void migrate_allocator(QGuestAllocator *src,
QTAILQ_INIT(src->free);
node = mlist_new(src->start, src->end - src->start);
QTAILQ_INSERT_HEAD(src->free, node, MLIST_ENTNAME);
- return;
}
diff --git a/tests/qtest/libqos/libqos.c b/tests/qtest/libqos/libqos.c
index 5c0fa1f..9b49d0d 100644
--- a/tests/qtest/libqos/libqos.c
+++ b/tests/qtest/libqos/libqos.c
@@ -2,7 +2,7 @@
#include "../libqtest.h"
#include "libqos.h"
#include "pci.h"
-#include "qapi/qmp/qdict.h"
+#include "qobject/qdict.h"
/*** Test Setup & Teardown ***/
@@ -117,13 +117,14 @@ void migrate(QOSState *from, QOSState *to, const char *uri)
g_assert(qdict_haskey(sub, "status"));
st = qdict_get_str(sub, "status");
- /* "setup", "active", "completed", "failed", "cancelled" */
+ /* "setup", "active", "device", "completed", "failed", "cancelled" */
if (strcmp(st, "completed") == 0) {
qobject_unref(rsp);
break;
}
if ((strcmp(st, "setup") == 0) || (strcmp(st, "active") == 0)
+ || (strcmp(st, "device") == 0)
|| (strcmp(st, "wait-unplug") == 0)) {
qobject_unref(rsp);
g_usleep(5000);
diff --git a/tests/qtest/libqos/meson.build b/tests/qtest/libqos/meson.build
index 558eb4c..1ddaf7b 100644
--- a/tests/qtest/libqos/meson.build
+++ b/tests/qtest/libqos/meson.build
@@ -32,7 +32,6 @@ libqos_srcs = files(
'i2c-omap.c',
'igb.c',
'sdhci.c',
- 'tpci200.c',
'virtio.c',
'virtio-balloon.c',
'virtio-blk.c',
@@ -52,7 +51,6 @@ libqos_srcs = files(
# qgraph machines:
'aarch64-xlnx-zcu102-machine.c',
'arm-imx25-pdk-machine.c',
- 'arm-n800-machine.c',
'arm-raspi2-machine.c',
'arm-sabrelite-machine.c',
'arm-smdkc210-machine.c',
@@ -68,8 +66,14 @@ if have_virtfs
libqos_srcs += files('virtio-9p.c', 'virtio-9p-client.c')
endif
+if config_all_devices.has_key('CONFIG_RISCV_IOMMU')
+ libqos_srcs += files('riscv-iommu.c')
+endif
+if config_all_devices.has_key('CONFIG_TPCI200')
+ libqos_srcs += files('tpci200.c')
+endif
+
libqos = static_library('qos', libqos_srcs + genh,
- name_suffix: 'fa',
build_by_default: false)
-qos = declare_dependency(link_whole: libqos)
+qos = declare_dependency(objects: libqos.extract_all_objects(recursive: false))
diff --git a/tests/qtest/libqos/pci-pc.c b/tests/qtest/libqos/pci-pc.c
index 9604628..147009f 100644
--- a/tests/qtest/libqos/pci-pc.c
+++ b/tests/qtest/libqos/pci-pc.c
@@ -13,7 +13,7 @@
#include "qemu/osdep.h"
#include "../libqtest.h"
#include "pci-pc.h"
-#include "qapi/qmp/qdict.h"
+#include "qobject/qdict.h"
#include "hw/pci/pci_regs.h"
#include "qemu/module.h"
diff --git a/tests/qtest/libqos/pci.c b/tests/qtest/libqos/pci.c
index b23d723..a59197b 100644
--- a/tests/qtest/libqos/pci.c
+++ b/tests/qtest/libqos/pci.c
@@ -328,8 +328,6 @@ bool qpci_msix_pending(QPCIDevice *dev, uint16_t entry)
g_assert(dev->msix_enabled);
pba_entry = qpci_io_readl(dev, dev->msix_pba_bar, dev->msix_pba_off + off);
- qpci_io_writel(dev, dev->msix_pba_bar, dev->msix_pba_off + off,
- pba_entry & ~(1 << bit_n));
return (pba_entry & (1 << bit_n)) != 0;
}
diff --git a/tests/qtest/libqos/qgraph.h b/tests/qtest/libqos/qgraph.h
index 1b5de02..81fbfdd 100644
--- a/tests/qtest/libqos/qgraph.h
+++ b/tests/qtest/libqos/qgraph.h
@@ -355,7 +355,7 @@ void qos_object_start_hw(QOSGraphObject *obj);
QOSGraphObject *qos_machine_new(QOSGraphNode *node, QTestState *qts);
/**
- * qos_machine_new(): instantiate a new driver node
+ * qos_driver_new(): instantiate a new driver node
* @node: A driver node to be instantiated
* @parent: A #QOSGraphObject to be consumed by the new driver node
* @alloc: An allocator to be used by the new driver node.
diff --git a/tests/qtest/libqos/qos_external.c b/tests/qtest/libqos/qos_external.c
index c6bb8bf..493ab74 100644
--- a/tests/qtest/libqos/qos_external.c
+++ b/tests/qtest/libqos/qos_external.c
@@ -19,11 +19,11 @@
#include "qemu/osdep.h"
#include <getopt.h>
#include "../libqtest.h"
-#include "qapi/qmp/qdict.h"
-#include "qapi/qmp/qbool.h"
-#include "qapi/qmp/qstring.h"
+#include "qobject/qdict.h"
+#include "qobject/qbool.h"
+#include "qobject/qstring.h"
#include "qemu/module.h"
-#include "qapi/qmp/qlist.h"
+#include "qobject/qlist.h"
#include "libqos-malloc.h"
#include "qgraph.h"
#include "qgraph_internal.h"
diff --git a/tests/qtest/libqos/riscv-iommu.c b/tests/qtest/libqos/riscv-iommu.c
new file mode 100644
index 0000000..01e3b31
--- /dev/null
+++ b/tests/qtest/libqos/riscv-iommu.c
@@ -0,0 +1,76 @@
+/*
+ * libqos driver riscv-iommu-pci framework
+ *
+ * Copyright (c) 2024 Ventana Micro Systems Inc.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or (at your
+ * option) any later version. See the COPYING file in the top-level directory.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "../libqtest.h"
+#include "qemu/module.h"
+#include "qgraph.h"
+#include "pci.h"
+#include "riscv-iommu.h"
+
+static void *riscv_iommu_pci_get_driver(void *obj, const char *interface)
+{
+ QRISCVIOMMU *r_iommu_pci = obj;
+
+ if (!g_strcmp0(interface, "pci-device")) {
+ return &r_iommu_pci->dev;
+ }
+
+ fprintf(stderr, "%s not present in riscv_iommu_pci\n", interface);
+ g_assert_not_reached();
+}
+
+static void riscv_iommu_pci_start_hw(QOSGraphObject *obj)
+{
+ QRISCVIOMMU *pci = (QRISCVIOMMU *)obj;
+ qpci_device_enable(&pci->dev);
+}
+
+static void riscv_iommu_pci_destructor(QOSGraphObject *obj)
+{
+ QRISCVIOMMU *pci = (QRISCVIOMMU *)obj;
+ qpci_iounmap(&pci->dev, pci->reg_bar);
+}
+
+static void *riscv_iommu_pci_create(void *pci_bus, QGuestAllocator *alloc,
+ void *addr)
+{
+ QRISCVIOMMU *r_iommu_pci = g_new0(QRISCVIOMMU, 1);
+ QPCIBus *bus = pci_bus;
+
+ qpci_device_init(&r_iommu_pci->dev, bus, addr);
+ r_iommu_pci->reg_bar = qpci_iomap(&r_iommu_pci->dev, 0, NULL);
+
+ r_iommu_pci->obj.get_driver = riscv_iommu_pci_get_driver;
+ r_iommu_pci->obj.start_hw = riscv_iommu_pci_start_hw;
+ r_iommu_pci->obj.destructor = riscv_iommu_pci_destructor;
+ return &r_iommu_pci->obj;
+}
+
+static void riscv_iommu_pci_register_nodes(void)
+{
+ QPCIAddress addr = {
+ .vendor_id = RISCV_IOMMU_PCI_VENDOR_ID,
+ .device_id = RISCV_IOMMU_PCI_DEVICE_ID,
+ .devfn = QPCI_DEVFN(1, 0),
+ };
+
+ QOSGraphEdgeOptions opts = {
+ .extra_device_opts = "addr=01.0",
+ };
+
+ add_qpci_address(&opts, &addr);
+
+ qos_node_create_driver("riscv-iommu-pci", riscv_iommu_pci_create);
+ qos_node_produces("riscv-iommu-pci", "pci-device");
+ qos_node_consumes("riscv-iommu-pci", "pci-bus", &opts);
+}
+
+libqos_init(riscv_iommu_pci_register_nodes);
diff --git a/tests/qtest/libqos/riscv-iommu.h b/tests/qtest/libqos/riscv-iommu.h
new file mode 100644
index 0000000..318db13
--- /dev/null
+++ b/tests/qtest/libqos/riscv-iommu.h
@@ -0,0 +1,101 @@
+/*
+ * libqos driver riscv-iommu-pci framework
+ *
+ * Copyright (c) 2024 Ventana Micro Systems Inc.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or (at your
+ * option) any later version. See the COPYING file in the top-level directory.
+ *
+ */
+
+#ifndef TESTS_LIBQOS_RISCV_IOMMU_H
+#define TESTS_LIBQOS_RISCV_IOMMU_H
+
+#include "qgraph.h"
+#include "pci.h"
+#include "qemu/bitops.h"
+
+#ifndef GENMASK_ULL
+#define GENMASK_ULL(h, l) (((~0ULL) >> (63 - (h) + (l))) << (l))
+#endif
+
+/*
+ * RISC-V IOMMU uses PCI_VENDOR_ID_REDHAT 0x1b36 and
+ * PCI_DEVICE_ID_REDHAT_RISCV_IOMMU 0x0014.
+ */
+#define RISCV_IOMMU_PCI_VENDOR_ID 0x1b36
+#define RISCV_IOMMU_PCI_DEVICE_ID 0x0014
+#define RISCV_IOMMU_PCI_DEVICE_CLASS 0x0806
+
+/* Common field positions */
+#define RISCV_IOMMU_QUEUE_ENABLE BIT(0)
+#define RISCV_IOMMU_QUEUE_INTR_ENABLE BIT(1)
+#define RISCV_IOMMU_QUEUE_MEM_FAULT BIT(8)
+#define RISCV_IOMMU_QUEUE_ACTIVE BIT(16)
+#define RISCV_IOMMU_QUEUE_BUSY BIT(17)
+
+#define RISCV_IOMMU_REG_CAP 0x0000
+#define RISCV_IOMMU_CAP_VERSION GENMASK_ULL(7, 0)
+
+#define RISCV_IOMMU_REG_DDTP 0x0010
+#define RISCV_IOMMU_DDTP_BUSY BIT_ULL(4)
+#define RISCV_IOMMU_DDTP_MODE GENMASK_ULL(3, 0)
+#define RISCV_IOMMU_DDTP_MODE_OFF 0
+
+#define RISCV_IOMMU_REG_CQCSR 0x0048
+#define RISCV_IOMMU_CQCSR_CQEN RISCV_IOMMU_QUEUE_ENABLE
+#define RISCV_IOMMU_CQCSR_CIE RISCV_IOMMU_QUEUE_INTR_ENABLE
+#define RISCV_IOMMU_CQCSR_CQON RISCV_IOMMU_QUEUE_ACTIVE
+#define RISCV_IOMMU_CQCSR_BUSY RISCV_IOMMU_QUEUE_BUSY
+
+#define RISCV_IOMMU_REG_FQCSR 0x004C
+#define RISCV_IOMMU_FQCSR_FQEN RISCV_IOMMU_QUEUE_ENABLE
+#define RISCV_IOMMU_FQCSR_FIE RISCV_IOMMU_QUEUE_INTR_ENABLE
+#define RISCV_IOMMU_FQCSR_FQON RISCV_IOMMU_QUEUE_ACTIVE
+#define RISCV_IOMMU_FQCSR_BUSY RISCV_IOMMU_QUEUE_BUSY
+
+#define RISCV_IOMMU_REG_PQCSR 0x0050
+#define RISCV_IOMMU_PQCSR_PQEN RISCV_IOMMU_QUEUE_ENABLE
+#define RISCV_IOMMU_PQCSR_PIE RISCV_IOMMU_QUEUE_INTR_ENABLE
+#define RISCV_IOMMU_PQCSR_PQON RISCV_IOMMU_QUEUE_ACTIVE
+#define RISCV_IOMMU_PQCSR_BUSY RISCV_IOMMU_QUEUE_BUSY
+
+#define RISCV_IOMMU_REG_IPSR 0x0054
+
+#define RISCV_IOMMU_REG_IVEC 0x02F8
+#define RISCV_IOMMU_REG_IVEC_CIV GENMASK_ULL(3, 0)
+#define RISCV_IOMMU_REG_IVEC_FIV GENMASK_ULL(7, 4)
+#define RISCV_IOMMU_REG_IVEC_PMIV GENMASK_ULL(11, 8)
+#define RISCV_IOMMU_REG_IVEC_PIV GENMASK_ULL(15, 12)
+
+#define RISCV_IOMMU_REG_CQB 0x0018
+#define RISCV_IOMMU_CQB_PPN_START 10
+#define RISCV_IOMMU_CQB_PPN_LEN 44
+#define RISCV_IOMMU_CQB_LOG2SZ_START 0
+#define RISCV_IOMMU_CQB_LOG2SZ_LEN 5
+
+#define RISCV_IOMMU_REG_CQT 0x0024
+
+#define RISCV_IOMMU_REG_FQB 0x0028
+#define RISCV_IOMMU_FQB_PPN_START 10
+#define RISCV_IOMMU_FQB_PPN_LEN 44
+#define RISCV_IOMMU_FQB_LOG2SZ_START 0
+#define RISCV_IOMMU_FQB_LOG2SZ_LEN 5
+
+#define RISCV_IOMMU_REG_FQT 0x0034
+
+#define RISCV_IOMMU_REG_PQB 0x0038
+#define RISCV_IOMMU_PQB_PPN_START 10
+#define RISCV_IOMMU_PQB_PPN_LEN 44
+#define RISCV_IOMMU_PQB_LOG2SZ_START 0
+#define RISCV_IOMMU_PQB_LOG2SZ_LEN 5
+
+#define RISCV_IOMMU_REG_PQT 0x0044
+
+typedef struct QRISCVIOMMU {
+ QOSGraphObject obj;
+ QPCIDevice dev;
+ QPCIBar reg_bar;
+} QRISCVIOMMU;
+
+#endif
diff --git a/tests/qtest/libqos/sdhci-cmd.h b/tests/qtest/libqos/sdhci-cmd.h
index 9e61dd4..90efa02 100644
--- a/tests/qtest/libqos/sdhci-cmd.h
+++ b/tests/qtest/libqos/sdhci-cmd.h
@@ -22,6 +22,7 @@
#define SDHC_ARGUMENT 0x08
#define SDHC_TRNMOD 0x0C
#define SDHC_CMDREG 0x0E
+#define SDHC_RSPREG0 0x10
#define SDHC_BDATA 0x20
#define SDHC_PRNSTS 0x24
#define SDHC_BLKGAP 0x2A
@@ -38,6 +39,7 @@
#define SDHC_TRNS_MULTI 0x0020
/* CMD Reg */
+#define SDHC_CMD_RESPONSE (3 << 0)
#define SDHC_CMD_DATA_PRESENT (1 << 5)
#define SDHC_ALL_SEND_CID (2 << 8)
#define SDHC_SEND_RELATIVE_ADDR (3 << 8)
diff --git a/tests/qtest/libqos/virtio-9p-client.c b/tests/qtest/libqos/virtio-9p-client.c
index b8adc8d..6ab4501 100644
--- a/tests/qtest/libqos/virtio-9p-client.c
+++ b/tests/qtest/libqos/virtio-9p-client.c
@@ -235,10 +235,11 @@ static const char *rmessage_name(uint8_t id)
id == P9_RMKDIR ? "RMKDIR" :
id == P9_RLCREATE ? "RLCREATE" :
id == P9_RSYMLINK ? "RSYMLINK" :
+ id == P9_RGETATTR ? "RGETATTR" :
id == P9_RLINK ? "RLINK" :
id == P9_RUNLINKAT ? "RUNLINKAT" :
id == P9_RFLUSH ? "RFLUSH" :
- id == P9_RREADDIR ? "READDIR" :
+ id == P9_RREADDIR ? "RREADDIR" :
"<unknown>";
}
@@ -556,6 +557,55 @@ void v9fs_rgetattr(P9Req *req, v9fs_attr *attr)
v9fs_req_free(req);
}
+/*
+ * size[4] Tsetattr tag[2] fid[4] valid[4] mode[4] uid[4] gid[4] size[8]
+ * atime_sec[8] atime_nsec[8] mtime_sec[8] mtime_nsec[8]
+ */
+TSetAttrRes v9fs_tsetattr(TSetAttrOpt opt)
+{
+ P9Req *req;
+ uint32_t err;
+
+ g_assert(opt.client);
+
+ req = v9fs_req_init(
+ opt.client, 4/*fid*/ + 4/*valid*/ + 4/*mode*/ + 4/*uid*/ + 4/*gid*/ +
+ 8/*size*/ + 8/*atime_sec*/ + 8/*atime_nsec*/ + 8/*mtime_sec*/ +
+ 8/*mtime_nsec*/, P9_TSETATTR, opt.tag
+ );
+ v9fs_uint32_write(req, opt.fid);
+ v9fs_uint32_write(req, (uint32_t) opt.attr.valid);
+ v9fs_uint32_write(req, opt.attr.mode);
+ v9fs_uint32_write(req, opt.attr.uid);
+ v9fs_uint32_write(req, opt.attr.gid);
+ v9fs_uint64_write(req, opt.attr.size);
+ v9fs_uint64_write(req, opt.attr.atime_sec);
+ v9fs_uint64_write(req, opt.attr.atime_nsec);
+ v9fs_uint64_write(req, opt.attr.mtime_sec);
+ v9fs_uint64_write(req, opt.attr.mtime_nsec);
+ v9fs_req_send(req);
+
+ if (!opt.requestOnly) {
+ v9fs_req_wait_for_reply(req, NULL);
+ if (opt.expectErr) {
+ v9fs_rlerror(req, &err);
+ g_assert_cmpint(err, ==, opt.expectErr);
+ } else {
+ v9fs_rsetattr(req);
+ }
+ req = NULL; /* request was freed */
+ }
+
+ return (TSetAttrRes) { .req = req };
+}
+
+/* size[4] Rsetattr tag[2] */
+void v9fs_rsetattr(P9Req *req)
+{
+ v9fs_req_recv(req, P9_RSETATTR);
+ v9fs_req_free(req);
+}
+
/* size[4] Treaddir tag[2] fid[4] offset[8] count[4] */
TReadDirRes v9fs_treaddir(TReadDirOpt opt)
{
diff --git a/tests/qtest/libqos/virtio-9p-client.h b/tests/qtest/libqos/virtio-9p-client.h
index 78228eb..e3221a3 100644
--- a/tests/qtest/libqos/virtio-9p-client.h
+++ b/tests/qtest/libqos/virtio-9p-client.h
@@ -65,6 +65,16 @@ typedef struct v9fs_attr {
#define P9_GETATTR_BASIC 0x000007ffULL /* Mask for fields up to BLOCKS */
#define P9_GETATTR_ALL 0x00003fffULL /* Mask for ALL fields */
+#define P9_SETATTR_MODE 0x00000001UL
+#define P9_SETATTR_UID 0x00000002UL
+#define P9_SETATTR_GID 0x00000004UL
+#define P9_SETATTR_SIZE 0x00000008UL
+#define P9_SETATTR_ATIME 0x00000010UL
+#define P9_SETATTR_MTIME 0x00000020UL
+#define P9_SETATTR_CTIME 0x00000040UL
+#define P9_SETATTR_ATIME_SET 0x00000080UL
+#define P9_SETATTR_MTIME_SET 0x00000100UL
+
struct V9fsDirent {
v9fs_qid qid;
uint64_t offset;
@@ -182,6 +192,28 @@ typedef struct TGetAttrRes {
P9Req *req;
} TGetAttrRes;
+/* options for 'Tsetattr' 9p request */
+typedef struct TSetAttrOpt {
+ /* 9P client being used (mandatory) */
+ QVirtio9P *client;
+ /* user supplied tag number being returned with response (optional) */
+ uint16_t tag;
+ /* file ID of file/dir whose attributes shall be modified (required) */
+ uint32_t fid;
+ /* new attribute values to be set by 9p server */
+ v9fs_attr attr;
+ /* only send Tsetattr request but not wait for a reply? (optional) */
+ bool requestOnly;
+ /* do we expect an Rlerror response, if yes which error code? (optional) */
+ uint32_t expectErr;
+} TSetAttrOpt;
+
+/* result of 'Tsetattr' 9p request */
+typedef struct TSetAttrRes {
+ /* if requestOnly was set: request object for further processing */
+ P9Req *req;
+} TSetAttrRes;
+
/* options for 'Treaddir' 9p request */
typedef struct TReadDirOpt {
/* 9P client being used (mandatory) */
@@ -470,6 +502,8 @@ TWalkRes v9fs_twalk(TWalkOpt opt);
void v9fs_rwalk(P9Req *req, uint16_t *nwqid, v9fs_qid **wqid);
TGetAttrRes v9fs_tgetattr(TGetAttrOpt);
void v9fs_rgetattr(P9Req *req, v9fs_attr *attr);
+TSetAttrRes v9fs_tsetattr(TSetAttrOpt opt);
+void v9fs_rsetattr(P9Req *req);
TReadDirRes v9fs_treaddir(TReadDirOpt);
void v9fs_rreaddir(P9Req *req, uint32_t *count, uint32_t *nentries,
struct V9fsDirent **entries);
diff --git a/tests/qtest/libqos/virtio-pci-modern.c b/tests/qtest/libqos/virtio-pci-modern.c
index 18d1188..4e67fcb 100644
--- a/tests/qtest/libqos/virtio-pci-modern.c
+++ b/tests/qtest/libqos/virtio-pci-modern.c
@@ -173,13 +173,11 @@ static bool get_config_isr_status(QVirtioDevice *d)
static void wait_config_isr_status(QVirtioDevice *d, gint64 timeout_us)
{
- QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
gint64 start_time = g_get_monotonic_time();
- do {
+ while (!get_config_isr_status(d)) {
g_assert(g_get_monotonic_time() - start_time <= timeout_us);
- qtest_clock_step(dev->pdev->bus->qts, 100);
- } while (!get_config_isr_status(d));
+ }
}
static void queue_select(QVirtioDevice *d, uint16_t index)
diff --git a/tests/qtest/libqos/virtio-pci.c b/tests/qtest/libqos/virtio-pci.c
index 485b8f6..002bf8b 100644
--- a/tests/qtest/libqos/virtio-pci.c
+++ b/tests/qtest/libqos/virtio-pci.c
@@ -171,13 +171,11 @@ static bool qvirtio_pci_get_config_isr_status(QVirtioDevice *d)
static void qvirtio_pci_wait_config_isr_status(QVirtioDevice *d,
gint64 timeout_us)
{
- QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
gint64 start_time = g_get_monotonic_time();
- do {
+ while (!qvirtio_pci_get_config_isr_status(d)) {
g_assert(g_get_monotonic_time() - start_time <= timeout_us);
- qtest_clock_step(dev->pdev->bus->qts, 100);
- } while (!qvirtio_pci_get_config_isr_status(d));
+ }
}
static void qvirtio_pci_queue_select(QVirtioDevice *d, uint16_t index)
diff --git a/tests/qtest/libqos/virtio-scmi.c b/tests/qtest/libqos/virtio-scmi.c
index ce8f4d5..6b5bd4d 100644
--- a/tests/qtest/libqos/virtio-scmi.c
+++ b/tests/qtest/libqos/virtio-scmi.c
@@ -1,7 +1,7 @@
/*
* virtio-scmi nodes for testing
*
- * SPDX-FileCopyrightText: Linaro Ltd
+ * Copyright (c) Linaro Ltd.
* SPDX-FileCopyrightText: Red Hat, Inc.
* SPDX-License-Identifier: GPL-2.0-or-later
*
diff --git a/tests/qtest/libqos/virtio.c b/tests/qtest/libqos/virtio.c
index a21b6ee..5a709d0 100644
--- a/tests/qtest/libqos/virtio.c
+++ b/tests/qtest/libqos/virtio.c
@@ -25,49 +25,63 @@
*/
static uint16_t qvirtio_readw(QVirtioDevice *d, QTestState *qts, uint64_t addr)
{
- uint16_t val = qtest_readw(qts, addr);
+ uint16_t val;
- if (d->features & (1ull << VIRTIO_F_VERSION_1) && qtest_big_endian(qts)) {
- val = bswap16(val);
+ if (d->features & (1ull << VIRTIO_F_VERSION_1)) {
+ qtest_memread(qts, addr, &val, sizeof(val));
+ val = le16_to_cpu(val);
+ } else {
+ val = qtest_readw(qts, addr);
}
+
return val;
}
static uint32_t qvirtio_readl(QVirtioDevice *d, QTestState *qts, uint64_t addr)
{
- uint32_t val = qtest_readl(qts, addr);
+ uint32_t val;
- if (d->features & (1ull << VIRTIO_F_VERSION_1) && qtest_big_endian(qts)) {
- val = bswap32(val);
+ if (d->features & (1ull << VIRTIO_F_VERSION_1)) {
+ qtest_memread(qts, addr, &val, sizeof(val));
+ val = le32_to_cpu(val);
+ } else {
+ val = qtest_readl(qts, addr);
}
+
return val;
}
static void qvirtio_writew(QVirtioDevice *d, QTestState *qts,
uint64_t addr, uint16_t val)
{
- if (d->features & (1ull << VIRTIO_F_VERSION_1) && qtest_big_endian(qts)) {
- val = bswap16(val);
+ if (d->features & (1ull << VIRTIO_F_VERSION_1)) {
+ val = cpu_to_le16(val);
+ qtest_memwrite(qts, addr, &val, sizeof(val));
+ } else {
+ qtest_writew(qts, addr, val);
}
- qtest_writew(qts, addr, val);
}
static void qvirtio_writel(QVirtioDevice *d, QTestState *qts,
uint64_t addr, uint32_t val)
{
- if (d->features & (1ull << VIRTIO_F_VERSION_1) && qtest_big_endian(qts)) {
- val = bswap32(val);
+ if (d->features & (1ull << VIRTIO_F_VERSION_1)) {
+ val = cpu_to_le32(val);
+ qtest_memwrite(qts, addr, &val, sizeof(val));
+ } else {
+ qtest_writel(qts, addr, val);
}
- qtest_writel(qts, addr, val);
}
static void qvirtio_writeq(QVirtioDevice *d, QTestState *qts,
uint64_t addr, uint64_t val)
{
- if (d->features & (1ull << VIRTIO_F_VERSION_1) && qtest_big_endian(qts)) {
- val = bswap64(val);
+ if (d->features & (1ull << VIRTIO_F_VERSION_1)) {
+ val = cpu_to_le64(val);
+ qtest_memwrite(qts, addr, &val, sizeof(val));
+ } else {
+ qtest_writeq(qts, addr, val);
}
- qtest_writeq(qts, addr, val);
}
uint8_t qvirtio_config_readb(QVirtioDevice *d, uint64_t addr)
@@ -170,7 +184,6 @@ void qvirtio_wait_queue_isr(QTestState *qts, QVirtioDevice *d,
gint64 start_time = g_get_monotonic_time();
for (;;) {
- qtest_clock_step(qts, 100);
if (d->bus->get_queue_isr_status(d, vq)) {
return;
}
@@ -192,7 +205,6 @@ uint8_t qvirtio_wait_status_byte_no_isr(QTestState *qts, QVirtioDevice *d,
uint8_t val;
while ((val = qtest_readb(qts, addr)) == 0xff) {
- qtest_clock_step(qts, 100);
g_assert(!d->bus->get_queue_isr_status(d, vq));
g_assert(g_get_monotonic_time() - start_time <= timeout_us);
}
@@ -219,14 +231,12 @@ void qvirtio_wait_used_elem(QTestState *qts, QVirtioDevice *d,
for (;;) {
uint32_t got_desc_idx;
- qtest_clock_step(qts, 100);
if (d->bus->get_queue_isr_status(d, vq) &&
qvirtqueue_get_buf(qts, vq, &got_desc_idx, len)) {
g_assert_cmpint(got_desc_idx, ==, desc_idx);
return;
}
-
g_assert(g_get_monotonic_time() - start_time <= timeout_us);
}
}
diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c
index c7f6897..94526b7 100644
--- a/tests/qtest/libqtest.c
+++ b/tests/qtest/libqtest.c
@@ -30,14 +30,15 @@
#include "libqtest.h"
#include "libqmp.h"
+#include "qemu/accel.h"
#include "qemu/ctype.h"
#include "qemu/cutils.h"
#include "qemu/sockets.h"
-#include "qapi/qmp/qdict.h"
-#include "qapi/qmp/qjson.h"
-#include "qapi/qmp/qlist.h"
-#include "qapi/qmp/qstring.h"
-#include "qapi/qmp/qbool.h"
+#include "qobject/qdict.h"
+#include "qobject/qjson.h"
+#include "qobject/qlist.h"
+#include "qobject/qstring.h"
+#include "qobject/qbool.h"
#define MAX_IRQ 256
@@ -75,6 +76,8 @@ struct QTestState
{
int fd;
int qmp_fd;
+ int sock;
+ int qmpsock;
pid_t qemu_pid; /* our child QEMU process */
int wstatus;
#ifdef _WIN32
@@ -215,6 +218,22 @@ static void qtest_check_status(QTestState *s)
#endif
}
+void qtest_system_reset_nowait(QTestState *s)
+{
+ /* Request the system reset, but do not wait for it to complete */
+ qtest_qmp_assert_success(s, "{'execute': 'system_reset' }");
+}
+
+void qtest_system_reset(QTestState *s)
+{
+ qtest_system_reset_nowait(s);
+ /*
+ * Wait for the RESET event, which is sent once the system reset
+ * has actually completed.
+ */
+ qtest_qmp_eventwait(s, "RESET");
+}
+
void qtest_wait_qemu(QTestState *s)
{
if (s->qemu_pid != -1) {
@@ -442,18 +461,19 @@ static QTestState *G_GNUC_PRINTF(2, 3) qtest_spawn_qemu(const char *qemu_bin,
return s;
}
+static char *qtest_socket_path(const char *suffix)
+{
+ return g_strdup_printf("%s/qtest-%d.%s", g_get_tmp_dir(), getpid(), suffix);
+}
+
static QTestState *qtest_init_internal(const char *qemu_bin,
- const char *extra_args)
+ const char *extra_args,
+ bool do_connect)
{
QTestState *s;
int sock, qmpsock, i;
- gchar *socket_path;
- gchar *qmp_socket_path;
-
- socket_path = g_strdup_printf("%s/qtest-%d.sock",
- g_get_tmp_dir(), getpid());
- qmp_socket_path = g_strdup_printf("%s/qtest-%d.qmp",
- g_get_tmp_dir(), getpid());
+ g_autofree gchar *socket_path = qtest_socket_path("sock");
+ g_autofree gchar *qmp_socket_path = qtest_socket_path("qmp");
/*
* It's possible that if an earlier test run crashed it might
@@ -485,22 +505,19 @@ static QTestState *qtest_init_internal(const char *qemu_bin,
qtest_client_set_rx_handler(s, qtest_client_socket_recv_line);
qtest_client_set_tx_handler(s, qtest_client_socket_send);
- s->fd = socket_accept(sock);
- if (s->fd >= 0) {
- s->qmp_fd = socket_accept(qmpsock);
- }
- unlink(socket_path);
- unlink(qmp_socket_path);
- g_free(socket_path);
- g_free(qmp_socket_path);
-
- g_assert(s->fd >= 0 && s->qmp_fd >= 0);
-
s->rx = g_string_new("");
for (i = 0; i < MAX_IRQ; i++) {
s->irq_level[i] = false;
}
+ s->fd = -1;
+ s->qmp_fd = -1;
+ s->sock = sock;
+ s->qmpsock = qmpsock;
+ if (do_connect) {
+ qtest_connect(s);
+ }
+
/*
* Stopping QEMU for debugging is not supported on Windows.
*
@@ -514,40 +531,70 @@ static QTestState *qtest_init_internal(const char *qemu_bin,
kill(s->qemu_pid, SIGSTOP);
}
#endif
- return s;
+
+ return s;
}
-QTestState *qtest_init_without_qmp_handshake(const char *extra_args)
+void qtest_connect(QTestState *s)
{
- return qtest_init_internal(qtest_qemu_binary(NULL), extra_args);
+ g_autofree gchar *socket_path = qtest_socket_path("sock");
+ g_autofree gchar *qmp_socket_path = qtest_socket_path("qmp");
+
+ g_assert(s->sock >= 0 && s->qmpsock >= 0);
+ s->fd = socket_accept(s->sock);
+ if (s->fd >= 0) {
+ s->qmp_fd = socket_accept(s->qmpsock);
+ }
+ unlink(socket_path);
+ unlink(qmp_socket_path);
+ g_assert(s->fd >= 0 && s->qmp_fd >= 0);
+ s->sock = s->qmpsock = -1;
+ /* ask endianness of the target */
+ s->big_endian = qtest_query_target_endianness(s);
}
-QTestState *qtest_init_with_env_no_handshake(const char *var,
- const char *extra_args)
+QTestState *qtest_init_without_qmp_handshake(const char *extra_args)
{
- return qtest_init_internal(qtest_qemu_binary(var), extra_args);
+ return qtest_init_internal(qtest_qemu_binary(NULL), extra_args, true);
}
-QTestState *qtest_init_with_env(const char *var, const char *extra_args)
+void qtest_qmp_handshake(QTestState *s, QList *capabilities)
{
- QTestState *s = qtest_init_internal(qtest_qemu_binary(var), extra_args);
- QDict *greeting;
-
- /* ask endianness of the target */
-
- s->big_endian = qtest_query_target_endianness(s);
-
/* Read the QMP greeting and then do the handshake */
- greeting = qtest_qmp_receive(s);
+ QDict *greeting = qtest_qmp_receive(s);
qobject_unref(greeting);
- qobject_unref(qtest_qmp(s, "{ 'execute': 'qmp_capabilities' }"));
+ if (capabilities) {
+ qtest_qmp_assert_success(s,
+ "{ 'execute': 'qmp_capabilities', "
+ "'arguments': { 'enable': %p } }",
+ qobject_ref(capabilities));
+ } else {
+ qtest_qmp_assert_success(s, "{ 'execute': 'qmp_capabilities' }");
+ }
+}
+
+QTestState *qtest_init_ext(const char *var, const char *extra_args,
+ QList *capabilities, bool do_connect)
+{
+ QTestState *s = qtest_init_internal(qtest_qemu_binary(var), extra_args,
+ do_connect);
+
+ if (do_connect) {
+ qtest_qmp_handshake(s, capabilities);
+ } else {
+ /*
+ * If the connection is delayed, the capabilities must be set
+ * at that moment.
+ */
+ assert(!capabilities);
+ }
return s;
}
QTestState *qtest_init(const char *extra_args)
{
- return qtest_init_with_env(NULL, extra_args);
+ return qtest_init_ext(NULL, extra_args, NULL, true);
}
QTestState *qtest_vinitf(const char *fmt, va_list ap)
@@ -749,12 +796,15 @@ QDict *qtest_qmp_receive(QTestState *s)
response, s->eventData)) {
/* Stash the event for a later consumption */
s->pending_events = g_list_append(s->pending_events, response);
+ } else {
+ qobject_unref(response);
}
}
}
QDict *qtest_qmp_receive_dict(QTestState *s)
{
+ g_assert(s->qmp_fd >= 0);
return qmp_fd_receive(s->qmp_fd);
}
@@ -782,12 +832,14 @@ int qtest_socket_server(const char *socket_path)
void qtest_qmp_vsend_fds(QTestState *s, int *fds, size_t fds_num,
const char *fmt, va_list ap)
{
+ g_assert(s->qmp_fd >= 0);
qmp_fd_vsend_fds(s->qmp_fd, fds, fds_num, fmt, ap);
}
#endif
void qtest_qmp_vsend(QTestState *s, const char *fmt, va_list ap)
{
+ g_assert(s->qmp_fd >= 0);
qmp_fd_vsend(s->qmp_fd, fmt, ap);
}
@@ -848,6 +900,7 @@ void qtest_qmp_send_raw(QTestState *s, const char *fmt, ...)
{
va_list ap;
+ g_assert(s->qmp_fd >= 0);
va_start(ap, fmt);
qmp_fd_vsend_raw(s->qmp_fd, fmt, ap);
va_end(ap);
@@ -951,15 +1004,62 @@ const char *qtest_get_arch(void)
return end + 1;
}
+static bool qtest_qom_has_concrete_type(const char *parent_typename,
+ const char *child_typename,
+ QList **cached_list)
+{
+ QList *list = cached_list ? *cached_list : NULL;
+ const QListEntry *p;
+ QObject *qobj;
+ QString *qstr;
+ QDict *devinfo;
+ int idx;
+
+ if (!list) {
+ QDict *resp;
+ QDict *args;
+ QTestState *qts = qtest_init("-machine none");
+
+ args = qdict_new();
+ qdict_put_bool(args, "abstract", false);
+ qdict_put_str(args, "implements", parent_typename);
+
+ resp = qtest_qmp(qts, "{'execute': 'qom-list-types', 'arguments': %p }",
+ args);
+ g_assert(qdict_haskey(resp, "return"));
+ list = qdict_get_qlist(resp, "return");
+ qobject_ref(list);
+ qobject_unref(resp);
+
+ qtest_quit(qts);
+
+ if (cached_list) {
+ *cached_list = list;
+ }
+ }
+
+ for (p = qlist_first(list), idx = 0; p; p = qlist_next(p), idx++) {
+ devinfo = qobject_to(QDict, qlist_entry_obj(p));
+ g_assert(devinfo);
+
+ qobj = qdict_get(devinfo, "name");
+ g_assert(qobj);
+ qstr = qobject_to(QString, qobj);
+ g_assert(qstr);
+ if (g_str_equal(qstring_get_str(qstr), child_typename)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
bool qtest_has_accel(const char *accel_name)
{
- if (g_str_equal(accel_name, "tcg")) {
-#if defined(CONFIG_TCG)
- return true;
-#else
- return false;
-#endif
- } else if (g_str_equal(accel_name, "kvm")) {
+ static QList *list;
+ g_autofree char *accel_type = NULL;
+
+ if (g_str_equal(accel_name, "kvm")) {
int i;
const char *arch = qtest_get_arch();
const char *targets[] = { CONFIG_KVM_TARGETS };
@@ -971,11 +1071,12 @@ bool qtest_has_accel(const char *accel_name)
}
}
}
- } else {
- /* not implemented */
- g_assert_not_reached();
+ return false;
}
- return false;
+
+ accel_type = g_strconcat(accel_name, ACCEL_CLASS_SUFFIX, NULL);
+
+ return qtest_qom_has_concrete_type("accel", accel_type, &list);
}
bool qtest_get_irq(QTestState *s, int num)
@@ -1205,6 +1306,33 @@ uint64_t qtest_rtas_call(QTestState *s, const char *name,
return 0;
}
+static void qtest_rsp_csr(QTestState *s, uint64_t *val)
+{
+ gchar **args;
+ uint64_t ret;
+ int rc;
+
+ args = qtest_rsp_args(s, 3);
+
+ rc = qemu_strtou64(args[1], NULL, 16, &ret);
+ g_assert(rc == 0);
+ rc = qemu_strtou64(args[2], NULL, 16, val);
+ g_assert(rc == 0);
+
+ g_strfreev(args);
+}
+
+uint64_t qtest_csr_call(QTestState *s, const char *name,
+ uint64_t cpu, int csr,
+ uint64_t *val)
+{
+ qtest_sendf(s, "csr %s 0x%"PRIx64" %d 0x%"PRIx64"\n",
+ name, cpu, csr, *val);
+
+ qtest_rsp_csr(s, val);
+ return 0;
+}
+
void qtest_add_func(const char *str, void (*fn)(void))
{
gchar *path = g_strdup_printf("/%s/%s", qtest_get_arch(), str);
@@ -1512,6 +1640,7 @@ static struct MachInfo *qtest_get_machines(const char *var)
int idx;
if (g_strcmp0(qemu_var, var)) {
+ g_free(qemu_var);
qemu_var = g_strdup(var);
/* new qemu, clear the cache */
@@ -1525,7 +1654,7 @@ static struct MachInfo *qtest_get_machines(const char *var)
silence_spawn_log = !g_test_verbose();
- qts = qtest_init_with_env(qemu_var, "-machine none");
+ qts = qtest_init_ext(qemu_var, "-machine none", NULL, true);
response = qtest_qmp(qts, "{ 'execute': 'query-machines' }");
g_assert(response);
list = qdict_get_qlist(response, "return");
@@ -1580,7 +1709,7 @@ static struct CpuModel *qtest_get_cpu_models(void)
silence_spawn_log = !g_test_verbose();
- qts = qtest_init_with_env(NULL, "-machine none");
+ qts = qtest_init_ext(NULL, "-machine none", NULL, true);
response = qtest_qmp(qts, "{ 'execute': 'query-cpu-definitions' }");
g_assert(response);
list = qdict_get_qlist(response, "return");
@@ -1650,7 +1779,9 @@ void qtest_cb_for_every_machine(void (*cb)(const char *machine),
/* Ignore machines that cannot be used for qtests */
if (!strncmp("xenfv", machines[i].name, 5) ||
g_str_equal("xenpv", machines[i].name) ||
- g_str_equal("xenpvh", machines[i].name)) {
+ g_str_equal("xenpvh", machines[i].name) ||
+ g_str_equal("vmapple", machines[i].name) ||
+ g_str_equal("nitro-enclave", machines[i].name)) {
continue;
}
if (!skip_old_versioned ||
@@ -1701,45 +1832,8 @@ bool qtest_has_machine(const char *machine)
bool qtest_has_device(const char *device)
{
static QList *list;
- const QListEntry *p;
- QObject *qobj;
- QString *qstr;
- QDict *devinfo;
- int idx;
-
- if (!list) {
- QDict *resp;
- QDict *args;
- QTestState *qts = qtest_init("-machine none");
-
- args = qdict_new();
- qdict_put_bool(args, "abstract", false);
- qdict_put_str(args, "implements", "device");
-
- resp = qtest_qmp(qts, "{'execute': 'qom-list-types', 'arguments': %p }",
- args);
- g_assert(qdict_haskey(resp, "return"));
- list = qdict_get_qlist(resp, "return");
- qobject_ref(list);
- qobject_unref(resp);
-
- qtest_quit(qts);
- }
-
- for (p = qlist_first(list), idx = 0; p; p = qlist_next(p), idx++) {
- devinfo = qobject_to(QDict, qlist_entry_obj(p));
- g_assert(devinfo);
- qobj = qdict_get(devinfo, "name");
- g_assert(qobj);
- qstr = qobject_to(QString, qobj);
- g_assert(qstr);
- if (g_str_equal(qstring_get_str(qstr), device)) {
- return true;
- }
- }
-
- return false;
+ return qtest_qom_has_concrete_type("device", device, &list);
}
/*
@@ -1920,7 +2014,6 @@ void qtest_client_inproc_recv(void *opaque, const char *str)
qts->rx = g_string_new(NULL);
}
g_string_append(qts->rx, str);
- return;
}
void qtest_qom_set_bool(QTestState *s, const char *path, const char *property,
diff --git a/tests/qtest/libqtest.h b/tests/qtest/libqtest.h
index c261b7e..b3f2e7f 100644
--- a/tests/qtest/libqtest.h
+++ b/tests/qtest/libqtest.h
@@ -17,8 +17,9 @@
#ifndef LIBQTEST_H
#define LIBQTEST_H
-#include "qapi/qmp/qobject.h"
-#include "qapi/qmp/qdict.h"
+#include "qobject/qobject.h"
+#include "qobject/qdict.h"
+#include "qobject/qlist.h"
#include "libqmp.h"
typedef struct QTestState QTestState;
@@ -56,20 +57,22 @@ QTestState *qtest_vinitf(const char *fmt, va_list ap) G_GNUC_PRINTF(1, 0);
QTestState *qtest_init(const char *extra_args);
/**
- * qtest_init_with_env:
+ * qtest_init_ext:
* @var: Environment variable from where to take the QEMU binary
* @extra_args: Other arguments to pass to QEMU. CAUTION: these
* arguments are subject to word splitting and shell evaluation.
+ * @capabilities: list of QMP capabilities (strings) to enable
+ * @do_connect: connect to qemu monitor and qtest socket.
*
* Like qtest_init(), but use a different environment variable for the
- * QEMU binary.
+ * QEMU binary, allow specify capabilities and skip connecting
+ * to QEMU monitor.
*
* Returns: #QTestState instance.
*/
-QTestState *qtest_init_with_env(const char *var, const char *extra_args);
+QTestState *qtest_init_ext(const char *var, const char *extra_args,
+ QList *capabilities, bool do_connect);
-QTestState *qtest_init_with_env_no_handshake(const char *var,
- const char *extra_args);
/**
* qtest_init_without_qmp_handshake:
* @extra_args: other arguments to pass to QEMU. CAUTION: these
@@ -80,6 +83,22 @@ QTestState *qtest_init_with_env_no_handshake(const char *var,
QTestState *qtest_init_without_qmp_handshake(const char *extra_args);
/**
+ * qtest_connect
+ * @s: #QTestState instance to connect
+ * Connect to qemu monitor and qtest socket, after skipping them in
+ * qtest_init_ext. Does not handshake with the monitor.
+ */
+void qtest_connect(QTestState *s);
+
+/**
+ * qtest_qmp_handshake:
+ * @s: #QTestState instance to operate on.
+ * @capabilities: list of QMP capabilities (strings) to enable
+ * Perform handshake after connecting to qemu monitor.
+ */
+void qtest_qmp_handshake(QTestState *s, QList *capabilities);
+
+/**
* qtest_init_with_serial:
* @extra_args: other arguments to pass to QEMU. CAUTION: these
* arguments are subject to word splitting and shell evaluation.
@@ -91,6 +110,31 @@ QTestState *qtest_init_without_qmp_handshake(const char *extra_args);
QTestState *qtest_init_with_serial(const char *extra_args, int *sock_fd);
/**
+ * qtest_system_reset:
+ * @s: #QTestState instance to operate on.
+ *
+ * Send a "system_reset" command to the QEMU under test, and wait for
+ * the reset to complete before returning.
+ */
+void qtest_system_reset(QTestState *s);
+
+/**
+ * qtest_system_reset_nowait:
+ * @s: #QTestState instance to operate on.
+ *
+ * Send a "system_reset" command to the QEMU under test, but do not
+ * wait for the reset to complete before returning. The caller is
+ * responsible for waiting for either the RESET event or some other
+ * event of interest to them before proceeding.
+ *
+ * This function should only be used if you're specifically testing
+ * for some other event; in that case you can't use qtest_system_reset()
+ * because it will read and discard any other QMP events that arrive
+ * before the RESET event.
+ */
+void qtest_system_reset_nowait(QTestState *s);
+
+/**
* qtest_wait_qemu:
* @s: #QTestState instance to operate on.
*
@@ -342,7 +386,7 @@ QDict *qtest_qmp_event_ref(QTestState *s, const char *event);
char *qtest_hmp(QTestState *s, const char *fmt, ...) G_GNUC_PRINTF(2, 3);
/**
- * qtest_hmpv:
+ * qtest_vhmp:
* @s: #QTestState instance to operate on.
* @fmt: HMP command to send to QEMU, formats arguments like vsprintf().
* @ap: HMP command arguments
@@ -578,6 +622,20 @@ uint64_t qtest_rtas_call(QTestState *s, const char *name,
uint32_t nret, uint64_t ret);
/**
+ * qtest_csr_call:
+ * @s: #QTestState instance to operate on.
+ * @name: name of the command to call.
+ * @cpu: hart number.
+ * @csr: CSR number.
+ * @val: Value for reading/writing.
+ *
+ * Call an RISC-V CSR read/write function
+ */
+uint64_t qtest_csr_call(QTestState *s, const char *name,
+ uint64_t cpu, int csr,
+ uint64_t *val);
+
+/**
* qtest_bufread:
* @s: #QTestState instance to operate on.
* @addr: Guest address to read from.
@@ -881,7 +939,7 @@ void qtest_qmp_assert_success(QTestState *qts, const char *fmt, ...)
#ifndef _WIN32
/**
- * qtest_qmp_fd_assert_success_ref:
+ * qtest_qmp_fds_assert_success_ref:
* @qts: QTestState instance to operate on
* @fds: the file descriptors to send
* @nfds: number of @fds to send
@@ -898,7 +956,7 @@ QDict *qtest_qmp_fds_assert_success_ref(QTestState *qts, int *fds, size_t nfds,
G_GNUC_PRINTF(4, 5);
/**
- * qtest_qmp_fd_assert_success:
+ * qtest_qmp_fds_assert_success:
* @qts: QTestState instance to operate on
* @fds: the file descriptors to send
* @nfds: number of @fds to send
diff --git a/tests/qtest/lsm303dlhc-mag-test.c b/tests/qtest/lsm303dlhc-mag-test.c
index 0f64e7f..55ef459 100644
--- a/tests/qtest/lsm303dlhc-mag-test.c
+++ b/tests/qtest/lsm303dlhc-mag-test.c
@@ -13,7 +13,7 @@
#include "libqtest-single.h"
#include "libqos/qgraph.h"
#include "libqos/i2c.h"
-#include "qapi/qmp/qdict.h"
+#include "qobject/qdict.h"
#define LSM303DLHC_MAG_TEST_ID "lsm303dlhc_mag-test"
#define LSM303DLHC_MAG_REG_CRA 0x00
diff --git a/tests/qtest/m48t59-test.c b/tests/qtest/m48t59-test.c
index 605797a..1e39a0e 100644
--- a/tests/qtest/m48t59-test.c
+++ b/tests/qtest/m48t59-test.c
@@ -247,11 +247,6 @@ static void base_setup(void)
base_year = 1968;
base_machine = "SS-5";
use_mmio = true;
- } else if (g_str_equal(arch, "ppc") || g_str_equal(arch, "ppc64")) {
- base = 0xF0000000;
- base_year = 1968;
- base_machine = "ref405ep";
- use_mmio = true;
} else {
g_assert_not_reached();
}
diff --git a/tests/qtest/machine-none-test.c b/tests/qtest/machine-none-test.c
index 05da7bc..b6a87d2 100644
--- a/tests/qtest/machine-none-test.c
+++ b/tests/qtest/machine-none-test.c
@@ -14,7 +14,7 @@
#include "qemu/cutils.h"
#include "libqtest.h"
-#include "qapi/qmp/qdict.h"
+#include "qobject/qdict.h"
struct arch2cpu {
@@ -30,7 +30,6 @@ static struct arch2cpu cpus_map[] = {
{ "x86_64", "qemu64,apic-id=0" },
{ "i386", "qemu32,apic-id=0" },
{ "alpha", "ev67" },
- { "cris", "crisv32" },
{ "m68k", "m5206" },
{ "microblaze", "any" },
{ "microblazeel", "any" },
diff --git a/tests/qtest/max34451-test.c b/tests/qtest/max34451-test.c
index dbf6ddc..5e0878c 100644
--- a/tests/qtest/max34451-test.c
+++ b/tests/qtest/max34451-test.c
@@ -11,8 +11,8 @@
#include "libqtest-single.h"
#include "libqos/qgraph.h"
#include "libqos/i2c.h"
-#include "qapi/qmp/qdict.h"
-#include "qapi/qmp/qnum.h"
+#include "qobject/qdict.h"
+#include "qobject/qnum.h"
#include "qemu/bitops.h"
#define TEST_ID "max34451-test"
diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
index 1279294..8ad8490 100644
--- a/tests/qtest/meson.build
+++ b/tests/qtest/meson.build
@@ -1,11 +1,14 @@
slow_qtests = {
+ 'ahci-test': 150,
'aspeed_smc-test': 360,
- 'bios-tables-test' : 610,
+ 'bios-tables-test' : 910,
'cdrom-test' : 610,
'device-introspect-test' : 720,
+ 'ide-test' : 120,
'migration-test' : 480,
'npcm7xx_pwm-test': 300,
'npcm7xx_watchdog_timer-test': 120,
+ 'qmp-cmd-test' : 120,
'qom-test' : 900,
'stm32l4x5_usart-test' : 600,
'test-hmp' : 240,
@@ -49,7 +52,16 @@ qtests_filter = \
qtests_i386 = \
(slirp.found() ? ['pxe-test'] : []) + \
qtests_filter + \
- (have_tools ? ['ahci-test'] : []) + \
+ (config_all_devices.has_key('CONFIG_ACPI_VMGENID') ? ['vmgenid-test'] : []) + \
+ (config_all_devices.has_key('CONFIG_AHCI_ICH9') and have_tools ? ['ahci-test'] : []) + \
+ (config_all_devices.has_key('CONFIG_AHCI_ICH9') ? ['tco-test'] : []) + \
+ (config_all_devices.has_key('CONFIG_FDC_ISA') ? ['fdc-test'] : []) + \
+ (config_all_devices.has_key('CONFIG_I440FX') ? ['fw_cfg-test'] : []) + \
+ (config_all_devices.has_key('CONFIG_FW_CFG_DMA') ? ['vmcoreinfo-test'] : []) + \
+ (config_all_devices.has_key('CONFIG_I440FX') ? ['i440fx-test'] : []) + \
+ (config_all_devices.has_key('CONFIG_I440FX') ? ['ide-test'] : []) + \
+ (config_all_devices.has_key('CONFIG_I440FX') ? ['numa-test'] : []) + \
+ (config_all_devices.has_key('CONFIG_I440FX') ? ['test-x86-cpuid-compat'] : []) + \
(config_all_devices.has_key('CONFIG_ISA_TESTDEV') ? ['endianness-test'] : []) + \
(config_all_devices.has_key('CONFIG_SGA') ? ['boot-serial-test'] : []) + \
(config_all_devices.has_key('CONFIG_ISA_IPMI_KCS') ? ['ipmi-kcs-test'] : []) + \
@@ -63,6 +75,7 @@ qtests_i386 = \
(config_all_devices.has_key('CONFIG_I82801B11') ? ['i82801b11-test'] : []) + \
(config_all_devices.has_key('CONFIG_IOH3420') ? ['ioh3420-test'] : []) + \
(config_all_devices.has_key('CONFIG_LPC_ICH9') ? ['lpc-ich9-test'] : []) + \
+ (config_all_devices.has_key('CONFIG_MC146818RTC') ? ['rtc-test'] : []) + \
(config_all_devices.has_key('CONFIG_USB_UHCI') ? ['usb-hcd-uhci-test'] : []) + \
(config_all_devices.has_key('CONFIG_USB_UHCI') and \
config_all_devices.has_key('CONFIG_USB_EHCI') ? ['usb-hcd-ehci-test'] : []) + \
@@ -76,9 +89,12 @@ qtests_i386 = \
(config_all_devices.has_key('CONFIG_MEGASAS_SCSI_PCI') ? ['fuzz-megasas-test'] : []) + \
(config_all_devices.has_key('CONFIG_LSI_SCSI_PCI') ? ['fuzz-lsi53c895a-test'] : []) + \
(config_all_devices.has_key('CONFIG_VIRTIO_SCSI') ? ['fuzz-virtio-scsi-test'] : []) + \
+ (config_all_devices.has_key('CONFIG_VIRTIO_BALLOON') ? ['virtio-balloon-test'] : []) + \
+ (config_all_devices.has_key('CONFIG_Q35') ? ['q35-test'] : []) + \
(config_all_devices.has_key('CONFIG_SB16') ? ['fuzz-sb16-test'] : []) + \
(config_all_devices.has_key('CONFIG_SDHCI_PCI') ? ['fuzz-sdcard-test'] : []) + \
(config_all_devices.has_key('CONFIG_ESP_PCI') ? ['am53c974-test'] : []) + \
+ (config_all_devices.has_key('CONFIG_VTD') ? ['intel-iommu-test'] : []) + \
(host_os != 'windows' and \
config_all_devices.has_key('CONFIG_ACPI_ERST') ? ['erst-test'] : []) + \
(config_all_devices.has_key('CONFIG_PCIE_PORT') and \
@@ -91,25 +107,16 @@ qtests_i386 = \
config_all_devices.has_key('CONFIG_PARALLEL') ? ['bios-tables-test'] : []) + \
qtests_pci + \
qtests_cxl + \
- ['fdc-test',
- 'ide-test',
+ [
'hd-geo-test',
'boot-order-test',
- 'rtc-test',
- 'i440fx-test',
- 'fw_cfg-test',
'device-plug-test',
'drive_del-test',
- 'tco-test',
'cpu-plug-test',
- 'q35-test',
- 'vmgenid-test',
'migration-test',
- 'test-x86-cpuid-compat',
- 'numa-test'
]
-if dbus_display
+if dbus_display and config_all_devices.has_key('CONFIG_VGA')
qtests_i386 += ['dbus-display-test']
endif
@@ -135,12 +142,14 @@ qtests_alpha = ['boot-serial-test'] + \
qtests_avr = [ 'boot-serial-test' ]
-qtests_hppa = ['boot-serial-test'] + \
+qtests_hppa = \
qtests_filter + \
(config_all_devices.has_key('CONFIG_VGA') ? ['display-vga-test'] : [])
qtests_loongarch64 = qtests_filter + \
- ['boot-serial-test', 'numa-test']
+ (config_all_devices.has_key('CONFIG_LOONGARCH_VIRT') ? ['numa-test'] : []) + \
+ ['boot-serial-test',
+ 'cpu-plug-test']
qtests_m68k = ['boot-serial-test'] + \
qtests_filter
@@ -162,7 +171,6 @@ qtests_mips64el = qtests_mips
qtests_ppc = \
qtests_filter + \
(config_all_devices.has_key('CONFIG_ISA_TESTDEV') ? ['endianness-test'] : []) + \
- (config_all_devices.has_key('CONFIG_M48T59') ? ['m48t59-test'] : []) + \
(config_all_accel.has_key('CONFIG_TCG') ? ['prom-env-test'] : []) + \
(config_all_accel.has_key('CONFIG_TCG') ? ['boot-serial-test'] : []) + \
['boot-order-test']
@@ -171,12 +179,15 @@ qtests_ppc64 = \
qtests_ppc + \
(config_all_devices.has_key('CONFIG_PSERIES') ? ['device-plug-test'] : []) + \
(config_all_devices.has_key('CONFIG_POWERNV') ? ['pnv-xscom-test'] : []) + \
+ (config_all_devices.has_key('CONFIG_POWERNV') ? ['pnv-xive2-test'] : []) + \
+ (config_all_devices.has_key('CONFIG_POWERNV') ? ['pnv-spi-seeprom-test'] : []) + \
(config_all_devices.has_key('CONFIG_POWERNV') ? ['pnv-host-i2c-test'] : []) + \
+ (config_all_devices.has_key('CONFIG_PSERIES') ? ['numa-test'] : []) + \
(config_all_devices.has_key('CONFIG_PSERIES') ? ['rtas-test'] : []) + \
(slirp.found() ? ['pxe-test'] : []) + \
(config_all_devices.has_key('CONFIG_USB_UHCI') ? ['usb-hcd-uhci-test'] : []) + \
(config_all_devices.has_key('CONFIG_USB_XHCI_NEC') ? ['usb-hcd-xhci-test'] : []) + \
- qtests_pci + ['migration-test', 'numa-test', 'cpu-plug-test', 'drive_del-test']
+ qtests_pci + ['migration-test', 'cpu-plug-test', 'drive_del-test']
qtests_sh4 = (config_all_devices.has_key('CONFIG_ISA_TESTDEV') ? ['endianness-test'] : [])
qtests_sh4eb = (config_all_devices.has_key('CONFIG_ISA_TESTDEV') ? ['endianness-test'] : [])
@@ -197,13 +208,18 @@ qtests_npcm7xx = \
'npcm7xx_sdhci-test',
'npcm7xx_smbus-test',
'npcm7xx_timer-test',
- 'npcm7xx_watchdog_timer-test',
- 'npcm_gmac-test'] + \
+ 'npcm7xx_watchdog_timer-test'] + \
(slirp.found() ? ['npcm7xx_emc-test'] : [])
+qtests_npcm8xx = \
+ ['npcm_gmac-test']
qtests_aspeed = \
- ['aspeed_hace-test',
- 'aspeed_smc-test',
- 'aspeed_gpio-test']
+ ['aspeed_gpio-test',
+ 'aspeed_hace-test',
+ 'aspeed_smc-test']
+qtests_aspeed64 = \
+ ['ast2700-gpio-test',
+ 'ast2700-hace-test',
+ 'ast2700-smc-test']
qtests_stm32l4x5 = \
['stm32l4x5_exti-test',
@@ -216,7 +232,8 @@ qtests_arm = \
(config_all_devices.has_key('CONFIG_MPS2') ? ['sse-timer-test'] : []) + \
(config_all_devices.has_key('CONFIG_CMSDK_APB_DUALTIMER') ? ['cmsdk-apb-dualtimer-test'] : []) + \
(config_all_devices.has_key('CONFIG_CMSDK_APB_TIMER') ? ['cmsdk-apb-timer-test'] : []) + \
- (config_all_devices.has_key('CONFIG_CMSDK_APB_WATCHDOG') ? ['cmsdk-apb-watchdog-test'] : []) + \
+ (config_all_devices.has_key('CONFIG_STELLARIS') or
+ config_all_devices.has_key('CONFIG_MPS2') ? ['cmsdk-apb-watchdog-test'] : []) + \
(config_all_devices.has_key('CONFIG_PFLASH_CFI02') and
config_all_devices.has_key('CONFIG_MUSICPAL') ? ['pflash-cfi02-test'] : []) + \
(config_all_devices.has_key('CONFIG_ASPEED_SOC') ? qtests_aspeed : []) + \
@@ -242,6 +259,8 @@ qtests_aarch64 = \
(config_all_devices.has_key('CONFIG_RASPI') ? ['bcm2835-dma-test', 'bcm2835-i2c-test'] : []) + \
(config_all_accel.has_key('CONFIG_TCG') and \
config_all_devices.has_key('CONFIG_TPM_TIS_I2C') ? ['tpm-tis-i2c-test'] : []) + \
+ (config_all_devices.has_key('CONFIG_ASPEED_SOC') ? qtests_aspeed64 : []) + \
+ (config_all_devices.has_key('CONFIG_NPCM8XX') ? qtests_npcm8xx : []) + \
['arm-cpu-features',
'numa-test',
'boot-serial-test',
@@ -259,6 +278,9 @@ qtests_s390x = \
qtests_riscv32 = \
(config_all_devices.has_key('CONFIG_SIFIVE_E_AON') ? ['sifive-e-aon-watchdog-test'] : [])
+qtests_riscv64 = ['riscv-csr-test'] + \
+ (unpack_edk2_blobs ? ['bios-tables-test'] : [])
+
qos_test_ss = ss.source_set()
qos_test_ss.add(
'ac97-test.c',
@@ -268,7 +290,6 @@ qos_test_ss.add(
'e1000-test.c',
'eepro100-test.c',
'es1370-test.c',
- 'ipoctal232-test.c',
'lsm303dlhc-mag-test.c',
'isl_pmbus_vr-test.c',
'max34451-test.c',
@@ -279,6 +300,7 @@ qos_test_ss.add(
'pca9552-test.c',
'pci-test.c',
'pcnet-test.c',
+ 'rs5c372-test.c',
'sdhci-test.c',
'spapr-phb-test.c',
'tmp105-test.c',
@@ -293,11 +315,15 @@ qos_test_ss.add(
'vmxnet3-test.c',
'igb-test.c',
'ufs-test.c',
+ 'riscv-iommu-test.c',
)
if config_all_devices.has_key('CONFIG_VIRTIO_SERIAL')
qos_test_ss.add(files('virtio-serial-test.c'))
endif
+if config_all_devices.has_key('CONFIG_IP_OCTAL_232')
+ qos_test_ss.add(files('ipoctal232-test.c'))
+endif
if host_os != 'windows'
qos_test_ss.add(files('e1000e-test.c'))
@@ -314,24 +340,44 @@ endif
tpmemu_files = ['tpm-emu.c', 'tpm-util.c', 'tpm-tests.c']
-migration_files = [files('migration-helpers.c')]
+migration_files = [files(
+ 'migration/bootfile.c',
+ 'migration/framework.c',
+ 'migration/migration-qmp.c',
+ 'migration/migration-util.c',
+ 'migration/compression-tests.c',
+ 'migration/cpr-tests.c',
+ 'migration/file-tests.c',
+ 'migration/misc-tests.c',
+ 'migration/precopy-tests.c',
+ 'migration/postcopy-tests.c',
+)]
+
+migration_tls_files = []
if gnutls.found()
- migration_files += [files('../unit/crypto-tls-psk-helpers.c'), gnutls]
+ migration_tls_files = [files('migration/tls-tests.c',
+ '../unit/crypto-tls-psk-helpers.c'), gnutls]
if tasn1.found()
- migration_files += [files('../unit/crypto-tls-x509-helpers.c',
- '../unit/pkix_asn1_tab.c'), tasn1]
+ migration_tls_files += [files('../unit/crypto-tls-x509-helpers.c'), tasn1]
endif
endif
qtests = {
+ 'aspeed_hace-test': files('aspeed-hace-utils.c', 'aspeed_hace-test.c'),
+ 'aspeed_smc-test': files('aspeed-smc-utils.c', 'aspeed_smc-test.c'),
+ 'ast2700-hace-test': files('aspeed-hace-utils.c', 'ast2700-hace-test.c'),
+ 'ast2700-smc-test': files('aspeed-smc-utils.c', 'ast2700-smc-test.c'),
'bios-tables-test': [io, 'boot-sector.c', 'acpi-utils.c', 'tpm-emu.c'],
'cdrom-test': files('boot-sector.c'),
- 'dbus-vmstate-test': files('migration-helpers.c') + dbus_vmstate1,
+ 'dbus-vmstate-test': files('migration/migration-qmp.c',
+ 'migration/migration-util.c') + dbus_vmstate1,
'erst-test': files('erst-test.c'),
'ivshmem-test': [rt, '../../contrib/ivshmem-server/ivshmem-server.c'],
- 'migration-test': migration_files,
+ 'migration-test': migration_files + migration_tls_files,
'pxe-test': files('boot-sector.c'),
+ 'pnv-xive2-test': files('pnv-xive2-common.c', 'pnv-xive2-flush-sync.c',
+ 'pnv-xive2-nvpg_bar.c'),
'qos-test': [chardev, io, qos_test_ss.apply({}).sources()],
'tpm-crb-swtpm-test': [io, tpmemu_files],
'tpm-crb-test': [io, tpmemu_files],
@@ -340,7 +386,7 @@ qtests = {
'tpm-tis-i2c-test': [io, tpmemu_files, 'qtest_aspeed.c'],
'tpm-tis-device-swtpm-test': [io, tpmemu_files, 'tpm-tis-util.c'],
'tpm-tis-device-test': [io, tpmemu_files, 'tpm-tis-util.c'],
- 'virtio-net-failover': files('migration-helpers.c'),
+ 'virtio-net-failover': migration_files,
'vmgenid-test': files('boot-sector.c', 'acpi-utils.c'),
'netdev-socket': files('netdev-socket.c', '../unit/socket-helpers.c'),
}
@@ -348,7 +394,7 @@ qtests = {
if vnc.found()
gvnc = dependency('gvnc-1.0', method: 'pkg-config', required: false)
if gvnc.found()
- qtests += {'vnc-display-test': [gvnc]}
+ qtests += {'vnc-display-test': [gvnc, keymap_targets]}
qtests_generic += [ 'vnc-display-test' ]
endif
endif
@@ -366,6 +412,8 @@ foreach dir : target_dirs
target_base = dir.split('-')[0]
qtest_emulator = emulators['qemu-system-' + target_base]
target_qtests = get_variable('qtests_' + target_base, []) + qtests_generic
+ has_kvm = ('CONFIG_KVM' in config_all_accel and host_os == 'linux'
+ and cpu == target_base and fs.exists('/dev/kvm'))
test_deps = roms
qtest_env = environment()
@@ -399,11 +447,18 @@ foreach dir : target_dirs
test: executable(test, src, dependencies: deps)
}
endif
+
+ test_args = ['--tap', '-k']
+
+ if test == 'migration-test' and has_kvm
+ test_args += ['--full']
+ endif
+
test('qtest-@0@/@1@'.format(target_base, test),
qtest_executables[test],
depends: [test_deps, qtest_emulator, emulator_modules],
env: qtest_env,
- args: ['--tap', '-k'],
+ args: test_args,
protocol: 'tap',
timeout: slow_qtests.get(test, 60),
priority: slow_qtests.get(test, 60),
diff --git a/tests/qtest/migration-helpers.c b/tests/qtest/migration-helpers.c
deleted file mode 100644
index 84f49db..0000000
--- a/tests/qtest/migration-helpers.c
+++ /dev/null
@@ -1,533 +0,0 @@
-/*
- * QTest migration helpers
- *
- * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates
- * based on the vhost-user-test.c that is:
- * Copyright (c) 2014 Virtual Open Systems Sarl.
- *
- * This work is licensed under the terms of the GNU GPL, version 2 or later.
- * See the COPYING file in the top-level directory.
- *
- */
-
-#include "qemu/osdep.h"
-#include "qemu/ctype.h"
-#include "qapi/qmp/qjson.h"
-#include "qapi/qapi-visit-sockets.h"
-#include "qapi/qobject-input-visitor.h"
-#include "qapi/error.h"
-#include "qapi/qmp/qlist.h"
-#include "qemu/cutils.h"
-#include "qemu/memalign.h"
-
-#include "migration-helpers.h"
-
-/*
- * Number of seconds we wait when looking for migration
- * status changes, to avoid test suite hanging forever
- * when things go wrong. Needs to be higher enough to
- * avoid false positives on loaded hosts.
- */
-#define MIGRATION_STATUS_WAIT_TIMEOUT 120
-
-static char *SocketAddress_to_str(SocketAddress *addr)
-{
- switch (addr->type) {
- case SOCKET_ADDRESS_TYPE_INET:
- return g_strdup_printf("tcp:%s:%s",
- addr->u.inet.host,
- addr->u.inet.port);
- case SOCKET_ADDRESS_TYPE_UNIX:
- return g_strdup_printf("unix:%s",
- addr->u.q_unix.path);
- case SOCKET_ADDRESS_TYPE_FD:
- return g_strdup_printf("fd:%s", addr->u.fd.str);
- case SOCKET_ADDRESS_TYPE_VSOCK:
- return g_strdup_printf("vsock:%s:%s",
- addr->u.vsock.cid,
- addr->u.vsock.port);
- default:
- return g_strdup("unknown address type");
- }
-}
-
-static QDict *SocketAddress_to_qdict(SocketAddress *addr)
-{
- QDict *dict = qdict_new();
-
- switch (addr->type) {
- case SOCKET_ADDRESS_TYPE_INET:
- qdict_put_str(dict, "type", "inet");
- qdict_put_str(dict, "host", addr->u.inet.host);
- qdict_put_str(dict, "port", addr->u.inet.port);
- break;
- case SOCKET_ADDRESS_TYPE_UNIX:
- qdict_put_str(dict, "type", "unix");
- qdict_put_str(dict, "path", addr->u.q_unix.path);
- break;
- case SOCKET_ADDRESS_TYPE_FD:
- qdict_put_str(dict, "type", "fd");
- qdict_put_str(dict, "str", addr->u.fd.str);
- break;
- case SOCKET_ADDRESS_TYPE_VSOCK:
- qdict_put_str(dict, "type", "vsock");
- qdict_put_str(dict, "cid", addr->u.vsock.cid);
- qdict_put_str(dict, "port", addr->u.vsock.port);
- break;
- default:
- g_assert_not_reached();
- break;
- }
-
- return dict;
-}
-
-static SocketAddress *migrate_get_socket_address(QTestState *who)
-{
- QDict *rsp;
- SocketAddressList *addrs;
- SocketAddress *addr;
- Visitor *iv = NULL;
- QObject *object;
-
- rsp = migrate_query(who);
- object = qdict_get(rsp, "socket-address");
-
- iv = qobject_input_visitor_new(object);
- visit_type_SocketAddressList(iv, NULL, &addrs, &error_abort);
- addr = addrs->value;
- visit_free(iv);
-
- qobject_unref(rsp);
- return addr;
-}
-
-static char *
-migrate_get_connect_uri(QTestState *who)
-{
- SocketAddress *addrs;
- char *connect_uri;
-
- addrs = migrate_get_socket_address(who);
- connect_uri = SocketAddress_to_str(addrs);
-
- qapi_free_SocketAddress(addrs);
- return connect_uri;
-}
-
-static QDict *
-migrate_get_connect_qdict(QTestState *who)
-{
- SocketAddress *addrs;
- QDict *connect_qdict;
-
- addrs = migrate_get_socket_address(who);
- connect_qdict = SocketAddress_to_qdict(addrs);
-
- qapi_free_SocketAddress(addrs);
- return connect_qdict;
-}
-
-static void migrate_set_ports(QTestState *to, QList *channel_list)
-{
- QDict *addr;
- QListEntry *entry;
- const char *addr_port = NULL;
-
- addr = migrate_get_connect_qdict(to);
-
- QLIST_FOREACH_ENTRY(channel_list, entry) {
- QDict *channel = qobject_to(QDict, qlist_entry_obj(entry));
- QDict *addrdict = qdict_get_qdict(channel, "addr");
-
- if (qdict_haskey(addrdict, "port") &&
- qdict_haskey(addr, "port") &&
- (strcmp(qdict_get_str(addrdict, "port"), "0") == 0)) {
- addr_port = qdict_get_str(addr, "port");
- qdict_put_str(addrdict, "port", g_strdup(addr_port));
- }
- }
-
- qobject_unref(addr);
-}
-
-bool migrate_watch_for_events(QTestState *who, const char *name,
- QDict *event, void *opaque)
-{
- QTestMigrationState *state = opaque;
-
- if (g_str_equal(name, "STOP")) {
- state->stop_seen = true;
- return true;
- } else if (g_str_equal(name, "SUSPEND")) {
- state->suspend_seen = true;
- return true;
- } else if (g_str_equal(name, "RESUME")) {
- state->resume_seen = true;
- return true;
- }
-
- return false;
-}
-
-void migrate_qmp_fail(QTestState *who, const char *uri,
- const char *channels, const char *fmt, ...)
-{
- va_list ap;
- QDict *args, *err;
-
- va_start(ap, fmt);
- args = qdict_from_vjsonf_nofail(fmt, ap);
- va_end(ap);
-
- g_assert(!qdict_haskey(args, "uri"));
- if (uri) {
- qdict_put_str(args, "uri", uri);
- }
-
- g_assert(!qdict_haskey(args, "channels"));
- if (channels) {
- QObject *channels_obj = qobject_from_json(channels, &error_abort);
- qdict_put_obj(args, "channels", channels_obj);
- }
-
- err = qtest_qmp_assert_failure_ref(
- who, "{ 'execute': 'migrate', 'arguments': %p}", args);
-
- g_assert(qdict_haskey(err, "desc"));
-
- qobject_unref(err);
-}
-
-/*
- * Send QMP command "migrate".
- * Arguments are built from @fmt... (formatted like
- * qobject_from_jsonf_nofail()) with "uri": @uri spliced in.
- */
-void migrate_qmp(QTestState *who, QTestState *to, const char *uri,
- const char *channels, const char *fmt, ...)
-{
- va_list ap;
- QDict *args;
- g_autofree char *connect_uri = NULL;
-
- va_start(ap, fmt);
- args = qdict_from_vjsonf_nofail(fmt, ap);
- va_end(ap);
-
- g_assert(!qdict_haskey(args, "uri"));
- if (uri) {
- qdict_put_str(args, "uri", uri);
- } else if (!channels) {
- connect_uri = migrate_get_connect_uri(to);
- qdict_put_str(args, "uri", connect_uri);
- }
-
- g_assert(!qdict_haskey(args, "channels"));
- if (channels) {
- QObject *channels_obj = qobject_from_json(channels, &error_abort);
- QList *channel_list = qobject_to(QList, channels_obj);
- migrate_set_ports(to, channel_list);
- qdict_put_obj(args, "channels", channels_obj);
- }
-
- qtest_qmp_assert_success(who,
- "{ 'execute': 'migrate', 'arguments': %p}", args);
-}
-
-void migrate_set_capability(QTestState *who, const char *capability,
- bool value)
-{
- qtest_qmp_assert_success(who,
- "{ 'execute': 'migrate-set-capabilities',"
- "'arguments': { "
- "'capabilities': [ { "
- "'capability': %s, 'state': %i } ] } }",
- capability, value);
-}
-
-void migrate_incoming_qmp(QTestState *to, const char *uri, const char *fmt, ...)
-{
- va_list ap;
- QDict *args, *rsp;
-
- va_start(ap, fmt);
- args = qdict_from_vjsonf_nofail(fmt, ap);
- va_end(ap);
-
- g_assert(!qdict_haskey(args, "uri"));
- qdict_put_str(args, "uri", uri);
-
- /* This function relies on the event to work, make sure it's enabled */
- migrate_set_capability(to, "events", true);
-
- rsp = qtest_qmp(to, "{ 'execute': 'migrate-incoming', 'arguments': %p}",
- args);
-
- if (!qdict_haskey(rsp, "return")) {
- g_autoptr(GString) s = qobject_to_json_pretty(QOBJECT(rsp), true);
- g_test_message("%s", s->str);
- }
-
- g_assert(qdict_haskey(rsp, "return"));
- qobject_unref(rsp);
-
- migration_event_wait(to, "setup");
-}
-
-/*
- * Note: caller is responsible to free the returned object via
- * qobject_unref() after use
- */
-QDict *migrate_query(QTestState *who)
-{
- return qtest_qmp_assert_success_ref(who, "{ 'execute': 'query-migrate' }");
-}
-
-QDict *migrate_query_not_failed(QTestState *who)
-{
- const char *status;
- QDict *rsp = migrate_query(who);
- status = qdict_get_str(rsp, "status");
- if (g_str_equal(status, "failed")) {
- g_printerr("query-migrate shows failed migration: %s\n",
- qdict_get_str(rsp, "error-desc"));
- }
- g_assert(!g_str_equal(status, "failed"));
- return rsp;
-}
-
-/*
- * Note: caller is responsible to free the returned object via
- * g_free() after use
- */
-static gchar *migrate_query_status(QTestState *who)
-{
- QDict *rsp_return = migrate_query(who);
- gchar *status = g_strdup(qdict_get_str(rsp_return, "status"));
-
- g_assert(status);
- qobject_unref(rsp_return);
-
- return status;
-}
-
-static bool check_migration_status(QTestState *who, const char *goal,
- const char **ungoals)
-{
- bool ready;
- char *current_status;
- const char **ungoal;
-
- current_status = migrate_query_status(who);
- ready = strcmp(current_status, goal) == 0;
- if (!ungoals) {
- g_assert_cmpstr(current_status, !=, "failed");
- /*
- * If looking for a state other than completed,
- * completion of migration would cause the test to
- * hang.
- */
- if (strcmp(goal, "completed") != 0) {
- g_assert_cmpstr(current_status, !=, "completed");
- }
- } else {
- for (ungoal = ungoals; *ungoal; ungoal++) {
- g_assert_cmpstr(current_status, !=, *ungoal);
- }
- }
- g_free(current_status);
- return ready;
-}
-
-void wait_for_migration_status(QTestState *who,
- const char *goal, const char **ungoals)
-{
- g_test_timer_start();
- while (!check_migration_status(who, goal, ungoals)) {
- usleep(1000);
-
- g_assert(g_test_timer_elapsed() < MIGRATION_STATUS_WAIT_TIMEOUT);
- }
-}
-
-void wait_for_migration_complete(QTestState *who)
-{
- wait_for_migration_status(who, "completed", NULL);
-}
-
-void wait_for_migration_fail(QTestState *from, bool allow_active)
-{
- g_test_timer_start();
- QDict *rsp_return;
- char *status;
- bool failed;
-
- do {
- status = migrate_query_status(from);
- bool result = !strcmp(status, "setup") || !strcmp(status, "failed") ||
- (allow_active && !strcmp(status, "active"));
- if (!result) {
- fprintf(stderr, "%s: unexpected status status=%s allow_active=%d\n",
- __func__, status, allow_active);
- }
- g_assert(result);
- failed = !strcmp(status, "failed");
- g_free(status);
-
- g_assert(g_test_timer_elapsed() < MIGRATION_STATUS_WAIT_TIMEOUT);
- } while (!failed);
-
- /* Is the machine currently running? */
- rsp_return = qtest_qmp_assert_success_ref(from,
- "{ 'execute': 'query-status' }");
- g_assert(qdict_haskey(rsp_return, "running"));
- g_assert(qdict_get_bool(rsp_return, "running"));
- qobject_unref(rsp_return);
-}
-
-char *find_common_machine_version(const char *mtype, const char *var1,
- const char *var2)
-{
- g_autofree char *type1 = qtest_resolve_machine_alias(var1, mtype);
- g_autofree char *type2 = qtest_resolve_machine_alias(var2, mtype);
-
- g_assert(type1 && type2);
-
- if (g_str_equal(type1, type2)) {
- /* either can be used */
- return g_strdup(type1);
- }
-
- if (qtest_has_machine_with_env(var2, type1)) {
- return g_strdup(type1);
- }
-
- if (qtest_has_machine_with_env(var1, type2)) {
- return g_strdup(type2);
- }
-
- g_test_message("No common machine version for machine type '%s' between "
- "binaries %s and %s", mtype, getenv(var1), getenv(var2));
- g_assert_not_reached();
-}
-
-char *resolve_machine_version(const char *alias, const char *var1,
- const char *var2)
-{
- const char *mname = g_getenv("QTEST_QEMU_MACHINE_TYPE");
- g_autofree char *machine_name = NULL;
-
- if (mname) {
- const char *dash = strrchr(mname, '-');
- const char *dot = strrchr(mname, '.');
-
- machine_name = g_strdup(mname);
-
- if (dash && dot) {
- assert(qtest_has_machine(machine_name));
- return g_steal_pointer(&machine_name);
- }
- /* else: probably an alias, let it be resolved below */
- } else {
- /* use the hardcoded alias */
- machine_name = g_strdup(alias);
- }
-
- return find_common_machine_version(machine_name, var1, var2);
-}
-
-typedef struct {
- char *name;
- void (*func)(void);
-} MigrationTest;
-
-static void migration_test_destroy(gpointer data)
-{
- MigrationTest *test = (MigrationTest *)data;
-
- g_free(test->name);
- g_free(test);
-}
-
-static void migration_test_wrapper(const void *data)
-{
- MigrationTest *test = (MigrationTest *)data;
-
- g_test_message("Running /%s%s", qtest_get_arch(), test->name);
- test->func();
-}
-
-void migration_test_add(const char *path, void (*fn)(void))
-{
- MigrationTest *test = g_new0(MigrationTest, 1);
-
- test->func = fn;
- test->name = g_strdup(path);
-
- qtest_add_data_func_full(path, test, migration_test_wrapper,
- migration_test_destroy);
-}
-
-#ifdef O_DIRECT
-/*
- * Probe for O_DIRECT support on the filesystem. Since this is used
- * for tests, be conservative, if anything fails, assume it's
- * unsupported.
- */
-bool probe_o_direct_support(const char *tmpfs)
-{
- g_autofree char *filename = g_strdup_printf("%s/probe-o-direct", tmpfs);
- int fd, flags = O_CREAT | O_RDWR | O_TRUNC | O_DIRECT;
- void *buf;
- ssize_t ret, len;
- uint64_t offset;
-
- fd = open(filename, flags, 0660);
- if (fd < 0) {
- unlink(filename);
- return false;
- }
-
- /*
- * Using 1MB alignment as conservative choice to satisfy any
- * plausible architecture default page size, and/or filesystem
- * alignment restrictions.
- */
- len = 0x100000;
- offset = 0x100000;
-
- buf = qemu_try_memalign(len, len);
- g_assert(buf);
-
- ret = pwrite(fd, buf, len, offset);
- unlink(filename);
- g_free(buf);
-
- if (ret < 0) {
- return false;
- }
-
- return true;
-}
-#endif
-
-/*
- * Wait for a "MIGRATION" event. This is what Libvirt uses to track
- * migration status changes.
- */
-void migration_event_wait(QTestState *s, const char *target)
-{
- QDict *response, *data;
- const char *status;
- bool found;
-
- do {
- response = qtest_qmp_eventwait_ref(s, "MIGRATION");
- data = qdict_get_qdict(response, "data");
- g_assert(data);
- status = qdict_get_str(data, "status");
- found = (strcmp(status, target) == 0);
- qobject_unref(response);
- } while (!found);
-}
diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c
index 571fc13..0893687 100644
--- a/tests/qtest/migration-test.c
+++ b/tests/qtest/migration-test.c
@@ -11,4044 +11,56 @@
*/
#include "qemu/osdep.h"
-
-#include "libqtest.h"
-#include "qapi/qmp/qdict.h"
+#include "migration/framework.h"
#include "qemu/module.h"
-#include "qemu/option.h"
-#include "qemu/range.h"
-#include "qemu/sockets.h"
-#include "chardev/char.h"
-#include "crypto/tlscredspsk.h"
-#include "qapi/qmp/qlist.h"
-#include "ppc-util.h"
-
-#include "migration-helpers.h"
-#include "tests/migration/migration-test.h"
-#ifdef CONFIG_GNUTLS
-# include "tests/unit/crypto-tls-psk-helpers.h"
-# ifdef CONFIG_TASN1
-# include "tests/unit/crypto-tls-x509-helpers.h"
-# endif /* CONFIG_TASN1 */
-#endif /* CONFIG_GNUTLS */
-
-/* For dirty ring test; so far only x86_64 is supported */
-#if defined(__linux__) && defined(HOST_X86_64)
-#include "linux/kvm.h"
-#endif
-
-unsigned start_address;
-unsigned end_address;
-static bool uffd_feature_thread_id;
-static QTestMigrationState src_state;
-static QTestMigrationState dst_state;
-
-/*
- * An initial 3 MB offset is used as that corresponds
- * to ~1 sec of data transfer with our bandwidth setting.
- */
-#define MAGIC_OFFSET_BASE (3 * 1024 * 1024)
-/*
- * A further 1k is added to ensure we're not a multiple
- * of TEST_MEM_PAGE_SIZE, thus avoid clash with writes
- * from the migration guest workload.
- */
-#define MAGIC_OFFSET_SHUFFLE 1024
-#define MAGIC_OFFSET (MAGIC_OFFSET_BASE + MAGIC_OFFSET_SHUFFLE)
-#define MAGIC_MARKER 0xFEED12345678CAFEULL
-
-/*
- * Dirtylimit stop working if dirty page rate error
- * value less than DIRTYLIMIT_TOLERANCE_RANGE
- */
-#define DIRTYLIMIT_TOLERANCE_RANGE 25 /* MB/s */
-
-#define ANALYZE_SCRIPT "scripts/analyze-migration.py"
-#define VMSTATE_CHECKER_SCRIPT "scripts/vmstate-static-checker.py"
-
-#define QEMU_VM_FILE_MAGIC 0x5145564d
-#define FILE_TEST_FILENAME "migfile"
-#define FILE_TEST_OFFSET 0x1000
-#define FILE_TEST_MARKER 'X'
-#define QEMU_ENV_SRC "QTEST_QEMU_BINARY_SRC"
-#define QEMU_ENV_DST "QTEST_QEMU_BINARY_DST"
-
-typedef enum PostcopyRecoveryFailStage {
- /*
- * "no failure" must be 0 as it's the default. OTOH, real failure
- * cases must be >0 to make sure they trigger by a "if" test.
- */
- POSTCOPY_FAIL_NONE = 0,
- POSTCOPY_FAIL_CHANNEL_ESTABLISH,
- POSTCOPY_FAIL_RECOVERY,
- POSTCOPY_FAIL_MAX
-} PostcopyRecoveryFailStage;
-
-#if defined(__linux__)
-#include <sys/syscall.h>
-#include <sys/vfs.h>
-#endif
-
-#if defined(__linux__) && defined(__NR_userfaultfd) && defined(CONFIG_EVENTFD)
-#include <sys/eventfd.h>
-#include <sys/ioctl.h>
-#include "qemu/userfaultfd.h"
-
-static bool ufd_version_check(void)
-{
- struct uffdio_api api_struct;
- uint64_t ioctl_mask;
-
- int ufd = uffd_open(O_CLOEXEC);
-
- if (ufd == -1) {
- g_test_message("Skipping test: userfaultfd not available");
- return false;
- }
-
- api_struct.api = UFFD_API;
- api_struct.features = 0;
- if (ioctl(ufd, UFFDIO_API, &api_struct)) {
- g_test_message("Skipping test: UFFDIO_API failed");
- return false;
- }
- uffd_feature_thread_id = api_struct.features & UFFD_FEATURE_THREAD_ID;
-
- ioctl_mask = 1ULL << _UFFDIO_REGISTER |
- 1ULL << _UFFDIO_UNREGISTER;
- if ((api_struct.ioctls & ioctl_mask) != ioctl_mask) {
- g_test_message("Skipping test: Missing userfault feature");
- return false;
- }
-
- return true;
-}
-
-#else
-static bool ufd_version_check(void)
-{
- g_test_message("Skipping test: Userfault not available (builtdtime)");
- return false;
-}
-
-#endif
-
-static char *tmpfs;
-static char *bootpath;
-
-/* The boot file modifies memory area in [start_address, end_address)
- * repeatedly. It outputs a 'B' at a fixed rate while it's still running.
- */
-#include "tests/migration/i386/a-b-bootblock.h"
-#include "tests/migration/aarch64/a-b-kernel.h"
-#include "tests/migration/ppc64/a-b-kernel.h"
-#include "tests/migration/s390x/a-b-bios.h"
-
-static void bootfile_create(char *dir, bool suspend_me)
-{
- const char *arch = qtest_get_arch();
- unsigned char *content;
- size_t len;
-
- bootpath = g_strdup_printf("%s/bootsect", dir);
- if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) {
- /* the assembled x86 boot sector should be exactly one sector large */
- g_assert(sizeof(x86_bootsect) == 512);
- x86_bootsect[SYM_suspend_me - SYM_start] = suspend_me;
- content = x86_bootsect;
- len = sizeof(x86_bootsect);
- } else if (g_str_equal(arch, "s390x")) {
- content = s390x_elf;
- len = sizeof(s390x_elf);
- } else if (strcmp(arch, "ppc64") == 0) {
- content = ppc64_kernel;
- len = sizeof(ppc64_kernel);
- } else if (strcmp(arch, "aarch64") == 0) {
- content = aarch64_kernel;
- len = sizeof(aarch64_kernel);
- g_assert(sizeof(aarch64_kernel) <= ARM_TEST_MAX_KERNEL_SIZE);
- } else {
- g_assert_not_reached();
- }
-
- FILE *bootfile = fopen(bootpath, "wb");
-
- g_assert_cmpint(fwrite(content, len, 1, bootfile), ==, 1);
- fclose(bootfile);
-}
-
-static void bootfile_delete(void)
-{
- unlink(bootpath);
- g_free(bootpath);
- bootpath = NULL;
-}
-
-/*
- * Wait for some output in the serial output file,
- * we get an 'A' followed by an endless string of 'B's
- * but on the destination we won't have the A (unless we enabled suspend/resume)
- */
-static void wait_for_serial(const char *side)
-{
- g_autofree char *serialpath = g_strdup_printf("%s/%s", tmpfs, side);
- FILE *serialfile = fopen(serialpath, "r");
-
- do {
- int readvalue = fgetc(serialfile);
-
- switch (readvalue) {
- case 'A':
- /* Fine */
- break;
-
- case 'B':
- /* It's alive! */
- fclose(serialfile);
- return;
-
- case EOF:
- fseek(serialfile, 0, SEEK_SET);
- usleep(1000);
- break;
-
- default:
- fprintf(stderr, "Unexpected %d on %s serial\n", readvalue, side);
- g_assert_not_reached();
- }
- } while (true);
-}
-
-static void wait_for_stop(QTestState *who, QTestMigrationState *state)
-{
- if (!state->stop_seen) {
- qtest_qmp_eventwait(who, "STOP");
- }
-}
-
-static void wait_for_resume(QTestState *who, QTestMigrationState *state)
-{
- if (!state->resume_seen) {
- qtest_qmp_eventwait(who, "RESUME");
- }
-}
-
-static void wait_for_suspend(QTestState *who, QTestMigrationState *state)
-{
- if (state->suspend_me && !state->suspend_seen) {
- qtest_qmp_eventwait(who, "SUSPEND");
- }
-}
-
-/*
- * It's tricky to use qemu's migration event capability with qtest,
- * events suddenly appearing confuse the qmp()/hmp() responses.
- */
-
-static int64_t read_ram_property_int(QTestState *who, const char *property)
-{
- QDict *rsp_return, *rsp_ram;
- int64_t result;
-
- rsp_return = migrate_query_not_failed(who);
- if (!qdict_haskey(rsp_return, "ram")) {
- /* Still in setup */
- result = 0;
- } else {
- rsp_ram = qdict_get_qdict(rsp_return, "ram");
- result = qdict_get_try_int(rsp_ram, property, 0);
- }
- qobject_unref(rsp_return);
- return result;
-}
-
-static int64_t read_migrate_property_int(QTestState *who, const char *property)
-{
- QDict *rsp_return;
- int64_t result;
-
- rsp_return = migrate_query_not_failed(who);
- result = qdict_get_try_int(rsp_return, property, 0);
- qobject_unref(rsp_return);
- return result;
-}
-
-static uint64_t get_migration_pass(QTestState *who)
-{
- return read_ram_property_int(who, "dirty-sync-count");
-}
-
-static void read_blocktime(QTestState *who)
-{
- QDict *rsp_return;
-
- rsp_return = migrate_query_not_failed(who);
- g_assert(qdict_haskey(rsp_return, "postcopy-blocktime"));
- qobject_unref(rsp_return);
-}
-
-/*
- * Wait for two changes in the migration pass count, but bail if we stop.
- */
-static void wait_for_migration_pass(QTestState *who)
-{
- uint64_t pass, prev_pass = 0, changes = 0;
-
- while (changes < 2 && !src_state.stop_seen && !src_state.suspend_seen) {
- usleep(1000);
- pass = get_migration_pass(who);
- changes += (pass != prev_pass);
- prev_pass = pass;
- }
-}
-
-static void check_guests_ram(QTestState *who)
-{
- /* Our ASM test will have been incrementing one byte from each page from
- * start_address to < end_address in order. This gives us a constraint
- * that any page's byte should be equal or less than the previous pages
- * byte (mod 256); and they should all be equal except for one transition
- * at the point where we meet the incrementer. (We're running this with
- * the guest stopped).
- */
- unsigned address;
- uint8_t first_byte;
- uint8_t last_byte;
- bool hit_edge = false;
- int bad = 0;
-
- qtest_memread(who, start_address, &first_byte, 1);
- last_byte = first_byte;
-
- for (address = start_address + TEST_MEM_PAGE_SIZE; address < end_address;
- address += TEST_MEM_PAGE_SIZE)
- {
- uint8_t b;
- qtest_memread(who, address, &b, 1);
- if (b != last_byte) {
- if (((b + 1) % 256) == last_byte && !hit_edge) {
- /* This is OK, the guest stopped at the point of
- * incrementing the previous page but didn't get
- * to us yet.
- */
- hit_edge = true;
- last_byte = b;
- } else {
- bad++;
- if (bad <= 10) {
- fprintf(stderr, "Memory content inconsistency at %x"
- " first_byte = %x last_byte = %x current = %x"
- " hit_edge = %x\n",
- address, first_byte, last_byte, b, hit_edge);
- }
- }
- }
- }
- if (bad >= 10) {
- fprintf(stderr, "and in another %d pages", bad - 10);
- }
- g_assert(bad == 0);
-}
-
-static void cleanup(const char *filename)
-{
- g_autofree char *path = g_strdup_printf("%s/%s", tmpfs, filename);
-
- unlink(path);
-}
-
-static long long migrate_get_parameter_int(QTestState *who,
- const char *parameter)
-{
- QDict *rsp;
- long long result;
-
- rsp = qtest_qmp_assert_success_ref(
- who, "{ 'execute': 'query-migrate-parameters' }");
- result = qdict_get_int(rsp, parameter);
- qobject_unref(rsp);
- return result;
-}
-
-static void migrate_check_parameter_int(QTestState *who, const char *parameter,
- long long value)
-{
- long long result;
-
- result = migrate_get_parameter_int(who, parameter);
- g_assert_cmpint(result, ==, value);
-}
-
-static void migrate_set_parameter_int(QTestState *who, const char *parameter,
- long long value)
-{
- qtest_qmp_assert_success(who,
- "{ 'execute': 'migrate-set-parameters',"
- "'arguments': { %s: %lld } }",
- parameter, value);
- migrate_check_parameter_int(who, parameter, value);
-}
-
-static char *migrate_get_parameter_str(QTestState *who,
- const char *parameter)
-{
- QDict *rsp;
- char *result;
-
- rsp = qtest_qmp_assert_success_ref(
- who, "{ 'execute': 'query-migrate-parameters' }");
- result = g_strdup(qdict_get_str(rsp, parameter));
- qobject_unref(rsp);
- return result;
-}
-
-static void migrate_check_parameter_str(QTestState *who, const char *parameter,
- const char *value)
-{
- g_autofree char *result = migrate_get_parameter_str(who, parameter);
- g_assert_cmpstr(result, ==, value);
-}
-
-static void migrate_set_parameter_str(QTestState *who, const char *parameter,
- const char *value)
-{
- qtest_qmp_assert_success(who,
- "{ 'execute': 'migrate-set-parameters',"
- "'arguments': { %s: %s } }",
- parameter, value);
- migrate_check_parameter_str(who, parameter, value);
-}
-
-static long long migrate_get_parameter_bool(QTestState *who,
- const char *parameter)
-{
- QDict *rsp;
- int result;
-
- rsp = qtest_qmp_assert_success_ref(
- who, "{ 'execute': 'query-migrate-parameters' }");
- result = qdict_get_bool(rsp, parameter);
- qobject_unref(rsp);
- return !!result;
-}
-
-static void migrate_check_parameter_bool(QTestState *who, const char *parameter,
- int value)
-{
- int result;
-
- result = migrate_get_parameter_bool(who, parameter);
- g_assert_cmpint(result, ==, value);
-}
-
-static void migrate_set_parameter_bool(QTestState *who, const char *parameter,
- int value)
-{
- qtest_qmp_assert_success(who,
- "{ 'execute': 'migrate-set-parameters',"
- "'arguments': { %s: %i } }",
- parameter, value);
- migrate_check_parameter_bool(who, parameter, value);
-}
-
-static void migrate_ensure_non_converge(QTestState *who)
-{
- /* Can't converge with 1ms downtime + 3 mbs bandwidth limit */
- migrate_set_parameter_int(who, "max-bandwidth", 3 * 1000 * 1000);
- migrate_set_parameter_int(who, "downtime-limit", 1);
-}
-
-static void migrate_ensure_converge(QTestState *who)
-{
- /* Should converge with 30s downtime + 1 gbs bandwidth limit */
- migrate_set_parameter_int(who, "max-bandwidth", 1 * 1000 * 1000 * 1000);
- migrate_set_parameter_int(who, "downtime-limit", 30 * 1000);
-}
-
-/*
- * Our goal is to ensure that we run a single full migration
- * iteration, and also dirty memory, ensuring that at least
- * one further iteration is required.
- *
- * We can't directly synchronize with the start of a migration
- * so we have to apply some tricks monitoring memory that is
- * transferred.
- *
- * Initially we set the migration bandwidth to an insanely
- * low value, with tiny max downtime too. This basically
- * guarantees migration will never complete.
- *
- * This will result in a test that is unacceptably slow though,
- * so we can't let the entire migration pass run at this speed.
- * Our intent is to let it run just long enough that we can
- * prove data prior to the marker has been transferred *AND*
- * also prove this transferred data is dirty again.
- *
- * Before migration starts, we write a 64-bit magic marker
- * into a fixed location in the src VM RAM.
- *
- * Then watch dst memory until the marker appears. This is
- * proof that start_address -> MAGIC_OFFSET_BASE has been
- * transferred.
- *
- * Finally we go back to the source and read a byte just
- * before the marker until we see it flip in value. This
- * is proof that start_address -> MAGIC_OFFSET_BASE
- * is now dirty again.
- *
- * IOW, we're guaranteed at least a 2nd migration pass
- * at this point.
- *
- * We can now let migration run at full speed to finish
- * the test
- */
-static void migrate_prepare_for_dirty_mem(QTestState *from)
-{
- /*
- * The guest workflow iterates from start_address to
- * end_address, writing 1 byte every TEST_MEM_PAGE_SIZE
- * bytes.
- *
- * IOW, if we write to mem at a point which is NOT
- * a multiple of TEST_MEM_PAGE_SIZE, our write won't
- * conflict with the migration workflow.
- *
- * We put in a marker here, that we'll use to determine
- * when the data has been transferred to the dst.
- */
- qtest_writeq(from, start_address + MAGIC_OFFSET, MAGIC_MARKER);
-}
-
-static void migrate_wait_for_dirty_mem(QTestState *from,
- QTestState *to)
-{
- uint64_t watch_address = start_address + MAGIC_OFFSET_BASE;
- uint64_t marker_address = start_address + MAGIC_OFFSET;
- uint8_t watch_byte;
-
- /*
- * Wait for the MAGIC_MARKER to get transferred, as an
- * indicator that a migration pass has made some known
- * amount of progress.
- */
- do {
- usleep(1000 * 10);
- } while (qtest_readq(to, marker_address) != MAGIC_MARKER);
-
-
- /* If suspended, src only iterates once, and watch_byte may never change */
- if (src_state.suspend_me) {
- return;
- }
-
- /*
- * Now ensure that already transferred bytes are
- * dirty again from the guest workload. Note the
- * guest byte value will wrap around and by chance
- * match the original watch_byte. This is harmless
- * as we'll eventually see a different value if we
- * keep watching
- */
- watch_byte = qtest_readb(from, watch_address);
- do {
- usleep(1000 * 10);
- } while (qtest_readb(from, watch_address) == watch_byte);
-}
-
-
-static void migrate_pause(QTestState *who)
-{
- qtest_qmp_assert_success(who, "{ 'execute': 'migrate-pause' }");
-}
-
-static void migrate_continue(QTestState *who, const char *state)
-{
- qtest_qmp_assert_success(who,
- "{ 'execute': 'migrate-continue',"
- " 'arguments': { 'state': %s } }",
- state);
-}
-
-static void migrate_recover(QTestState *who, const char *uri)
-{
- qtest_qmp_assert_success(who,
- "{ 'execute': 'migrate-recover', "
- " 'id': 'recover-cmd', "
- " 'arguments': { 'uri': %s } }",
- uri);
-}
-
-static void migrate_cancel(QTestState *who)
-{
- qtest_qmp_assert_success(who, "{ 'execute': 'migrate_cancel' }");
-}
-
-static void migrate_postcopy_start(QTestState *from, QTestState *to)
-{
- qtest_qmp_assert_success(from, "{ 'execute': 'migrate-start-postcopy' }");
-
- wait_for_stop(from, &src_state);
- qtest_qmp_eventwait(to, "RESUME");
-}
-
-typedef struct {
- /*
- * QTEST_LOG=1 may override this. When QTEST_LOG=1, we always dump errors
- * unconditionally, because it means the user would like to be verbose.
- */
- bool hide_stderr;
- bool use_shmem;
- /* only launch the target process */
- bool only_target;
- /* Use dirty ring if true; dirty logging otherwise */
- bool use_dirty_ring;
- const char *opts_source;
- const char *opts_target;
- /* suspend the src before migrating to dest. */
- bool suspend_me;
-} MigrateStart;
-
-/*
- * A hook that runs after the src and dst QEMUs have been
- * created, but before the migration is started. This can
- * be used to set migration parameters and capabilities.
- *
- * Returns: NULL, or a pointer to opaque state to be
- * later passed to the TestMigrateFinishHook
- */
-typedef void * (*TestMigrateStartHook)(QTestState *from,
- QTestState *to);
-
-/*
- * A hook that runs after the migration has finished,
- * regardless of whether it succeeded or failed, but
- * before QEMU has terminated (unless it self-terminated
- * due to migration error)
- *
- * @opaque is a pointer to state previously returned
- * by the TestMigrateStartHook if any, or NULL.
- */
-typedef void (*TestMigrateFinishHook)(QTestState *from,
- QTestState *to,
- void *opaque);
-
-typedef struct {
- /* Optional: fine tune start parameters */
- MigrateStart start;
-
- /* Required: the URI for the dst QEMU to listen on */
- const char *listen_uri;
-
- /*
- * Optional: the URI for the src QEMU to connect to
- * If NULL, then it will query the dst QEMU for its actual
- * listening address and use that as the connect address.
- * This allows for dynamically picking a free TCP port.
- */
- const char *connect_uri;
-
- /*
- * Optional: JSON-formatted list of src QEMU URIs. If a port is
- * defined as '0' in any QDict key a value of '0' will be
- * automatically converted to the correct destination port.
- */
- const char *connect_channels;
-
- /* Optional: callback to run at start to set migration parameters */
- TestMigrateStartHook start_hook;
- /* Optional: callback to run at finish to cleanup */
- TestMigrateFinishHook finish_hook;
-
- /*
- * Optional: normally we expect the migration process to complete.
- *
- * There can be a variety of reasons and stages in which failure
- * can happen during tests.
- *
- * If a failure is expected to happen at time of establishing
- * the connection, then MIG_TEST_FAIL will indicate that the dst
- * QEMU is expected to stay running and accept future migration
- * connections.
- *
- * If a failure is expected to happen while processing the
- * migration stream, then MIG_TEST_FAIL_DEST_QUIT_ERR will indicate
- * that the dst QEMU is expected to quit with non-zero exit status
- */
- enum {
- /* This test should succeed, the default */
- MIG_TEST_SUCCEED = 0,
- /* This test should fail, dest qemu should keep alive */
- MIG_TEST_FAIL,
- /* This test should fail, dest qemu should fail with abnormal status */
- MIG_TEST_FAIL_DEST_QUIT_ERR,
- /* The QMP command for this migration should fail with an error */
- MIG_TEST_QMP_ERROR,
- } result;
-
- /*
- * Optional: set number of migration passes to wait for, if live==true.
- * If zero, then merely wait for a few MB of dirty data
- */
- unsigned int iterations;
-
- /*
- * Optional: whether the guest CPUs should be running during a precopy
- * migration test. We used to always run with live but it took much
- * longer so we reduced live tests to only the ones that have solid
- * reason to be tested live-only. For each of the new test cases for
- * precopy please provide justifications to use live explicitly (please
- * refer to existing ones with live=true), or use live=off by default.
- */
- bool live;
-
- /* Postcopy specific fields */
- void *postcopy_data;
- bool postcopy_preempt;
- PostcopyRecoveryFailStage postcopy_recovery_fail_stage;
-} MigrateCommon;
-
-static int test_migrate_start(QTestState **from, QTestState **to,
- const char *uri, MigrateStart *args)
-{
- g_autofree gchar *arch_source = NULL;
- g_autofree gchar *arch_target = NULL;
- /* options for source and target */
- g_autofree gchar *arch_opts = NULL;
- g_autofree gchar *cmd_source = NULL;
- g_autofree gchar *cmd_target = NULL;
- const gchar *ignore_stderr;
- g_autofree char *shmem_opts = NULL;
- g_autofree char *shmem_path = NULL;
- const char *kvm_opts = NULL;
- const char *arch = qtest_get_arch();
- const char *memory_size;
- const char *machine_alias, *machine_opts = "";
- g_autofree char *machine = NULL;
-
- if (args->use_shmem) {
- if (!g_file_test("/dev/shm", G_FILE_TEST_IS_DIR)) {
- g_test_skip("/dev/shm is not supported");
- return -1;
- }
- }
-
- dst_state = (QTestMigrationState) { };
- src_state = (QTestMigrationState) { };
- bootfile_create(tmpfs, args->suspend_me);
- src_state.suspend_me = args->suspend_me;
-
- if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) {
- memory_size = "150M";
-
- if (g_str_equal(arch, "i386")) {
- machine_alias = "pc";
- } else {
- machine_alias = "q35";
- }
- arch_opts = g_strdup_printf(
- "-drive if=none,id=d0,file=%s,format=raw "
- "-device ide-hd,drive=d0,secs=1,cyls=1,heads=1", bootpath);
- start_address = X86_TEST_MEM_START;
- end_address = X86_TEST_MEM_END;
- } else if (g_str_equal(arch, "s390x")) {
- memory_size = "128M";
- machine_alias = "s390-ccw-virtio";
- arch_opts = g_strdup_printf("-bios %s", bootpath);
- start_address = S390_TEST_MEM_START;
- end_address = S390_TEST_MEM_END;
- } else if (strcmp(arch, "ppc64") == 0) {
- memory_size = "256M";
- start_address = PPC_TEST_MEM_START;
- end_address = PPC_TEST_MEM_END;
- machine_alias = "pseries";
- machine_opts = "vsmt=8";
- arch_opts = g_strdup_printf(
- "-nodefaults -machine " PSERIES_DEFAULT_CAPABILITIES " "
- "-bios %s", bootpath);
- } else if (strcmp(arch, "aarch64") == 0) {
- memory_size = "150M";
- machine_alias = "virt";
- machine_opts = "gic-version=3";
- arch_opts = g_strdup_printf("-cpu max -kernel %s", bootpath);
- start_address = ARM_TEST_MEM_START;
- end_address = ARM_TEST_MEM_END;
- } else {
- g_assert_not_reached();
- }
-
- if (!getenv("QTEST_LOG") && args->hide_stderr) {
-#ifndef _WIN32
- ignore_stderr = "2>/dev/null";
-#else
- /*
- * On Windows the QEMU executable is created via CreateProcess() and
- * IO redirection does not work, so don't bother adding IO redirection
- * to the command line.
- */
- ignore_stderr = "";
-#endif
- } else {
- ignore_stderr = "";
- }
-
- if (args->use_shmem) {
- shmem_path = g_strdup_printf("/dev/shm/qemu-%d", getpid());
- shmem_opts = g_strdup_printf(
- "-object memory-backend-file,id=mem0,size=%s"
- ",mem-path=%s,share=on -numa node,memdev=mem0",
- memory_size, shmem_path);
- }
-
- if (args->use_dirty_ring) {
- kvm_opts = ",dirty-ring-size=4096";
- }
-
- if (!qtest_has_machine(machine_alias)) {
- g_autofree char *msg = g_strdup_printf("machine %s not supported", machine_alias);
- g_test_skip(msg);
- return -1;
- }
-
- machine = resolve_machine_version(machine_alias, QEMU_ENV_SRC,
- QEMU_ENV_DST);
-
- g_test_message("Using machine type: %s", machine);
-
- cmd_source = g_strdup_printf("-accel kvm%s -accel tcg "
- "-machine %s,%s "
- "-name source,debug-threads=on "
- "-m %s "
- "-serial file:%s/src_serial "
- "%s %s %s %s %s",
- kvm_opts ? kvm_opts : "",
- machine, machine_opts,
- memory_size, tmpfs,
- arch_opts ? arch_opts : "",
- arch_source ? arch_source : "",
- shmem_opts ? shmem_opts : "",
- args->opts_source ? args->opts_source : "",
- ignore_stderr);
- if (!args->only_target) {
- *from = qtest_init_with_env(QEMU_ENV_SRC, cmd_source);
- qtest_qmp_set_event_callback(*from,
- migrate_watch_for_events,
- &src_state);
- }
-
- cmd_target = g_strdup_printf("-accel kvm%s -accel tcg "
- "-machine %s,%s "
- "-name target,debug-threads=on "
- "-m %s "
- "-serial file:%s/dest_serial "
- "-incoming %s "
- "%s %s %s %s %s",
- kvm_opts ? kvm_opts : "",
- machine, machine_opts,
- memory_size, tmpfs, uri,
- arch_opts ? arch_opts : "",
- arch_target ? arch_target : "",
- shmem_opts ? shmem_opts : "",
- args->opts_target ? args->opts_target : "",
- ignore_stderr);
- *to = qtest_init_with_env(QEMU_ENV_DST, cmd_target);
- qtest_qmp_set_event_callback(*to,
- migrate_watch_for_events,
- &dst_state);
-
- /*
- * Remove shmem file immediately to avoid memory leak in test failed case.
- * It's valid because QEMU has already opened this file
- */
- if (args->use_shmem) {
- unlink(shmem_path);
- }
-
- /*
- * Always enable migration events. Libvirt always uses it, let's try
- * to mimic as closer as that.
- */
- migrate_set_capability(*from, "events", true);
- migrate_set_capability(*to, "events", true);
-
- return 0;
-}
-
-static void test_migrate_end(QTestState *from, QTestState *to, bool test_dest)
-{
- unsigned char dest_byte_a, dest_byte_b, dest_byte_c, dest_byte_d;
-
- qtest_quit(from);
-
- if (test_dest) {
- qtest_memread(to, start_address, &dest_byte_a, 1);
-
- /* Destination still running, wait for a byte to change */
- do {
- qtest_memread(to, start_address, &dest_byte_b, 1);
- usleep(1000 * 10);
- } while (dest_byte_a == dest_byte_b);
-
- qtest_qmp_assert_success(to, "{ 'execute' : 'stop'}");
-
- /* With it stopped, check nothing changes */
- qtest_memread(to, start_address, &dest_byte_c, 1);
- usleep(1000 * 200);
- qtest_memread(to, start_address, &dest_byte_d, 1);
- g_assert_cmpint(dest_byte_c, ==, dest_byte_d);
-
- check_guests_ram(to);
- }
-
- qtest_quit(to);
-
- cleanup("migsocket");
- cleanup("src_serial");
- cleanup("dest_serial");
- cleanup(FILE_TEST_FILENAME);
-}
-
-#ifdef CONFIG_GNUTLS
-struct TestMigrateTLSPSKData {
- char *workdir;
- char *workdiralt;
- char *pskfile;
- char *pskfilealt;
-};
-
-static void *
-test_migrate_tls_psk_start_common(QTestState *from,
- QTestState *to,
- bool mismatch)
-{
- struct TestMigrateTLSPSKData *data =
- g_new0(struct TestMigrateTLSPSKData, 1);
-
- data->workdir = g_strdup_printf("%s/tlscredspsk0", tmpfs);
- data->pskfile = g_strdup_printf("%s/%s", data->workdir,
- QCRYPTO_TLS_CREDS_PSKFILE);
- g_mkdir_with_parents(data->workdir, 0700);
- test_tls_psk_init(data->pskfile);
-
- if (mismatch) {
- data->workdiralt = g_strdup_printf("%s/tlscredspskalt0", tmpfs);
- data->pskfilealt = g_strdup_printf("%s/%s", data->workdiralt,
- QCRYPTO_TLS_CREDS_PSKFILE);
- g_mkdir_with_parents(data->workdiralt, 0700);
- test_tls_psk_init_alt(data->pskfilealt);
- }
-
- qtest_qmp_assert_success(from,
- "{ 'execute': 'object-add',"
- " 'arguments': { 'qom-type': 'tls-creds-psk',"
- " 'id': 'tlscredspsk0',"
- " 'endpoint': 'client',"
- " 'dir': %s,"
- " 'username': 'qemu'} }",
- data->workdir);
-
- qtest_qmp_assert_success(to,
- "{ 'execute': 'object-add',"
- " 'arguments': { 'qom-type': 'tls-creds-psk',"
- " 'id': 'tlscredspsk0',"
- " 'endpoint': 'server',"
- " 'dir': %s } }",
- mismatch ? data->workdiralt : data->workdir);
-
- migrate_set_parameter_str(from, "tls-creds", "tlscredspsk0");
- migrate_set_parameter_str(to, "tls-creds", "tlscredspsk0");
-
- return data;
-}
-
-static void *
-test_migrate_tls_psk_start_match(QTestState *from,
- QTestState *to)
-{
- return test_migrate_tls_psk_start_common(from, to, false);
-}
-
-static void *
-test_migrate_tls_psk_start_mismatch(QTestState *from,
- QTestState *to)
-{
- return test_migrate_tls_psk_start_common(from, to, true);
-}
-
-static void
-test_migrate_tls_psk_finish(QTestState *from,
- QTestState *to,
- void *opaque)
-{
- struct TestMigrateTLSPSKData *data = opaque;
-
- test_tls_psk_cleanup(data->pskfile);
- if (data->pskfilealt) {
- test_tls_psk_cleanup(data->pskfilealt);
- }
- rmdir(data->workdir);
- if (data->workdiralt) {
- rmdir(data->workdiralt);
- }
-
- g_free(data->workdiralt);
- g_free(data->pskfilealt);
- g_free(data->workdir);
- g_free(data->pskfile);
- g_free(data);
-}
-
-#ifdef CONFIG_TASN1
-typedef struct {
- char *workdir;
- char *keyfile;
- char *cacert;
- char *servercert;
- char *serverkey;
- char *clientcert;
- char *clientkey;
-} TestMigrateTLSX509Data;
-
-typedef struct {
- bool verifyclient;
- bool clientcert;
- bool hostileclient;
- bool authzclient;
- const char *certhostname;
- const char *certipaddr;
-} TestMigrateTLSX509;
-
-static void *
-test_migrate_tls_x509_start_common(QTestState *from,
- QTestState *to,
- TestMigrateTLSX509 *args)
-{
- TestMigrateTLSX509Data *data = g_new0(TestMigrateTLSX509Data, 1);
-
- data->workdir = g_strdup_printf("%s/tlscredsx5090", tmpfs);
- data->keyfile = g_strdup_printf("%s/key.pem", data->workdir);
-
- data->cacert = g_strdup_printf("%s/ca-cert.pem", data->workdir);
- data->serverkey = g_strdup_printf("%s/server-key.pem", data->workdir);
- data->servercert = g_strdup_printf("%s/server-cert.pem", data->workdir);
- if (args->clientcert) {
- data->clientkey = g_strdup_printf("%s/client-key.pem", data->workdir);
- data->clientcert = g_strdup_printf("%s/client-cert.pem", data->workdir);
- }
-
- g_mkdir_with_parents(data->workdir, 0700);
-
- test_tls_init(data->keyfile);
-#ifndef _WIN32
- g_assert(link(data->keyfile, data->serverkey) == 0);
-#else
- g_assert(CreateHardLink(data->serverkey, data->keyfile, NULL) != 0);
-#endif
- if (args->clientcert) {
-#ifndef _WIN32
- g_assert(link(data->keyfile, data->clientkey) == 0);
-#else
- g_assert(CreateHardLink(data->clientkey, data->keyfile, NULL) != 0);
-#endif
- }
-
- TLS_ROOT_REQ_SIMPLE(cacertreq, data->cacert);
- if (args->clientcert) {
- TLS_CERT_REQ_SIMPLE_CLIENT(servercertreq, cacertreq,
- args->hostileclient ?
- QCRYPTO_TLS_TEST_CLIENT_HOSTILE_NAME :
- QCRYPTO_TLS_TEST_CLIENT_NAME,
- data->clientcert);
- }
-
- TLS_CERT_REQ_SIMPLE_SERVER(clientcertreq, cacertreq,
- data->servercert,
- args->certhostname,
- args->certipaddr);
-
- qtest_qmp_assert_success(from,
- "{ 'execute': 'object-add',"
- " 'arguments': { 'qom-type': 'tls-creds-x509',"
- " 'id': 'tlscredsx509client0',"
- " 'endpoint': 'client',"
- " 'dir': %s,"
- " 'sanity-check': true,"
- " 'verify-peer': true} }",
- data->workdir);
- migrate_set_parameter_str(from, "tls-creds", "tlscredsx509client0");
- if (args->certhostname) {
- migrate_set_parameter_str(from, "tls-hostname", args->certhostname);
- }
-
- qtest_qmp_assert_success(to,
- "{ 'execute': 'object-add',"
- " 'arguments': { 'qom-type': 'tls-creds-x509',"
- " 'id': 'tlscredsx509server0',"
- " 'endpoint': 'server',"
- " 'dir': %s,"
- " 'sanity-check': true,"
- " 'verify-peer': %i} }",
- data->workdir, args->verifyclient);
- migrate_set_parameter_str(to, "tls-creds", "tlscredsx509server0");
-
- if (args->authzclient) {
- qtest_qmp_assert_success(to,
- "{ 'execute': 'object-add',"
- " 'arguments': { 'qom-type': 'authz-simple',"
- " 'id': 'tlsauthz0',"
- " 'identity': %s} }",
- "CN=" QCRYPTO_TLS_TEST_CLIENT_NAME);
- migrate_set_parameter_str(to, "tls-authz", "tlsauthz0");
- }
-
- return data;
-}
-
-/*
- * The normal case: match server's cert hostname against
- * whatever host we were telling QEMU to connect to (if any)
- */
-static void *
-test_migrate_tls_x509_start_default_host(QTestState *from,
- QTestState *to)
-{
- TestMigrateTLSX509 args = {
- .verifyclient = true,
- .clientcert = true,
- .certipaddr = "127.0.0.1"
- };
- return test_migrate_tls_x509_start_common(from, to, &args);
-}
-
-/*
- * The unusual case: the server's cert is different from
- * the address we're telling QEMU to connect to (if any),
- * so we must give QEMU an explicit hostname to validate
- */
-static void *
-test_migrate_tls_x509_start_override_host(QTestState *from,
- QTestState *to)
-{
- TestMigrateTLSX509 args = {
- .verifyclient = true,
- .clientcert = true,
- .certhostname = "qemu.org",
- };
- return test_migrate_tls_x509_start_common(from, to, &args);
-}
-
-/*
- * The unusual case: the server's cert is different from
- * the address we're telling QEMU to connect to, and so we
- * expect the client to reject the server
- */
-static void *
-test_migrate_tls_x509_start_mismatch_host(QTestState *from,
- QTestState *to)
-{
- TestMigrateTLSX509 args = {
- .verifyclient = true,
- .clientcert = true,
- .certipaddr = "10.0.0.1",
- };
- return test_migrate_tls_x509_start_common(from, to, &args);
-}
-
-static void *
-test_migrate_tls_x509_start_friendly_client(QTestState *from,
- QTestState *to)
-{
- TestMigrateTLSX509 args = {
- .verifyclient = true,
- .clientcert = true,
- .authzclient = true,
- .certipaddr = "127.0.0.1",
- };
- return test_migrate_tls_x509_start_common(from, to, &args);
-}
-
-static void *
-test_migrate_tls_x509_start_hostile_client(QTestState *from,
- QTestState *to)
-{
- TestMigrateTLSX509 args = {
- .verifyclient = true,
- .clientcert = true,
- .hostileclient = true,
- .authzclient = true,
- .certipaddr = "127.0.0.1",
- };
- return test_migrate_tls_x509_start_common(from, to, &args);
-}
-
-/*
- * The case with no client certificate presented,
- * and no server verification
- */
-static void *
-test_migrate_tls_x509_start_allow_anon_client(QTestState *from,
- QTestState *to)
-{
- TestMigrateTLSX509 args = {
- .certipaddr = "127.0.0.1",
- };
- return test_migrate_tls_x509_start_common(from, to, &args);
-}
-
-/*
- * The case with no client certificate presented,
- * and server verification rejecting
- */
-static void *
-test_migrate_tls_x509_start_reject_anon_client(QTestState *from,
- QTestState *to)
-{
- TestMigrateTLSX509 args = {
- .verifyclient = true,
- .certipaddr = "127.0.0.1",
- };
- return test_migrate_tls_x509_start_common(from, to, &args);
-}
-
-static void
-test_migrate_tls_x509_finish(QTestState *from,
- QTestState *to,
- void *opaque)
-{
- TestMigrateTLSX509Data *data = opaque;
-
- test_tls_cleanup(data->keyfile);
- g_free(data->keyfile);
-
- unlink(data->cacert);
- g_free(data->cacert);
- unlink(data->servercert);
- g_free(data->servercert);
- unlink(data->serverkey);
- g_free(data->serverkey);
-
- if (data->clientcert) {
- unlink(data->clientcert);
- g_free(data->clientcert);
- }
- if (data->clientkey) {
- unlink(data->clientkey);
- g_free(data->clientkey);
- }
-
- rmdir(data->workdir);
- g_free(data->workdir);
-
- g_free(data);
-}
-#endif /* CONFIG_TASN1 */
-#endif /* CONFIG_GNUTLS */
-
-static int migrate_postcopy_prepare(QTestState **from_ptr,
- QTestState **to_ptr,
- MigrateCommon *args)
-{
- QTestState *from, *to;
-
- if (test_migrate_start(&from, &to, "defer", &args->start)) {
- return -1;
- }
-
- if (args->start_hook) {
- args->postcopy_data = args->start_hook(from, to);
- }
-
- migrate_set_capability(from, "postcopy-ram", true);
- migrate_set_capability(to, "postcopy-ram", true);
- migrate_set_capability(to, "postcopy-blocktime", true);
-
- if (args->postcopy_preempt) {
- migrate_set_capability(from, "postcopy-preempt", true);
- migrate_set_capability(to, "postcopy-preempt", true);
- }
-
- migrate_ensure_non_converge(from);
-
- migrate_prepare_for_dirty_mem(from);
- qtest_qmp_assert_success(to, "{ 'execute': 'migrate-incoming',"
- " 'arguments': { "
- " 'channels': [ { 'channel-type': 'main',"
- " 'addr': { 'transport': 'socket',"
- " 'type': 'inet',"
- " 'host': '127.0.0.1',"
- " 'port': '0' } } ] } }");
-
- /* Wait for the first serial output from the source */
- wait_for_serial("src_serial");
- wait_for_suspend(from, &src_state);
-
- migrate_qmp(from, to, NULL, NULL, "{}");
-
- migrate_wait_for_dirty_mem(from, to);
-
- *from_ptr = from;
- *to_ptr = to;
-
- return 0;
-}
-
-static void migrate_postcopy_complete(QTestState *from, QTestState *to,
- MigrateCommon *args)
-{
- wait_for_migration_complete(from);
-
- if (args->start.suspend_me) {
- /* wakeup succeeds only if guest is suspended */
- qtest_qmp_assert_success(to, "{'execute': 'system_wakeup'}");
- }
-
- /* Make sure we get at least one "B" on destination */
- wait_for_serial("dest_serial");
-
- if (uffd_feature_thread_id) {
- read_blocktime(to);
- }
-
- if (args->finish_hook) {
- args->finish_hook(from, to, args->postcopy_data);
- args->postcopy_data = NULL;
- }
-
- test_migrate_end(from, to, true);
-}
-
-static void test_postcopy_common(MigrateCommon *args)
-{
- QTestState *from, *to;
-
- if (migrate_postcopy_prepare(&from, &to, args)) {
- return;
- }
- migrate_postcopy_start(from, to);
- migrate_postcopy_complete(from, to, args);
-}
-
-static void test_postcopy(void)
-{
- MigrateCommon args = { };
-
- test_postcopy_common(&args);
-}
-
-static void test_postcopy_suspend(void)
-{
- MigrateCommon args = {
- .start.suspend_me = true,
- };
-
- test_postcopy_common(&args);
-}
-
-static void test_postcopy_preempt(void)
-{
- MigrateCommon args = {
- .postcopy_preempt = true,
- };
-
- test_postcopy_common(&args);
-}
-
-#ifdef CONFIG_GNUTLS
-static void test_postcopy_tls_psk(void)
-{
- MigrateCommon args = {
- .start_hook = test_migrate_tls_psk_start_match,
- .finish_hook = test_migrate_tls_psk_finish,
- };
-
- test_postcopy_common(&args);
-}
-
-static void test_postcopy_preempt_tls_psk(void)
-{
- MigrateCommon args = {
- .postcopy_preempt = true,
- .start_hook = test_migrate_tls_psk_start_match,
- .finish_hook = test_migrate_tls_psk_finish,
- };
-
- test_postcopy_common(&args);
-}
-#endif
-
-static void wait_for_postcopy_status(QTestState *one, const char *status)
-{
- wait_for_migration_status(one, status,
- (const char * []) { "failed", "active",
- "completed", NULL });
-}
-
-static void postcopy_recover_fail(QTestState *from, QTestState *to,
- PostcopyRecoveryFailStage stage)
-{
-#ifndef _WIN32
- bool fail_early = (stage == POSTCOPY_FAIL_CHANNEL_ESTABLISH);
- int ret, pair1[2], pair2[2];
- char c;
-
- g_assert(stage > POSTCOPY_FAIL_NONE && stage < POSTCOPY_FAIL_MAX);
-
- /* Create two unrelated socketpairs */
- ret = qemu_socketpair(PF_LOCAL, SOCK_STREAM, 0, pair1);
- g_assert_cmpint(ret, ==, 0);
-
- ret = qemu_socketpair(PF_LOCAL, SOCK_STREAM, 0, pair2);
- g_assert_cmpint(ret, ==, 0);
-
- /*
- * Give the guests unpaired ends of the sockets, so they'll all blocked
- * at reading. This mimics a wrong channel established.
- */
- qtest_qmp_fds_assert_success(from, &pair1[0], 1,
- "{ 'execute': 'getfd',"
- " 'arguments': { 'fdname': 'fd-mig' }}");
- qtest_qmp_fds_assert_success(to, &pair2[0], 1,
- "{ 'execute': 'getfd',"
- " 'arguments': { 'fdname': 'fd-mig' }}");
-
- /*
- * Write the 1st byte as QEMU_VM_COMMAND (0x8) for the dest socket, to
- * emulate the 1st byte of a real recovery, but stops from there to
- * keep dest QEMU in RECOVER. This is needed so that we can kick off
- * the recover process on dest QEMU (by triggering the G_IO_IN event).
- *
- * NOTE: this trick is not needed on src QEMUs, because src doesn't
- * rely on an pre-existing G_IO_IN event, so it will always trigger the
- * upcoming recovery anyway even if it can read nothing.
- */
-#define QEMU_VM_COMMAND 0x08
- c = QEMU_VM_COMMAND;
- ret = send(pair2[1], &c, 1, 0);
- g_assert_cmpint(ret, ==, 1);
-
- if (stage == POSTCOPY_FAIL_CHANNEL_ESTABLISH) {
- /*
- * This will make src QEMU to fail at an early stage when trying to
- * resume later, where it shouldn't reach RECOVER stage at all.
- */
- close(pair1[1]);
- }
-
- migrate_recover(to, "fd:fd-mig");
- migrate_qmp(from, to, "fd:fd-mig", NULL, "{'resume': true}");
-
- /*
- * Source QEMU has an extra RECOVER_SETUP phase, dest doesn't have it.
- * Make sure it appears along the way.
- */
- migration_event_wait(from, "postcopy-recover-setup");
-
- if (fail_early) {
- /*
- * When fails at reconnection, src QEMU will automatically goes
- * back to PAUSED state. Making sure there is an event in this
- * case: Libvirt relies on this to detect early reconnection
- * errors.
- */
- migration_event_wait(from, "postcopy-paused");
- } else {
- /*
- * We want to test "fail later" at RECOVER stage here. Make sure
- * both QEMU instances will go into RECOVER stage first, then test
- * kicking them out using migrate-pause.
- *
- * Explicitly check the RECOVER event on src, that's what Libvirt
- * relies on, rather than polling.
- */
- migration_event_wait(from, "postcopy-recover");
- wait_for_postcopy_status(from, "postcopy-recover");
-
- /* Need an explicit kick on src QEMU in this case */
- migrate_pause(from);
- }
-
- /*
- * For all failure cases, we'll reach such states on both sides now.
- * Check them.
- */
- wait_for_postcopy_status(from, "postcopy-paused");
- wait_for_postcopy_status(to, "postcopy-recover");
-
- /*
- * Kick dest QEMU out too. This is normally not needed in reality
- * because when the channel is shutdown it should also happen on src.
- * However here we used separate socket pairs so we need to do that
- * explicitly.
- */
- migrate_pause(to);
- wait_for_postcopy_status(to, "postcopy-paused");
-
- close(pair1[0]);
- close(pair2[0]);
- close(pair2[1]);
-
- if (stage != POSTCOPY_FAIL_CHANNEL_ESTABLISH) {
- close(pair1[1]);
- }
-#endif
-}
-
-static void test_postcopy_recovery_common(MigrateCommon *args)
-{
- QTestState *from, *to;
- g_autofree char *uri = NULL;
-
- /* Always hide errors for postcopy recover tests since they're expected */
- args->start.hide_stderr = true;
-
- if (migrate_postcopy_prepare(&from, &to, args)) {
- return;
- }
-
- /* Turn postcopy speed down, 4K/s is slow enough on any machines */
- migrate_set_parameter_int(from, "max-postcopy-bandwidth", 4096);
-
- /* Now we start the postcopy */
- migrate_postcopy_start(from, to);
-
- /*
- * Wait until postcopy is really started; we can only run the
- * migrate-pause command during a postcopy
- */
- wait_for_migration_status(from, "postcopy-active", NULL);
-
- /*
- * Manually stop the postcopy migration. This emulates a network
- * failure with the migration socket
- */
- migrate_pause(from);
-
- /*
- * Wait for destination side to reach postcopy-paused state. The
- * migrate-recover command can only succeed if destination machine
- * is in the paused state
- */
- wait_for_postcopy_status(to, "postcopy-paused");
- wait_for_postcopy_status(from, "postcopy-paused");
-
- if (args->postcopy_recovery_fail_stage) {
- /*
- * Test when a wrong socket specified for recover, and then the
- * ability to kick it out, and continue with a correct socket.
- */
- postcopy_recover_fail(from, to, args->postcopy_recovery_fail_stage);
- /* continue with a good recovery */
- }
-
- /*
- * Create a new socket to emulate a new channel that is different
- * from the broken migration channel; tell the destination to
- * listen to the new port
- */
- uri = g_strdup_printf("unix:%s/migsocket-recover", tmpfs);
- migrate_recover(to, uri);
-
- /*
- * Try to rebuild the migration channel using the resume flag and
- * the newly created channel
- */
- migrate_qmp(from, to, uri, NULL, "{'resume': true}");
-
- /* Restore the postcopy bandwidth to unlimited */
- migrate_set_parameter_int(from, "max-postcopy-bandwidth", 0);
-
- migrate_postcopy_complete(from, to, args);
-}
-
-static void test_postcopy_recovery(void)
-{
- MigrateCommon args = { };
-
- test_postcopy_recovery_common(&args);
-}
-
-static void test_postcopy_recovery_fail_handshake(void)
-{
- MigrateCommon args = {
- .postcopy_recovery_fail_stage = POSTCOPY_FAIL_RECOVERY,
- };
-
- test_postcopy_recovery_common(&args);
-}
-
-static void test_postcopy_recovery_fail_reconnect(void)
-{
- MigrateCommon args = {
- .postcopy_recovery_fail_stage = POSTCOPY_FAIL_CHANNEL_ESTABLISH,
- };
-
- test_postcopy_recovery_common(&args);
-}
-
-#ifdef CONFIG_GNUTLS
-static void test_postcopy_recovery_tls_psk(void)
-{
- MigrateCommon args = {
- .start_hook = test_migrate_tls_psk_start_match,
- .finish_hook = test_migrate_tls_psk_finish,
- };
-
- test_postcopy_recovery_common(&args);
-}
-#endif
-
-static void test_postcopy_preempt_recovery(void)
-{
- MigrateCommon args = {
- .postcopy_preempt = true,
- };
-
- test_postcopy_recovery_common(&args);
-}
-
-#ifdef CONFIG_GNUTLS
-/* This contains preempt+recovery+tls test altogether */
-static void test_postcopy_preempt_all(void)
-{
- MigrateCommon args = {
- .postcopy_preempt = true,
- .start_hook = test_migrate_tls_psk_start_match,
- .finish_hook = test_migrate_tls_psk_finish,
- };
-
- test_postcopy_recovery_common(&args);
-}
-
-#endif
-
-static void test_baddest(void)
-{
- MigrateStart args = {
- .hide_stderr = true
- };
- QTestState *from, *to;
-
- if (test_migrate_start(&from, &to, "tcp:127.0.0.1:0", &args)) {
- return;
- }
- migrate_qmp(from, to, "tcp:127.0.0.1:0", NULL, "{}");
- wait_for_migration_fail(from, false);
- test_migrate_end(from, to, false);
-}
-
-#ifndef _WIN32
-static void test_analyze_script(void)
-{
- MigrateStart args = {
- .opts_source = "-uuid 11111111-1111-1111-1111-111111111111",
- };
- QTestState *from, *to;
- g_autofree char *uri = NULL;
- g_autofree char *file = NULL;
- int pid, wstatus;
- const char *python = g_getenv("PYTHON");
-
- if (!python) {
- g_test_skip("PYTHON variable not set");
- return;
- }
-
- /* dummy url */
- if (test_migrate_start(&from, &to, "tcp:127.0.0.1:0", &args)) {
- return;
- }
-
- /*
- * Setting these two capabilities causes the "configuration"
- * vmstate to include subsections for them. The script needs to
- * parse those subsections properly.
- */
- migrate_set_capability(from, "validate-uuid", true);
- migrate_set_capability(from, "x-ignore-shared", true);
-
- file = g_strdup_printf("%s/migfile", tmpfs);
- uri = g_strdup_printf("exec:cat > %s", file);
-
- migrate_ensure_converge(from);
- migrate_qmp(from, to, uri, NULL, "{}");
- wait_for_migration_complete(from);
-
- pid = fork();
- if (!pid) {
- close(1);
- open("/dev/null", O_WRONLY);
- execl(python, python, ANALYZE_SCRIPT, "-f", file, NULL);
- g_assert_not_reached();
- }
-
- g_assert(waitpid(pid, &wstatus, 0) == pid);
- if (!WIFEXITED(wstatus) || WEXITSTATUS(wstatus) != 0) {
- g_test_message("Failed to analyze the migration stream");
- g_test_fail();
- }
- test_migrate_end(from, to, false);
- cleanup("migfile");
-}
-
-static void test_vmstate_checker_script(void)
-{
- g_autofree gchar *cmd_src = NULL;
- g_autofree gchar *cmd_dst = NULL;
- g_autofree gchar *vmstate_src = NULL;
- g_autofree gchar *vmstate_dst = NULL;
- const char *machine_alias, *machine_opts = "";
- g_autofree char *machine = NULL;
- const char *arch = qtest_get_arch();
- int pid, wstatus;
- const char *python = g_getenv("PYTHON");
-
- if (!getenv(QEMU_ENV_SRC) && !getenv(QEMU_ENV_DST)) {
- g_test_skip("Test needs two different QEMU versions");
- return;
- }
-
- if (!python) {
- g_test_skip("PYTHON variable not set");
- return;
- }
-
- if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) {
- if (g_str_equal(arch, "i386")) {
- machine_alias = "pc";
- } else {
- machine_alias = "q35";
- }
- } else if (g_str_equal(arch, "s390x")) {
- machine_alias = "s390-ccw-virtio";
- } else if (strcmp(arch, "ppc64") == 0) {
- machine_alias = "pseries";
- } else if (strcmp(arch, "aarch64") == 0) {
- machine_alias = "virt";
- } else {
- g_assert_not_reached();
- }
-
- if (!qtest_has_machine(machine_alias)) {
- g_autofree char *msg = g_strdup_printf("machine %s not supported", machine_alias);
- g_test_skip(msg);
- return;
- }
-
- machine = resolve_machine_version(machine_alias, QEMU_ENV_SRC,
- QEMU_ENV_DST);
-
- vmstate_src = g_strdup_printf("%s/vmstate-src", tmpfs);
- vmstate_dst = g_strdup_printf("%s/vmstate-dst", tmpfs);
-
- cmd_dst = g_strdup_printf("-machine %s,%s -dump-vmstate %s",
- machine, machine_opts, vmstate_dst);
- cmd_src = g_strdup_printf("-machine %s,%s -dump-vmstate %s",
- machine, machine_opts, vmstate_src);
-
- qtest_init_with_env_no_handshake(QEMU_ENV_SRC, cmd_src);
- qtest_init_with_env_no_handshake(QEMU_ENV_DST, cmd_dst);
-
- pid = fork();
- if (!pid) {
- close(1);
- open("/dev/null", O_WRONLY);
- execl(python, python, VMSTATE_CHECKER_SCRIPT,
- "-s", vmstate_src,
- "-d", vmstate_dst,
- NULL);
- g_assert_not_reached();
- }
-
- g_assert(waitpid(pid, &wstatus, 0) == pid);
- if (!WIFEXITED(wstatus) || WEXITSTATUS(wstatus) != 0) {
- g_test_message("Failed to run vmstate-static-checker.py");
- g_test_fail();
- }
-
- cleanup("vmstate-src");
- cleanup("vmstate-dst");
-}
-#endif
-
-static void test_precopy_common(MigrateCommon *args)
-{
- QTestState *from, *to;
- void *data_hook = NULL;
-
- if (test_migrate_start(&from, &to, args->listen_uri, &args->start)) {
- return;
- }
-
- if (args->start_hook) {
- data_hook = args->start_hook(from, to);
- }
-
- /* Wait for the first serial output from the source */
- if (args->result == MIG_TEST_SUCCEED) {
- wait_for_serial("src_serial");
- wait_for_suspend(from, &src_state);
- }
-
- if (args->live) {
- migrate_ensure_non_converge(from);
- migrate_prepare_for_dirty_mem(from);
- } else {
- /*
- * Testing non-live migration, we allow it to run at
- * full speed to ensure short test case duration.
- * For tests expected to fail, we don't need to
- * change anything.
- */
- if (args->result == MIG_TEST_SUCCEED) {
- qtest_qmp_assert_success(from, "{ 'execute' : 'stop'}");
- wait_for_stop(from, &src_state);
- migrate_ensure_converge(from);
- }
- }
-
- if (args->result == MIG_TEST_QMP_ERROR) {
- migrate_qmp_fail(from, args->connect_uri, args->connect_channels, "{}");
- goto finish;
- }
-
- migrate_qmp(from, to, args->connect_uri, args->connect_channels, "{}");
-
- if (args->result != MIG_TEST_SUCCEED) {
- bool allow_active = args->result == MIG_TEST_FAIL;
- wait_for_migration_fail(from, allow_active);
-
- if (args->result == MIG_TEST_FAIL_DEST_QUIT_ERR) {
- qtest_set_expected_status(to, EXIT_FAILURE);
- }
- } else {
- if (args->live) {
- /*
- * For initial iteration(s) we must do a full pass,
- * but for the final iteration, we need only wait
- * for some dirty mem before switching to converge
- */
- while (args->iterations > 1) {
- wait_for_migration_pass(from);
- args->iterations--;
- }
- migrate_wait_for_dirty_mem(from, to);
-
- migrate_ensure_converge(from);
-
- /*
- * We do this first, as it has a timeout to stop us
- * hanging forever if migration didn't converge
- */
- wait_for_migration_complete(from);
-
- wait_for_stop(from, &src_state);
-
- } else {
- wait_for_migration_complete(from);
- /*
- * Must wait for dst to finish reading all incoming
- * data on the socket before issuing 'cont' otherwise
- * it'll be ignored
- */
- wait_for_migration_complete(to);
-
- qtest_qmp_assert_success(to, "{ 'execute' : 'cont'}");
- }
-
- wait_for_resume(to, &dst_state);
-
- if (args->start.suspend_me) {
- /* wakeup succeeds only if guest is suspended */
- qtest_qmp_assert_success(to, "{'execute': 'system_wakeup'}");
- }
-
- wait_for_serial("dest_serial");
- }
-
-finish:
- if (args->finish_hook) {
- args->finish_hook(from, to, data_hook);
- }
-
- test_migrate_end(from, to, args->result == MIG_TEST_SUCCEED);
-}
-
-static void file_dirty_offset_region(void)
-{
- g_autofree char *path = g_strdup_printf("%s/%s", tmpfs, FILE_TEST_FILENAME);
- size_t size = FILE_TEST_OFFSET;
- g_autofree char *data = g_new0(char, size);
-
- memset(data, FILE_TEST_MARKER, size);
- g_assert(g_file_set_contents(path, data, size, NULL));
-}
-
-static void file_check_offset_region(void)
-{
- g_autofree char *path = g_strdup_printf("%s/%s", tmpfs, FILE_TEST_FILENAME);
- size_t size = FILE_TEST_OFFSET;
- g_autofree char *expected = g_new0(char, size);
- g_autofree char *actual = NULL;
- uint64_t *stream_start;
-
- /*
- * Ensure the skipped offset region's data has not been touched
- * and the migration stream starts at the right place.
- */
-
- memset(expected, FILE_TEST_MARKER, size);
-
- g_assert(g_file_get_contents(path, &actual, NULL, NULL));
- g_assert(!memcmp(actual, expected, size));
-
- stream_start = (uint64_t *)(actual + size);
- g_assert_cmpint(cpu_to_be64(*stream_start) >> 32, ==, QEMU_VM_FILE_MAGIC);
-}
-
-static void test_file_common(MigrateCommon *args, bool stop_src)
-{
- QTestState *from, *to;
- void *data_hook = NULL;
- bool check_offset = false;
-
- if (test_migrate_start(&from, &to, args->listen_uri, &args->start)) {
- return;
- }
-
- /*
- * File migration is never live. We can keep the source VM running
- * during migration, but the destination will not be running
- * concurrently.
- */
- g_assert_false(args->live);
-
- if (g_strrstr(args->connect_uri, "offset=")) {
- check_offset = true;
- /*
- * This comes before the start_hook because it's equivalent to
- * a management application creating the file and writing to
- * it so hooks should expect the file to be already present.
- */
- file_dirty_offset_region();
- }
-
- if (args->start_hook) {
- data_hook = args->start_hook(from, to);
- }
-
- migrate_ensure_converge(from);
- wait_for_serial("src_serial");
-
- if (stop_src) {
- qtest_qmp_assert_success(from, "{ 'execute' : 'stop'}");
- wait_for_stop(from, &src_state);
- }
-
- if (args->result == MIG_TEST_QMP_ERROR) {
- migrate_qmp_fail(from, args->connect_uri, NULL, "{}");
- goto finish;
- }
-
- migrate_qmp(from, to, args->connect_uri, NULL, "{}");
- wait_for_migration_complete(from);
-
- /*
- * We need to wait for the source to finish before starting the
- * destination.
- */
- migrate_incoming_qmp(to, args->connect_uri, "{}");
- wait_for_migration_complete(to);
-
- if (stop_src) {
- qtest_qmp_assert_success(to, "{ 'execute' : 'cont'}");
- }
- wait_for_resume(to, &dst_state);
-
- wait_for_serial("dest_serial");
-
- if (check_offset) {
- file_check_offset_region();
- }
-
-finish:
- if (args->finish_hook) {
- args->finish_hook(from, to, data_hook);
- }
-
- test_migrate_end(from, to, args->result == MIG_TEST_SUCCEED);
-}
-
-static void test_precopy_unix_plain(void)
-{
- g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs);
- MigrateCommon args = {
- .listen_uri = uri,
- .connect_uri = uri,
- /*
- * The simplest use case of precopy, covering smoke tests of
- * get-dirty-log dirty tracking.
- */
- .live = true,
- };
-
- test_precopy_common(&args);
-}
-
-static void test_precopy_unix_suspend_live(void)
-{
- g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs);
- MigrateCommon args = {
- .listen_uri = uri,
- .connect_uri = uri,
- /*
- * despite being live, the test is fast because the src
- * suspends immediately.
- */
- .live = true,
- .start.suspend_me = true,
- };
-
- test_precopy_common(&args);
-}
-
-static void test_precopy_unix_suspend_notlive(void)
-{
- g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs);
- MigrateCommon args = {
- .listen_uri = uri,
- .connect_uri = uri,
- .start.suspend_me = true,
- };
-
- test_precopy_common(&args);
-}
-
-static void test_precopy_unix_dirty_ring(void)
-{
- g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs);
- MigrateCommon args = {
- .start = {
- .use_dirty_ring = true,
- },
- .listen_uri = uri,
- .connect_uri = uri,
- /*
- * Besides the precopy/unix basic test, cover dirty ring interface
- * rather than get-dirty-log.
- */
- .live = true,
- };
-
- test_precopy_common(&args);
-}
-
-#ifdef CONFIG_GNUTLS
-static void test_precopy_unix_tls_psk(void)
-{
- g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs);
- MigrateCommon args = {
- .connect_uri = uri,
- .listen_uri = uri,
- .start_hook = test_migrate_tls_psk_start_match,
- .finish_hook = test_migrate_tls_psk_finish,
- };
-
- test_precopy_common(&args);
-}
-
-#ifdef CONFIG_TASN1
-static void test_precopy_unix_tls_x509_default_host(void)
-{
- g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs);
- MigrateCommon args = {
- .start = {
- .hide_stderr = true,
- },
- .connect_uri = uri,
- .listen_uri = uri,
- .start_hook = test_migrate_tls_x509_start_default_host,
- .finish_hook = test_migrate_tls_x509_finish,
- .result = MIG_TEST_FAIL_DEST_QUIT_ERR,
- };
-
- test_precopy_common(&args);
-}
-
-static void test_precopy_unix_tls_x509_override_host(void)
-{
- g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs);
- MigrateCommon args = {
- .connect_uri = uri,
- .listen_uri = uri,
- .start_hook = test_migrate_tls_x509_start_override_host,
- .finish_hook = test_migrate_tls_x509_finish,
- };
-
- test_precopy_common(&args);
-}
-#endif /* CONFIG_TASN1 */
-#endif /* CONFIG_GNUTLS */
-
-#if 0
-/* Currently upset on aarch64 TCG */
-static void test_ignore_shared(void)
-{
- g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs);
- QTestState *from, *to;
-
- if (test_migrate_start(&from, &to, uri, false, true, NULL, NULL)) {
- return;
- }
-
- migrate_ensure_non_converge(from);
- migrate_prepare_for_dirty_mem(from);
-
- migrate_set_capability(from, "x-ignore-shared", true);
- migrate_set_capability(to, "x-ignore-shared", true);
-
- /* Wait for the first serial output from the source */
- wait_for_serial("src_serial");
-
- migrate_qmp(from, to, uri, NULL, "{}");
-
- migrate_wait_for_dirty_mem(from, to);
-
- wait_for_stop(from, &src_state);
-
- qtest_qmp_eventwait(to, "RESUME");
-
- wait_for_serial("dest_serial");
- wait_for_migration_complete(from);
-
- /* Check whether shared RAM has been really skipped */
- g_assert_cmpint(read_ram_property_int(from, "transferred"), <, 1024 * 1024);
-
- test_migrate_end(from, to, true);
-}
-#endif
-
-static void *
-test_migrate_xbzrle_start(QTestState *from,
- QTestState *to)
-{
- migrate_set_parameter_int(from, "xbzrle-cache-size", 33554432);
-
- migrate_set_capability(from, "xbzrle", true);
- migrate_set_capability(to, "xbzrle", true);
-
- return NULL;
-}
-
-static void test_precopy_unix_xbzrle(void)
-{
- g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs);
- MigrateCommon args = {
- .connect_uri = uri,
- .listen_uri = uri,
- .start_hook = test_migrate_xbzrle_start,
- .iterations = 2,
- /*
- * XBZRLE needs pages to be modified when doing the 2nd+ round
- * iteration to have real data pushed to the stream.
- */
- .live = true,
- };
-
- test_precopy_common(&args);
-}
-
-static void test_precopy_file(void)
-{
- g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs,
- FILE_TEST_FILENAME);
- MigrateCommon args = {
- .connect_uri = uri,
- .listen_uri = "defer",
- };
-
- test_file_common(&args, true);
-}
-
-#ifndef _WIN32
-static void fdset_add_fds(QTestState *qts, const char *file, int flags,
- int num_fds, bool direct_io)
-{
- for (int i = 0; i < num_fds; i++) {
- int fd;
-
-#ifdef O_DIRECT
- /* only secondary channels can use direct-io */
- if (direct_io && i != 0) {
- flags |= O_DIRECT;
- }
-#endif
-
- fd = open(file, flags, 0660);
- assert(fd != -1);
-
- qtest_qmp_fds_assert_success(qts, &fd, 1, "{'execute': 'add-fd', "
- "'arguments': {'fdset-id': 1}}");
- close(fd);
- }
-}
-
-static void *file_offset_fdset_start_hook(QTestState *from, QTestState *to)
-{
- g_autofree char *file = g_strdup_printf("%s/%s", tmpfs, FILE_TEST_FILENAME);
-
- fdset_add_fds(from, file, O_WRONLY, 1, false);
- fdset_add_fds(to, file, O_RDONLY, 1, false);
-
- return NULL;
-}
-
-static void test_precopy_file_offset_fdset(void)
-{
- g_autofree char *uri = g_strdup_printf("file:/dev/fdset/1,offset=%d",
- FILE_TEST_OFFSET);
- MigrateCommon args = {
- .connect_uri = uri,
- .listen_uri = "defer",
- .start_hook = file_offset_fdset_start_hook,
- };
-
- test_file_common(&args, false);
-}
-#endif
-
-static void test_precopy_file_offset(void)
-{
- g_autofree char *uri = g_strdup_printf("file:%s/%s,offset=%d", tmpfs,
- FILE_TEST_FILENAME,
- FILE_TEST_OFFSET);
- MigrateCommon args = {
- .connect_uri = uri,
- .listen_uri = "defer",
- };
-
- test_file_common(&args, false);
-}
-
-static void test_precopy_file_offset_bad(void)
-{
- /* using a value not supported by qemu_strtosz() */
- g_autofree char *uri = g_strdup_printf("file:%s/%s,offset=0x20M",
- tmpfs, FILE_TEST_FILENAME);
- MigrateCommon args = {
- .connect_uri = uri,
- .listen_uri = "defer",
- .result = MIG_TEST_QMP_ERROR,
- };
-
- test_file_common(&args, false);
-}
-
-static void *test_mode_reboot_start(QTestState *from, QTestState *to)
-{
- migrate_set_parameter_str(from, "mode", "cpr-reboot");
- migrate_set_parameter_str(to, "mode", "cpr-reboot");
-
- migrate_set_capability(from, "x-ignore-shared", true);
- migrate_set_capability(to, "x-ignore-shared", true);
-
- return NULL;
-}
-
-static void *migrate_mapped_ram_start(QTestState *from, QTestState *to)
-{
- migrate_set_capability(from, "mapped-ram", true);
- migrate_set_capability(to, "mapped-ram", true);
-
- return NULL;
-}
-
-static void test_mode_reboot(void)
-{
- g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs,
- FILE_TEST_FILENAME);
- MigrateCommon args = {
- .start.use_shmem = true,
- .connect_uri = uri,
- .listen_uri = "defer",
- .start_hook = test_mode_reboot_start
- };
-
- test_file_common(&args, true);
-}
-
-static void test_precopy_file_mapped_ram_live(void)
-{
- g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs,
- FILE_TEST_FILENAME);
- MigrateCommon args = {
- .connect_uri = uri,
- .listen_uri = "defer",
- .start_hook = migrate_mapped_ram_start,
- };
-
- test_file_common(&args, false);
-}
-
-static void test_precopy_file_mapped_ram(void)
-{
- g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs,
- FILE_TEST_FILENAME);
- MigrateCommon args = {
- .connect_uri = uri,
- .listen_uri = "defer",
- .start_hook = migrate_mapped_ram_start,
- };
-
- test_file_common(&args, true);
-}
-
-static void *migrate_multifd_mapped_ram_start(QTestState *from, QTestState *to)
-{
- migrate_mapped_ram_start(from, to);
-
- migrate_set_parameter_int(from, "multifd-channels", 4);
- migrate_set_parameter_int(to, "multifd-channels", 4);
-
- migrate_set_capability(from, "multifd", true);
- migrate_set_capability(to, "multifd", true);
-
- return NULL;
-}
-
-static void test_multifd_file_mapped_ram_live(void)
-{
- g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs,
- FILE_TEST_FILENAME);
- MigrateCommon args = {
- .connect_uri = uri,
- .listen_uri = "defer",
- .start_hook = migrate_multifd_mapped_ram_start,
- };
-
- test_file_common(&args, false);
-}
-
-static void test_multifd_file_mapped_ram(void)
-{
- g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs,
- FILE_TEST_FILENAME);
- MigrateCommon args = {
- .connect_uri = uri,
- .listen_uri = "defer",
- .start_hook = migrate_multifd_mapped_ram_start,
- };
-
- test_file_common(&args, true);
-}
-
-static void *multifd_mapped_ram_dio_start(QTestState *from, QTestState *to)
-{
- migrate_multifd_mapped_ram_start(from, to);
-
- migrate_set_parameter_bool(from, "direct-io", true);
- migrate_set_parameter_bool(to, "direct-io", true);
-
- return NULL;
-}
-
-static void test_multifd_file_mapped_ram_dio(void)
-{
- g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs,
- FILE_TEST_FILENAME);
- MigrateCommon args = {
- .connect_uri = uri,
- .listen_uri = "defer",
- .start_hook = multifd_mapped_ram_dio_start,
- };
-
- if (!probe_o_direct_support(tmpfs)) {
- g_test_skip("Filesystem does not support O_DIRECT");
- return;
- }
-
- test_file_common(&args, true);
-}
-
-#ifndef _WIN32
-static void multifd_mapped_ram_fdset_end(QTestState *from, QTestState *to,
- void *opaque)
-{
- QDict *resp;
- QList *fdsets;
-
- /*
- * Remove the fdsets after migration, otherwise a second migration
- * would fail due fdset reuse.
- */
- qtest_qmp_assert_success(from, "{'execute': 'remove-fd', "
- "'arguments': { 'fdset-id': 1}}");
-
- /*
- * Make sure no fdsets are left after migration, otherwise a
- * second migration would fail due fdset reuse.
- */
- resp = qtest_qmp(from, "{'execute': 'query-fdsets', "
- "'arguments': {}}");
- g_assert(qdict_haskey(resp, "return"));
- fdsets = qdict_get_qlist(resp, "return");
- g_assert(fdsets && qlist_empty(fdsets));
-}
-
-static void *multifd_mapped_ram_fdset_dio(QTestState *from, QTestState *to)
-{
- g_autofree char *file = g_strdup_printf("%s/%s", tmpfs, FILE_TEST_FILENAME);
-
- fdset_add_fds(from, file, O_WRONLY, 2, true);
- fdset_add_fds(to, file, O_RDONLY, 2, true);
-
- migrate_multifd_mapped_ram_start(from, to);
- migrate_set_parameter_bool(from, "direct-io", true);
- migrate_set_parameter_bool(to, "direct-io", true);
-
- return NULL;
-}
-
-static void *multifd_mapped_ram_fdset(QTestState *from, QTestState *to)
-{
- g_autofree char *file = g_strdup_printf("%s/%s", tmpfs, FILE_TEST_FILENAME);
-
- fdset_add_fds(from, file, O_WRONLY, 2, false);
- fdset_add_fds(to, file, O_RDONLY, 2, false);
-
- migrate_multifd_mapped_ram_start(from, to);
-
- return NULL;
-}
-
-static void test_multifd_file_mapped_ram_fdset(void)
-{
- g_autofree char *uri = g_strdup_printf("file:/dev/fdset/1,offset=%d",
- FILE_TEST_OFFSET);
- MigrateCommon args = {
- .connect_uri = uri,
- .listen_uri = "defer",
- .start_hook = multifd_mapped_ram_fdset,
- .finish_hook = multifd_mapped_ram_fdset_end,
- };
-
- test_file_common(&args, true);
-}
-
-static void test_multifd_file_mapped_ram_fdset_dio(void)
-{
- g_autofree char *uri = g_strdup_printf("file:/dev/fdset/1,offset=%d",
- FILE_TEST_OFFSET);
- MigrateCommon args = {
- .connect_uri = uri,
- .listen_uri = "defer",
- .start_hook = multifd_mapped_ram_fdset_dio,
- .finish_hook = multifd_mapped_ram_fdset_end,
- };
-
- if (!probe_o_direct_support(tmpfs)) {
- g_test_skip("Filesystem does not support O_DIRECT");
- return;
- }
-
- test_file_common(&args, true);
-}
-#endif /* !_WIN32 */
-
-static void test_precopy_tcp_plain(void)
-{
- MigrateCommon args = {
- .listen_uri = "tcp:127.0.0.1:0",
- };
-
- test_precopy_common(&args);
-}
-
-static void *test_migrate_switchover_ack_start(QTestState *from, QTestState *to)
-{
-
- migrate_set_capability(from, "return-path", true);
- migrate_set_capability(to, "return-path", true);
-
- migrate_set_capability(from, "switchover-ack", true);
- migrate_set_capability(to, "switchover-ack", true);
-
- return NULL;
-}
-
-static void test_precopy_tcp_switchover_ack(void)
-{
- MigrateCommon args = {
- .listen_uri = "tcp:127.0.0.1:0",
- .start_hook = test_migrate_switchover_ack_start,
- /*
- * Source VM must be running in order to consider the switchover ACK
- * when deciding to do switchover or not.
- */
- .live = true,
- };
-
- test_precopy_common(&args);
-}
-
-#ifdef CONFIG_GNUTLS
-static void test_precopy_tcp_tls_psk_match(void)
-{
- MigrateCommon args = {
- .listen_uri = "tcp:127.0.0.1:0",
- .start_hook = test_migrate_tls_psk_start_match,
- .finish_hook = test_migrate_tls_psk_finish,
- };
-
- test_precopy_common(&args);
-}
-
-static void test_precopy_tcp_tls_psk_mismatch(void)
-{
- MigrateCommon args = {
- .start = {
- .hide_stderr = true,
- },
- .listen_uri = "tcp:127.0.0.1:0",
- .start_hook = test_migrate_tls_psk_start_mismatch,
- .finish_hook = test_migrate_tls_psk_finish,
- .result = MIG_TEST_FAIL,
- };
-
- test_precopy_common(&args);
-}
-
-#ifdef CONFIG_TASN1
-static void test_precopy_tcp_tls_x509_default_host(void)
-{
- MigrateCommon args = {
- .listen_uri = "tcp:127.0.0.1:0",
- .start_hook = test_migrate_tls_x509_start_default_host,
- .finish_hook = test_migrate_tls_x509_finish,
- };
-
- test_precopy_common(&args);
-}
-
-static void test_precopy_tcp_tls_x509_override_host(void)
-{
- MigrateCommon args = {
- .listen_uri = "tcp:127.0.0.1:0",
- .start_hook = test_migrate_tls_x509_start_override_host,
- .finish_hook = test_migrate_tls_x509_finish,
- };
-
- test_precopy_common(&args);
-}
-
-static void test_precopy_tcp_tls_x509_mismatch_host(void)
-{
- MigrateCommon args = {
- .start = {
- .hide_stderr = true,
- },
- .listen_uri = "tcp:127.0.0.1:0",
- .start_hook = test_migrate_tls_x509_start_mismatch_host,
- .finish_hook = test_migrate_tls_x509_finish,
- .result = MIG_TEST_FAIL_DEST_QUIT_ERR,
- };
-
- test_precopy_common(&args);
-}
-
-static void test_precopy_tcp_tls_x509_friendly_client(void)
-{
- MigrateCommon args = {
- .listen_uri = "tcp:127.0.0.1:0",
- .start_hook = test_migrate_tls_x509_start_friendly_client,
- .finish_hook = test_migrate_tls_x509_finish,
- };
-
- test_precopy_common(&args);
-}
-
-static void test_precopy_tcp_tls_x509_hostile_client(void)
-{
- MigrateCommon args = {
- .start = {
- .hide_stderr = true,
- },
- .listen_uri = "tcp:127.0.0.1:0",
- .start_hook = test_migrate_tls_x509_start_hostile_client,
- .finish_hook = test_migrate_tls_x509_finish,
- .result = MIG_TEST_FAIL,
- };
-
- test_precopy_common(&args);
-}
-
-static void test_precopy_tcp_tls_x509_allow_anon_client(void)
-{
- MigrateCommon args = {
- .listen_uri = "tcp:127.0.0.1:0",
- .start_hook = test_migrate_tls_x509_start_allow_anon_client,
- .finish_hook = test_migrate_tls_x509_finish,
- };
-
- test_precopy_common(&args);
-}
-
-static void test_precopy_tcp_tls_x509_reject_anon_client(void)
-{
- MigrateCommon args = {
- .start = {
- .hide_stderr = true,
- },
- .listen_uri = "tcp:127.0.0.1:0",
- .start_hook = test_migrate_tls_x509_start_reject_anon_client,
- .finish_hook = test_migrate_tls_x509_finish,
- .result = MIG_TEST_FAIL,
- };
-
- test_precopy_common(&args);
-}
-#endif /* CONFIG_TASN1 */
-#endif /* CONFIG_GNUTLS */
-
-#ifndef _WIN32
-static void *test_migrate_fd_start_hook(QTestState *from,
- QTestState *to)
-{
- int ret;
- int pair[2];
-
- /* Create two connected sockets for migration */
- ret = qemu_socketpair(PF_LOCAL, SOCK_STREAM, 0, pair);
- g_assert_cmpint(ret, ==, 0);
-
- /* Send the 1st socket to the target */
- qtest_qmp_fds_assert_success(to, &pair[0], 1,
- "{ 'execute': 'getfd',"
- " 'arguments': { 'fdname': 'fd-mig' }}");
- close(pair[0]);
-
- /* Start incoming migration from the 1st socket */
- migrate_incoming_qmp(to, "fd:fd-mig", "{}");
-
- /* Send the 2nd socket to the target */
- qtest_qmp_fds_assert_success(from, &pair[1], 1,
- "{ 'execute': 'getfd',"
- " 'arguments': { 'fdname': 'fd-mig' }}");
- close(pair[1]);
-
- return NULL;
-}
-
-static void test_migrate_fd_finish_hook(QTestState *from,
- QTestState *to,
- void *opaque)
-{
- QDict *rsp;
- const char *error_desc;
-
- /* Test closing fds */
- /* We assume, that QEMU removes named fd from its list,
- * so this should fail */
- rsp = qtest_qmp(from, "{ 'execute': 'closefd',"
- " 'arguments': { 'fdname': 'fd-mig' }}");
- g_assert_true(qdict_haskey(rsp, "error"));
- error_desc = qdict_get_str(qdict_get_qdict(rsp, "error"), "desc");
- g_assert_cmpstr(error_desc, ==, "File descriptor named 'fd-mig' not found");
- qobject_unref(rsp);
-
- rsp = qtest_qmp(to, "{ 'execute': 'closefd',"
- " 'arguments': { 'fdname': 'fd-mig' }}");
- g_assert_true(qdict_haskey(rsp, "error"));
- error_desc = qdict_get_str(qdict_get_qdict(rsp, "error"), "desc");
- g_assert_cmpstr(error_desc, ==, "File descriptor named 'fd-mig' not found");
- qobject_unref(rsp);
-}
-
-static void test_migrate_precopy_fd_socket(void)
-{
- MigrateCommon args = {
- .listen_uri = "defer",
- .connect_uri = "fd:fd-mig",
- .start_hook = test_migrate_fd_start_hook,
- .finish_hook = test_migrate_fd_finish_hook
- };
- test_precopy_common(&args);
-}
-
-static void *migrate_precopy_fd_file_start(QTestState *from, QTestState *to)
-{
- g_autofree char *file = g_strdup_printf("%s/%s", tmpfs, FILE_TEST_FILENAME);
- int src_flags = O_CREAT | O_RDWR;
- int dst_flags = O_CREAT | O_RDWR;
- int fds[2];
-
- fds[0] = open(file, src_flags, 0660);
- assert(fds[0] != -1);
-
- fds[1] = open(file, dst_flags, 0660);
- assert(fds[1] != -1);
-
-
- qtest_qmp_fds_assert_success(to, &fds[0], 1,
- "{ 'execute': 'getfd',"
- " 'arguments': { 'fdname': 'fd-mig' }}");
-
- qtest_qmp_fds_assert_success(from, &fds[1], 1,
- "{ 'execute': 'getfd',"
- " 'arguments': { 'fdname': 'fd-mig' }}");
-
- close(fds[0]);
- close(fds[1]);
-
- return NULL;
-}
-
-static void test_migrate_precopy_fd_file(void)
-{
- MigrateCommon args = {
- .listen_uri = "defer",
- .connect_uri = "fd:fd-mig",
- .start_hook = migrate_precopy_fd_file_start,
- .finish_hook = test_migrate_fd_finish_hook
- };
- test_file_common(&args, true);
-}
-#endif /* _WIN32 */
-
-static void do_test_validate_uuid(MigrateStart *args, bool should_fail)
-{
- g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs);
- QTestState *from, *to;
-
- if (test_migrate_start(&from, &to, uri, args)) {
- return;
- }
-
- /*
- * UUID validation is at the begin of migration. So, the main process of
- * migration is not interesting for us here. Thus, set huge downtime for
- * very fast migration.
- */
- migrate_set_parameter_int(from, "downtime-limit", 1000000);
- migrate_set_capability(from, "validate-uuid", true);
-
- /* Wait for the first serial output from the source */
- wait_for_serial("src_serial");
-
- migrate_qmp(from, to, uri, NULL, "{}");
-
- if (should_fail) {
- qtest_set_expected_status(to, EXIT_FAILURE);
- wait_for_migration_fail(from, true);
- } else {
- wait_for_migration_complete(from);
- }
-
- test_migrate_end(from, to, false);
-}
-
-static void test_validate_uuid(void)
-{
- MigrateStart args = {
- .opts_source = "-uuid 11111111-1111-1111-1111-111111111111",
- .opts_target = "-uuid 11111111-1111-1111-1111-111111111111",
- };
-
- do_test_validate_uuid(&args, false);
-}
-
-static void test_validate_uuid_error(void)
-{
- MigrateStart args = {
- .opts_source = "-uuid 11111111-1111-1111-1111-111111111111",
- .opts_target = "-uuid 22222222-2222-2222-2222-222222222222",
- .hide_stderr = true,
- };
-
- do_test_validate_uuid(&args, true);
-}
-
-static void test_validate_uuid_src_not_set(void)
-{
- MigrateStart args = {
- .opts_target = "-uuid 22222222-2222-2222-2222-222222222222",
- .hide_stderr = true,
- };
-
- do_test_validate_uuid(&args, false);
-}
-
-static void test_validate_uuid_dst_not_set(void)
-{
- MigrateStart args = {
- .opts_source = "-uuid 11111111-1111-1111-1111-111111111111",
- .hide_stderr = true,
- };
-
- do_test_validate_uuid(&args, false);
-}
-
-static void do_test_validate_uri_channel(MigrateCommon *args)
-{
- QTestState *from, *to;
-
- if (test_migrate_start(&from, &to, args->listen_uri, &args->start)) {
- return;
- }
-
- /* Wait for the first serial output from the source */
- wait_for_serial("src_serial");
-
- /*
- * 'uri' and 'channels' validation is checked even before the migration
- * starts.
- */
- migrate_qmp_fail(from, args->connect_uri, args->connect_channels, "{}");
- test_migrate_end(from, to, false);
-}
-
-static void test_validate_uri_channels_both_set(void)
-{
- MigrateCommon args = {
- .start = {
- .hide_stderr = true,
- },
- .listen_uri = "defer",
- .connect_uri = "tcp:127.0.0.1:0",
- .connect_channels = "[ { 'channel-type': 'main',"
- " 'addr': { 'transport': 'socket',"
- " 'type': 'inet',"
- " 'host': '127.0.0.1',"
- " 'port': '0' } } ]",
- };
-
- do_test_validate_uri_channel(&args);
-}
-
-static void test_validate_uri_channels_none_set(void)
-{
- MigrateCommon args = {
- .start = {
- .hide_stderr = true,
- },
- .listen_uri = "defer",
- };
-
- do_test_validate_uri_channel(&args);
-}
-
-/*
- * The way auto_converge works, we need to do too many passes to
- * run this test. Auto_converge logic is only run once every
- * three iterations, so:
- *
- * - 3 iterations without auto_converge enabled
- * - 3 iterations with pct = 5
- * - 3 iterations with pct = 30
- * - 3 iterations with pct = 55
- * - 3 iterations with pct = 80
- * - 3 iterations with pct = 95 (max(95, 80 + 25))
- *
- * To make things even worse, we need to run the initial stage at
- * 3MB/s so we enter autoconverge even when host is (over)loaded.
- */
-static void test_migrate_auto_converge(void)
-{
- g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs);
- MigrateStart args = {};
- QTestState *from, *to;
- int64_t percentage;
-
- /*
- * We want the test to be stable and as fast as possible.
- * E.g., with 1Gb/s bandwidth migration may pass without throttling,
- * so we need to decrease a bandwidth.
- */
- const int64_t init_pct = 5, inc_pct = 25, max_pct = 95;
-
- if (test_migrate_start(&from, &to, uri, &args)) {
- return;
- }
-
- migrate_set_capability(from, "auto-converge", true);
- migrate_set_parameter_int(from, "cpu-throttle-initial", init_pct);
- migrate_set_parameter_int(from, "cpu-throttle-increment", inc_pct);
- migrate_set_parameter_int(from, "max-cpu-throttle", max_pct);
-
- /*
- * Set the initial parameters so that the migration could not converge
- * without throttling.
- */
- migrate_ensure_non_converge(from);
-
- /* To check remaining size after precopy */
- migrate_set_capability(from, "pause-before-switchover", true);
-
- /* Wait for the first serial output from the source */
- wait_for_serial("src_serial");
-
- migrate_qmp(from, to, uri, NULL, "{}");
-
- /* Wait for throttling begins */
- percentage = 0;
- do {
- percentage = read_migrate_property_int(from, "cpu-throttle-percentage");
- if (percentage != 0) {
- break;
- }
- usleep(20);
- g_assert_false(src_state.stop_seen);
- } while (true);
- /* The first percentage of throttling should be at least init_pct */
- g_assert_cmpint(percentage, >=, init_pct);
- /* Now, when we tested that throttling works, let it converge */
- migrate_ensure_converge(from);
-
- /*
- * Wait for pre-switchover status to check last throttle percentage
- * and remaining. These values will be zeroed later
- */
- wait_for_migration_status(from, "pre-switchover", NULL);
-
- /* The final percentage of throttling shouldn't be greater than max_pct */
- percentage = read_migrate_property_int(from, "cpu-throttle-percentage");
- g_assert_cmpint(percentage, <=, max_pct);
- migrate_continue(from, "pre-switchover");
-
- qtest_qmp_eventwait(to, "RESUME");
- wait_for_serial("dest_serial");
- wait_for_migration_complete(from);
-
- test_migrate_end(from, to, true);
-}
-
-static void *
-test_migrate_precopy_tcp_multifd_start_common(QTestState *from,
- QTestState *to,
- const char *method)
-{
- migrate_set_parameter_int(from, "multifd-channels", 16);
- migrate_set_parameter_int(to, "multifd-channels", 16);
-
- migrate_set_parameter_str(from, "multifd-compression", method);
- migrate_set_parameter_str(to, "multifd-compression", method);
-
- migrate_set_capability(from, "multifd", true);
- migrate_set_capability(to, "multifd", true);
-
- /* Start incoming migration from the 1st socket */
- migrate_incoming_qmp(to, "tcp:127.0.0.1:0", "{}");
-
- return NULL;
-}
-
-static void *
-test_migrate_precopy_tcp_multifd_start(QTestState *from,
- QTestState *to)
-{
- return test_migrate_precopy_tcp_multifd_start_common(from, to, "none");
-}
-
-static void *
-test_migrate_precopy_tcp_multifd_start_zero_page_legacy(QTestState *from,
- QTestState *to)
-{
- test_migrate_precopy_tcp_multifd_start_common(from, to, "none");
- migrate_set_parameter_str(from, "zero-page-detection", "legacy");
- return NULL;
-}
-
-static void *
-test_migration_precopy_tcp_multifd_start_no_zero_page(QTestState *from,
- QTestState *to)
-{
- test_migrate_precopy_tcp_multifd_start_common(from, to, "none");
- migrate_set_parameter_str(from, "zero-page-detection", "none");
- return NULL;
-}
-
-static void *
-test_migrate_precopy_tcp_multifd_zlib_start(QTestState *from,
- QTestState *to)
-{
- /*
- * Overloading this test to also check that set_parameter does not error.
- * This is also done in the tests for the other compression methods.
- */
- migrate_set_parameter_int(from, "multifd-zlib-level", 2);
- migrate_set_parameter_int(to, "multifd-zlib-level", 2);
-
- return test_migrate_precopy_tcp_multifd_start_common(from, to, "zlib");
-}
-
-#ifdef CONFIG_ZSTD
-static void *
-test_migrate_precopy_tcp_multifd_zstd_start(QTestState *from,
- QTestState *to)
-{
- migrate_set_parameter_int(from, "multifd-zstd-level", 2);
- migrate_set_parameter_int(to, "multifd-zstd-level", 2);
-
- return test_migrate_precopy_tcp_multifd_start_common(from, to, "zstd");
-}
-#endif /* CONFIG_ZSTD */
-
-#ifdef CONFIG_QPL
-static void *
-test_migrate_precopy_tcp_multifd_qpl_start(QTestState *from,
- QTestState *to)
-{
- return test_migrate_precopy_tcp_multifd_start_common(from, to, "qpl");
-}
-#endif /* CONFIG_QPL */
-#ifdef CONFIG_UADK
-static void *
-test_migrate_precopy_tcp_multifd_uadk_start(QTestState *from,
- QTestState *to)
-{
- return test_migrate_precopy_tcp_multifd_start_common(from, to, "uadk");
-}
-#endif /* CONFIG_UADK */
-
-static void test_multifd_tcp_uri_none(void)
-{
- MigrateCommon args = {
- .listen_uri = "defer",
- .start_hook = test_migrate_precopy_tcp_multifd_start,
- /*
- * Multifd is more complicated than most of the features, it
- * directly takes guest page buffers when sending, make sure
- * everything will work alright even if guest page is changing.
- */
- .live = true,
- };
- test_precopy_common(&args);
-}
-
-static void test_multifd_tcp_zero_page_legacy(void)
-{
- MigrateCommon args = {
- .listen_uri = "defer",
- .start_hook = test_migrate_precopy_tcp_multifd_start_zero_page_legacy,
- /*
- * Multifd is more complicated than most of the features, it
- * directly takes guest page buffers when sending, make sure
- * everything will work alright even if guest page is changing.
- */
- .live = true,
- };
- test_precopy_common(&args);
-}
-
-static void test_multifd_tcp_no_zero_page(void)
-{
- MigrateCommon args = {
- .listen_uri = "defer",
- .start_hook = test_migration_precopy_tcp_multifd_start_no_zero_page,
- /*
- * Multifd is more complicated than most of the features, it
- * directly takes guest page buffers when sending, make sure
- * everything will work alright even if guest page is changing.
- */
- .live = true,
- };
- test_precopy_common(&args);
-}
-
-static void test_multifd_tcp_channels_none(void)
-{
- MigrateCommon args = {
- .listen_uri = "defer",
- .start_hook = test_migrate_precopy_tcp_multifd_start,
- .live = true,
- .connect_channels = "[ { 'channel-type': 'main',"
- " 'addr': { 'transport': 'socket',"
- " 'type': 'inet',"
- " 'host': '127.0.0.1',"
- " 'port': '0' } } ]",
- };
- test_precopy_common(&args);
-}
-
-static void test_multifd_tcp_zlib(void)
-{
- MigrateCommon args = {
- .listen_uri = "defer",
- .start_hook = test_migrate_precopy_tcp_multifd_zlib_start,
- };
- test_precopy_common(&args);
-}
-
-#ifdef CONFIG_ZSTD
-static void test_multifd_tcp_zstd(void)
-{
- MigrateCommon args = {
- .listen_uri = "defer",
- .start_hook = test_migrate_precopy_tcp_multifd_zstd_start,
- };
- test_precopy_common(&args);
-}
-#endif
-
-#ifdef CONFIG_QPL
-static void test_multifd_tcp_qpl(void)
-{
- MigrateCommon args = {
- .listen_uri = "defer",
- .start_hook = test_migrate_precopy_tcp_multifd_qpl_start,
- };
- test_precopy_common(&args);
-}
-#endif
-
-#ifdef CONFIG_UADK
-static void test_multifd_tcp_uadk(void)
-{
- MigrateCommon args = {
- .listen_uri = "defer",
- .start_hook = test_migrate_precopy_tcp_multifd_uadk_start,
- };
- test_precopy_common(&args);
-}
-#endif
-
-#ifdef CONFIG_GNUTLS
-static void *
-test_migrate_multifd_tcp_tls_psk_start_match(QTestState *from,
- QTestState *to)
-{
- test_migrate_precopy_tcp_multifd_start_common(from, to, "none");
- return test_migrate_tls_psk_start_match(from, to);
-}
-
-static void *
-test_migrate_multifd_tcp_tls_psk_start_mismatch(QTestState *from,
- QTestState *to)
-{
- test_migrate_precopy_tcp_multifd_start_common(from, to, "none");
- return test_migrate_tls_psk_start_mismatch(from, to);
-}
-
-#ifdef CONFIG_TASN1
-static void *
-test_migrate_multifd_tls_x509_start_default_host(QTestState *from,
- QTestState *to)
-{
- test_migrate_precopy_tcp_multifd_start_common(from, to, "none");
- return test_migrate_tls_x509_start_default_host(from, to);
-}
-
-static void *
-test_migrate_multifd_tls_x509_start_override_host(QTestState *from,
- QTestState *to)
-{
- test_migrate_precopy_tcp_multifd_start_common(from, to, "none");
- return test_migrate_tls_x509_start_override_host(from, to);
-}
-
-static void *
-test_migrate_multifd_tls_x509_start_mismatch_host(QTestState *from,
- QTestState *to)
-{
- test_migrate_precopy_tcp_multifd_start_common(from, to, "none");
- return test_migrate_tls_x509_start_mismatch_host(from, to);
-}
-
-static void *
-test_migrate_multifd_tls_x509_start_allow_anon_client(QTestState *from,
- QTestState *to)
+static void parse_args(int *argc_p, char ***argv_p, bool *full_set)
{
- test_migrate_precopy_tcp_multifd_start_common(from, to, "none");
- return test_migrate_tls_x509_start_allow_anon_client(from, to);
-}
-
-static void *
-test_migrate_multifd_tls_x509_start_reject_anon_client(QTestState *from,
- QTestState *to)
-{
- test_migrate_precopy_tcp_multifd_start_common(from, to, "none");
- return test_migrate_tls_x509_start_reject_anon_client(from, to);
-}
-#endif /* CONFIG_TASN1 */
-
-static void test_multifd_tcp_tls_psk_match(void)
-{
- MigrateCommon args = {
- .listen_uri = "defer",
- .start_hook = test_migrate_multifd_tcp_tls_psk_start_match,
- .finish_hook = test_migrate_tls_psk_finish,
- };
- test_precopy_common(&args);
-}
-
-static void test_multifd_tcp_tls_psk_mismatch(void)
-{
- MigrateCommon args = {
- .start = {
- .hide_stderr = true,
- },
- .listen_uri = "defer",
- .start_hook = test_migrate_multifd_tcp_tls_psk_start_mismatch,
- .finish_hook = test_migrate_tls_psk_finish,
- .result = MIG_TEST_FAIL,
- };
- test_precopy_common(&args);
-}
-
-#ifdef CONFIG_TASN1
-static void test_multifd_tcp_tls_x509_default_host(void)
-{
- MigrateCommon args = {
- .listen_uri = "defer",
- .start_hook = test_migrate_multifd_tls_x509_start_default_host,
- .finish_hook = test_migrate_tls_x509_finish,
- };
- test_precopy_common(&args);
-}
-
-static void test_multifd_tcp_tls_x509_override_host(void)
-{
- MigrateCommon args = {
- .listen_uri = "defer",
- .start_hook = test_migrate_multifd_tls_x509_start_override_host,
- .finish_hook = test_migrate_tls_x509_finish,
- };
- test_precopy_common(&args);
-}
-
-static void test_multifd_tcp_tls_x509_mismatch_host(void)
-{
- /*
- * This has different behaviour to the non-multifd case.
- *
- * In non-multifd case when client aborts due to mismatched
- * cert host, the server has already started trying to load
- * migration state, and so it exits with I/O failure.
- *
- * In multifd case when client aborts due to mismatched
- * cert host, the server is still waiting for the other
- * multifd connections to arrive so hasn't started trying
- * to load migration state, and thus just aborts the migration
- * without exiting.
- */
- MigrateCommon args = {
- .start = {
- .hide_stderr = true,
- },
- .listen_uri = "defer",
- .start_hook = test_migrate_multifd_tls_x509_start_mismatch_host,
- .finish_hook = test_migrate_tls_x509_finish,
- .result = MIG_TEST_FAIL,
- };
- test_precopy_common(&args);
-}
-
-static void test_multifd_tcp_tls_x509_allow_anon_client(void)
-{
- MigrateCommon args = {
- .listen_uri = "defer",
- .start_hook = test_migrate_multifd_tls_x509_start_allow_anon_client,
- .finish_hook = test_migrate_tls_x509_finish,
- };
- test_precopy_common(&args);
-}
-
-static void test_multifd_tcp_tls_x509_reject_anon_client(void)
-{
- MigrateCommon args = {
- .start = {
- .hide_stderr = true,
- },
- .listen_uri = "defer",
- .start_hook = test_migrate_multifd_tls_x509_start_reject_anon_client,
- .finish_hook = test_migrate_tls_x509_finish,
- .result = MIG_TEST_FAIL,
- };
- test_precopy_common(&args);
-}
-#endif /* CONFIG_TASN1 */
-#endif /* CONFIG_GNUTLS */
-
-/*
- * This test does:
- * source target
- * migrate_incoming
- * migrate
- * migrate_cancel
- * launch another target
- * migrate
- *
- * And see that it works
- */
-static void test_multifd_tcp_cancel(void)
-{
- MigrateStart args = {
- .hide_stderr = true,
- };
- QTestState *from, *to, *to2;
-
- if (test_migrate_start(&from, &to, "defer", &args)) {
- return;
- }
-
- migrate_ensure_non_converge(from);
- migrate_prepare_for_dirty_mem(from);
-
- migrate_set_parameter_int(from, "multifd-channels", 16);
- migrate_set_parameter_int(to, "multifd-channels", 16);
-
- migrate_set_capability(from, "multifd", true);
- migrate_set_capability(to, "multifd", true);
-
- /* Start incoming migration from the 1st socket */
- migrate_incoming_qmp(to, "tcp:127.0.0.1:0", "{}");
+ int argc = *argc_p;
+ char **argv = *argv_p;
+ int i, j;
- /* Wait for the first serial output from the source */
- wait_for_serial("src_serial");
-
- migrate_qmp(from, to, NULL, NULL, "{}");
-
- migrate_wait_for_dirty_mem(from, to);
-
- migrate_cancel(from);
-
- /* Make sure QEMU process "to" exited */
- qtest_set_expected_status(to, EXIT_FAILURE);
- qtest_wait_qemu(to);
-
- args = (MigrateStart){
- .only_target = true,
- };
-
- if (test_migrate_start(&from, &to2, "defer", &args)) {
- return;
- }
-
- migrate_set_parameter_int(to2, "multifd-channels", 16);
-
- migrate_set_capability(to2, "multifd", true);
-
- /* Start incoming migration from the 1st socket */
- migrate_incoming_qmp(to2, "tcp:127.0.0.1:0", "{}");
-
- wait_for_migration_status(from, "cancelled", NULL);
-
- migrate_ensure_non_converge(from);
-
- migrate_qmp(from, to2, NULL, NULL, "{}");
-
- migrate_wait_for_dirty_mem(from, to2);
-
- migrate_ensure_converge(from);
-
- wait_for_stop(from, &src_state);
- qtest_qmp_eventwait(to2, "RESUME");
-
- wait_for_serial("dest_serial");
- wait_for_migration_complete(from);
- test_migrate_end(from, to2, true);
-}
-
-static void calc_dirty_rate(QTestState *who, uint64_t calc_time)
-{
- qtest_qmp_assert_success(who,
- "{ 'execute': 'calc-dirty-rate',"
- "'arguments': { "
- "'calc-time': %" PRIu64 ","
- "'mode': 'dirty-ring' }}",
- calc_time);
-}
-
-static QDict *query_dirty_rate(QTestState *who)
-{
- return qtest_qmp_assert_success_ref(who,
- "{ 'execute': 'query-dirty-rate' }");
-}
-
-static void dirtylimit_set_all(QTestState *who, uint64_t dirtyrate)
-{
- qtest_qmp_assert_success(who,
- "{ 'execute': 'set-vcpu-dirty-limit',"
- "'arguments': { "
- "'dirty-rate': %" PRIu64 " } }",
- dirtyrate);
-}
-
-static void cancel_vcpu_dirty_limit(QTestState *who)
-{
- qtest_qmp_assert_success(who,
- "{ 'execute': 'cancel-vcpu-dirty-limit' }");
-}
-
-static QDict *query_vcpu_dirty_limit(QTestState *who)
-{
- QDict *rsp;
-
- rsp = qtest_qmp(who, "{ 'execute': 'query-vcpu-dirty-limit' }");
- g_assert(!qdict_haskey(rsp, "error"));
- g_assert(qdict_haskey(rsp, "return"));
-
- return rsp;
-}
-
-static bool calc_dirtyrate_ready(QTestState *who)
-{
- QDict *rsp_return;
- gchar *status;
-
- rsp_return = query_dirty_rate(who);
- g_assert(rsp_return);
-
- status = g_strdup(qdict_get_str(rsp_return, "status"));
- g_assert(status);
-
- return g_strcmp0(status, "measuring");
-}
-
-static void wait_for_calc_dirtyrate_complete(QTestState *who,
- int64_t time_s)
-{
- int max_try_count = 10000;
- usleep(time_s * 1000000);
-
- while (!calc_dirtyrate_ready(who) && max_try_count--) {
- usleep(1000);
- }
-
- /*
- * Set the timeout with 10 s(max_try_count * 1000us),
- * if dirtyrate measurement not complete, fail test.
- */
- g_assert_cmpint(max_try_count, !=, 0);
-}
-
-static int64_t get_dirty_rate(QTestState *who)
-{
- QDict *rsp_return;
- gchar *status;
- QList *rates;
- const QListEntry *entry;
- QDict *rate;
- int64_t dirtyrate;
-
- rsp_return = query_dirty_rate(who);
- g_assert(rsp_return);
-
- status = g_strdup(qdict_get_str(rsp_return, "status"));
- g_assert(status);
- g_assert_cmpstr(status, ==, "measured");
-
- rates = qdict_get_qlist(rsp_return, "vcpu-dirty-rate");
- g_assert(rates && !qlist_empty(rates));
-
- entry = qlist_first(rates);
- g_assert(entry);
-
- rate = qobject_to(QDict, qlist_entry_obj(entry));
- g_assert(rate);
-
- dirtyrate = qdict_get_try_int(rate, "dirty-rate", -1);
-
- qobject_unref(rsp_return);
- return dirtyrate;
-}
-
-static int64_t get_limit_rate(QTestState *who)
-{
- QDict *rsp_return;
- QList *rates;
- const QListEntry *entry;
- QDict *rate;
- int64_t dirtyrate;
-
- rsp_return = query_vcpu_dirty_limit(who);
- g_assert(rsp_return);
-
- rates = qdict_get_qlist(rsp_return, "return");
- g_assert(rates && !qlist_empty(rates));
-
- entry = qlist_first(rates);
- g_assert(entry);
-
- rate = qobject_to(QDict, qlist_entry_obj(entry));
- g_assert(rate);
-
- dirtyrate = qdict_get_try_int(rate, "limit-rate", -1);
-
- qobject_unref(rsp_return);
- return dirtyrate;
-}
-
-static QTestState *dirtylimit_start_vm(void)
-{
- QTestState *vm = NULL;
- g_autofree gchar *cmd = NULL;
-
- bootfile_create(tmpfs, false);
- cmd = g_strdup_printf("-accel kvm,dirty-ring-size=4096 "
- "-name dirtylimit-test,debug-threads=on "
- "-m 150M -smp 1 "
- "-serial file:%s/vm_serial "
- "-drive file=%s,format=raw ",
- tmpfs, bootpath);
-
- vm = qtest_init(cmd);
- return vm;
-}
-
-static void dirtylimit_stop_vm(QTestState *vm)
-{
- qtest_quit(vm);
- cleanup("vm_serial");
-}
-
-static void test_vcpu_dirty_limit(void)
-{
- QTestState *vm;
- int64_t origin_rate;
- int64_t quota_rate;
- int64_t rate ;
- int max_try_count = 20;
- int hit = 0;
-
- /* Start vm for vcpu dirtylimit test */
- vm = dirtylimit_start_vm();
-
- /* Wait for the first serial output from the vm*/
- wait_for_serial("vm_serial");
-
- /* Do dirtyrate measurement with calc time equals 1s */
- calc_dirty_rate(vm, 1);
-
- /* Sleep calc time and wait for calc dirtyrate complete */
- wait_for_calc_dirtyrate_complete(vm, 1);
-
- /* Query original dirty page rate */
- origin_rate = get_dirty_rate(vm);
-
- /* VM booted from bootsect should dirty memory steadily */
- assert(origin_rate != 0);
-
- /* Setup quota dirty page rate at half of origin */
- quota_rate = origin_rate / 2;
-
- /* Set dirtylimit */
- dirtylimit_set_all(vm, quota_rate);
-
- /*
- * Check if set-vcpu-dirty-limit and query-vcpu-dirty-limit
- * works literally
- */
- g_assert_cmpint(quota_rate, ==, get_limit_rate(vm));
-
- /* Sleep a bit to check if it take effect */
- usleep(2000000);
-
- /*
- * Check if dirtylimit take effect realistically, set the
- * timeout with 20 s(max_try_count * 1s), if dirtylimit
- * doesn't take effect, fail test.
- */
- while (--max_try_count) {
- calc_dirty_rate(vm, 1);
- wait_for_calc_dirtyrate_complete(vm, 1);
- rate = get_dirty_rate(vm);
-
- /*
- * Assume hitting if current rate is less
- * than quota rate (within accepting error)
- */
- if (rate < (quota_rate + DIRTYLIMIT_TOLERANCE_RANGE)) {
- hit = 1;
- break;
+ j = 1;
+ for (i = 1; i < argc; i++) {
+ if (g_str_equal(argv[i], "--full")) {
+ *full_set = true;
+ continue;
}
- }
-
- g_assert_cmpint(hit, ==, 1);
-
- hit = 0;
- max_try_count = 20;
-
- /* Check if dirtylimit cancellation take effect */
- cancel_vcpu_dirty_limit(vm);
- while (--max_try_count) {
- calc_dirty_rate(vm, 1);
- wait_for_calc_dirtyrate_complete(vm, 1);
- rate = get_dirty_rate(vm);
-
- /*
- * Assume dirtylimit be canceled if current rate is
- * greater than quota rate (within accepting error)
- */
- if (rate > (quota_rate + DIRTYLIMIT_TOLERANCE_RANGE)) {
- hit = 1;
- break;
+ argv[j++] = argv[i];
+ if (i >= j) {
+ argv[i] = NULL;
}
}
-
- g_assert_cmpint(hit, ==, 1);
- dirtylimit_stop_vm(vm);
-}
-
-static void migrate_dirty_limit_wait_showup(QTestState *from,
- const int64_t period,
- const int64_t value)
-{
- /* Enable dirty limit capability */
- migrate_set_capability(from, "dirty-limit", true);
-
- /* Set dirty limit parameters */
- migrate_set_parameter_int(from, "x-vcpu-dirty-limit-period", period);
- migrate_set_parameter_int(from, "vcpu-dirty-limit", value);
-
- /* Make sure migrate can't converge */
- migrate_ensure_non_converge(from);
-
- /* To check limit rate after precopy */
- migrate_set_capability(from, "pause-before-switchover", true);
-
- /* Wait for the serial output from the source */
- wait_for_serial("src_serial");
-}
-
-/*
- * This test does:
- * source destination
- * start vm
- * start incoming vm
- * migrate
- * wait dirty limit to begin
- * cancel migrate
- * cancellation check
- * restart incoming vm
- * migrate
- * wait dirty limit to begin
- * wait pre-switchover event
- * convergence condition check
- *
- * And see if dirty limit migration works correctly.
- * This test case involves many passes, so it runs in slow mode only.
- */
-static void test_migrate_dirty_limit(void)
-{
- g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs);
- QTestState *from, *to;
- int64_t remaining;
- uint64_t throttle_us_per_full;
- /*
- * We want the test to be stable and as fast as possible.
- * E.g., with 1Gb/s bandwidth migration may pass without dirty limit,
- * so we need to decrease a bandwidth.
- */
- const int64_t dirtylimit_period = 1000, dirtylimit_value = 50;
- const int64_t max_bandwidth = 400000000; /* ~400Mb/s */
- const int64_t downtime_limit = 250; /* 250ms */
- /*
- * We migrate through unix-socket (> 500Mb/s).
- * Thus, expected migration speed ~= bandwidth limit (< 500Mb/s).
- * So, we can predict expected_threshold
- */
- const int64_t expected_threshold = max_bandwidth * downtime_limit / 1000;
- int max_try_count = 10;
- MigrateCommon args = {
- .start = {
- .hide_stderr = true,
- .use_dirty_ring = true,
- },
- .listen_uri = uri,
- .connect_uri = uri,
- };
-
- /* Start src, dst vm */
- if (test_migrate_start(&from, &to, args.listen_uri, &args.start)) {
- return;
- }
-
- /* Prepare for dirty limit migration and wait src vm show up */
- migrate_dirty_limit_wait_showup(from, dirtylimit_period, dirtylimit_value);
-
- /* Start migrate */
- migrate_qmp(from, to, args.connect_uri, NULL, "{}");
-
- /* Wait for dirty limit throttle begin */
- throttle_us_per_full = 0;
- while (throttle_us_per_full == 0) {
- throttle_us_per_full =
- read_migrate_property_int(from, "dirty-limit-throttle-time-per-round");
- usleep(100);
- g_assert_false(src_state.stop_seen);
- }
-
- /* Now cancel migrate and wait for dirty limit throttle switch off */
- migrate_cancel(from);
- wait_for_migration_status(from, "cancelled", NULL);
-
- /* Check if dirty limit throttle switched off, set timeout 1ms */
- do {
- throttle_us_per_full =
- read_migrate_property_int(from, "dirty-limit-throttle-time-per-round");
- usleep(100);
- g_assert_false(src_state.stop_seen);
- } while (throttle_us_per_full != 0 && --max_try_count);
-
- /* Assert dirty limit is not in service */
- g_assert_cmpint(throttle_us_per_full, ==, 0);
-
- args = (MigrateCommon) {
- .start = {
- .only_target = true,
- .use_dirty_ring = true,
- },
- .listen_uri = uri,
- .connect_uri = uri,
- };
-
- /* Restart dst vm, src vm already show up so we needn't wait anymore */
- if (test_migrate_start(&from, &to, args.listen_uri, &args.start)) {
- return;
- }
-
- /* Start migrate */
- migrate_qmp(from, to, args.connect_uri, NULL, "{}");
-
- /* Wait for dirty limit throttle begin */
- throttle_us_per_full = 0;
- while (throttle_us_per_full == 0) {
- throttle_us_per_full =
- read_migrate_property_int(from, "dirty-limit-throttle-time-per-round");
- usleep(100);
- g_assert_false(src_state.stop_seen);
- }
-
- /*
- * The dirty limit rate should equals the return value of
- * query-vcpu-dirty-limit if dirty limit cap set
- */
- g_assert_cmpint(dirtylimit_value, ==, get_limit_rate(from));
-
- /* Now, we have tested if dirty limit works, let it converge */
- migrate_set_parameter_int(from, "downtime-limit", downtime_limit);
- migrate_set_parameter_int(from, "max-bandwidth", max_bandwidth);
-
- /*
- * Wait for pre-switchover status to check if migration
- * satisfy the convergence condition
- */
- wait_for_migration_status(from, "pre-switchover", NULL);
-
- remaining = read_ram_property_int(from, "remaining");
- g_assert_cmpint(remaining, <,
- (expected_threshold + expected_threshold / 100));
-
- migrate_continue(from, "pre-switchover");
-
- qtest_qmp_eventwait(to, "RESUME");
-
- wait_for_serial("dest_serial");
- wait_for_migration_complete(from);
-
- test_migrate_end(from, to, true);
-}
-
-static bool kvm_dirty_ring_supported(void)
-{
-#if defined(__linux__) && defined(HOST_X86_64)
- int ret, kvm_fd = open("/dev/kvm", O_RDONLY);
-
- if (kvm_fd < 0) {
- return false;
- }
-
- ret = ioctl(kvm_fd, KVM_CHECK_EXTENSION, KVM_CAP_DIRTY_LOG_RING);
- close(kvm_fd);
-
- /* We test with 4096 slots */
- if (ret < 4096) {
- return false;
- }
-
- return true;
-#else
- return false;
-#endif
+ *argc_p = j;
}
int main(int argc, char **argv)
{
- bool has_kvm, has_tcg;
- bool has_uffd, is_x86;
- const char *arch;
- g_autoptr(GError) err = NULL;
- const char *qemu_src = getenv(QEMU_ENV_SRC);
- const char *qemu_dst = getenv(QEMU_ENV_DST);
+ MigrationTestEnv *env;
int ret;
+ bool full_set = false;
- g_test_init(&argc, &argv, NULL);
-
- /*
- * The default QTEST_QEMU_BINARY must always be provided because
- * that is what helpers use to query the accel type and
- * architecture.
- */
- if (qemu_src && qemu_dst) {
- g_test_message("Only one of %s, %s is allowed",
- QEMU_ENV_SRC, QEMU_ENV_DST);
- exit(1);
- }
-
- has_kvm = qtest_has_accel("kvm");
- has_tcg = qtest_has_accel("tcg");
-
- if (!has_tcg && !has_kvm) {
- g_test_skip("No KVM or TCG accelerator available");
- return 0;
- }
-
- has_uffd = ufd_version_check();
- arch = qtest_get_arch();
- is_x86 = !strcmp(arch, "i386") || !strcmp(arch, "x86_64");
-
- tmpfs = g_dir_make_tmp("migration-test-XXXXXX", &err);
- if (!tmpfs) {
- g_test_message("Can't create temporary directory in %s: %s",
- g_get_tmp_dir(), err->message);
- }
- g_assert(tmpfs);
+ /* strip the --full option if it's present */
+ parse_args(&argc, &argv, &full_set);
+ g_test_init(&argc, &argv, NULL);
+ env = migration_get_env();
+ env->full_set = full_set;
module_call_init(MODULE_INIT_QOM);
- migration_test_add("/migration/bad_dest", test_baddest);
-#ifndef _WIN32
- migration_test_add("/migration/analyze-script", test_analyze_script);
- migration_test_add("/migration/vmstate-checker-script",
- test_vmstate_checker_script);
-#endif
-
- /*
- * On s390x with TCG, migration is observed to hang due to the 'pending'
- * state of the flic interrupt controller not being migrated or
- * reconstructed post-migration. Disable it until the problem is resolved.
- */
- if (g_str_equal(arch, "s390x") && !has_kvm) {
- g_test_message("Skipping tests: s390x host with KVM is required");
- goto test_add_done;
- }
-
- if (is_x86) {
- migration_test_add("/migration/precopy/unix/suspend/live",
- test_precopy_unix_suspend_live);
- migration_test_add("/migration/precopy/unix/suspend/notlive",
- test_precopy_unix_suspend_notlive);
- }
-
- if (has_uffd) {
- migration_test_add("/migration/postcopy/plain", test_postcopy);
- migration_test_add("/migration/postcopy/recovery/plain",
- test_postcopy_recovery);
- migration_test_add("/migration/postcopy/preempt/plain",
- test_postcopy_preempt);
- migration_test_add("/migration/postcopy/preempt/recovery/plain",
- test_postcopy_preempt_recovery);
- migration_test_add("/migration/postcopy/recovery/double-failures/handshake",
- test_postcopy_recovery_fail_handshake);
- migration_test_add("/migration/postcopy/recovery/double-failures/reconnect",
- test_postcopy_recovery_fail_reconnect);
- if (is_x86) {
- migration_test_add("/migration/postcopy/suspend",
- test_postcopy_suspend);
- }
- }
-
- migration_test_add("/migration/precopy/unix/plain",
- test_precopy_unix_plain);
- migration_test_add("/migration/precopy/unix/xbzrle",
- test_precopy_unix_xbzrle);
- migration_test_add("/migration/precopy/file",
- test_precopy_file);
- migration_test_add("/migration/precopy/file/offset",
- test_precopy_file_offset);
-#ifndef _WIN32
- migration_test_add("/migration/precopy/file/offset/fdset",
- test_precopy_file_offset_fdset);
-#endif
- migration_test_add("/migration/precopy/file/offset/bad",
- test_precopy_file_offset_bad);
-
- /*
- * Our CI system has problems with shared memory.
- * Don't run this test until we find a workaround.
- */
- if (getenv("QEMU_TEST_FLAKY_TESTS")) {
- migration_test_add("/migration/mode/reboot", test_mode_reboot);
- }
-
- migration_test_add("/migration/precopy/file/mapped-ram",
- test_precopy_file_mapped_ram);
- migration_test_add("/migration/precopy/file/mapped-ram/live",
- test_precopy_file_mapped_ram_live);
-
- migration_test_add("/migration/multifd/file/mapped-ram",
- test_multifd_file_mapped_ram);
- migration_test_add("/migration/multifd/file/mapped-ram/live",
- test_multifd_file_mapped_ram_live);
-
- migration_test_add("/migration/multifd/file/mapped-ram/dio",
- test_multifd_file_mapped_ram_dio);
-
-#ifndef _WIN32
- migration_test_add("/migration/multifd/file/mapped-ram/fdset",
- test_multifd_file_mapped_ram_fdset);
- migration_test_add("/migration/multifd/file/mapped-ram/fdset/dio",
- test_multifd_file_mapped_ram_fdset_dio);
-#endif
-
-#ifdef CONFIG_GNUTLS
- migration_test_add("/migration/precopy/unix/tls/psk",
- test_precopy_unix_tls_psk);
-
- if (has_uffd) {
- /*
- * NOTE: psk test is enough for postcopy, as other types of TLS
- * channels are tested under precopy. Here what we want to test is the
- * general postcopy path that has TLS channel enabled.
- */
- migration_test_add("/migration/postcopy/tls/psk",
- test_postcopy_tls_psk);
- migration_test_add("/migration/postcopy/recovery/tls/psk",
- test_postcopy_recovery_tls_psk);
- migration_test_add("/migration/postcopy/preempt/tls/psk",
- test_postcopy_preempt_tls_psk);
- migration_test_add("/migration/postcopy/preempt/recovery/tls/psk",
- test_postcopy_preempt_all);
- }
-#ifdef CONFIG_TASN1
- migration_test_add("/migration/precopy/unix/tls/x509/default-host",
- test_precopy_unix_tls_x509_default_host);
- migration_test_add("/migration/precopy/unix/tls/x509/override-host",
- test_precopy_unix_tls_x509_override_host);
-#endif /* CONFIG_TASN1 */
-#endif /* CONFIG_GNUTLS */
-
- migration_test_add("/migration/precopy/tcp/plain", test_precopy_tcp_plain);
-
- migration_test_add("/migration/precopy/tcp/plain/switchover-ack",
- test_precopy_tcp_switchover_ack);
-
-#ifdef CONFIG_GNUTLS
- migration_test_add("/migration/precopy/tcp/tls/psk/match",
- test_precopy_tcp_tls_psk_match);
- migration_test_add("/migration/precopy/tcp/tls/psk/mismatch",
- test_precopy_tcp_tls_psk_mismatch);
-#ifdef CONFIG_TASN1
- migration_test_add("/migration/precopy/tcp/tls/x509/default-host",
- test_precopy_tcp_tls_x509_default_host);
- migration_test_add("/migration/precopy/tcp/tls/x509/override-host",
- test_precopy_tcp_tls_x509_override_host);
- migration_test_add("/migration/precopy/tcp/tls/x509/mismatch-host",
- test_precopy_tcp_tls_x509_mismatch_host);
- migration_test_add("/migration/precopy/tcp/tls/x509/friendly-client",
- test_precopy_tcp_tls_x509_friendly_client);
- migration_test_add("/migration/precopy/tcp/tls/x509/hostile-client",
- test_precopy_tcp_tls_x509_hostile_client);
- migration_test_add("/migration/precopy/tcp/tls/x509/allow-anon-client",
- test_precopy_tcp_tls_x509_allow_anon_client);
- migration_test_add("/migration/precopy/tcp/tls/x509/reject-anon-client",
- test_precopy_tcp_tls_x509_reject_anon_client);
-#endif /* CONFIG_TASN1 */
-#endif /* CONFIG_GNUTLS */
-
- /* migration_test_add("/migration/ignore_shared", test_ignore_shared); */
-#ifndef _WIN32
- migration_test_add("/migration/precopy/fd/tcp",
- test_migrate_precopy_fd_socket);
- migration_test_add("/migration/precopy/fd/file",
- test_migrate_precopy_fd_file);
-#endif
- migration_test_add("/migration/validate_uuid", test_validate_uuid);
- migration_test_add("/migration/validate_uuid_error",
- test_validate_uuid_error);
- migration_test_add("/migration/validate_uuid_src_not_set",
- test_validate_uuid_src_not_set);
- migration_test_add("/migration/validate_uuid_dst_not_set",
- test_validate_uuid_dst_not_set);
- migration_test_add("/migration/validate_uri/channels/both_set",
- test_validate_uri_channels_both_set);
- migration_test_add("/migration/validate_uri/channels/none_set",
- test_validate_uri_channels_none_set);
- /*
- * See explanation why this test is slow on function definition
- */
- if (g_test_slow()) {
- migration_test_add("/migration/auto_converge",
- test_migrate_auto_converge);
- if (g_str_equal(arch, "x86_64") &&
- has_kvm && kvm_dirty_ring_supported()) {
- migration_test_add("/migration/dirty_limit",
- test_migrate_dirty_limit);
- }
- }
- migration_test_add("/migration/multifd/tcp/uri/plain/none",
- test_multifd_tcp_uri_none);
- migration_test_add("/migration/multifd/tcp/channels/plain/none",
- test_multifd_tcp_channels_none);
- migration_test_add("/migration/multifd/tcp/plain/zero-page/legacy",
- test_multifd_tcp_zero_page_legacy);
- migration_test_add("/migration/multifd/tcp/plain/zero-page/none",
- test_multifd_tcp_no_zero_page);
- migration_test_add("/migration/multifd/tcp/plain/cancel",
- test_multifd_tcp_cancel);
- migration_test_add("/migration/multifd/tcp/plain/zlib",
- test_multifd_tcp_zlib);
-#ifdef CONFIG_ZSTD
- migration_test_add("/migration/multifd/tcp/plain/zstd",
- test_multifd_tcp_zstd);
-#endif
-#ifdef CONFIG_QPL
- migration_test_add("/migration/multifd/tcp/plain/qpl",
- test_multifd_tcp_qpl);
-#endif
-#ifdef CONFIG_UADK
- migration_test_add("/migration/multifd/tcp/plain/uadk",
- test_multifd_tcp_uadk);
-#endif
-#ifdef CONFIG_GNUTLS
- migration_test_add("/migration/multifd/tcp/tls/psk/match",
- test_multifd_tcp_tls_psk_match);
- migration_test_add("/migration/multifd/tcp/tls/psk/mismatch",
- test_multifd_tcp_tls_psk_mismatch);
-#ifdef CONFIG_TASN1
- migration_test_add("/migration/multifd/tcp/tls/x509/default-host",
- test_multifd_tcp_tls_x509_default_host);
- migration_test_add("/migration/multifd/tcp/tls/x509/override-host",
- test_multifd_tcp_tls_x509_override_host);
- migration_test_add("/migration/multifd/tcp/tls/x509/mismatch-host",
- test_multifd_tcp_tls_x509_mismatch_host);
- migration_test_add("/migration/multifd/tcp/tls/x509/allow-anon-client",
- test_multifd_tcp_tls_x509_allow_anon_client);
- migration_test_add("/migration/multifd/tcp/tls/x509/reject-anon-client",
- test_multifd_tcp_tls_x509_reject_anon_client);
-#endif /* CONFIG_TASN1 */
-#endif /* CONFIG_GNUTLS */
-
- if (g_str_equal(arch, "x86_64") && has_kvm && kvm_dirty_ring_supported()) {
- migration_test_add("/migration/dirty_ring",
- test_precopy_unix_dirty_ring);
- migration_test_add("/migration/vcpu_dirty_limit",
- test_vcpu_dirty_limit);
- }
-
-test_add_done:
+ migration_test_add_tls(env);
+ migration_test_add_compression(env);
+ migration_test_add_postcopy(env);
+ migration_test_add_file(env);
+ migration_test_add_precopy(env);
+ migration_test_add_cpr(env);
+ migration_test_add_misc(env);
ret = g_test_run();
g_assert_cmpint(ret, ==, 0);
- bootfile_delete();
- ret = rmdir(tmpfs);
- if (ret != 0) {
- g_test_message("unable to rmdir: path (%s): %s",
- tmpfs, strerror(errno));
- }
- g_free(tmpfs);
+ ret = migration_env_clean(env);
return ret;
}
diff --git a/tests/qtest/migration/Makefile b/tests/qtest/migration/Makefile
new file mode 100644
index 0000000..2c5ee28
--- /dev/null
+++ b/tests/qtest/migration/Makefile
@@ -0,0 +1,40 @@
+#
+# Copyright (c) 2018 Red Hat, Inc. and/or its affiliates
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or later.
+# See the COPYING file in the top-level directory.
+#
+
+TARGET_LIST = i386 aarch64 s390x ppc64
+
+SRC_PATH = ../..
+
+.PHONY: help $(TARGET_LIST)
+help:
+ @echo "Create migration guest includes. We generate a binary."
+ @echo "And then convert that binary to an include file that can be"
+ @echo "run in a guest."
+ @echo "Possible operations are:"
+ @echo
+ @echo " $(MAKE) clean Remove all intermediate files"
+ @echo " $(MAKE) target Generate for that target"
+ @echo " $(MAKE) CROSS_PREFIX=... target"
+ @echo " Cross-compile than target"
+ @echo " Possible targets are: $(TARGET_LIST)"
+
+override define __note
+/* This file is automatically generated from the assembly file in
+ * tests/migration/$@. Edit that file and then run "make all"
+ * inside tests/migration to update, and then remember to send both
+ * the header and the assembler differences in your patch submission.
+ */
+endef
+export __note
+
+$(TARGET_LIST):
+ $(MAKE) CROSS_PREFIX=$(CROSS_PREFIX) -C $@
+
+clean:
+ for target in $(TARGET_LIST); do \
+ $(MAKE) -C $$target clean; \
+ done
diff --git a/tests/qtest/migration/aarch64/Makefile b/tests/qtest/migration/aarch64/Makefile
new file mode 100644
index 0000000..9c4fa18
--- /dev/null
+++ b/tests/qtest/migration/aarch64/Makefile
@@ -0,0 +1,18 @@
+# To specify cross compiler prefix, use CROSS_PREFIX=
+# $ make CROSS_PREFIX=aarch64-linux-gnu-
+
+.PHONY: all clean
+all: a-b-kernel.h
+
+a-b-kernel.h: aarch64.kernel
+ echo "$$__note" > $@
+ xxd -i $< | sed -e 's/.*int.*//' >> $@
+
+aarch64.kernel: aarch64.elf
+ $(CROSS_PREFIX)objcopy -O binary $< $@
+
+aarch64.elf: a-b-kernel.S
+ $(CROSS_PREFIX)gcc -o $@ -nostdlib -Wl,--build-id=none $<
+
+clean:
+ $(RM) *.kernel *.elf
diff --git a/tests/qtest/migration/aarch64/a-b-kernel.S b/tests/qtest/migration/aarch64/a-b-kernel.S
new file mode 100644
index 0000000..a4103ec
--- /dev/null
+++ b/tests/qtest/migration/aarch64/a-b-kernel.S
@@ -0,0 +1,74 @@
+#
+# Copyright (c) 2018 Red Hat, Inc. and/or its affiliates
+#
+# Author:
+# Wei Huang <wei@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.
+#
+# Note: Please make sure the compiler compiles the assembly code below with
+# pc-relative address. Also the branch instructions should use relative
+# addresses only.
+
+#include "../migration-test.h"
+
+.section .text
+
+ .globl _start
+
+_start:
+ /* disable MMU to use phys mem address */
+ mrs x0, sctlr_el1
+ bic x0, x0, #(1<<0)
+ msr sctlr_el1, x0
+ isb
+
+ /* traverse test memory region */
+ mov x0, #ARM_TEST_MEM_START
+ mov x1, #ARM_TEST_MEM_END
+
+ /* output char 'A' to PL011 */
+ mov w3, 'A'
+ mov x2, #ARM_MACH_VIRT_UART
+ strb w3, [x2]
+
+ /* clean up memory */
+ mov w3, #0
+ mov x4, x0
+clean:
+ strb w3, [x4]
+ add x4, x4, #TEST_MEM_PAGE_SIZE
+ cmp x4, x1
+ ble clean
+
+ /* w5 keeps a counter so we can limit the output speed */
+ mov w5, #0
+
+ /* main body */
+mainloop:
+ mov x4, x0
+
+innerloop:
+ /* increment the first byte of each page by 1 */
+ ldrb w3, [x4]
+ add w3, w3, #1
+ strb w3, [x4]
+
+ /* make sure QEMU user space can see consistent data as MMU is off */
+ dc civac, x4
+
+ add x4, x4, #TEST_MEM_PAGE_SIZE
+ cmp x4, x1
+ blt innerloop
+
+ add w5, w5, #1
+ and w5, w5, #0x1f
+ cmp w5, #0
+ bne mainloop
+
+ /* output char 'B' to PL011 */
+ mov w3, 'B'
+ strb w3, [x2]
+
+ b mainloop
diff --git a/tests/qtest/migration/aarch64/a-b-kernel.h b/tests/qtest/migration/aarch64/a-b-kernel.h
new file mode 100644
index 0000000..34e518d
--- /dev/null
+++ b/tests/qtest/migration/aarch64/a-b-kernel.h
@@ -0,0 +1,18 @@
+/* This file is automatically generated from the assembly file in
+ * tests/migration/aarch64. Edit that file and then run "make all"
+ * inside tests/migration to update, and then remember to send both
+ * the header and the assembler differences in your patch submission.
+ */
+unsigned char aarch64_kernel[] = {
+ 0x00, 0x10, 0x38, 0xd5, 0x00, 0xf8, 0x7f, 0x92, 0x00, 0x10, 0x18, 0xd5,
+ 0xdf, 0x3f, 0x03, 0xd5, 0x00, 0x02, 0xa8, 0xd2, 0x01, 0xc8, 0xa8, 0xd2,
+ 0x23, 0x08, 0x80, 0x52, 0x02, 0x20, 0xa1, 0xd2, 0x43, 0x00, 0x00, 0x39,
+ 0x03, 0x00, 0x80, 0x52, 0xe4, 0x03, 0x00, 0xaa, 0x83, 0x00, 0x00, 0x39,
+ 0x84, 0x04, 0x40, 0x91, 0x9f, 0x00, 0x01, 0xeb, 0xad, 0xff, 0xff, 0x54,
+ 0x05, 0x00, 0x80, 0x52, 0xe4, 0x03, 0x00, 0xaa, 0x83, 0x00, 0x40, 0x39,
+ 0x63, 0x04, 0x00, 0x11, 0x83, 0x00, 0x00, 0x39, 0x24, 0x7e, 0x0b, 0xd5,
+ 0x84, 0x04, 0x40, 0x91, 0x9f, 0x00, 0x01, 0xeb, 0x4b, 0xff, 0xff, 0x54,
+ 0xa5, 0x04, 0x00, 0x11, 0xa5, 0x10, 0x00, 0x12, 0xbf, 0x00, 0x00, 0x71,
+ 0xa1, 0xfe, 0xff, 0x54, 0x43, 0x08, 0x80, 0x52, 0x43, 0x00, 0x00, 0x39,
+ 0xf2, 0xff, 0xff, 0x17
+};
diff --git a/tests/qtest/migration/bootfile.c b/tests/qtest/migration/bootfile.c
new file mode 100644
index 0000000..fac059d
--- /dev/null
+++ b/tests/qtest/migration/bootfile.c
@@ -0,0 +1,70 @@
+/*
+ * Guest code setup for migration tests
+ *
+ * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates
+ * based on the vhost-user-test.c that is:
+ * Copyright (c) 2014 Virtual Open Systems Sarl.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#include "qemu/osdep.h"
+
+/*
+ * The boot file modifies memory area in [start_address, end_address)
+ * repeatedly. It outputs a 'B' at a fixed rate while it's still running.
+ */
+#include "bootfile.h"
+#include "i386/a-b-bootblock.h"
+#include "aarch64/a-b-kernel.h"
+#include "ppc64/a-b-kernel.h"
+#include "s390x/a-b-bios.h"
+
+static char *bootpath;
+
+void bootfile_delete(void)
+{
+ if (!bootpath) {
+ return;
+ }
+ unlink(bootpath);
+ g_free(bootpath);
+ bootpath = NULL;
+}
+
+char *bootfile_create(const char *arch, const char *dir, bool suspend_me)
+{
+ unsigned char *content;
+ size_t len;
+
+ bootfile_delete();
+ bootpath = g_strdup_printf("%s/bootsect", dir);
+ if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) {
+ /* the assembled x86 boot sector should be exactly one sector large */
+ g_assert(sizeof(x86_bootsect) == 512);
+ x86_bootsect[SYM_suspend_me - SYM_start] = suspend_me;
+ content = x86_bootsect;
+ len = sizeof(x86_bootsect);
+ } else if (g_str_equal(arch, "s390x")) {
+ content = s390x_elf;
+ len = sizeof(s390x_elf);
+ } else if (strcmp(arch, "ppc64") == 0) {
+ content = ppc64_kernel;
+ len = sizeof(ppc64_kernel);
+ } else if (strcmp(arch, "aarch64") == 0) {
+ content = aarch64_kernel;
+ len = sizeof(aarch64_kernel);
+ g_assert(sizeof(aarch64_kernel) <= ARM_TEST_MAX_KERNEL_SIZE);
+ } else {
+ g_assert_not_reached();
+ }
+
+ FILE *bootfile = fopen(bootpath, "wb");
+
+ g_assert_cmpint(fwrite(content, len, 1, bootfile), ==, 1);
+ fclose(bootfile);
+
+ return bootpath;
+}
diff --git a/tests/qtest/migration/bootfile.h b/tests/qtest/migration/bootfile.h
new file mode 100644
index 0000000..6d6a673
--- /dev/null
+++ b/tests/qtest/migration/bootfile.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2018 Red Hat, Inc. and/or its affiliates
+ *
+ * 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 BOOTFILE_H
+#define BOOTFILE_H
+
+/* Common */
+#define TEST_MEM_PAGE_SIZE 4096
+
+/* x86 */
+#define X86_TEST_MEM_START (1 * 1024 * 1024)
+#define X86_TEST_MEM_END (100 * 1024 * 1024)
+
+/* S390 */
+#define S390_TEST_MEM_START (1 * 1024 * 1024)
+#define S390_TEST_MEM_END (100 * 1024 * 1024)
+
+/* PPC */
+#define PPC_TEST_MEM_START (1 * 1024 * 1024)
+#define PPC_TEST_MEM_END (100 * 1024 * 1024)
+#define PPC_H_PUT_TERM_CHAR 0x58
+
+/* ARM */
+#define ARM_TEST_MEM_START (0x40000000 + 1 * 1024 * 1024)
+#define ARM_TEST_MEM_END (0x40000000 + 100 * 1024 * 1024)
+#define ARM_MACH_VIRT_UART 0x09000000
+/* AArch64 kernel load address is 0x40080000, and the test memory starts at
+ * 0x40100000. So the maximum allowable kernel size is 512KB.
+ */
+#define ARM_TEST_MAX_KERNEL_SIZE (512 * 1024)
+
+void bootfile_delete(void);
+char *bootfile_create(const char *arch, const char *dir, bool suspend_me);
+
+#endif /* BOOTFILE_H */
diff --git a/tests/qtest/migration/compression-tests.c b/tests/qtest/migration/compression-tests.c
new file mode 100644
index 0000000..b827665
--- /dev/null
+++ b/tests/qtest/migration/compression-tests.c
@@ -0,0 +1,226 @@
+/*
+ * QTest testcases for migration compression
+ *
+ * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates
+ * based on the vhost-user-test.c that is:
+ * Copyright (c) 2014 Virtual Open Systems Sarl.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "libqtest.h"
+#include "migration/framework.h"
+#include "migration/migration-qmp.h"
+#include "migration/migration-util.h"
+#include "qemu/module.h"
+
+
+static char *tmpfs;
+
+#ifdef CONFIG_ZSTD
+static void *
+migrate_hook_start_precopy_tcp_multifd_zstd(QTestState *from,
+ QTestState *to)
+{
+ migrate_set_parameter_int(from, "multifd-zstd-level", 2);
+ migrate_set_parameter_int(to, "multifd-zstd-level", 2);
+
+ return migrate_hook_start_precopy_tcp_multifd_common(from, to, "zstd");
+}
+
+static void test_multifd_tcp_zstd(void)
+{
+ MigrateCommon args = {
+ .listen_uri = "defer",
+ .start = {
+ .caps[MIGRATION_CAPABILITY_MULTIFD] = true,
+ },
+ .start_hook = migrate_hook_start_precopy_tcp_multifd_zstd,
+ };
+ test_precopy_common(&args);
+}
+
+static void test_multifd_postcopy_tcp_zstd(void)
+{
+ MigrateCommon args = {
+ .listen_uri = "defer",
+ .start = {
+ .caps[MIGRATION_CAPABILITY_MULTIFD] = true,
+ .caps[MIGRATION_CAPABILITY_POSTCOPY_RAM] = true,
+ },
+ .start_hook = migrate_hook_start_precopy_tcp_multifd_zstd,
+ };
+
+ test_precopy_common(&args);
+}
+#endif /* CONFIG_ZSTD */
+
+#ifdef CONFIG_QATZIP
+static void *
+migrate_hook_start_precopy_tcp_multifd_qatzip(QTestState *from,
+ QTestState *to)
+{
+ migrate_set_parameter_int(from, "multifd-qatzip-level", 2);
+ migrate_set_parameter_int(to, "multifd-qatzip-level", 2);
+
+ return migrate_hook_start_precopy_tcp_multifd_common(from, to, "qatzip");
+}
+
+static void test_multifd_tcp_qatzip(void)
+{
+ MigrateCommon args = {
+ .listen_uri = "defer",
+ .start = {
+ .caps[MIGRATION_CAPABILITY_MULTIFD] = true,
+ },
+ .start_hook = migrate_hook_start_precopy_tcp_multifd_qatzip,
+ };
+ test_precopy_common(&args);
+}
+#endif
+
+#ifdef CONFIG_QPL
+static void *
+migrate_hook_start_precopy_tcp_multifd_qpl(QTestState *from,
+ QTestState *to)
+{
+ return migrate_hook_start_precopy_tcp_multifd_common(from, to, "qpl");
+}
+
+static void test_multifd_tcp_qpl(void)
+{
+ MigrateCommon args = {
+ .listen_uri = "defer",
+ .start = {
+ .caps[MIGRATION_CAPABILITY_MULTIFD] = true,
+ },
+ .start_hook = migrate_hook_start_precopy_tcp_multifd_qpl,
+ };
+ test_precopy_common(&args);
+}
+#endif /* CONFIG_QPL */
+
+#ifdef CONFIG_UADK
+static void *
+migrate_hook_start_precopy_tcp_multifd_uadk(QTestState *from,
+ QTestState *to)
+{
+ return migrate_hook_start_precopy_tcp_multifd_common(from, to, "uadk");
+}
+
+static void test_multifd_tcp_uadk(void)
+{
+ MigrateCommon args = {
+ .listen_uri = "defer",
+ .start = {
+ .caps[MIGRATION_CAPABILITY_MULTIFD] = true,
+ },
+ .start_hook = migrate_hook_start_precopy_tcp_multifd_uadk,
+ };
+ test_precopy_common(&args);
+}
+#endif /* CONFIG_UADK */
+
+static void *
+migrate_hook_start_xbzrle(QTestState *from,
+ QTestState *to)
+{
+ migrate_set_parameter_int(from, "xbzrle-cache-size", 33554432);
+ return NULL;
+}
+
+static void test_precopy_unix_xbzrle(void)
+{
+ g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs);
+ MigrateCommon args = {
+ .connect_uri = uri,
+ .listen_uri = uri,
+ .start_hook = migrate_hook_start_xbzrle,
+ .iterations = 2,
+ .start = {
+ .caps[MIGRATION_CAPABILITY_XBZRLE] = true,
+ },
+ /*
+ * XBZRLE needs pages to be modified when doing the 2nd+ round
+ * iteration to have real data pushed to the stream.
+ */
+ .live = true,
+ };
+
+ test_precopy_common(&args);
+}
+
+static void *
+migrate_hook_start_precopy_tcp_multifd_zlib(QTestState *from,
+ QTestState *to)
+{
+ /*
+ * Overloading this test to also check that set_parameter does not error.
+ * This is also done in the tests for the other compression methods.
+ */
+ migrate_set_parameter_int(from, "multifd-zlib-level", 2);
+ migrate_set_parameter_int(to, "multifd-zlib-level", 2);
+
+ return migrate_hook_start_precopy_tcp_multifd_common(from, to, "zlib");
+}
+
+static void test_multifd_tcp_zlib(void)
+{
+ MigrateCommon args = {
+ .listen_uri = "defer",
+ .start = {
+ .caps[MIGRATION_CAPABILITY_MULTIFD] = true,
+ },
+ .start_hook = migrate_hook_start_precopy_tcp_multifd_zlib,
+ };
+ test_precopy_common(&args);
+}
+
+static void migration_test_add_compression_smoke(MigrationTestEnv *env)
+{
+ migration_test_add("/migration/multifd/tcp/plain/zlib",
+ test_multifd_tcp_zlib);
+}
+
+void migration_test_add_compression(MigrationTestEnv *env)
+{
+ tmpfs = env->tmpfs;
+
+ migration_test_add_compression_smoke(env);
+
+ if (!env->full_set) {
+ return;
+ }
+
+#ifdef CONFIG_ZSTD
+ migration_test_add("/migration/multifd/tcp/plain/zstd",
+ test_multifd_tcp_zstd);
+ if (env->has_uffd) {
+ migration_test_add("/migration/multifd+postcopy/tcp/plain/zstd",
+ test_multifd_postcopy_tcp_zstd);
+ }
+#endif
+
+#ifdef CONFIG_QATZIP
+ migration_test_add("/migration/multifd/tcp/plain/qatzip",
+ test_multifd_tcp_qatzip);
+#endif
+
+#ifdef CONFIG_QPL
+ migration_test_add("/migration/multifd/tcp/plain/qpl",
+ test_multifd_tcp_qpl);
+#endif
+
+#ifdef CONFIG_UADK
+ migration_test_add("/migration/multifd/tcp/plain/uadk",
+ test_multifd_tcp_uadk);
+#endif
+
+ if (g_test_slow()) {
+ migration_test_add("/migration/precopy/unix/xbzrle",
+ test_precopy_unix_xbzrle);
+ }
+}
diff --git a/tests/qtest/migration/cpr-tests.c b/tests/qtest/migration/cpr-tests.c
new file mode 100644
index 0000000..5e764a6
--- /dev/null
+++ b/tests/qtest/migration/cpr-tests.c
@@ -0,0 +1,136 @@
+/*
+ * QTest testcases for CPR
+ *
+ * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates
+ * based on the vhost-user-test.c that is:
+ * Copyright (c) 2014 Virtual Open Systems Sarl.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "libqtest.h"
+#include "migration/framework.h"
+#include "migration/migration-qmp.h"
+#include "migration/migration-util.h"
+
+
+static char *tmpfs;
+
+static void *migrate_hook_start_mode_reboot(QTestState *from, QTestState *to)
+{
+ migrate_set_parameter_str(from, "mode", "cpr-reboot");
+ migrate_set_parameter_str(to, "mode", "cpr-reboot");
+
+ return NULL;
+}
+
+static void test_mode_reboot(void)
+{
+ g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs,
+ FILE_TEST_FILENAME);
+ MigrateCommon args = {
+ .start.use_shmem = true,
+ .connect_uri = uri,
+ .listen_uri = "defer",
+ .start_hook = migrate_hook_start_mode_reboot,
+ .start = {
+ .caps[MIGRATION_CAPABILITY_X_IGNORE_SHARED] = true,
+ },
+ };
+
+ test_file_common(&args, true);
+}
+
+static void *test_mode_transfer_start(QTestState *from, QTestState *to)
+{
+ migrate_set_parameter_str(from, "mode", "cpr-transfer");
+ return NULL;
+}
+
+/*
+ * cpr-transfer mode cannot use the target monitor prior to starting the
+ * migration, and cannot connect synchronously to the monitor, so defer
+ * the target connection.
+ */
+static void test_mode_transfer_common(bool incoming_defer)
+{
+ g_autofree char *cpr_path = g_strdup_printf("%s/cpr.sock", tmpfs);
+ g_autofree char *mig_path = g_strdup_printf("%s/migsocket", tmpfs);
+ g_autofree char *uri = g_strdup_printf("unix:%s", mig_path);
+ g_autofree char *opts_target = NULL;
+
+ const char *opts = "-machine aux-ram-share=on -nodefaults";
+ g_autofree const char *cpr_channel = g_strdup_printf(
+ "cpr,addr.transport=socket,addr.type=unix,addr.path=%s",
+ cpr_path);
+
+ g_autofree char *connect_channels = g_strdup_printf(
+ "[ { 'channel-type': 'main',"
+ " 'addr': { 'transport': 'socket',"
+ " 'type': 'unix',"
+ " 'path': '%s' } } ]",
+ mig_path);
+
+ /*
+ * Set up a UNIX domain socket for the CPR channel before
+ * launching the destination VM, to avoid timing issues
+ * during connection setup.
+ */
+ int cpr_sockfd = qtest_socket_server(cpr_path);
+ g_assert(cpr_sockfd >= 0);
+
+ opts_target = g_strdup_printf("-incoming cpr,addr.transport=socket,"
+ "addr.type=fd,addr.str=%d %s",
+ cpr_sockfd, opts);
+ MigrateCommon args = {
+ .start.opts_source = opts,
+ .start.opts_target = opts_target,
+ .start.defer_target_connect = true,
+ .start.memory_backend = "-object memory-backend-memfd,id=pc.ram,size=%s"
+ " -machine memory-backend=pc.ram",
+ .listen_uri = incoming_defer ? "defer" : uri,
+ .connect_channels = connect_channels,
+ .cpr_channel = cpr_channel,
+ .start_hook = test_mode_transfer_start,
+ };
+
+ test_precopy_common(&args);
+}
+
+static void test_mode_transfer(void)
+{
+ test_mode_transfer_common(NULL);
+}
+
+static void test_mode_transfer_defer(void)
+{
+ test_mode_transfer_common(true);
+}
+
+void migration_test_add_cpr(MigrationTestEnv *env)
+{
+ tmpfs = env->tmpfs;
+
+ /* no tests in the smoke set for now */
+
+ if (!env->full_set) {
+ return;
+ }
+
+ /*
+ * Our CI system has problems with shared memory.
+ * Don't run this test until we find a workaround.
+ */
+ if (getenv("QEMU_TEST_FLAKY_TESTS")) {
+ migration_test_add("/migration/mode/reboot", test_mode_reboot);
+ }
+
+ if (env->has_kvm) {
+ migration_test_add("/migration/mode/transfer", test_mode_transfer);
+ migration_test_add("/migration/mode/transfer/defer",
+ test_mode_transfer_defer);
+ }
+}
diff --git a/tests/qtest/migration/file-tests.c b/tests/qtest/migration/file-tests.c
new file mode 100644
index 0000000..4d78ce0
--- /dev/null
+++ b/tests/qtest/migration/file-tests.c
@@ -0,0 +1,341 @@
+/*
+ * QTest testcases for migration to file
+ *
+ * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates
+ * based on the vhost-user-test.c that is:
+ * Copyright (c) 2014 Virtual Open Systems Sarl.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "libqtest.h"
+#include "migration/framework.h"
+#include "migration/migration-qmp.h"
+#include "migration/migration-util.h"
+#include "qobject/qlist.h"
+
+
+static char *tmpfs;
+
+static void test_precopy_file(void)
+{
+ g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs,
+ FILE_TEST_FILENAME);
+ MigrateCommon args = {
+ .connect_uri = uri,
+ .listen_uri = "defer",
+ };
+
+ test_file_common(&args, true);
+}
+
+#ifndef _WIN32
+static void fdset_add_fds(QTestState *qts, const char *file, int flags,
+ int num_fds, bool direct_io)
+{
+ for (int i = 0; i < num_fds; i++) {
+ int fd;
+
+#ifdef O_DIRECT
+ /* only secondary channels can use direct-io */
+ if (direct_io && i != 0) {
+ flags |= O_DIRECT;
+ }
+#endif
+
+ fd = open(file, flags, 0660);
+ assert(fd != -1);
+
+ qtest_qmp_fds_assert_success(qts, &fd, 1, "{'execute': 'add-fd', "
+ "'arguments': {'fdset-id': 1}}");
+ close(fd);
+ }
+}
+
+static void *migrate_hook_start_file_offset_fdset(QTestState *from,
+ QTestState *to)
+{
+ g_autofree char *file = g_strdup_printf("%s/%s", tmpfs, FILE_TEST_FILENAME);
+
+ fdset_add_fds(from, file, O_WRONLY, 1, false);
+ fdset_add_fds(to, file, O_RDONLY, 1, false);
+
+ return NULL;
+}
+
+static void test_precopy_file_offset_fdset(void)
+{
+ g_autofree char *uri = g_strdup_printf("file:/dev/fdset/1,offset=%d",
+ FILE_TEST_OFFSET);
+ MigrateCommon args = {
+ .connect_uri = uri,
+ .listen_uri = "defer",
+ .start_hook = migrate_hook_start_file_offset_fdset,
+ };
+
+ test_file_common(&args, false);
+}
+#endif
+
+static void test_precopy_file_offset(void)
+{
+ g_autofree char *uri = g_strdup_printf("file:%s/%s,offset=%d", tmpfs,
+ FILE_TEST_FILENAME,
+ FILE_TEST_OFFSET);
+ MigrateCommon args = {
+ .connect_uri = uri,
+ .listen_uri = "defer",
+ };
+
+ test_file_common(&args, false);
+}
+
+static void test_precopy_file_offset_bad(void)
+{
+ /* using a value not supported by qemu_strtosz() */
+ g_autofree char *uri = g_strdup_printf("file:%s/%s,offset=0x20M",
+ tmpfs, FILE_TEST_FILENAME);
+ MigrateCommon args = {
+ .connect_uri = uri,
+ .listen_uri = "defer",
+ .result = MIG_TEST_QMP_ERROR,
+ };
+
+ test_file_common(&args, false);
+}
+
+static void test_precopy_file_mapped_ram_live(void)
+{
+ g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs,
+ FILE_TEST_FILENAME);
+ MigrateCommon args = {
+ .connect_uri = uri,
+ .listen_uri = "defer",
+ .start = {
+ .caps[MIGRATION_CAPABILITY_MAPPED_RAM] = true,
+ },
+ };
+
+ test_file_common(&args, false);
+}
+
+static void test_precopy_file_mapped_ram(void)
+{
+ g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs,
+ FILE_TEST_FILENAME);
+ MigrateCommon args = {
+ .connect_uri = uri,
+ .listen_uri = "defer",
+ .start = {
+ .caps[MIGRATION_CAPABILITY_MAPPED_RAM] = true,
+ },
+ };
+
+ test_file_common(&args, true);
+}
+
+static void test_multifd_file_mapped_ram_live(void)
+{
+ g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs,
+ FILE_TEST_FILENAME);
+ MigrateCommon args = {
+ .connect_uri = uri,
+ .listen_uri = "defer",
+ .start = {
+ .caps[MIGRATION_CAPABILITY_MULTIFD] = true,
+ .caps[MIGRATION_CAPABILITY_MAPPED_RAM] = true,
+ },
+ };
+
+ test_file_common(&args, false);
+}
+
+static void test_multifd_file_mapped_ram(void)
+{
+ g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs,
+ FILE_TEST_FILENAME);
+ MigrateCommon args = {
+ .connect_uri = uri,
+ .listen_uri = "defer",
+ .start = {
+ .caps[MIGRATION_CAPABILITY_MULTIFD] = true,
+ .caps[MIGRATION_CAPABILITY_MAPPED_RAM] = true,
+ },
+ };
+
+ test_file_common(&args, true);
+}
+
+static void *migrate_hook_start_multifd_mapped_ram_dio(QTestState *from,
+ QTestState *to)
+{
+ migrate_set_parameter_bool(from, "direct-io", true);
+ migrate_set_parameter_bool(to, "direct-io", true);
+
+ return NULL;
+}
+
+static void test_multifd_file_mapped_ram_dio(void)
+{
+ g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs,
+ FILE_TEST_FILENAME);
+ MigrateCommon args = {
+ .connect_uri = uri,
+ .listen_uri = "defer",
+ .start_hook = migrate_hook_start_multifd_mapped_ram_dio,
+ .start = {
+ .caps[MIGRATION_CAPABILITY_MAPPED_RAM] = true,
+ .caps[MIGRATION_CAPABILITY_MULTIFD] = true,
+ },
+ };
+
+ if (!probe_o_direct_support(tmpfs)) {
+ g_test_skip("Filesystem does not support O_DIRECT");
+ return;
+ }
+
+ test_file_common(&args, true);
+}
+
+#ifndef _WIN32
+static void migrate_hook_end_multifd_mapped_ram_fdset(QTestState *from,
+ QTestState *to,
+ void *opaque)
+{
+ QDict *resp;
+ QList *fdsets;
+
+ /*
+ * Remove the fdsets after migration, otherwise a second migration
+ * would fail due fdset reuse.
+ */
+ qtest_qmp_assert_success(from, "{'execute': 'remove-fd', "
+ "'arguments': { 'fdset-id': 1}}");
+
+ /*
+ * Make sure no fdsets are left after migration, otherwise a
+ * second migration would fail due fdset reuse.
+ */
+ resp = qtest_qmp(from, "{'execute': 'query-fdsets', "
+ "'arguments': {}}");
+ g_assert(qdict_haskey(resp, "return"));
+ fdsets = qdict_get_qlist(resp, "return");
+ g_assert(fdsets && qlist_empty(fdsets));
+ qobject_unref(resp);
+}
+
+static void *migrate_hook_start_multifd_mapped_ram_fdset_dio(QTestState *from,
+ QTestState *to)
+{
+ g_autofree char *file = g_strdup_printf("%s/%s", tmpfs, FILE_TEST_FILENAME);
+
+ fdset_add_fds(from, file, O_WRONLY, 2, true);
+ fdset_add_fds(to, file, O_RDONLY, 2, true);
+
+ migrate_set_parameter_bool(from, "direct-io", true);
+ migrate_set_parameter_bool(to, "direct-io", true);
+
+ return NULL;
+}
+
+static void *migrate_hook_start_multifd_mapped_ram_fdset(QTestState *from,
+ QTestState *to)
+{
+ g_autofree char *file = g_strdup_printf("%s/%s", tmpfs, FILE_TEST_FILENAME);
+
+ fdset_add_fds(from, file, O_WRONLY, 2, false);
+ fdset_add_fds(to, file, O_RDONLY, 2, false);
+
+ return NULL;
+}
+
+static void test_multifd_file_mapped_ram_fdset(void)
+{
+ g_autofree char *uri = g_strdup_printf("file:/dev/fdset/1,offset=%d",
+ FILE_TEST_OFFSET);
+ MigrateCommon args = {
+ .connect_uri = uri,
+ .listen_uri = "defer",
+ .start_hook = migrate_hook_start_multifd_mapped_ram_fdset,
+ .end_hook = migrate_hook_end_multifd_mapped_ram_fdset,
+ .start = {
+ .caps[MIGRATION_CAPABILITY_MAPPED_RAM] = true,
+ .caps[MIGRATION_CAPABILITY_MULTIFD] = true,
+ },
+ };
+
+ test_file_common(&args, true);
+}
+
+static void test_multifd_file_mapped_ram_fdset_dio(void)
+{
+ g_autofree char *uri = g_strdup_printf("file:/dev/fdset/1,offset=%d",
+ FILE_TEST_OFFSET);
+ MigrateCommon args = {
+ .connect_uri = uri,
+ .listen_uri = "defer",
+ .start_hook = migrate_hook_start_multifd_mapped_ram_fdset_dio,
+ .end_hook = migrate_hook_end_multifd_mapped_ram_fdset,
+ .start = {
+ .caps[MIGRATION_CAPABILITY_MAPPED_RAM] = true,
+ .caps[MIGRATION_CAPABILITY_MULTIFD] = true,
+ },
+ };
+
+ if (!probe_o_direct_support(tmpfs)) {
+ g_test_skip("Filesystem does not support O_DIRECT");
+ return;
+ }
+
+ test_file_common(&args, true);
+}
+#endif /* !_WIN32 */
+
+static void migration_test_add_file_smoke(MigrationTestEnv *env)
+{
+ migration_test_add("/migration/precopy/file",
+ test_precopy_file);
+
+ migration_test_add("/migration/multifd/file/mapped-ram/dio",
+ test_multifd_file_mapped_ram_dio);
+}
+
+void migration_test_add_file(MigrationTestEnv *env)
+{
+ tmpfs = env->tmpfs;
+
+ migration_test_add_file_smoke(env);
+
+ if (!env->full_set) {
+ return;
+ }
+
+ migration_test_add("/migration/precopy/file/offset",
+ test_precopy_file_offset);
+#ifndef _WIN32
+ migration_test_add("/migration/precopy/file/offset/fdset",
+ test_precopy_file_offset_fdset);
+#endif
+ migration_test_add("/migration/precopy/file/offset/bad",
+ test_precopy_file_offset_bad);
+
+ migration_test_add("/migration/precopy/file/mapped-ram",
+ test_precopy_file_mapped_ram);
+ migration_test_add("/migration/precopy/file/mapped-ram/live",
+ test_precopy_file_mapped_ram_live);
+
+ migration_test_add("/migration/multifd/file/mapped-ram",
+ test_multifd_file_mapped_ram);
+ migration_test_add("/migration/multifd/file/mapped-ram/live",
+ test_multifd_file_mapped_ram_live);
+
+#ifndef _WIN32
+ migration_test_add("/migration/multifd/file/mapped-ram/fdset",
+ test_multifd_file_mapped_ram_fdset);
+ migration_test_add("/migration/multifd/file/mapped-ram/fdset/dio",
+ test_multifd_file_mapped_ram_fdset_dio);
+#endif
+}
diff --git a/tests/qtest/migration/framework.c b/tests/qtest/migration/framework.c
new file mode 100644
index 0000000..407c902
--- /dev/null
+++ b/tests/qtest/migration/framework.c
@@ -0,0 +1,1066 @@
+/*
+ * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates
+ * based on the vhost-user-test.c that is:
+ * Copyright (c) 2014 Virtual Open Systems Sarl.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#include "qemu/osdep.h"
+
+#include "chardev/char.h"
+#include "crypto/tlscredspsk.h"
+#include "libqtest.h"
+#include "migration/bootfile.h"
+#include "migration/framework.h"
+#include "migration/migration-qmp.h"
+#include "migration/migration-util.h"
+#include "ppc-util.h"
+#include "qapi/error.h"
+#include "qobject/qjson.h"
+#include "qobject/qlist.h"
+#include "qemu/module.h"
+#include "qemu/option.h"
+#include "qemu/range.h"
+#include "qemu/sockets.h"
+
+
+#define QEMU_VM_FILE_MAGIC 0x5145564d
+#define QEMU_ENV_SRC "QTEST_QEMU_BINARY_SRC"
+#define QEMU_ENV_DST "QTEST_QEMU_BINARY_DST"
+#define MULTIFD_TEST_CHANNELS 4
+
+unsigned start_address;
+unsigned end_address;
+static QTestMigrationState src_state;
+static QTestMigrationState dst_state;
+static char *tmpfs;
+
+/*
+ * An initial 3 MB offset is used as that corresponds
+ * to ~1 sec of data transfer with our bandwidth setting.
+ */
+#define MAGIC_OFFSET_BASE (3 * 1024 * 1024)
+/*
+ * A further 1k is added to ensure we're not a multiple
+ * of TEST_MEM_PAGE_SIZE, thus avoid clash with writes
+ * from the migration guest workload.
+ */
+#define MAGIC_OFFSET_SHUFFLE 1024
+#define MAGIC_OFFSET (MAGIC_OFFSET_BASE + MAGIC_OFFSET_SHUFFLE)
+#define MAGIC_MARKER 0xFEED12345678CAFEULL
+
+
+/*
+ * Wait for some output in the serial output file,
+ * we get an 'A' followed by an endless string of 'B's
+ * but on the destination we won't have the A (unless we enabled suspend/resume)
+ */
+void wait_for_serial(const char *side)
+{
+ g_autofree char *serialpath = g_strdup_printf("%s/%s", tmpfs, side);
+ FILE *serialfile = fopen(serialpath, "r");
+
+ do {
+ int readvalue = fgetc(serialfile);
+
+ switch (readvalue) {
+ case 'A':
+ /* Fine */
+ break;
+
+ case 'B':
+ /* It's alive! */
+ fclose(serialfile);
+ return;
+
+ case EOF:
+ fseek(serialfile, 0, SEEK_SET);
+ usleep(1000);
+ break;
+
+ default:
+ fprintf(stderr, "Unexpected %d on %s serial\n", readvalue, side);
+ g_assert_not_reached();
+ }
+ } while (true);
+}
+
+void migrate_prepare_for_dirty_mem(QTestState *from)
+{
+ /*
+ * The guest workflow iterates from start_address to
+ * end_address, writing 1 byte every TEST_MEM_PAGE_SIZE
+ * bytes.
+ *
+ * IOW, if we write to mem at a point which is NOT
+ * a multiple of TEST_MEM_PAGE_SIZE, our write won't
+ * conflict with the migration workflow.
+ *
+ * We put in a marker here, that we'll use to determine
+ * when the data has been transferred to the dst.
+ */
+ qtest_writeq(from, start_address + MAGIC_OFFSET, MAGIC_MARKER);
+}
+
+void migrate_wait_for_dirty_mem(QTestState *from, QTestState *to)
+{
+ uint64_t watch_address = start_address + MAGIC_OFFSET_BASE;
+ uint64_t marker_address = start_address + MAGIC_OFFSET;
+ uint8_t watch_byte;
+
+ /*
+ * Wait for the MAGIC_MARKER to get transferred, as an
+ * indicator that a migration pass has made some known
+ * amount of progress.
+ */
+ do {
+ usleep(1000 * 10);
+ } while (qtest_readq(to, marker_address) != MAGIC_MARKER);
+
+
+ /* If suspended, src only iterates once, and watch_byte may never change */
+ if (src_state.suspend_me) {
+ return;
+ }
+
+ /*
+ * Now ensure that already transferred bytes are
+ * dirty again from the guest workload. Note the
+ * guest byte value will wrap around and by chance
+ * match the original watch_byte. This is harmless
+ * as we'll eventually see a different value if we
+ * keep watching
+ */
+ watch_byte = qtest_readb(from, watch_address);
+ do {
+ usleep(1000 * 10);
+ } while (qtest_readb(from, watch_address) == watch_byte);
+}
+
+static void check_guests_ram(QTestState *who)
+{
+ /*
+ * Our ASM test will have been incrementing one byte from each page from
+ * start_address to < end_address in order. This gives us a constraint
+ * that any page's byte should be equal or less than the previous pages
+ * byte (mod 256); and they should all be equal except for one transition
+ * at the point where we meet the incrementer. (We're running this with
+ * the guest stopped).
+ */
+ unsigned address;
+ uint8_t first_byte;
+ uint8_t last_byte;
+ bool hit_edge = false;
+ int bad = 0;
+
+ qtest_memread(who, start_address, &first_byte, 1);
+ last_byte = first_byte;
+
+ for (address = start_address + TEST_MEM_PAGE_SIZE; address < end_address;
+ address += TEST_MEM_PAGE_SIZE)
+ {
+ uint8_t b;
+ qtest_memread(who, address, &b, 1);
+ if (b != last_byte) {
+ if (((b + 1) % 256) == last_byte && !hit_edge) {
+ /*
+ * This is OK, the guest stopped at the point of
+ * incrementing the previous page but didn't get
+ * to us yet.
+ */
+ hit_edge = true;
+ last_byte = b;
+ } else {
+ bad++;
+ if (bad <= 10) {
+ fprintf(stderr, "Memory content inconsistency at %x"
+ " first_byte = %x last_byte = %x current = %x"
+ " hit_edge = %x\n",
+ address, first_byte, last_byte, b, hit_edge);
+ }
+ }
+ }
+ }
+ if (bad >= 10) {
+ fprintf(stderr, "and in another %d pages", bad - 10);
+ }
+ g_assert(bad == 0);
+}
+
+static void cleanup(const char *filename)
+{
+ g_autofree char *path = g_strdup_printf("%s/%s", tmpfs, filename);
+
+ unlink(path);
+}
+
+static QList *migrate_start_get_qmp_capabilities(const MigrateStart *args)
+{
+ QList *capabilities = NULL;
+
+ if (args->oob) {
+ capabilities = qlist_new();
+ qlist_append_str(capabilities, "oob");
+ }
+ return capabilities;
+}
+
+static void migrate_start_set_capabilities(QTestState *from, QTestState *to,
+ MigrateStart *args)
+{
+ /*
+ * MigrationCapability_lookup and MIGRATION_CAPABILITY_ constants
+ * are from qapi-types-migration.h.
+ */
+ for (uint8_t i = 0; i < MIGRATION_CAPABILITY__MAX; i++) {
+ if (!args->caps[i]) {
+ continue;
+ }
+ if (from) {
+ migrate_set_capability(from,
+ MigrationCapability_lookup.array[i], true);
+ }
+ if (to) {
+ migrate_set_capability(to,
+ MigrationCapability_lookup.array[i], true);
+ }
+ }
+
+ /*
+ * Always enable migration events. Libvirt always uses it, let's try
+ * to mimic as closer as that.
+ */
+ migrate_set_capability(from, "events", true);
+ if (!args->defer_target_connect) {
+ migrate_set_capability(to, "events", true);
+ }
+
+ /*
+ * Default number of channels should be fine for most
+ * tests. Individual tests can override by calling
+ * migrate_set_parameter() directly.
+ */
+ if (args->caps[MIGRATION_CAPABILITY_MULTIFD]) {
+ migrate_set_parameter_int(from, "multifd-channels",
+ MULTIFD_TEST_CHANNELS);
+ migrate_set_parameter_int(to, "multifd-channels",
+ MULTIFD_TEST_CHANNELS);
+ }
+
+ return;
+}
+
+int migrate_start(QTestState **from, QTestState **to, const char *uri,
+ MigrateStart *args)
+{
+ /* options for source and target */
+ g_autofree gchar *arch_opts = NULL;
+ g_autofree gchar *cmd_source = NULL;
+ g_autofree gchar *cmd_target = NULL;
+ const gchar *ignore_stderr;
+ g_autofree char *shmem_opts = NULL;
+ g_autofree char *shmem_path = NULL;
+ const char *kvm_opts = NULL;
+ const char *arch = qtest_get_arch();
+ const char *memory_size;
+ const char *machine_alias, *machine_opts = "";
+ g_autofree char *machine = NULL;
+ const char *bootpath;
+ g_autoptr(QList) capabilities = migrate_start_get_qmp_capabilities(args);
+ g_autofree char *memory_backend = NULL;
+ const char *events;
+
+ if (args->use_shmem) {
+ if (!g_file_test("/dev/shm", G_FILE_TEST_IS_DIR)) {
+ g_test_skip("/dev/shm is not supported");
+ return -1;
+ }
+ }
+
+ dst_state = (QTestMigrationState) { };
+ src_state = (QTestMigrationState) { };
+ bootpath = bootfile_create(arch, tmpfs, args->suspend_me);
+ src_state.suspend_me = args->suspend_me;
+
+ if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) {
+ memory_size = "150M";
+
+ if (g_str_equal(arch, "i386")) {
+ machine_alias = "pc";
+ } else {
+ machine_alias = "q35";
+ }
+ arch_opts = g_strdup_printf(
+ "-drive if=none,id=d0,file=%s,format=raw "
+ "-device ide-hd,drive=d0,secs=1,cyls=1,heads=1", bootpath);
+ start_address = X86_TEST_MEM_START;
+ end_address = X86_TEST_MEM_END;
+ } else if (g_str_equal(arch, "s390x")) {
+ memory_size = "128M";
+ machine_alias = "s390-ccw-virtio";
+ arch_opts = g_strdup_printf("-bios %s", bootpath);
+ start_address = S390_TEST_MEM_START;
+ end_address = S390_TEST_MEM_END;
+ } else if (strcmp(arch, "ppc64") == 0) {
+ memory_size = "256M";
+ start_address = PPC_TEST_MEM_START;
+ end_address = PPC_TEST_MEM_END;
+ machine_alias = "pseries";
+ machine_opts = "vsmt=8";
+ arch_opts = g_strdup_printf(
+ "-nodefaults -machine " PSERIES_DEFAULT_CAPABILITIES " "
+ "-bios %s", bootpath);
+ } else if (strcmp(arch, "aarch64") == 0) {
+ memory_size = "150M";
+ machine_alias = "virt";
+ machine_opts = "gic-version=3";
+ arch_opts = g_strdup_printf("-cpu max -kernel %s", bootpath);
+ start_address = ARM_TEST_MEM_START;
+ end_address = ARM_TEST_MEM_END;
+ } else {
+ g_assert_not_reached();
+ }
+
+ if (!getenv("QTEST_LOG") && args->hide_stderr) {
+#ifndef _WIN32
+ ignore_stderr = "2>/dev/null";
+#else
+ /*
+ * On Windows the QEMU executable is created via CreateProcess() and
+ * IO redirection does not work, so don't bother adding IO redirection
+ * to the command line.
+ */
+ ignore_stderr = "";
+#endif
+ } else {
+ ignore_stderr = "";
+ }
+
+ if (args->use_shmem) {
+ shmem_path = g_strdup_printf("/dev/shm/qemu-%d", getpid());
+ shmem_opts = g_strdup_printf(
+ "-object memory-backend-file,id=mem0,size=%s"
+ ",mem-path=%s,share=on -numa node,memdev=mem0",
+ memory_size, shmem_path);
+ }
+
+ if (args->memory_backend) {
+ memory_backend = g_strdup_printf(args->memory_backend, memory_size);
+ } else {
+ memory_backend = g_strdup_printf("-m %s ", memory_size);
+ }
+
+ if (args->use_dirty_ring) {
+ kvm_opts = ",dirty-ring-size=4096";
+ }
+
+ if (!qtest_has_machine(machine_alias)) {
+ g_autofree char *msg = g_strdup_printf("machine %s not supported", machine_alias);
+ g_test_skip(msg);
+ return -1;
+ }
+
+ machine = resolve_machine_version(machine_alias, QEMU_ENV_SRC,
+ QEMU_ENV_DST);
+
+ g_test_message("Using machine type: %s", machine);
+
+ cmd_source = g_strdup_printf("-accel kvm%s -accel tcg "
+ "-machine %s,%s "
+ "-name source,debug-threads=on "
+ "%s "
+ "-serial file:%s/src_serial "
+ "%s %s %s %s",
+ kvm_opts ? kvm_opts : "",
+ machine, machine_opts,
+ memory_backend, tmpfs,
+ arch_opts ? arch_opts : "",
+ shmem_opts ? shmem_opts : "",
+ args->opts_source ? args->opts_source : "",
+ ignore_stderr);
+ if (!args->only_target) {
+ *from = qtest_init_ext(QEMU_ENV_SRC, cmd_source, capabilities, true);
+ qtest_qmp_set_event_callback(*from,
+ migrate_watch_for_events,
+ &src_state);
+ }
+
+ /*
+ * If the monitor connection is deferred, enable events on the command line
+ * so none are missed. This is for testing only, do not set migration
+ * options like this in general.
+ */
+ events = args->defer_target_connect ? "-global migration.x-events=on" : "";
+
+ cmd_target = g_strdup_printf("-accel kvm%s -accel tcg "
+ "-machine %s,%s "
+ "-name target,debug-threads=on "
+ "%s "
+ "-serial file:%s/dest_serial "
+ "-incoming %s "
+ "%s %s %s %s %s",
+ kvm_opts ? kvm_opts : "",
+ machine, machine_opts,
+ memory_backend, tmpfs, uri,
+ events,
+ arch_opts ? arch_opts : "",
+ shmem_opts ? shmem_opts : "",
+ args->opts_target ? args->opts_target : "",
+ ignore_stderr);
+ *to = qtest_init_ext(QEMU_ENV_DST, cmd_target, capabilities,
+ !args->defer_target_connect);
+ qtest_qmp_set_event_callback(*to,
+ migrate_watch_for_events,
+ &dst_state);
+
+ /*
+ * Remove shmem file immediately to avoid memory leak in test failed case.
+ * It's valid because QEMU has already opened this file
+ */
+ if (args->use_shmem) {
+ unlink(shmem_path);
+ }
+
+ migrate_start_set_capabilities(*from, *to, args);
+
+ return 0;
+}
+
+void migrate_end(QTestState *from, QTestState *to, bool test_dest)
+{
+ unsigned char dest_byte_a, dest_byte_b, dest_byte_c, dest_byte_d;
+
+ qtest_quit(from);
+
+ if (test_dest) {
+ qtest_memread(to, start_address, &dest_byte_a, 1);
+
+ /* Destination still running, wait for a byte to change */
+ do {
+ qtest_memread(to, start_address, &dest_byte_b, 1);
+ usleep(1000 * 10);
+ } while (dest_byte_a == dest_byte_b);
+
+ qtest_qmp_assert_success(to, "{ 'execute' : 'stop'}");
+
+ /* With it stopped, check nothing changes */
+ qtest_memread(to, start_address, &dest_byte_c, 1);
+ usleep(1000 * 200);
+ qtest_memread(to, start_address, &dest_byte_d, 1);
+ g_assert_cmpint(dest_byte_c, ==, dest_byte_d);
+
+ check_guests_ram(to);
+ }
+
+ qtest_quit(to);
+
+ cleanup("migsocket");
+ cleanup("cpr.sock");
+ cleanup("src_serial");
+ cleanup("dest_serial");
+ cleanup(FILE_TEST_FILENAME);
+}
+
+static int migrate_postcopy_prepare(QTestState **from_ptr,
+ QTestState **to_ptr,
+ MigrateCommon *args)
+{
+ QTestState *from, *to;
+
+ /* set postcopy capabilities */
+ args->start.caps[MIGRATION_CAPABILITY_POSTCOPY_BLOCKTIME] = true;
+ args->start.caps[MIGRATION_CAPABILITY_POSTCOPY_RAM] = true;
+
+ if (migrate_start(&from, &to, "defer", &args->start)) {
+ return -1;
+ }
+
+ if (args->start_hook) {
+ args->postcopy_data = args->start_hook(from, to);
+ }
+
+ migrate_ensure_non_converge(from);
+ migrate_prepare_for_dirty_mem(from);
+ qtest_qmp_assert_success(to, "{ 'execute': 'migrate-incoming',"
+ " 'arguments': { "
+ " 'channels': [ { 'channel-type': 'main',"
+ " 'addr': { 'transport': 'socket',"
+ " 'type': 'inet',"
+ " 'host': '127.0.0.1',"
+ " 'port': '0' } } ] } }");
+
+ /* Wait for the first serial output from the source */
+ wait_for_serial("src_serial");
+ wait_for_suspend(from, &src_state);
+
+ migrate_qmp(from, to, NULL, NULL, "{}");
+
+ migrate_wait_for_dirty_mem(from, to);
+
+ *from_ptr = from;
+ *to_ptr = to;
+
+ return 0;
+}
+
+static void migrate_postcopy_complete(QTestState *from, QTestState *to,
+ MigrateCommon *args)
+{
+ MigrationTestEnv *env = migration_get_env();
+
+ wait_for_migration_complete(from);
+
+ if (args->start.suspend_me) {
+ /* wakeup succeeds only if guest is suspended */
+ qtest_qmp_assert_success(to, "{'execute': 'system_wakeup'}");
+ }
+
+ /* Make sure we get at least one "B" on destination */
+ wait_for_serial("dest_serial");
+
+ if (env->uffd_feature_thread_id) {
+ read_blocktime(to);
+ }
+
+ if (args->end_hook) {
+ args->end_hook(from, to, args->postcopy_data);
+ args->postcopy_data = NULL;
+ }
+
+ migrate_end(from, to, true);
+}
+
+void test_postcopy_common(MigrateCommon *args)
+{
+ QTestState *from, *to;
+
+ if (migrate_postcopy_prepare(&from, &to, args)) {
+ return;
+ }
+ migrate_postcopy_start(from, to, &src_state);
+ migrate_postcopy_complete(from, to, args);
+}
+
+static void wait_for_postcopy_status(QTestState *one, const char *status)
+{
+ wait_for_migration_status(one, status,
+ (const char * []) {
+ "failed", "active",
+ "completed", NULL
+ });
+}
+
+static void postcopy_recover_fail(QTestState *from, QTestState *to,
+ PostcopyRecoveryFailStage stage)
+{
+#ifndef _WIN32
+ bool fail_early = (stage == POSTCOPY_FAIL_CHANNEL_ESTABLISH);
+ int ret, pair1[2], pair2[2];
+ char c;
+
+ g_assert(stage > POSTCOPY_FAIL_NONE && stage < POSTCOPY_FAIL_MAX);
+
+ /* Create two unrelated socketpairs */
+ ret = qemu_socketpair(PF_LOCAL, SOCK_STREAM, 0, pair1);
+ g_assert_cmpint(ret, ==, 0);
+
+ ret = qemu_socketpair(PF_LOCAL, SOCK_STREAM, 0, pair2);
+ g_assert_cmpint(ret, ==, 0);
+
+ /*
+ * Give the guests unpaired ends of the sockets, so they'll all blocked
+ * at reading. This mimics a wrong channel established.
+ */
+ qtest_qmp_fds_assert_success(from, &pair1[0], 1,
+ "{ 'execute': 'getfd',"
+ " 'arguments': { 'fdname': 'fd-mig' }}");
+ qtest_qmp_fds_assert_success(to, &pair2[0], 1,
+ "{ 'execute': 'getfd',"
+ " 'arguments': { 'fdname': 'fd-mig' }}");
+
+ /*
+ * Write the 1st byte as QEMU_VM_COMMAND (0x8) for the dest socket, to
+ * emulate the 1st byte of a real recovery, but stops from there to
+ * keep dest QEMU in RECOVER. This is needed so that we can kick off
+ * the recover process on dest QEMU (by triggering the G_IO_IN event).
+ *
+ * NOTE: this trick is not needed on src QEMUs, because src doesn't
+ * rely on an pre-existing G_IO_IN event, so it will always trigger the
+ * upcoming recovery anyway even if it can read nothing.
+ */
+#define QEMU_VM_COMMAND 0x08
+ c = QEMU_VM_COMMAND;
+ ret = send(pair2[1], &c, 1, 0);
+ g_assert_cmpint(ret, ==, 1);
+
+ if (stage == POSTCOPY_FAIL_CHANNEL_ESTABLISH) {
+ /*
+ * This will make src QEMU to fail at an early stage when trying to
+ * resume later, where it shouldn't reach RECOVER stage at all.
+ */
+ close(pair1[1]);
+ }
+
+ migrate_recover(to, "fd:fd-mig");
+ migrate_qmp(from, to, "fd:fd-mig", NULL, "{'resume': true}");
+
+ /*
+ * Source QEMU has an extra RECOVER_SETUP phase, dest doesn't have it.
+ * Make sure it appears along the way.
+ */
+ migration_event_wait(from, "postcopy-recover-setup");
+
+ if (fail_early) {
+ /*
+ * When fails at reconnection, src QEMU will automatically goes
+ * back to PAUSED state. Making sure there is an event in this
+ * case: Libvirt relies on this to detect early reconnection
+ * errors.
+ */
+ migration_event_wait(from, "postcopy-paused");
+ } else {
+ /*
+ * We want to test "fail later" at RECOVER stage here. Make sure
+ * both QEMU instances will go into RECOVER stage first, then test
+ * kicking them out using migrate-pause.
+ *
+ * Explicitly check the RECOVER event on src, that's what Libvirt
+ * relies on, rather than polling.
+ */
+ migration_event_wait(from, "postcopy-recover");
+ wait_for_postcopy_status(from, "postcopy-recover");
+
+ /* Need an explicit kick on src QEMU in this case */
+ migrate_pause(from);
+ }
+
+ /*
+ * For all failure cases, we'll reach such states on both sides now.
+ * Check them.
+ */
+ wait_for_postcopy_status(from, "postcopy-paused");
+ wait_for_postcopy_status(to, "postcopy-recover");
+
+ /*
+ * Kick dest QEMU out too. This is normally not needed in reality
+ * because when the channel is shutdown it should also happen on src.
+ * However here we used separate socket pairs so we need to do that
+ * explicitly.
+ */
+ migrate_pause(to);
+ wait_for_postcopy_status(to, "postcopy-paused");
+
+ close(pair1[0]);
+ close(pair2[0]);
+ close(pair2[1]);
+
+ if (stage != POSTCOPY_FAIL_CHANNEL_ESTABLISH) {
+ close(pair1[1]);
+ }
+#endif
+}
+
+void test_postcopy_recovery_common(MigrateCommon *args)
+{
+ QTestState *from, *to;
+ g_autofree char *uri = NULL;
+
+ /*
+ * Always enable OOB QMP capability for recovery tests, migrate-recover is
+ * executed out-of-band
+ */
+ args->start.oob = true;
+
+ /* Always hide errors for postcopy recover tests since they're expected */
+ args->start.hide_stderr = true;
+
+ if (migrate_postcopy_prepare(&from, &to, args)) {
+ return;
+ }
+
+ /* Turn postcopy speed down, 4K/s is slow enough on any machines */
+ migrate_set_parameter_int(from, "max-postcopy-bandwidth", 4096);
+
+ /* Now we start the postcopy */
+ migrate_postcopy_start(from, to, &src_state);
+
+ /*
+ * Wait until postcopy is really started; we can only run the
+ * migrate-pause command during a postcopy
+ */
+ wait_for_migration_status(from, "postcopy-active", NULL);
+
+ /*
+ * Manually stop the postcopy migration. This emulates a network
+ * failure with the migration socket
+ */
+ migrate_pause(from);
+
+ /*
+ * Wait for destination side to reach postcopy-paused state. The
+ * migrate-recover command can only succeed if destination machine
+ * is in the paused state
+ */
+ wait_for_postcopy_status(to, "postcopy-paused");
+ wait_for_postcopy_status(from, "postcopy-paused");
+
+ if (args->postcopy_recovery_fail_stage) {
+ /*
+ * Test when a wrong socket specified for recover, and then the
+ * ability to kick it out, and continue with a correct socket.
+ */
+ postcopy_recover_fail(from, to, args->postcopy_recovery_fail_stage);
+ /* continue with a good recovery */
+ }
+
+ /*
+ * Create a new socket to emulate a new channel that is different
+ * from the broken migration channel; tell the destination to
+ * listen to the new port
+ */
+ uri = g_strdup_printf("unix:%s/migsocket-recover", tmpfs);
+ migrate_recover(to, uri);
+
+ /*
+ * Try to rebuild the migration channel using the resume flag and
+ * the newly created channel
+ */
+ migrate_qmp(from, to, uri, NULL, "{'resume': true}");
+
+ /* Restore the postcopy bandwidth to unlimited */
+ migrate_set_parameter_int(from, "max-postcopy-bandwidth", 0);
+
+ migrate_postcopy_complete(from, to, args);
+}
+
+void test_precopy_common(MigrateCommon *args)
+{
+ QTestState *from, *to;
+ void *data_hook = NULL;
+ QObject *in_channels = NULL;
+ QObject *out_channels = NULL;
+
+ g_assert(!args->cpr_channel || args->connect_channels);
+
+ if (migrate_start(&from, &to, args->listen_uri, &args->start)) {
+ return;
+ }
+
+ if (args->start_hook) {
+ data_hook = args->start_hook(from, to);
+ }
+
+ /* Wait for the first serial output from the source */
+ if (args->result == MIG_TEST_SUCCEED) {
+ wait_for_serial("src_serial");
+ wait_for_suspend(from, &src_state);
+ }
+
+ if (args->live) {
+ migrate_ensure_non_converge(from);
+ migrate_prepare_for_dirty_mem(from);
+ } else {
+ /*
+ * Testing non-live migration, we allow it to run at
+ * full speed to ensure short test case duration.
+ * For tests expected to fail, we don't need to
+ * change anything.
+ */
+ if (args->result == MIG_TEST_SUCCEED) {
+ qtest_qmp_assert_success(from, "{ 'execute' : 'stop'}");
+ wait_for_stop(from, &src_state);
+ migrate_ensure_converge(from);
+ }
+ }
+
+ /*
+ * The cpr channel must be included in outgoing channels, but not in
+ * migrate-incoming channels.
+ */
+ if (args->connect_channels) {
+ if (args->start.defer_target_connect &&
+ !strcmp(args->listen_uri, "defer")) {
+ in_channels = qobject_from_json(args->connect_channels,
+ &error_abort);
+ }
+ out_channels = qobject_from_json(args->connect_channels, &error_abort);
+
+ if (args->cpr_channel) {
+ QList *channels_list = qobject_to(QList, out_channels);
+ QObject *obj = migrate_str_to_channel(args->cpr_channel);
+
+ qlist_append(channels_list, obj);
+ }
+ }
+
+ if (args->result == MIG_TEST_QMP_ERROR) {
+ migrate_qmp_fail(from, args->connect_uri, out_channels, "{}");
+ goto finish;
+ }
+
+ migrate_qmp(from, to, args->connect_uri, out_channels, "{}");
+
+ if (args->start.defer_target_connect) {
+ qtest_connect(to);
+ qtest_qmp_handshake(to, NULL);
+ if (!strcmp(args->listen_uri, "defer")) {
+ migrate_incoming_qmp(to, args->connect_uri, in_channels, "{}");
+ }
+ }
+
+ if (args->result != MIG_TEST_SUCCEED) {
+ bool allow_active = args->result == MIG_TEST_FAIL;
+ wait_for_migration_fail(from, allow_active);
+
+ if (args->result == MIG_TEST_FAIL_DEST_QUIT_ERR) {
+ qtest_set_expected_status(to, EXIT_FAILURE);
+ }
+ } else {
+ if (args->live) {
+ /*
+ * For initial iteration(s) we must do a full pass,
+ * but for the final iteration, we need only wait
+ * for some dirty mem before switching to converge
+ */
+ while (args->iterations > 1) {
+ wait_for_migration_pass(from, &src_state);
+ args->iterations--;
+ }
+ migrate_wait_for_dirty_mem(from, to);
+
+ migrate_ensure_converge(from);
+
+ /*
+ * We do this first, as it has a timeout to stop us
+ * hanging forever if migration didn't converge
+ */
+ wait_for_migration_complete(from);
+
+ wait_for_stop(from, &src_state);
+
+ } else {
+ wait_for_migration_complete(from);
+ /*
+ * Must wait for dst to finish reading all incoming
+ * data on the socket before issuing 'cont' otherwise
+ * it'll be ignored
+ */
+ wait_for_migration_complete(to);
+
+ qtest_qmp_assert_success(to, "{ 'execute' : 'cont'}");
+ }
+
+ wait_for_resume(to, &dst_state);
+
+ if (args->start.suspend_me) {
+ /* wakeup succeeds only if guest is suspended */
+ qtest_qmp_assert_success(to, "{'execute': 'system_wakeup'}");
+ }
+
+ wait_for_serial("dest_serial");
+ }
+
+finish:
+ if (args->end_hook) {
+ args->end_hook(from, to, data_hook);
+ }
+
+ migrate_end(from, to, args->result == MIG_TEST_SUCCEED);
+}
+
+static void file_dirty_offset_region(void)
+{
+ g_autofree char *path = g_strdup_printf("%s/%s", tmpfs, FILE_TEST_FILENAME);
+ size_t size = FILE_TEST_OFFSET;
+ g_autofree char *data = g_new0(char, size);
+
+ memset(data, FILE_TEST_MARKER, size);
+ g_assert(g_file_set_contents(path, data, size, NULL));
+}
+
+static void file_check_offset_region(void)
+{
+ g_autofree char *path = g_strdup_printf("%s/%s", tmpfs, FILE_TEST_FILENAME);
+ size_t size = FILE_TEST_OFFSET;
+ g_autofree char *expected = g_new0(char, size);
+ g_autofree char *actual = NULL;
+ uint64_t *stream_start;
+
+ /*
+ * Ensure the skipped offset region's data has not been touched
+ * and the migration stream starts at the right place.
+ */
+
+ memset(expected, FILE_TEST_MARKER, size);
+
+ g_assert(g_file_get_contents(path, &actual, NULL, NULL));
+ g_assert(!memcmp(actual, expected, size));
+
+ stream_start = (uint64_t *)(actual + size);
+ g_assert_cmpint(cpu_to_be64(*stream_start) >> 32, ==, QEMU_VM_FILE_MAGIC);
+}
+
+void test_file_common(MigrateCommon *args, bool stop_src)
+{
+ QTestState *from, *to;
+ void *data_hook = NULL;
+ bool check_offset = false;
+
+ if (migrate_start(&from, &to, args->listen_uri, &args->start)) {
+ return;
+ }
+
+ /*
+ * File migration is never live. We can keep the source VM running
+ * during migration, but the destination will not be running
+ * concurrently.
+ */
+ g_assert_false(args->live);
+
+ if (g_strrstr(args->connect_uri, "offset=")) {
+ check_offset = true;
+ /*
+ * This comes before the start_hook because it's equivalent to
+ * a management application creating the file and writing to
+ * it so hooks should expect the file to be already present.
+ */
+ file_dirty_offset_region();
+ }
+
+ if (args->start_hook) {
+ data_hook = args->start_hook(from, to);
+ }
+
+ migrate_ensure_converge(from);
+ wait_for_serial("src_serial");
+
+ if (stop_src) {
+ qtest_qmp_assert_success(from, "{ 'execute' : 'stop'}");
+ wait_for_stop(from, &src_state);
+ }
+
+ if (args->result == MIG_TEST_QMP_ERROR) {
+ migrate_qmp_fail(from, args->connect_uri, NULL, "{}");
+ goto finish;
+ }
+
+ migrate_qmp(from, to, args->connect_uri, NULL, "{}");
+ wait_for_migration_complete(from);
+
+ /*
+ * We need to wait for the source to finish before starting the
+ * destination.
+ */
+ migrate_incoming_qmp(to, args->connect_uri, NULL, "{}");
+ wait_for_migration_complete(to);
+
+ if (stop_src) {
+ qtest_qmp_assert_success(to, "{ 'execute' : 'cont'}");
+ }
+ wait_for_resume(to, &dst_state);
+
+ wait_for_serial("dest_serial");
+
+ if (check_offset) {
+ file_check_offset_region();
+ }
+
+finish:
+ if (args->end_hook) {
+ args->end_hook(from, to, data_hook);
+ }
+
+ migrate_end(from, to, args->result == MIG_TEST_SUCCEED);
+}
+
+void *migrate_hook_start_precopy_tcp_multifd_common(QTestState *from,
+ QTestState *to,
+ const char *method)
+{
+ migrate_set_parameter_str(from, "multifd-compression", method);
+ migrate_set_parameter_str(to, "multifd-compression", method);
+
+ /* Start incoming migration from the 1st socket */
+ migrate_incoming_qmp(to, "tcp:127.0.0.1:0", NULL, "{}");
+
+ return NULL;
+}
+
+QTestMigrationState *get_src(void)
+{
+ return &src_state;
+}
+
+MigrationTestEnv *migration_get_env(void)
+{
+ static MigrationTestEnv *env;
+ g_autoptr(GError) err = NULL;
+
+ if (env) {
+ return env;
+ }
+
+ env = g_new0(MigrationTestEnv, 1);
+ env->qemu_src = getenv(QEMU_ENV_SRC);
+ env->qemu_dst = getenv(QEMU_ENV_DST);
+
+ /*
+ * The default QTEST_QEMU_BINARY must always be provided because
+ * that is what helpers use to query the accel type and
+ * architecture.
+ */
+ if (env->qemu_src && env->qemu_dst) {
+ g_test_message("Only one of %s, %s is allowed",
+ QEMU_ENV_SRC, QEMU_ENV_DST);
+ exit(1);
+ }
+
+ env->has_kvm = qtest_has_accel("kvm");
+ env->has_tcg = qtest_has_accel("tcg");
+
+ if (!env->has_tcg && !env->has_kvm) {
+ g_test_skip("No KVM or TCG accelerator available");
+ return env;
+ }
+
+ env->has_dirty_ring = kvm_dirty_ring_supported();
+ env->has_uffd = ufd_version_check(&env->uffd_feature_thread_id);
+ env->arch = qtest_get_arch();
+ env->is_x86 = !strcmp(env->arch, "i386") || !strcmp(env->arch, "x86_64");
+
+ env->tmpfs = g_dir_make_tmp("migration-test-XXXXXX", &err);
+ if (!env->tmpfs) {
+ g_test_message("Can't create temporary directory in %s: %s",
+ g_get_tmp_dir(), err->message);
+ }
+ g_assert(env->tmpfs);
+
+ tmpfs = env->tmpfs;
+
+ return env;
+}
+
+int migration_env_clean(MigrationTestEnv *env)
+{
+ char *tmpfs;
+ int ret = 0;
+
+ if (!env) {
+ return ret;
+ }
+
+ bootfile_delete();
+
+ tmpfs = env->tmpfs;
+ ret = rmdir(tmpfs);
+ if (ret != 0) {
+ g_test_message("unable to rmdir: path (%s): %s",
+ tmpfs, strerror(errno));
+ }
+ g_free(tmpfs);
+
+ return ret;
+}
diff --git a/tests/qtest/migration/framework.h b/tests/qtest/migration/framework.h
new file mode 100644
index 0000000..01e425e
--- /dev/null
+++ b/tests/qtest/migration/framework.h
@@ -0,0 +1,251 @@
+/*
+ * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates
+ * based on the vhost-user-test.c that is:
+ * Copyright (c) 2014 Virtual Open Systems Sarl.
+ *
+ * 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 TEST_FRAMEWORK_H
+#define TEST_FRAMEWORK_H
+
+#include "libqtest.h"
+#include <qapi/qapi-types-migration.h>
+
+#define FILE_TEST_FILENAME "migfile"
+#define FILE_TEST_OFFSET 0x1000
+#define FILE_TEST_MARKER 'X'
+
+typedef struct MigrationTestEnv {
+ bool has_kvm;
+ bool has_tcg;
+ bool has_uffd;
+ bool uffd_feature_thread_id;
+ bool has_dirty_ring;
+ bool is_x86;
+ bool full_set;
+ const char *arch;
+ const char *qemu_src;
+ const char *qemu_dst;
+ char *tmpfs;
+} MigrationTestEnv;
+
+MigrationTestEnv *migration_get_env(void);
+int migration_env_clean(MigrationTestEnv *env);
+
+/*
+ * A hook that runs after the src and dst QEMUs have been
+ * created, but before the migration is started. This can
+ * be used to set migration parameters and capabilities.
+ *
+ * Returns: NULL, or a pointer to opaque state to be
+ * later passed to the TestMigrateEndHook
+ */
+typedef void * (*TestMigrateStartHook)(QTestState *from,
+ QTestState *to);
+
+/*
+ * A hook that runs after the migration has finished,
+ * regardless of whether it succeeded or failed, but
+ * before QEMU has terminated (unless it self-terminated
+ * due to migration error)
+ *
+ * @opaque is a pointer to state previously returned
+ * by the TestMigrateStartHook if any, or NULL.
+ */
+typedef void (*TestMigrateEndHook)(QTestState *from,
+ QTestState *to,
+ void *opaque);
+
+/*
+ * Our goal is to ensure that we run a single full migration
+ * iteration, and also dirty memory, ensuring that at least
+ * one further iteration is required.
+ *
+ * We can't directly synchronize with the start of a migration
+ * so we have to apply some tricks monitoring memory that is
+ * transferred.
+ *
+ * Initially we set the migration bandwidth to an insanely
+ * low value, with tiny max downtime too. This basically
+ * guarantees migration will never complete.
+ *
+ * This will result in a test that is unacceptably slow though,
+ * so we can't let the entire migration pass run at this speed.
+ * Our intent is to let it run just long enough that we can
+ * prove data prior to the marker has been transferred *AND*
+ * also prove this transferred data is dirty again.
+ *
+ * Before migration starts, we write a 64-bit magic marker
+ * into a fixed location in the src VM RAM.
+ *
+ * Then watch dst memory until the marker appears. This is
+ * proof that start_address -> MAGIC_OFFSET_BASE has been
+ * transferred.
+ *
+ * Finally we go back to the source and read a byte just
+ * before the marker until we see it flip in value. This
+ * is proof that start_address -> MAGIC_OFFSET_BASE
+ * is now dirty again.
+ *
+ * IOW, we're guaranteed at least a 2nd migration pass
+ * at this point.
+ *
+ * We can now let migration run at full speed to finish
+ * the test
+ */
+typedef struct {
+ /*
+ * QTEST_LOG=1 may override this. When QTEST_LOG=1, we always dump errors
+ * unconditionally, because it means the user would like to be verbose.
+ */
+ bool hide_stderr;
+ bool use_shmem;
+ /* only launch the target process */
+ bool only_target;
+ /* Use dirty ring if true; dirty logging otherwise */
+ bool use_dirty_ring;
+ const char *opts_source;
+ const char *opts_target;
+ /* suspend the src before migrating to dest. */
+ bool suspend_me;
+ /* enable OOB QMP capability */
+ bool oob;
+ /*
+ * Format string for the main memory backend, containing one %s where the
+ * size is plugged in. If omitted, "-m %s" is used.
+ */
+ const char *memory_backend;
+
+ /* Do not connect to target monitor and qtest sockets in qtest_init */
+ bool defer_target_connect;
+
+ /*
+ * Migration capabilities to be set in both source and
+ * destination. For unilateral capabilities, use
+ * migration_set_capabilities().
+ */
+ bool caps[MIGRATION_CAPABILITY__MAX];
+} MigrateStart;
+
+typedef enum PostcopyRecoveryFailStage {
+ /*
+ * "no failure" must be 0 as it's the default. OTOH, real failure
+ * cases must be >0 to make sure they trigger by a "if" test.
+ */
+ POSTCOPY_FAIL_NONE = 0,
+ POSTCOPY_FAIL_CHANNEL_ESTABLISH,
+ POSTCOPY_FAIL_RECOVERY,
+ POSTCOPY_FAIL_MAX
+} PostcopyRecoveryFailStage;
+
+typedef struct {
+ /* Optional: fine tune start parameters */
+ MigrateStart start;
+
+ /* Required: the URI for the dst QEMU to listen on */
+ const char *listen_uri;
+
+ /*
+ * Optional: the URI for the src QEMU to connect to
+ * If NULL, then it will query the dst QEMU for its actual
+ * listening address and use that as the connect address.
+ * This allows for dynamically picking a free TCP port.
+ */
+ const char *connect_uri;
+
+ /*
+ * Optional: JSON-formatted list of src QEMU URIs. If a port is
+ * defined as '0' in any QDict key a value of '0' will be
+ * automatically converted to the correct destination port.
+ */
+ const char *connect_channels;
+
+ /* Optional: the cpr migration channel, in JSON or dotted keys format */
+ const char *cpr_channel;
+
+ /* Optional: callback to run at start to set migration parameters */
+ TestMigrateStartHook start_hook;
+ /* Optional: callback to run at finish to cleanup */
+ TestMigrateEndHook end_hook;
+
+ /*
+ * Optional: normally we expect the migration process to complete.
+ *
+ * There can be a variety of reasons and stages in which failure
+ * can happen during tests.
+ *
+ * If a failure is expected to happen at time of establishing
+ * the connection, then MIG_TEST_FAIL will indicate that the dst
+ * QEMU is expected to stay running and accept future migration
+ * connections.
+ *
+ * If a failure is expected to happen while processing the
+ * migration stream, then MIG_TEST_FAIL_DEST_QUIT_ERR will indicate
+ * that the dst QEMU is expected to quit with non-zero exit status
+ */
+ enum {
+ /* This test should succeed, the default */
+ MIG_TEST_SUCCEED = 0,
+ /* This test should fail, dest qemu should keep alive */
+ MIG_TEST_FAIL,
+ /* This test should fail, dest qemu should fail with abnormal status */
+ MIG_TEST_FAIL_DEST_QUIT_ERR,
+ /* The QMP command for this migration should fail with an error */
+ MIG_TEST_QMP_ERROR,
+ } result;
+
+ /*
+ * Optional: set number of migration passes to wait for, if live==true.
+ * If zero, then merely wait for a few MB of dirty data
+ */
+ unsigned int iterations;
+
+ /*
+ * Optional: whether the guest CPUs should be running during a precopy
+ * migration test. We used to always run with live but it took much
+ * longer so we reduced live tests to only the ones that have solid
+ * reason to be tested live-only. For each of the new test cases for
+ * precopy please provide justifications to use live explicitly (please
+ * refer to existing ones with live=true), or use live=off by default.
+ */
+ bool live;
+
+ /* Postcopy specific fields */
+ void *postcopy_data;
+ PostcopyRecoveryFailStage postcopy_recovery_fail_stage;
+} MigrateCommon;
+
+void wait_for_serial(const char *side);
+void migrate_prepare_for_dirty_mem(QTestState *from);
+void migrate_wait_for_dirty_mem(QTestState *from, QTestState *to);
+int migrate_start(QTestState **from, QTestState **to, const char *uri,
+ MigrateStart *args);
+void migrate_end(QTestState *from, QTestState *to, bool test_dest);
+
+void test_postcopy_common(MigrateCommon *args);
+void test_postcopy_recovery_common(MigrateCommon *args);
+void test_precopy_common(MigrateCommon *args);
+void test_file_common(MigrateCommon *args, bool stop_src);
+void *migrate_hook_start_precopy_tcp_multifd_common(QTestState *from,
+ QTestState *to,
+ const char *method);
+
+typedef struct QTestMigrationState QTestMigrationState;
+QTestMigrationState *get_src(void);
+
+#ifdef CONFIG_GNUTLS
+void migration_test_add_tls(MigrationTestEnv *env);
+#else
+static inline void migration_test_add_tls(MigrationTestEnv *env) {};
+#endif
+void migration_test_add_compression(MigrationTestEnv *env);
+void migration_test_add_postcopy(MigrationTestEnv *env);
+void migration_test_add_file(MigrationTestEnv *env);
+void migration_test_add_precopy(MigrationTestEnv *env);
+void migration_test_add_cpr(MigrationTestEnv *env);
+void migration_test_add_misc(MigrationTestEnv *env);
+
+#endif /* TEST_FRAMEWORK_H */
diff --git a/tests/qtest/migration/i386/Makefile b/tests/qtest/migration/i386/Makefile
new file mode 100644
index 0000000..37a72ae
--- /dev/null
+++ b/tests/qtest/migration/i386/Makefile
@@ -0,0 +1,23 @@
+# To specify cross compiler prefix, use CROSS_PREFIX=
+# $ make CROSS_PREFIX=x86_64-linux-gnu-
+
+.PHONY: all clean
+all: a-b-bootblock.h
+
+a-b-bootblock.h: x86.bootsect x86.o
+ echo "$$__note" > header.tmp
+ xxd -i $< | sed -e 's/.*int.*//' >> header.tmp
+ nm x86.o | awk '{print "#define SYM_"$$3" 0x"$$1}' >> header.tmp
+ mv header.tmp $@
+
+x86.bootsect: x86.boot
+ dd if=$< of=$@ bs=256 count=2 skip=124
+
+x86.boot: x86.o
+ $(CROSS_PREFIX)objcopy -O binary $< $@
+
+x86.o: a-b-bootblock.S
+ $(CROSS_PREFIX)gcc -I.. -m32 -march=i486 -c $< -o $@
+
+clean:
+ @rm -rf *.boot *.o *.bootsect
diff --git a/tests/qtest/migration/i386/a-b-bootblock.S b/tests/qtest/migration/i386/a-b-bootblock.S
new file mode 100644
index 0000000..6f39eb6
--- /dev/null
+++ b/tests/qtest/migration/i386/a-b-bootblock.S
@@ -0,0 +1,145 @@
+# x86 bootblock used in migration test
+# repeatedly increments the first byte of each page in a 100MB
+# range.
+# Outputs an initial 'A' on serial followed by repeated 'B's
+#
+# Copyright (c) 2016 Red Hat, Inc. and/or its affiliates
+# This work is licensed under the terms of the GNU GPL, version 2 or later.
+# See the COPYING file in the top-level directory.
+#
+# Author: dgilbert@redhat.com
+
+#include "migration-test.h"
+
+#define ACPI_ENABLE 0xf1
+#define ACPI_PORT_SMI_CMD 0xb2
+#define ACPI_PM_BASE 0x600
+#define PM1A_CNT_OFFSET 4
+
+#define ACPI_SCI_ENABLE 0x0001
+#define ACPI_SLEEP_TYPE 0x0400
+#define ACPI_SLEEP_ENABLE 0x2000
+#define SLEEP (ACPI_SCI_ENABLE + ACPI_SLEEP_TYPE + ACPI_SLEEP_ENABLE)
+
+#define LOW_ADDR X86_TEST_MEM_START
+#define HIGH_ADDR X86_TEST_MEM_END
+
+/* Save the suspended status at an address that is not written in the loop. */
+#define suspended (X86_TEST_MEM_START + 4)
+
+.code16
+.org 0x7c00
+ .file "fill.s"
+ .text
+ .globl start
+ .type start, @function
+start: # at 0x7c00 ?
+ cli
+ lgdt gdtdesc
+ mov $1,%eax
+ mov %eax,%cr0 # Protected mode enable
+ data32 ljmp $8,$0x7c20
+
+.org 0x7c20
+.code32
+ # A20 enable - not sure I actually need this
+ inb $0x92,%al
+ or $2,%al
+ outb %al, $0x92
+
+ # set up DS for the whole of RAM (needed on KVM)
+ mov $16,%eax
+ mov %eax,%ds
+
+# Start from 1MB
+.set TEST_MEM_START, X86_TEST_MEM_START
+.set TEST_MEM_END, X86_TEST_MEM_END
+
+ mov $65,%ax
+ mov $0x3f8,%dx
+ outb %al,%dx
+
+ # bl keeps a counter so we limit the output speed
+ mov $0, %bl
+
+pre_zero:
+ mov $TEST_MEM_START,%eax
+do_zero:
+ movb $0, (%eax)
+ add $4096,%eax
+ cmp $TEST_MEM_END,%eax
+ jl do_zero
+
+mainloop:
+ mov $TEST_MEM_START,%eax
+innerloop:
+ incb (%eax)
+ add $4096,%eax
+ cmp $TEST_MEM_END,%eax
+ jl innerloop
+
+ inc %bl
+ andb $0x3f,%bl
+ jnz mainloop
+
+ mov $66,%ax
+ mov $0x3f8,%dx
+ outb %al,%dx
+
+ # should this test suspend?
+ mov (suspend_me),%eax
+ cmp $0,%eax
+ je mainloop
+
+ # are we waking after suspend? do not suspend again.
+ mov $suspended,%eax
+ mov (%eax),%eax
+ cmp $1,%eax
+ je mainloop
+
+ # enable acpi
+ mov $ACPI_ENABLE,%al
+ outb %al,$ACPI_PORT_SMI_CMD
+
+ # suspend to ram
+ mov $suspended,%eax
+ movl $1,(%eax)
+ mov $SLEEP,%ax
+ mov $(ACPI_PM_BASE + PM1A_CNT_OFFSET),%dx
+ outw %ax,%dx
+ # not reached. The wakeup causes reset and restart at 0x7c00, and we
+ # do not save and restore registers as a real kernel would do.
+
+
+ # GDT magic from old (GPLv2) Grub startup.S
+ .p2align 2 /* force 4-byte alignment */
+gdt:
+ .word 0, 0
+ .byte 0, 0, 0, 0
+
+ /* -- code segment --
+ * base = 0x00000000, limit = 0xFFFFF (4 KiB Granularity), present
+ * type = 32bit code execute/read, DPL = 0
+ */
+ .word 0xFFFF, 0
+ .byte 0, 0x9A, 0xCF, 0
+
+ /* -- data segment --
+ * base = 0x00000000, limit 0xFFFFF (4 KiB Granularity), present
+ * type = 32 bit data read/write, DPL = 0
+ */
+ .word 0xFFFF, 0
+ .byte 0, 0x92, 0xCF, 0
+
+gdtdesc:
+ .word 0x27 /* limit */
+ .long gdt /* addr */
+
+ /* test launcher can poke a 1 here to exercise suspend */
+suspend_me:
+ .int 0
+
+/* I'm a bootable disk */
+.org 0x7dfe
+ .byte 0x55
+ .byte 0xAA
diff --git a/tests/qtest/migration/i386/a-b-bootblock.h b/tests/qtest/migration/i386/a-b-bootblock.h
new file mode 100644
index 0000000..c83f871
--- /dev/null
+++ b/tests/qtest/migration/i386/a-b-bootblock.h
@@ -0,0 +1,61 @@
+/* This file is automatically generated from the assembly file in
+ * tests/migration/i386. Edit that file and then run "make all"
+ * inside tests/migration to update, and then remember to send both
+ * the header and the assembler differences in your patch submission.
+ */
+unsigned char x86_bootsect[] = {
+ 0xfa, 0x0f, 0x01, 0x16, 0xb8, 0x7c, 0x66, 0xb8, 0x01, 0x00, 0x00, 0x00,
+ 0x0f, 0x22, 0xc0, 0x66, 0xea, 0x20, 0x7c, 0x00, 0x00, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe4, 0x92, 0x0c, 0x02,
+ 0xe6, 0x92, 0xb8, 0x10, 0x00, 0x00, 0x00, 0x8e, 0xd8, 0x66, 0xb8, 0x41,
+ 0x00, 0x66, 0xba, 0xf8, 0x03, 0xee, 0xb3, 0x00, 0xb8, 0x00, 0x00, 0x10,
+ 0x00, 0xc6, 0x00, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x3d, 0x00, 0x00,
+ 0x40, 0x06, 0x7c, 0xf1, 0xb8, 0x00, 0x00, 0x10, 0x00, 0xfe, 0x00, 0x05,
+ 0x00, 0x10, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x40, 0x06, 0x7c, 0xf2, 0xfe,
+ 0xc3, 0x80, 0xe3, 0x3f, 0x75, 0xe6, 0x66, 0xb8, 0x42, 0x00, 0x66, 0xba,
+ 0xf8, 0x03, 0xee, 0xa1, 0xbe, 0x7c, 0x00, 0x00, 0x83, 0xf8, 0x00, 0x74,
+ 0xd3, 0xb8, 0x04, 0x00, 0x10, 0x00, 0x8b, 0x00, 0x83, 0xf8, 0x01, 0x74,
+ 0xc7, 0xb0, 0xf1, 0xe6, 0xb2, 0xb8, 0x04, 0x00, 0x10, 0x00, 0xc7, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x66, 0xb8, 0x01, 0x24, 0x66, 0xba, 0x04, 0x06,
+ 0x66, 0xef, 0x66, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0x00, 0x00, 0x00, 0x9a, 0xcf, 0x00, 0xff, 0xff, 0x00, 0x00,
+ 0x00, 0x92, 0xcf, 0x00, 0x27, 0x00, 0xa0, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0xaa
+};
+
+#define SYM_do_zero 0x00007c3d
+#define SYM_gdt 0x00007ca0
+#define SYM_gdtdesc 0x00007cb8
+#define SYM_innerloop 0x00007c51
+#define SYM_mainloop 0x00007c4c
+#define SYM_pre_zero 0x00007c38
+#define SYM_start 0x00007c00
+#define SYM_suspend_me 0x00007cbe
+#define SYM_TEST_MEM_END 0x06400000
+#define SYM_TEST_MEM_START 0x00100000
diff --git a/tests/qtest/migration/migration-qmp.c b/tests/qtest/migration/migration-qmp.c
new file mode 100644
index 0000000..fb59741
--- /dev/null
+++ b/tests/qtest/migration/migration-qmp.c
@@ -0,0 +1,520 @@
+/*
+ * QTest QMP helpers for migration
+ *
+ * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates
+ * based on the vhost-user-test.c that is:
+ * Copyright (c) 2014 Virtual Open Systems Sarl.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "libqtest.h"
+#include "migration-qmp.h"
+#include "migration-util.h"
+#include "qapi/error.h"
+#include "qapi/qapi-types-migration.h"
+#include "qapi/qapi-visit-migration.h"
+#include "qobject/qdict.h"
+#include "qobject/qjson.h"
+#include "qobject/qlist.h"
+#include "qapi/qobject-input-visitor.h"
+#include "qapi/qobject-output-visitor.h"
+
+/*
+ * Number of seconds we wait when looking for migration
+ * status changes, to avoid test suite hanging forever
+ * when things go wrong. Needs to be higher enough to
+ * avoid false positives on loaded hosts.
+ */
+#define MIGRATION_STATUS_WAIT_TIMEOUT 120
+
+/*
+ * Wait for a "MIGRATION" event. This is what Libvirt uses to track
+ * migration status changes.
+ */
+void migration_event_wait(QTestState *s, const char *target)
+{
+ QDict *response, *data;
+ const char *status;
+ bool found;
+
+ do {
+ response = qtest_qmp_eventwait_ref(s, "MIGRATION");
+ data = qdict_get_qdict(response, "data");
+ g_assert(data);
+ status = qdict_get_str(data, "status");
+ found = (strcmp(status, target) == 0);
+ qobject_unref(response);
+ } while (!found);
+}
+
+/*
+ * Convert a string representing a single channel to an object.
+ * @str may be in JSON or dotted keys format.
+ */
+QObject *migrate_str_to_channel(const char *str)
+{
+ Visitor *v;
+ MigrationChannel *channel;
+ QObject *obj;
+
+ /* Create the channel */
+ v = qobject_input_visitor_new_str(str, "channel-type", &error_abort);
+ visit_type_MigrationChannel(v, NULL, &channel, &error_abort);
+ visit_free(v);
+
+ /* Create the object */
+ v = qobject_output_visitor_new(&obj);
+ visit_type_MigrationChannel(v, NULL, &channel, &error_abort);
+ visit_complete(v, &obj);
+ visit_free(v);
+
+ qapi_free_MigrationChannel(channel);
+ return obj;
+}
+
+void migrate_qmp_fail(QTestState *who, const char *uri,
+ QObject *channels, const char *fmt, ...)
+{
+ va_list ap;
+ QDict *args, *err;
+
+ va_start(ap, fmt);
+ args = qdict_from_vjsonf_nofail(fmt, ap);
+ va_end(ap);
+
+ g_assert(!qdict_haskey(args, "uri"));
+ if (uri) {
+ qdict_put_str(args, "uri", uri);
+ }
+
+ g_assert(!qdict_haskey(args, "channels"));
+ if (channels) {
+ qdict_put_obj(args, "channels", channels);
+ }
+
+ err = qtest_qmp_assert_failure_ref(
+ who, "{ 'execute': 'migrate', 'arguments': %p}", args);
+
+ g_assert(qdict_haskey(err, "desc"));
+
+ qobject_unref(err);
+}
+
+/*
+ * Send QMP command "migrate".
+ * Arguments are built from @fmt... (formatted like
+ * qobject_from_jsonf_nofail()) with "uri": @uri spliced in.
+ */
+void migrate_qmp(QTestState *who, QTestState *to, const char *uri,
+ QObject *channels, const char *fmt, ...)
+{
+ va_list ap;
+ QDict *args;
+ g_autofree char *connect_uri = NULL;
+
+ va_start(ap, fmt);
+ args = qdict_from_vjsonf_nofail(fmt, ap);
+ va_end(ap);
+
+ g_assert(!qdict_haskey(args, "uri"));
+ if (uri) {
+ qdict_put_str(args, "uri", uri);
+ } else if (!channels) {
+ connect_uri = migrate_get_connect_uri(to);
+ qdict_put_str(args, "uri", connect_uri);
+ }
+
+ g_assert(!qdict_haskey(args, "channels"));
+ if (channels) {
+ QList *channel_list = qobject_to(QList, channels);
+ migrate_set_ports(to, channel_list);
+ qdict_put_obj(args, "channels", channels);
+ }
+
+ qtest_qmp_assert_success(who,
+ "{ 'execute': 'migrate', 'arguments': %p}", args);
+}
+
+void migrate_set_capability(QTestState *who, const char *capability,
+ bool value)
+{
+ qtest_qmp_assert_success(who,
+ "{ 'execute': 'migrate-set-capabilities',"
+ "'arguments': { "
+ "'capabilities': [ { "
+ "'capability': %s, 'state': %i } ] } }",
+ capability, value);
+}
+
+void migrate_incoming_qmp(QTestState *to, const char *uri, QObject *channels,
+ const char *fmt, ...)
+{
+ va_list ap;
+ QDict *args, *rsp;
+
+ va_start(ap, fmt);
+ args = qdict_from_vjsonf_nofail(fmt, ap);
+ va_end(ap);
+
+ g_assert(!qdict_haskey(args, "uri"));
+ if (uri) {
+ qdict_put_str(args, "uri", uri);
+ }
+
+ g_assert(!qdict_haskey(args, "channels"));
+ if (channels) {
+ qdict_put_obj(args, "channels", channels);
+ }
+
+ /* This function relies on the event to work, make sure it's enabled */
+ migrate_set_capability(to, "events", true);
+
+ rsp = qtest_qmp(to, "{ 'execute': 'migrate-incoming', 'arguments': %p}",
+ args);
+
+ if (!qdict_haskey(rsp, "return")) {
+ g_autoptr(GString) s = qobject_to_json_pretty(QOBJECT(rsp), true);
+ g_test_message("%s", s->str);
+ }
+
+ g_assert(qdict_haskey(rsp, "return"));
+ qobject_unref(rsp);
+
+ migration_event_wait(to, "setup");
+}
+
+static bool check_migration_status(QTestState *who, const char *goal,
+ const char **ungoals)
+{
+ bool ready;
+ char *current_status;
+ const char **ungoal;
+
+ current_status = migrate_query_status(who);
+ ready = strcmp(current_status, goal) == 0;
+ if (!ungoals) {
+ g_assert_cmpstr(current_status, !=, "failed");
+ /*
+ * If looking for a state other than completed,
+ * completion of migration would cause the test to
+ * hang.
+ */
+ if (strcmp(goal, "completed") != 0) {
+ g_assert_cmpstr(current_status, !=, "completed");
+ }
+ } else {
+ for (ungoal = ungoals; *ungoal; ungoal++) {
+ g_assert_cmpstr(current_status, !=, *ungoal);
+ }
+ }
+ g_free(current_status);
+ return ready;
+}
+
+void wait_for_migration_status(QTestState *who,
+ const char *goal, const char **ungoals)
+{
+ g_test_timer_start();
+ while (!check_migration_status(who, goal, ungoals)) {
+ usleep(1000);
+
+ g_assert(g_test_timer_elapsed() < MIGRATION_STATUS_WAIT_TIMEOUT);
+ }
+}
+
+void wait_for_migration_complete(QTestState *who)
+{
+ wait_for_migration_status(who, "completed", NULL);
+}
+
+void wait_for_migration_fail(QTestState *from, bool allow_active)
+{
+ g_test_timer_start();
+ QDict *rsp_return;
+ char *status;
+ bool failed;
+
+ do {
+ status = migrate_query_status(from);
+ bool result = !strcmp(status, "setup") || !strcmp(status, "failed") ||
+ (allow_active && !strcmp(status, "active"));
+ if (!result) {
+ fprintf(stderr, "%s: unexpected status status=%s allow_active=%d\n",
+ __func__, status, allow_active);
+ }
+ g_assert(result);
+ failed = !strcmp(status, "failed");
+ g_free(status);
+
+ g_assert(g_test_timer_elapsed() < MIGRATION_STATUS_WAIT_TIMEOUT);
+ } while (!failed);
+
+ /* Is the machine currently running? */
+ rsp_return = qtest_qmp_assert_success_ref(from,
+ "{ 'execute': 'query-status' }");
+ g_assert(qdict_haskey(rsp_return, "running"));
+ g_assert(qdict_get_bool(rsp_return, "running"));
+ qobject_unref(rsp_return);
+}
+
+void wait_for_stop(QTestState *who, QTestMigrationState *state)
+{
+ if (!state->stop_seen) {
+ qtest_qmp_eventwait(who, "STOP");
+ }
+}
+
+void wait_for_resume(QTestState *who, QTestMigrationState *state)
+{
+ if (!state->resume_seen) {
+ qtest_qmp_eventwait(who, "RESUME");
+ }
+}
+
+void wait_for_suspend(QTestState *who, QTestMigrationState *state)
+{
+ if (state->suspend_me && !state->suspend_seen) {
+ qtest_qmp_eventwait(who, "SUSPEND");
+ }
+}
+
+/*
+ * Note: caller is responsible to free the returned object via
+ * qobject_unref() after use
+ */
+QDict *migrate_query(QTestState *who)
+{
+ return qtest_qmp_assert_success_ref(who, "{ 'execute': 'query-migrate' }");
+}
+
+QDict *migrate_query_not_failed(QTestState *who)
+{
+ const char *status;
+ QDict *rsp = migrate_query(who);
+ status = qdict_get_str(rsp, "status");
+ if (g_str_equal(status, "failed")) {
+ g_printerr("query-migrate shows failed migration: %s\n",
+ qdict_get_str(rsp, "error-desc"));
+ }
+ g_assert(!g_str_equal(status, "failed"));
+ return rsp;
+}
+
+/*
+ * Note: caller is responsible to free the returned object via
+ * g_free() after use
+ */
+gchar *migrate_query_status(QTestState *who)
+{
+ QDict *rsp_return = migrate_query(who);
+ gchar *status = g_strdup(qdict_get_str(rsp_return, "status"));
+
+ g_assert(status);
+ qobject_unref(rsp_return);
+
+ return status;
+}
+
+int64_t read_ram_property_int(QTestState *who, const char *property)
+{
+ QDict *rsp_return, *rsp_ram;
+ int64_t result;
+
+ rsp_return = migrate_query_not_failed(who);
+ if (!qdict_haskey(rsp_return, "ram")) {
+ /* Still in setup */
+ result = 0;
+ } else {
+ rsp_ram = qdict_get_qdict(rsp_return, "ram");
+ result = qdict_get_try_int(rsp_ram, property, 0);
+ }
+ qobject_unref(rsp_return);
+ return result;
+}
+
+int64_t read_migrate_property_int(QTestState *who, const char *property)
+{
+ QDict *rsp_return;
+ int64_t result;
+
+ rsp_return = migrate_query_not_failed(who);
+ result = qdict_get_try_int(rsp_return, property, 0);
+ qobject_unref(rsp_return);
+ return result;
+}
+
+uint64_t get_migration_pass(QTestState *who)
+{
+ return read_ram_property_int(who, "dirty-sync-count");
+}
+
+void read_blocktime(QTestState *who)
+{
+ QDict *rsp_return;
+
+ rsp_return = migrate_query_not_failed(who);
+ g_assert(qdict_haskey(rsp_return, "postcopy-blocktime"));
+ qobject_unref(rsp_return);
+}
+
+/*
+ * Wait for two changes in the migration pass count, but bail if we stop.
+ */
+void wait_for_migration_pass(QTestState *who, QTestMigrationState *src_state)
+{
+ uint64_t pass, prev_pass = 0, changes = 0;
+
+ while (changes < 2 && !src_state->stop_seen && !src_state->suspend_seen) {
+ usleep(1000);
+ pass = get_migration_pass(who);
+ changes += (pass != prev_pass);
+ prev_pass = pass;
+ }
+}
+
+static long long migrate_get_parameter_int(QTestState *who,
+ const char *parameter)
+{
+ QDict *rsp;
+ long long result;
+
+ rsp = qtest_qmp_assert_success_ref(
+ who, "{ 'execute': 'query-migrate-parameters' }");
+ result = qdict_get_int(rsp, parameter);
+ qobject_unref(rsp);
+ return result;
+}
+
+static void migrate_check_parameter_int(QTestState *who, const char *parameter,
+ long long value)
+{
+ long long result;
+
+ result = migrate_get_parameter_int(who, parameter);
+ g_assert_cmpint(result, ==, value);
+}
+
+void migrate_set_parameter_int(QTestState *who, const char *parameter,
+ long long value)
+{
+ qtest_qmp_assert_success(who,
+ "{ 'execute': 'migrate-set-parameters',"
+ "'arguments': { %s: %lld } }",
+ parameter, value);
+ migrate_check_parameter_int(who, parameter, value);
+}
+
+static char *migrate_get_parameter_str(QTestState *who, const char *parameter)
+{
+ QDict *rsp;
+ char *result;
+
+ rsp = qtest_qmp_assert_success_ref(
+ who, "{ 'execute': 'query-migrate-parameters' }");
+ result = g_strdup(qdict_get_str(rsp, parameter));
+ qobject_unref(rsp);
+ return result;
+}
+
+static void migrate_check_parameter_str(QTestState *who, const char *parameter,
+ const char *value)
+{
+ g_autofree char *result = migrate_get_parameter_str(who, parameter);
+ g_assert_cmpstr(result, ==, value);
+}
+
+void migrate_set_parameter_str(QTestState *who, const char *parameter,
+ const char *value)
+{
+ qtest_qmp_assert_success(who,
+ "{ 'execute': 'migrate-set-parameters',"
+ "'arguments': { %s: %s } }",
+ parameter, value);
+ migrate_check_parameter_str(who, parameter, value);
+}
+
+static long long migrate_get_parameter_bool(QTestState *who,
+ const char *parameter)
+{
+ QDict *rsp;
+ int result;
+
+ rsp = qtest_qmp_assert_success_ref(
+ who, "{ 'execute': 'query-migrate-parameters' }");
+ result = qdict_get_bool(rsp, parameter);
+ qobject_unref(rsp);
+ return !!result;
+}
+
+static void migrate_check_parameter_bool(QTestState *who, const char *parameter,
+ int value)
+{
+ int result;
+
+ result = migrate_get_parameter_bool(who, parameter);
+ g_assert_cmpint(result, ==, value);
+}
+
+void migrate_set_parameter_bool(QTestState *who, const char *parameter,
+ int value)
+{
+ qtest_qmp_assert_success(who,
+ "{ 'execute': 'migrate-set-parameters',"
+ "'arguments': { %s: %i } }",
+ parameter, value);
+ migrate_check_parameter_bool(who, parameter, value);
+}
+
+void migrate_ensure_non_converge(QTestState *who)
+{
+ /* Can't converge with 1ms downtime + 3 mbs bandwidth limit */
+ migrate_set_parameter_int(who, "max-bandwidth", 3 * 1000 * 1000);
+ migrate_set_parameter_int(who, "downtime-limit", 1);
+}
+
+void migrate_ensure_converge(QTestState *who)
+{
+ /* Should converge with 30s downtime + 1 gbs bandwidth limit */
+ migrate_set_parameter_int(who, "max-bandwidth", 1 * 1000 * 1000 * 1000);
+ migrate_set_parameter_int(who, "downtime-limit", 30 * 1000);
+}
+
+void migrate_pause(QTestState *who)
+{
+ qtest_qmp_assert_success(who, "{ 'execute': 'migrate-pause' }");
+}
+
+void migrate_continue(QTestState *who, const char *state)
+{
+ qtest_qmp_assert_success(who,
+ "{ 'execute': 'migrate-continue',"
+ " 'arguments': { 'state': %s } }",
+ state);
+}
+
+void migrate_recover(QTestState *who, const char *uri)
+{
+ qtest_qmp_assert_success(who,
+ "{ 'exec-oob': 'migrate-recover', "
+ " 'id': 'recover-cmd', "
+ " 'arguments': { 'uri': %s } }",
+ uri);
+}
+
+void migrate_cancel(QTestState *who)
+{
+ qtest_qmp_assert_success(who, "{ 'execute': 'migrate_cancel' }");
+}
+
+void migrate_postcopy_start(QTestState *from, QTestState *to,
+ QTestMigrationState *src_state)
+{
+ qtest_qmp_assert_success(from, "{ 'execute': 'migrate-start-postcopy' }");
+
+ wait_for_stop(from, src_state);
+ qtest_qmp_eventwait(to, "RESUME");
+}
diff --git a/tests/qtest/migration/migration-qmp.h b/tests/qtest/migration/migration-qmp.h
new file mode 100644
index 0000000..faa8181
--- /dev/null
+++ b/tests/qtest/migration/migration-qmp.h
@@ -0,0 +1,48 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#ifndef MIGRATION_QMP_H
+#define MIGRATION_QMP_H
+
+#include "migration-util.h"
+
+QObject *migrate_str_to_channel(const char *str);
+
+G_GNUC_PRINTF(4, 5)
+void migrate_qmp_fail(QTestState *who, const char *uri,
+ QObject *channels, const char *fmt, ...);
+
+G_GNUC_PRINTF(5, 6)
+void migrate_qmp(QTestState *who, QTestState *to, const char *uri,
+ QObject *channels, const char *fmt, ...);
+
+G_GNUC_PRINTF(4, 5)
+void migrate_incoming_qmp(QTestState *who, const char *uri,
+ QObject *channels, const char *fmt, ...);
+
+void migration_event_wait(QTestState *s, const char *target);
+void migrate_set_capability(QTestState *who, const char *capability,
+ bool value);
+int64_t read_ram_property_int(QTestState *who, const char *property);
+void migrate_set_parameter_int(QTestState *who, const char *parameter,
+ long long value);
+void wait_for_stop(QTestState *who, QTestMigrationState *state);
+void wait_for_resume(QTestState *who, QTestMigrationState *state);
+void wait_for_suspend(QTestState *who, QTestMigrationState *state);
+gchar *migrate_query_status(QTestState *who);
+int64_t read_migrate_property_int(QTestState *who, const char *property);
+uint64_t get_migration_pass(QTestState *who);
+void read_blocktime(QTestState *who);
+void wait_for_migration_pass(QTestState *who, QTestMigrationState *src_state);
+void migrate_set_parameter_str(QTestState *who, const char *parameter,
+ const char *value);
+void migrate_set_parameter_bool(QTestState *who, const char *parameter,
+ int value);
+void migrate_ensure_non_converge(QTestState *who);
+void migrate_ensure_converge(QTestState *who);
+void migrate_pause(QTestState *who);
+void migrate_continue(QTestState *who, const char *state);
+void migrate_recover(QTestState *who, const char *uri);
+void migrate_cancel(QTestState *who);
+void migrate_postcopy_start(QTestState *from, QTestState *to,
+ QTestMigrationState *src_state);
+
+#endif /* MIGRATION_QMP_H */
diff --git a/tests/qtest/migration/migration-util.c b/tests/qtest/migration/migration-util.c
new file mode 100644
index 0000000..642cf50
--- /dev/null
+++ b/tests/qtest/migration/migration-util.c
@@ -0,0 +1,398 @@
+/*
+ * QTest migration utilities
+ *
+ * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates
+ * based on the vhost-user-test.c that is:
+ * Copyright (c) 2014 Virtual Open Systems Sarl.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/ctype.h"
+#include "qapi/qapi-visit-sockets.h"
+#include "qapi/qobject-input-visitor.h"
+#include "qapi/error.h"
+#include "qobject/qlist.h"
+#include "qemu/cutils.h"
+#include "qemu/memalign.h"
+
+#include "migration/bootfile.h"
+#include "migration/migration-util.h"
+
+#if defined(__linux__)
+#include <sys/ioctl.h>
+#include <sys/syscall.h>
+#endif
+
+/* for uffd_version_check() */
+#if defined(__linux__) && defined(__NR_userfaultfd) && defined(CONFIG_EVENTFD)
+#include <sys/eventfd.h>
+#include "qemu/userfaultfd.h"
+#endif
+
+/* For dirty ring test; so far only x86_64 is supported */
+#if defined(__linux__) && defined(HOST_X86_64)
+#include "linux/kvm.h"
+#endif
+
+
+static char *SocketAddress_to_str(SocketAddress *addr)
+{
+ switch (addr->type) {
+ case SOCKET_ADDRESS_TYPE_INET:
+ return g_strdup_printf("tcp:%s:%s",
+ addr->u.inet.host,
+ addr->u.inet.port);
+ case SOCKET_ADDRESS_TYPE_UNIX:
+ return g_strdup_printf("unix:%s",
+ addr->u.q_unix.path);
+ case SOCKET_ADDRESS_TYPE_FD:
+ return g_strdup_printf("fd:%s", addr->u.fd.str);
+ case SOCKET_ADDRESS_TYPE_VSOCK:
+ return g_strdup_printf("vsock:%s:%s",
+ addr->u.vsock.cid,
+ addr->u.vsock.port);
+ default:
+ return g_strdup("unknown address type");
+ }
+}
+
+static QDict *SocketAddress_to_qdict(SocketAddress *addr)
+{
+ QDict *dict = qdict_new();
+
+ switch (addr->type) {
+ case SOCKET_ADDRESS_TYPE_INET:
+ qdict_put_str(dict, "type", "inet");
+ qdict_put_str(dict, "host", addr->u.inet.host);
+ qdict_put_str(dict, "port", addr->u.inet.port);
+ break;
+ case SOCKET_ADDRESS_TYPE_UNIX:
+ qdict_put_str(dict, "type", "unix");
+ qdict_put_str(dict, "path", addr->u.q_unix.path);
+ break;
+ case SOCKET_ADDRESS_TYPE_FD:
+ qdict_put_str(dict, "type", "fd");
+ qdict_put_str(dict, "str", addr->u.fd.str);
+ break;
+ case SOCKET_ADDRESS_TYPE_VSOCK:
+ qdict_put_str(dict, "type", "vsock");
+ qdict_put_str(dict, "cid", addr->u.vsock.cid);
+ qdict_put_str(dict, "port", addr->u.vsock.port);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ return dict;
+}
+
+static SocketAddressList *migrate_get_socket_address(QTestState *who)
+{
+ QDict *rsp;
+ SocketAddressList *addrs;
+ Visitor *iv = NULL;
+ QObject *object;
+
+ rsp = migrate_query(who);
+ object = qdict_get(rsp, "socket-address");
+
+ iv = qobject_input_visitor_new(object);
+ visit_type_SocketAddressList(iv, NULL, &addrs, &error_abort);
+ visit_free(iv);
+
+ qobject_unref(rsp);
+ return addrs;
+}
+
+char *migrate_get_connect_uri(QTestState *who)
+{
+ SocketAddressList *addrs;
+ char *connect_uri;
+
+ addrs = migrate_get_socket_address(who);
+ connect_uri = SocketAddress_to_str(addrs->value);
+
+ qapi_free_SocketAddressList(addrs);
+ return connect_uri;
+}
+
+static QDict *
+migrate_get_connect_qdict(QTestState *who)
+{
+ SocketAddressList *addrs;
+ QDict *connect_qdict;
+
+ addrs = migrate_get_socket_address(who);
+ connect_qdict = SocketAddress_to_qdict(addrs->value);
+
+ qapi_free_SocketAddressList(addrs);
+ return connect_qdict;
+}
+
+void migrate_set_ports(QTestState *to, QList *channel_list)
+{
+ g_autoptr(QDict) addr = NULL;
+ QListEntry *entry;
+ const char *addr_port = NULL;
+
+ QLIST_FOREACH_ENTRY(channel_list, entry) {
+ QDict *channel = qobject_to(QDict, qlist_entry_obj(entry));
+ QDict *addrdict = qdict_get_qdict(channel, "addr");
+
+ if (!qdict_haskey(addrdict, "port") ||
+ strcmp(qdict_get_str(addrdict, "port"), "0")) {
+ continue;
+ }
+
+ /*
+ * Fetch addr only if needed, so tests that are not yet connected to
+ * the monitor do not query it. Such tests cannot use port=0.
+ */
+ if (!addr) {
+ addr = migrate_get_connect_qdict(to);
+ }
+
+ if (qdict_haskey(addr, "port")) {
+ addr_port = qdict_get_str(addr, "port");
+ qdict_put_str(addrdict, "port", addr_port);
+ }
+ }
+}
+
+bool migrate_watch_for_events(QTestState *who, const char *name,
+ QDict *event, void *opaque)
+{
+ QTestMigrationState *state = opaque;
+
+ if (g_str_equal(name, "STOP")) {
+ state->stop_seen = true;
+ return true;
+ } else if (g_str_equal(name, "SUSPEND")) {
+ state->suspend_seen = true;
+ return true;
+ } else if (g_str_equal(name, "RESUME")) {
+ state->resume_seen = true;
+ return true;
+ }
+
+ return false;
+}
+
+char *find_common_machine_version(const char *mtype, const char *var1,
+ const char *var2)
+{
+ g_autofree char *type1 = qtest_resolve_machine_alias(var1, mtype);
+ g_autofree char *type2 = qtest_resolve_machine_alias(var2, mtype);
+
+ g_assert(type1 && type2);
+
+ if (g_str_equal(type1, type2)) {
+ /* either can be used */
+ return g_strdup(type1);
+ }
+
+ if (qtest_has_machine_with_env(var2, type1)) {
+ return g_strdup(type1);
+ }
+
+ if (qtest_has_machine_with_env(var1, type2)) {
+ return g_strdup(type2);
+ }
+
+ g_test_message("No common machine version for machine type '%s' between "
+ "binaries %s and %s", mtype, getenv(var1), getenv(var2));
+ g_assert_not_reached();
+}
+
+char *resolve_machine_version(const char *alias, const char *var1,
+ const char *var2)
+{
+ const char *mname = g_getenv("QTEST_QEMU_MACHINE_TYPE");
+ g_autofree char *machine_name = NULL;
+
+ if (mname) {
+ const char *dash = strrchr(mname, '-');
+ const char *dot = strrchr(mname, '.');
+
+ machine_name = g_strdup(mname);
+
+ if (dash && dot) {
+ assert(qtest_has_machine(machine_name));
+ return g_steal_pointer(&machine_name);
+ }
+ /* else: probably an alias, let it be resolved below */
+ } else {
+ /* use the hardcoded alias */
+ machine_name = g_strdup(alias);
+ }
+
+ return find_common_machine_version(machine_name, var1, var2);
+}
+
+typedef struct {
+ char *name;
+ void (*func)(void);
+ void (*func_full)(void *);
+} MigrationTest;
+
+static void migration_test_destroy(gpointer data)
+{
+ MigrationTest *test = (MigrationTest *)data;
+
+ g_free(test->name);
+ g_free(test);
+}
+
+static void migration_test_wrapper(const void *data)
+{
+ MigrationTest *test = (MigrationTest *)data;
+
+ g_test_message("Running /%s%s", qtest_get_arch(), test->name);
+ test->func();
+}
+
+void migration_test_add(const char *path, void (*fn)(void))
+{
+ MigrationTest *test = g_new0(MigrationTest, 1);
+
+ test->func = fn;
+ test->name = g_strdup(path);
+
+ qtest_add_data_func_full(path, test, migration_test_wrapper,
+ migration_test_destroy);
+}
+
+static void migration_test_wrapper_full(const void *data)
+{
+ MigrationTest *test = (MigrationTest *)data;
+
+ g_test_message("Running /%s%s", qtest_get_arch(), test->name);
+ test->func_full(test->name);
+}
+
+void migration_test_add_suffix(const char *path, const char *suffix,
+ void (*fn)(void *))
+{
+ MigrationTest *test = g_new0(MigrationTest, 1);
+
+ g_assert(g_str_has_suffix(path, "/"));
+ g_assert(!g_str_has_prefix(suffix, "/"));
+
+ test->func_full = fn;
+ test->name = g_strconcat(path, suffix, NULL);
+
+ qtest_add_data_func_full(test->name, test, migration_test_wrapper_full,
+ migration_test_destroy);
+}
+
+#ifdef O_DIRECT
+/*
+ * Probe for O_DIRECT support on the filesystem. Since this is used
+ * for tests, be conservative, if anything fails, assume it's
+ * unsupported.
+ */
+bool probe_o_direct_support(const char *tmpfs)
+{
+ g_autofree char *filename = g_strdup_printf("%s/probe-o-direct", tmpfs);
+ int fd, flags = O_CREAT | O_RDWR | O_TRUNC | O_DIRECT;
+ void *buf;
+ ssize_t ret, len;
+ uint64_t offset;
+
+ fd = open(filename, flags, 0660);
+ if (fd < 0) {
+ unlink(filename);
+ return false;
+ }
+
+ /*
+ * Using 1MB alignment as conservative choice to satisfy any
+ * plausible architecture default page size, and/or filesystem
+ * alignment restrictions.
+ */
+ len = 0x100000;
+ offset = 0x100000;
+
+ buf = qemu_try_memalign(len, len);
+ g_assert(buf);
+ memset(buf, 0, len);
+
+ ret = pwrite(fd, buf, len, offset);
+ unlink(filename);
+ g_free(buf);
+
+ if (ret < 0) {
+ return false;
+ }
+
+ return true;
+}
+#endif
+
+#if defined(__linux__) && defined(__NR_userfaultfd) && defined(CONFIG_EVENTFD)
+bool ufd_version_check(bool *uffd_feature_thread_id)
+{
+ struct uffdio_api api_struct;
+ uint64_t ioctl_mask;
+
+ int ufd = uffd_open(O_CLOEXEC);
+
+ if (ufd == -1) {
+ g_test_message("Skipping test: userfaultfd not available");
+ return false;
+ }
+
+ api_struct.api = UFFD_API;
+ api_struct.features = 0;
+ if (ioctl(ufd, UFFDIO_API, &api_struct)) {
+ g_test_message("Skipping test: UFFDIO_API failed");
+ return false;
+ }
+
+ if (uffd_feature_thread_id) {
+ *uffd_feature_thread_id = api_struct.features & UFFD_FEATURE_THREAD_ID;
+ }
+
+ ioctl_mask = (1ULL << _UFFDIO_REGISTER |
+ 1ULL << _UFFDIO_UNREGISTER);
+ if ((api_struct.ioctls & ioctl_mask) != ioctl_mask) {
+ g_test_message("Skipping test: Missing userfault feature");
+ return false;
+ }
+
+ return true;
+}
+#else
+bool ufd_version_check(bool *uffd_feature_thread_id)
+{
+ g_test_message("Skipping test: Userfault not available (builtdtime)");
+ return false;
+}
+#endif
+
+bool kvm_dirty_ring_supported(void)
+{
+#if defined(__linux__) && defined(HOST_X86_64)
+ int ret, kvm_fd = open("/dev/kvm", O_RDONLY);
+
+ if (kvm_fd < 0) {
+ return false;
+ }
+
+ ret = ioctl(kvm_fd, KVM_CHECK_EXTENSION, KVM_CAP_DIRTY_LOG_RING);
+ close(kvm_fd);
+
+ /* We test with 4096 slots */
+ if (ret < 4096) {
+ return false;
+ }
+
+ return true;
+#else
+ return false;
+#endif
+}
diff --git a/tests/qtest/migration-helpers.h b/tests/qtest/migration/migration-util.h
index 72dba36..44815e9 100644
--- a/tests/qtest/migration-helpers.h
+++ b/tests/qtest/migration/migration-util.h
@@ -10,8 +10,8 @@
*
*/
-#ifndef MIGRATION_HELPERS_H
-#define MIGRATION_HELPERS_H
+#ifndef MIGRATION_UTIL_H
+#define MIGRATION_UTIL_H
#include "libqtest.h"
@@ -25,21 +25,6 @@ typedef struct QTestMigrationState {
bool migrate_watch_for_events(QTestState *who, const char *name,
QDict *event, void *opaque);
-G_GNUC_PRINTF(5, 6)
-void migrate_qmp(QTestState *who, QTestState *to, const char *uri,
- const char *channels, const char *fmt, ...);
-
-G_GNUC_PRINTF(3, 4)
-void migrate_incoming_qmp(QTestState *who, const char *uri,
- const char *fmt, ...);
-
-G_GNUC_PRINTF(4, 5)
-void migrate_qmp_fail(QTestState *who, const char *uri,
- const char *channels, const char *fmt, ...);
-
-void migrate_set_capability(QTestState *who, const char *capability,
- bool value);
-
QDict *migrate_query(QTestState *who);
QDict *migrate_query_not_failed(QTestState *who);
@@ -62,7 +47,13 @@ static inline bool probe_o_direct_support(const char *tmpfs)
return false;
}
#endif
+
+bool ufd_version_check(bool *uffd_feature_thread_id);
+bool kvm_dirty_ring_supported(void);
void migration_test_add(const char *path, void (*fn)(void));
-void migration_event_wait(QTestState *s, const char *target);
+void migration_test_add_suffix(const char *path, const char *suffix,
+ void (*fn)(void *));
+char *migrate_get_connect_uri(QTestState *who);
+void migrate_set_ports(QTestState *to, QList *channel_list);
-#endif /* MIGRATION_HELPERS_H */
+#endif /* MIGRATION_UTIL_H */
diff --git a/tests/qtest/migration/misc-tests.c b/tests/qtest/migration/misc-tests.c
new file mode 100644
index 0000000..5499525
--- /dev/null
+++ b/tests/qtest/migration/misc-tests.c
@@ -0,0 +1,297 @@
+/*
+ * QTest testcases for migration
+ *
+ * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates
+ * based on the vhost-user-test.c that is:
+ * Copyright (c) 2014 Virtual Open Systems Sarl.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qobject/qjson.h"
+#include "libqtest.h"
+#include "migration/framework.h"
+#include "migration/migration-qmp.h"
+#include "migration/migration-util.h"
+
+#define ANALYZE_SCRIPT "scripts/analyze-migration.py"
+
+static char *tmpfs;
+
+static void test_baddest(void)
+{
+ MigrateStart args = {
+ .hide_stderr = true
+ };
+ QTestState *from, *to;
+
+ if (migrate_start(&from, &to, "tcp:127.0.0.1:0", &args)) {
+ return;
+ }
+ migrate_qmp(from, to, "tcp:127.0.0.1:0", NULL, "{}");
+ wait_for_migration_fail(from, false);
+ migrate_end(from, to, false);
+}
+
+#ifndef _WIN32
+static void test_analyze_script(void)
+{
+ MigrateStart args = {
+ .opts_source = "-uuid 11111111-1111-1111-1111-111111111111",
+ };
+ QTestState *from, *to;
+ g_autofree char *uri = NULL;
+ g_autofree char *file = NULL;
+ int pid, wstatus;
+ const char *python = g_getenv("PYTHON");
+
+ if (!python) {
+ g_test_skip("PYTHON variable not set");
+ return;
+ }
+
+ /* dummy url */
+ if (migrate_start(&from, &to, "tcp:127.0.0.1:0", &args)) {
+ return;
+ }
+
+ /*
+ * Setting these two capabilities causes the "configuration"
+ * vmstate to include subsections for them. The script needs to
+ * parse those subsections properly.
+ */
+ migrate_set_capability(from, "validate-uuid", true);
+ migrate_set_capability(from, "x-ignore-shared", true);
+
+ file = g_strdup_printf("%s/migfile", tmpfs);
+ uri = g_strdup_printf("exec:cat > %s", file);
+
+ migrate_ensure_converge(from);
+ migrate_qmp(from, to, uri, NULL, "{}");
+ wait_for_migration_complete(from);
+
+ pid = fork();
+ if (!pid) {
+ close(1);
+ open("/dev/null", O_WRONLY);
+ execl(python, python, ANALYZE_SCRIPT, "-f", file, NULL);
+ g_assert_not_reached();
+ }
+
+ g_assert(waitpid(pid, &wstatus, 0) == pid);
+ if (!WIFEXITED(wstatus) || WEXITSTATUS(wstatus) != 0) {
+ g_test_message("Failed to analyze the migration stream");
+ g_test_fail();
+ }
+ migrate_end(from, to, false);
+ unlink(file);
+}
+#endif
+
+static void test_ignore_shared(void)
+{
+ g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs);
+ QTestState *from, *to;
+ MigrateStart args = {
+ .use_shmem = true,
+ .caps[MIGRATION_CAPABILITY_X_IGNORE_SHARED] = true,
+ };
+
+ if (migrate_start(&from, &to, uri, &args)) {
+ return;
+ }
+
+ migrate_ensure_non_converge(from);
+ migrate_prepare_for_dirty_mem(from);
+
+ /* Wait for the first serial output from the source */
+ wait_for_serial("src_serial");
+
+ migrate_qmp(from, to, uri, NULL, "{}");
+
+ migrate_wait_for_dirty_mem(from, to);
+
+ wait_for_stop(from, get_src());
+
+ qtest_qmp_eventwait(to, "RESUME");
+
+ wait_for_serial("dest_serial");
+ wait_for_migration_complete(from);
+
+ /* Check whether shared RAM has been really skipped */
+ g_assert_cmpint(
+ read_ram_property_int(from, "transferred"), <, 4 * 1024 * 1024);
+
+ migrate_end(from, to, true);
+}
+
+static void do_test_validate_uuid(MigrateStart *args, bool should_fail)
+{
+ g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs);
+ QTestState *from, *to;
+
+ if (migrate_start(&from, &to, uri, args)) {
+ return;
+ }
+
+ /*
+ * UUID validation is at the begin of migration. So, the main process of
+ * migration is not interesting for us here. Thus, set huge downtime for
+ * very fast migration.
+ */
+ migrate_set_parameter_int(from, "downtime-limit", 1000000);
+ migrate_set_capability(from, "validate-uuid", true);
+
+ /* Wait for the first serial output from the source */
+ wait_for_serial("src_serial");
+
+ migrate_qmp(from, to, uri, NULL, "{}");
+
+ if (should_fail) {
+ qtest_set_expected_status(to, EXIT_FAILURE);
+ wait_for_migration_fail(from, true);
+ } else {
+ wait_for_migration_complete(from);
+ }
+
+ migrate_end(from, to, false);
+}
+
+static void test_validate_uuid(void)
+{
+ MigrateStart args = {
+ .opts_source = "-uuid 11111111-1111-1111-1111-111111111111",
+ .opts_target = "-uuid 11111111-1111-1111-1111-111111111111",
+ };
+
+ do_test_validate_uuid(&args, false);
+}
+
+static void test_validate_uuid_error(void)
+{
+ MigrateStart args = {
+ .opts_source = "-uuid 11111111-1111-1111-1111-111111111111",
+ .opts_target = "-uuid 22222222-2222-2222-2222-222222222222",
+ .hide_stderr = true,
+ };
+
+ do_test_validate_uuid(&args, true);
+}
+
+static void test_validate_uuid_src_not_set(void)
+{
+ MigrateStart args = {
+ .opts_target = "-uuid 22222222-2222-2222-2222-222222222222",
+ .hide_stderr = true,
+ };
+
+ do_test_validate_uuid(&args, false);
+}
+
+static void test_validate_uuid_dst_not_set(void)
+{
+ MigrateStart args = {
+ .opts_source = "-uuid 11111111-1111-1111-1111-111111111111",
+ .hide_stderr = true,
+ };
+
+ do_test_validate_uuid(&args, false);
+}
+
+static void do_test_validate_uri_channel(MigrateCommon *args)
+{
+ QTestState *from, *to;
+ QObject *channels;
+
+ if (migrate_start(&from, &to, args->listen_uri, &args->start)) {
+ return;
+ }
+
+ /* Wait for the first serial output from the source */
+ wait_for_serial("src_serial");
+
+ /*
+ * 'uri' and 'channels' validation is checked even before the migration
+ * starts.
+ */
+ channels = args->connect_channels ?
+ qobject_from_json(args->connect_channels, &error_abort) :
+ NULL;
+ migrate_qmp_fail(from, args->connect_uri, channels, "{}");
+
+ migrate_end(from, to, false);
+}
+
+static void test_validate_uri_channels_both_set(void)
+{
+ MigrateCommon args = {
+ .start = {
+ .hide_stderr = true,
+ },
+ .listen_uri = "defer",
+ .connect_uri = "tcp:127.0.0.1:0",
+ .connect_channels = ("[ { ""'channel-type': 'main',"
+ " 'addr': { 'transport': 'socket',"
+ " 'type': 'inet',"
+ " 'host': '127.0.0.1',"
+ " 'port': '0' } } ]"),
+ };
+
+ do_test_validate_uri_channel(&args);
+}
+
+static void test_validate_uri_channels_none_set(void)
+{
+ MigrateCommon args = {
+ .start = {
+ .hide_stderr = true,
+ },
+ .listen_uri = "defer",
+ };
+
+ do_test_validate_uri_channel(&args);
+}
+
+static void migration_test_add_misc_smoke(MigrationTestEnv *env)
+{
+#ifndef _WIN32
+ migration_test_add("/migration/analyze-script", test_analyze_script);
+#endif
+}
+
+void migration_test_add_misc(MigrationTestEnv *env)
+{
+ tmpfs = env->tmpfs;
+
+ migration_test_add_misc_smoke(env);
+
+ if (!env->full_set) {
+ return;
+ }
+
+ migration_test_add("/migration/bad_dest", test_baddest);
+
+ /*
+ * Our CI system has problems with shared memory.
+ * Don't run this test until we find a workaround.
+ */
+ if (getenv("QEMU_TEST_FLAKY_TESTS")) {
+ migration_test_add("/migration/ignore-shared", test_ignore_shared);
+ }
+
+ migration_test_add("/migration/validate_uuid", test_validate_uuid);
+ migration_test_add("/migration/validate_uuid_error",
+ test_validate_uuid_error);
+ migration_test_add("/migration/validate_uuid_src_not_set",
+ test_validate_uuid_src_not_set);
+ migration_test_add("/migration/validate_uuid_dst_not_set",
+ test_validate_uuid_dst_not_set);
+ migration_test_add("/migration/validate_uri/channels/both_set",
+ test_validate_uri_channels_both_set);
+ migration_test_add("/migration/validate_uri/channels/none_set",
+ test_validate_uri_channels_none_set);
+}
diff --git a/tests/qtest/migration/postcopy-tests.c b/tests/qtest/migration/postcopy-tests.c
new file mode 100644
index 0000000..3773525
--- /dev/null
+++ b/tests/qtest/migration/postcopy-tests.c
@@ -0,0 +1,149 @@
+/*
+ * QTest testcases for postcopy migration
+ *
+ * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates
+ * based on the vhost-user-test.c that is:
+ * Copyright (c) 2014 Virtual Open Systems Sarl.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "libqtest.h"
+#include "migration/framework.h"
+#include "migration/migration-util.h"
+#include "qobject/qlist.h"
+#include "qemu/module.h"
+#include "qemu/option.h"
+#include "qemu/range.h"
+#include "qemu/sockets.h"
+
+static void test_postcopy(void)
+{
+ MigrateCommon args = { };
+
+ test_postcopy_common(&args);
+}
+
+static void test_postcopy_suspend(void)
+{
+ MigrateCommon args = {
+ .start.suspend_me = true,
+ };
+
+ test_postcopy_common(&args);
+}
+
+static void test_postcopy_preempt(void)
+{
+ MigrateCommon args = {
+ .start = {
+ .caps[MIGRATION_CAPABILITY_POSTCOPY_PREEMPT] = true,
+ },
+ };
+
+ test_postcopy_common(&args);
+}
+
+static void test_postcopy_recovery(void)
+{
+ MigrateCommon args = { };
+
+ test_postcopy_recovery_common(&args);
+}
+
+static void test_postcopy_recovery_fail_handshake(void)
+{
+ MigrateCommon args = {
+ .postcopy_recovery_fail_stage = POSTCOPY_FAIL_RECOVERY,
+ };
+
+ test_postcopy_recovery_common(&args);
+}
+
+static void test_postcopy_recovery_fail_reconnect(void)
+{
+ MigrateCommon args = {
+ .postcopy_recovery_fail_stage = POSTCOPY_FAIL_CHANNEL_ESTABLISH,
+ };
+
+ test_postcopy_recovery_common(&args);
+}
+
+static void test_postcopy_preempt_recovery(void)
+{
+ MigrateCommon args = {
+ .start = {
+ .caps[MIGRATION_CAPABILITY_POSTCOPY_PREEMPT] = true,
+ },
+ };
+
+ test_postcopy_recovery_common(&args);
+}
+
+static void migration_test_add_postcopy_smoke(MigrationTestEnv *env)
+{
+ if (env->has_uffd) {
+ migration_test_add("/migration/postcopy/plain", test_postcopy);
+ migration_test_add("/migration/postcopy/recovery/plain",
+ test_postcopy_recovery);
+ migration_test_add("/migration/postcopy/preempt/plain",
+ test_postcopy_preempt);
+ }
+}
+
+static void test_multifd_postcopy(void)
+{
+ MigrateCommon args = {
+ .start = {
+ .caps[MIGRATION_CAPABILITY_MULTIFD] = true,
+ },
+ };
+
+ test_postcopy_common(&args);
+}
+
+static void test_multifd_postcopy_preempt(void)
+{
+ MigrateCommon args = {
+ .start = {
+ .caps[MIGRATION_CAPABILITY_MULTIFD] = true,
+ .caps[MIGRATION_CAPABILITY_POSTCOPY_PREEMPT] = true,
+ },
+ };
+
+ test_postcopy_common(&args);
+}
+
+void migration_test_add_postcopy(MigrationTestEnv *env)
+{
+ migration_test_add_postcopy_smoke(env);
+
+ if (!env->full_set) {
+ return;
+ }
+
+ if (env->has_uffd) {
+ migration_test_add("/migration/postcopy/preempt/recovery/plain",
+ test_postcopy_preempt_recovery);
+
+ migration_test_add(
+ "/migration/postcopy/recovery/double-failures/handshake",
+ test_postcopy_recovery_fail_handshake);
+
+ migration_test_add(
+ "/migration/postcopy/recovery/double-failures/reconnect",
+ test_postcopy_recovery_fail_reconnect);
+
+ migration_test_add("/migration/multifd+postcopy/plain",
+ test_multifd_postcopy);
+ migration_test_add("/migration/multifd+postcopy/preempt/plain",
+ test_multifd_postcopy_preempt);
+ if (env->is_x86) {
+ migration_test_add("/migration/postcopy/suspend",
+ test_postcopy_suspend);
+ }
+ }
+}
diff --git a/tests/qtest/migration/ppc64/Makefile b/tests/qtest/migration/ppc64/Makefile
new file mode 100644
index 0000000..a3a2d98
--- /dev/null
+++ b/tests/qtest/migration/ppc64/Makefile
@@ -0,0 +1,15 @@
+.PHONY: all clean
+all: a-b-kernel.h
+
+a-b-kernel.h: ppc64.kernel
+ echo "$$__note" > $@
+ xxd -i $< | sed -e 's/.*int.*//' >> $@
+
+ppc64.kernel: ppc64.elf
+ $(CROSS_PREFIX)objcopy -O binary -S $< $@
+
+ppc64.elf: a-b-kernel.S
+ $(CROSS_PREFIX)gcc -static -o $@ -nostdlib -Wl,--build-id=none $<
+
+clean:
+ $(RM) *.kernel *.elf
diff --git a/tests/qtest/migration/ppc64/a-b-kernel.S b/tests/qtest/migration/ppc64/a-b-kernel.S
new file mode 100644
index 0000000..0613a8d
--- /dev/null
+++ b/tests/qtest/migration/ppc64/a-b-kernel.S
@@ -0,0 +1,66 @@
+#
+# Copyright (c) 2024 IBM, Inc
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or later.
+# See the COPYING file in the top-level directory.
+
+#include "../migration-test.h"
+
+.section .text
+
+.macro print ch
+ li %r3,PPC_H_PUT_TERM_CHAR
+ li %r4,0
+ li %r5,1
+ li %r6,\ch
+ sldi %r6,%r6,56
+ sc 1
+.endm
+
+ .globl _start
+_start:
+. = 0x100
+ /*
+ * Enter 64-bit mode. Not necessary because the test uses 32-bit
+ * addresses, but those constants could easily be changed and break
+ * in 32-bit mode.
+ */
+ mfmsr %r9
+ li %r10,-1
+ rldimi %r9,%r10,63,0
+ mtmsrd %r9
+
+ /*
+ * Set up test memory region. Non-volatiles are used because the
+ * hcall can clobber regs.
+ * r20 - start address
+ * r21 - number of pages
+ */
+ lis %r20,PPC_TEST_MEM_START@h
+ ori %r20,%r20,PPC_TEST_MEM_START@l
+ lis %r9,PPC_TEST_MEM_END@h
+ ori %r9,%r9,PPC_TEST_MEM_END@l
+ subf %r21,%r20,%r9
+ li %r10,TEST_MEM_PAGE_SIZE
+ divd %r21,%r21,%r10
+
+ print 'A'
+
+ li %r3,0
+ mr %r9,%r20
+ mtctr %r21
+1: stb %r3,0(%r9)
+ addi %r9,%r9,TEST_MEM_PAGE_SIZE
+ bdnz 1b
+
+loop:
+ mr %r9,%r20
+ mtctr %r21
+1: lbz %r3,0(%r9)
+ addi %r3,%r3,1
+ stb %r3,0(%r9)
+ addi %r9,%r9,TEST_MEM_PAGE_SIZE
+ bdnz 1b
+
+ print 'B'
+ b loop
diff --git a/tests/qtest/migration/ppc64/a-b-kernel.h b/tests/qtest/migration/ppc64/a-b-kernel.h
new file mode 100644
index 0000000..673317e
--- /dev/null
+++ b/tests/qtest/migration/ppc64/a-b-kernel.h
@@ -0,0 +1,42 @@
+/* This file is automatically generated from the assembly file in
+ * tests/migration/ppc64. Edit that file and then run "make all"
+ * inside tests/migration to update, and then remember to send both
+ * the header and the assembler differences in your patch submission.
+ */
+unsigned char ppc64_kernel[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x7d, 0x20, 0x00, 0xa6, 0x39, 0x40, 0xff, 0xff,
+ 0x79, 0x49, 0xf8, 0x0e, 0x7d, 0x20, 0x01, 0x64, 0x3e, 0x80, 0x00, 0x10,
+ 0x62, 0x94, 0x00, 0x00, 0x3d, 0x20, 0x06, 0x40, 0x61, 0x29, 0x00, 0x00,
+ 0x7e, 0xb4, 0x48, 0x50, 0x39, 0x40, 0x10, 0x00, 0x7e, 0xb5, 0x53, 0xd2,
+ 0x38, 0x60, 0x00, 0x58, 0x38, 0x80, 0x00, 0x00, 0x38, 0xa0, 0x00, 0x01,
+ 0x38, 0xc0, 0x00, 0x41, 0x78, 0xc6, 0xc1, 0xc6, 0x44, 0x00, 0x00, 0x22,
+ 0x38, 0x60, 0x00, 0x00, 0x7e, 0x89, 0xa3, 0x78, 0x7e, 0xa9, 0x03, 0xa6,
+ 0x98, 0x69, 0x00, 0x00, 0x39, 0x29, 0x10, 0x00, 0x42, 0x00, 0xff, 0xf8,
+ 0x7e, 0x89, 0xa3, 0x78, 0x7e, 0xa9, 0x03, 0xa6, 0x88, 0x69, 0x00, 0x00,
+ 0x38, 0x63, 0x00, 0x01, 0x98, 0x69, 0x00, 0x00, 0x39, 0x29, 0x10, 0x00,
+ 0x42, 0x00, 0xff, 0xf0, 0x38, 0x60, 0x00, 0x58, 0x38, 0x80, 0x00, 0x00,
+ 0x38, 0xa0, 0x00, 0x01, 0x38, 0xc0, 0x00, 0x42, 0x78, 0xc6, 0xc1, 0xc6,
+ 0x44, 0x00, 0x00, 0x22, 0x4b, 0xff, 0xff, 0xcc
+};
+
diff --git a/tests/qtest/migration/precopy-tests.c b/tests/qtest/migration/precopy-tests.c
new file mode 100644
index 0000000..bb38292
--- /dev/null
+++ b/tests/qtest/migration/precopy-tests.c
@@ -0,0 +1,1337 @@
+/*
+ * QTest testcase for precopy migration
+ *
+ * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates
+ * based on the vhost-user-test.c that is:
+ * Copyright (c) 2014 Virtual Open Systems Sarl.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "chardev/char.h"
+#include "crypto/tlscredspsk.h"
+#include "libqtest.h"
+#include "migration/bootfile.h"
+#include "migration/framework.h"
+#include "migration/migration-qmp.h"
+#include "migration/migration-util.h"
+#include "ppc-util.h"
+#include "qobject/qlist.h"
+#include "qapi-types-migration.h"
+#include "qemu/module.h"
+#include "qemu/option.h"
+#include "qemu/range.h"
+#include "qemu/sockets.h"
+
+
+/*
+ * Dirtylimit stop working if dirty page rate error
+ * value less than DIRTYLIMIT_TOLERANCE_RANGE
+ */
+#define DIRTYLIMIT_TOLERANCE_RANGE 25 /* MB/s */
+
+static char *tmpfs;
+
+static void test_precopy_unix_plain(void)
+{
+ g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs);
+ MigrateCommon args = {
+ .listen_uri = uri,
+ .connect_uri = uri,
+ /*
+ * The simplest use case of precopy, covering smoke tests of
+ * get-dirty-log dirty tracking.
+ */
+ .live = true,
+ };
+
+ test_precopy_common(&args);
+}
+
+static void test_precopy_unix_suspend_live(void)
+{
+ g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs);
+ MigrateCommon args = {
+ .listen_uri = uri,
+ .connect_uri = uri,
+ /*
+ * despite being live, the test is fast because the src
+ * suspends immediately.
+ */
+ .live = true,
+ .start.suspend_me = true,
+ };
+
+ test_precopy_common(&args);
+}
+
+static void test_precopy_unix_suspend_notlive(void)
+{
+ g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs);
+ MigrateCommon args = {
+ .listen_uri = uri,
+ .connect_uri = uri,
+ .start.suspend_me = true,
+ };
+
+ test_precopy_common(&args);
+}
+
+static void test_precopy_unix_dirty_ring(void)
+{
+ g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs);
+ MigrateCommon args = {
+ .start = {
+ .use_dirty_ring = true,
+ },
+ .listen_uri = uri,
+ .connect_uri = uri,
+ /*
+ * Besides the precopy/unix basic test, cover dirty ring interface
+ * rather than get-dirty-log.
+ */
+ .live = true,
+ };
+
+ test_precopy_common(&args);
+}
+
+#ifdef CONFIG_RDMA
+
+#include <sys/resource.h>
+
+/*
+ * During migration over RDMA, it will try to pin portions of guest memory,
+ * typically exceeding 100MB in this test, while the remainder will be
+ * transmitted as compressed zero pages.
+ *
+ * REQUIRED_MEMLOCK_SZ indicates the minimal mlock size in the current context.
+ */
+#define REQUIRED_MEMLOCK_SZ (128 << 20) /* 128MB */
+
+/* check 'ulimit -l' */
+static bool mlock_check(void)
+{
+ uid_t uid;
+ struct rlimit rlim;
+
+ uid = getuid();
+ if (uid == 0) {
+ return true;
+ }
+
+ if (getrlimit(RLIMIT_MEMLOCK, &rlim) != 0) {
+ return false;
+ }
+
+ return rlim.rlim_cur >= REQUIRED_MEMLOCK_SZ;
+}
+
+#define RDMA_MIGRATION_HELPER "scripts/rdma-migration-helper.sh"
+static int new_rdma_link(char *buffer, bool ipv6)
+{
+ char cmd[256];
+ bool verbose = g_getenv("QTEST_LOG");
+
+ snprintf(cmd, sizeof(cmd), "IP_FAMILY=%s %s detect %s",
+ ipv6 ? "ipv6" : "ipv4", RDMA_MIGRATION_HELPER,
+ verbose ? "" : "2>/dev/null");
+
+ FILE *pipe = popen(cmd, "r");
+ if (pipe == NULL) {
+ perror("Failed to run script");
+ return -1;
+ }
+
+ int idx = 0;
+ while (fgets(buffer + idx, 128 - idx, pipe) != NULL) {
+ idx += strlen(buffer);
+ }
+
+ int status = pclose(pipe);
+ if (status == -1) {
+ perror("Error reported by pclose()");
+ return -1;
+ } else if (WIFEXITED(status)) {
+ return WEXITSTATUS(status);
+ }
+
+ return -1;
+}
+
+static void __test_precopy_rdma_plain(bool ipv6)
+{
+ char buffer[128] = {};
+
+ if (!mlock_check()) {
+ g_test_skip("'ulimit -l' is too small, require >=128M");
+ return;
+ }
+
+ if (new_rdma_link(buffer, ipv6)) {
+ g_test_skip("No rdma link available\n"
+ "# To enable the test:\n"
+ "# Run \'" RDMA_MIGRATION_HELPER " setup\' with root to "
+ "setup a new rdma/rxe link and rerun the test\n"
+ "# Optional: run 'scripts/rdma-migration-helper.sh clean' "
+ "to revert the 'setup'");
+ return;
+ }
+
+ /*
+ * TODO: query a free port instead of hard code.
+ * 29200=('R'+'D'+'M'+'A')*100
+ **/
+ g_autofree char *uri = g_strdup_printf("rdma:%s:29200", buffer);
+
+ MigrateCommon args = {
+ .listen_uri = uri,
+ .connect_uri = uri,
+ };
+
+ test_precopy_common(&args);
+}
+
+static void test_precopy_rdma_plain(void)
+{
+ __test_precopy_rdma_plain(false);
+}
+
+static void test_precopy_rdma_plain_ipv6(void)
+{
+ __test_precopy_rdma_plain(true);
+}
+#endif
+
+static void test_precopy_tcp_plain(void)
+{
+ MigrateCommon args = {
+ .listen_uri = "tcp:127.0.0.1:0",
+ };
+
+ test_precopy_common(&args);
+}
+
+static void test_precopy_tcp_switchover_ack(void)
+{
+ MigrateCommon args = {
+ .listen_uri = "tcp:127.0.0.1:0",
+ .start = {
+ .caps[MIGRATION_CAPABILITY_RETURN_PATH] = true,
+ .caps[MIGRATION_CAPABILITY_SWITCHOVER_ACK] = true,
+ },
+ /*
+ * Source VM must be running in order to consider the switchover ACK
+ * when deciding to do switchover or not.
+ */
+ .live = true,
+ };
+
+ test_precopy_common(&args);
+}
+
+#ifndef _WIN32
+static void *migrate_hook_start_fd(QTestState *from,
+ QTestState *to)
+{
+ int ret;
+ int pair[2];
+
+ /* Create two connected sockets for migration */
+ ret = qemu_socketpair(PF_LOCAL, SOCK_STREAM, 0, pair);
+ g_assert_cmpint(ret, ==, 0);
+
+ /* Send the 1st socket to the target */
+ qtest_qmp_fds_assert_success(to, &pair[0], 1,
+ "{ 'execute': 'getfd',"
+ " 'arguments': { 'fdname': 'fd-mig' }}");
+ close(pair[0]);
+
+ /* Start incoming migration from the 1st socket */
+ migrate_incoming_qmp(to, "fd:fd-mig", NULL, "{}");
+
+ /* Send the 2nd socket to the target */
+ qtest_qmp_fds_assert_success(from, &pair[1], 1,
+ "{ 'execute': 'getfd',"
+ " 'arguments': { 'fdname': 'fd-mig' }}");
+ close(pair[1]);
+
+ return NULL;
+}
+
+static void migrate_hook_end_fd(QTestState *from,
+ QTestState *to,
+ void *opaque)
+{
+ QDict *rsp;
+ const char *error_desc;
+
+ /* Test closing fds */
+ /*
+ * We assume, that QEMU removes named fd from its list,
+ * so this should fail.
+ */
+ rsp = qtest_qmp(from,
+ "{ 'execute': 'closefd',"
+ " 'arguments': { 'fdname': 'fd-mig' }}");
+ g_assert_true(qdict_haskey(rsp, "error"));
+ error_desc = qdict_get_str(qdict_get_qdict(rsp, "error"), "desc");
+ g_assert_cmpstr(error_desc, ==, "File descriptor named 'fd-mig' not found");
+ qobject_unref(rsp);
+
+ rsp = qtest_qmp(to,
+ "{ 'execute': 'closefd',"
+ " 'arguments': { 'fdname': 'fd-mig' }}");
+ g_assert_true(qdict_haskey(rsp, "error"));
+ error_desc = qdict_get_str(qdict_get_qdict(rsp, "error"), "desc");
+ g_assert_cmpstr(error_desc, ==, "File descriptor named 'fd-mig' not found");
+ qobject_unref(rsp);
+}
+
+static void test_precopy_fd_socket(void)
+{
+ MigrateCommon args = {
+ .listen_uri = "defer",
+ .connect_uri = "fd:fd-mig",
+ .start_hook = migrate_hook_start_fd,
+ .end_hook = migrate_hook_end_fd,
+ };
+ test_precopy_common(&args);
+}
+
+static void *migrate_hook_start_precopy_fd_file(QTestState *from,
+ QTestState *to)
+{
+ g_autofree char *file = g_strdup_printf("%s/%s", tmpfs, FILE_TEST_FILENAME);
+ int src_flags = O_CREAT | O_RDWR;
+ int dst_flags = O_CREAT | O_RDWR;
+ int fds[2];
+
+ fds[0] = open(file, src_flags, 0660);
+ assert(fds[0] != -1);
+
+ fds[1] = open(file, dst_flags, 0660);
+ assert(fds[1] != -1);
+
+
+ qtest_qmp_fds_assert_success(to, &fds[0], 1,
+ "{ 'execute': 'getfd',"
+ " 'arguments': { 'fdname': 'fd-mig' }}");
+
+ qtest_qmp_fds_assert_success(from, &fds[1], 1,
+ "{ 'execute': 'getfd',"
+ " 'arguments': { 'fdname': 'fd-mig' }}");
+
+ close(fds[0]);
+ close(fds[1]);
+
+ return NULL;
+}
+
+static void test_precopy_fd_file(void)
+{
+ MigrateCommon args = {
+ .listen_uri = "defer",
+ .connect_uri = "fd:fd-mig",
+ .start_hook = migrate_hook_start_precopy_fd_file,
+ .end_hook = migrate_hook_end_fd,
+ };
+ test_file_common(&args, true);
+}
+#endif /* _WIN32 */
+
+/*
+ * The way auto_converge works, we need to do too many passes to
+ * run this test. Auto_converge logic is only run once every
+ * three iterations, so:
+ *
+ * - 3 iterations without auto_converge enabled
+ * - 3 iterations with pct = 5
+ * - 3 iterations with pct = 30
+ * - 3 iterations with pct = 55
+ * - 3 iterations with pct = 80
+ * - 3 iterations with pct = 95 (max(95, 80 + 25))
+ *
+ * To make things even worse, we need to run the initial stage at
+ * 3MB/s so we enter autoconverge even when host is (over)loaded.
+ */
+static void test_auto_converge(void)
+{
+ g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs);
+ MigrateStart args = {};
+ QTestState *from, *to;
+ int64_t percentage;
+
+ /*
+ * We want the test to be stable and as fast as possible.
+ * E.g., with 1Gb/s bandwidth migration may pass without throttling,
+ * so we need to decrease a bandwidth.
+ */
+ const int64_t init_pct = 5, inc_pct = 25, max_pct = 95;
+ uint64_t prev_dirty_sync_cnt, dirty_sync_cnt;
+ int max_try_count, hit = 0;
+
+ if (migrate_start(&from, &to, uri, &args)) {
+ return;
+ }
+
+ migrate_set_capability(from, "auto-converge", true);
+ migrate_set_parameter_int(from, "cpu-throttle-initial", init_pct);
+ migrate_set_parameter_int(from, "cpu-throttle-increment", inc_pct);
+ migrate_set_parameter_int(from, "max-cpu-throttle", max_pct);
+
+ /*
+ * Set the initial parameters so that the migration could not converge
+ * without throttling.
+ */
+ migrate_ensure_non_converge(from);
+
+ /* To check remaining size after precopy */
+ migrate_set_capability(from, "pause-before-switchover", true);
+
+ /* Wait for the first serial output from the source */
+ wait_for_serial("src_serial");
+
+ migrate_qmp(from, to, uri, NULL, "{}");
+
+ /* Wait for throttling begins */
+ percentage = 0;
+ do {
+ percentage = read_migrate_property_int(from, "cpu-throttle-percentage");
+ if (percentage != 0) {
+ break;
+ }
+ usleep(20);
+ g_assert_false(get_src()->stop_seen);
+ } while (true);
+ /* The first percentage of throttling should be at least init_pct */
+ g_assert_cmpint(percentage, >=, init_pct);
+
+ /*
+ * End the loop when the dirty sync count greater than 1.
+ */
+ while ((dirty_sync_cnt = get_migration_pass(from)) < 2) {
+ usleep(1000 * 1000);
+ }
+
+ prev_dirty_sync_cnt = dirty_sync_cnt;
+
+ /*
+ * The RAMBlock dirty sync count must changes in 5 seconds, here we set
+ * the timeout to 10 seconds to ensure it changes.
+ *
+ * Note that migrate_ensure_non_converge set the max-bandwidth to 3MB/s,
+ * while the qtest mem is >= 100MB, one iteration takes at least 33s (100/3)
+ * to complete; this ensures that the RAMBlock dirty sync occurs.
+ */
+ max_try_count = 10;
+ while (--max_try_count) {
+ dirty_sync_cnt = get_migration_pass(from);
+ if (dirty_sync_cnt != prev_dirty_sync_cnt) {
+ hit = 1;
+ break;
+ }
+ prev_dirty_sync_cnt = dirty_sync_cnt;
+ sleep(1);
+ }
+ g_assert_cmpint(hit, ==, 1);
+
+ /* Now, when we tested that throttling works, let it converge */
+ migrate_ensure_converge(from);
+
+ /*
+ * Wait for pre-switchover status to check last throttle percentage
+ * and remaining. These values will be zeroed later
+ */
+ wait_for_migration_status(from, "pre-switchover", NULL);
+
+ /* The final percentage of throttling shouldn't be greater than max_pct */
+ percentage = read_migrate_property_int(from, "cpu-throttle-percentage");
+ g_assert_cmpint(percentage, <=, max_pct);
+ migrate_continue(from, "pre-switchover");
+
+ qtest_qmp_eventwait(to, "RESUME");
+
+ wait_for_serial("dest_serial");
+ wait_for_migration_complete(from);
+
+ migrate_end(from, to, true);
+}
+
+static void *
+migrate_hook_start_precopy_tcp_multifd(QTestState *from,
+ QTestState *to)
+{
+ return migrate_hook_start_precopy_tcp_multifd_common(from, to, "none");
+}
+
+static void *
+migrate_hook_start_precopy_tcp_multifd_zero_page_legacy(QTestState *from,
+ QTestState *to)
+{
+ migrate_hook_start_precopy_tcp_multifd_common(from, to, "none");
+ migrate_set_parameter_str(from, "zero-page-detection", "legacy");
+ return NULL;
+}
+
+static void *
+migrate_hook_start_precopy_tcp_multifd_no_zero_page(QTestState *from,
+ QTestState *to)
+{
+ migrate_hook_start_precopy_tcp_multifd_common(from, to, "none");
+ migrate_set_parameter_str(from, "zero-page-detection", "none");
+ return NULL;
+}
+
+static void test_multifd_tcp_uri_none(void)
+{
+ MigrateCommon args = {
+ .listen_uri = "defer",
+ .start_hook = migrate_hook_start_precopy_tcp_multifd,
+ .start = {
+ .caps[MIGRATION_CAPABILITY_MULTIFD] = true,
+ },
+ /*
+ * Multifd is more complicated than most of the features, it
+ * directly takes guest page buffers when sending, make sure
+ * everything will work alright even if guest page is changing.
+ */
+ .live = true,
+ };
+ test_precopy_common(&args);
+}
+
+static void test_multifd_tcp_zero_page_legacy(void)
+{
+ MigrateCommon args = {
+ .listen_uri = "defer",
+ .start_hook = migrate_hook_start_precopy_tcp_multifd_zero_page_legacy,
+ .start = {
+ .caps[MIGRATION_CAPABILITY_MULTIFD] = true,
+ },
+ /*
+ * Multifd is more complicated than most of the features, it
+ * directly takes guest page buffers when sending, make sure
+ * everything will work alright even if guest page is changing.
+ */
+ .live = true,
+ };
+ test_precopy_common(&args);
+}
+
+static void test_multifd_tcp_no_zero_page(void)
+{
+ MigrateCommon args = {
+ .listen_uri = "defer",
+ .start_hook = migrate_hook_start_precopy_tcp_multifd_no_zero_page,
+ .start = {
+ .caps[MIGRATION_CAPABILITY_MULTIFD] = true,
+ },
+ /*
+ * Multifd is more complicated than most of the features, it
+ * directly takes guest page buffers when sending, make sure
+ * everything will work alright even if guest page is changing.
+ */
+ .live = true,
+ };
+ test_precopy_common(&args);
+}
+
+static void test_multifd_tcp_channels_none(void)
+{
+ MigrateCommon args = {
+ .listen_uri = "defer",
+ .start_hook = migrate_hook_start_precopy_tcp_multifd,
+ .live = true,
+ .start = {
+ .caps[MIGRATION_CAPABILITY_MULTIFD] = true,
+ },
+ .connect_channels = ("[ { 'channel-type': 'main',"
+ " 'addr': { 'transport': 'socket',"
+ " 'type': 'inet',"
+ " 'host': '127.0.0.1',"
+ " 'port': '0' } } ]"),
+ };
+ test_precopy_common(&args);
+}
+
+/*
+ * This test does:
+ * source target
+ * migrate_incoming
+ * migrate
+ * migrate_cancel
+ * launch another target
+ * migrate
+ *
+ * And see that it works
+ */
+static void test_multifd_tcp_cancel(bool postcopy_ram)
+{
+ MigrateStart args = {
+ .hide_stderr = true,
+ };
+ QTestState *from, *to, *to2;
+
+ if (migrate_start(&from, &to, "defer", &args)) {
+ return;
+ }
+
+ migrate_ensure_non_converge(from);
+ migrate_prepare_for_dirty_mem(from);
+
+ if (postcopy_ram) {
+ migrate_set_capability(from, "postcopy-ram", true);
+ migrate_set_capability(to, "postcopy-ram", true);
+ }
+
+ migrate_set_parameter_int(from, "multifd-channels", 16);
+ migrate_set_parameter_int(to, "multifd-channels", 16);
+
+ migrate_set_capability(from, "multifd", true);
+ migrate_set_capability(to, "multifd", true);
+
+ /* Start incoming migration from the 1st socket */
+ migrate_incoming_qmp(to, "tcp:127.0.0.1:0", NULL, "{}");
+
+ /* Wait for the first serial output from the source */
+ wait_for_serial("src_serial");
+
+ migrate_qmp(from, to, NULL, NULL, "{}");
+
+ migrate_wait_for_dirty_mem(from, to);
+
+ migrate_cancel(from);
+
+ /* Make sure QEMU process "to" exited */
+ qtest_set_expected_status(to, EXIT_FAILURE);
+ qtest_wait_qemu(to);
+ qtest_quit(to);
+
+ /*
+ * Ensure the source QEMU finishes its cancellation process before we
+ * proceed with the setup of the next migration. The migrate_start()
+ * function and others might want to interact with the source in a way that
+ * is not possible while the migration is not canceled properly. For
+ * example, setting migration capabilities when the migration is still
+ * running leads to an error.
+ */
+ wait_for_migration_status(from, "cancelled", NULL);
+
+ args = (MigrateStart){
+ .only_target = true,
+ };
+
+ if (migrate_start(&from, &to2, "defer", &args)) {
+ return;
+ }
+
+ if (postcopy_ram) {
+ migrate_set_capability(to2, "postcopy-ram", true);
+ }
+
+ migrate_set_parameter_int(to2, "multifd-channels", 16);
+
+ migrate_set_capability(to2, "multifd", true);
+
+ /* Start incoming migration from the 1st socket */
+ migrate_incoming_qmp(to2, "tcp:127.0.0.1:0", NULL, "{}");
+
+ migrate_ensure_non_converge(from);
+
+ migrate_qmp(from, to2, NULL, NULL, "{}");
+
+ migrate_wait_for_dirty_mem(from, to2);
+
+ migrate_ensure_converge(from);
+
+ wait_for_stop(from, get_src());
+ qtest_qmp_eventwait(to2, "RESUME");
+
+ wait_for_serial("dest_serial");
+ wait_for_migration_complete(from);
+ migrate_end(from, to2, true);
+}
+
+static void test_multifd_precopy_tcp_cancel(void)
+{
+ test_multifd_tcp_cancel(false);
+}
+
+static void test_multifd_postcopy_tcp_cancel(void)
+{
+ test_multifd_tcp_cancel(true);
+}
+
+static void test_cancel_src_after_failed(QTestState *from, QTestState *to,
+ const char *uri, const char *phase)
+{
+ /*
+ * No migrate_incoming_qmp() at the start to force source into
+ * failed state during migrate_qmp().
+ */
+
+ wait_for_serial("src_serial");
+ migrate_ensure_converge(from);
+
+ migrate_qmp(from, to, uri, NULL, "{}");
+
+ migration_event_wait(from, phase);
+ migrate_cancel(from);
+
+ /* cancelling will not move the migration out of 'failed' */
+
+ wait_for_migration_status(from, "failed",
+ (const char * []) { "completed", NULL });
+
+ /*
+ * Not waiting for the destination because it never started
+ * migration.
+ */
+}
+
+static void test_cancel_src_after_cancelled(QTestState *from, QTestState *to,
+ const char *uri, const char *phase)
+{
+ migrate_incoming_qmp(to, uri, NULL, "{ 'exit-on-error': false }");
+
+ wait_for_serial("src_serial");
+ migrate_ensure_converge(from);
+
+ migrate_qmp(from, to, uri, NULL, "{}");
+
+ /* To move to cancelled/cancelling */
+ migrate_cancel(from);
+ migration_event_wait(from, phase);
+
+ /* The migrate_cancel under test */
+ migrate_cancel(from);
+
+ wait_for_migration_status(from, "cancelled",
+ (const char * []) { "completed", NULL });
+
+ wait_for_migration_status(to, "failed",
+ (const char * []) { "completed", NULL });
+}
+
+static void test_cancel_src_after_complete(QTestState *from, QTestState *to,
+ const char *uri, const char *phase)
+{
+ migrate_incoming_qmp(to, uri, NULL, "{ 'exit-on-error': false }");
+
+ wait_for_serial("src_serial");
+ migrate_ensure_converge(from);
+
+ migrate_qmp(from, to, uri, NULL, "{}");
+
+ migration_event_wait(from, phase);
+ migrate_cancel(from);
+
+ /*
+ * qmp_migrate_cancel() exits early if migration is not running
+ * anymore, the status will not change to cancelled.
+ */
+ wait_for_migration_complete(from);
+ wait_for_migration_complete(to);
+}
+
+static void test_cancel_src_after_none(QTestState *from, QTestState *to,
+ const char *uri, const char *phase)
+{
+ /*
+ * Test that cancelling without a migration happening does not
+ * affect subsequent migrations
+ */
+ migrate_cancel(to);
+
+ wait_for_serial("src_serial");
+ migrate_cancel(from);
+
+ migrate_incoming_qmp(to, uri, NULL, "{ 'exit-on-error': false }");
+
+ migrate_ensure_converge(from);
+ migrate_qmp(from, to, uri, NULL, "{}");
+
+ wait_for_migration_complete(from);
+ wait_for_migration_complete(to);
+}
+
+static void test_cancel_src_pre_switchover(QTestState *from, QTestState *to,
+ const char *uri, const char *phase)
+{
+ migrate_set_capability(from, "pause-before-switchover", true);
+ migrate_set_capability(to, "pause-before-switchover", true);
+
+ migrate_set_capability(from, "multifd", true);
+ migrate_set_capability(to, "multifd", true);
+
+ migrate_incoming_qmp(to, uri, NULL, "{ 'exit-on-error': false }");
+
+ wait_for_serial("src_serial");
+ migrate_ensure_converge(from);
+
+ migrate_qmp(from, to, uri, NULL, "{}");
+
+ migration_event_wait(from, phase);
+ migrate_cancel(from);
+ migration_event_wait(from, "cancelling");
+
+ wait_for_migration_status(from, "cancelled",
+ (const char * []) { "completed", NULL });
+
+ wait_for_migration_status(to, "failed",
+ (const char * []) { "completed", NULL });
+}
+
+static void test_cancel_src_after_status(void *opaque)
+{
+ const char *test_path = opaque;
+ g_autofree char *phase = g_path_get_basename(test_path);
+ g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs);
+ QTestState *from, *to;
+ MigrateStart args = {
+ .hide_stderr = true,
+ };
+
+ if (migrate_start(&from, &to, "defer", &args)) {
+ return;
+ }
+
+ if (g_str_equal(phase, "cancelling") ||
+ g_str_equal(phase, "cancelled")) {
+ test_cancel_src_after_cancelled(from, to, uri, phase);
+
+ } else if (g_str_equal(phase, "completed")) {
+ test_cancel_src_after_complete(from, to, uri, phase);
+
+ } else if (g_str_equal(phase, "failed")) {
+ test_cancel_src_after_failed(from, to, uri, phase);
+
+ } else if (g_str_equal(phase, "none")) {
+ test_cancel_src_after_none(from, to, uri, phase);
+
+ } else {
+ /* any state that comes before pre-switchover */
+ test_cancel_src_pre_switchover(from, to, uri, phase);
+ }
+
+ migrate_end(from, to, false);
+}
+
+static void calc_dirty_rate(QTestState *who, uint64_t calc_time)
+{
+ qtest_qmp_assert_success(who,
+ "{ 'execute': 'calc-dirty-rate',"
+ "'arguments': { "
+ "'calc-time': %" PRIu64 ","
+ "'mode': 'dirty-ring' }}",
+ calc_time);
+}
+
+static QDict *query_dirty_rate(QTestState *who)
+{
+ return qtest_qmp_assert_success_ref(who,
+ "{ 'execute': 'query-dirty-rate' }");
+}
+
+static void dirtylimit_set_all(QTestState *who, uint64_t dirtyrate)
+{
+ qtest_qmp_assert_success(who,
+ "{ 'execute': 'set-vcpu-dirty-limit',"
+ "'arguments': { "
+ "'dirty-rate': %" PRIu64 " } }",
+ dirtyrate);
+}
+
+static void cancel_vcpu_dirty_limit(QTestState *who)
+{
+ qtest_qmp_assert_success(who,
+ "{ 'execute': 'cancel-vcpu-dirty-limit' }");
+}
+
+static QDict *query_vcpu_dirty_limit(QTestState *who)
+{
+ QDict *rsp;
+
+ rsp = qtest_qmp(who, "{ 'execute': 'query-vcpu-dirty-limit' }");
+ g_assert(!qdict_haskey(rsp, "error"));
+ g_assert(qdict_haskey(rsp, "return"));
+
+ return rsp;
+}
+
+static bool calc_dirtyrate_ready(QTestState *who)
+{
+ QDict *rsp_return;
+ const char *status;
+ bool ready;
+
+ rsp_return = query_dirty_rate(who);
+ g_assert(rsp_return);
+
+ status = qdict_get_str(rsp_return, "status");
+ g_assert(status);
+ ready = g_strcmp0(status, "measuring");
+ qobject_unref(rsp_return);
+
+ return ready;
+}
+
+static void wait_for_calc_dirtyrate_complete(QTestState *who,
+ int64_t time_s)
+{
+ int max_try_count = 10000;
+ usleep(time_s * 1000000);
+
+ while (!calc_dirtyrate_ready(who) && max_try_count--) {
+ usleep(1000);
+ }
+
+ /*
+ * Set the timeout with 10 s(max_try_count * 1000us),
+ * if dirtyrate measurement not complete, fail test.
+ */
+ g_assert_cmpint(max_try_count, !=, 0);
+}
+
+static int64_t get_dirty_rate(QTestState *who)
+{
+ QDict *rsp_return;
+ const char *status;
+ QList *rates;
+ const QListEntry *entry;
+ QDict *rate;
+ int64_t dirtyrate;
+
+ rsp_return = query_dirty_rate(who);
+ g_assert(rsp_return);
+
+ status = qdict_get_str(rsp_return, "status");
+ g_assert(status);
+ g_assert_cmpstr(status, ==, "measured");
+
+ rates = qdict_get_qlist(rsp_return, "vcpu-dirty-rate");
+ g_assert(rates && !qlist_empty(rates));
+
+ entry = qlist_first(rates);
+ g_assert(entry);
+
+ rate = qobject_to(QDict, qlist_entry_obj(entry));
+ g_assert(rate);
+
+ dirtyrate = qdict_get_try_int(rate, "dirty-rate", -1);
+
+ qobject_unref(rsp_return);
+ return dirtyrate;
+}
+
+static int64_t get_limit_rate(QTestState *who)
+{
+ QDict *rsp_return;
+ QList *rates;
+ const QListEntry *entry;
+ QDict *rate;
+ int64_t dirtyrate;
+
+ rsp_return = query_vcpu_dirty_limit(who);
+ g_assert(rsp_return);
+
+ rates = qdict_get_qlist(rsp_return, "return");
+ g_assert(rates && !qlist_empty(rates));
+
+ entry = qlist_first(rates);
+ g_assert(entry);
+
+ rate = qobject_to(QDict, qlist_entry_obj(entry));
+ g_assert(rate);
+
+ dirtyrate = qdict_get_try_int(rate, "limit-rate", -1);
+
+ qobject_unref(rsp_return);
+ return dirtyrate;
+}
+
+static QTestState *dirtylimit_start_vm(void)
+{
+ QTestState *vm = NULL;
+ g_autofree gchar *cmd = NULL;
+ const char *bootpath;
+
+ bootpath = bootfile_create(qtest_get_arch(), tmpfs, false);
+ cmd = g_strdup_printf("-accel kvm,dirty-ring-size=4096 "
+ "-name dirtylimit-test,debug-threads=on "
+ "-m 150M -smp 1 "
+ "-serial file:%s/vm_serial "
+ "-drive file=%s,format=raw ",
+ tmpfs, bootpath);
+
+ vm = qtest_init(cmd);
+ return vm;
+}
+
+static void dirtylimit_stop_vm(QTestState *vm)
+{
+ g_autofree char *path = g_strdup_printf("%s/%s", tmpfs, "vm_serial");
+
+ qtest_quit(vm);
+ unlink(path);
+}
+
+static void test_vcpu_dirty_limit(void)
+{
+ QTestState *vm;
+ int64_t origin_rate;
+ int64_t quota_rate;
+ int64_t rate ;
+ int max_try_count = 20;
+ int hit = 0;
+
+ /* Start vm for vcpu dirtylimit test */
+ vm = dirtylimit_start_vm();
+
+ /* Wait for the first serial output from the vm*/
+ wait_for_serial("vm_serial");
+
+ /* Do dirtyrate measurement with calc time equals 1s */
+ calc_dirty_rate(vm, 1);
+
+ /* Sleep calc time and wait for calc dirtyrate complete */
+ wait_for_calc_dirtyrate_complete(vm, 1);
+
+ /* Query original dirty page rate */
+ origin_rate = get_dirty_rate(vm);
+
+ /* VM booted from bootsect should dirty memory steadily */
+ assert(origin_rate != 0);
+
+ /* Setup quota dirty page rate at half of origin */
+ quota_rate = origin_rate / 2;
+
+ /* Set dirtylimit */
+ dirtylimit_set_all(vm, quota_rate);
+
+ /*
+ * Check if set-vcpu-dirty-limit and query-vcpu-dirty-limit
+ * works literally
+ */
+ g_assert_cmpint(quota_rate, ==, get_limit_rate(vm));
+
+ /* Sleep a bit to check if it take effect */
+ usleep(2000000);
+
+ /*
+ * Check if dirtylimit take effect realistically, set the
+ * timeout with 20 s(max_try_count * 1s), if dirtylimit
+ * doesn't take effect, fail test.
+ */
+ while (--max_try_count) {
+ calc_dirty_rate(vm, 1);
+ wait_for_calc_dirtyrate_complete(vm, 1);
+ rate = get_dirty_rate(vm);
+
+ /*
+ * Assume hitting if current rate is less
+ * than quota rate (within accepting error)
+ */
+ if (rate < (quota_rate + DIRTYLIMIT_TOLERANCE_RANGE)) {
+ hit = 1;
+ break;
+ }
+ }
+
+ g_assert_cmpint(hit, ==, 1);
+
+ hit = 0;
+ max_try_count = 20;
+
+ /* Check if dirtylimit cancellation take effect */
+ cancel_vcpu_dirty_limit(vm);
+ while (--max_try_count) {
+ calc_dirty_rate(vm, 1);
+ wait_for_calc_dirtyrate_complete(vm, 1);
+ rate = get_dirty_rate(vm);
+
+ /*
+ * Assume dirtylimit be canceled if current rate is
+ * greater than quota rate (within accepting error)
+ */
+ if (rate > (quota_rate + DIRTYLIMIT_TOLERANCE_RANGE)) {
+ hit = 1;
+ break;
+ }
+ }
+
+ g_assert_cmpint(hit, ==, 1);
+ dirtylimit_stop_vm(vm);
+}
+
+static void migrate_dirty_limit_wait_showup(QTestState *from,
+ const int64_t period,
+ const int64_t value)
+{
+ /* Enable dirty limit capability */
+ migrate_set_capability(from, "dirty-limit", true);
+
+ /* Set dirty limit parameters */
+ migrate_set_parameter_int(from, "x-vcpu-dirty-limit-period", period);
+ migrate_set_parameter_int(from, "vcpu-dirty-limit", value);
+
+ /* Make sure migrate can't converge */
+ migrate_ensure_non_converge(from);
+
+ /* To check limit rate after precopy */
+ migrate_set_capability(from, "pause-before-switchover", true);
+
+ /* Wait for the serial output from the source */
+ wait_for_serial("src_serial");
+}
+
+/*
+ * This test does:
+ * source destination
+ * start vm
+ * start incoming vm
+ * migrate
+ * wait dirty limit to begin
+ * cancel migrate
+ * cancellation check
+ * restart incoming vm
+ * migrate
+ * wait dirty limit to begin
+ * wait pre-switchover event
+ * convergence condition check
+ *
+ * And see if dirty limit migration works correctly.
+ * This test case involves many passes, so it runs in slow mode only.
+ */
+static void test_dirty_limit(void)
+{
+ g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs);
+ QTestState *from, *to;
+ int64_t remaining;
+ uint64_t throttle_us_per_full;
+ /*
+ * We want the test to be stable and as fast as possible.
+ * E.g., with 1Gb/s bandwidth migration may pass without dirty limit,
+ * so we need to decrease a bandwidth.
+ */
+ const int64_t dirtylimit_period = 1000, dirtylimit_value = 50;
+ const int64_t max_bandwidth = 400000000; /* ~400Mb/s */
+ const int64_t downtime_limit = 250; /* 250ms */
+ /*
+ * We migrate through unix-socket (> 500Mb/s).
+ * Thus, expected migration speed ~= bandwidth limit (< 500Mb/s).
+ * So, we can predict expected_threshold
+ */
+ const int64_t expected_threshold = max_bandwidth * downtime_limit / 1000;
+ int max_try_count = 10;
+ MigrateCommon args = {
+ .start = {
+ .hide_stderr = true,
+ .use_dirty_ring = true,
+ },
+ .listen_uri = uri,
+ .connect_uri = uri,
+ };
+
+ /* Start src, dst vm */
+ if (migrate_start(&from, &to, args.listen_uri, &args.start)) {
+ return;
+ }
+
+ /* Prepare for dirty limit migration and wait src vm show up */
+ migrate_dirty_limit_wait_showup(from, dirtylimit_period, dirtylimit_value);
+
+ /* Start migrate */
+ migrate_qmp(from, to, args.connect_uri, NULL, "{}");
+
+ /* Wait for dirty limit throttle begin */
+ throttle_us_per_full = 0;
+ while (throttle_us_per_full == 0) {
+ throttle_us_per_full =
+ read_migrate_property_int(from,
+ "dirty-limit-throttle-time-per-round");
+ usleep(100);
+ g_assert_false(get_src()->stop_seen);
+ }
+
+ /* Now cancel migrate and wait for dirty limit throttle switch off */
+ migrate_cancel(from);
+ wait_for_migration_status(from, "cancelled", NULL);
+
+ /* destination always fails after cancel */
+ migration_event_wait(to, "failed");
+ qtest_set_expected_status(to, EXIT_FAILURE);
+ qtest_quit(to);
+
+ /* Check if dirty limit throttle switched off, set timeout 1ms */
+ do {
+ throttle_us_per_full =
+ read_migrate_property_int(from,
+ "dirty-limit-throttle-time-per-round");
+ usleep(100);
+ g_assert_false(get_src()->stop_seen);
+ } while (throttle_us_per_full != 0 && --max_try_count);
+
+ /* Assert dirty limit is not in service */
+ g_assert_cmpint(throttle_us_per_full, ==, 0);
+
+ args = (MigrateCommon) {
+ .start = {
+ .only_target = true,
+ .use_dirty_ring = true,
+ },
+ .listen_uri = uri,
+ .connect_uri = uri,
+ };
+
+ /* Restart dst vm, src vm already show up so we needn't wait anymore */
+ if (migrate_start(&from, &to, args.listen_uri, &args.start)) {
+ return;
+ }
+
+ /* Start migrate */
+ migrate_qmp(from, to, args.connect_uri, NULL, "{}");
+
+ /* Wait for dirty limit throttle begin */
+ throttle_us_per_full = 0;
+ while (throttle_us_per_full == 0) {
+ throttle_us_per_full =
+ read_migrate_property_int(from,
+ "dirty-limit-throttle-time-per-round");
+ usleep(100);
+ g_assert_false(get_src()->stop_seen);
+ }
+
+ /*
+ * The dirty limit rate should equals the return value of
+ * query-vcpu-dirty-limit if dirty limit cap set
+ */
+ g_assert_cmpint(dirtylimit_value, ==, get_limit_rate(from));
+
+ /* Now, we have tested if dirty limit works, let it converge */
+ migrate_set_parameter_int(from, "downtime-limit", downtime_limit);
+ migrate_set_parameter_int(from, "max-bandwidth", max_bandwidth);
+
+ /*
+ * Wait for pre-switchover status to check if migration
+ * satisfy the convergence condition
+ */
+ wait_for_migration_status(from, "pre-switchover", NULL);
+
+ remaining = read_ram_property_int(from, "remaining");
+ g_assert_cmpint(remaining, <,
+ (expected_threshold + expected_threshold / 100));
+
+ migrate_continue(from, "pre-switchover");
+
+ qtest_qmp_eventwait(to, "RESUME");
+
+ wait_for_serial("dest_serial");
+ wait_for_migration_complete(from);
+
+ migrate_end(from, to, true);
+}
+
+static void migration_test_add_precopy_smoke(MigrationTestEnv *env)
+{
+ if (env->is_x86) {
+ migration_test_add("/migration/precopy/unix/suspend/live",
+ test_precopy_unix_suspend_live);
+ migration_test_add("/migration/precopy/unix/suspend/notlive",
+ test_precopy_unix_suspend_notlive);
+ }
+
+ migration_test_add("/migration/precopy/unix/plain",
+ test_precopy_unix_plain);
+
+ migration_test_add("/migration/precopy/tcp/plain", test_precopy_tcp_plain);
+ migration_test_add("/migration/multifd/tcp/uri/plain/none",
+ test_multifd_tcp_uri_none);
+ migration_test_add("/migration/multifd/tcp/plain/cancel",
+ test_multifd_precopy_tcp_cancel);
+ if (env->has_uffd) {
+ migration_test_add("/migration/multifd+postcopy/tcp/plain/cancel",
+ test_multifd_postcopy_tcp_cancel);
+ }
+
+#ifdef CONFIG_RDMA
+ migration_test_add("/migration/precopy/rdma/plain",
+ test_precopy_rdma_plain);
+ migration_test_add("/migration/precopy/rdma/plain/ipv6",
+ test_precopy_rdma_plain_ipv6);
+#endif
+}
+
+void migration_test_add_precopy(MigrationTestEnv *env)
+{
+ tmpfs = env->tmpfs;
+
+ migration_test_add_precopy_smoke(env);
+
+ if (!env->full_set) {
+ return;
+ }
+
+ migration_test_add("/migration/precopy/tcp/plain/switchover-ack",
+ test_precopy_tcp_switchover_ack);
+
+#ifndef _WIN32
+ migration_test_add("/migration/precopy/fd/tcp",
+ test_precopy_fd_socket);
+ migration_test_add("/migration/precopy/fd/file",
+ test_precopy_fd_file);
+#endif
+
+ /*
+ * See explanation why this test is slow on function definition
+ */
+ if (g_test_slow()) {
+ migration_test_add("/migration/auto_converge",
+ test_auto_converge);
+ if (g_str_equal(env->arch, "x86_64") &&
+ env->has_kvm && env->has_dirty_ring) {
+ migration_test_add("/dirty_limit",
+ test_dirty_limit);
+ }
+ }
+ migration_test_add("/migration/multifd/tcp/channels/plain/none",
+ test_multifd_tcp_channels_none);
+ migration_test_add("/migration/multifd/tcp/plain/zero-page/legacy",
+ test_multifd_tcp_zero_page_legacy);
+ migration_test_add("/migration/multifd/tcp/plain/zero-page/none",
+ test_multifd_tcp_no_zero_page);
+ if (g_str_equal(env->arch, "x86_64")
+ && env->has_kvm && env->has_dirty_ring) {
+
+ migration_test_add("/migration/dirty_ring",
+ test_precopy_unix_dirty_ring);
+ if (qtest_has_machine("pc") && g_test_slow()) {
+ migration_test_add("/migration/vcpu_dirty_limit",
+ test_vcpu_dirty_limit);
+ }
+ }
+
+ /* ensure new status don't go unnoticed */
+ assert(MIGRATION_STATUS__MAX == 15);
+
+ for (int i = MIGRATION_STATUS_NONE; i < MIGRATION_STATUS__MAX; i++) {
+ switch (i) {
+ case MIGRATION_STATUS_DEVICE: /* happens too fast */
+ case MIGRATION_STATUS_WAIT_UNPLUG: /* no support in tests */
+ case MIGRATION_STATUS_COLO: /* no support in tests */
+ case MIGRATION_STATUS_POSTCOPY_ACTIVE: /* postcopy can't be cancelled */
+ case MIGRATION_STATUS_POSTCOPY_PAUSED:
+ case MIGRATION_STATUS_POSTCOPY_RECOVER_SETUP:
+ case MIGRATION_STATUS_POSTCOPY_RECOVER:
+ continue;
+ default:
+ migration_test_add_suffix("/migration/cancel/src/after/",
+ MigrationStatus_str(i),
+ test_cancel_src_after_status);
+ }
+ }
+}
diff --git a/tests/qtest/migration/s390x/Makefile b/tests/qtest/migration/s390x/Makefile
new file mode 100644
index 0000000..6671de2
--- /dev/null
+++ b/tests/qtest/migration/s390x/Makefile
@@ -0,0 +1,24 @@
+# To specify cross compiler prefix, use CROSS_PREFIX=
+# $ make CROSS_PREFIX=s390x-linux-gnu-
+
+.PHONY: all clean
+all: a-b-bios.h
+fwdir=../../../pc-bios/s390-ccw
+
+CFLAGS+=-ffreestanding -fno-delete-null-pointer-checks -fPIE -Os \
+ -msoft-float -march=z900 -fno-asynchronous-unwind-tables \
+ -fno-stack-protector -Wl,-pie -Wl,--build-id=none -nostdlib
+
+a-b-bios.h: s390x.elf
+ echo "$$__note" > header.tmp
+ xxd -i $< | sed -e 's/.*int.*//' >> header.tmp
+ mv header.tmp $@
+
+# We use common-page-size=16 to avoid big padding in the ELF file
+s390x.elf: a-b-bios.c
+ $(CROSS_PREFIX)gcc $(CFLAGS) -I$(fwdir) $(fwdir)/start.S \
+ $(fwdir)/sclp.c -Wl,-zcommon-page-size=16 -o $@ $<
+ $(CROSS_PREFIX)strip $@
+
+clean:
+ @rm -rf *.elf *.o
diff --git a/tests/qtest/migration/s390x/a-b-bios.c b/tests/qtest/migration/s390x/a-b-bios.c
new file mode 100644
index 0000000..ff99a3e
--- /dev/null
+++ b/tests/qtest/migration/s390x/a-b-bios.c
@@ -0,0 +1,44 @@
+/*
+ * S390 guest code used in migration tests
+ *
+ * Copyright 2018 Thomas Huth, Red Hat Inc.
+ *
+ * This code 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.
+ */
+
+#define LOADPARM_LEN 8 /* Needed for sclp.h */
+
+#include <libc.h>
+#include <s390-ccw.h>
+#include <sclp.h>
+
+char stack[0x8000] __attribute__((aligned(4096)));
+
+#define START_ADDRESS (1024 * 1024)
+#define END_ADDRESS (100 * 1024 * 1024)
+
+void main(void)
+{
+ unsigned long addr;
+
+ sclp_setup();
+ sclp_print("A");
+
+ /*
+ * Make sure all of the pages have consistent contents before incrementing
+ * the first byte below.
+ */
+ for (addr = START_ADDRESS; addr < END_ADDRESS; addr += 4096) {
+ *(volatile char *)addr = 0;
+ }
+
+ while (1) {
+ for (addr = START_ADDRESS; addr < END_ADDRESS; addr += 4096) {
+ *(volatile char *)addr += 1; /* Change pages */
+ }
+ sclp_print("B");
+ }
+}
diff --git a/tests/qtest/migration/s390x/a-b-bios.h b/tests/qtest/migration/s390x/a-b-bios.h
new file mode 100644
index 0000000..96103da
--- /dev/null
+++ b/tests/qtest/migration/s390x/a-b-bios.h
@@ -0,0 +1,279 @@
+/* This file is automatically generated from the a-b-bios.c file in
+ * tests/migration/s390x. Edit that file and then run "make all"
+ * inside tests/migration to update, and then remember to send both
+ * the header and the assembler differences in your patch submission.
+ */
+unsigned char s390x_elf[] = {
+ 0x7f, 0x45, 0x4c, 0x46, 0x02, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x16, 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xa8, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x80,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x38, 0x00, 0x07, 0x00, 0x40,
+ 0x00, 0x0d, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x88, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x01, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08,
+ 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x01, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc8, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xac,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xac, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xb8, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x17, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0xb8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x38, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x01, 0x18, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
+ 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x07, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0xb8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0xb8, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x01, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x20,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x64, 0x74, 0xe5, 0x51,
+ 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x10, 0x64, 0x74, 0xe5, 0x52, 0x00, 0x00, 0x00, 0x04,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xb8, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x17, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0xb8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x38, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x01, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x2f, 0x6c, 0x69, 0x62, 0x2f, 0x6c, 0x64, 0x36, 0x34, 0x2e, 0x73, 0x6f,
+ 0x2e, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x02, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x03, 0xa8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0xf0, 0xeb, 0xef, 0xf0, 0x70,
+ 0x00, 0x24, 0xa7, 0xfb, 0xff, 0x60, 0xc0, 0xe5, 0x00, 0x00, 0x01, 0x5f,
+ 0xc0, 0x20, 0x00, 0x00, 0x02, 0xa8, 0xc0, 0xe5, 0x00, 0x00, 0x01, 0x75,
+ 0xa5, 0x2e, 0x00, 0x10, 0xa7, 0x19, 0x63, 0x00, 0x92, 0x00, 0x20, 0x00,
+ 0xa7, 0x2b, 0x10, 0x00, 0xa7, 0x17, 0xff, 0xfc, 0xa5, 0x1e, 0x00, 0x10,
+ 0xa7, 0x29, 0x63, 0x00, 0xe3, 0x30, 0x10, 0x00, 0x00, 0x90, 0xa7, 0x3a,
+ 0x00, 0x01, 0x42, 0x30, 0x10, 0x00, 0xa7, 0x1b, 0x10, 0x00, 0xa7, 0x27,
+ 0xff, 0xf7, 0xc0, 0x20, 0x00, 0x00, 0x02, 0x8a, 0xc0, 0xe5, 0x00, 0x00,
+ 0x01, 0x56, 0xa7, 0xf4, 0xff, 0xeb, 0x07, 0x07, 0xc0, 0xf0, 0x00, 0x00,
+ 0x4e, 0x5c, 0xc0, 0x20, 0x00, 0x00, 0x00, 0x7d, 0xe3, 0x20, 0x20, 0x00,
+ 0x00, 0x04, 0xc0, 0x30, 0x00, 0x00, 0x96, 0xa3, 0xb9, 0x0b, 0x00, 0x32,
+ 0xb9, 0x02, 0x00, 0x33, 0xa7, 0x84, 0x00, 0x19, 0xa7, 0x3b, 0xff, 0xff,
+ 0xeb, 0x43, 0x00, 0x08, 0x00, 0x0c, 0xb9, 0x02, 0x00, 0x44, 0xb9, 0x04,
+ 0x00, 0x12, 0xa7, 0x84, 0x00, 0x09, 0xd7, 0xff, 0x10, 0x00, 0x10, 0x00,
+ 0x41, 0x10, 0x11, 0x00, 0xa7, 0x47, 0xff, 0xfb, 0xc0, 0x20, 0x00, 0x00,
+ 0x00, 0x0d, 0x44, 0x30, 0x20, 0x00, 0xc0, 0x20, 0x00, 0x00, 0x00, 0x5b,
+ 0xd2, 0x0f, 0x01, 0xd0, 0x20, 0x00, 0xa7, 0xf4, 0xff, 0xa1, 0xd7, 0x00,
+ 0x10, 0x00, 0x10, 0x00, 0xc0, 0x10, 0x00, 0x00, 0x00, 0x50, 0xb2, 0xb2,
+ 0x10, 0x00, 0xa7, 0xf4, 0x00, 0x00, 0xeb, 0x00, 0xf0, 0x00, 0x00, 0x25,
+ 0x96, 0x02, 0xf0, 0x06, 0xeb, 0x00, 0xf0, 0x00, 0x00, 0x2f, 0xc0, 0x10,
+ 0x00, 0x00, 0x00, 0x2a, 0xe3, 0x10, 0x01, 0xb8, 0x00, 0x24, 0xc0, 0x10,
+ 0x00, 0x00, 0x00, 0x4b, 0xd2, 0x07, 0x01, 0xb0, 0x10, 0x00, 0xc0, 0x10,
+ 0x00, 0x00, 0x00, 0x3d, 0xb2, 0xb2, 0x10, 0x00, 0xeb, 0x66, 0xf0, 0x00,
+ 0x00, 0x25, 0x96, 0xff, 0xf0, 0x04, 0xeb, 0x66, 0xf0, 0x00, 0x00, 0x2f,
+ 0xc0, 0x10, 0x00, 0x00, 0x00, 0x1a, 0xe3, 0x10, 0x01, 0xf8, 0x00, 0x24,
+ 0xc0, 0x10, 0x00, 0x00, 0x00, 0x36, 0xd2, 0x07, 0x01, 0xf0, 0x10, 0x00,
+ 0xc0, 0x10, 0x00, 0x00, 0x00, 0x24, 0xb2, 0xb2, 0x10, 0x00, 0xeb, 0x00,
+ 0xf0, 0x00, 0x00, 0x25, 0x94, 0xfd, 0xf0, 0x06, 0xeb, 0x00, 0xf0, 0x00,
+ 0x00, 0x2f, 0x07, 0xfe, 0xeb, 0x66, 0xf0, 0x00, 0x00, 0x25, 0x94, 0x00,
+ 0xf0, 0x04, 0xeb, 0x66, 0xf0, 0x00, 0x00, 0x2f, 0x07, 0xfe, 0x07, 0x07,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0xf0, 0x00, 0x02, 0x00, 0x01,
+ 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x03, 0x02, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0xeb, 0xbf, 0xf0, 0x58,
+ 0x00, 0x24, 0xc0, 0x10, 0x00, 0x00, 0x4e, 0x0d, 0xa7, 0xfb, 0xff, 0x60,
+ 0xb2, 0x20, 0x00, 0x21, 0xb2, 0x22, 0x00, 0xb0, 0x88, 0xb0, 0x00, 0x1c,
+ 0xc0, 0xe5, 0xff, 0xff, 0xff, 0x91, 0xa7, 0xbe, 0x00, 0x03, 0xa7, 0x84,
+ 0x00, 0x13, 0xa7, 0xbe, 0x00, 0x02, 0xa7, 0x28, 0x00, 0x00, 0xa7, 0x74,
+ 0x00, 0x04, 0xa7, 0x28, 0xff, 0xfe, 0xe3, 0x40, 0xf1, 0x10, 0x00, 0x04,
+ 0xb9, 0x14, 0x00, 0x22, 0xeb, 0xbf, 0xf0, 0xf8, 0x00, 0x04, 0x07, 0xf4,
+ 0xa7, 0x28, 0xff, 0xff, 0xa7, 0xf4, 0xff, 0xf5, 0x07, 0x07, 0x07, 0x07,
+ 0xeb, 0xbf, 0xf0, 0x58, 0x00, 0x24, 0xc0, 0xd0, 0x00, 0x00, 0x01, 0x25,
+ 0xa7, 0xfb, 0xff, 0x60, 0xa7, 0xb9, 0x00, 0x00, 0xa7, 0x19, 0x00, 0x00,
+ 0xc0, 0x40, 0x00, 0x00, 0x4d, 0xd8, 0xa7, 0x3b, 0x00, 0x01, 0xa7, 0x37,
+ 0x00, 0x23, 0xc0, 0x20, 0x00, 0x00, 0x4d, 0xd1, 0x18, 0x31, 0xa7, 0x1a,
+ 0x00, 0x06, 0x40, 0x10, 0x20, 0x08, 0xa7, 0x3a, 0x00, 0x0e, 0xa7, 0x18,
+ 0x1a, 0x00, 0x40, 0x30, 0x20, 0x00, 0x92, 0x00, 0x20, 0x02, 0x40, 0x10,
+ 0x20, 0x0a, 0xe3, 0x20, 0xd0, 0x00, 0x00, 0x04, 0xc0, 0xe5, 0xff, 0xff,
+ 0xff, 0xac, 0xe3, 0x40, 0xf1, 0x10, 0x00, 0x04, 0xb9, 0x04, 0x00, 0x2b,
+ 0xeb, 0xbf, 0xf0, 0xf8, 0x00, 0x04, 0x07, 0xf4, 0xb9, 0x04, 0x00, 0x51,
+ 0xa7, 0x5b, 0x00, 0x01, 0xa7, 0x09, 0x0f, 0xf7, 0xb9, 0x21, 0x00, 0x50,
+ 0xa7, 0x24, 0xff, 0xd7, 0x41, 0xeb, 0x20, 0x00, 0x95, 0x0a, 0xe0, 0x00,
+ 0xa7, 0x74, 0x00, 0x08, 0x41, 0x11, 0x40, 0x0e, 0x92, 0x0d, 0x10, 0x00,
+ 0xb9, 0x04, 0x00, 0x15, 0x43, 0x5b, 0x20, 0x00, 0x42, 0x51, 0x40, 0x0e,
+ 0xa7, 0xbb, 0x00, 0x01, 0x41, 0x10, 0x10, 0x01, 0xa7, 0xf4, 0xff, 0xbf,
+ 0xc0, 0x50, 0x00, 0x00, 0x00, 0xd8, 0xc0, 0x10, 0x00, 0x00, 0x4d, 0x8d,
+ 0xa7, 0x48, 0x00, 0x1c, 0x40, 0x40, 0x10, 0x00, 0x50, 0x20, 0x10, 0x0c,
+ 0xa7, 0x48, 0x00, 0x04, 0xe3, 0x20, 0x50, 0x00, 0x00, 0x04, 0x40, 0x40,
+ 0x10, 0x0a, 0x50, 0x30, 0x10, 0x10, 0xc0, 0xf4, 0xff, 0xff, 0xff, 0x6b,
+ 0xa7, 0x39, 0x00, 0x40, 0xa7, 0x29, 0x00, 0x00, 0xc0, 0xf4, 0xff, 0xff,
+ 0xff, 0xe4, 0x07, 0x07, 0xb9, 0x04, 0x00, 0x13, 0xa7, 0x2a, 0xff, 0xff,
+ 0xb9, 0x04, 0x00, 0x34, 0xa7, 0x48, 0x00, 0x01, 0x15, 0x24, 0xa7, 0x24,
+ 0x00, 0x07, 0xb9, 0x04, 0x00, 0x21, 0xc0, 0xf4, 0xff, 0xff, 0xff, 0x7f,
+ 0xa7, 0x29, 0xff, 0xff, 0x07, 0xfe, 0x07, 0x07, 0xa7, 0x39, 0x00, 0x00,
+ 0x41, 0x13, 0x20, 0x00, 0x95, 0x00, 0x10, 0x00, 0xa7, 0x74, 0x00, 0x05,
+ 0xc0, 0xf4, 0xff, 0xff, 0xff, 0x70, 0xa7, 0x3b, 0x00, 0x01, 0xa7, 0xf4,
+ 0xff, 0xf5, 0x07, 0x07, 0xeb, 0xbf, 0xf0, 0x58, 0x00, 0x24, 0xc0, 0xd0,
+ 0x00, 0x00, 0x00, 0x95, 0xa7, 0xfb, 0xff, 0x60, 0xb9, 0x04, 0x00, 0xb2,
+ 0xa7, 0x19, 0x00, 0x20, 0xc0, 0x20, 0x00, 0x00, 0x4d, 0x40, 0x92, 0x00,
+ 0x20, 0x00, 0xa7, 0x2b, 0x00, 0x01, 0xa7, 0x17, 0xff, 0xfc, 0xc0, 0x10,
+ 0x00, 0x00, 0x4d, 0x37, 0xa7, 0x28, 0x10, 0x00, 0x40, 0x20, 0x10, 0x00,
+ 0xe3, 0x20, 0xd0, 0x00, 0x00, 0x04, 0xc0, 0xe5, 0xff, 0xff, 0xff, 0x1d,
+ 0x12, 0x22, 0xa7, 0x74, 0x00, 0x19, 0xa7, 0x19, 0x00, 0x00, 0xc0, 0x40,
+ 0x00, 0x00, 0x00, 0x79, 0xa7, 0x39, 0x00, 0x08, 0xc0, 0x20, 0x00, 0x00,
+ 0x4d, 0x2c, 0x41, 0x21, 0x20, 0x00, 0xe3, 0x20, 0x20, 0x00, 0x00, 0x90,
+ 0x43, 0x22, 0x40, 0x00, 0x42, 0x21, 0xb0, 0x00, 0xa7, 0x1b, 0x00, 0x01,
+ 0xa7, 0x37, 0xff, 0xf2, 0xe3, 0x40, 0xf1, 0x10, 0x00, 0x04, 0xeb, 0xbf,
+ 0xf0, 0xf8, 0x00, 0x04, 0x07, 0xf4, 0x07, 0x07, 0xeb, 0xaf, 0xf0, 0x50,
+ 0x00, 0x24, 0xc0, 0xd0, 0x00, 0x00, 0x00, 0x55, 0xa7, 0xfb, 0xff, 0x60,
+ 0xa7, 0x19, 0x0f, 0xf8, 0xb9, 0x21, 0x00, 0x31, 0xb9, 0x04, 0x00, 0xa2,
+ 0xa7, 0xc4, 0x00, 0x2a, 0xa7, 0xb9, 0x0f, 0xf8, 0xc0, 0x10, 0x00, 0x00,
+ 0x4c, 0xf6, 0xa7, 0x28, 0x10, 0x00, 0x40, 0x20, 0x10, 0x00, 0x92, 0x00,
+ 0x10, 0x02, 0xe3, 0x20, 0xd0, 0x00, 0x00, 0x04, 0xc0, 0xe5, 0xff, 0xff,
+ 0xfe, 0xda, 0xa7, 0xbb, 0x00, 0x01, 0xa7, 0x19, 0x00, 0x00, 0xa7, 0xb7,
+ 0x00, 0x17, 0xc0, 0x10, 0x00, 0x00, 0x4c, 0xe1, 0xe3, 0x40, 0xf1, 0x10,
+ 0x00, 0x04, 0xe3, 0x20, 0x10, 0x08, 0x00, 0x91, 0xa7, 0x2a, 0xff, 0xf9,
+ 0xb9, 0x14, 0x00, 0x22, 0xeb, 0xaf, 0xf0, 0xf0, 0x00, 0x04, 0x07, 0xf4,
+ 0xb9, 0x04, 0x00, 0xb3, 0xa7, 0xf4, 0xff, 0xd8, 0xc0, 0x20, 0x00, 0x00,
+ 0x4c, 0xcc, 0x41, 0x31, 0xa0, 0x00, 0x41, 0x21, 0x20, 0x00, 0xa7, 0x1b,
+ 0x00, 0x01, 0xd2, 0x00, 0x30, 0x00, 0x20, 0x0f, 0xa7, 0xf4, 0xff, 0xdd,
+ 0x07, 0x07, 0x07, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0x00, 0x05,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05,
+ 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+ 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+ 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+ 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+ 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+ 0x2e, 0x2e, 0x2e, 0x2e, 0x20, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+ 0x2e, 0x2e, 0x2e, 0x2e, 0x3c, 0x28, 0x2b, 0x7c, 0x26, 0x2e, 0x2e, 0x2e,
+ 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x21, 0x24, 0x2a, 0x29, 0x3b, 0x2e,
+ 0x2d, 0x2f, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2c,
+ 0x25, 0x5f, 0x3e, 0x3f, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+ 0x2e, 0x60, 0x3a, 0x23, 0x40, 0x27, 0x3d, 0x22, 0x2e, 0x61, 0x62, 0x63,
+ 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+ 0x2e, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x2e, 0x2e,
+ 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
+ 0x79, 0x7a, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+ 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+ 0x2e, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x2e, 0x2e,
+ 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50,
+ 0x51, 0x52, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x53, 0x54,
+ 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x2e, 0x2e,
+ 0x2e, 0x2e, 0x2e, 0x2e, 0x41, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x6f, 0xff, 0xfe, 0xf5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x02, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf8, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x30,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x6f, 0xff, 0xff, 0xfb,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0x6f, 0xff, 0xff, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x17, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x43, 0x43, 0x3a,
+ 0x20, 0x28, 0x55, 0x62, 0x75, 0x6e, 0x74, 0x75, 0x20, 0x31, 0x31, 0x2e,
+ 0x34, 0x2e, 0x30, 0x2d, 0x31, 0x75, 0x62, 0x75, 0x6e, 0x74, 0x75, 0x31,
+ 0x7e, 0x32, 0x32, 0x2e, 0x30, 0x34, 0x29, 0x20, 0x31, 0x31, 0x2e, 0x34,
+ 0x2e, 0x30, 0x00, 0x00, 0x2e, 0x73, 0x68, 0x73, 0x74, 0x72, 0x74, 0x61,
+ 0x62, 0x00, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x00, 0x2e, 0x67,
+ 0x6e, 0x75, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x00, 0x2e, 0x64, 0x79, 0x6e,
+ 0x73, 0x79, 0x6d, 0x00, 0x2e, 0x64, 0x79, 0x6e, 0x73, 0x74, 0x72, 0x00,
+ 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x2e, 0x64, 0x79, 0x6e, 0x00, 0x2e, 0x74,
+ 0x65, 0x78, 0x74, 0x00, 0x2e, 0x72, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x00,
+ 0x2e, 0x64, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x00, 0x2e, 0x67, 0x6f,
+ 0x74, 0x00, 0x2e, 0x62, 0x73, 0x73, 0x00, 0x2e, 0x63, 0x6f, 0x6d, 0x6d,
+ 0x65, 0x6e, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc8, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x6f, 0xff, 0xff, 0xf6,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x01, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x03,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d,
+ 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf8, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x01, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30,
+ 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18,
+ 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x28,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x28, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x00, 0x04,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x02, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x30,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x03,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x37,
+ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x48, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x02, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x40,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x88,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x88, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x01, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, 0x06,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x17, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xb8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x20, 0x00, 0x00, 0x00, 0x04,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x4e,
+ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0xd8, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x08, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0xf0, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0xf0,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x09, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
diff --git a/tests/qtest/migration/tls-tests.c b/tests/qtest/migration/tls-tests.c
new file mode 100644
index 0000000..21e9fec
--- /dev/null
+++ b/tests/qtest/migration/tls-tests.c
@@ -0,0 +1,871 @@
+/*
+ * QTest testcases for TLS migration
+ *
+ * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates
+ * based on the vhost-user-test.c that is:
+ * Copyright (c) 2014 Virtual Open Systems Sarl.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "crypto/tlscredspsk.h"
+#include "libqtest.h"
+#include "migration/framework.h"
+#include "migration/migration-qmp.h"
+#include "migration/migration-util.h"
+
+#include "tests/unit/crypto-tls-psk-helpers.h"
+#ifdef CONFIG_TASN1
+# include "tests/unit/crypto-tls-x509-helpers.h"
+#endif /* CONFIG_TASN1 */
+
+
+struct TestMigrateTLSPSKData {
+ char *workdir;
+ char *workdiralt;
+ char *pskfile;
+ char *pskfilealt;
+};
+
+static char *tmpfs;
+
+static void *
+migrate_hook_start_tls_psk_common(QTestState *from,
+ QTestState *to,
+ bool mismatch)
+{
+ struct TestMigrateTLSPSKData *data =
+ g_new0(struct TestMigrateTLSPSKData, 1);
+
+ data->workdir = g_strdup_printf("%s/tlscredspsk0", tmpfs);
+ data->pskfile = g_strdup_printf("%s/%s", data->workdir,
+ QCRYPTO_TLS_CREDS_PSKFILE);
+ g_mkdir_with_parents(data->workdir, 0700);
+ test_tls_psk_init(data->pskfile);
+
+ if (mismatch) {
+ data->workdiralt = g_strdup_printf("%s/tlscredspskalt0", tmpfs);
+ data->pskfilealt = g_strdup_printf("%s/%s", data->workdiralt,
+ QCRYPTO_TLS_CREDS_PSKFILE);
+ g_mkdir_with_parents(data->workdiralt, 0700);
+ test_tls_psk_init_alt(data->pskfilealt);
+ }
+
+ qtest_qmp_assert_success(from,
+ "{ 'execute': 'object-add',"
+ " 'arguments': { 'qom-type': 'tls-creds-psk',"
+ " 'id': 'tlscredspsk0',"
+ " 'endpoint': 'client',"
+ " 'dir': %s,"
+ " 'username': 'qemu'} }",
+ data->workdir);
+
+ qtest_qmp_assert_success(to,
+ "{ 'execute': 'object-add',"
+ " 'arguments': { 'qom-type': 'tls-creds-psk',"
+ " 'id': 'tlscredspsk0',"
+ " 'endpoint': 'server',"
+ " 'dir': %s } }",
+ mismatch ? data->workdiralt : data->workdir);
+
+ migrate_set_parameter_str(from, "tls-creds", "tlscredspsk0");
+ migrate_set_parameter_str(to, "tls-creds", "tlscredspsk0");
+
+ return data;
+}
+
+static void *
+migrate_hook_start_tls_psk_match(QTestState *from,
+ QTestState *to)
+{
+ return migrate_hook_start_tls_psk_common(from, to, false);
+}
+
+static void *
+migrate_hook_start_tls_psk_mismatch(QTestState *from,
+ QTestState *to)
+{
+ return migrate_hook_start_tls_psk_common(from, to, true);
+}
+
+static void
+migrate_hook_end_tls_psk(QTestState *from,
+ QTestState *to,
+ void *opaque)
+{
+ struct TestMigrateTLSPSKData *data = opaque;
+
+ test_tls_psk_cleanup(data->pskfile);
+ if (data->pskfilealt) {
+ test_tls_psk_cleanup(data->pskfilealt);
+ }
+ rmdir(data->workdir);
+ if (data->workdiralt) {
+ rmdir(data->workdiralt);
+ }
+
+ g_free(data->workdiralt);
+ g_free(data->pskfilealt);
+ g_free(data->workdir);
+ g_free(data->pskfile);
+ g_free(data);
+}
+
+#ifdef CONFIG_TASN1
+typedef struct {
+ char *workdir;
+ char *keyfile;
+ char *cacert;
+ char *servercert;
+ char *serverkey;
+ char *clientcert;
+ char *clientkey;
+} TestMigrateTLSX509Data;
+
+typedef struct {
+ bool verifyclient;
+ bool clientcert;
+ bool hostileclient;
+ bool authzclient;
+ const char *certhostname;
+ const char *certipaddr;
+} TestMigrateTLSX509;
+
+static void *
+migrate_hook_start_tls_x509_common(QTestState *from,
+ QTestState *to,
+ TestMigrateTLSX509 *args)
+{
+ TestMigrateTLSX509Data *data = g_new0(TestMigrateTLSX509Data, 1);
+
+ data->workdir = g_strdup_printf("%s/tlscredsx5090", tmpfs);
+ data->keyfile = g_strdup_printf("%s/key.pem", data->workdir);
+
+ data->cacert = g_strdup_printf("%s/ca-cert.pem", data->workdir);
+ data->serverkey = g_strdup_printf("%s/server-key.pem", data->workdir);
+ data->servercert = g_strdup_printf("%s/server-cert.pem", data->workdir);
+ if (args->clientcert) {
+ data->clientkey = g_strdup_printf("%s/client-key.pem", data->workdir);
+ data->clientcert = g_strdup_printf("%s/client-cert.pem", data->workdir);
+ }
+
+ g_mkdir_with_parents(data->workdir, 0700);
+
+ test_tls_init(data->keyfile);
+#ifndef _WIN32
+ g_assert(link(data->keyfile, data->serverkey) == 0);
+#else
+ g_assert(CreateHardLink(data->serverkey, data->keyfile, NULL) != 0);
+#endif
+ if (args->clientcert) {
+#ifndef _WIN32
+ g_assert(link(data->keyfile, data->clientkey) == 0);
+#else
+ g_assert(CreateHardLink(data->clientkey, data->keyfile, NULL) != 0);
+#endif
+ }
+
+ TLS_ROOT_REQ_SIMPLE(cacertreq, data->cacert);
+ if (args->clientcert) {
+ TLS_CERT_REQ_SIMPLE_CLIENT(servercertreq, cacertreq,
+ args->hostileclient ?
+ QCRYPTO_TLS_TEST_CLIENT_HOSTILE_NAME :
+ QCRYPTO_TLS_TEST_CLIENT_NAME,
+ data->clientcert);
+ test_tls_deinit_cert(&servercertreq);
+ }
+
+ TLS_CERT_REQ_SIMPLE_SERVER(clientcertreq, cacertreq,
+ data->servercert,
+ args->certhostname,
+ args->certipaddr);
+ test_tls_deinit_cert(&clientcertreq);
+ test_tls_deinit_cert(&cacertreq);
+
+ qtest_qmp_assert_success(from,
+ "{ 'execute': 'object-add',"
+ " 'arguments': { 'qom-type': 'tls-creds-x509',"
+ " 'id': 'tlscredsx509client0',"
+ " 'endpoint': 'client',"
+ " 'dir': %s,"
+ " 'sanity-check': true,"
+ " 'verify-peer': true} }",
+ data->workdir);
+ migrate_set_parameter_str(from, "tls-creds", "tlscredsx509client0");
+ if (args->certhostname) {
+ migrate_set_parameter_str(from, "tls-hostname", args->certhostname);
+ }
+
+ qtest_qmp_assert_success(to,
+ "{ 'execute': 'object-add',"
+ " 'arguments': { 'qom-type': 'tls-creds-x509',"
+ " 'id': 'tlscredsx509server0',"
+ " 'endpoint': 'server',"
+ " 'dir': %s,"
+ " 'sanity-check': true,"
+ " 'verify-peer': %i} }",
+ data->workdir, args->verifyclient);
+ migrate_set_parameter_str(to, "tls-creds", "tlscredsx509server0");
+
+ if (args->authzclient) {
+ qtest_qmp_assert_success(to,
+ "{ 'execute': 'object-add',"
+ " 'arguments': { 'qom-type': 'authz-simple',"
+ " 'id': 'tlsauthz0',"
+ " 'identity': %s} }",
+ "CN=" QCRYPTO_TLS_TEST_CLIENT_NAME);
+ migrate_set_parameter_str(to, "tls-authz", "tlsauthz0");
+ }
+
+ return data;
+}
+
+/*
+ * The normal case: match server's cert hostname against
+ * whatever host we were telling QEMU to connect to (if any)
+ */
+static void *
+migrate_hook_start_tls_x509_default_host(QTestState *from,
+ QTestState *to)
+{
+ TestMigrateTLSX509 args = {
+ .verifyclient = true,
+ .clientcert = true,
+ .certipaddr = "127.0.0.1"
+ };
+ return migrate_hook_start_tls_x509_common(from, to, &args);
+}
+
+/*
+ * The unusual case: the server's cert is different from
+ * the address we're telling QEMU to connect to (if any),
+ * so we must give QEMU an explicit hostname to validate
+ */
+static void *
+migrate_hook_start_tls_x509_override_host(QTestState *from,
+ QTestState *to)
+{
+ TestMigrateTLSX509 args = {
+ .verifyclient = true,
+ .clientcert = true,
+ .certhostname = "qemu.org",
+ };
+ return migrate_hook_start_tls_x509_common(from, to, &args);
+}
+
+/*
+ * The unusual case: the server's cert is different from
+ * the address we're telling QEMU to connect to, and so we
+ * expect the client to reject the server
+ */
+static void *
+migrate_hook_start_tls_x509_mismatch_host(QTestState *from,
+ QTestState *to)
+{
+ TestMigrateTLSX509 args = {
+ .verifyclient = true,
+ .clientcert = true,
+ .certipaddr = "10.0.0.1",
+ };
+ return migrate_hook_start_tls_x509_common(from, to, &args);
+}
+
+static void *
+migrate_hook_start_tls_x509_friendly_client(QTestState *from,
+ QTestState *to)
+{
+ TestMigrateTLSX509 args = {
+ .verifyclient = true,
+ .clientcert = true,
+ .authzclient = true,
+ .certipaddr = "127.0.0.1",
+ };
+ return migrate_hook_start_tls_x509_common(from, to, &args);
+}
+
+static void *
+migrate_hook_start_tls_x509_hostile_client(QTestState *from,
+ QTestState *to)
+{
+ TestMigrateTLSX509 args = {
+ .verifyclient = true,
+ .clientcert = true,
+ .hostileclient = true,
+ .authzclient = true,
+ .certipaddr = "127.0.0.1",
+ };
+ return migrate_hook_start_tls_x509_common(from, to, &args);
+}
+
+/*
+ * The case with no client certificate presented,
+ * and no server verification
+ */
+static void *
+migrate_hook_start_tls_x509_allow_anon_client(QTestState *from,
+ QTestState *to)
+{
+ TestMigrateTLSX509 args = {
+ .certipaddr = "127.0.0.1",
+ };
+ return migrate_hook_start_tls_x509_common(from, to, &args);
+}
+
+/*
+ * The case with no client certificate presented,
+ * and server verification rejecting
+ */
+static void *
+migrate_hook_start_tls_x509_reject_anon_client(QTestState *from,
+ QTestState *to)
+{
+ TestMigrateTLSX509 args = {
+ .verifyclient = true,
+ .certipaddr = "127.0.0.1",
+ };
+ return migrate_hook_start_tls_x509_common(from, to, &args);
+}
+
+static void
+migrate_hook_end_tls_x509(QTestState *from,
+ QTestState *to,
+ void *opaque)
+{
+ TestMigrateTLSX509Data *data = opaque;
+
+ test_tls_cleanup(data->keyfile);
+ g_free(data->keyfile);
+
+ unlink(data->cacert);
+ g_free(data->cacert);
+ unlink(data->servercert);
+ g_free(data->servercert);
+ unlink(data->serverkey);
+ g_free(data->serverkey);
+
+ if (data->clientcert) {
+ unlink(data->clientcert);
+ g_free(data->clientcert);
+ }
+ if (data->clientkey) {
+ unlink(data->clientkey);
+ g_free(data->clientkey);
+ }
+
+ rmdir(data->workdir);
+ g_free(data->workdir);
+
+ g_free(data);
+}
+#endif /* CONFIG_TASN1 */
+
+static void test_postcopy_tls_psk(void)
+{
+ MigrateCommon args = {
+ .start_hook = migrate_hook_start_tls_psk_match,
+ .end_hook = migrate_hook_end_tls_psk,
+ };
+
+ test_postcopy_common(&args);
+}
+
+static void test_postcopy_preempt_tls_psk(void)
+{
+ MigrateCommon args = {
+ .start_hook = migrate_hook_start_tls_psk_match,
+ .end_hook = migrate_hook_end_tls_psk,
+ .start = {
+ .caps[MIGRATION_CAPABILITY_POSTCOPY_PREEMPT] = true,
+ },
+ };
+
+ test_postcopy_common(&args);
+}
+
+static void test_postcopy_recovery_tls_psk(void)
+{
+ MigrateCommon args = {
+ .start_hook = migrate_hook_start_tls_psk_match,
+ .end_hook = migrate_hook_end_tls_psk,
+ };
+
+ test_postcopy_recovery_common(&args);
+}
+
+static void test_multifd_postcopy_recovery_tls_psk(void)
+{
+ MigrateCommon args = {
+ .start_hook = migrate_hook_start_tls_psk_match,
+ .end_hook = migrate_hook_end_tls_psk,
+ .start = {
+ .caps[MIGRATION_CAPABILITY_MULTIFD] = true,
+ },
+ };
+
+ test_postcopy_recovery_common(&args);
+}
+
+/* This contains preempt+recovery+tls test altogether */
+static void test_postcopy_preempt_all(void)
+{
+ MigrateCommon args = {
+ .start_hook = migrate_hook_start_tls_psk_match,
+ .end_hook = migrate_hook_end_tls_psk,
+ .start = {
+ .caps[MIGRATION_CAPABILITY_POSTCOPY_PREEMPT] = true,
+ },
+ };
+
+ test_postcopy_recovery_common(&args);
+}
+
+static void test_multifd_postcopy_preempt_recovery_tls_psk(void)
+{
+ MigrateCommon args = {
+ .start_hook = migrate_hook_start_tls_psk_match,
+ .end_hook = migrate_hook_end_tls_psk,
+ .start = {
+ .caps[MIGRATION_CAPABILITY_MULTIFD] = true,
+ .caps[MIGRATION_CAPABILITY_POSTCOPY_PREEMPT] = true,
+ },
+ };
+
+ test_postcopy_recovery_common(&args);
+}
+
+static void test_precopy_unix_tls_psk(void)
+{
+ g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs);
+ MigrateCommon args = {
+ .connect_uri = uri,
+ .listen_uri = uri,
+ .start_hook = migrate_hook_start_tls_psk_match,
+ .end_hook = migrate_hook_end_tls_psk,
+ };
+
+ test_precopy_common(&args);
+}
+
+#ifdef CONFIG_TASN1
+static void test_precopy_unix_tls_x509_default_host(void)
+{
+ g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs);
+ MigrateCommon args = {
+ .start = {
+ .hide_stderr = true,
+ },
+ .connect_uri = uri,
+ .listen_uri = uri,
+ .start_hook = migrate_hook_start_tls_x509_default_host,
+ .end_hook = migrate_hook_end_tls_x509,
+ .result = MIG_TEST_FAIL_DEST_QUIT_ERR,
+ };
+
+ test_precopy_common(&args);
+}
+
+static void test_precopy_unix_tls_x509_override_host(void)
+{
+ g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs);
+ MigrateCommon args = {
+ .connect_uri = uri,
+ .listen_uri = uri,
+ .start_hook = migrate_hook_start_tls_x509_override_host,
+ .end_hook = migrate_hook_end_tls_x509,
+ };
+
+ test_precopy_common(&args);
+}
+#endif /* CONFIG_TASN1 */
+
+static void test_precopy_tcp_tls_psk_match(void)
+{
+ MigrateCommon args = {
+ .listen_uri = "tcp:127.0.0.1:0",
+ .start_hook = migrate_hook_start_tls_psk_match,
+ .end_hook = migrate_hook_end_tls_psk,
+ };
+
+ test_precopy_common(&args);
+}
+
+static void test_precopy_tcp_tls_psk_mismatch(void)
+{
+ MigrateCommon args = {
+ .start = {
+ .hide_stderr = true,
+ },
+ .listen_uri = "tcp:127.0.0.1:0",
+ .start_hook = migrate_hook_start_tls_psk_mismatch,
+ .end_hook = migrate_hook_end_tls_psk,
+ .result = MIG_TEST_FAIL,
+ };
+
+ test_precopy_common(&args);
+}
+
+#ifdef CONFIG_TASN1
+static void test_precopy_tcp_tls_x509_default_host(void)
+{
+ MigrateCommon args = {
+ .listen_uri = "tcp:127.0.0.1:0",
+ .start_hook = migrate_hook_start_tls_x509_default_host,
+ .end_hook = migrate_hook_end_tls_x509,
+ };
+
+ test_precopy_common(&args);
+}
+
+static void test_precopy_tcp_tls_x509_override_host(void)
+{
+ MigrateCommon args = {
+ .listen_uri = "tcp:127.0.0.1:0",
+ .start_hook = migrate_hook_start_tls_x509_override_host,
+ .end_hook = migrate_hook_end_tls_x509,
+ };
+
+ test_precopy_common(&args);
+}
+
+static void test_precopy_tcp_tls_x509_mismatch_host(void)
+{
+ MigrateCommon args = {
+ .start = {
+ .hide_stderr = true,
+ },
+ .listen_uri = "tcp:127.0.0.1:0",
+ .start_hook = migrate_hook_start_tls_x509_mismatch_host,
+ .end_hook = migrate_hook_end_tls_x509,
+ .result = MIG_TEST_FAIL_DEST_QUIT_ERR,
+ };
+
+ test_precopy_common(&args);
+}
+
+static void test_precopy_tcp_tls_x509_friendly_client(void)
+{
+ MigrateCommon args = {
+ .listen_uri = "tcp:127.0.0.1:0",
+ .start_hook = migrate_hook_start_tls_x509_friendly_client,
+ .end_hook = migrate_hook_end_tls_x509,
+ };
+
+ test_precopy_common(&args);
+}
+
+static void test_precopy_tcp_tls_x509_hostile_client(void)
+{
+ MigrateCommon args = {
+ .start = {
+ .hide_stderr = true,
+ },
+ .listen_uri = "tcp:127.0.0.1:0",
+ .start_hook = migrate_hook_start_tls_x509_hostile_client,
+ .end_hook = migrate_hook_end_tls_x509,
+ .result = MIG_TEST_FAIL,
+ };
+
+ test_precopy_common(&args);
+}
+
+static void test_precopy_tcp_tls_x509_allow_anon_client(void)
+{
+ MigrateCommon args = {
+ .listen_uri = "tcp:127.0.0.1:0",
+ .start_hook = migrate_hook_start_tls_x509_allow_anon_client,
+ .end_hook = migrate_hook_end_tls_x509,
+ };
+
+ test_precopy_common(&args);
+}
+
+static void test_precopy_tcp_tls_x509_reject_anon_client(void)
+{
+ MigrateCommon args = {
+ .start = {
+ .hide_stderr = true,
+ },
+ .listen_uri = "tcp:127.0.0.1:0",
+ .start_hook = migrate_hook_start_tls_x509_reject_anon_client,
+ .end_hook = migrate_hook_end_tls_x509,
+ .result = MIG_TEST_FAIL,
+ };
+
+ test_precopy_common(&args);
+}
+#endif /* CONFIG_TASN1 */
+
+static void *
+migrate_hook_start_multifd_tcp_tls_psk_match(QTestState *from,
+ QTestState *to)
+{
+ migrate_hook_start_precopy_tcp_multifd_common(from, to, "none");
+ return migrate_hook_start_tls_psk_match(from, to);
+}
+
+static void *
+migrate_hook_start_multifd_tcp_tls_psk_mismatch(QTestState *from,
+ QTestState *to)
+{
+ migrate_hook_start_precopy_tcp_multifd_common(from, to, "none");
+ return migrate_hook_start_tls_psk_mismatch(from, to);
+}
+
+#ifdef CONFIG_TASN1
+static void *
+migrate_hook_start_multifd_tls_x509_default_host(QTestState *from,
+ QTestState *to)
+{
+ migrate_hook_start_precopy_tcp_multifd_common(from, to, "none");
+ return migrate_hook_start_tls_x509_default_host(from, to);
+}
+
+static void *
+migrate_hook_start_multifd_tls_x509_override_host(QTestState *from,
+ QTestState *to)
+{
+ migrate_hook_start_precopy_tcp_multifd_common(from, to, "none");
+ return migrate_hook_start_tls_x509_override_host(from, to);
+}
+
+static void *
+migrate_hook_start_multifd_tls_x509_mismatch_host(QTestState *from,
+ QTestState *to)
+{
+ migrate_hook_start_precopy_tcp_multifd_common(from, to, "none");
+ return migrate_hook_start_tls_x509_mismatch_host(from, to);
+}
+
+static void *
+migrate_hook_start_multifd_tls_x509_allow_anon_client(QTestState *from,
+ QTestState *to)
+{
+ migrate_hook_start_precopy_tcp_multifd_common(from, to, "none");
+ return migrate_hook_start_tls_x509_allow_anon_client(from, to);
+}
+
+static void *
+migrate_hook_start_multifd_tls_x509_reject_anon_client(QTestState *from,
+ QTestState *to)
+{
+ migrate_hook_start_precopy_tcp_multifd_common(from, to, "none");
+ return migrate_hook_start_tls_x509_reject_anon_client(from, to);
+}
+#endif /* CONFIG_TASN1 */
+
+static void test_multifd_tcp_tls_psk_match(void)
+{
+ MigrateCommon args = {
+ .listen_uri = "defer",
+ .start_hook = migrate_hook_start_multifd_tcp_tls_psk_match,
+ .end_hook = migrate_hook_end_tls_psk,
+ .start = {
+ .caps[MIGRATION_CAPABILITY_MULTIFD] = true,
+ },
+ };
+ test_precopy_common(&args);
+}
+
+static void test_multifd_tcp_tls_psk_mismatch(void)
+{
+ MigrateCommon args = {
+ .start = {
+ .hide_stderr = true,
+ .caps[MIGRATION_CAPABILITY_MULTIFD] = true,
+ },
+ .listen_uri = "defer",
+ .start_hook = migrate_hook_start_multifd_tcp_tls_psk_mismatch,
+ .end_hook = migrate_hook_end_tls_psk,
+ .result = MIG_TEST_FAIL,
+ };
+ test_precopy_common(&args);
+}
+
+static void test_multifd_postcopy_tcp_tls_psk_match(void)
+{
+ MigrateCommon args = {
+ .start = {
+ .caps[MIGRATION_CAPABILITY_MULTIFD] = true,
+ .caps[MIGRATION_CAPABILITY_POSTCOPY_RAM] = true,
+ },
+ .listen_uri = "defer",
+ .start_hook = migrate_hook_start_multifd_tcp_tls_psk_match,
+ .end_hook = migrate_hook_end_tls_psk,
+ };
+
+ test_precopy_common(&args);
+}
+
+#ifdef CONFIG_TASN1
+static void test_multifd_tcp_tls_x509_default_host(void)
+{
+ MigrateCommon args = {
+ .listen_uri = "defer",
+ .start_hook = migrate_hook_start_multifd_tls_x509_default_host,
+ .end_hook = migrate_hook_end_tls_x509,
+ .start = {
+ .caps[MIGRATION_CAPABILITY_MULTIFD] = true,
+ },
+ };
+ test_precopy_common(&args);
+}
+
+static void test_multifd_tcp_tls_x509_override_host(void)
+{
+ MigrateCommon args = {
+ .listen_uri = "defer",
+ .start_hook = migrate_hook_start_multifd_tls_x509_override_host,
+ .end_hook = migrate_hook_end_tls_x509,
+ .start = {
+ .caps[MIGRATION_CAPABILITY_MULTIFD] = true,
+ },
+ };
+ test_precopy_common(&args);
+}
+
+static void test_multifd_tcp_tls_x509_mismatch_host(void)
+{
+ /*
+ * This has different behaviour to the non-multifd case.
+ *
+ * In non-multifd case when client aborts due to mismatched
+ * cert host, the server has already started trying to load
+ * migration state, and so it exits with I/O failure.
+ *
+ * In multifd case when client aborts due to mismatched
+ * cert host, the server is still waiting for the other
+ * multifd connections to arrive so hasn't started trying
+ * to load migration state, and thus just aborts the migration
+ * without exiting.
+ */
+ MigrateCommon args = {
+ .start = {
+ .hide_stderr = true,
+ .caps[MIGRATION_CAPABILITY_MULTIFD] = true,
+ },
+ .listen_uri = "defer",
+ .start_hook = migrate_hook_start_multifd_tls_x509_mismatch_host,
+ .end_hook = migrate_hook_end_tls_x509,
+ .result = MIG_TEST_FAIL,
+ };
+ test_precopy_common(&args);
+}
+
+static void test_multifd_tcp_tls_x509_allow_anon_client(void)
+{
+ MigrateCommon args = {
+ .listen_uri = "defer",
+ .start_hook = migrate_hook_start_multifd_tls_x509_allow_anon_client,
+ .end_hook = migrate_hook_end_tls_x509,
+ .start = {
+ .caps[MIGRATION_CAPABILITY_MULTIFD] = true,
+ },
+ };
+ test_precopy_common(&args);
+}
+
+static void test_multifd_tcp_tls_x509_reject_anon_client(void)
+{
+ MigrateCommon args = {
+ .start = {
+ .hide_stderr = true,
+ .caps[MIGRATION_CAPABILITY_MULTIFD] = true,
+ },
+ .listen_uri = "defer",
+ .start_hook = migrate_hook_start_multifd_tls_x509_reject_anon_client,
+ .end_hook = migrate_hook_end_tls_x509,
+ .result = MIG_TEST_FAIL,
+ };
+ test_precopy_common(&args);
+}
+#endif /* CONFIG_TASN1 */
+
+static void migration_test_add_tls_smoke(MigrationTestEnv *env)
+{
+ migration_test_add("/migration/precopy/tcp/tls/psk/match",
+ test_precopy_tcp_tls_psk_match);
+}
+
+void migration_test_add_tls(MigrationTestEnv *env)
+{
+ tmpfs = env->tmpfs;
+
+ migration_test_add_tls_smoke(env);
+
+ if (!env->full_set) {
+ return;
+ }
+
+ migration_test_add("/migration/precopy/unix/tls/psk",
+ test_precopy_unix_tls_psk);
+
+ if (env->has_uffd) {
+ /*
+ * NOTE: psk test is enough for postcopy, as other types of TLS
+ * channels are tested under precopy. Here what we want to test is the
+ * general postcopy path that has TLS channel enabled.
+ */
+ migration_test_add("/migration/postcopy/tls/psk",
+ test_postcopy_tls_psk);
+ migration_test_add("/migration/postcopy/recovery/tls/psk",
+ test_postcopy_recovery_tls_psk);
+ migration_test_add("/migration/postcopy/preempt/tls/psk",
+ test_postcopy_preempt_tls_psk);
+ migration_test_add("/migration/postcopy/preempt/recovery/tls/psk",
+ test_postcopy_preempt_all);
+ migration_test_add("/migration/multifd+postcopy/recovery/tls/psk",
+ test_multifd_postcopy_recovery_tls_psk);
+ migration_test_add(
+ "/migration/multifd+postcopy/preempt/recovery/tls/psk",
+ test_multifd_postcopy_preempt_recovery_tls_psk);
+ }
+#ifdef CONFIG_TASN1
+ migration_test_add("/migration/precopy/unix/tls/x509/default-host",
+ test_precopy_unix_tls_x509_default_host);
+ migration_test_add("/migration/precopy/unix/tls/x509/override-host",
+ test_precopy_unix_tls_x509_override_host);
+#endif /* CONFIG_TASN1 */
+
+ migration_test_add("/migration/precopy/tcp/tls/psk/mismatch",
+ test_precopy_tcp_tls_psk_mismatch);
+#ifdef CONFIG_TASN1
+ migration_test_add("/migration/precopy/tcp/tls/x509/default-host",
+ test_precopy_tcp_tls_x509_default_host);
+ migration_test_add("/migration/precopy/tcp/tls/x509/override-host",
+ test_precopy_tcp_tls_x509_override_host);
+ migration_test_add("/migration/precopy/tcp/tls/x509/mismatch-host",
+ test_precopy_tcp_tls_x509_mismatch_host);
+ migration_test_add("/migration/precopy/tcp/tls/x509/friendly-client",
+ test_precopy_tcp_tls_x509_friendly_client);
+ migration_test_add("/migration/precopy/tcp/tls/x509/hostile-client",
+ test_precopy_tcp_tls_x509_hostile_client);
+ migration_test_add("/migration/precopy/tcp/tls/x509/allow-anon-client",
+ test_precopy_tcp_tls_x509_allow_anon_client);
+ migration_test_add("/migration/precopy/tcp/tls/x509/reject-anon-client",
+ test_precopy_tcp_tls_x509_reject_anon_client);
+#endif /* CONFIG_TASN1 */
+
+ migration_test_add("/migration/multifd/tcp/tls/psk/match",
+ test_multifd_tcp_tls_psk_match);
+ migration_test_add("/migration/multifd/tcp/tls/psk/mismatch",
+ test_multifd_tcp_tls_psk_mismatch);
+ if (env->has_uffd) {
+ migration_test_add("/migration/multifd+postcopy/tcp/tls/psk/match",
+ test_multifd_postcopy_tcp_tls_psk_match);
+ }
+#ifdef CONFIG_TASN1
+ migration_test_add("/migration/multifd/tcp/tls/x509/default-host",
+ test_multifd_tcp_tls_x509_default_host);
+ migration_test_add("/migration/multifd/tcp/tls/x509/override-host",
+ test_multifd_tcp_tls_x509_override_host);
+ migration_test_add("/migration/multifd/tcp/tls/x509/mismatch-host",
+ test_multifd_tcp_tls_x509_mismatch_host);
+ migration_test_add("/migration/multifd/tcp/tls/x509/allow-anon-client",
+ test_multifd_tcp_tls_x509_allow_anon_client);
+ migration_test_add("/migration/multifd/tcp/tls/x509/reject-anon-client",
+ test_multifd_tcp_tls_x509_reject_anon_client);
+#endif /* CONFIG_TASN1 */
+}
diff --git a/tests/qtest/netdev-socket.c b/tests/qtest/netdev-socket.c
index fc7d119..b731af0 100644
--- a/tests/qtest/netdev-socket.c
+++ b/tests/qtest/netdev-socket.c
@@ -11,7 +11,7 @@
#include <glib/gstdio.h>
#include "../unit/socket-helpers.h"
#include "libqtest.h"
-#include "qapi/qmp/qstring.h"
+#include "qobject/qstring.h"
#include "qemu/sockets.h"
#include "qapi/qobject-input-visitor.h"
#include "qapi/qapi-visit-sockets.h"
@@ -204,7 +204,7 @@ static void test_stream_unix_reconnect(void)
qts1 = qtest_initf("-nodefaults -M none "
"-netdev stream,server=false,id=st0,addr.type=unix,"
- "addr.path=%s,reconnect=1", path);
+ "addr.path=%s,reconnect-ms=1000", path);
wait_stream_connected(qts0, "st0", &addr);
g_assert_cmpint(addr->type, ==, SOCKET_ADDRESS_TYPE_UNIX);
diff --git a/tests/qtest/npcm7xx_adc-test.c b/tests/qtest/npcm7xx_adc-test.c
index e751a72..8bc89b8 100644
--- a/tests/qtest/npcm7xx_adc-test.c
+++ b/tests/qtest/npcm7xx_adc-test.c
@@ -18,7 +18,7 @@
#include "qemu/bitops.h"
#include "qemu/timer.h"
#include "libqtest.h"
-#include "qapi/qmp/qdict.h"
+#include "qobject/qdict.h"
#define REF_HZ (25000000)
diff --git a/tests/qtest/npcm7xx_emc-test.c b/tests/qtest/npcm7xx_emc-test.c
index 2e1a1a6..eeedb27 100644
--- a/tests/qtest/npcm7xx_emc-test.c
+++ b/tests/qtest/npcm7xx_emc-test.c
@@ -16,8 +16,8 @@
#include "qemu/osdep.h"
#include "libqos/libqos.h"
-#include "qapi/qmp/qdict.h"
-#include "qapi/qmp/qnum.h"
+#include "qobject/qdict.h"
+#include "qobject/qnum.h"
#include "qemu/bitops.h"
#include "qemu/iov.h"
diff --git a/tests/qtest/npcm7xx_pwm-test.c b/tests/qtest/npcm7xx_pwm-test.c
index b53a43c..052ea87 100644
--- a/tests/qtest/npcm7xx_pwm-test.c
+++ b/tests/qtest/npcm7xx_pwm-test.c
@@ -17,8 +17,8 @@
#include "qemu/osdep.h"
#include "qemu/bitops.h"
#include "libqtest.h"
-#include "qapi/qmp/qdict.h"
-#include "qapi/qmp/qnum.h"
+#include "qobject/qdict.h"
+#include "qobject/qnum.h"
static int verbosity_level;
diff --git a/tests/qtest/npcm7xx_sdhci-test.c b/tests/qtest/npcm7xx_sdhci-test.c
index 5d68540..01f237a 100644
--- a/tests/qtest/npcm7xx_sdhci-test.c
+++ b/tests/qtest/npcm7xx_sdhci-test.c
@@ -30,6 +30,8 @@ char *sd_path;
static QTestState *setup_sd_card(void)
{
+ uint16_t rca;
+
QTestState *qts = qtest_initf(
"-machine kudo-bmc "
"-device sd-card,drive=drive0 "
@@ -43,8 +45,10 @@ static QTestState *setup_sd_card(void)
sdhci_cmd_regs(qts, NPCM7XX_MMC_BA, 0, 0, 0, 0, SDHC_APP_CMD);
sdhci_cmd_regs(qts, NPCM7XX_MMC_BA, 0, 0, 0x41200000, 0, (41 << 8));
sdhci_cmd_regs(qts, NPCM7XX_MMC_BA, 0, 0, 0, 0, SDHC_ALL_SEND_CID);
- sdhci_cmd_regs(qts, NPCM7XX_MMC_BA, 0, 0, 0, 0, SDHC_SEND_RELATIVE_ADDR);
- sdhci_cmd_regs(qts, NPCM7XX_MMC_BA, 0, 0, 0x45670000, 0,
+ sdhci_cmd_regs(qts, NPCM7XX_MMC_BA, 0, 0, 0, 0, SDHC_SEND_RELATIVE_ADDR
+ | SDHC_CMD_RESPONSE);
+ rca = qtest_readl(qts, NPCM7XX_MMC_BA + SDHC_RSPREG0) >> 16;
+ sdhci_cmd_regs(qts, NPCM7XX_MMC_BA, 0, 0, rca << 16, 0,
SDHC_SELECT_DESELECT_CARD);
return qts;
diff --git a/tests/qtest/npcm7xx_timer-test.c b/tests/qtest/npcm7xx_timer-test.c
index 58f58c2..4371104 100644
--- a/tests/qtest/npcm7xx_timer-test.c
+++ b/tests/qtest/npcm7xx_timer-test.c
@@ -465,7 +465,6 @@ static void test_periodic_interrupt(gconstpointer test_data)
int i;
tim_reset(td);
- clock_step_next();
tim_write_ticr(td, count);
tim_write_tcsr(td, CEN | IE | MODE_PERIODIC | PRESCALE(ps));
diff --git a/tests/qtest/npcm7xx_watchdog_timer-test.c b/tests/qtest/npcm7xx_watchdog_timer-test.c
index 981b853..521ea78 100644
--- a/tests/qtest/npcm7xx_watchdog_timer-test.c
+++ b/tests/qtest/npcm7xx_watchdog_timer-test.c
@@ -18,7 +18,7 @@
#include "qemu/timer.h"
#include "libqtest.h"
-#include "qapi/qmp/qdict.h"
+#include "qobject/qdict.h"
#define WTCR_OFFSET 0x1c
#define REF_HZ (25000000)
diff --git a/tests/qtest/npcm_gmac-test.c b/tests/qtest/npcm_gmac-test.c
index c28b471..1317da2 100644
--- a/tests/qtest/npcm_gmac-test.c
+++ b/tests/qtest/npcm_gmac-test.c
@@ -36,7 +36,7 @@ typedef struct TestData {
const GMACModule *module;
} TestData;
-/* Values extracted from hw/arm/npcm7xx.c */
+/* Values extracted from hw/arm/npcm8xx.c */
static const GMACModule gmac_module_list[] = {
{
.irq = 14,
@@ -46,6 +46,14 @@ static const GMACModule gmac_module_list[] = {
.irq = 15,
.base_addr = 0xf0804000
},
+ {
+ .irq = 16,
+ .base_addr = 0xf0806000
+ },
+ {
+ .irq = 17,
+ .base_addr = 0xf0808000
+ }
};
/* Returns the index of the GMAC module. */
@@ -174,18 +182,32 @@ static uint32_t gmac_read(QTestState *qts, const GMACModule *mod,
return qtest_readl(qts, mod->base_addr + regno);
}
+static uint16_t pcs_read(QTestState *qts, const GMACModule *mod,
+ NPCMRegister regno)
+{
+ uint32_t write_value = (regno & 0x3ffe00) >> 9;
+ qtest_writel(qts, PCS_BASE_ADDRESS + NPCM_PCS_IND_AC_BA, write_value);
+ uint32_t read_offset = regno & 0x1ff;
+ return qtest_readl(qts, PCS_BASE_ADDRESS + read_offset);
+}
+
/* Check that GMAC registers are reset to default value */
static void test_init(gconstpointer test_data)
{
const TestData *td = test_data;
const GMACModule *mod = td->module;
- QTestState *qts = qtest_init("-machine npcm750-evb");
+ QTestState *qts = qtest_init("-machine npcm845-evb");
#define CHECK_REG32(regno, value) \
do { \
g_assert_cmphex(gmac_read(qts, mod, (regno)), ==, (value)); \
} while (0)
+#define CHECK_REG_PCS(regno, value) \
+ do { \
+ g_assert_cmphex(pcs_read(qts, mod, (regno)), ==, (value)); \
+ } while (0)
+
CHECK_REG32(NPCM_DMA_BUS_MODE, 0x00020100);
CHECK_REG32(NPCM_DMA_XMT_POLL_DEMAND, 0);
CHECK_REG32(NPCM_DMA_RCV_POLL_DEMAND, 0);
@@ -235,6 +257,63 @@ static void test_init(gconstpointer test_data)
CHECK_REG32(NPCM_GMAC_PTP_TAR, 0);
CHECK_REG32(NPCM_GMAC_PTP_TTSR, 0);
+ if (mod->base_addr == 0xf0802000) {
+ CHECK_REG_PCS(NPCM_PCS_SR_CTL_ID1, 0x699e);
+ CHECK_REG_PCS(NPCM_PCS_SR_CTL_ID2, 0);
+ CHECK_REG_PCS(NPCM_PCS_SR_CTL_STS, 0x8000);
+
+ CHECK_REG_PCS(NPCM_PCS_SR_MII_CTRL, 0x1140);
+ CHECK_REG_PCS(NPCM_PCS_SR_MII_STS, 0x0109);
+ CHECK_REG_PCS(NPCM_PCS_SR_MII_DEV_ID1, 0x699e);
+ CHECK_REG_PCS(NPCM_PCS_SR_MII_DEV_ID2, 0x0ced0);
+ CHECK_REG_PCS(NPCM_PCS_SR_MII_AN_ADV, 0x0020);
+ CHECK_REG_PCS(NPCM_PCS_SR_MII_LP_BABL, 0);
+ CHECK_REG_PCS(NPCM_PCS_SR_MII_AN_EXPN, 0);
+ CHECK_REG_PCS(NPCM_PCS_SR_MII_EXT_STS, 0xc000);
+
+ CHECK_REG_PCS(NPCM_PCS_SR_TIM_SYNC_ABL, 0x0003);
+ CHECK_REG_PCS(NPCM_PCS_SR_TIM_SYNC_TX_MAX_DLY_LWR, 0x0038);
+ CHECK_REG_PCS(NPCM_PCS_SR_TIM_SYNC_TX_MAX_DLY_UPR, 0);
+ CHECK_REG_PCS(NPCM_PCS_SR_TIM_SYNC_TX_MIN_DLY_LWR, 0x0038);
+ CHECK_REG_PCS(NPCM_PCS_SR_TIM_SYNC_TX_MIN_DLY_UPR, 0);
+ CHECK_REG_PCS(NPCM_PCS_SR_TIM_SYNC_RX_MAX_DLY_LWR, 0x0058);
+ CHECK_REG_PCS(NPCM_PCS_SR_TIM_SYNC_RX_MAX_DLY_UPR, 0);
+ CHECK_REG_PCS(NPCM_PCS_SR_TIM_SYNC_RX_MIN_DLY_LWR, 0x0048);
+ CHECK_REG_PCS(NPCM_PCS_SR_TIM_SYNC_RX_MIN_DLY_UPR, 0);
+
+ CHECK_REG_PCS(NPCM_PCS_VR_MII_MMD_DIG_CTRL1, 0x2400);
+ CHECK_REG_PCS(NPCM_PCS_VR_MII_AN_CTRL, 0);
+ CHECK_REG_PCS(NPCM_PCS_VR_MII_AN_INTR_STS, 0x000a);
+ CHECK_REG_PCS(NPCM_PCS_VR_MII_TC, 0);
+ CHECK_REG_PCS(NPCM_PCS_VR_MII_DBG_CTRL, 0);
+ CHECK_REG_PCS(NPCM_PCS_VR_MII_EEE_MCTRL0, 0x899c);
+ CHECK_REG_PCS(NPCM_PCS_VR_MII_EEE_TXTIMER, 0);
+ CHECK_REG_PCS(NPCM_PCS_VR_MII_EEE_RXTIMER, 0);
+ CHECK_REG_PCS(NPCM_PCS_VR_MII_LINK_TIMER_CTRL, 0);
+ CHECK_REG_PCS(NPCM_PCS_VR_MII_EEE_MCTRL1, 0);
+ CHECK_REG_PCS(NPCM_PCS_VR_MII_DIG_STS, 0x0010);
+ CHECK_REG_PCS(NPCM_PCS_VR_MII_ICG_ERRCNT1, 0);
+ CHECK_REG_PCS(NPCM_PCS_VR_MII_MISC_STS, 0);
+ CHECK_REG_PCS(NPCM_PCS_VR_MII_RX_LSTS, 0);
+ CHECK_REG_PCS(NPCM_PCS_VR_MII_MP_TX_BSTCTRL0, 0x00a);
+ CHECK_REG_PCS(NPCM_PCS_VR_MII_MP_TX_LVLCTRL0, 0x007f);
+ CHECK_REG_PCS(NPCM_PCS_VR_MII_MP_TX_GENCTRL0, 0x0001);
+ CHECK_REG_PCS(NPCM_PCS_VR_MII_MP_TX_GENCTRL1, 0);
+ CHECK_REG_PCS(NPCM_PCS_VR_MII_MP_TX_STS, 0);
+ CHECK_REG_PCS(NPCM_PCS_VR_MII_MP_RX_GENCTRL0, 0x0100);
+ CHECK_REG_PCS(NPCM_PCS_VR_MII_MP_RX_GENCTRL1, 0x1100);
+ CHECK_REG_PCS(NPCM_PCS_VR_MII_MP_RX_LOS_CTRL0, 0x000e);
+ CHECK_REG_PCS(NPCM_PCS_VR_MII_MP_MPLL_CTRL0, 0x0100);
+ CHECK_REG_PCS(NPCM_PCS_VR_MII_MP_MPLL_CTRL1, 0x0032);
+ CHECK_REG_PCS(NPCM_PCS_VR_MII_MP_MPLL_STS, 0x0001);
+ CHECK_REG_PCS(NPCM_PCS_VR_MII_MP_MISC_CTRL2, 0);
+ CHECK_REG_PCS(NPCM_PCS_VR_MII_MP_LVL_CTRL, 0x0019);
+ CHECK_REG_PCS(NPCM_PCS_VR_MII_MP_MISC_CTRL0, 0);
+ CHECK_REG_PCS(NPCM_PCS_VR_MII_MP_MISC_CTRL1, 0);
+ CHECK_REG_PCS(NPCM_PCS_VR_MII_DIG_CTRL2, 0);
+ CHECK_REG_PCS(NPCM_PCS_VR_MII_DIG_ERRCNT_SEL, 0);
+ }
+
qtest_quit(qts);
}
@@ -242,7 +321,7 @@ static void gmac_add_test(const char *name, const TestData* td,
GTestDataFunc fn)
{
g_autofree char *full_name = g_strdup_printf(
- "npcm7xx_gmac/gmac[%d]/%s", gmac_module_index(td->module), name);
+ "npcm8xx_gmac/gmac[%d]/%s", gmac_module_index(td->module), name);
qtest_add_data_func(full_name, td, fn);
}
diff --git a/tests/qtest/numa-test.c b/tests/qtest/numa-test.c
index ede4189..d657f38 100644
--- a/tests/qtest/numa-test.c
+++ b/tests/qtest/numa-test.c
@@ -11,8 +11,8 @@
#include "qemu/osdep.h"
#include "libqtest.h"
-#include "qapi/qmp/qdict.h"
-#include "qapi/qmp/qlist.h"
+#include "qobject/qdict.h"
+#include "qobject/qlist.h"
static char *make_cli(const GString *generic_cli, const char *test_cli)
{
@@ -162,7 +162,7 @@ static void pc_numa_cpu(const void *data)
} else if (socket == 1 && core == 1 && thread == 1) {
g_assert_cmpint(node, ==, 1);
} else {
- g_assert(false);
+ g_assert_not_reached();
}
qobject_unref(e);
}
@@ -207,7 +207,7 @@ static void spapr_numa_cpu(const void *data)
} else if (core == 3) {
g_assert_cmpint(node, ==, 1);
} else {
- g_assert(false);
+ g_assert_not_reached();
}
qobject_unref(e);
}
@@ -257,7 +257,7 @@ static void aarch64_numa_cpu(const void *data)
} else if (socket == 1 && cluster == 0 && core == 0 && thread == 0) {
g_assert_cmpint(node, ==, 0);
} else {
- g_assert(false);
+ g_assert_not_reached();
}
qobject_unref(e);
}
@@ -305,7 +305,7 @@ static void loongarch64_numa_cpu(const void *data)
} else if (socket == 1 && core == 0 && thread == 0) {
g_assert_cmpint(node, ==, 0);
} else {
- g_assert(false);
+ g_assert_not_reached();
}
qobject_unref(e);
}
@@ -367,7 +367,7 @@ static void pc_dynamic_cpu_cfg(const void *data)
} else if (socket == 1) {
g_assert_cmpint(node, ==, 0);
} else {
- g_assert(false);
+ g_assert_not_reached();
}
qobject_unref(e);
}
diff --git a/tests/qtest/pnv-host-i2c-test.c b/tests/qtest/pnv-host-i2c-test.c
index 7f64d59..51e613e 100644
--- a/tests/qtest/pnv-host-i2c-test.c
+++ b/tests/qtest/pnv-host-i2c-test.c
@@ -191,12 +191,10 @@ static uint8_t pnv_i2c_pca9554_read_pins(PnvI2cDev *dev)
{
uint8_t send_buf[1];
uint8_t recv_buf[1];
- uint8_t inputs;
send_buf[0] = PCA9554_INPUT;
pnv_i2c_send(dev, send_buf, 1);
pnv_i2c_recv(dev, recv_buf, 1);
- inputs = recv_buf[0];
- return inputs;
+ return recv_buf[0];
}
static void pnv_i2c_pca9554_flip_polarity(PnvI2cDev *dev)
diff --git a/tests/qtest/pnv-spi-seeprom-test.c b/tests/qtest/pnv-spi-seeprom-test.c
new file mode 100644
index 0000000..600493c
--- /dev/null
+++ b/tests/qtest/pnv-spi-seeprom-test.c
@@ -0,0 +1,110 @@
+/*
+ * QTest testcase for PowerNV 10 Seeprom Communications
+ *
+ * Copyright (c) 2024, IBM Corporation.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#include <unistd.h>
+#include "qemu/osdep.h"
+#include "libqtest.h"
+#include "qemu/bswap.h"
+#include "hw/ssi/pnv_spi_regs.h"
+#include "pnv-xscom.h"
+
+#define FLASH_SIZE (512 * 1024)
+#define SPIC2_XSCOM_BASE 0xc0040
+
+/* To transmit READ opcode and address */
+#define READ_OP_TDR_DATA 0x0300010000000000
+/*
+ * N1 shift - tx 4 bytes (transmit opcode and address)
+ * N2 shift - tx and rx 8 bytes.
+ */
+#define READ_OP_COUNTER_CONFIG 0x2040000000002b00
+/* SEQ_OP_SELECT_RESPONDER - N1 Shift - N2 Shift * 5 - SEQ_OP_STOP */
+#define READ_OP_SEQUENCER 0x1130404040404010
+
+/* To transmit WREN(Set Write Enable Latch in status0 register) opcode */
+#define WRITE_OP_WREN 0x0600000000000000
+/* To transmit WRITE opcode, address and data */
+#define WRITE_OP_TDR_DATA 0x0300010012345678
+/* N1 shift - tx 8 bytes (transmit opcode, address and data) */
+#define WRITE_OP_COUNTER_CONFIG 0x4000000000002000
+/* SEQ_OP_SELECT_RESPONDER - N1 Shift - SEQ_OP_STOP */
+#define WRITE_OP_SEQUENCER 0x1130100000000000
+
+static void pnv_spi_xscom_write(QTestState *qts, const PnvChip *chip,
+ uint32_t reg, uint64_t val)
+{
+ uint32_t pcba = SPIC2_XSCOM_BASE + reg;
+ qtest_writeq(qts, pnv_xscom_addr(chip, pcba), val);
+}
+
+static uint64_t pnv_spi_xscom_read(QTestState *qts, const PnvChip *chip,
+ uint32_t reg)
+{
+ uint32_t pcba = SPIC2_XSCOM_BASE + reg;
+ return qtest_readq(qts, pnv_xscom_addr(chip, pcba));
+}
+
+static void spi_seeprom_transaction(QTestState *qts, const PnvChip *chip)
+{
+ /* SPI transactions to SEEPROM to read from SEEPROM image */
+ pnv_spi_xscom_write(qts, chip, SPI_CTR_CFG_REG, READ_OP_COUNTER_CONFIG);
+ pnv_spi_xscom_write(qts, chip, SPI_SEQ_OP_REG, READ_OP_SEQUENCER);
+ pnv_spi_xscom_write(qts, chip, SPI_XMIT_DATA_REG, READ_OP_TDR_DATA);
+ pnv_spi_xscom_write(qts, chip, SPI_XMIT_DATA_REG, 0);
+ /* Read 5*8 bytes from SEEPROM at 0x100 */
+ uint64_t rdr_val = pnv_spi_xscom_read(qts, chip, SPI_RCV_DATA_REG);
+ g_test_message("RDR READ = 0x%" PRIx64, rdr_val);
+ rdr_val = pnv_spi_xscom_read(qts, chip, SPI_RCV_DATA_REG);
+ rdr_val = pnv_spi_xscom_read(qts, chip, SPI_RCV_DATA_REG);
+ rdr_val = pnv_spi_xscom_read(qts, chip, SPI_RCV_DATA_REG);
+ rdr_val = pnv_spi_xscom_read(qts, chip, SPI_RCV_DATA_REG);
+ g_test_message("RDR READ = 0x%" PRIx64, rdr_val);
+
+ /* SPI transactions to SEEPROM to write to SEEPROM image */
+ pnv_spi_xscom_write(qts, chip, SPI_CTR_CFG_REG, WRITE_OP_COUNTER_CONFIG);
+ /* Set Write Enable Latch bit of status0 register */
+ pnv_spi_xscom_write(qts, chip, SPI_SEQ_OP_REG, WRITE_OP_SEQUENCER);
+ pnv_spi_xscom_write(qts, chip, SPI_XMIT_DATA_REG, WRITE_OP_WREN);
+ /* write 8 bytes to SEEPROM at 0x100 */
+ pnv_spi_xscom_write(qts, chip, SPI_SEQ_OP_REG, WRITE_OP_SEQUENCER);
+ pnv_spi_xscom_write(qts, chip, SPI_XMIT_DATA_REG, WRITE_OP_TDR_DATA);
+}
+
+static void test_spi_seeprom(const void *data)
+{
+ const PnvChip *chip = data;
+ QTestState *qts = NULL;
+ g_autofree char *tmp_path = NULL;
+ int ret;
+ int fd;
+
+ /* Create a temporary raw image */
+ fd = g_file_open_tmp("qtest-seeprom-XXXXXX", &tmp_path, NULL);
+ g_assert(fd >= 0);
+ ret = ftruncate(fd, FLASH_SIZE);
+ g_assert(ret == 0);
+ close(fd);
+
+ qts = qtest_initf("-machine powernv10 -smp 2,cores=2,"
+ "threads=1 -accel tcg,thread=single -nographic "
+ "-blockdev node-name=pib_spic2,driver=file,"
+ "filename=%s -device 25csm04,bus=chip0.spi.2,cs=0,"
+ "drive=pib_spic2", tmp_path);
+ spi_seeprom_transaction(qts, chip);
+ qtest_quit(qts);
+ unlink(tmp_path);
+}
+
+int main(int argc, char **argv)
+{
+ g_test_init(&argc, &argv, NULL);
+ char *tname = g_strdup_printf("pnv-xscom/spi-seeprom/%s",
+ pnv_chips[3].cpu_model);
+ qtest_add_data_func(tname, &pnv_chips[3], test_spi_seeprom);
+ g_free(tname);
+ return g_test_run();
+}
diff --git a/tests/qtest/pnv-xive2-common.c b/tests/qtest/pnv-xive2-common.c
new file mode 100644
index 0000000..bf2bce00
--- /dev/null
+++ b/tests/qtest/pnv-xive2-common.c
@@ -0,0 +1,190 @@
+/*
+ * QTest testcase for PowerNV 10 interrupt controller (xive2)
+ * - Common functions for XIVE2 tests
+ *
+ * Copyright (c) 2024, IBM Corporation.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#include "qemu/osdep.h"
+#include "libqtest.h"
+
+#include "pnv-xive2-common.h"
+
+
+static uint64_t pnv_xscom_addr(uint32_t pcba)
+{
+ return P10_XSCOM_BASE | ((uint64_t) pcba << 3);
+}
+
+static uint64_t pnv_xive_xscom_addr(uint32_t reg)
+{
+ return pnv_xscom_addr(XIVE_XSCOM + reg);
+}
+
+uint64_t pnv_xive_xscom_read(QTestState *qts, uint32_t reg)
+{
+ return qtest_readq(qts, pnv_xive_xscom_addr(reg));
+}
+
+void pnv_xive_xscom_write(QTestState *qts, uint32_t reg, uint64_t val)
+{
+ qtest_writeq(qts, pnv_xive_xscom_addr(reg), val);
+}
+
+static void xive_get_struct(QTestState *qts, uint64_t src, void *dest,
+ size_t size)
+{
+ uint8_t *destination = (uint8_t *)dest;
+ size_t i;
+
+ for (i = 0; i < size; i++) {
+ *(destination + i) = qtest_readb(qts, src + i);
+ }
+}
+
+static void xive_copy_struct(QTestState *qts, void *src, uint64_t dest,
+ size_t size)
+{
+ uint8_t *source = (uint8_t *)src;
+ size_t i;
+
+ for (i = 0; i < size; i++) {
+ qtest_writeb(qts, dest + i, *(source + i));
+ }
+}
+
+uint64_t xive_get_queue_addr(uint32_t end_index)
+{
+ return XIVE_QUEUE_MEM + (uint64_t)end_index * XIVE_QUEUE_SIZE;
+}
+
+uint8_t get_esb(QTestState *qts, uint32_t index, uint8_t page,
+ uint32_t offset)
+{
+ uint64_t addr;
+
+ addr = XIVE_ESB_ADDR + ((uint64_t)index << (XIVE_PAGE_SHIFT + 1));
+ if (page == 1) {
+ addr += 1 << XIVE_PAGE_SHIFT;
+ }
+ return qtest_readb(qts, addr + offset);
+}
+
+void set_esb(QTestState *qts, uint32_t index, uint8_t page,
+ uint32_t offset, uint32_t val)
+{
+ uint64_t addr;
+
+ addr = XIVE_ESB_ADDR + ((uint64_t)index << (XIVE_PAGE_SHIFT + 1));
+ if (page == 1) {
+ addr += 1 << XIVE_PAGE_SHIFT;
+ }
+ return qtest_writel(qts, addr + offset, cpu_to_be32(val));
+}
+
+void get_nvp(QTestState *qts, uint32_t index, Xive2Nvp* nvp)
+{
+ uint64_t addr = XIVE_NVP_MEM + (uint64_t)index * sizeof(Xive2Nvp);
+ xive_get_struct(qts, addr, nvp, sizeof(Xive2Nvp));
+}
+
+void set_nvp(QTestState *qts, uint32_t index, uint8_t first)
+{
+ uint64_t nvp_addr;
+ Xive2Nvp nvp;
+ uint64_t report_addr;
+
+ nvp_addr = XIVE_NVP_MEM + (uint64_t)index * sizeof(Xive2Nvp);
+ report_addr = (XIVE_REPORT_MEM + (uint64_t)index * XIVE_REPORT_SIZE) >> 8;
+
+ memset(&nvp, 0, sizeof(nvp));
+ nvp.w0 = xive_set_field32(NVP2_W0_VALID, 0, 1);
+ nvp.w0 = xive_set_field32(NVP2_W0_PGOFIRST, nvp.w0, first);
+ nvp.w6 = xive_set_field32(NVP2_W6_REPORTING_LINE, nvp.w6,
+ (report_addr >> 24) & 0xfffffff);
+ nvp.w7 = xive_set_field32(NVP2_W7_REPORTING_LINE, nvp.w7,
+ report_addr & 0xffffff);
+ xive_copy_struct(qts, &nvp, nvp_addr, sizeof(nvp));
+}
+
+static uint64_t get_cl_pair_addr(Xive2Nvp *nvp)
+{
+ uint64_t upper = xive_get_field32(0x0fffffff, nvp->w6);
+ uint64_t lower = xive_get_field32(0xffffff00, nvp->w7);
+ return (upper << 32) | (lower << 8);
+}
+
+void get_cl_pair(QTestState *qts, Xive2Nvp *nvp, uint8_t *cl_pair)
+{
+ uint64_t addr = get_cl_pair_addr(nvp);
+ xive_get_struct(qts, addr, cl_pair, XIVE_REPORT_SIZE);
+}
+
+void set_cl_pair(QTestState *qts, Xive2Nvp *nvp, uint8_t *cl_pair)
+{
+ uint64_t addr = get_cl_pair_addr(nvp);
+ xive_copy_struct(qts, cl_pair, addr, XIVE_REPORT_SIZE);
+}
+
+void set_nvg(QTestState *qts, uint32_t index, uint8_t next)
+{
+ uint64_t nvg_addr;
+ Xive2Nvgc nvg;
+
+ nvg_addr = XIVE_NVG_MEM + (uint64_t)index * sizeof(Xive2Nvgc);
+
+ memset(&nvg, 0, sizeof(nvg));
+ nvg.w0 = xive_set_field32(NVGC2_W0_VALID, 0, 1);
+ nvg.w0 = xive_set_field32(NVGC2_W0_PGONEXT, nvg.w0, next);
+ xive_copy_struct(qts, &nvg, nvg_addr, sizeof(nvg));
+}
+
+void set_eas(QTestState *qts, uint32_t index, uint32_t end_index,
+ uint32_t data)
+{
+ uint64_t eas_addr;
+ Xive2Eas eas;
+
+ eas_addr = XIVE_EAS_MEM + (uint64_t)index * sizeof(Xive2Eas);
+
+ memset(&eas, 0, sizeof(eas));
+ eas.w = xive_set_field64(EAS2_VALID, 0, 1);
+ eas.w = xive_set_field64(EAS2_END_INDEX, eas.w, end_index);
+ eas.w = xive_set_field64(EAS2_END_DATA, eas.w, data);
+ xive_copy_struct(qts, &eas, eas_addr, sizeof(eas));
+}
+
+void set_end(QTestState *qts, uint32_t index, uint32_t nvp_index,
+ uint8_t priority, bool i)
+{
+ uint64_t end_addr, queue_addr, queue_hi, queue_lo;
+ uint8_t queue_size;
+ Xive2End end;
+
+ end_addr = XIVE_END_MEM + (uint64_t)index * sizeof(Xive2End);
+ queue_addr = xive_get_queue_addr(index);
+ queue_hi = (queue_addr >> 32) & END2_W2_EQ_ADDR_HI;
+ queue_lo = queue_addr & END2_W3_EQ_ADDR_LO;
+ queue_size = ctz16(XIVE_QUEUE_SIZE) - 12;
+
+ memset(&end, 0, sizeof(end));
+ end.w0 = xive_set_field32(END2_W0_VALID, 0, 1);
+ end.w0 = xive_set_field32(END2_W0_ENQUEUE, end.w0, 1);
+ end.w0 = xive_set_field32(END2_W0_UCOND_NOTIFY, end.w0, 1);
+ end.w0 = xive_set_field32(END2_W0_BACKLOG, end.w0, 1);
+
+ end.w1 = xive_set_field32(END2_W1_GENERATION, 0, 1);
+
+ end.w2 = cpu_to_be32(queue_hi);
+
+ end.w3 = cpu_to_be32(queue_lo);
+ end.w3 = xive_set_field32(END2_W3_QSIZE, end.w3, queue_size);
+
+ end.w6 = xive_set_field32(END2_W6_IGNORE, 0, i);
+ end.w6 = xive_set_field32(END2_W6_VP_OFFSET, end.w6, nvp_index);
+
+ end.w7 = xive_set_field32(END2_W7_F0_PRIORITY, 0, priority);
+ xive_copy_struct(qts, &end, end_addr, sizeof(end));
+}
+
diff --git a/tests/qtest/pnv-xive2-common.h b/tests/qtest/pnv-xive2-common.h
new file mode 100644
index 0000000..2077c05
--- /dev/null
+++ b/tests/qtest/pnv-xive2-common.h
@@ -0,0 +1,112 @@
+/*
+ * QTest testcase for PowerNV 10 interrupt controller (xive2)
+ *
+ * Copyright (c) 2024, IBM Corporation.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef TEST_PNV_XIVE2_COMMON_H
+#define TEST_PNV_XIVE2_COMMON_H
+
+#define PPC_BIT(bit) (0x8000000000000000ULL >> (bit))
+#define PPC_BIT32(bit) (0x80000000 >> (bit))
+#define PPC_BIT8(bit) (0x80 >> (bit))
+#define PPC_BITMASK(bs, be) ((PPC_BIT(bs) - PPC_BIT(be)) | PPC_BIT(bs))
+#define PPC_BITMASK32(bs, be) ((PPC_BIT32(bs) - PPC_BIT32(be)) | \
+ PPC_BIT32(bs))
+#include "qemu/bswap.h"
+#include "hw/intc/pnv_xive2_regs.h"
+#include "hw/ppc/xive_regs.h"
+#include "hw/ppc/xive2_regs.h"
+
+/*
+ * sizing:
+ * 128 interrupts
+ * => ESB BAR range: 16M
+ * 256 ENDs
+ * => END BAR range: 16M
+ * 256 VPs
+ * => NVPG,NVC BAR range: 32M
+ */
+#define MAX_IRQS 128
+#define MAX_ENDS 256
+#define MAX_VPS 256
+
+#define XIVE_PAGE_SHIFT 16
+
+#define XIVE_TRIGGER_PAGE 0
+#define XIVE_EOI_PAGE 1
+
+#define XIVE_IC_ADDR 0x0006030200000000ull
+#define XIVE_IC_TM_INDIRECT (XIVE_IC_ADDR + (256 << XIVE_PAGE_SHIFT))
+#define XIVE_IC_BAR ((0x3ull << 62) | XIVE_IC_ADDR)
+#define XIVE_TM_BAR 0xc006030203180000ull
+#define XIVE_ESB_ADDR 0x0006050000000000ull
+#define XIVE_ESB_BAR ((0x3ull << 62) | XIVE_ESB_ADDR)
+#define XIVE_END_BAR 0xc006060000000000ull
+#define XIVE_NVPG_ADDR 0x0006040000000000ull
+#define XIVE_NVPG_BAR ((0x3ull << 62) | XIVE_NVPG_ADDR)
+#define XIVE_NVC_ADDR 0x0006030208000000ull
+#define XIVE_NVC_BAR ((0x3ull << 62) | XIVE_NVC_ADDR)
+
+/*
+ * Memory layout
+ * A check is done when a table is configured to ensure that the max
+ * size of the resource fits in the table.
+ */
+#define XIVE_VST_SIZE 0x10000ull /* must be at least 4k */
+
+#define XIVE_MEM_START 0x10000000ull
+#define XIVE_ESB_MEM XIVE_MEM_START
+#define XIVE_EAS_MEM (XIVE_ESB_MEM + XIVE_VST_SIZE)
+#define XIVE_END_MEM (XIVE_EAS_MEM + XIVE_VST_SIZE)
+#define XIVE_NVP_MEM (XIVE_END_MEM + XIVE_VST_SIZE)
+#define XIVE_NVG_MEM (XIVE_NVP_MEM + XIVE_VST_SIZE)
+#define XIVE_NVC_MEM (XIVE_NVG_MEM + XIVE_VST_SIZE)
+#define XIVE_SYNC_MEM (XIVE_NVC_MEM + XIVE_VST_SIZE)
+#define XIVE_QUEUE_MEM (XIVE_SYNC_MEM + XIVE_VST_SIZE)
+#define XIVE_QUEUE_SIZE 4096 /* per End */
+#define XIVE_REPORT_MEM (XIVE_QUEUE_MEM + XIVE_QUEUE_SIZE * MAX_VPS)
+#define XIVE_REPORT_SIZE 256 /* two cache lines per NVP */
+#define XIVE_MEM_END (XIVE_REPORT_MEM + XIVE_REPORT_SIZE * MAX_VPS)
+
+#define P10_XSCOM_BASE 0x000603fc00000000ull
+#define XIVE_XSCOM 0x2010800ull
+
+#define XIVE_ESB_RESET 0b00
+#define XIVE_ESB_OFF 0b01
+#define XIVE_ESB_PENDING 0b10
+#define XIVE_ESB_QUEUED 0b11
+
+#define XIVE_ESB_GET 0x800
+#define XIVE_ESB_SET_PQ_00 0xc00 /* Load */
+#define XIVE_ESB_SET_PQ_01 0xd00 /* Load */
+#define XIVE_ESB_SET_PQ_10 0xe00 /* Load */
+#define XIVE_ESB_SET_PQ_11 0xf00 /* Load */
+
+#define XIVE_ESB_STORE_EOI 0x400 /* Store */
+
+
+extern uint64_t pnv_xive_xscom_read(QTestState *qts, uint32_t reg);
+extern void pnv_xive_xscom_write(QTestState *qts, uint32_t reg, uint64_t val);
+extern uint64_t xive_get_queue_addr(uint32_t end_index);
+extern uint8_t get_esb(QTestState *qts, uint32_t index, uint8_t page,
+ uint32_t offset);
+extern void set_esb(QTestState *qts, uint32_t index, uint8_t page,
+ uint32_t offset, uint32_t val);
+extern void get_nvp(QTestState *qts, uint32_t index, Xive2Nvp* nvp);
+extern void set_nvp(QTestState *qts, uint32_t index, uint8_t first);
+extern void get_cl_pair(QTestState *qts, Xive2Nvp *nvp, uint8_t *cl_pair);
+extern void set_cl_pair(QTestState *qts, Xive2Nvp *nvp, uint8_t *cl_pair);
+extern void set_nvg(QTestState *qts, uint32_t index, uint8_t next);
+extern void set_eas(QTestState *qts, uint32_t index, uint32_t end_index,
+ uint32_t data);
+extern void set_end(QTestState *qts, uint32_t index, uint32_t nvp_index,
+ uint8_t priority, bool i);
+
+
+void test_flush_sync_inject(QTestState *qts);
+void test_nvpg_bar(QTestState *qts);
+
+#endif /* TEST_PNV_XIVE2_COMMON_H */
diff --git a/tests/qtest/pnv-xive2-flush-sync.c b/tests/qtest/pnv-xive2-flush-sync.c
new file mode 100644
index 0000000..142826b
--- /dev/null
+++ b/tests/qtest/pnv-xive2-flush-sync.c
@@ -0,0 +1,205 @@
+/*
+ * QTest testcase for PowerNV 10 interrupt controller (xive2)
+ * - Test cache flush/queue sync injection
+ *
+ * Copyright (c) 2024, IBM Corporation.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#include "qemu/osdep.h"
+#include "libqtest.h"
+
+#include "pnv-xive2-common.h"
+#include "hw/intc/pnv_xive2_regs.h"
+#include "hw/ppc/xive_regs.h"
+#include "hw/ppc/xive2_regs.h"
+
+#define PNV_XIVE2_QUEUE_IPI 0x00
+#define PNV_XIVE2_QUEUE_HW 0x01
+#define PNV_XIVE2_QUEUE_NXC 0x02
+#define PNV_XIVE2_QUEUE_INT 0x03
+#define PNV_XIVE2_QUEUE_OS 0x04
+#define PNV_XIVE2_QUEUE_POOL 0x05
+#define PNV_XIVE2_QUEUE_HARD 0x06
+#define PNV_XIVE2_CACHE_ENDC 0x08
+#define PNV_XIVE2_CACHE_ESBC 0x09
+#define PNV_XIVE2_CACHE_EASC 0x0a
+#define PNV_XIVE2_QUEUE_NXC_LD_LCL_NCO 0x10
+#define PNV_XIVE2_QUEUE_NXC_LD_LCL_CO 0x11
+#define PNV_XIVE2_QUEUE_NXC_ST_LCL_NCI 0x12
+#define PNV_XIVE2_QUEUE_NXC_ST_LCL_CI 0x13
+#define PNV_XIVE2_QUEUE_NXC_ST_RMT_NCI 0x14
+#define PNV_XIVE2_QUEUE_NXC_ST_RMT_CI 0x15
+#define PNV_XIVE2_CACHE_NXC 0x18
+
+#define PNV_XIVE2_SYNC_IPI 0x000
+#define PNV_XIVE2_SYNC_HW 0x080
+#define PNV_XIVE2_SYNC_NxC 0x100
+#define PNV_XIVE2_SYNC_INT 0x180
+#define PNV_XIVE2_SYNC_OS_ESC 0x200
+#define PNV_XIVE2_SYNC_POOL_ESC 0x280
+#define PNV_XIVE2_SYNC_HARD_ESC 0x300
+#define PNV_XIVE2_SYNC_NXC_LD_LCL_NCO 0x800
+#define PNV_XIVE2_SYNC_NXC_LD_LCL_CO 0x880
+#define PNV_XIVE2_SYNC_NXC_ST_LCL_NCI 0x900
+#define PNV_XIVE2_SYNC_NXC_ST_LCL_CI 0x980
+#define PNV_XIVE2_SYNC_NXC_ST_RMT_NCI 0xA00
+#define PNV_XIVE2_SYNC_NXC_ST_RMT_CI 0xA80
+
+
+static uint64_t get_sync_addr(uint32_t src_pir, int ic_topo_id, int type)
+{
+ int thread_nr = src_pir & 0x7f;
+ uint64_t addr = XIVE_SYNC_MEM + thread_nr * 512 + ic_topo_id * 32 + type;
+ return addr;
+}
+
+static uint8_t get_sync(QTestState *qts, uint32_t src_pir, int ic_topo_id,
+ int type)
+{
+ uint64_t addr = get_sync_addr(src_pir, ic_topo_id, type);
+ return qtest_readb(qts, addr);
+}
+
+static void clr_sync(QTestState *qts, uint32_t src_pir, int ic_topo_id,
+ int type)
+{
+ uint64_t addr = get_sync_addr(src_pir, ic_topo_id, type);
+ qtest_writeb(qts, addr, 0x0);
+}
+
+static void inject_cache_flush(QTestState *qts, int ic_topo_id,
+ uint64_t scom_addr)
+{
+ (void)ic_topo_id;
+ pnv_xive_xscom_write(qts, scom_addr, 0);
+}
+
+static void inject_queue_sync(QTestState *qts, int ic_topo_id, uint64_t offset)
+{
+ (void)ic_topo_id;
+ uint64_t addr = XIVE_IC_ADDR + (VST_SYNC << XIVE_PAGE_SHIFT) + offset;
+ qtest_writeq(qts, addr, 0);
+}
+
+static void inject_op(QTestState *qts, int ic_topo_id, int type)
+{
+ switch (type) {
+ case PNV_XIVE2_QUEUE_IPI:
+ inject_queue_sync(qts, ic_topo_id, PNV_XIVE2_SYNC_IPI);
+ break;
+ case PNV_XIVE2_QUEUE_HW:
+ inject_queue_sync(qts, ic_topo_id, PNV_XIVE2_SYNC_HW);
+ break;
+ case PNV_XIVE2_QUEUE_NXC:
+ inject_queue_sync(qts, ic_topo_id, PNV_XIVE2_SYNC_NxC);
+ break;
+ case PNV_XIVE2_QUEUE_INT:
+ inject_queue_sync(qts, ic_topo_id, PNV_XIVE2_SYNC_INT);
+ break;
+ case PNV_XIVE2_QUEUE_OS:
+ inject_queue_sync(qts, ic_topo_id, PNV_XIVE2_SYNC_OS_ESC);
+ break;
+ case PNV_XIVE2_QUEUE_POOL:
+ inject_queue_sync(qts, ic_topo_id, PNV_XIVE2_SYNC_POOL_ESC);
+ break;
+ case PNV_XIVE2_QUEUE_HARD:
+ inject_queue_sync(qts, ic_topo_id, PNV_XIVE2_SYNC_HARD_ESC);
+ break;
+ case PNV_XIVE2_CACHE_ENDC:
+ inject_cache_flush(qts, ic_topo_id, X_VC_ENDC_FLUSH_INJECT);
+ break;
+ case PNV_XIVE2_CACHE_ESBC:
+ inject_cache_flush(qts, ic_topo_id, X_VC_ESBC_FLUSH_INJECT);
+ break;
+ case PNV_XIVE2_CACHE_EASC:
+ inject_cache_flush(qts, ic_topo_id, X_VC_EASC_FLUSH_INJECT);
+ break;
+ case PNV_XIVE2_QUEUE_NXC_LD_LCL_NCO:
+ inject_queue_sync(qts, ic_topo_id, PNV_XIVE2_SYNC_NXC_LD_LCL_NCO);
+ break;
+ case PNV_XIVE2_QUEUE_NXC_LD_LCL_CO:
+ inject_queue_sync(qts, ic_topo_id, PNV_XIVE2_SYNC_NXC_LD_LCL_CO);
+ break;
+ case PNV_XIVE2_QUEUE_NXC_ST_LCL_NCI:
+ inject_queue_sync(qts, ic_topo_id, PNV_XIVE2_SYNC_NXC_ST_LCL_NCI);
+ break;
+ case PNV_XIVE2_QUEUE_NXC_ST_LCL_CI:
+ inject_queue_sync(qts, ic_topo_id, PNV_XIVE2_SYNC_NXC_ST_LCL_CI);
+ break;
+ case PNV_XIVE2_QUEUE_NXC_ST_RMT_NCI:
+ inject_queue_sync(qts, ic_topo_id, PNV_XIVE2_SYNC_NXC_ST_RMT_NCI);
+ break;
+ case PNV_XIVE2_QUEUE_NXC_ST_RMT_CI:
+ inject_queue_sync(qts, ic_topo_id, PNV_XIVE2_SYNC_NXC_ST_RMT_CI);
+ break;
+ case PNV_XIVE2_CACHE_NXC:
+ inject_cache_flush(qts, ic_topo_id, X_PC_NXC_FLUSH_INJECT);
+ break;
+ default:
+ g_assert_not_reached();
+ break;
+ }
+}
+
+const uint8_t xive_inject_tests[] = {
+ PNV_XIVE2_QUEUE_IPI,
+ PNV_XIVE2_QUEUE_HW,
+ PNV_XIVE2_QUEUE_NXC,
+ PNV_XIVE2_QUEUE_INT,
+ PNV_XIVE2_QUEUE_OS,
+ PNV_XIVE2_QUEUE_POOL,
+ PNV_XIVE2_QUEUE_HARD,
+ PNV_XIVE2_CACHE_ENDC,
+ PNV_XIVE2_CACHE_ESBC,
+ PNV_XIVE2_CACHE_EASC,
+ PNV_XIVE2_QUEUE_NXC_LD_LCL_NCO,
+ PNV_XIVE2_QUEUE_NXC_LD_LCL_CO,
+ PNV_XIVE2_QUEUE_NXC_ST_LCL_NCI,
+ PNV_XIVE2_QUEUE_NXC_ST_LCL_CI,
+ PNV_XIVE2_QUEUE_NXC_ST_RMT_NCI,
+ PNV_XIVE2_QUEUE_NXC_ST_RMT_CI,
+ PNV_XIVE2_CACHE_NXC,
+};
+
+void test_flush_sync_inject(QTestState *qts)
+{
+ int ic_topo_id = 0;
+
+ /*
+ * Writes performed by qtest are not done in the context of a thread.
+ * This means that QEMU XIVE code doesn't have a way to determine what
+ * thread is originating the write. In order to allow for some testing,
+ * QEMU XIVE code will assume a PIR of 0 when unable to determine the
+ * source thread for cache flush and queue sync inject operations.
+ * See hw/intc/pnv_xive2.c: pnv_xive2_inject_notify() for details.
+ */
+ int src_pir = 0;
+ int test_nr;
+ uint8_t byte;
+
+ g_test_message("=========================================================");
+ g_test_message("Starting cache flush/queue sync injection tests...");
+
+ for (test_nr = 0; test_nr < sizeof(xive_inject_tests);
+ test_nr++) {
+ int op_type = xive_inject_tests[test_nr];
+
+ g_test_message("Running test %d", test_nr);
+
+ /* start with status byte set to 0 */
+ clr_sync(qts, src_pir, ic_topo_id, op_type);
+ byte = get_sync(qts, src_pir, ic_topo_id, op_type);
+ g_assert_cmphex(byte, ==, 0);
+
+ /* request cache flush or queue sync operation */
+ inject_op(qts, ic_topo_id, op_type);
+
+ /* verify that status byte was written to 0xff */
+ byte = get_sync(qts, src_pir, ic_topo_id, op_type);
+ g_assert_cmphex(byte, ==, 0xff);
+
+ clr_sync(qts, src_pir, ic_topo_id, op_type);
+ }
+}
+
diff --git a/tests/qtest/pnv-xive2-nvpg_bar.c b/tests/qtest/pnv-xive2-nvpg_bar.c
new file mode 100644
index 0000000..6ac8d36
--- /dev/null
+++ b/tests/qtest/pnv-xive2-nvpg_bar.c
@@ -0,0 +1,152 @@
+/*
+ * QTest testcase for PowerNV 10 interrupt controller (xive2)
+ * - Test NVPG BAR MMIO operations
+ *
+ * Copyright (c) 2024, IBM Corporation.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#include "qemu/osdep.h"
+#include "libqtest.h"
+
+#include "pnv-xive2-common.h"
+
+#define NVPG_BACKLOG_OP_SHIFT 10
+#define NVPG_BACKLOG_PRIO_SHIFT 4
+
+#define XIVE_PRIORITY_MAX 7
+
+enum NVx {
+ NVP,
+ NVG,
+ NVC
+};
+
+typedef enum {
+ INCR_STORE = 0b100,
+ INCR_LOAD = 0b000,
+ DECR_STORE = 0b101,
+ DECR_LOAD = 0b001,
+ READ_x = 0b010,
+ READ_y = 0b011,
+} backlog_op;
+
+static uint32_t nvpg_backlog_op(QTestState *qts, backlog_op op,
+ enum NVx type, uint64_t index,
+ uint8_t priority, uint8_t delta)
+{
+ uint64_t addr, offset;
+ uint32_t count = 0;
+
+ switch (type) {
+ case NVP:
+ addr = XIVE_NVPG_ADDR + (index << (XIVE_PAGE_SHIFT + 1));
+ break;
+ case NVG:
+ addr = XIVE_NVPG_ADDR + (index << (XIVE_PAGE_SHIFT + 1)) +
+ (1 << XIVE_PAGE_SHIFT);
+ break;
+ case NVC:
+ addr = XIVE_NVC_ADDR + (index << XIVE_PAGE_SHIFT);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ offset = (op & 0b11) << NVPG_BACKLOG_OP_SHIFT;
+ offset |= priority << NVPG_BACKLOG_PRIO_SHIFT;
+ if (op >> 2) {
+ qtest_writeb(qts, addr + offset, delta);
+ } else {
+ count = qtest_readw(qts, addr + offset);
+ }
+ return count;
+}
+
+void test_nvpg_bar(QTestState *qts)
+{
+ uint32_t nvp_target = 0x11;
+ uint32_t group_target = 0x17; /* size 16 */
+ uint32_t vp_irq = 33, group_irq = 47;
+ uint32_t vp_end = 3, group_end = 97;
+ uint32_t vp_irq_data = 0x33333333;
+ uint32_t group_irq_data = 0x66666666;
+ uint8_t vp_priority = 0, group_priority = 5;
+ uint32_t vp_count[XIVE_PRIORITY_MAX + 1] = { 0 };
+ uint32_t group_count[XIVE_PRIORITY_MAX + 1] = { 0 };
+ uint32_t count, delta;
+ uint8_t i;
+
+ g_test_message("=========================================================");
+ g_test_message("Testing NVPG BAR operations");
+
+ set_nvg(qts, group_target, 0);
+ set_nvp(qts, nvp_target, 0x04);
+ set_nvp(qts, group_target, 0x04);
+
+ /*
+ * Setup: trigger a VP-specific interrupt and a group interrupt
+ * so that the backlog counters are initialized to something else
+ * than 0 for at least one priority level
+ */
+ set_eas(qts, vp_irq, vp_end, vp_irq_data);
+ set_end(qts, vp_end, nvp_target, vp_priority, false /* group */);
+
+ set_eas(qts, group_irq, group_end, group_irq_data);
+ set_end(qts, group_end, group_target, group_priority, true /* group */);
+
+ get_esb(qts, vp_irq, XIVE_EOI_PAGE, XIVE_ESB_SET_PQ_00);
+ set_esb(qts, vp_irq, XIVE_TRIGGER_PAGE, 0, 0);
+ vp_count[vp_priority]++;
+
+ get_esb(qts, group_irq, XIVE_EOI_PAGE, XIVE_ESB_SET_PQ_00);
+ set_esb(qts, group_irq, XIVE_TRIGGER_PAGE, 0, 0);
+ group_count[group_priority]++;
+
+ /* check the initial counters */
+ for (i = 0; i <= XIVE_PRIORITY_MAX; i++) {
+ count = nvpg_backlog_op(qts, READ_x, NVP, nvp_target, i, 0);
+ g_assert_cmpuint(count, ==, vp_count[i]);
+
+ count = nvpg_backlog_op(qts, READ_y, NVG, group_target, i, 0);
+ g_assert_cmpuint(count, ==, group_count[i]);
+ }
+
+ /* do a few ops on the VP. Counter can only be 0 and 1 */
+ vp_priority = 2;
+ delta = 7;
+ nvpg_backlog_op(qts, INCR_STORE, NVP, nvp_target, vp_priority, delta);
+ vp_count[vp_priority] = 1;
+ count = nvpg_backlog_op(qts, INCR_LOAD, NVP, nvp_target, vp_priority, 0);
+ g_assert_cmpuint(count, ==, vp_count[vp_priority]);
+ count = nvpg_backlog_op(qts, READ_y, NVP, nvp_target, vp_priority, 0);
+ g_assert_cmpuint(count, ==, vp_count[vp_priority]);
+
+ count = nvpg_backlog_op(qts, DECR_LOAD, NVP, nvp_target, vp_priority, 0);
+ g_assert_cmpuint(count, ==, vp_count[vp_priority]);
+ vp_count[vp_priority] = 0;
+ nvpg_backlog_op(qts, DECR_STORE, NVP, nvp_target, vp_priority, delta);
+ count = nvpg_backlog_op(qts, READ_x, NVP, nvp_target, vp_priority, 0);
+ g_assert_cmpuint(count, ==, vp_count[vp_priority]);
+
+ /* do a few ops on the group */
+ group_priority = 2;
+ delta = 9;
+ /* can't go negative */
+ nvpg_backlog_op(qts, DECR_STORE, NVG, group_target, group_priority, delta);
+ count = nvpg_backlog_op(qts, READ_y, NVG, group_target, group_priority, 0);
+ g_assert_cmpuint(count, ==, 0);
+ nvpg_backlog_op(qts, INCR_STORE, NVG, group_target, group_priority, delta);
+ group_count[group_priority] += delta;
+ count = nvpg_backlog_op(qts, INCR_LOAD, NVG, group_target,
+ group_priority, delta);
+ g_assert_cmpuint(count, ==, group_count[group_priority]);
+ group_count[group_priority]++;
+
+ count = nvpg_backlog_op(qts, DECR_LOAD, NVG, group_target,
+ group_priority, delta);
+ g_assert_cmpuint(count, ==, group_count[group_priority]);
+ group_count[group_priority]--;
+ count = nvpg_backlog_op(qts, READ_x, NVG, group_target, group_priority, 0);
+ g_assert_cmpuint(count, ==, group_count[group_priority]);
+}
diff --git a/tests/qtest/pnv-xive2-test.c b/tests/qtest/pnv-xive2-test.c
new file mode 100644
index 0000000..5313d4e
--- /dev/null
+++ b/tests/qtest/pnv-xive2-test.c
@@ -0,0 +1,585 @@
+/*
+ * QTest testcase for PowerNV 10 interrupt controller (xive2)
+ * - Test irq to hardware thread
+ * - Test 'Pull Thread Context to Odd Thread Reporting Line'
+ * - Test irq to hardware group
+ * - Test irq to hardware group going through backlog
+ * - Test irq to pool thread
+ *
+ * Copyright (c) 2024, IBM Corporation.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#include "qemu/osdep.h"
+#include "libqtest.h"
+
+#include "pnv-xive2-common.h"
+#include "hw/intc/pnv_xive2_regs.h"
+#include "hw/ppc/xive_regs.h"
+#include "hw/ppc/xive2_regs.h"
+
+#define SMT 4 /* some tests will break if less than 4 */
+
+
+static void set_table(QTestState *qts, uint64_t type, uint64_t addr)
+{
+ uint64_t vsd, size, log_size;
+
+ /*
+ * First, let's make sure that all the resources used fit in the
+ * given table.
+ */
+ switch (type) {
+ case VST_ESB:
+ size = MAX_IRQS / 4;
+ break;
+ case VST_EAS:
+ size = MAX_IRQS * 8;
+ break;
+ case VST_END:
+ size = MAX_ENDS * 32;
+ break;
+ case VST_NVP:
+ case VST_NVG:
+ case VST_NVC:
+ size = MAX_VPS * 32;
+ break;
+ case VST_SYNC:
+ size = 64 * 1024;
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ g_assert_cmpuint(size, <=, XIVE_VST_SIZE);
+ log_size = ctzl(XIVE_VST_SIZE) - 12;
+
+ vsd = ((uint64_t) VSD_MODE_EXCLUSIVE) << 62 | addr | log_size;
+ pnv_xive_xscom_write(qts, X_VC_VSD_TABLE_ADDR, type << 48);
+ pnv_xive_xscom_write(qts, X_VC_VSD_TABLE_DATA, vsd);
+
+ if (type != VST_EAS && type != VST_IC && type != VST_ERQ) {
+ pnv_xive_xscom_write(qts, X_PC_VSD_TABLE_ADDR, type << 48);
+ pnv_xive_xscom_write(qts, X_PC_VSD_TABLE_DATA, vsd);
+ }
+}
+
+static void set_tima8(QTestState *qts, uint32_t pir, uint32_t offset,
+ uint8_t b)
+{
+ uint64_t ic_addr;
+
+ ic_addr = XIVE_IC_TM_INDIRECT + (pir << XIVE_PAGE_SHIFT);
+ qtest_writeb(qts, ic_addr + offset, b);
+}
+
+static void set_tima32(QTestState *qts, uint32_t pir, uint32_t offset,
+ uint32_t l)
+{
+ uint64_t ic_addr;
+
+ ic_addr = XIVE_IC_TM_INDIRECT + (pir << XIVE_PAGE_SHIFT);
+ qtest_writel(qts, ic_addr + offset, l);
+}
+
+static uint8_t get_tima8(QTestState *qts, uint32_t pir, uint32_t offset)
+{
+ uint64_t ic_addr;
+
+ ic_addr = XIVE_IC_TM_INDIRECT + (pir << XIVE_PAGE_SHIFT);
+ return qtest_readb(qts, ic_addr + offset);
+}
+
+static uint16_t get_tima16(QTestState *qts, uint32_t pir, uint32_t offset)
+{
+ uint64_t ic_addr;
+
+ ic_addr = XIVE_IC_TM_INDIRECT + (pir << XIVE_PAGE_SHIFT);
+ return qtest_readw(qts, ic_addr + offset);
+}
+
+static uint32_t get_tima32(QTestState *qts, uint32_t pir, uint32_t offset)
+{
+ uint64_t ic_addr;
+
+ ic_addr = XIVE_IC_TM_INDIRECT + (pir << XIVE_PAGE_SHIFT);
+ return qtest_readl(qts, ic_addr + offset);
+}
+
+static void reset_pool_threads(QTestState *qts)
+{
+ uint8_t first_group = 0;
+ int i;
+
+ for (i = 0; i < SMT; i++) {
+ uint32_t nvp_idx = 0x100 + i;
+ set_nvp(qts, nvp_idx, first_group);
+ set_tima32(qts, i, TM_QW2_HV_POOL + TM_WORD0, 0x000000ff);
+ set_tima32(qts, i, TM_QW2_HV_POOL + TM_WORD1, 0);
+ set_tima32(qts, i, TM_QW2_HV_POOL + TM_WORD2, TM_QW2W2_VP | nvp_idx);
+ }
+}
+
+static void reset_hw_threads(QTestState *qts)
+{
+ uint8_t first_group = 0;
+ uint32_t w1 = 0x000000ff;
+ int i;
+
+ if (SMT >= 4) {
+ /* define 2 groups of 2, part of a bigger group of size 4 */
+ set_nvg(qts, 0x80, 0x02);
+ set_nvg(qts, 0x82, 0x02);
+ set_nvg(qts, 0x81, 0);
+ first_group = 0x01;
+ w1 = 0x000300ff;
+ }
+
+ for (i = 0; i < SMT; i++) {
+ set_nvp(qts, 0x80 + i, first_group);
+ set_tima32(qts, i, TM_QW3_HV_PHYS + TM_WORD0, 0x00ff00ff);
+ set_tima32(qts, i, TM_QW3_HV_PHYS + TM_WORD1, w1);
+ set_tima32(qts, i, TM_QW3_HV_PHYS + TM_WORD2, 0x80000000);
+ }
+}
+
+static void reset_state(QTestState *qts)
+{
+ size_t mem_used = XIVE_MEM_END - XIVE_MEM_START;
+
+ qtest_memset(qts, XIVE_MEM_START, 0, mem_used);
+ reset_hw_threads(qts);
+ reset_pool_threads(qts);
+}
+
+static void init_xive(QTestState *qts)
+{
+ uint64_t val1, val2, range;
+
+ /*
+ * We can take a few shortcuts here, as we know the default values
+ * used for xive initialization
+ */
+
+ /*
+ * Set the BARs.
+ * We reuse the same values used by firmware to ease debug.
+ */
+ pnv_xive_xscom_write(qts, X_CQ_IC_BAR, XIVE_IC_BAR);
+ pnv_xive_xscom_write(qts, X_CQ_TM_BAR, XIVE_TM_BAR);
+
+ /* ESB and NVPG use 2 pages per resource. The others only one page */
+ range = (MAX_IRQS << 17) >> 25;
+ val1 = XIVE_ESB_BAR | range;
+ pnv_xive_xscom_write(qts, X_CQ_ESB_BAR, val1);
+
+ range = (MAX_ENDS << 16) >> 25;
+ val1 = XIVE_END_BAR | range;
+ pnv_xive_xscom_write(qts, X_CQ_END_BAR, val1);
+
+ range = (MAX_VPS << 17) >> 25;
+ val1 = XIVE_NVPG_BAR | range;
+ pnv_xive_xscom_write(qts, X_CQ_NVPG_BAR, val1);
+
+ range = (MAX_VPS << 16) >> 25;
+ val1 = XIVE_NVC_BAR | range;
+ pnv_xive_xscom_write(qts, X_CQ_NVC_BAR, val1);
+
+ /*
+ * Enable hw threads.
+ * We check the value written. Useless with current
+ * implementation, but it validates the xscom read path and it's
+ * what the hardware procedure says
+ */
+ val1 = 0xF000000000000000ull; /* core 0, 4 threads */
+ pnv_xive_xscom_write(qts, X_TCTXT_EN0, val1);
+ val2 = pnv_xive_xscom_read(qts, X_TCTXT_EN0);
+ g_assert_cmphex(val1, ==, val2);
+
+ /* Memory tables */
+ set_table(qts, VST_ESB, XIVE_ESB_MEM);
+ set_table(qts, VST_EAS, XIVE_EAS_MEM);
+ set_table(qts, VST_END, XIVE_END_MEM);
+ set_table(qts, VST_NVP, XIVE_NVP_MEM);
+ set_table(qts, VST_NVG, XIVE_NVG_MEM);
+ set_table(qts, VST_NVC, XIVE_NVC_MEM);
+ set_table(qts, VST_SYNC, XIVE_SYNC_MEM);
+
+ reset_hw_threads(qts);
+ reset_pool_threads(qts);
+}
+
+static void test_hw_irq(QTestState *qts)
+{
+ uint32_t irq = 2;
+ uint32_t irq_data = 0x600df00d;
+ uint32_t end_index = 5;
+ uint32_t target_pir = 1;
+ uint32_t target_nvp = 0x80 + target_pir;
+ uint8_t priority = 5;
+ uint32_t reg32;
+ uint16_t reg16;
+ uint8_t pq, nsr, cppr;
+
+ g_test_message("=========================================================");
+ g_test_message("Testing irq %d to hardware thread %d", irq, target_pir);
+
+ /* irq config */
+ set_eas(qts, irq, end_index, irq_data);
+ set_end(qts, end_index, target_nvp, priority, false /* group */);
+
+ /* enable and trigger irq */
+ get_esb(qts, irq, XIVE_EOI_PAGE, XIVE_ESB_SET_PQ_00);
+ set_esb(qts, irq, XIVE_TRIGGER_PAGE, 0, 0);
+
+ /* check irq is raised on cpu */
+ pq = get_esb(qts, irq, XIVE_EOI_PAGE, XIVE_ESB_GET);
+ g_assert_cmpuint(pq, ==, XIVE_ESB_PENDING);
+
+ reg32 = get_tima32(qts, target_pir, TM_QW3_HV_PHYS + TM_WORD0);
+ nsr = reg32 >> 24;
+ cppr = (reg32 >> 16) & 0xFF;
+ g_assert_cmphex(nsr, ==, 0x80);
+ g_assert_cmphex(cppr, ==, 0xFF);
+
+ /* ack the irq */
+ reg16 = get_tima16(qts, target_pir, TM_SPC_ACK_HV_REG);
+ nsr = reg16 >> 8;
+ cppr = reg16 & 0xFF;
+ g_assert_cmphex(nsr, ==, 0x80);
+ g_assert_cmphex(cppr, ==, priority);
+
+ /* check irq data is what was configured */
+ reg32 = qtest_readl(qts, xive_get_queue_addr(end_index));
+ g_assert_cmphex((reg32 & 0x7fffffff), ==, (irq_data & 0x7fffffff));
+
+ /* End Of Interrupt */
+ set_esb(qts, irq, XIVE_EOI_PAGE, XIVE_ESB_STORE_EOI, 0);
+ pq = get_esb(qts, irq, XIVE_EOI_PAGE, XIVE_ESB_GET);
+ g_assert_cmpuint(pq, ==, XIVE_ESB_RESET);
+
+ /* reset CPPR */
+ set_tima8(qts, target_pir, TM_QW3_HV_PHYS + TM_CPPR, 0xFF);
+ reg32 = get_tima32(qts, target_pir, TM_QW3_HV_PHYS + TM_WORD0);
+ nsr = reg32 >> 24;
+ cppr = (reg32 >> 16) & 0xFF;
+ g_assert_cmphex(nsr, ==, 0x00);
+ g_assert_cmphex(cppr, ==, 0xFF);
+}
+
+static void test_pool_irq(QTestState *qts)
+{
+ uint32_t irq = 2;
+ uint32_t irq_data = 0x600d0d06;
+ uint32_t end_index = 5;
+ uint32_t target_pir = 1;
+ uint32_t target_nvp = 0x100 + target_pir;
+ uint8_t priority = 5;
+ uint32_t reg32;
+ uint16_t reg16;
+ uint8_t pq, nsr, cppr, ipb;
+
+ g_test_message("=========================================================");
+ g_test_message("Testing irq %d to pool thread %d", irq, target_pir);
+
+ /* irq config */
+ set_eas(qts, irq, end_index, irq_data);
+ set_end(qts, end_index, target_nvp, priority, false /* group */);
+
+ /* enable and trigger irq */
+ get_esb(qts, irq, XIVE_EOI_PAGE, XIVE_ESB_SET_PQ_00);
+ set_esb(qts, irq, XIVE_TRIGGER_PAGE, 0, 0);
+
+ /* check irq is raised on cpu */
+ pq = get_esb(qts, irq, XIVE_EOI_PAGE, XIVE_ESB_GET);
+ g_assert_cmpuint(pq, ==, XIVE_ESB_PENDING);
+
+ /* check TIMA values in the PHYS ring (shared by POOL ring) */
+ reg32 = get_tima32(qts, target_pir, TM_QW3_HV_PHYS + TM_WORD0);
+ nsr = reg32 >> 24;
+ cppr = (reg32 >> 16) & 0xFF;
+ g_assert_cmphex(nsr, ==, 0x40);
+ g_assert_cmphex(cppr, ==, 0xFF);
+
+ /* check TIMA values in the POOL ring */
+ reg32 = get_tima32(qts, target_pir, TM_QW2_HV_POOL + TM_WORD0);
+ nsr = reg32 >> 24;
+ cppr = (reg32 >> 16) & 0xFF;
+ ipb = (reg32 >> 8) & 0xFF;
+ g_assert_cmphex(nsr, ==, 0);
+ g_assert_cmphex(cppr, ==, 0);
+ g_assert_cmphex(ipb, ==, 0x80 >> priority);
+
+ /* ack the irq */
+ reg16 = get_tima16(qts, target_pir, TM_SPC_ACK_HV_REG);
+ nsr = reg16 >> 8;
+ cppr = reg16 & 0xFF;
+ g_assert_cmphex(nsr, ==, 0x40);
+ g_assert_cmphex(cppr, ==, priority);
+
+ /* check irq data is what was configured */
+ reg32 = qtest_readl(qts, xive_get_queue_addr(end_index));
+ g_assert_cmphex((reg32 & 0x7fffffff), ==, (irq_data & 0x7fffffff));
+
+ /* check IPB is cleared in the POOL ring */
+ reg32 = get_tima32(qts, target_pir, TM_QW2_HV_POOL + TM_WORD0);
+ ipb = (reg32 >> 8) & 0xFF;
+ g_assert_cmphex(ipb, ==, 0);
+
+ /* End Of Interrupt */
+ set_esb(qts, irq, XIVE_EOI_PAGE, XIVE_ESB_STORE_EOI, 0);
+ pq = get_esb(qts, irq, XIVE_EOI_PAGE, XIVE_ESB_GET);
+ g_assert_cmpuint(pq, ==, XIVE_ESB_RESET);
+
+ /* reset CPPR */
+ set_tima8(qts, target_pir, TM_QW3_HV_PHYS + TM_CPPR, 0xFF);
+ reg32 = get_tima32(qts, target_pir, TM_QW3_HV_PHYS + TM_WORD0);
+ nsr = reg32 >> 24;
+ cppr = (reg32 >> 16) & 0xFF;
+ g_assert_cmphex(nsr, ==, 0x00);
+ g_assert_cmphex(cppr, ==, 0xFF);
+}
+
+#define XIVE_ODD_CL 0x80
+static void test_pull_thread_ctx_to_odd_thread_cl(QTestState *qts)
+{
+ uint32_t target_pir = 1;
+ uint32_t target_nvp = 0x80 + target_pir;
+ Xive2Nvp nvp;
+ uint8_t cl_pair[XIVE_REPORT_SIZE];
+ uint32_t qw1w0, qw3w0, qw1w2, qw2w2;
+ uint8_t qw3b8;
+ uint32_t cl_word;
+ uint32_t word2;
+
+ g_test_message("=========================================================");
+ g_test_message("Testing 'Pull Thread Context to Odd Thread Reporting " \
+ "Line'");
+
+ /* clear odd cache line prior to pull operation */
+ memset(cl_pair, 0, sizeof(cl_pair));
+ get_nvp(qts, target_nvp, &nvp);
+ set_cl_pair(qts, &nvp, cl_pair);
+
+ /* Read some values from TIMA that we expect to see in cacheline */
+ qw1w0 = get_tima32(qts, target_pir, TM_QW1_OS + TM_WORD0);
+ qw3w0 = get_tima32(qts, target_pir, TM_QW3_HV_PHYS + TM_WORD0);
+ qw1w2 = get_tima32(qts, target_pir, TM_QW1_OS + TM_WORD2);
+ qw2w2 = get_tima32(qts, target_pir, TM_QW2_HV_POOL + TM_WORD2);
+ qw3b8 = get_tima8(qts, target_pir, TM_QW3_HV_PHYS + TM_WORD2);
+
+ /* Execute the pull operation */
+ set_tima8(qts, target_pir, TM_SPC_PULL_PHYS_CTX_OL, 0);
+
+ /* Verify odd cache line values match TIMA after pull operation */
+ get_cl_pair(qts, &nvp, cl_pair);
+ memcpy(&cl_word, &cl_pair[XIVE_ODD_CL + TM_QW1_OS + TM_WORD0], 4);
+ g_assert_cmphex(qw1w0, ==, be32_to_cpu(cl_word));
+ memcpy(&cl_word, &cl_pair[XIVE_ODD_CL + TM_QW3_HV_PHYS + TM_WORD0], 4);
+ g_assert_cmphex(qw3w0, ==, be32_to_cpu(cl_word));
+ memcpy(&cl_word, &cl_pair[XIVE_ODD_CL + TM_QW1_OS + TM_WORD2], 4);
+ g_assert_cmphex(qw1w2, ==, be32_to_cpu(cl_word));
+ memcpy(&cl_word, &cl_pair[XIVE_ODD_CL + TM_QW2_HV_POOL + TM_WORD2], 4);
+ g_assert_cmphex(qw2w2, ==, be32_to_cpu(cl_word));
+ g_assert_cmphex(qw3b8, ==,
+ cl_pair[XIVE_ODD_CL + TM_QW3_HV_PHYS + TM_WORD2]);
+
+ /* Verify that all TIMA valid bits for target thread are cleared */
+ word2 = get_tima32(qts, target_pir, TM_QW1_OS + TM_WORD2);
+ g_assert_cmphex(xive_get_field32(TM_QW1W2_VO, word2), ==, 0);
+ word2 = get_tima32(qts, target_pir, TM_QW2_HV_POOL + TM_WORD2);
+ g_assert_cmphex(xive_get_field32(TM_QW2W2_VP, word2), ==, 0);
+ word2 = get_tima32(qts, target_pir, TM_QW3_HV_PHYS + TM_WORD2);
+ g_assert_cmphex(xive_get_field32(TM_QW3W2_VT, word2), ==, 0);
+}
+
+static void test_hw_group_irq(QTestState *qts)
+{
+ uint32_t irq = 100;
+ uint32_t irq_data = 0xdeadbeef;
+ uint32_t end_index = 23;
+ uint32_t chosen_one;
+ uint32_t target_nvp = 0x81; /* group size = 4 */
+ uint8_t priority = 6;
+ uint32_t reg32;
+ uint16_t reg16;
+ uint8_t pq, nsr, cppr;
+
+ g_test_message("=========================================================");
+ g_test_message("Testing irq %d to hardware group of size 4", irq);
+
+ /* irq config */
+ set_eas(qts, irq, end_index, irq_data);
+ set_end(qts, end_index, target_nvp, priority, true /* group */);
+
+ /* enable and trigger irq */
+ get_esb(qts, irq, XIVE_EOI_PAGE, XIVE_ESB_SET_PQ_00);
+ set_esb(qts, irq, XIVE_TRIGGER_PAGE, 0, 0);
+
+ /* check irq is raised on cpu */
+ pq = get_esb(qts, irq, XIVE_EOI_PAGE, XIVE_ESB_GET);
+ g_assert_cmpuint(pq, ==, XIVE_ESB_PENDING);
+
+ /* find the targeted vCPU */
+ for (chosen_one = 0; chosen_one < SMT; chosen_one++) {
+ reg32 = get_tima32(qts, chosen_one, TM_QW3_HV_PHYS + TM_WORD0);
+ nsr = reg32 >> 24;
+ if (nsr == 0x82) {
+ break;
+ }
+ }
+ g_assert_cmphex(chosen_one, <, SMT);
+ cppr = (reg32 >> 16) & 0xFF;
+ g_assert_cmphex(nsr, ==, 0x82);
+ g_assert_cmphex(cppr, ==, 0xFF);
+
+ /* ack the irq */
+ reg16 = get_tima16(qts, chosen_one, TM_SPC_ACK_HV_REG);
+ nsr = reg16 >> 8;
+ cppr = reg16 & 0xFF;
+ g_assert_cmphex(nsr, ==, 0x82);
+ g_assert_cmphex(cppr, ==, priority);
+
+ /* check irq data is what was configured */
+ reg32 = qtest_readl(qts, xive_get_queue_addr(end_index));
+ g_assert_cmphex((reg32 & 0x7fffffff), ==, (irq_data & 0x7fffffff));
+
+ /* End Of Interrupt */
+ set_esb(qts, irq, XIVE_EOI_PAGE, XIVE_ESB_STORE_EOI, 0);
+ pq = get_esb(qts, irq, XIVE_EOI_PAGE, XIVE_ESB_GET);
+ g_assert_cmpuint(pq, ==, XIVE_ESB_RESET);
+
+ /* reset CPPR */
+ set_tima8(qts, chosen_one, TM_QW3_HV_PHYS + TM_CPPR, 0xFF);
+ reg32 = get_tima32(qts, chosen_one, TM_QW3_HV_PHYS + TM_WORD0);
+ nsr = reg32 >> 24;
+ cppr = (reg32 >> 16) & 0xFF;
+ g_assert_cmphex(nsr, ==, 0x00);
+ g_assert_cmphex(cppr, ==, 0xFF);
+}
+
+static void test_hw_group_irq_backlog(QTestState *qts)
+{
+ uint32_t irq = 31;
+ uint32_t irq_data = 0x01234567;
+ uint32_t end_index = 129;
+ uint32_t target_nvp = 0x81; /* group size = 4 */
+ uint32_t chosen_one = 3;
+ uint8_t blocking_priority, priority = 3;
+ uint32_t reg32;
+ uint16_t reg16;
+ uint8_t pq, nsr, cppr, lsmfb, i;
+
+ g_test_message("=========================================================");
+ g_test_message("Testing irq %d to hardware group of size 4 going " \
+ "through backlog",
+ irq);
+
+ /*
+ * set current priority of all threads in the group to something
+ * higher than what we're about to trigger
+ */
+ blocking_priority = priority - 1;
+ for (i = 0; i < SMT; i++) {
+ set_tima8(qts, i, TM_QW3_HV_PHYS + TM_CPPR, blocking_priority);
+ }
+
+ /* irq config */
+ set_eas(qts, irq, end_index, irq_data);
+ set_end(qts, end_index, target_nvp, priority, true /* group */);
+
+ /* enable and trigger irq */
+ get_esb(qts, irq, XIVE_EOI_PAGE, XIVE_ESB_SET_PQ_00);
+ set_esb(qts, irq, XIVE_TRIGGER_PAGE, 0, 0);
+
+ /* check irq is raised on cpu */
+ pq = get_esb(qts, irq, XIVE_EOI_PAGE, XIVE_ESB_GET);
+ g_assert_cmpuint(pq, ==, XIVE_ESB_PENDING);
+
+ /* check no interrupt is pending on the 2 possible targets */
+ for (i = 0; i < SMT; i++) {
+ reg32 = get_tima32(qts, i, TM_QW3_HV_PHYS + TM_WORD0);
+ nsr = reg32 >> 24;
+ cppr = (reg32 >> 16) & 0xFF;
+ lsmfb = reg32 & 0xFF;
+ g_assert_cmphex(nsr, ==, 0x0);
+ g_assert_cmphex(cppr, ==, blocking_priority);
+ g_assert_cmphex(lsmfb, ==, priority);
+ }
+
+ /* lower priority of one thread */
+ set_tima8(qts, chosen_one, TM_QW3_HV_PHYS + TM_CPPR, priority + 1);
+
+ /* check backlogged interrupt is presented */
+ reg32 = get_tima32(qts, chosen_one, TM_QW3_HV_PHYS + TM_WORD0);
+ nsr = reg32 >> 24;
+ cppr = (reg32 >> 16) & 0xFF;
+ g_assert_cmphex(nsr, ==, 0x82);
+ g_assert_cmphex(cppr, ==, priority + 1);
+
+ /* ack the irq */
+ reg16 = get_tima16(qts, chosen_one, TM_SPC_ACK_HV_REG);
+ nsr = reg16 >> 8;
+ cppr = reg16 & 0xFF;
+ g_assert_cmphex(nsr, ==, 0x82);
+ g_assert_cmphex(cppr, ==, priority);
+
+ /* check irq data is what was configured */
+ reg32 = qtest_readl(qts, xive_get_queue_addr(end_index));
+ g_assert_cmphex((reg32 & 0x7fffffff), ==, (irq_data & 0x7fffffff));
+
+ /* End Of Interrupt */
+ set_esb(qts, irq, XIVE_EOI_PAGE, XIVE_ESB_STORE_EOI, 0);
+ pq = get_esb(qts, irq, XIVE_EOI_PAGE, XIVE_ESB_GET);
+ g_assert_cmpuint(pq, ==, XIVE_ESB_RESET);
+
+ /* reset CPPR */
+ set_tima8(qts, chosen_one, TM_QW3_HV_PHYS + TM_CPPR, 0xFF);
+ reg32 = get_tima32(qts, chosen_one, TM_QW3_HV_PHYS + TM_WORD0);
+ nsr = reg32 >> 24;
+ cppr = (reg32 >> 16) & 0xFF;
+ lsmfb = reg32 & 0xFF;
+ g_assert_cmphex(nsr, ==, 0x00);
+ g_assert_cmphex(cppr, ==, 0xFF);
+ g_assert_cmphex(lsmfb, ==, 0xFF);
+}
+
+static void test_xive(void)
+{
+ QTestState *qts;
+
+ qts = qtest_initf("-M powernv10 -smp %d,cores=1,threads=%d -nographic "
+ "-nodefaults -serial mon:stdio -S "
+ "-d guest_errors -trace '*xive*'",
+ SMT, SMT);
+ init_xive(qts);
+
+ test_hw_irq(qts);
+
+ /* omit reset_state here and use settings from test_hw_irq */
+ test_pull_thread_ctx_to_odd_thread_cl(qts);
+
+ reset_state(qts);
+ test_pool_irq(qts);
+
+ reset_state(qts);
+ test_hw_group_irq(qts);
+
+ reset_state(qts);
+ test_hw_group_irq_backlog(qts);
+
+ reset_state(qts);
+ test_flush_sync_inject(qts);
+
+ reset_state(qts);
+ test_nvpg_bar(qts);
+
+ qtest_quit(qts);
+}
+
+int main(int argc, char **argv)
+{
+ g_test_init(&argc, &argv, NULL);
+ qtest_add_func("xive2", test_xive);
+ return g_test_run();
+}
diff --git a/tests/qtest/pnv-xscom.h b/tests/qtest/pnv-xscom.h
index 6f62941..5aa1701 100644
--- a/tests/qtest/pnv-xscom.h
+++ b/tests/qtest/pnv-xscom.h
@@ -56,7 +56,7 @@ static const PnvChip pnv_chips[] = {
.chip_type = PNV_CHIP_POWER10,
.cpu_model = "POWER10",
.xscom_base = 0x000603fc00000000ull,
- .cfam_id = 0x120da04900008000ull,
+ .cfam_id = 0x220da04980000000ull,
.first_core = 0x0,
.num_i2c = 4,
},
diff --git a/tests/qtest/pvpanic-pci-test.c b/tests/qtest/pvpanic-pci-test.c
index 2c05b37..f788a44 100644
--- a/tests/qtest/pvpanic-pci-test.c
+++ b/tests/qtest/pvpanic-pci-test.c
@@ -13,9 +13,10 @@
#include "qemu/osdep.h"
#include "libqtest.h"
-#include "qapi/qmp/qdict.h"
+#include "qobject/qdict.h"
#include "libqos/pci.h"
#include "libqos/pci-pc.h"
+#include "hw/misc/pvpanic.h"
#include "hw/pci/pci_regs.h"
static void test_panic_nopause(void)
@@ -34,7 +35,7 @@ static void test_panic_nopause(void)
bar = qpci_iomap(dev, 0, NULL);
qpci_memread(dev, bar, 0, &val, sizeof(val));
- g_assert_cmpuint(val, ==, 3);
+ g_assert_cmpuint(val, ==, PVPANIC_EVENTS);
val = 1;
qpci_memwrite(dev, bar, 0, &val, sizeof(val));
@@ -67,7 +68,7 @@ static void test_panic(void)
bar = qpci_iomap(dev, 0, NULL);
qpci_memread(dev, bar, 0, &val, sizeof(val));
- g_assert_cmpuint(val, ==, 3);
+ g_assert_cmpuint(val, ==, PVPANIC_EVENTS);
val = 1;
qpci_memwrite(dev, bar, 0, &val, sizeof(val));
@@ -84,11 +85,50 @@ static void test_panic(void)
qtest_quit(qts);
}
+static void test_pvshutdown(void)
+{
+ uint8_t val;
+ QDict *response, *data;
+ QTestState *qts;
+ QPCIBus *pcibus;
+ QPCIDevice *dev;
+ QPCIBar bar;
+
+ qts = qtest_init("-device pvpanic-pci,addr=04.0");
+ pcibus = qpci_new_pc(qts, NULL);
+ dev = qpci_device_find(pcibus, QPCI_DEVFN(0x4, 0x0));
+ qpci_device_enable(dev);
+ bar = qpci_iomap(dev, 0, NULL);
+
+ qpci_memread(dev, bar, 0, &val, sizeof(val));
+ g_assert_cmpuint(val, ==, PVPANIC_EVENTS);
+
+ val = PVPANIC_SHUTDOWN;
+ qpci_memwrite(dev, bar, 0, &val, sizeof(val));
+
+ response = qtest_qmp_eventwait_ref(qts, "GUEST_PVSHUTDOWN");
+ qobject_unref(response);
+
+ response = qtest_qmp_eventwait_ref(qts, "SHUTDOWN");
+ g_assert(qdict_haskey(response, "data"));
+ data = qdict_get_qdict(response, "data");
+ g_assert(qdict_haskey(data, "guest"));
+ g_assert(qdict_get_bool(data, "guest"));
+ g_assert(qdict_haskey(data, "reason"));
+ g_assert_cmpstr(qdict_get_str(data, "reason"), ==, "guest-shutdown");
+ qobject_unref(response);
+
+ g_free(dev);
+ qpci_free_pc(pcibus);
+ qtest_quit(qts);
+}
+
int main(int argc, char **argv)
{
g_test_init(&argc, &argv, NULL);
qtest_add_func("/pvpanic-pci/panic", test_panic);
qtest_add_func("/pvpanic-pci/panic-nopause", test_panic_nopause);
+ qtest_add_func("/pvpanic-pci/pvshutdown", test_pvshutdown);
return g_test_run();
}
diff --git a/tests/qtest/pvpanic-test.c b/tests/qtest/pvpanic-test.c
index 78f1cf8..5606baf 100644
--- a/tests/qtest/pvpanic-test.c
+++ b/tests/qtest/pvpanic-test.c
@@ -9,7 +9,8 @@
#include "qemu/osdep.h"
#include "libqtest.h"
-#include "qapi/qmp/qdict.h"
+#include "qobject/qdict.h"
+#include "hw/misc/pvpanic.h"
static void test_panic_nopause(void)
{
@@ -20,7 +21,7 @@ static void test_panic_nopause(void)
qts = qtest_init("-device pvpanic -action panic=none");
val = qtest_inb(qts, 0x505);
- g_assert_cmpuint(val, ==, 3);
+ g_assert_cmpuint(val, ==, PVPANIC_EVENTS);
qtest_outb(qts, 0x505, 0x1);
@@ -43,7 +44,7 @@ static void test_panic(void)
qts = qtest_init("-device pvpanic -action panic=pause");
val = qtest_inb(qts, 0x505);
- g_assert_cmpuint(val, ==, 3);
+ g_assert_cmpuint(val, ==, PVPANIC_EVENTS);
qtest_outb(qts, 0x505, 0x1);
@@ -57,11 +58,40 @@ static void test_panic(void)
qtest_quit(qts);
}
+static void test_pvshutdown(void)
+{
+ uint8_t val;
+ QDict *response, *data;
+ QTestState *qts;
+
+ qts = qtest_init("-device pvpanic");
+
+ val = qtest_inb(qts, 0x505);
+ g_assert_cmpuint(val, ==, PVPANIC_EVENTS);
+
+ qtest_outb(qts, 0x505, PVPANIC_SHUTDOWN);
+
+ response = qtest_qmp_eventwait_ref(qts, "GUEST_PVSHUTDOWN");
+ qobject_unref(response);
+
+ response = qtest_qmp_eventwait_ref(qts, "SHUTDOWN");
+ g_assert(qdict_haskey(response, "data"));
+ data = qdict_get_qdict(response, "data");
+ g_assert(qdict_haskey(data, "guest"));
+ g_assert(qdict_get_bool(data, "guest"));
+ g_assert(qdict_haskey(data, "reason"));
+ g_assert_cmpstr(qdict_get_str(data, "reason"), ==, "guest-shutdown");
+ qobject_unref(response);
+
+ qtest_quit(qts);
+}
+
int main(int argc, char **argv)
{
g_test_init(&argc, &argv, NULL);
qtest_add_func("/pvpanic/panic", test_panic);
qtest_add_func("/pvpanic/panic-nopause", test_panic_nopause);
+ qtest_add_func("/pvpanic/pvshutdown", test_pvshutdown);
return g_test_run();
}
diff --git a/tests/qtest/q35-test.c b/tests/qtest/q35-test.c
index c922d81..62fff49 100644
--- a/tests/qtest/q35-test.c
+++ b/tests/qtest/q35-test.c
@@ -14,7 +14,7 @@
#include "libqos/pci.h"
#include "libqos/pci-pc.h"
#include "hw/pci-host/q35.h"
-#include "qapi/qmp/qdict.h"
+#include "qobject/qdict.h"
#define TSEG_SIZE_TEST_GUEST_RAM_MBYTES 128
@@ -83,7 +83,6 @@ static void test_smram_lock(void)
{
QPCIBus *pcibus;
QPCIDevice *pcidev;
- QDict *response;
QTestState *qts;
qts = qtest_init("-M q35");
@@ -107,10 +106,7 @@ static void test_smram_lock(void)
g_assert(smram_test_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_OPEN) == false);
/* reset */
- response = qtest_qmp(qts, "{'execute': 'system_reset', 'arguments': {} }");
- g_assert(response);
- g_assert(!qdict_haskey(response, "error"));
- qobject_unref(response);
+ qtest_system_reset(qts);
/* check open is settable again */
smram_set_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_OPEN, false);
@@ -194,7 +190,6 @@ static void test_smram_smbase_lock(void)
{
QPCIBus *pcibus;
QPCIDevice *pcidev;
- QDict *response;
QTestState *qts;
int i;
@@ -237,10 +232,7 @@ static void test_smram_smbase_lock(void)
}
/* reset */
- response = qtest_qmp(qts, "{'execute': 'system_reset', 'arguments': {} }");
- g_assert(response);
- g_assert(!qdict_haskey(response, "error"));
- qobject_unref(response);
+ qtest_system_reset(qts);
/* check RAM at SMBASE is available after reset */
g_assert_cmpint(qtest_readb(qts, SMBASE), ==, SMRAM_TEST_PATTERN);
@@ -254,41 +246,6 @@ static void test_smram_smbase_lock(void)
qtest_quit(qts);
}
-static void test_without_smram_base(void)
-{
- QPCIBus *pcibus;
- QPCIDevice *pcidev;
- QTestState *qts;
- int i;
-
- qts = qtest_init("-M pc-q35-4.1");
-
- pcibus = qpci_new_pc(qts, NULL);
- g_assert(pcibus != NULL);
-
- pcidev = qpci_device_find(pcibus, 0);
- g_assert(pcidev != NULL);
-
- /* check that RAM is accessible */
- qtest_writeb(qts, SMBASE, SMRAM_TEST_PATTERN);
- g_assert_cmpint(qtest_readb(qts, SMBASE), ==, SMRAM_TEST_PATTERN);
-
- /* check that writing to 0x9c succeeds */
- for (i = 0; i <= 0xff; i++) {
- qpci_config_writeb(pcidev, MCH_HOST_BRIDGE_F_SMBASE, i);
- g_assert(qpci_config_readb(pcidev, MCH_HOST_BRIDGE_F_SMBASE) == i);
- }
-
- /* check that RAM is still accessible */
- qtest_writeb(qts, SMBASE, SMRAM_TEST_PATTERN + 1);
- g_assert_cmpint(qtest_readb(qts, SMBASE), ==, (SMRAM_TEST_PATTERN + 1));
-
- g_free(pcidev);
- qpci_free_pc(pcibus);
-
- qtest_quit(qts);
-}
-
int main(int argc, char **argv)
{
g_test_init(&argc, &argv, NULL);
@@ -301,6 +258,6 @@ int main(int argc, char **argv)
qtest_add_data_func("/q35/tseg-size/ext/16mb", &tseg_ext_16mb,
test_tseg_size);
qtest_add_func("/q35/smram/smbase_lock", test_smram_smbase_lock);
- qtest_add_func("/q35/smram/legacy_smbase", test_without_smram_base);
+
return g_test_run();
}
diff --git a/tests/qtest/qmp-cmd-test.c b/tests/qtest/qmp-cmd-test.c
index 2c15f60..040d042 100644
--- a/tests/qtest/qmp-cmd-test.c
+++ b/tests/qtest/qmp-cmd-test.c
@@ -14,7 +14,7 @@
#include "libqtest.h"
#include "qapi/error.h"
#include "qapi/qapi-visit-introspect.h"
-#include "qapi/qmp/qdict.h"
+#include "qobject/qdict.h"
#include "qapi/qobject-input-visitor.h"
const char common_args[] = "-nodefaults -machine none";
@@ -100,6 +100,7 @@ static bool query_is_ignored(const char *cmd)
/* Success depends on target arch: */
"query-cpu-definitions", /* arm, i386, ppc, s390x */
"query-gic-capabilities", /* arm */
+ "query-s390x-cpu-polarization", /* s390x */
/* Success depends on target-specific build configuration: */
"query-pci", /* CONFIG_PCI */
"x-query-virtio", /* CONFIG_VIRTIO */
diff --git a/tests/qtest/qmp-test.c b/tests/qtest/qmp-test.c
index 22957fa..edf0886 100644
--- a/tests/qtest/qmp-test.c
+++ b/tests/qtest/qmp-test.c
@@ -14,10 +14,10 @@
#include "libqtest.h"
#include "qapi/error.h"
#include "qapi/qapi-visit-control.h"
-#include "qapi/qmp/qdict.h"
-#include "qapi/qmp/qlist.h"
+#include "qobject/qdict.h"
+#include "qobject/qlist.h"
#include "qapi/qobject-input-visitor.h"
-#include "qapi/qmp/qstring.h"
+#include "qobject/qstring.h"
const char common_args[] = "-nodefaults -machine none";
diff --git a/tests/qtest/qom-test.c b/tests/qtest/qom-test.c
index d677f87..27d70bc 100644
--- a/tests/qtest/qom-test.c
+++ b/tests/qtest/qom-test.c
@@ -9,8 +9,8 @@
#include "qemu/osdep.h"
-#include "qapi/qmp/qdict.h"
-#include "qapi/qmp/qlist.h"
+#include "qobject/qdict.h"
+#include "qobject/qlist.h"
#include "qemu/cutils.h"
#include "libqtest.h"
@@ -88,6 +88,17 @@ static void test_machine(gconstpointer data)
qts = qtest_initf("-machine %s", machine);
+ if (g_test_slow()) {
+ /* Make sure we can get the machine class properties: */
+ g_autofree char *qom_machine = g_strdup_printf("%s-machine", machine);
+
+ response = qtest_qmp(qts, "{ 'execute': 'qom-list-properties',"
+ " 'arguments': { 'typename': %s } }",
+ qom_machine);
+ g_assert(response);
+ qobject_unref(response);
+ }
+
test_properties(qts, "/machine", true);
response = qtest_qmp(qts, "{ 'execute': 'quit' }");
diff --git a/tests/qtest/qos-test.c b/tests/qtest/qos-test.c
index 5da4091..abfd4b9 100644
--- a/tests/qtest/qos-test.c
+++ b/tests/qtest/qos-test.c
@@ -20,7 +20,7 @@
#include <getopt.h>
#include "libqtest-single.h"
#include "qapi/error.h"
-#include "qapi/qmp/qdict.h"
+#include "qobject/qdict.h"
#include "qemu/module.h"
#include "qapi/qobject-input-visitor.h"
#include "qapi/qapi-visit-machine.h"
@@ -33,7 +33,6 @@
static char *old_path;
-
/**
* qos_set_machines_devices_available(): sets availability of qgraph
* machines and devices.
@@ -104,8 +103,7 @@ static void restart_qemu_or_continue(char *path)
old_path = g_strdup(path);
qtest_start(path);
} else { /* if cmd line is the same, reset the guest */
- qobject_unref(qmp("{ 'execute': 'system_reset' }"));
- qmp_eventwait("RESET");
+ qtest_system_reset(global_qtest);
}
}
@@ -191,6 +189,12 @@ static void subprocess_run_one_test(const void *arg)
g_test_trap_assert_passed();
}
+static void destroy_pathv(void *arg)
+{
+ g_free(((char **)arg)[0]);
+ g_free(arg);
+}
+
/*
* in this function, 2 path will be built:
* path_str, a one-string path (ex "pc/i440FX-pcihost/...")
@@ -295,10 +299,13 @@ static void walk_path(QOSGraphNode *orig_path, int len)
if (path->u.test.subprocess) {
gchar *subprocess_path = g_strdup_printf("/%s/%s/subprocess",
qtest_get_arch(), path_str);
- qtest_add_data_func(path_str, subprocess_path, subprocess_run_one_test);
- g_test_add_data_func(subprocess_path, path_vec, run_one_test);
+ qtest_add_data_func_full(path_str, subprocess_path,
+ subprocess_run_one_test, g_free);
+ g_test_add_data_func_full(subprocess_path, path_vec,
+ run_one_test, destroy_pathv);
} else {
- qtest_add_data_func(path_str, path_vec, run_one_test);
+ qtest_add_data_func_full(path_str, path_vec,
+ run_one_test, destroy_pathv);
}
g_free(path_str);
diff --git a/tests/qtest/readconfig-test.c b/tests/qtest/readconfig-test.c
index 760f974..c6f32a4 100644
--- a/tests/qtest/readconfig-test.c
+++ b/tests/qtest/readconfig-test.c
@@ -13,10 +13,10 @@
#include "qapi/qapi-visit-machine.h"
#include "qapi/qapi-visit-qom.h"
#include "qapi/qapi-visit-ui.h"
-#include "qapi/qmp/qdict.h"
-#include "qapi/qmp/qlist.h"
+#include "qobject/qdict.h"
+#include "qobject/qlist.h"
#include "qapi/qobject-input-visitor.h"
-#include "qapi/qmp/qstring.h"
+#include "qobject/qstring.h"
#include "qemu/units.h"
static QTestState *qtest_init_with_config(const char *cfgdata)
diff --git a/tests/qtest/riscv-csr-test.c b/tests/qtest/riscv-csr-test.c
new file mode 100644
index 0000000..ff5c29e
--- /dev/null
+++ b/tests/qtest/riscv-csr-test.c
@@ -0,0 +1,56 @@
+/*
+ * QTest testcase for RISC-V CSRs
+ *
+ * Copyright (c) 2024 Syntacore.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "qemu/osdep.h"
+#include "libqtest.h"
+
+#define CSR_MVENDORID 0xf11
+#define CSR_MISELECT 0x350
+
+static void run_test_csr(void)
+{
+ uint64_t res;
+ uint64_t val = 0;
+
+ QTestState *qts = qtest_init("-machine virt -cpu veyron-v1");
+
+ res = qtest_csr_call(qts, "get_csr", 0, CSR_MVENDORID, &val);
+
+ g_assert_cmpint(res, ==, 0);
+ g_assert_cmpint(val, ==, 0x61f);
+
+ val = 0xff;
+ res = qtest_csr_call(qts, "set_csr", 0, CSR_MISELECT, &val);
+
+ g_assert_cmpint(res, ==, 0);
+
+ val = 0;
+ res = qtest_csr_call(qts, "get_csr", 0, CSR_MISELECT, &val);
+
+ g_assert_cmpint(res, ==, 0);
+ g_assert_cmpint(val, ==, 0xff);
+
+ qtest_quit(qts);
+}
+
+int main(int argc, char **argv)
+{
+ g_test_init(&argc, &argv, NULL);
+
+ qtest_add_func("/cpu/csr", run_test_csr);
+
+ return g_test_run();
+}
diff --git a/tests/qtest/riscv-iommu-test.c b/tests/qtest/riscv-iommu-test.c
new file mode 100644
index 0000000..df0c781
--- /dev/null
+++ b/tests/qtest/riscv-iommu-test.c
@@ -0,0 +1,210 @@
+/*
+ * QTest testcase for RISC-V IOMMU
+ *
+ * Copyright (c) 2024 Ventana Micro Systems Inc.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or (at your
+ * option) any later version. See the COPYING file in the top-level directory.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "libqtest-single.h"
+#include "qemu/module.h"
+#include "libqos/qgraph.h"
+#include "libqos/riscv-iommu.h"
+#include "hw/pci/pci_regs.h"
+
+static uint32_t riscv_iommu_read_reg32(QRISCVIOMMU *r_iommu, int reg_offset)
+{
+ return qpci_io_readl(&r_iommu->dev, r_iommu->reg_bar, reg_offset);
+}
+
+static uint64_t riscv_iommu_read_reg64(QRISCVIOMMU *r_iommu, int reg_offset)
+{
+ return qpci_io_readq(&r_iommu->dev, r_iommu->reg_bar, reg_offset);
+}
+
+static void riscv_iommu_write_reg32(QRISCVIOMMU *r_iommu, int reg_offset,
+ uint32_t val)
+{
+ qpci_io_writel(&r_iommu->dev, r_iommu->reg_bar, reg_offset, val);
+}
+
+static void riscv_iommu_write_reg64(QRISCVIOMMU *r_iommu, int reg_offset,
+ uint64_t val)
+{
+ qpci_io_writeq(&r_iommu->dev, r_iommu->reg_bar, reg_offset, val);
+}
+
+static void test_pci_config(void *obj, void *data, QGuestAllocator *t_alloc)
+{
+ QRISCVIOMMU *r_iommu = obj;
+ QPCIDevice *dev = &r_iommu->dev;
+ uint16_t vendorid, deviceid, classid;
+
+ vendorid = qpci_config_readw(dev, PCI_VENDOR_ID);
+ deviceid = qpci_config_readw(dev, PCI_DEVICE_ID);
+ classid = qpci_config_readw(dev, PCI_CLASS_DEVICE);
+
+ g_assert_cmpuint(vendorid, ==, RISCV_IOMMU_PCI_VENDOR_ID);
+ g_assert_cmpuint(deviceid, ==, RISCV_IOMMU_PCI_DEVICE_ID);
+ g_assert_cmpuint(classid, ==, RISCV_IOMMU_PCI_DEVICE_CLASS);
+}
+
+static void test_reg_reset(void *obj, void *data, QGuestAllocator *t_alloc)
+{
+ QRISCVIOMMU *r_iommu = obj;
+ uint64_t cap;
+ uint32_t reg;
+
+ cap = riscv_iommu_read_reg64(r_iommu, RISCV_IOMMU_REG_CAP);
+ g_assert_cmpuint(cap & RISCV_IOMMU_CAP_VERSION, ==, 0x10);
+
+ reg = riscv_iommu_read_reg32(r_iommu, RISCV_IOMMU_REG_CQCSR);
+ g_assert_cmpuint(reg & RISCV_IOMMU_CQCSR_CQEN, ==, 0);
+ g_assert_cmpuint(reg & RISCV_IOMMU_CQCSR_CIE, ==, 0);
+ g_assert_cmpuint(reg & RISCV_IOMMU_CQCSR_CQON, ==, 0);
+ g_assert_cmpuint(reg & RISCV_IOMMU_CQCSR_BUSY, ==, 0);
+
+ reg = riscv_iommu_read_reg32(r_iommu, RISCV_IOMMU_REG_FQCSR);
+ g_assert_cmpuint(reg & RISCV_IOMMU_FQCSR_FQEN, ==, 0);
+ g_assert_cmpuint(reg & RISCV_IOMMU_FQCSR_FIE, ==, 0);
+ g_assert_cmpuint(reg & RISCV_IOMMU_FQCSR_FQON, ==, 0);
+ g_assert_cmpuint(reg & RISCV_IOMMU_FQCSR_BUSY, ==, 0);
+
+ reg = riscv_iommu_read_reg32(r_iommu, RISCV_IOMMU_REG_PQCSR);
+ g_assert_cmpuint(reg & RISCV_IOMMU_PQCSR_PQEN, ==, 0);
+ g_assert_cmpuint(reg & RISCV_IOMMU_PQCSR_PIE, ==, 0);
+ g_assert_cmpuint(reg & RISCV_IOMMU_PQCSR_PQON, ==, 0);
+ g_assert_cmpuint(reg & RISCV_IOMMU_PQCSR_BUSY, ==, 0);
+
+ reg = riscv_iommu_read_reg32(r_iommu, RISCV_IOMMU_REG_DDTP);
+ g_assert_cmpuint(reg & RISCV_IOMMU_DDTP_BUSY, ==, 0);
+ g_assert_cmpuint(reg & RISCV_IOMMU_DDTP_MODE, ==,
+ RISCV_IOMMU_DDTP_MODE_OFF);
+
+ reg = riscv_iommu_read_reg32(r_iommu, RISCV_IOMMU_REG_IPSR);
+ g_assert_cmpuint(reg, ==, 0);
+}
+
+/*
+ * Common timeout-based poll for CQCSR, FQCSR and PQCSR. All
+ * their ON bits are mapped as RISCV_IOMMU_QUEUE_ACTIVE (16),
+ */
+static void qtest_wait_for_queue_active(QRISCVIOMMU *r_iommu,
+ uint32_t queue_csr)
+{
+ QTestState *qts = global_qtest;
+ guint64 timeout_us = 2 * 1000 * 1000;
+ gint64 start_time = g_get_monotonic_time();
+ uint32_t reg;
+
+ for (;;) {
+ qtest_clock_step(qts, 100);
+
+ reg = riscv_iommu_read_reg32(r_iommu, queue_csr);
+ if (reg & RISCV_IOMMU_QUEUE_ACTIVE) {
+ break;
+ }
+ g_assert(g_get_monotonic_time() - start_time <= timeout_us);
+ }
+}
+
+/*
+ * Goes through the queue activation procedures of chapter 6.2,
+ * "Guidelines for initialization", of the RISCV-IOMMU spec.
+ */
+static void test_iommu_init_queues(void *obj, void *data,
+ QGuestAllocator *t_alloc)
+{
+ QRISCVIOMMU *r_iommu = obj;
+ uint64_t reg64, q_addr;
+ uint32_t reg;
+ int k = 2;
+
+ reg64 = riscv_iommu_read_reg64(r_iommu, RISCV_IOMMU_REG_CAP);
+ g_assert_cmpuint(reg64 & RISCV_IOMMU_CAP_VERSION, ==, 0x10);
+
+ /*
+ * Program the command queue. Write 0xF to civ, fiv, pmiv and
+ * piv. With the current PCI device impl we expect 2 writable
+ * bits for each (k = 2) since we have N = 4 total vectors (2^k).
+ */
+ riscv_iommu_write_reg32(r_iommu, RISCV_IOMMU_REG_IVEC, 0xFFFF);
+ reg = riscv_iommu_read_reg32(r_iommu, RISCV_IOMMU_REG_IVEC);
+ g_assert_cmpuint(reg & RISCV_IOMMU_REG_IVEC_CIV, ==, 0x3);
+ g_assert_cmpuint(reg & RISCV_IOMMU_REG_IVEC_FIV, ==, 0x30);
+ g_assert_cmpuint(reg & RISCV_IOMMU_REG_IVEC_PMIV, ==, 0x300);
+ g_assert_cmpuint(reg & RISCV_IOMMU_REG_IVEC_PIV, ==, 0x3000);
+
+ /* Alloc a 4*16 bytes buffer and use it to set cqb */
+ q_addr = guest_alloc(t_alloc, 4 * 16);
+ reg64 = 0;
+ deposit64(reg64, RISCV_IOMMU_CQB_PPN_START,
+ RISCV_IOMMU_CQB_PPN_LEN, q_addr);
+ deposit64(reg64, RISCV_IOMMU_CQB_LOG2SZ_START,
+ RISCV_IOMMU_CQB_LOG2SZ_LEN, k - 1);
+ riscv_iommu_write_reg64(r_iommu, RISCV_IOMMU_REG_CQB, reg64);
+
+ /* cqt = 0, cqcsr.cqen = 1, poll cqcsr.cqon until it reads 1 */
+ riscv_iommu_write_reg32(r_iommu, RISCV_IOMMU_REG_CQT, 0);
+
+ reg = riscv_iommu_read_reg32(r_iommu, RISCV_IOMMU_REG_CQCSR);
+ reg |= RISCV_IOMMU_CQCSR_CQEN;
+ riscv_iommu_write_reg32(r_iommu, RISCV_IOMMU_REG_CQCSR, reg);
+
+ qtest_wait_for_queue_active(r_iommu, RISCV_IOMMU_REG_CQCSR);
+
+ /*
+ * Program the fault queue. Alloc a 4*32 bytes (instead of 4*16)
+ * buffer and use it to set fqb.
+ */
+ q_addr = guest_alloc(t_alloc, 4 * 32);
+ reg64 = 0;
+ deposit64(reg64, RISCV_IOMMU_FQB_PPN_START,
+ RISCV_IOMMU_FQB_PPN_LEN, q_addr);
+ deposit64(reg64, RISCV_IOMMU_FQB_LOG2SZ_START,
+ RISCV_IOMMU_FQB_LOG2SZ_LEN, k - 1);
+ riscv_iommu_write_reg64(r_iommu, RISCV_IOMMU_REG_FQB, reg64);
+
+ /* fqt = 0, fqcsr.fqen = 1, poll fqcsr.fqon until it reads 1 */
+ riscv_iommu_write_reg32(r_iommu, RISCV_IOMMU_REG_FQT, 0);
+
+ reg = riscv_iommu_read_reg32(r_iommu, RISCV_IOMMU_REG_FQCSR);
+ reg |= RISCV_IOMMU_FQCSR_FQEN;
+ riscv_iommu_write_reg32(r_iommu, RISCV_IOMMU_REG_FQCSR, reg);
+
+ qtest_wait_for_queue_active(r_iommu, RISCV_IOMMU_REG_FQCSR);
+
+ /*
+ * Program the page-request queue. Alloc a 4*16 bytes buffer
+ * and use it to set pqb.
+ */
+ q_addr = guest_alloc(t_alloc, 4 * 16);
+ reg64 = 0;
+ deposit64(reg64, RISCV_IOMMU_PQB_PPN_START,
+ RISCV_IOMMU_PQB_PPN_LEN, q_addr);
+ deposit64(reg64, RISCV_IOMMU_PQB_LOG2SZ_START,
+ RISCV_IOMMU_PQB_LOG2SZ_LEN, k - 1);
+ riscv_iommu_write_reg64(r_iommu, RISCV_IOMMU_REG_PQB, reg64);
+
+ /* pqt = 0, pqcsr.pqen = 1, poll pqcsr.pqon until it reads 1 */
+ riscv_iommu_write_reg32(r_iommu, RISCV_IOMMU_REG_PQT, 0);
+
+ reg = riscv_iommu_read_reg32(r_iommu, RISCV_IOMMU_REG_PQCSR);
+ reg |= RISCV_IOMMU_PQCSR_PQEN;
+ riscv_iommu_write_reg32(r_iommu, RISCV_IOMMU_REG_PQCSR, reg);
+
+ qtest_wait_for_queue_active(r_iommu, RISCV_IOMMU_REG_PQCSR);
+}
+
+static void register_riscv_iommu_test(void)
+{
+ qos_add_test("pci_config", "riscv-iommu-pci", test_pci_config, NULL);
+ qos_add_test("reg_reset", "riscv-iommu-pci", test_reg_reset, NULL);
+ qos_add_test("iommu_init_queues", "riscv-iommu-pci",
+ test_iommu_init_queues, NULL);
+}
+
+libqos_init(register_riscv_iommu_test);
diff --git a/tests/qtest/rs5c372-test.c b/tests/qtest/rs5c372-test.c
new file mode 100644
index 0000000..0f6a9b6
--- /dev/null
+++ b/tests/qtest/rs5c372-test.c
@@ -0,0 +1,43 @@
+/*
+ * QTest testcase for the RS5C372 RTC
+ *
+ * Copyright (c) 2025 Bernhard Beschow <shentey@gmail.com>
+ *
+ * Based on ds1338-test.c
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/bcd.h"
+#include "libqos/i2c.h"
+
+#define RS5C372_ADDR 0x32
+
+static void rs5c372_read_date(void *obj, void *data, QGuestAllocator *alloc)
+{
+ QI2CDevice *i2cdev = obj;
+
+ uint8_t resp[0x10];
+ time_t now = time(NULL);
+ struct tm *utc = gmtime(&now);
+
+ i2c_read_block(i2cdev, 0, resp, sizeof(resp));
+
+ /* check retrieved time against local time */
+ g_assert_cmpuint(from_bcd(resp[5]), == , utc->tm_mday);
+ g_assert_cmpuint(from_bcd(resp[6]), == , 1 + utc->tm_mon);
+ g_assert_cmpuint(2000 + from_bcd(resp[7]), == , 1900 + utc->tm_year);
+}
+
+static void rs5c372_register_nodes(void)
+{
+ QOSGraphEdgeOptions opts = { };
+ add_qi2c_address(&opts, &(QI2CAddress) { RS5C372_ADDR });
+
+ qos_node_create_driver("rs5c372", i2c_device_create);
+ qos_node_consumes("rs5c372", "i2c-bus", &opts);
+ qos_add_test("read_date", "rs5c372", rs5c372_read_date, NULL);
+}
+
+libqos_init(rs5c372_register_nodes);
diff --git a/tests/qtest/rtl8139-test.c b/tests/qtest/rtl8139-test.c
index eedf90f..55f671f 100644
--- a/tests/qtest/rtl8139-test.c
+++ b/tests/qtest/rtl8139-test.c
@@ -65,7 +65,7 @@ PORT(IntrMask, w, 0x3c)
PORT(IntrStatus, w, 0x3E)
PORT(TimerInt, l, 0x54)
-#define fatal(...) do { g_test_message(__VA_ARGS__); g_assert(0); } while (0)
+#define fatal(...) do { g_test_message(__VA_ARGS__); g_assert_not_reached(); } while (0)
static void test_timer(void)
{
diff --git a/tests/qtest/stm32l4x5.h b/tests/qtest/stm32l4x5.h
new file mode 100644
index 0000000..2d21cc6
--- /dev/null
+++ b/tests/qtest/stm32l4x5.h
@@ -0,0 +1,42 @@
+/*
+ * QTest testcase header for STM32L4X5 :
+ * used for consolidating common objects in stm32l4x5_*-test.c
+ *
+ * Copyright (c) 2024 Arnaud Minier <arnaud.minier@telecom-paris.fr>
+ * Copyright (c) 2024 Inès Varhol <ines.varhol@telecom-paris.fr>
+ *
+ * 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 "libqtest.h"
+
+/* copied from clock.h */
+#define CLOCK_PERIOD_1SEC (1000000000llu << 32)
+#define CLOCK_PERIOD_FROM_HZ(hz) (((hz) != 0) ? CLOCK_PERIOD_1SEC / (hz) : 0u)
+/*
+ * MSI (4 MHz) is used as system clock source after startup
+ * from Reset.
+ * AHB, APB1 and APB2 prescalers are set to 1 at reset.
+ */
+#define SYSCLK_PERIOD CLOCK_PERIOD_FROM_HZ(4000000)
+#define RCC_AHB2ENR 0x4002104C
+#define RCC_APB1ENR1 0x40021058
+#define RCC_APB1ENR2 0x4002105C
+#define RCC_APB2ENR 0x40021060
+
+
+static inline uint64_t get_clock_period(QTestState *qts, const char *path)
+{
+ uint64_t clock_period = 0;
+ QDict *r;
+
+ r = qtest_qmp(qts, "{ 'execute': 'qom-get', 'arguments':"
+ " { 'path': %s, 'property': 'qtest-clock-period'} }", path);
+ g_assert_false(qdict_haskey(r, "error"));
+ clock_period = qdict_get_int(r, "return");
+ qobject_unref(r);
+ return clock_period;
+}
+
+
diff --git a/tests/qtest/stm32l4x5_exti-test.c b/tests/qtest/stm32l4x5_exti-test.c
index 7092860..7e39c99 100644
--- a/tests/qtest/stm32l4x5_exti-test.c
+++ b/tests/qtest/stm32l4x5_exti-test.c
@@ -448,6 +448,9 @@ static void test_masked_interrupt(void)
g_assert_cmphex(exti_readl(EXTI_PR1), ==, 0x00000000);
/* Check that the interrupt isn't pending in NVIC */
g_assert_false(check_nvic_pending(EXTI1_IRQ));
+
+ /* Clean EXTI */
+ exti_set_irq(1, 0);
}
static void test_interrupt(void)
@@ -498,6 +501,9 @@ static void test_interrupt(void)
/* Clean NVIC */
unpend_nvic_irq(EXTI1_IRQ);
g_assert_false(check_nvic_pending(EXTI1_IRQ));
+
+ /* Clean EXTI */
+ exti_set_irq(1, 0);
}
static void test_orred_interrupts(void)
@@ -531,6 +537,8 @@ static void test_orred_interrupts(void)
unpend_nvic_irq(EXTI5_9_IRQ);
g_assert_false(check_nvic_pending(EXTI5_9_IRQ));
+
+ exti_set_irq(i, 0);
}
}
diff --git a/tests/qtest/stm32l4x5_gpio-test.c b/tests/qtest/stm32l4x5_gpio-test.c
index 72a7823..3c6ea71 100644
--- a/tests/qtest/stm32l4x5_gpio-test.c
+++ b/tests/qtest/stm32l4x5_gpio-test.c
@@ -10,6 +10,7 @@
#include "qemu/osdep.h"
#include "libqtest-single.h"
+#include "stm32l4x5.h"
#define GPIO_BASE_ADDR 0x48000000
#define GPIO_SIZE 0x400
@@ -168,14 +169,6 @@ static uint32_t reset(uint32_t gpio, unsigned int offset)
return 0x0;
}
-static void system_reset(void)
-{
- QDict *r;
- r = qtest_qmp(global_qtest, "{'execute': 'system_reset'}");
- g_assert_false(qdict_haskey(r, "error"));
- qobject_unref(r);
-}
-
static void test_idr_reset_value(void)
{
/*
@@ -213,7 +206,7 @@ static void test_idr_reset_value(void)
gpio_writel(GPIO_H, OTYPER, 0xDEADBEEF);
gpio_writel(GPIO_H, PUPDR, 0xDEADBEEF);
- system_reset();
+ qtest_system_reset(global_qtest);
uint32_t moder = gpio_readl(GPIO_A, MODER);
uint32_t odr = gpio_readl(GPIO_A, ODR);
@@ -505,6 +498,26 @@ static void test_bsrr_brr(const void *data)
gpio_writel(gpio, ODR, reset(gpio, ODR));
}
+static void test_clock_enable(void)
+{
+ /*
+ * For each GPIO, enable its clock in RCC
+ * and check that its clock period changes to SYSCLK_PERIOD
+ */
+ unsigned int gpio_id;
+
+ for (uint32_t gpio = GPIO_A; gpio <= GPIO_H; gpio += GPIO_B - GPIO_A) {
+ gpio_id = get_gpio_id(gpio);
+ g_autofree char *path = g_strdup_printf("/machine/soc/gpio%c/clk",
+ gpio_id + 'a');
+ g_assert_cmpuint(get_clock_period(global_qtest, path), ==, 0);
+ /* Enable the gpio clock */
+ writel(RCC_AHB2ENR, readl(RCC_AHB2ENR) | (0x1 << gpio_id));
+ g_assert_cmpuint(get_clock_period(global_qtest, path), ==,
+ SYSCLK_PERIOD);
+ }
+}
+
int main(int argc, char **argv)
{
int ret;
@@ -556,6 +569,8 @@ int main(int argc, char **argv)
qtest_add_data_func("stm32l4x5/gpio/test_bsrr_brr2",
test_data(GPIO_D, 0),
test_bsrr_brr);
+ qtest_add_func("stm32l4x5/gpio/test_clock_enable",
+ test_clock_enable);
qtest_start("-machine b-l475e-iot01a");
ret = g_test_run();
diff --git a/tests/qtest/stm32l4x5_syscfg-test.c b/tests/qtest/stm32l4x5_syscfg-test.c
index 506ca08..376c80e 100644
--- a/tests/qtest/stm32l4x5_syscfg-test.c
+++ b/tests/qtest/stm32l4x5_syscfg-test.c
@@ -10,6 +10,7 @@
#include "qemu/osdep.h"
#include "libqtest-single.h"
+#include "stm32l4x5.h"
#define SYSCFG_BASE_ADDR 0x40010000
#define SYSCFG_MEMRMP 0x00
@@ -26,7 +27,9 @@
#define INVALID_ADDR 0x2C
/* SoC forwards GPIOs to SysCfg */
-#define SYSCFG "/machine/soc"
+#define SOC "/machine/soc"
+#define SYSCFG "/machine/soc/syscfg"
+#define SYSCFG_CLK "/machine/soc/syscfg/clk"
#define EXTI "/machine/soc/exti"
static void syscfg_writel(unsigned int offset, uint32_t value)
@@ -41,15 +44,7 @@ static uint32_t syscfg_readl(unsigned int offset)
static void syscfg_set_irq(int num, int level)
{
- qtest_set_irq_in(global_qtest, SYSCFG, NULL, num, level);
-}
-
-static void system_reset(void)
-{
- QDict *response;
- response = qtest_qmp(global_qtest, "{'execute': 'system_reset'}");
- g_assert(qdict_haskey(response, "return"));
- qobject_unref(response);
+ qtest_set_irq_in(global_qtest, SOC, NULL, num, level);
}
static void test_reset(void)
@@ -179,7 +174,7 @@ static void test_set_only_bits(void)
syscfg_writel(SYSCFG_SWPR2, 0x00000000);
g_assert_cmphex(syscfg_readl(SYSCFG_SWPR2), ==, 0xFFFFFFFF);
- system_reset();
+ qtest_system_reset(global_qtest);
}
static void test_clear_only_bits(void)
@@ -191,7 +186,7 @@ static void test_clear_only_bits(void)
syscfg_writel(SYSCFG_CFGR1, 0x00000001);
g_assert_cmphex(syscfg_readl(SYSCFG_CFGR1), ==, 0x00000000);
- system_reset();
+ qtest_system_reset(global_qtest);
}
static void test_interrupt(void)
@@ -221,10 +216,10 @@ static void test_interrupt(void)
g_assert_true(get_irq(1));
/* Clean the test */
- syscfg_writel(SYSCFG_EXTICR1, 0x00000000);
syscfg_set_irq(0, 0);
- syscfg_set_irq(15, 0);
+ /* irq 15 is high at reset because GPIOA15 is high at reset */
syscfg_set_irq(17, 0);
+ syscfg_writel(SYSCFG_EXTICR1, 0x00000000);
}
static void test_irq_pin_multiplexer(void)
@@ -237,21 +232,21 @@ static void test_irq_pin_multiplexer(void)
syscfg_set_irq(0, 1);
- /* Check that irq 0 was set and irq 15 wasn't */
+ /* Check that irq 0 was set and irq 2 wasn't */
g_assert_true(get_irq(0));
- g_assert_false(get_irq(15));
+ g_assert_false(get_irq(2));
/* Clean the test */
syscfg_set_irq(0, 0);
- syscfg_set_irq(15, 1);
+ syscfg_set_irq(2, 1);
- /* Check that irq 15 was set and irq 0 wasn't */
- g_assert_true(get_irq(15));
+ /* Check that irq 2 was set and irq 0 wasn't */
+ g_assert_true(get_irq(2));
g_assert_false(get_irq(0));
/* Clean the test */
- syscfg_set_irq(15, 0);
+ syscfg_set_irq(2, 0);
}
static void test_irq_gpio_multiplexer(void)
@@ -301,6 +296,17 @@ static void test_irq_gpio_multiplexer(void)
syscfg_writel(SYSCFG_EXTICR1, 0x00000000);
}
+static void test_clock_enable(void)
+{
+ g_assert_cmpuint(get_clock_period(global_qtest, SYSCFG_CLK), ==, 0);
+
+ /* Enable SYSCFG clock */
+ writel(RCC_APB2ENR, readl(RCC_APB2ENR) | (0x1 << 0));
+
+ g_assert_cmpuint(get_clock_period(global_qtest, SYSCFG_CLK), ==,
+ SYSCLK_PERIOD);
+}
+
int main(int argc, char **argv)
{
int ret;
@@ -325,6 +331,8 @@ int main(int argc, char **argv)
test_irq_pin_multiplexer);
qtest_add_func("stm32l4x5/syscfg/test_irq_gpio_multiplexer",
test_irq_gpio_multiplexer);
+ qtest_add_func("stm32l4x5/syscfg/test_clock_enable",
+ test_clock_enable);
qtest_start("-machine b-l475e-iot01a");
ret = g_test_run();
diff --git a/tests/qtest/stm32l4x5_usart-test.c b/tests/qtest/stm32l4x5_usart-test.c
index 8902518..98a7472 100644
--- a/tests/qtest/stm32l4x5_usart-test.c
+++ b/tests/qtest/stm32l4x5_usart-test.c
@@ -12,6 +12,7 @@
#include "libqtest.h"
#include "hw/misc/stm32l4x5_rcc_internals.h"
#include "hw/registerfields.h"
+#include "stm32l4x5.h"
#define RCC_BASE_ADDR 0x40021000
/* Use USART 1 ADDR, assume the others work the same */
@@ -36,6 +37,8 @@ REG32(GTPR, 0x10)
REG32(RTOR, 0x14)
REG32(RQR, 0x18)
REG32(ISR, 0x1C)
+ FIELD(ISR, REACK, 22, 1)
+ FIELD(ISR, TEACK, 21, 1)
FIELD(ISR, TXE, 7, 1)
FIELD(ISR, RXNE, 5, 1)
FIELD(ISR, ORE, 3, 1)
@@ -191,7 +194,7 @@ static void init_uart(QTestState *qts)
/* Enable the transmitter, the receiver and the USART. */
qtest_writel(qts, (USART1_BASE_ADDR + A_CR1),
- R_CR1_UE_MASK | R_CR1_RE_MASK | R_CR1_TE_MASK);
+ cr1 | R_CR1_UE_MASK | R_CR1_RE_MASK | R_CR1_TE_MASK);
}
static void test_write_read(void)
@@ -202,6 +205,8 @@ static void test_write_read(void)
qtest_writel(qts, USART1_BASE_ADDR + A_TDR, 0xFFFFFFFF);
const uint32_t tdr = qtest_readl(qts, USART1_BASE_ADDR + A_TDR);
g_assert_cmpuint(tdr, ==, 0x000001FF);
+
+ qtest_quit(qts);
}
static void test_receive_char(void)
@@ -296,10 +301,65 @@ static void test_send_str(void)
qtest_quit(qts);
}
-int main(int argc, char **argv)
+static void test_ack(void)
{
- int ret;
+ uint32_t cr1;
+ uint32_t isr;
+ QTestState *qts = qtest_init("-M b-l475e-iot01a");
+
+ init_uart(qts);
+
+ cr1 = qtest_readl(qts, (USART1_BASE_ADDR + A_CR1));
+ /* Disable the transmitter and receiver. */
+ qtest_writel(qts, (USART1_BASE_ADDR + A_CR1),
+ cr1 & ~(R_CR1_RE_MASK | R_CR1_TE_MASK));
+
+ /* Test ISR ACK for transmitter and receiver disabled */
+ isr = qtest_readl(qts, (USART1_BASE_ADDR + A_ISR));
+ g_assert_false(isr & R_ISR_TEACK_MASK);
+ g_assert_false(isr & R_ISR_REACK_MASK);
+
+ /* Enable the transmitter and receiver. */
+ qtest_writel(qts, (USART1_BASE_ADDR + A_CR1),
+ cr1 | (R_CR1_RE_MASK | R_CR1_TE_MASK));
+
+ /* Test ISR ACK for transmitter and receiver disabled */
+ isr = qtest_readl(qts, (USART1_BASE_ADDR + A_ISR));
+ g_assert_true(isr & R_ISR_TEACK_MASK);
+ g_assert_true(isr & R_ISR_REACK_MASK);
+
+ qtest_quit(qts);
+}
+
+static void check_clock(QTestState *qts, const char *path, uint32_t rcc_reg,
+ uint32_t reg_offset)
+{
+ g_assert_cmpuint(get_clock_period(qts, path), ==, 0);
+ qtest_writel(qts, rcc_reg, qtest_readl(qts, rcc_reg) | (0x1 << reg_offset));
+ g_assert_cmpuint(get_clock_period(qts, path), ==, SYSCLK_PERIOD);
+}
+
+static void test_clock_enable(void)
+{
+ /*
+ * For each USART device, enable its clock in RCC
+ * and check that its clock frequency is SYSCLK_PERIOD
+ */
+ QTestState *qts = qtest_init("-M b-l475e-iot01a");
+
+ check_clock(qts, "machine/soc/usart[0]/clk", RCC_APB2ENR, 14);
+ check_clock(qts, "machine/soc/usart[1]/clk", RCC_APB1ENR1, 17);
+ check_clock(qts, "machine/soc/usart[2]/clk", RCC_APB1ENR1, 18);
+ check_clock(qts, "machine/soc/uart[0]/clk", RCC_APB1ENR1, 19);
+ check_clock(qts, "machine/soc/uart[1]/clk", RCC_APB1ENR1, 20);
+ check_clock(qts, "machine/soc/lpuart1/clk", RCC_APB1ENR2, 0);
+
+ qtest_quit(qts);
+}
+
+int main(int argc, char **argv)
+{
g_test_init(&argc, &argv, NULL);
g_test_set_nonfatal_assertions();
@@ -308,8 +368,8 @@ int main(int argc, char **argv)
qtest_add_func("stm32l4x5/usart/send_char", test_send_char);
qtest_add_func("stm32l4x5/usart/receive_str", test_receive_str);
qtest_add_func("stm32l4x5/usart/send_str", test_send_str);
- ret = g_test_run();
-
- return ret;
+ qtest_add_func("stm32l4x5/usart/ack", test_ack);
+ qtest_add_func("stm32l4x5/usart/clock_enable", test_clock_enable);
+ return g_test_run();
}
diff --git a/tests/qtest/tco-test.c b/tests/qtest/tco-test.c
index 0547d41..20ccefa 100644
--- a/tests/qtest/tco-test.c
+++ b/tests/qtest/tco-test.c
@@ -12,7 +12,7 @@
#include "libqtest.h"
#include "libqos/pci.h"
#include "libqos/pci-pc.h"
-#include "qapi/qmp/qdict.h"
+#include "qobject/qdict.h"
#include "hw/pci/pci_regs.h"
#include "hw/southbridge/ich9.h"
#include "hw/acpi/ich9.h"
diff --git a/tests/qtest/test-filter-mirror.c b/tests/qtest/test-filter-mirror.c
index f3865f7..723d2c2 100644
--- a/tests/qtest/test-filter-mirror.c
+++ b/tests/qtest/test-filter-mirror.c
@@ -10,7 +10,7 @@
#include "qemu/osdep.h"
#include "libqtest.h"
-#include "qapi/qmp/qdict.h"
+#include "qobject/qdict.h"
#include "qemu/iov.h"
#include "qemu/sockets.h"
#include "qemu/error-report.h"
diff --git a/tests/qtest/test-filter-redirector.c b/tests/qtest/test-filter-redirector.c
index a77d5fd..a996a80 100644
--- a/tests/qtest/test-filter-redirector.c
+++ b/tests/qtest/test-filter-redirector.c
@@ -52,7 +52,7 @@
#include "qemu/osdep.h"
#include "libqtest.h"
-#include "qapi/qmp/qdict.h"
+#include "qobject/qdict.h"
#include "qemu/iov.h"
#include "qemu/sockets.h"
#include "qemu/error-report.h"
diff --git a/tests/qtest/test-netfilter.c b/tests/qtest/test-netfilter.c
index b09ef7f..326d4bd 100644
--- a/tests/qtest/test-netfilter.c
+++ b/tests/qtest/test-netfilter.c
@@ -10,7 +10,7 @@
#include "qemu/osdep.h"
#include "libqtest-single.h"
-#include "qapi/qmp/qdict.h"
+#include "qobject/qdict.h"
/* add a netfilter to a netdev and then remove it */
static void add_one_netfilter(void)
diff --git a/tests/qtest/test-x86-cpuid-compat.c b/tests/qtest/test-x86-cpuid-compat.c
index b9e7e5e..456e2af 100644
--- a/tests/qtest/test-x86-cpuid-compat.c
+++ b/tests/qtest/test-x86-cpuid-compat.c
@@ -1,8 +1,8 @@
#include "qemu/osdep.h"
-#include "qapi/qmp/qdict.h"
-#include "qapi/qmp/qlist.h"
-#include "qapi/qmp/qnum.h"
-#include "qapi/qmp/qbool.h"
+#include "qobject/qdict.h"
+#include "qobject/qlist.h"
+#include "qobject/qnum.h"
+#include "qobject/qbool.h"
#include "libqtest-single.h"
static char *get_cpu0_qom_path(void)
@@ -193,7 +193,6 @@ static void add_feature_test(const char *name, const char *cpu,
args->bitnr = bitnr;
args->expected_value = expected_value;
qtest_add_data_func(name, args, test_feature_flag);
- return;
}
static void test_plus_minus_subprocess(void)
@@ -357,19 +356,6 @@ int main(int argc, char **argv)
"486", "xstore=on", "pc-i440fx-2.7",
"xlevel2", 0);
}
- /*
- * QEMU 2.3.0 had auto-level enabled for CPUID[7], already,
- * and the compat code that sets default level shouldn't
- * disable the auto-level=7 code:
- */
- if (qtest_has_machine("pc-i440fx-2.3")) {
- add_cpuid_test("x86/cpuid/auto-level7/pc-i440fx-2.3/off",
- "Penryn", NULL, "pc-i440fx-2.3",
- "level", 4);
- add_cpuid_test("x86/cpuid/auto-level7/pc-i440fx-2.3/on",
- "Penryn", "erms=on", "pc-i440fx-2.3",
- "level", 7);
- }
if (qtest_has_machine("pc-i440fx-2.9")) {
add_cpuid_test("x86/cpuid/auto-level7/pc-i440fx-2.9/off",
"Conroe", NULL, "pc-i440fx-2.9",
@@ -379,25 +365,6 @@ int main(int argc, char **argv)
"level", 10);
}
- /*
- * xlevel doesn't have any feature that triggers auto-level
- * code on old machine-types. Just check that the compat code
- * is working correctly:
- */
- if (qtest_has_machine("pc-i440fx-2.3")) {
- add_cpuid_test("x86/cpuid/xlevel-compat/pc-i440fx-2.3",
- "SandyBridge", NULL, "pc-i440fx-2.3",
- "xlevel", 0x8000000a);
- }
- if (qtest_has_machine("pc-i440fx-2.4")) {
- add_cpuid_test("x86/cpuid/xlevel-compat/pc-i440fx-2.4/npt-off",
- "SandyBridge", NULL, "pc-i440fx-2.4",
- "xlevel", 0x80000008);
- add_cpuid_test("x86/cpuid/xlevel-compat/pc-i440fx-2.4/npt-on",
- "SandyBridge", "svm=on,npt=on", "pc-i440fx-2.4",
- "xlevel", 0x80000008);
- }
-
/* Test feature parsing */
add_feature_test("x86/cpuid/features/plus",
"486", "+arat",
diff --git a/tests/qtest/tmp105-test.c b/tests/qtest/tmp105-test.c
index 3678646..3b114a5 100644
--- a/tests/qtest/tmp105-test.c
+++ b/tests/qtest/tmp105-test.c
@@ -12,7 +12,7 @@
#include "libqtest-single.h"
#include "libqos/qgraph.h"
#include "libqos/i2c.h"
-#include "qapi/qmp/qdict.h"
+#include "qobject/qdict.h"
#include "hw/sensor/tmp105_regs.h"
#define TMP105_TEST_ID "tmp105-test"
@@ -100,9 +100,9 @@ static void send_and_receive(void *obj, void *data, QGuestAllocator *alloc)
g_assert_cmphex(value, ==, 0x14f0);
i2c_set16(i2cdev, TMP105_REG_T_LOW, 0x1234);
- g_assert_cmphex(i2c_get16(i2cdev, TMP105_REG_T_LOW), ==, 0x1234);
+ g_assert_cmphex(i2c_get16(i2cdev, TMP105_REG_T_LOW), ==, 0x1230);
i2c_set16(i2cdev, TMP105_REG_T_HIGH, 0x4231);
- g_assert_cmphex(i2c_get16(i2cdev, TMP105_REG_T_HIGH), ==, 0x4231);
+ g_assert_cmphex(i2c_get16(i2cdev, TMP105_REG_T_HIGH), ==, 0x4230);
}
static void tmp105_register_nodes(void)
diff --git a/tests/qtest/tpm-emu.c b/tests/qtest/tpm-emu.c
index 2bf8ff4..9e4c200 100644
--- a/tests/qtest/tpm-emu.c
+++ b/tests/qtest/tpm-emu.c
@@ -16,8 +16,8 @@
#include "backends/tpm/tpm_ioctl.h"
#include "io/channel-socket.h"
#include "qapi/error.h"
-#include "qapi/qmp/qlist.h"
-#include "qapi/qmp/qstring.h"
+#include "qobject/qlist.h"
+#include "qobject/qstring.h"
#include "tpm-emu.h"
void tpm_emu_test_wait_cond(TPMTestState *s)
diff --git a/tests/qtest/tpm-emu.h b/tests/qtest/tpm-emu.h
index 712cee9..59c8009 100644
--- a/tests/qtest/tpm-emu.h
+++ b/tests/qtest/tpm-emu.h
@@ -21,7 +21,7 @@
#include "qemu/sockets.h"
#include "io/channel.h"
-#include "sysemu/tpm.h"
+#include "system/tpm.h"
#include "libqtest.h"
struct tpm_hdr {
diff --git a/tests/qtest/tpm-tests.c b/tests/qtest/tpm-tests.c
index fb94496..197714f 100644
--- a/tests/qtest/tpm-tests.c
+++ b/tests/qtest/tpm-tests.c
@@ -114,7 +114,7 @@ void tpm_test_swtpm_migration_test(const char *src_tpm_path,
sizeof(tpm_pcrread_resp));
tpm_util_migrate(src_qemu, uri);
- tpm_util_wait_for_migration_complete(src_qemu);
+ tpm_util_wait_for_migration_complete(dst_qemu);
tpm_util_pcrread(dst_qemu, tx, tpm_pcrread_resp,
sizeof(tpm_pcrread_resp));
diff --git a/tests/qtest/tpm-util.c b/tests/qtest/tpm-util.c
index 1c0319e..2cb2dd4 100644
--- a/tests/qtest/tpm-util.c
+++ b/tests/qtest/tpm-util.c
@@ -18,7 +18,7 @@
#include "hw/acpi/tpm.h"
#include "libqtest.h"
#include "tpm-util.h"
-#include "qapi/qmp/qdict.h"
+#include "qobject/qdict.h"
void tpm_util_crb_transfer(QTestState *s,
const unsigned char *req, size_t req_size,
diff --git a/tests/qtest/ufs-test.c b/tests/qtest/ufs-test.c
index 82ec3f0..4867ccf 100644
--- a/tests/qtest/ufs-test.c
+++ b/tests/qtest/ufs-test.c
@@ -8,13 +8,14 @@
#include "qemu/osdep.h"
#include "qemu/module.h"
-#include "qemu/units.h"
#include "libqtest.h"
#include "libqos/qgraph.h"
#include "libqos/pci.h"
#include "scsi/constants.h"
#include "block/ufs.h"
+#include "qemu/bitmap.h"
+#define DWORD_BYTE 4
/* Test images sizes in Bytes */
#define TEST_IMAGE_SIZE (64 * 1024 * 1024)
/* Timeout for various operations, in seconds. */
@@ -26,6 +27,12 @@
#define UTP_COMMAND_DESCRIPTOR_SIZE 4096
#define UTP_RESPONSE_UPIU_OFFSET 1024
#define UTP_PRDT_UPIU_OFFSET 2048
+#define UTRD_TEST_SLOT 0
+#define UFS_MAX_CMD_DESC 32
+/* Constants for MCQ */
+#define TEST_QID 0
+#define QUEUE_SIZE 32
+#define UFS_MCQ_MAX_QNUM 32
typedef struct QUfs QUfs;
@@ -34,12 +41,22 @@ struct QUfs {
QPCIDevice dev;
QPCIBar bar;
- uint64_t utrlba;
- uint64_t utmrlba;
+ DECLARE_BITMAP(cmd_desc_bitmap, UFS_MAX_CMD_DESC);
uint64_t cmd_desc_addr;
uint64_t data_buffer_addr;
bool enabled;
+ bool support_mcq;
+
+ /* for legacy doorbell mode */
+ uint64_t utrlba;
+
+ /* for mcq mode */
+ uint32_t maxq;
+ uint64_t sqlba[UFS_MCQ_MAX_QNUM];
+ uint64_t cqlba[UFS_MCQ_MAX_QNUM];
+ uint64_t sqdao[UFS_MCQ_MAX_QNUM];
+ uint64_t cqdao[UFS_MCQ_MAX_QNUM];
};
static inline uint32_t ufs_rreg(QUfs *ufs, size_t offset)
@@ -52,6 +69,24 @@ static inline void ufs_wreg(QUfs *ufs, size_t offset, uint32_t value)
qpci_io_writel(&ufs->dev, ufs->bar, offset, value);
}
+static int alloc_cmd_desc_slot(QUfs *ufs)
+{
+ int slot = find_first_zero_bit(ufs->cmd_desc_bitmap, UFS_MAX_CMD_DESC);
+ if (slot == UFS_MAX_CMD_DESC) {
+ g_assert_not_reached();
+ }
+ set_bit(slot, ufs->cmd_desc_bitmap);
+ return slot;
+}
+
+static void release_cmd_desc_slot(QUfs *ufs, int slot)
+{
+ if (!test_bit(slot, ufs->cmd_desc_bitmap)) {
+ g_assert_not_reached();
+ }
+ clear_bit(slot, ufs->cmd_desc_bitmap);
+}
+
static void ufs_wait_for_irq(QUfs *ufs)
{
uint64_t end_time;
@@ -64,14 +99,11 @@ static void ufs_wait_for_irq(QUfs *ufs)
} while (is == 0 && g_get_monotonic_time() < end_time);
}
-static UtpTransferReqDesc ufs_build_req_utrd(uint64_t cmd_desc_addr,
- uint8_t slot,
+static UtpTransferReqDesc ufs_build_req_utrd(uint64_t command_desc_base_addr,
uint32_t data_direction,
uint16_t prd_table_length)
{
UtpTransferReqDesc req = { 0 };
- uint64_t command_desc_base_addr =
- cmd_desc_addr + slot * UTP_COMMAND_DESCRIPTOR_SIZE;
req.header.dword_0 =
cpu_to_le32(1 << 28 | data_direction | UFS_UTP_REQ_DESC_INT_CMD);
@@ -88,80 +120,140 @@ static UtpTransferReqDesc ufs_build_req_utrd(uint64_t cmd_desc_addr,
return req;
}
-static void ufs_send_nop_out(QUfs *ufs, uint8_t slot,
- UtpTransferReqDesc *utrd_out, UtpUpiuRsp *rsp_out)
+static enum UtpOcsCodes
+__ufs_send_transfer_request_doorbell(QUfs *ufs, uint8_t lun,
+ const UtpTransferReqDesc *utrd)
{
- /* Build up utp transfer request descriptor */
- UtpTransferReqDesc utrd = ufs_build_req_utrd(ufs->cmd_desc_addr, slot,
- UFS_UTP_NO_DATA_TRANSFER, 0);
- uint64_t utrd_addr = ufs->utrlba + slot * sizeof(UtpTransferReqDesc);
+ uint64_t utrd_addr =
+ ufs->utrlba + UTRD_TEST_SLOT * sizeof(UtpTransferReqDesc);
+ UtpTransferReqDesc utrd_result;
+
+ qtest_memwrite(ufs->dev.bus->qts, utrd_addr, utrd, sizeof(*utrd));
+
+ /* Ring the doorbell */
+ ufs_wreg(ufs, A_UTRLDBR, 1);
+ ufs_wait_for_irq(ufs);
+ g_assert_true(FIELD_EX32(ufs_rreg(ufs, A_IS), IS, UTRCS));
+ ufs_wreg(ufs, A_IS, FIELD_DP32(0, IS, UTRCS, 1));
+
+ /* Handle completed command */
+ qtest_memread(ufs->dev.bus->qts, utrd_addr, &utrd_result,
+ sizeof(utrd_result));
+ return le32_to_cpu(utrd_result.header.dword_2) & 0xf;
+}
+
+static enum UtpOcsCodes
+__ufs_send_transfer_request_mcq(QUfs *ufs, uint8_t lun,
+ const UtpTransferReqDesc *utrd)
+{
+ uint32_t sqtp = ufs_rreg(ufs, ufs->sqdao[TEST_QID] + 0x4);
+ uint64_t utrd_addr = ufs->sqlba[TEST_QID] + sqtp;
+ uint32_t cqhp;
+ uint64_t cqentry_addr;
+ UfsCqEntry cqentry;
+
+ qtest_memwrite(ufs->dev.bus->qts, utrd_addr, utrd, sizeof(*utrd));
+
+ /* Insert a new entry into the submission queue */
+ sqtp = ufs_rreg(ufs, ufs->sqdao[TEST_QID] + 0x4);
+ sqtp = (sqtp + sizeof(UfsSqEntry)) % (QUEUE_SIZE * sizeof(UfsSqEntry));
+ ufs_wreg(ufs, ufs->sqdao[TEST_QID] + 0x4, sqtp);
+ ufs_wait_for_irq(ufs);
+ g_assert_true(FIELD_EX32(ufs_rreg(ufs, A_IS), IS, CQES));
+ ufs_wreg(ufs, A_IS, FIELD_DP32(0, IS, CQES, 1));
+
+ /* Handle the completed command from the completion queue */
+ cqhp = ufs_rreg(ufs, ufs->cqdao[TEST_QID]);
+ cqentry_addr = ufs->cqlba[TEST_QID] + cqhp;
+ qtest_memread(ufs->dev.bus->qts, cqentry_addr, &cqentry, sizeof(cqentry));
+ ufs_wreg(ufs, ufs->cqdao[TEST_QID], cqhp);
+
+ return cqentry.status;
+}
+
+static enum UtpOcsCodes
+ufs_send_transfer_request_sync(QUfs *ufs, uint8_t lun,
+ const UtpTransferReqDesc *utrd)
+{
+ if (ufs->support_mcq) {
+ return __ufs_send_transfer_request_mcq(ufs, lun, utrd);
+ }
+
+ return __ufs_send_transfer_request_doorbell(ufs, lun, utrd);
+}
+
+static enum UtpOcsCodes ufs_send_nop_out(QUfs *ufs, UtpUpiuRsp *rsp_out)
+{
+ int cmd_desc_slot = alloc_cmd_desc_slot(ufs);
uint64_t req_upiu_addr =
- ufs->cmd_desc_addr + slot * UTP_COMMAND_DESCRIPTOR_SIZE;
+ ufs->cmd_desc_addr + cmd_desc_slot * UTP_COMMAND_DESCRIPTOR_SIZE;
uint64_t rsp_upiu_addr = req_upiu_addr + UTP_RESPONSE_UPIU_OFFSET;
- qtest_memwrite(ufs->dev.bus->qts, utrd_addr, &utrd, sizeof(utrd));
/* Build up request upiu */
UtpUpiuReq req_upiu = { 0 };
req_upiu.header.trans_type = UFS_UPIU_TRANSACTION_NOP_OUT;
- req_upiu.header.task_tag = slot;
+ req_upiu.header.task_tag = cmd_desc_slot;
qtest_memwrite(ufs->dev.bus->qts, req_upiu_addr, &req_upiu,
sizeof(req_upiu));
- /* Ring Doorbell */
- ufs_wreg(ufs, A_UTRLDBR, 1);
- ufs_wait_for_irq(ufs);
- g_assert_true(FIELD_EX32(ufs_rreg(ufs, A_IS), IS, UTRCS));
- ufs_wreg(ufs, A_IS, FIELD_DP32(0, IS, UTRCS, 1));
+ /* Build up utp transfer request descriptor */
+ UtpTransferReqDesc utrd =
+ ufs_build_req_utrd(req_upiu_addr, UFS_UTP_NO_DATA_TRANSFER, 0);
+
+ /* Send Transfer Request */
+ enum UtpOcsCodes ret = ufs_send_transfer_request_sync(ufs, 0, &utrd);
- qtest_memread(ufs->dev.bus->qts, utrd_addr, utrd_out, sizeof(*utrd_out));
qtest_memread(ufs->dev.bus->qts, rsp_upiu_addr, rsp_out, sizeof(*rsp_out));
+ release_cmd_desc_slot(ufs, cmd_desc_slot);
+ return ret;
}
-static void ufs_send_query(QUfs *ufs, uint8_t slot, uint8_t query_function,
- uint8_t query_opcode, uint8_t idn, uint8_t index,
- UtpTransferReqDesc *utrd_out, UtpUpiuRsp *rsp_out)
+static enum UtpOcsCodes ufs_send_query(QUfs *ufs, uint8_t query_function,
+ uint8_t query_opcode, uint8_t idn,
+ uint8_t index, uint8_t selector,
+ uint32_t attr_value, UtpUpiuRsp *rsp_out)
{
- /* Build up utp transfer request descriptor */
- UtpTransferReqDesc utrd = ufs_build_req_utrd(ufs->cmd_desc_addr, slot,
- UFS_UTP_NO_DATA_TRANSFER, 0);
- uint64_t utrd_addr = ufs->utrlba + slot * sizeof(UtpTransferReqDesc);
+ int cmd_desc_slot = alloc_cmd_desc_slot(ufs);
uint64_t req_upiu_addr =
- ufs->cmd_desc_addr + slot * UTP_COMMAND_DESCRIPTOR_SIZE;
+ ufs->cmd_desc_addr + cmd_desc_slot * UTP_COMMAND_DESCRIPTOR_SIZE;
uint64_t rsp_upiu_addr = req_upiu_addr + UTP_RESPONSE_UPIU_OFFSET;
- qtest_memwrite(ufs->dev.bus->qts, utrd_addr, &utrd, sizeof(utrd));
/* Build up request upiu */
UtpUpiuReq req_upiu = { 0 };
req_upiu.header.trans_type = UFS_UPIU_TRANSACTION_QUERY_REQ;
req_upiu.header.query_func = query_function;
- req_upiu.header.task_tag = slot;
+ req_upiu.header.task_tag = cmd_desc_slot;
/*
- * QEMU UFS does not currently support Write descriptor and Write attribute,
+ * QEMU UFS does not currently support Write descriptor,
* so the value of data_segment_length is always 0.
*/
req_upiu.header.data_segment_length = 0;
req_upiu.qr.opcode = query_opcode;
req_upiu.qr.idn = idn;
req_upiu.qr.index = index;
+ req_upiu.qr.selector = selector;
+ req_upiu.qr.value = cpu_to_be32(attr_value);
+ req_upiu.qr.length = UFS_QUERY_DESC_MAX_SIZE;
qtest_memwrite(ufs->dev.bus->qts, req_upiu_addr, &req_upiu,
sizeof(req_upiu));
- /* Ring Doorbell */
- ufs_wreg(ufs, A_UTRLDBR, 1);
- ufs_wait_for_irq(ufs);
- g_assert_true(FIELD_EX32(ufs_rreg(ufs, A_IS), IS, UTRCS));
- ufs_wreg(ufs, A_IS, FIELD_DP32(0, IS, UTRCS, 1));
+ /* Build up utp transfer request descriptor */
+ UtpTransferReqDesc utrd =
+ ufs_build_req_utrd(req_upiu_addr, UFS_UTP_NO_DATA_TRANSFER, 0);
+
+ /* Send Transfer Request */
+ enum UtpOcsCodes ret = ufs_send_transfer_request_sync(ufs, 0, &utrd);
- qtest_memread(ufs->dev.bus->qts, utrd_addr, utrd_out, sizeof(*utrd_out));
qtest_memread(ufs->dev.bus->qts, rsp_upiu_addr, rsp_out, sizeof(*rsp_out));
+ release_cmd_desc_slot(ufs, cmd_desc_slot);
+ return ret;
}
-static void ufs_send_scsi_command(QUfs *ufs, uint8_t slot, uint8_t lun,
- const uint8_t *cdb, const uint8_t *data_in,
- size_t data_in_len, uint8_t *data_out,
- size_t data_out_len,
- UtpTransferReqDesc *utrd_out,
- UtpUpiuRsp *rsp_out)
+static enum UtpOcsCodes
+ufs_send_scsi_command(QUfs *ufs, uint8_t lun, const uint8_t *cdb,
+ const uint8_t *data_in, size_t data_in_len,
+ uint8_t *data_out, size_t data_out_len,
+ UtpUpiuRsp *rsp_out)
{
/* Build up PRDT */
@@ -171,8 +263,9 @@ static void ufs_send_scsi_command(QUfs *ufs, uint8_t slot, uint8_t lun,
uint8_t flags;
uint16_t prd_table_length, i;
uint32_t data_direction, data_len;
+ int cmd_desc_slot = alloc_cmd_desc_slot(ufs);
uint64_t req_upiu_addr =
- ufs->cmd_desc_addr + slot * UTP_COMMAND_DESCRIPTOR_SIZE;
+ ufs->cmd_desc_addr + cmd_desc_slot * UTP_COMMAND_DESCRIPTOR_SIZE;
uint64_t prdt_addr = req_upiu_addr + UTP_PRDT_UPIU_OFFSET;
g_assert_true(data_in_len < MAX_PRD_ENTRY_COUNT * PRD_ENTRY_DATA_SIZE);
@@ -214,36 +307,33 @@ static void ufs_send_scsi_command(QUfs *ufs, uint8_t slot, uint8_t lun,
qtest_memwrite(ufs->dev.bus->qts, prdt_addr, entries,
prd_table_length * sizeof(UfshcdSgEntry));
- /* Build up utp transfer request descriptor */
- UtpTransferReqDesc utrd = ufs_build_req_utrd(
- ufs->cmd_desc_addr, slot, data_direction, prd_table_length);
- uint64_t utrd_addr = ufs->utrlba + slot * sizeof(UtpTransferReqDesc);
uint64_t rsp_upiu_addr = req_upiu_addr + UTP_RESPONSE_UPIU_OFFSET;
- qtest_memwrite(ufs->dev.bus->qts, utrd_addr, &utrd, sizeof(utrd));
/* Build up request upiu */
UtpUpiuReq req_upiu = { 0 };
req_upiu.header.trans_type = UFS_UPIU_TRANSACTION_COMMAND;
req_upiu.header.flags = flags;
req_upiu.header.lun = lun;
- req_upiu.header.task_tag = slot;
+ req_upiu.header.task_tag = cmd_desc_slot;
req_upiu.sc.exp_data_transfer_len = cpu_to_be32(data_len);
memcpy(req_upiu.sc.cdb, cdb, UFS_CDB_SIZE);
qtest_memwrite(ufs->dev.bus->qts, req_upiu_addr, &req_upiu,
sizeof(req_upiu));
- /* Ring Doorbell */
- ufs_wreg(ufs, A_UTRLDBR, 1);
- ufs_wait_for_irq(ufs);
- g_assert_true(FIELD_EX32(ufs_rreg(ufs, A_IS), IS, UTRCS));
- ufs_wreg(ufs, A_IS, FIELD_DP32(0, IS, UTRCS, 1));
+ /* Build up utp transfer request descriptor */
+ UtpTransferReqDesc utrd =
+ ufs_build_req_utrd(req_upiu_addr, data_direction, prd_table_length);
+
+ /* Send Transfer Request */
+ enum UtpOcsCodes ret = ufs_send_transfer_request_sync(ufs, lun, &utrd);
- qtest_memread(ufs->dev.bus->qts, utrd_addr, utrd_out, sizeof(*utrd_out));
qtest_memread(ufs->dev.bus->qts, rsp_upiu_addr, rsp_out, sizeof(*rsp_out));
if (data_out_len) {
qtest_memread(ufs->dev.bus->qts, ufs->data_buffer_addr, data_out,
data_out_len);
}
+ release_cmd_desc_slot(ufs, cmd_desc_slot);
+ return ret;
}
/**
@@ -253,10 +343,10 @@ static void ufs_send_scsi_command(QUfs *ufs, uint8_t slot, uint8_t lun,
static void ufs_init(QUfs *ufs, QGuestAllocator *alloc)
{
uint64_t end_time;
- uint32_t nutrs, nutmrs;
+ uint32_t nutrs;
uint32_t hcs, is, ucmdarg2, cap;
uint32_t hce = 0, ie = 0;
- UtpTransferReqDesc utrd;
+ enum UtpOcsCodes ocs;
UtpUpiuRsp rsp_upiu;
ufs->bar = qpci_iomap(&ufs->dev, 0, NULL);
@@ -301,9 +391,12 @@ static void ufs_init(QUfs *ufs, QGuestAllocator *alloc)
hcs = ufs_rreg(ufs, A_HCS);
g_assert_true(FIELD_EX32(hcs, HCS, DP));
g_assert_true(FIELD_EX32(hcs, HCS, UTRLRDY));
- g_assert_true(FIELD_EX32(hcs, HCS, UTMRLRDY));
g_assert_true(FIELD_EX32(hcs, HCS, UCRDY));
+ /* Check MCQ support */
+ cap = ufs_rreg(ufs, A_CAP);
+ ufs->support_mcq = FIELD_EX32(cap, CAP, MCQS);
+
/* Enable all interrupt functions */
ie = FIELD_DP32(ie, IE, UTRCE, 1);
ie = FIELD_DP32(ie, IE, UEE, 1);
@@ -316,44 +409,89 @@ static void ufs_init(QUfs *ufs, QGuestAllocator *alloc)
ie = FIELD_DP32(ie, IE, HCFEE, 1);
ie = FIELD_DP32(ie, IE, SBFEE, 1);
ie = FIELD_DP32(ie, IE, CEFEE, 1);
+ if (ufs->support_mcq) {
+ ie = FIELD_DP32(ie, IE, CQEE, 1);
+ }
ufs_wreg(ufs, A_IE, ie);
ufs_wreg(ufs, A_UTRIACR, 0);
- /* Enable transfer request and task management request */
- cap = ufs_rreg(ufs, A_CAP);
- nutrs = FIELD_EX32(cap, CAP, NUTRS) + 1;
- nutmrs = FIELD_EX32(cap, CAP, NUTMRS) + 1;
+ /* Enable transfer request */
ufs->cmd_desc_addr =
- guest_alloc(alloc, nutrs * UTP_COMMAND_DESCRIPTOR_SIZE);
+ guest_alloc(alloc, UFS_MAX_CMD_DESC * UTP_COMMAND_DESCRIPTOR_SIZE);
ufs->data_buffer_addr =
guest_alloc(alloc, MAX_PRD_ENTRY_COUNT * PRD_ENTRY_DATA_SIZE);
- ufs->utrlba = guest_alloc(alloc, nutrs * sizeof(UtpTransferReqDesc));
- ufs->utmrlba = guest_alloc(alloc, nutmrs * sizeof(UtpTaskReqDesc));
- ufs_wreg(ufs, A_UTRLBA, ufs->utrlba & 0xffffffff);
- ufs_wreg(ufs, A_UTRLBAU, ufs->utrlba >> 32);
- ufs_wreg(ufs, A_UTMRLBA, ufs->utmrlba & 0xffffffff);
- ufs_wreg(ufs, A_UTMRLBAU, ufs->utmrlba >> 32);
- ufs_wreg(ufs, A_UTRLRSR, 1);
- ufs_wreg(ufs, A_UTMRLRSR, 1);
+ if (ufs->support_mcq) {
+ uint32_t mcqcap, qid, qcfgptr, mcq_reg_offset;
+ uint32_t cqattr = 0, sqattr = 0;
+
+ mcqcap = ufs_rreg(ufs, A_MCQCAP);
+ qcfgptr = FIELD_EX32(mcqcap, MCQCAP, QCFGPTR);
+ ufs->maxq = FIELD_EX32(mcqcap, MCQCAP, MAXQ) + 1;
+ for (qid = 0; qid < ufs->maxq; ++qid) {
+ ufs->sqlba[qid] =
+ guest_alloc(alloc, QUEUE_SIZE * sizeof(UtpTransferReqDesc));
+ ufs->cqlba[qid] =
+ guest_alloc(alloc, QUEUE_SIZE * sizeof(UtpTransferReqDesc));
+ mcq_reg_offset = qcfgptr * 0x200 + qid * 0x40;
+
+ ufs_wreg(ufs, mcq_reg_offset + A_SQLBA,
+ ufs->sqlba[qid] & 0xffffffff);
+ ufs_wreg(ufs, mcq_reg_offset + A_SQUBA, ufs->sqlba[qid] >> 32);
+ ufs_wreg(ufs, mcq_reg_offset + A_CQLBA,
+ ufs->cqlba[qid] & 0xffffffff);
+ ufs_wreg(ufs, mcq_reg_offset + A_CQUBA, ufs->cqlba[qid] >> 32);
+
+ /* Enable Completion Queue */
+ cqattr = FIELD_DP32(cqattr, CQATTR, CQEN, 1);
+ cqattr = FIELD_DP32(cqattr, CQATTR, SIZE,
+ QUEUE_SIZE * sizeof(UtpTransferReqDesc) /
+ DWORD_BYTE);
+ ufs_wreg(ufs, mcq_reg_offset + A_CQATTR, cqattr);
+
+ /* Enable Submission Queue */
+ sqattr = FIELD_DP32(sqattr, SQATTR, SQEN, 1);
+ sqattr = FIELD_DP32(sqattr, SQATTR, SIZE,
+ QUEUE_SIZE * sizeof(UtpTransferReqDesc) /
+ DWORD_BYTE);
+ sqattr = FIELD_DP32(sqattr, SQATTR, CQID, qid);
+ ufs_wreg(ufs, mcq_reg_offset + A_SQATTR, sqattr);
+
+ /* Cache head & tail pointer */
+ ufs->sqdao[qid] = ufs_rreg(ufs, mcq_reg_offset + A_SQDAO);
+ ufs->cqdao[qid] = ufs_rreg(ufs, mcq_reg_offset + A_CQDAO);
+ }
+ } else {
+ nutrs = FIELD_EX32(cap, CAP, NUTRS) + 1;
+ ufs->utrlba = guest_alloc(alloc, nutrs * sizeof(UtpTransferReqDesc));
+
+ ufs_wreg(ufs, A_UTRLBA, ufs->utrlba & 0xffffffff);
+ ufs_wreg(ufs, A_UTRLBAU, ufs->utrlba >> 32);
+ ufs_wreg(ufs, A_UTRLRSR, 1);
+ }
/* Send nop out to test transfer request */
- ufs_send_nop_out(ufs, 0, &utrd, &rsp_upiu);
- g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS);
+ ocs = ufs_send_nop_out(ufs, &rsp_upiu);
+ g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS);
/* Set fDeviceInit flag via query request */
- ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST,
- UFS_UPIU_QUERY_OPCODE_SET_FLAG,
- UFS_QUERY_FLAG_IDN_FDEVICEINIT, 0, &utrd, &rsp_upiu);
- g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS);
+ ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST,
+ UFS_UPIU_QUERY_OPCODE_SET_FLAG,
+ UFS_QUERY_FLAG_IDN_FDEVICEINIT, 0, 0, 0, &rsp_upiu);
+ g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS);
+ g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS);
/* Wait for device to reset */
end_time = g_get_monotonic_time() + TIMEOUT_SECONDS * G_TIME_SPAN_SECOND;
do {
qtest_clock_step(ufs->dev.bus->qts, 100);
- ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST,
- UFS_UPIU_QUERY_OPCODE_READ_FLAG,
- UFS_QUERY_FLAG_IDN_FDEVICEINIT, 0, &utrd, &rsp_upiu);
+ ocs =
+ ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST,
+ UFS_UPIU_QUERY_OPCODE_READ_FLAG,
+ UFS_QUERY_FLAG_IDN_FDEVICEINIT, 0, 0, 0, &rsp_upiu);
+ g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS);
+ g_assert_cmpuint(rsp_upiu.header.response, ==,
+ UFS_COMMAND_RESULT_SUCCESS);
} while (be32_to_cpu(rsp_upiu.qr.value) != 0 &&
g_get_monotonic_time() < end_time);
g_assert_cmpuint(be32_to_cpu(rsp_upiu.qr.value), ==, 0);
@@ -364,8 +502,15 @@ static void ufs_init(QUfs *ufs, QGuestAllocator *alloc)
static void ufs_exit(QUfs *ufs, QGuestAllocator *alloc)
{
if (ufs->enabled) {
- guest_free(alloc, ufs->utrlba);
- guest_free(alloc, ufs->utmrlba);
+ if (ufs->support_mcq) {
+ for (uint32_t qid = 0; qid < ufs->maxq; ++qid) {
+ guest_free(alloc, ufs->sqlba[qid]);
+ guest_free(alloc, ufs->cqlba[qid]);
+ }
+ } else {
+ guest_free(alloc, ufs->utrlba);
+ }
+
guest_free(alloc, ufs->cmd_desc_addr);
guest_free(alloc, ufs->data_buffer_addr);
}
@@ -428,15 +573,15 @@ static void ufstest_init(void *obj, void *data, QGuestAllocator *alloc)
const uint8_t request_sense_cdb[UFS_CDB_SIZE] = {
REQUEST_SENSE,
};
- UtpTransferReqDesc utrd;
+ enum UtpOcsCodes ocs;
UtpUpiuRsp rsp_upiu;
ufs_init(ufs, alloc);
/* Check REPORT_LUNS */
- ufs_send_scsi_command(ufs, 0, 0, report_luns_cdb, NULL, 0, buf, sizeof(buf),
- &utrd, &rsp_upiu);
- g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS);
+ ocs = ufs_send_scsi_command(ufs, 0, report_luns_cdb, NULL, 0, buf,
+ sizeof(buf), &rsp_upiu);
+ g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS);
g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, GOOD);
/* LUN LIST LENGTH should be 8, in big endian */
g_assert_cmpuint(buf[3], ==, 8);
@@ -444,15 +589,15 @@ static void ufstest_init(void *obj, void *data, QGuestAllocator *alloc)
g_assert_cmpuint(buf[9], ==, 0);
/* Clear Unit Attention */
- ufs_send_scsi_command(ufs, 0, 0, request_sense_cdb, NULL, 0, buf,
- sizeof(buf), &utrd, &rsp_upiu);
- g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS);
+ ocs = ufs_send_scsi_command(ufs, 0, request_sense_cdb, NULL, 0, buf,
+ sizeof(buf), &rsp_upiu);
+ g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS);
g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, CHECK_CONDITION);
/* Check TEST_UNIT_READY */
- ufs_send_scsi_command(ufs, 0, 0, test_unit_ready_cdb, NULL, 0, NULL, 0,
- &utrd, &rsp_upiu);
- g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS);
+ ocs = ufs_send_scsi_command(ufs, 0, test_unit_ready_cdb, NULL, 0, NULL, 0,
+ &rsp_upiu);
+ g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS);
g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, GOOD);
ufs_exit(ufs, alloc);
@@ -494,22 +639,22 @@ static void ufstest_read_write(void *obj, void *data, QGuestAllocator *alloc)
WRITE_10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00
};
uint32_t block_size;
- UtpTransferReqDesc utrd;
+ enum UtpOcsCodes ocs;
UtpUpiuRsp rsp_upiu;
const int test_lun = 1;
ufs_init(ufs, alloc);
/* Clear Unit Attention */
- ufs_send_scsi_command(ufs, 0, test_lun, request_sense_cdb, NULL, 0,
- read_buf, sizeof(read_buf), &utrd, &rsp_upiu);
- g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS);
+ ocs = ufs_send_scsi_command(ufs, test_lun, request_sense_cdb, NULL, 0,
+ read_buf, sizeof(read_buf), &rsp_upiu);
+ g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS);
g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, CHECK_CONDITION);
/* Read capacity */
- ufs_send_scsi_command(ufs, 0, test_lun, read_capacity_cdb, NULL, 0,
- read_buf, sizeof(read_buf), &utrd, &rsp_upiu);
- g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS);
+ ocs = ufs_send_scsi_command(ufs, test_lun, read_capacity_cdb, NULL, 0,
+ read_buf, sizeof(read_buf), &rsp_upiu);
+ g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS);
g_assert_cmpuint(rsp_upiu.header.scsi_status, ==,
UFS_COMMAND_RESULT_SUCCESS);
block_size = ldl_be_p(&read_buf[8]);
@@ -517,16 +662,16 @@ static void ufstest_read_write(void *obj, void *data, QGuestAllocator *alloc)
/* Write data */
memset(write_buf, 0xab, block_size);
- ufs_send_scsi_command(ufs, 0, test_lun, write_cdb, write_buf, block_size,
- NULL, 0, &utrd, &rsp_upiu);
- g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS);
+ ocs = ufs_send_scsi_command(ufs, test_lun, write_cdb, write_buf, block_size,
+ NULL, 0, &rsp_upiu);
+ g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS);
g_assert_cmpuint(rsp_upiu.header.scsi_status, ==,
UFS_COMMAND_RESULT_SUCCESS);
/* Read data and verify */
- ufs_send_scsi_command(ufs, 0, test_lun, read_cdb, NULL, 0, read_buf,
- block_size, &utrd, &rsp_upiu);
- g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS);
+ ocs = ufs_send_scsi_command(ufs, test_lun, read_cdb, NULL, 0, read_buf,
+ block_size, &rsp_upiu);
+ g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS);
g_assert_cmpuint(rsp_upiu.header.scsi_status, ==,
UFS_COMMAND_RESULT_SUCCESS);
g_assert_cmpint(memcmp(read_buf, write_buf, block_size), ==, 0);
@@ -534,6 +679,384 @@ static void ufstest_read_write(void *obj, void *data, QGuestAllocator *alloc)
ufs_exit(ufs, alloc);
}
+static void ufstest_query_flag_request(void *obj, void *data,
+ QGuestAllocator *alloc)
+{
+ QUfs *ufs = obj;
+
+ enum UtpOcsCodes ocs;
+ UtpUpiuRsp rsp_upiu;
+ ufs_init(ufs, alloc);
+
+ /* Read read-only flag */
+ ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST,
+ UFS_UPIU_QUERY_OPCODE_READ_FLAG,
+ UFS_QUERY_FLAG_IDN_FDEVICEINIT, 0, 0, 0, &rsp_upiu);
+ g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS);
+ g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS);
+ g_assert_cmpuint(rsp_upiu.qr.opcode, ==, UFS_UPIU_QUERY_OPCODE_READ_FLAG);
+ g_assert_cmpuint(rsp_upiu.qr.idn, ==, UFS_QUERY_FLAG_IDN_FDEVICEINIT);
+ g_assert_cmpuint(rsp_upiu.qr.value, ==, be32_to_cpu(0));
+
+ /* Flag Set, Clear, Toggle Test with fDeviceLifeSpanModeEn */
+ ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST,
+ UFS_UPIU_QUERY_OPCODE_READ_FLAG,
+ UFS_QUERY_FLAG_IDN_LIFE_SPAN_MODE_ENABLE, 0, 0, 0,
+ &rsp_upiu);
+ g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS);
+ g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS);
+ g_assert_cmpuint(rsp_upiu.qr.value, ==, be32_to_cpu(0));
+
+ ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST,
+ UFS_UPIU_QUERY_OPCODE_SET_FLAG,
+ UFS_QUERY_FLAG_IDN_LIFE_SPAN_MODE_ENABLE, 0, 0, 0,
+ &rsp_upiu);
+ g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS);
+ g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS);
+ g_assert_cmpuint(rsp_upiu.qr.value, ==, be32_to_cpu(1));
+
+ ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST,
+ UFS_UPIU_QUERY_OPCODE_CLEAR_FLAG,
+ UFS_QUERY_FLAG_IDN_LIFE_SPAN_MODE_ENABLE, 0, 0, 0,
+ &rsp_upiu);
+ g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS);
+ g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS);
+ g_assert_cmpuint(rsp_upiu.qr.value, ==, be32_to_cpu(0));
+
+ ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST,
+ UFS_UPIU_QUERY_OPCODE_TOGGLE_FLAG,
+ UFS_QUERY_FLAG_IDN_LIFE_SPAN_MODE_ENABLE, 0, 0, 0,
+ &rsp_upiu);
+ g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS);
+ g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS);
+ g_assert_cmpuint(rsp_upiu.qr.value, ==, be32_to_cpu(1));
+
+ ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST,
+ UFS_UPIU_QUERY_OPCODE_TOGGLE_FLAG,
+ UFS_QUERY_FLAG_IDN_LIFE_SPAN_MODE_ENABLE, 0, 0, 0,
+ &rsp_upiu);
+ g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS);
+ g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS);
+ g_assert_cmpuint(rsp_upiu.qr.value, ==, be32_to_cpu(0));
+
+ /* Read Write-only Flag (Intended Failure) */
+ ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST,
+ UFS_UPIU_QUERY_OPCODE_READ_FLAG,
+ UFS_QUERY_FLAG_IDN_PURGE_ENABLE, 0, 0, 0, &rsp_upiu);
+ g_assert_cmpuint(ocs, ==, UFS_OCS_INVALID_CMD_TABLE_ATTR);
+ g_assert_cmpuint(rsp_upiu.header.response, ==,
+ UFS_QUERY_RESULT_NOT_READABLE);
+
+ /* Write Read-Only Flag (Intended Failure) */
+ ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST,
+ UFS_UPIU_QUERY_OPCODE_SET_FLAG,
+ UFS_QUERY_FLAG_IDN_BUSY_RTC, 0, 0, 0, &rsp_upiu);
+ g_assert_cmpuint(ocs, ==, UFS_OCS_INVALID_CMD_TABLE_ATTR);
+ g_assert_cmpuint(rsp_upiu.header.response, ==,
+ UFS_QUERY_RESULT_NOT_WRITEABLE);
+
+ ufs_exit(ufs, alloc);
+}
+
+static void ufstest_query_attr_request(void *obj, void *data,
+ QGuestAllocator *alloc)
+{
+ QUfs *ufs = obj;
+
+ enum UtpOcsCodes ocs;
+ UtpUpiuRsp rsp_upiu;
+ ufs_init(ufs, alloc);
+
+ /* Read Readable Attributes*/
+ ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST,
+ UFS_UPIU_QUERY_OPCODE_READ_ATTR,
+ UFS_QUERY_ATTR_IDN_BOOT_LU_EN, 0, 0, 0, &rsp_upiu);
+ g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS);
+ g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS);
+ g_assert_cmpuint(rsp_upiu.qr.opcode, ==, UFS_UPIU_QUERY_OPCODE_READ_ATTR);
+ g_assert_cmpuint(rsp_upiu.qr.idn, ==, UFS_QUERY_ATTR_IDN_BOOT_LU_EN);
+ g_assert_cmpuint(rsp_upiu.qr.value, ==, cpu_to_be32(0x00));
+
+ ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST,
+ UFS_UPIU_QUERY_OPCODE_READ_ATTR,
+ UFS_QUERY_ATTR_IDN_BKOPS_STATUS, 0, 0, 0, &rsp_upiu);
+ g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS);
+ g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS);
+ g_assert_cmpuint(rsp_upiu.qr.value, ==, cpu_to_be32(0x00));
+
+ ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST,
+ UFS_UPIU_QUERY_OPCODE_READ_ATTR,
+ UFS_QUERY_ATTR_IDN_CASE_ROUGH_TEMP, 0, 0, 0,
+ &rsp_upiu);
+ g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS);
+ g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS);
+ g_assert_cmpuint(rsp_upiu.qr.value, ==, cpu_to_be32(0x00));
+
+ ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST,
+ UFS_UPIU_QUERY_OPCODE_READ_ATTR,
+ UFS_QUERY_ATTR_IDN_HIGH_TEMP_BOUND, 0, 0, 0,
+ &rsp_upiu);
+ g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS);
+ g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS);
+ g_assert_cmpuint(rsp_upiu.qr.value, ==, cpu_to_be32(160));
+
+ ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST,
+ UFS_UPIU_QUERY_OPCODE_READ_ATTR,
+ UFS_QUERY_ATTR_IDN_LOW_TEMP_BOUND, 0, 0, 0,
+ &rsp_upiu);
+ g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS);
+ g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS);
+ g_assert_cmpuint(rsp_upiu.qr.value, ==, cpu_to_be32(60));
+
+ /* Write Writable Attributes & Read Again */
+ ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST,
+ UFS_UPIU_QUERY_OPCODE_WRITE_ATTR,
+ UFS_QUERY_ATTR_IDN_ACTIVE_ICC_LVL, 0, 0, 0x03,
+ &rsp_upiu);
+ g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS);
+ g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS);
+ g_assert_cmpuint(rsp_upiu.qr.value, ==, cpu_to_be32(0x03));
+
+ ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST,
+ UFS_UPIU_QUERY_OPCODE_WRITE_ATTR,
+ UFS_QUERY_ATTR_IDN_EE_CONTROL, 0, 0, 0x07, &rsp_upiu);
+ g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS);
+ g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS);
+ g_assert_cmpuint(rsp_upiu.qr.value, ==, cpu_to_be32(0x07));
+
+ ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST,
+ UFS_UPIU_QUERY_OPCODE_READ_ATTR,
+ UFS_QUERY_ATTR_IDN_ACTIVE_ICC_LVL, 0, 0, 0, &rsp_upiu);
+ g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS);
+ g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS);
+ g_assert_cmpuint(rsp_upiu.qr.value, ==, cpu_to_be32(0x03));
+
+ ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST,
+ UFS_UPIU_QUERY_OPCODE_READ_ATTR,
+ UFS_QUERY_ATTR_IDN_EE_CONTROL, 0, 0, 0, &rsp_upiu);
+ g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS);
+ g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS);
+ g_assert_cmpuint(rsp_upiu.qr.value, ==, cpu_to_be32(0x07));
+
+ /* Write Invalid Value (Intended Error) */
+ ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST,
+ UFS_UPIU_QUERY_OPCODE_WRITE_ATTR,
+ UFS_QUERY_ATTR_IDN_ACTIVE_ICC_LVL, 0, 0, 0x10,
+ &rsp_upiu);
+ g_assert_cmpuint(ocs, ==, UFS_OCS_INVALID_CMD_TABLE_ATTR);
+ g_assert_cmpuint(rsp_upiu.header.response, ==,
+ UFS_QUERY_RESULT_INVALID_VALUE);
+
+ ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST,
+ UFS_UPIU_QUERY_OPCODE_READ_ATTR,
+ UFS_QUERY_ATTR_IDN_ACTIVE_ICC_LVL, 0, 0, 0, &rsp_upiu);
+ g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS);
+ g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS);
+ g_assert_cmpuint(rsp_upiu.qr.value, ==, cpu_to_be32(0x03));
+
+ /* Read Write-Only Attribute (Intended Error) */
+ ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST,
+ UFS_UPIU_QUERY_OPCODE_READ_ATTR,
+ UFS_QUERY_ATTR_IDN_SECONDS_PASSED, 0, 0, 0, &rsp_upiu);
+ g_assert_cmpuint(ocs, ==, UFS_OCS_INVALID_CMD_TABLE_ATTR);
+ g_assert_cmpuint(rsp_upiu.header.response, ==,
+ UFS_QUERY_RESULT_NOT_READABLE);
+
+ /* Write Read-Only Attribute (Intended Error) */
+ ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST,
+ UFS_UPIU_QUERY_OPCODE_WRITE_ATTR,
+ UFS_QUERY_ATTR_IDN_POWER_MODE, 0, 0, 0x01, &rsp_upiu);
+ g_assert_cmpuint(ocs, ==, UFS_OCS_INVALID_CMD_TABLE_ATTR);
+ g_assert_cmpuint(rsp_upiu.header.response, ==,
+ UFS_QUERY_RESULT_NOT_WRITEABLE);
+
+ ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST,
+ UFS_UPIU_QUERY_OPCODE_READ_ATTR,
+ UFS_QUERY_ATTR_IDN_POWER_MODE, 0, 0, 0, &rsp_upiu);
+ g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS);
+ g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS);
+ g_assert_cmpuint(rsp_upiu.qr.value, ==, cpu_to_be32(0x00));
+
+ /* Reset Written Attributes */
+ ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST,
+ UFS_UPIU_QUERY_OPCODE_WRITE_ATTR,
+ UFS_QUERY_ATTR_IDN_ACTIVE_ICC_LVL, 0, 0, 0, &rsp_upiu);
+ g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS);
+ g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS);
+ g_assert_cmpuint(rsp_upiu.qr.value, ==, cpu_to_be32(0x00));
+
+ ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST,
+ UFS_UPIU_QUERY_OPCODE_WRITE_ATTR,
+ UFS_QUERY_ATTR_IDN_EE_CONTROL, 0, 0, 0, &rsp_upiu);
+ g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS);
+ g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS);
+ g_assert_cmpuint(rsp_upiu.qr.value, ==, cpu_to_be32(0x00));
+
+ ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST,
+ UFS_UPIU_QUERY_OPCODE_READ_ATTR,
+ UFS_QUERY_ATTR_IDN_ACTIVE_ICC_LVL, 0, 0, 0, &rsp_upiu);
+ g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS);
+ g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS);
+ g_assert_cmpuint(rsp_upiu.qr.value, ==, cpu_to_be32(0x00));
+
+ ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST,
+ UFS_UPIU_QUERY_OPCODE_READ_ATTR,
+ UFS_QUERY_ATTR_IDN_EE_CONTROL, 0, 0, 0, &rsp_upiu);
+ g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS);
+ g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS);
+ g_assert_cmpuint(rsp_upiu.qr.value, ==, cpu_to_be32(0x00));
+
+ ufs_exit(ufs, alloc);
+}
+
+static void ufstest_query_desc_request(void *obj, void *data,
+ QGuestAllocator *alloc)
+{
+ QUfs *ufs = obj;
+
+ enum UtpOcsCodes ocs;
+ UtpUpiuRsp rsp_upiu;
+ ufs_init(ufs, alloc);
+
+ /* Write Descriptor is not supported yet */
+
+ /* Read Device Descriptor */
+ ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST,
+ UFS_UPIU_QUERY_OPCODE_READ_DESC,
+ UFS_QUERY_DESC_IDN_DEVICE, 0, 0, 0, &rsp_upiu);
+ g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS);
+ g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS);
+ g_assert_cmpuint(rsp_upiu.qr.opcode, ==, UFS_UPIU_QUERY_OPCODE_READ_DESC);
+ g_assert_cmpuint(rsp_upiu.qr.idn, ==, UFS_QUERY_DESC_IDN_DEVICE);
+ g_assert_cmpuint(rsp_upiu.qr.data[0], ==, sizeof(DeviceDescriptor));
+ g_assert_cmpuint(rsp_upiu.qr.data[1], ==, UFS_QUERY_DESC_IDN_DEVICE);
+
+ /* Read Configuration Descriptor is not supported yet*/
+
+ /* Read Unit Descriptor */
+ ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST,
+ UFS_UPIU_QUERY_OPCODE_READ_DESC,
+ UFS_QUERY_DESC_IDN_UNIT, 0, 0, 0, &rsp_upiu);
+ g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS);
+ g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS);
+ g_assert_cmpuint(rsp_upiu.qr.data[0], ==, sizeof(UnitDescriptor));
+ g_assert_cmpuint(rsp_upiu.qr.data[1], ==, UFS_QUERY_DESC_IDN_UNIT);
+ g_assert_cmpuint(rsp_upiu.qr.data[2], ==, 0);
+
+ ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST,
+ UFS_UPIU_QUERY_OPCODE_READ_DESC,
+ UFS_QUERY_DESC_IDN_UNIT, 1, 0, 0, &rsp_upiu);
+ g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS);
+ g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS);
+ g_assert_cmpuint(rsp_upiu.qr.data[0], ==, sizeof(UnitDescriptor));
+ g_assert_cmpuint(rsp_upiu.qr.data[1], ==, UFS_QUERY_DESC_IDN_UNIT);
+ g_assert_cmpuint(rsp_upiu.qr.data[2], ==, 1);
+
+ ocs =
+ ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST,
+ UFS_UPIU_QUERY_OPCODE_READ_DESC, UFS_QUERY_DESC_IDN_UNIT,
+ UFS_UPIU_RPMB_WLUN, 0, 0, &rsp_upiu);
+ g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS);
+ g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS);
+ g_assert_cmpuint(rsp_upiu.qr.data[0], ==, sizeof(RpmbUnitDescriptor));
+ g_assert_cmpuint(rsp_upiu.qr.data[1], ==, UFS_QUERY_DESC_IDN_UNIT);
+ g_assert_cmpuint(rsp_upiu.qr.data[2], ==, UFS_UPIU_RPMB_WLUN);
+
+ /* Read Interconnect Descriptor */
+ ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST,
+ UFS_UPIU_QUERY_OPCODE_READ_DESC,
+ UFS_QUERY_DESC_IDN_INTERCONNECT, 0, 0, 0, &rsp_upiu);
+ g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS);
+ g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS);
+ g_assert_cmpuint(rsp_upiu.qr.data[0], ==, sizeof(InterconnectDescriptor));
+ g_assert_cmpuint(rsp_upiu.qr.data[1], ==, UFS_QUERY_DESC_IDN_INTERCONNECT);
+
+ /* Read String Descriptor */
+ ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST,
+ UFS_UPIU_QUERY_OPCODE_READ_DESC,
+ UFS_QUERY_DESC_IDN_STRING, 0, 0, 0, &rsp_upiu);
+ g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS);
+ g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS);
+ g_assert_cmpuint(rsp_upiu.qr.data[0], ==, 0x12);
+ g_assert_cmpuint(rsp_upiu.qr.data[1], ==, UFS_QUERY_DESC_IDN_STRING);
+
+ ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST,
+ UFS_UPIU_QUERY_OPCODE_READ_DESC,
+ UFS_QUERY_DESC_IDN_STRING, 1, 0, 0, &rsp_upiu);
+ g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS);
+ g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS);
+ g_assert_cmpuint(rsp_upiu.qr.data[0], ==, 0x22);
+ g_assert_cmpuint(rsp_upiu.qr.data[1], ==, UFS_QUERY_DESC_IDN_STRING);
+
+ ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST,
+ UFS_UPIU_QUERY_OPCODE_READ_DESC,
+ UFS_QUERY_DESC_IDN_STRING, 4, 0, 0, &rsp_upiu);
+ g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS);
+ g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS);
+ g_assert_cmpuint(rsp_upiu.qr.data[0], ==, 0x0a);
+ g_assert_cmpuint(rsp_upiu.qr.data[1], ==, UFS_QUERY_DESC_IDN_STRING);
+
+ /* Read Geometry Descriptor */
+ ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST,
+ UFS_UPIU_QUERY_OPCODE_READ_DESC,
+ UFS_QUERY_DESC_IDN_GEOMETRY, 0, 0, 0, &rsp_upiu);
+ g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS);
+ g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS);
+ g_assert_cmpuint(rsp_upiu.qr.data[0], ==, sizeof(GeometryDescriptor));
+ g_assert_cmpuint(rsp_upiu.qr.data[1], ==, UFS_QUERY_DESC_IDN_GEOMETRY);
+
+ /* Read Power Descriptor */
+ ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST,
+ UFS_UPIU_QUERY_OPCODE_READ_DESC,
+ UFS_QUERY_DESC_IDN_POWER, 0, 0, 0, &rsp_upiu);
+ g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS);
+ g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS);
+ g_assert_cmpuint(rsp_upiu.qr.data[0], ==,
+ sizeof(PowerParametersDescriptor));
+ g_assert_cmpuint(rsp_upiu.qr.data[1], ==, UFS_QUERY_DESC_IDN_POWER);
+
+ /* Read Health Descriptor */
+ ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST,
+ UFS_UPIU_QUERY_OPCODE_READ_DESC,
+ UFS_QUERY_DESC_IDN_HEALTH, 0, 0, 0, &rsp_upiu);
+ g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS);
+ g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS);
+ g_assert_cmpuint(rsp_upiu.qr.data[0], ==, sizeof(DeviceHealthDescriptor));
+ g_assert_cmpuint(rsp_upiu.qr.data[1], ==, UFS_QUERY_DESC_IDN_HEALTH);
+
+ /* Invalid Index (Intended Failure) */
+ ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST,
+ UFS_UPIU_QUERY_OPCODE_READ_DESC,
+ UFS_QUERY_DESC_IDN_UNIT, 4, 0, 0, &rsp_upiu);
+ g_assert_cmpuint(ocs, ==, UFS_OCS_INVALID_CMD_TABLE_ATTR);
+ g_assert_cmpuint(rsp_upiu.header.response, ==,
+ UFS_QUERY_RESULT_INVALID_INDEX);
+
+ ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST,
+ UFS_UPIU_QUERY_OPCODE_READ_DESC,
+ UFS_QUERY_DESC_IDN_STRING, 5, 0, 0, &rsp_upiu);
+ g_assert_cmpuint(ocs, ==, UFS_OCS_INVALID_CMD_TABLE_ATTR);
+ g_assert_cmpuint(rsp_upiu.header.response, ==,
+ UFS_QUERY_RESULT_INVALID_INDEX);
+
+ /* Invalid Selector (Intended Failure) */
+ ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST,
+ UFS_UPIU_QUERY_OPCODE_READ_DESC,
+ UFS_QUERY_DESC_IDN_DEVICE, 0, 1, 0, &rsp_upiu);
+ g_assert_cmpuint(ocs, ==, UFS_OCS_INVALID_CMD_TABLE_ATTR);
+ g_assert_cmpuint(rsp_upiu.header.response, ==,
+ UFS_QUERY_RESULT_INVALID_SELECTOR);
+
+ ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST,
+ UFS_UPIU_QUERY_OPCODE_READ_DESC,
+ UFS_QUERY_DESC_IDN_STRING, 0, 1, 0, &rsp_upiu);
+ g_assert_cmpuint(ocs, ==, UFS_OCS_INVALID_CMD_TABLE_ATTR);
+ g_assert_cmpuint(rsp_upiu.header.response, ==,
+ UFS_QUERY_RESULT_INVALID_SELECTOR);
+
+ ufs_exit(ufs, alloc);
+}
+
static void drive_destroy(void *path)
{
unlink(path);
@@ -575,12 +1098,16 @@ static void ufs_register_nodes(void)
QOSGraphEdgeOptions edge_opts = {
.before_cmd_line = "-blockdev null-co,node-name=drv0,read-zeroes=on",
.after_cmd_line = "-device ufs-lu,bus=ufs0,drive=drv0,lun=0",
- .extra_device_opts = "addr=04.0,id=ufs0,nutrs=32,nutmrs=8"
+ .extra_device_opts = "addr=04.0,id=ufs0"
};
- QOSGraphTestOptions io_test_opts = {
- .before = ufs_blk_test_setup,
- };
+ QOSGraphTestOptions io_test_opts = { .before = ufs_blk_test_setup,
+ .edge.extra_device_opts =
+ "mcq=false,nutrs=32,nutmrs=8" };
+
+ QOSGraphTestOptions mcq_test_opts = { .before = ufs_blk_test_setup,
+ .edge.extra_device_opts =
+ "mcq=true,mcq-maxq=1" };
add_qpci_address(&edge_opts, &(QPCIAddress){ .devfn = QPCI_DEVFN(4, 0) });
@@ -600,7 +1127,14 @@ static void ufs_register_nodes(void)
return;
}
qos_add_test("init", "ufs", ufstest_init, NULL);
- qos_add_test("read-write", "ufs", ufstest_read_write, &io_test_opts);
+ qos_add_test("legacy-read-write", "ufs", ufstest_read_write, &io_test_opts);
+ qos_add_test("mcq-read-write", "ufs", ufstest_read_write, &mcq_test_opts);
+ qos_add_test("query-flag", "ufs", ufstest_query_flag_request,
+ &io_test_opts);
+ qos_add_test("query-attribute", "ufs", ufstest_query_attr_request,
+ &io_test_opts);
+ qos_add_test("query-desciptor", "ufs", ufstest_query_desc_request,
+ &io_test_opts);
}
libqos_init(ufs_register_nodes);
diff --git a/tests/qtest/vhost-user-blk-test.c b/tests/qtest/vhost-user-blk-test.c
index 117b9ac..ea90d41 100644
--- a/tests/qtest/vhost-user-blk-test.c
+++ b/tests/qtest/vhost-user-blk-test.c
@@ -906,7 +906,7 @@ static void start_vhost_user_blk(GString *cmd_line, int vus_instances,
vhost_user_blk_bin);
g_string_append_printf(cmd_line,
- " -object memory-backend-memfd,id=mem,size=256M,share=on "
+ " -object memory-backend-shm,id=mem,size=256M "
" -M memory-backend=mem -m 256M ");
for (i = 0; i < vus_instances; i++) {
diff --git a/tests/qtest/vhost-user-test.c b/tests/qtest/vhost-user-test.c
index d4e4372..75cb3e4 100644
--- a/tests/qtest/vhost-user-test.c
+++ b/tests/qtest/vhost-user-test.c
@@ -12,7 +12,7 @@
#include "libqtest-single.h"
#include "qapi/error.h"
-#include "qapi/qmp/qdict.h"
+#include "qobject/qdict.h"
#include "qemu/config-file.h"
#include "qemu/option.h"
#include "qemu/range.h"
@@ -20,7 +20,7 @@
#include "chardev/char-fe.h"
#include "qemu/memfd.h"
#include "qemu/module.h"
-#include "sysemu/sysemu.h"
+#include "system/system.h"
#include "libqos/libqos.h"
#include "libqos/pci-pc.h"
#include "libqos/virtio-pci.h"
@@ -44,6 +44,8 @@
"mem-path=%s,share=on -numa node,memdev=mem"
#define QEMU_CMD_MEMFD " -m %d -object memory-backend-memfd,id=mem,size=%dM," \
" -numa node,memdev=mem"
+#define QEMU_CMD_SHM " -m %d -object memory-backend-shm,id=mem,size=%dM," \
+ " -numa node,memdev=mem"
#define QEMU_CMD_CHR " -chardev socket,id=%s,path=%s%s"
#define QEMU_CMD_NETDEV " -netdev vhost-user,id=hs0,chardev=%s,vhostforce=on"
@@ -195,6 +197,7 @@ enum test_memfd {
TEST_MEMFD_AUTO,
TEST_MEMFD_YES,
TEST_MEMFD_NO,
+ TEST_MEMFD_SHM,
};
static void append_vhost_net_opts(TestServer *s, GString *cmd_line,
@@ -228,6 +231,8 @@ static void append_mem_opts(TestServer *server, GString *cmd_line,
if (memfd == TEST_MEMFD_YES) {
g_string_append_printf(cmd_line, QEMU_CMD_MEMFD, size, size);
+ } else if (memfd == TEST_MEMFD_SHM) {
+ g_string_append_printf(cmd_line, QEMU_CMD_SHM, size, size);
} else {
const char *root = init_hugepagefs() ? : server->tmpfs;
@@ -458,7 +463,10 @@ static void chr_read(void *opaque, const uint8_t *buf, int size)
case VHOST_USER_SET_VRING_KICK:
case VHOST_USER_SET_VRING_CALL:
/* consume the fd */
- qemu_chr_fe_get_msgfds(chr, &fd, 1);
+ if (!qemu_chr_fe_get_msgfds(chr, &fd, 1) && fd < 0) {
+ qos_printf("call fd: %d, do not set non-blocking\n", fd);
+ break;
+ }
/*
* This is a non-blocking eventfd.
* The receive function forces it to be blocking,
@@ -788,6 +796,19 @@ static void *vhost_user_test_setup_memfd(GString *cmd_line, void *arg)
return server;
}
+static void *vhost_user_test_setup_shm(GString *cmd_line, void *arg)
+{
+ TestServer *server = test_server_new("vhost-user-test", arg);
+ test_server_listen(server);
+
+ append_mem_opts(server, cmd_line, 256, TEST_MEMFD_SHM);
+ server->vu_ops->append_opts(server, cmd_line, "");
+
+ g_test_queue_destroy(vhost_user_test_cleanup, server);
+
+ return server;
+}
+
static void test_read_guest_mem(void *obj, void *arg, QGuestAllocator *alloc)
{
TestServer *server = arg;
@@ -899,7 +920,7 @@ static void wait_for_rings_started(TestServer *s, size_t count)
static inline void test_server_connect(TestServer *server)
{
- test_server_create_chr(server, ",reconnect=1");
+ test_server_create_chr(server, ",reconnect-ms=1000");
}
static gboolean
@@ -928,7 +949,7 @@ static void *vhost_user_test_setup_reconnect(GString *cmd_line, void *arg)
{
TestServer *s = test_server_new("reconnect", arg);
- g_thread_new("connect", connect_thread, s);
+ g_thread_unref(g_thread_new("connect", connect_thread, s));
append_mem_opts(s, cmd_line, 256, TEST_MEMFD_AUTO);
s->vu_ops->append_opts(s, cmd_line, ",server=on");
@@ -965,7 +986,7 @@ static void *vhost_user_test_setup_connect_fail(GString *cmd_line, void *arg)
s->test_fail = true;
- g_thread_new("connect", connect_thread, s);
+ g_thread_unref(g_thread_new("connect", connect_thread, s));
append_mem_opts(s, cmd_line, 256, TEST_MEMFD_AUTO);
s->vu_ops->append_opts(s, cmd_line, ",server=on");
@@ -980,7 +1001,7 @@ static void *vhost_user_test_setup_flags_mismatch(GString *cmd_line, void *arg)
s->test_flags = TEST_FLAGS_DISCONNECT;
- g_thread_new("connect", connect_thread, s);
+ g_thread_unref(g_thread_new("connect", connect_thread, s));
append_mem_opts(s, cmd_line, 256, TEST_MEMFD_AUTO);
s->vu_ops->append_opts(s, cmd_line, ",server=on");
@@ -1022,7 +1043,8 @@ static void test_multiqueue(void *obj, void *arg, QGuestAllocator *alloc)
static uint64_t vu_net_get_features(TestServer *s)
{
- uint64_t features = 0x1ULL << VHOST_F_LOG_ALL |
+ uint64_t features = 0x1ULL << VIRTIO_F_VERSION_1 |
+ 0x1ULL << VHOST_F_LOG_ALL |
0x1ULL << VHOST_USER_F_PROTOCOL_FEATURES;
if (s->queues > 1) {
@@ -1081,6 +1103,11 @@ static void register_vhost_user_test(void)
"virtio-net",
test_read_guest_mem, &opts);
+ opts.before = vhost_user_test_setup_shm;
+ qos_add_test("vhost-user/read-guest-mem/shm",
+ "virtio-net",
+ test_read_guest_mem, &opts);
+
if (qemu_memfd_check(MFD_ALLOW_SEALING)) {
opts.before = vhost_user_test_setup_memfd;
qos_add_test("vhost-user/read-guest-mem/memfd",
diff --git a/tests/qtest/virtio-9p-test.c b/tests/qtest/virtio-9p-test.c
index 3c8cd23..ac38ccf 100644
--- a/tests/qtest/virtio-9p-test.c
+++ b/tests/qtest/virtio-9p-test.c
@@ -20,6 +20,7 @@
#define tversion(...) v9fs_tversion((TVersionOpt) __VA_ARGS__)
#define tattach(...) v9fs_tattach((TAttachOpt) __VA_ARGS__)
#define tgetattr(...) v9fs_tgetattr((TGetAttrOpt) __VA_ARGS__)
+#define tsetattr(...) v9fs_tsetattr((TSetAttrOpt) __VA_ARGS__)
#define treaddir(...) v9fs_treaddir((TReadDirOpt) __VA_ARGS__)
#define tlopen(...) v9fs_tlopen((TLOpenOpt) __VA_ARGS__)
#define twrite(...) v9fs_twrite((TWriteOpt) __VA_ARGS__)
@@ -693,6 +694,64 @@ static void fs_unlinkat_hardlink(void *obj, void *data,
g_assert(stat(real_file, &st_real) == 0);
}
+static void fs_use_after_unlink(void *obj, void *data,
+ QGuestAllocator *t_alloc)
+{
+ QVirtio9P *v9p = obj;
+ v9fs_set_allocator(t_alloc);
+ static const uint32_t write_count = P9_MAX_SIZE / 2;
+ g_autofree char *real_file = virtio_9p_test_path("09/doa_file");
+ g_autofree char *buf = g_malloc0(write_count);
+ struct stat st_file;
+ struct v9fs_attr attr;
+ uint32_t fid_file;
+ uint32_t count;
+
+ tattach({ .client = v9p });
+
+ /* create a file "09/doa_file" and make sure it exists and is regular */
+ tmkdir({ .client = v9p, .atPath = "/", .name = "09" });
+ tlcreate({ .client = v9p, .atPath = "09", .name = "doa_file" });
+ g_assert(stat(real_file, &st_file) == 0);
+ g_assert((st_file.st_mode & S_IFMT) == S_IFREG);
+
+ /* request a FID for that regular file that we can work with next */
+ fid_file = twalk({
+ .client = v9p, .fid = 0, .path = "09/doa_file"
+ }).newfid;
+ g_assert(fid_file != 0);
+
+ /* now first open the file in write mode before ... */
+ tlopen({ .client = v9p, .fid = fid_file, .flags = O_WRONLY });
+ /* ... removing the file from file system */
+ tunlinkat({ .client = v9p, .atPath = "09", .name = "doa_file" });
+
+ /* file is removed, but we still have it open, so this should succeed */
+ tgetattr({
+ .client = v9p, .fid = fid_file, .request_mask = P9_GETATTR_BASIC,
+ .rgetattr.attr = &attr
+ });
+ count = twrite({
+ .client = v9p, .fid = fid_file, .offset = 0, .count = write_count,
+ .data = buf
+ }).count;
+ g_assert_cmpint(count, ==, write_count);
+
+ /* truncate file to (arbitrarily chosen) size 2001 */
+ tsetattr({
+ .client = v9p, .fid = fid_file, .attr = (v9fs_attr) {
+ .valid = P9_SETATTR_SIZE,
+ .size = 2001
+ }
+ });
+ /* truncate apparently succeeded, let's double-check the size */
+ tgetattr({
+ .client = v9p, .fid = fid_file, .request_mask = P9_GETATTR_BASIC,
+ .rgetattr.attr = &attr
+ });
+ g_assert_cmpint(attr.size, ==, 2001);
+}
+
static void cleanup_9p_local_driver(void *data)
{
/* remove previously created test dir when test is completed */
@@ -758,6 +817,8 @@ static void register_virtio_9p_test(void)
qos_add_test("local/hardlink_file", "virtio-9p", fs_hardlink_file, &opts);
qos_add_test("local/unlinkat_hardlink", "virtio-9p", fs_unlinkat_hardlink,
&opts);
+ qos_add_test("local/use_after_unlink", "virtio-9p", fs_use_after_unlink,
+ &opts);
}
libqos_init(register_virtio_9p_test);
diff --git a/tests/qtest/virtio-balloon-test.c b/tests/qtest/virtio-balloon-test.c
new file mode 100644
index 0000000..ecdd363
--- /dev/null
+++ b/tests/qtest/virtio-balloon-test.c
@@ -0,0 +1,57 @@
+/*
+ * QTest test cases for virtio balloon device
+ *
+ * Copyright (c) 2024 Gao Shiyuan <gaoshiyuan@baidu.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "libqtest.h"
+#include "standard-headers/linux/virtio_balloon.h"
+
+/*
+ * https://gitlab.com/qemu-project/qemu/-/issues/2576
+ * Used to trigger:
+ * virtio_address_space_lookup: Assertion `mrs.mr' failed.
+ */
+static void oss_fuzz_71649(void)
+{
+ QTestState *s = qtest_init("-device virtio-balloon -machine q35"
+ " -nodefaults");
+
+ qtest_outl(s, 0xcf8, 0x80000890);
+ qtest_outl(s, 0xcfc, 0x2);
+ qtest_outl(s, 0xcf8, 0x80000891);
+ qtest_inl(s, 0xcfc);
+ qtest_quit(s);
+}
+
+static void query_stats(void)
+{
+ QTestState *s = qtest_init("-device virtio-balloon,id=balloon"
+ " -nodefaults");
+ QDict *ret = qtest_qmp_assert_success_ref(
+ s,
+ "{ 'execute': 'qom-get', 'arguments': " \
+ "{ 'path': '/machine/peripheral/balloon', " \
+ " 'property': 'guest-stats' } }");
+ QDict *stats = qdict_get_qdict(ret, "stats");
+
+ /* We expect 1 entry in the dict for each known kernel stat */
+ assert(qdict_size(stats) == VIRTIO_BALLOON_S_NR);
+
+ qobject_unref(ret);
+ qtest_quit(s);
+}
+
+int main(int argc, char **argv)
+{
+ g_test_init(&argc, &argv, NULL);
+
+ qtest_add_func("virtio-balloon/oss_fuzz_71649", oss_fuzz_71649);
+ qtest_add_func("virtio-balloon/query-stats", query_stats);
+
+ return g_test_run();
+}
+
diff --git a/tests/qtest/virtio-iommu-test.c b/tests/qtest/virtio-iommu-test.c
index afb2259..98ffa27 100644
--- a/tests/qtest/virtio-iommu-test.c
+++ b/tests/qtest/virtio-iommu-test.c
@@ -105,7 +105,7 @@ static int send_map(QTestState *qts, QVirtioIOMMU *v_iommu,
QVirtQueue *vq = v_iommu->vq;
uint64_t ro_addr, wr_addr;
uint32_t free_head;
- struct virtio_iommu_req_map req;
+ struct virtio_iommu_req_map req = {};
size_t ro_size = sizeof(req) - sizeof(struct virtio_iommu_req_tail);
size_t wr_size = sizeof(struct virtio_iommu_req_tail);
struct virtio_iommu_req_tail buffer;
@@ -147,7 +147,7 @@ static int send_unmap(QTestState *qts, QVirtioIOMMU *v_iommu,
QVirtQueue *vq = v_iommu->vq;
uint64_t ro_addr, wr_addr;
uint32_t free_head;
- struct virtio_iommu_req_unmap req;
+ struct virtio_iommu_req_unmap req = {};
size_t ro_size = sizeof(req) - sizeof(struct virtio_iommu_req_tail);
size_t wr_size = sizeof(struct virtio_iommu_req_tail);
struct virtio_iommu_req_tail buffer;
diff --git a/tests/qtest/virtio-net-failover.c b/tests/qtest/virtio-net-failover.c
index 73dfabc..5baf81c 100644
--- a/tests/qtest/virtio-net-failover.c
+++ b/tests/qtest/virtio-net-failover.c
@@ -11,10 +11,11 @@
#include "libqtest.h"
#include "libqos/pci.h"
#include "libqos/pci-pc.h"
-#include "migration-helpers.h"
-#include "qapi/qmp/qdict.h"
-#include "qapi/qmp/qlist.h"
-#include "qapi/qmp/qjson.h"
+#include "migration/migration-qmp.h"
+#include "migration/migration-util.h"
+#include "qobject/qdict.h"
+#include "qobject/qlist.h"
+#include "qobject/qjson.h"
#include "libqos/malloc-pc.h"
#include "libqos/virtio-pci.h"
#include "hw/pci/pci.h"
@@ -772,7 +773,7 @@ static void test_migrate_in(gconstpointer opaque)
check_one_card(qts, true, "standby0", MAC_STANDBY0);
check_one_card(qts, false, "primary0", MAC_PRIMARY0);
- migrate_incoming_qmp(qts, uri, "{}");
+ migrate_incoming_qmp(qts, uri, NULL, "{}");
resp = get_failover_negociated_event(qts);
g_assert_cmpstr(qdict_get_str(resp, "device-id"), ==, "standby0");
@@ -894,7 +895,7 @@ static void test_off_migrate_in(gconstpointer opaque)
check_one_card(qts, true, "standby0", MAC_STANDBY0);
check_one_card(qts, true, "primary0", MAC_PRIMARY0);
- migrate_incoming_qmp(qts, uri, "{}");
+ migrate_incoming_qmp(qts, uri, NULL, "{}");
check_one_card(qts, true, "standby0", MAC_STANDBY0);
check_one_card(qts, true, "primary0", MAC_PRIMARY0);
@@ -1021,7 +1022,7 @@ static void test_guest_off_migrate_in(gconstpointer opaque)
check_one_card(qts, true, "standby0", MAC_STANDBY0);
check_one_card(qts, false, "primary0", MAC_PRIMARY0);
- migrate_incoming_qmp(qts, uri, "{}");
+ migrate_incoming_qmp(qts, uri, NULL, "{}");
check_one_card(qts, true, "standby0", MAC_STANDBY0);
check_one_card(qts, false, "primary0", MAC_PRIMARY0);
@@ -1746,7 +1747,7 @@ static void test_multi_in(gconstpointer opaque)
check_one_card(qts, true, "standby1", MAC_STANDBY1);
check_one_card(qts, false, "primary1", MAC_PRIMARY1);
- migrate_incoming_qmp(qts, uri, "{}");
+ migrate_incoming_qmp(qts, uri, NULL, "{}");
resp = get_failover_negociated_event(qts);
g_assert_cmpstr(qdict_get_str(resp, "device-id"), ==, "standby0");
diff --git a/tests/qtest/virtio-net-test.c b/tests/qtest/virtio-net-test.c
index 2df75c9..60e5229 100644
--- a/tests/qtest/virtio-net-test.c
+++ b/tests/qtest/virtio-net-test.c
@@ -11,7 +11,7 @@
#include "libqtest-single.h"
#include "qemu/iov.h"
#include "qemu/module.h"
-#include "qapi/qmp/qdict.h"
+#include "qobject/qdict.h"
#include "hw/virtio/virtio-net.h"
#include "libqos/qgraph.h"
#include "libqos/virtio-net.h"
diff --git a/tests/qtest/vmcoreinfo-test.c b/tests/qtest/vmcoreinfo-test.c
new file mode 100644
index 0000000..dcf3b5a
--- /dev/null
+++ b/tests/qtest/vmcoreinfo-test.c
@@ -0,0 +1,90 @@
+/*
+ * qtest vmcoreinfo test case
+ *
+ * Copyright Red Hat. 2025.
+ *
+ * Authors:
+ * Ani Sinha <anisinha@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/units.h"
+#include "libqos/libqos-pc.h"
+#include "libqtest.h"
+#include "standard-headers/linux/qemu_fw_cfg.h"
+#include "libqos/fw_cfg.h"
+#include "qemu/bswap.h"
+#include "hw/misc/vmcoreinfo.h"
+
+static void test_vmcoreinfo_write_basic(void)
+{
+ QFWCFG *fw_cfg;
+ QOSState *qs;
+ FWCfgVMCoreInfo info;
+ size_t filesize;
+ uint16_t guest_format;
+ uint16_t host_format;
+ uint32_t size;
+ uint64_t paddr;
+
+ qs = qtest_pc_boot("-device vmcoreinfo");
+ fw_cfg = pc_fw_cfg_init(qs->qts);
+
+ memset(&info, 0 , sizeof(info));
+ /* read vmcoreinfo and read back the host format */
+ filesize = qfw_cfg_read_file(fw_cfg, qs, FW_CFG_VMCOREINFO_FILENAME,
+ &info, sizeof(info));
+ g_assert_cmpint(filesize, ==, sizeof(info));
+
+ host_format = le16_to_cpu(info.host_format);
+ g_assert_cmpint(host_format, ==, FW_CFG_VMCOREINFO_FORMAT_ELF);
+
+ memset(&info, 0 , sizeof(info));
+ info.guest_format = cpu_to_le16(FW_CFG_VMCOREINFO_FORMAT_ELF);
+ info.size = cpu_to_le32(1 * MiB);
+ info.paddr = cpu_to_le64(0xffffff00);
+ info.host_format = cpu_to_le16(host_format);
+
+ /* write the values to the host */
+ filesize = qfw_cfg_write_file(fw_cfg, qs, FW_CFG_VMCOREINFO_FILENAME,
+ &info, sizeof(info));
+ g_assert_cmpint(filesize, ==, sizeof(info));
+
+ memset(&info, 0 , sizeof(info));
+
+ /* now read back the values we wrote and compare that they are the same */
+ filesize = qfw_cfg_read_file(fw_cfg, qs, FW_CFG_VMCOREINFO_FILENAME,
+ &info, sizeof(info));
+ g_assert_cmpint(filesize, ==, sizeof(info));
+
+ size = le32_to_cpu(info.size);
+ paddr = le64_to_cpu(info.paddr);
+ guest_format = le16_to_cpu(info.guest_format);
+
+ g_assert_cmpint(size, ==, 1 * MiB);
+ g_assert_cmpint(paddr, ==, 0xffffff00);
+ g_assert_cmpint(guest_format, ==, FW_CFG_VMCOREINFO_FORMAT_ELF);
+
+ pc_fw_cfg_uninit(fw_cfg);
+ qtest_shutdown(qs);
+}
+
+int main(int argc, char **argv)
+{
+ const char *arch = qtest_get_arch();
+
+ g_test_init(&argc, &argv, NULL);
+
+ if (strcmp(arch, "i386") && strcmp(arch, "x86_64")) {
+ /* skip for non-x86 */
+ exit(EXIT_SUCCESS);
+ }
+
+ qtest_add_func("vmcoreinfo/basic-write",
+ test_vmcoreinfo_write_basic);
+
+ return g_test_run();
+}
diff --git a/tests/qtest/vmgenid-test.c b/tests/qtest/vmgenid-test.c
index 29fee9e..e613374 100644
--- a/tests/qtest/vmgenid-test.c
+++ b/tests/qtest/vmgenid-test.c
@@ -15,7 +15,7 @@
#include "boot-sector.h"
#include "acpi-utils.h"
#include "libqtest.h"
-#include "qapi/qmp/qdict.h"
+#include "qobject/qdict.h"
#define VGID_GUID "324e6eaf-d1d1-4bf6-bf41-b9bb6c91fb87"
#define VMGENID_GUID_OFFSET 40 /* allow space for
diff --git a/tests/qtest/wdt_ib700-test.c b/tests/qtest/wdt_ib700-test.c
index 797288d..1754757 100644
--- a/tests/qtest/wdt_ib700-test.c
+++ b/tests/qtest/wdt_ib700-test.c
@@ -9,7 +9,7 @@
#include "qemu/osdep.h"
#include "libqtest.h"
-#include "qapi/qmp/qdict.h"
+#include "qobject/qdict.h"
#include "qemu/timer.h"
static void qmp_check_no_event(QTestState *s)