aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/qtest/libqos/libqos.c49
-rw-r--r--tests/qtest/libqos/libqos.h2
-rw-r--r--tests/qtest/libqtest.c52
-rw-r--r--tests/qtest/libqtest.h20
-rw-r--r--tests/qtest/migration-test.c143
-rw-r--r--tests/qtest/readconfig-test.c204
-rw-r--r--tests/tcg/s390x/Makefile.softmmu-target1
-rw-r--r--tests/tcg/s390x/Makefile.target3
-rw-r--r--tests/tcg/s390x/epsw.c23
-rw-r--r--tests/tcg/s390x/gdbstub/test-svc.py2
-rw-r--r--tests/tcg/s390x/hello-s390x-asm.S4
-rw-r--r--tests/tcg/s390x/larl.c21
-rw-r--r--tests/tcg/s390x/lra.S19
-rw-r--r--tests/tcg/s390x/mdeb.c30
-rw-r--r--tests/tcg/s390x/mie3-mvcrl.c46
15 files changed, 535 insertions, 84 deletions
diff --git a/tests/qtest/libqos/libqos.c b/tests/qtest/libqos/libqos.c
index 5ffda080ec..5c0fa1f7c5 100644
--- a/tests/qtest/libqos/libqos.c
+++ b/tests/qtest/libqos/libqos.c
@@ -137,56 +137,9 @@ void migrate(QOSState *from, QOSState *to, const char *uri)
migrate_allocator(&from->alloc, &to->alloc);
}
-bool have_qemu_img(void)
-{
- char *rpath;
- const char *path = getenv("QTEST_QEMU_IMG");
- if (!path) {
- return false;
- }
-
- rpath = realpath(path, NULL);
- if (!rpath) {
- return false;
- } else {
- free(rpath);
- return true;
- }
-}
-
-void mkimg(const char *file, const char *fmt, unsigned size_mb)
-{
- gchar *cli;
- bool ret;
- int rc;
- GError *err = NULL;
- char *qemu_img_path;
- gchar *out, *out2;
- char *qemu_img_abs_path;
-
- qemu_img_path = getenv("QTEST_QEMU_IMG");
- g_assert(qemu_img_path);
- qemu_img_abs_path = realpath(qemu_img_path, NULL);
- g_assert(qemu_img_abs_path);
-
- cli = g_strdup_printf("%s create -f %s %s %uM", qemu_img_abs_path,
- fmt, file, size_mb);
- ret = g_spawn_command_line_sync(cli, &out, &out2, &rc, &err);
- if (err || !g_spawn_check_exit_status(rc, &err)) {
- fprintf(stderr, "%s\n", err->message);
- g_error_free(err);
- }
- g_assert(ret && !err);
-
- g_free(out);
- g_free(out2);
- g_free(cli);
- free(qemu_img_abs_path);
-}
-
void mkqcow2(const char *file, unsigned size_mb)
{
- return mkimg(file, "qcow2", size_mb);
+ g_assert_true(mkimg(file, "qcow2", size_mb));
}
void prepare_blkdebug_script(const char *debug_fn, const char *event)
diff --git a/tests/qtest/libqos/libqos.h b/tests/qtest/libqos/libqos.h
index 12d05b2365..c04950e2b1 100644
--- a/tests/qtest/libqos/libqos.h
+++ b/tests/qtest/libqos/libqos.h
@@ -27,8 +27,6 @@ QOSState *qtest_boot(QOSOps *ops, const char *cmdline_fmt, ...)
G_GNUC_PRINTF(2, 3);
void qtest_common_shutdown(QOSState *qs);
void qtest_shutdown(QOSState *qs);
-bool have_qemu_img(void);
-void mkimg(const char *file, const char *fmt, unsigned size_mb);
void mkqcow2(const char *file, unsigned size_mb);
void migrate(QOSState *from, QOSState *to, const char *uri);
void prepare_blkdebug_script(const char *debug_fn, const char *event);
diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c
index 79152f0ec3..c22dfc30d3 100644
--- a/tests/qtest/libqtest.c
+++ b/tests/qtest/libqtest.c
@@ -1742,3 +1742,55 @@ bool qtest_qom_get_bool(QTestState *s, const char *path, const char *property)
return b;
}
+
+bool have_qemu_img(void)
+{
+ char *rpath;
+ const char *path = getenv("QTEST_QEMU_IMG");
+ if (!path) {
+ return false;
+ }
+
+ rpath = realpath(path, NULL);
+ if (!rpath) {
+ return false;
+ } else {
+ free(rpath);
+ return true;
+ }
+}
+
+bool mkimg(const char *file, const char *fmt, unsigned size_mb)
+{
+ gchar *cli;
+ bool ret;
+ int rc;
+ GError *err = NULL;
+ char *qemu_img_path;
+ gchar *out, *out2;
+ char *qemu_img_abs_path;
+
+ qemu_img_path = getenv("QTEST_QEMU_IMG");
+ if (!qemu_img_path) {
+ return false;
+ }
+ qemu_img_abs_path = realpath(qemu_img_path, NULL);
+ if (!qemu_img_abs_path) {
+ return false;
+ }
+
+ cli = g_strdup_printf("%s create -f %s %s %uM", qemu_img_abs_path,
+ fmt, file, size_mb);
+ ret = g_spawn_command_line_sync(cli, &out, &out2, &rc, &err);
+ if (err || !g_spawn_check_exit_status(rc, &err)) {
+ fprintf(stderr, "%s\n", err->message);
+ g_error_free(err);
+ }
+
+ g_free(out);
+ g_free(out2);
+ g_free(cli);
+ free(qemu_img_abs_path);
+
+ return ret && !err;
+}
diff --git a/tests/qtest/libqtest.h b/tests/qtest/libqtest.h
index 913acc3d5c..3a71bc45fc 100644
--- a/tests/qtest/libqtest.h
+++ b/tests/qtest/libqtest.h
@@ -994,4 +994,24 @@ bool qtest_qom_get_bool(QTestState *s, const char *path, const char *property);
*/
pid_t qtest_pid(QTestState *s);
+/**
+ * have_qemu_img:
+ *
+ * Returns: true if "qemu-img" is available.
+ */
+bool have_qemu_img(void);
+
+/**
+ * mkimg:
+ * @file: File name of the image that should be created
+ * @fmt: Format, e.g. "qcow2" or "raw"
+ * @size_mb: Size of the image in megabytes
+ *
+ * Create a disk image with qemu-img. Note that the QTEST_QEMU_IMG
+ * environment variable must point to the qemu-img file.
+ *
+ * Returns: true if the image has been created successfully.
+ */
+bool mkimg(const char *file, const char *fmt, unsigned size_mb);
+
#endif
diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c
index b9cc194100..efa8c729db 100644
--- a/tests/qtest/migration-test.c
+++ b/tests/qtest/migration-test.c
@@ -47,6 +47,20 @@ static bool got_src_stop;
static bool got_dst_resume;
/*
+ * 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
*/
@@ -445,6 +459,91 @@ static void migrate_ensure_converge(QTestState *who)
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 untill 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);
+
+ /*
+ * 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' }");
@@ -577,7 +676,10 @@ typedef struct {
MIG_TEST_FAIL_DEST_QUIT_ERR,
} result;
- /* Optional: set number of migration passes to wait for, if live==true */
+ /*
+ * 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;
/*
@@ -1165,12 +1267,14 @@ static int migrate_postcopy_prepare(QTestState **from_ptr,
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, uri, "{}");
- wait_for_migration_pass(from);
+ migrate_wait_for_dirty_mem(from, to);
*from_ptr = from;
*to_ptr = to;
@@ -1405,14 +1509,8 @@ static void test_precopy_common(MigrateCommon *args)
}
if (args->live) {
- /*
- * Testing live migration, we want to ensure that some
- * memory is re-dirtied after being transferred, so that
- * we exercise logic for dirty page handling. We achieve
- * this with a ridiculosly low bandwidth that guarantees
- * non-convergance.
- */
migrate_ensure_non_converge(from);
+ migrate_prepare_for_dirty_mem(from);
} else {
/*
* Testing non-live migration, we allow it to run at
@@ -1447,13 +1545,16 @@ static void test_precopy_common(MigrateCommon *args)
}
} else {
if (args->live) {
- if (args->iterations) {
- while (args->iterations--) {
- wait_for_migration_pass(from);
- }
- } else {
+ /*
+ * 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);
@@ -1586,6 +1687,9 @@ static void test_ignore_shared(void)
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);
@@ -1594,7 +1698,7 @@ static void test_ignore_shared(void)
migrate_qmp(from, uri, "{}");
- wait_for_migration_pass(from);
+ migrate_wait_for_dirty_mem(from, to);
if (!got_src_stop) {
qtest_qmp_eventwait(from, "STOP");
@@ -2325,6 +2429,7 @@ static void test_multifd_tcp_cancel(void)
}
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);
@@ -2343,7 +2448,7 @@ static void test_multifd_tcp_cancel(void)
migrate_qmp(from, uri, "{}");
- wait_for_migration_pass(from);
+ migrate_wait_for_dirty_mem(from, to);
migrate_cancel(from);
@@ -2372,11 +2477,13 @@ static void test_multifd_tcp_cancel(void)
wait_for_migration_status(from, "cancelled", NULL);
- migrate_ensure_converge(from);
+ migrate_ensure_non_converge(from);
migrate_qmp(from, uri, "{}");
- wait_for_migration_pass(from);
+ migrate_wait_for_dirty_mem(from, to);
+
+ migrate_ensure_converge(from);
if (!got_src_stop) {
qtest_qmp_eventwait(from, "STOP");
diff --git a/tests/qtest/readconfig-test.c b/tests/qtest/readconfig-test.c
index ac7242451b..760f974e63 100644
--- a/tests/qtest/readconfig-test.c
+++ b/tests/qtest/readconfig-test.c
@@ -48,7 +48,7 @@ static QTestState *qtest_init_with_config(const char *cfgdata)
return qts;
}
-static void test_x86_memdev_resp(QObject *res)
+static void test_x86_memdev_resp(QObject *res, const char *mem_id, int size)
{
Visitor *v;
g_autoptr(MemdevList) memdevs = NULL;
@@ -63,8 +63,8 @@ static void test_x86_memdev_resp(QObject *res)
g_assert(!memdevs->next);
memdev = memdevs->value;
- g_assert_cmpstr(memdev->id, ==, "ram");
- g_assert_cmpint(memdev->size, ==, 200 * MiB);
+ g_assert_cmpstr(memdev->id, ==, mem_id);
+ g_assert_cmpint(memdev->size, ==, size * MiB);
visit_free(v);
}
@@ -80,7 +80,7 @@ static void test_x86_memdev(void)
qts = qtest_init_with_config(cfgdata);
/* Test valid command */
resp = qtest_qmp(qts, "{ 'execute': 'query-memdev' }");
- test_x86_memdev_resp(qdict_get(resp, "return"));
+ test_x86_memdev_resp(qdict_get(resp, "return"), "ram", 200);
qobject_unref(resp);
qtest_quit(qts);
@@ -197,6 +197,189 @@ static void test_docs_config_ich9(void)
qtest_quit(qts);
}
+#if defined(CONFIG_POSIX) && defined(CONFIG_SLIRP)
+
+static char *make_temp_img(const char *template, const char *format, int size)
+{
+ GError *error = NULL;
+ char *temp_name;
+ int fd;
+
+ /* Create a temporary image names */
+ fd = g_file_open_tmp(template, &temp_name, &error);
+ if (fd == -1) {
+ fprintf(stderr, "unable to create file: %s\n", error->message);
+ g_error_free(error);
+ return NULL;
+ }
+ close(fd);
+
+ if (!mkimg(temp_name, format, size)) {
+ fprintf(stderr, "qemu-img failed to create %s\n", temp_name);
+ g_free(temp_name);
+ return NULL;
+ }
+
+ return temp_name;
+}
+
+struct device {
+ const char *name;
+ const char *type;
+};
+
+static void test_docs_q35(const char *input_file, struct device *devices)
+{
+ QTestState *qts;
+ QDict *resp;
+ QObject *qobj;
+ int ret, i;
+ g_autofree char *cfg_file = NULL, *sedcmd = NULL;
+ g_autofree char *hd_file = NULL, *cd_file = NULL;
+
+ /* Check that all the devices are available in the QEMU binary */
+ for (i = 0; devices[i].name; i++) {
+ if (!qtest_has_device(devices[i].type)) {
+ g_test_skip("one of the required devices is not available");
+ return;
+ }
+ }
+
+ hd_file = make_temp_img("qtest_disk_XXXXXX.qcow2", "qcow2", 1);
+ cd_file = make_temp_img("qtest_cdrom_XXXXXX.iso", "raw", 1);
+ if (!hd_file || !cd_file) {
+ g_test_skip("could not create disk images");
+ goto cleanup;
+ }
+
+ /* Create a temporary config file where we replace the disk image names */
+ ret = g_file_open_tmp("q35-emulated-XXXXXX.cfg", &cfg_file, NULL);
+ if (ret == -1) {
+ g_test_skip("could not create temporary config file");
+ goto cleanup;
+ }
+ close(ret);
+
+ sedcmd = g_strdup_printf("sed -e 's,guest.qcow2,%s,' -e 's,install.iso,%s,'"
+ " %s %s > '%s'",
+ hd_file, cd_file,
+ !qtest_has_accel("kvm") ? "-e '/accel/d'" : "",
+ input_file, cfg_file);
+ ret = system(sedcmd);
+ if (ret) {
+ g_test_skip("could not modify temporary config file");
+ goto cleanup;
+ }
+
+ qts = qtest_initf("-machine none -nodefaults -readconfig %s", cfg_file);
+
+ /* Check memory size */
+ resp = qtest_qmp(qts, "{ 'execute': 'query-memdev' }");
+ test_x86_memdev_resp(qdict_get(resp, "return"), "pc.ram", 1024);
+ qobject_unref(resp);
+
+ resp = qtest_qmp(qts, "{ 'execute': 'qom-list',"
+ " 'arguments': {'path': '/machine/peripheral' }}");
+ qobj = qdict_get(resp, "return");
+
+ /* Check that all the devices have been created */
+ for (i = 0; devices[i].name; i++) {
+ test_object_available(qobj, devices[i].name, devices[i].type);
+ }
+
+ qobject_unref(resp);
+
+ qtest_quit(qts);
+
+cleanup:
+ if (hd_file) {
+ unlink(hd_file);
+ }
+ if (cd_file) {
+ unlink(cd_file);
+ }
+ if (cfg_file) {
+ unlink(cfg_file);
+ }
+}
+
+static void test_docs_q35_emulated(void)
+{
+ struct device devices[] = {
+ { "ich9-pcie-port-1", "ioh3420" },
+ { "ich9-pcie-port-2", "ioh3420" },
+ { "ich9-pcie-port-3", "ioh3420" },
+ { "ich9-pcie-port-4", "ioh3420" },
+ { "ich9-pci-bridge", "i82801b11-bridge" },
+ { "ich9-ehci-1", "ich9-usb-ehci1" },
+ { "ich9-ehci-2", "ich9-usb-ehci2" },
+ { "ich9-uhci-1", "ich9-usb-uhci1" },
+ { "ich9-uhci-2", "ich9-usb-uhci2" },
+ { "ich9-uhci-3", "ich9-usb-uhci3" },
+ { "ich9-uhci-4", "ich9-usb-uhci4" },
+ { "ich9-uhci-5", "ich9-usb-uhci5" },
+ { "ich9-uhci-6", "ich9-usb-uhci6" },
+ { "sata-disk", "ide-hd" },
+ { "sata-optical-disk", "ide-cd" },
+ { "net", "e1000" },
+ { "video", "VGA" },
+ { "ich9-hda-audio", "ich9-intel-hda" },
+ { "ich9-hda-duplex", "hda-duplex" },
+ { NULL, NULL }
+ };
+
+ test_docs_q35("docs/config/q35-emulated.cfg", devices);
+}
+
+static void test_docs_q35_virtio_graphical(void)
+{
+ struct device devices[] = {
+ { "pcie.1", "pcie-root-port" },
+ { "pcie.2", "pcie-root-port" },
+ { "pcie.3", "pcie-root-port" },
+ { "pcie.4", "pcie-root-port" },
+ { "pcie.5", "pcie-root-port" },
+ { "pcie.6", "pcie-root-port" },
+ { "pcie.7", "pcie-root-port" },
+ { "pcie.8", "pcie-root-port" },
+ { "scsi", "virtio-scsi-pci" },
+ { "scsi-disk", "scsi-hd" },
+ { "scsi-optical-disk", "scsi-cd" },
+ { "net", "virtio-net-pci" },
+ { "usb", "nec-usb-xhci" },
+ { "tablet", "usb-tablet" },
+ { "video", "qxl-vga" },
+ { "sound", "ich9-intel-hda" },
+ { "duplex", "hda-duplex" },
+ { NULL, NULL }
+ };
+
+ test_docs_q35("docs/config/q35-virtio-graphical.cfg", devices);
+}
+
+static void test_docs_q35_virtio_serial(void)
+{
+ struct device devices[] = {
+ { "pcie.1", "pcie-root-port" },
+ { "pcie.2", "pcie-root-port" },
+ { "pcie.3", "pcie-root-port" },
+ { "pcie.4", "pcie-root-port" },
+ { "pcie.5", "pcie-root-port" },
+ { "pcie.6", "pcie-root-port" },
+ { "pcie.7", "pcie-root-port" },
+ { "pcie.8", "pcie-root-port" },
+ { "scsi", "virtio-scsi-pci" },
+ { "scsi-disk", "scsi-hd" },
+ { "scsi-optical-disk", "scsi-cd" },
+ { "net", "virtio-net-pci" },
+ { NULL, NULL }
+ };
+
+ test_docs_q35("docs/config/q35-virtio-serial.cfg", devices);
+}
+
+#endif /* CONFIG_LINUX */
+
int main(int argc, char *argv[])
{
const char *arch;
@@ -211,6 +394,19 @@ int main(int argc, char *argv[])
qtest_has_device("ich9-usb-uhci1")) {
qtest_add_func("readconfig/x86/ich9-ehci-uhci", test_docs_config_ich9);
}
+#if defined(CONFIG_POSIX) && defined(CONFIG_SLIRP)
+ qtest_add_func("readconfig/x86/q35-emulated", test_docs_q35_emulated);
+ qtest_add_func("readconfig/x86/q35-virtio-graphical",
+ test_docs_q35_virtio_graphical);
+ if (g_test_slow()) {
+ /*
+ * q35-virtio-serial.cfg is a subset of q35-virtio-graphical.cfg,
+ * so we can skip the test in quick mode
+ */
+ qtest_add_func("readconfig/x86/q35-virtio-serial",
+ test_docs_q35_virtio_serial);
+ }
+#endif
}
#if defined(CONFIG_SPICE) && !defined(__FreeBSD__)
qtest_add_func("readconfig/spice", test_spice);
diff --git a/tests/tcg/s390x/Makefile.softmmu-target b/tests/tcg/s390x/Makefile.softmmu-target
index 44dfd71629..242c7b0f83 100644
--- a/tests/tcg/s390x/Makefile.softmmu-target
+++ b/tests/tcg/s390x/Makefile.softmmu-target
@@ -20,6 +20,7 @@ ASM_TESTS = \
sam \
lpsw \
lpswe-early \
+ lra \
ssm-early \
stosm-early \
unaligned-lowcore
diff --git a/tests/tcg/s390x/Makefile.target b/tests/tcg/s390x/Makefile.target
index 85abfbb98c..19fbbc6e53 100644
--- a/tests/tcg/s390x/Makefile.target
+++ b/tests/tcg/s390x/Makefile.target
@@ -36,6 +36,9 @@ TESTS+=rxsbg
TESTS+=ex-relative-long
TESTS+=ex-branch
TESTS+=mxdb
+TESTS+=epsw
+TESTS+=larl
+TESTS+=mdeb
cdsg: CFLAGS+=-pthread
cdsg: LDFLAGS+=-pthread
diff --git a/tests/tcg/s390x/epsw.c b/tests/tcg/s390x/epsw.c
new file mode 100644
index 0000000000..affb1a5e3a
--- /dev/null
+++ b/tests/tcg/s390x/epsw.c
@@ -0,0 +1,23 @@
+/*
+ * Test the EPSW instruction.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#include <assert.h>
+#include <stdlib.h>
+
+int main(void)
+{
+ unsigned long r1 = 0x1234567887654321UL, r2 = 0x8765432112345678UL;
+
+ asm("cr %[r1],%[r2]\n" /* cc = 1 */
+ "epsw %[r1],%[r2]"
+ : [r1] "+r" (r1), [r2] "+r" (r2) : : "cc");
+
+ /* Do not check the R and RI bits. */
+ r1 &= ~0x40000008UL;
+ assert(r1 == 0x1234567807051001UL);
+ assert(r2 == 0x8765432180000000UL);
+
+ return EXIT_SUCCESS;
+}
diff --git a/tests/tcg/s390x/gdbstub/test-svc.py b/tests/tcg/s390x/gdbstub/test-svc.py
index 7851ca7284..18fad3f163 100644
--- a/tests/tcg/s390x/gdbstub/test-svc.py
+++ b/tests/tcg/s390x/gdbstub/test-svc.py
@@ -25,7 +25,7 @@ def run_test():
gdb.execute("si")
report("larl\t" in gdb.execute("x/i $pc", False, True), "insn #2")
gdb.execute("si")
- report("lghi\t" in gdb.execute("x/i $pc", False, True), "insn #3")
+ report("lgrl\t" in gdb.execute("x/i $pc", False, True), "insn #3")
gdb.execute("si")
report("svc\t" in gdb.execute("x/i $pc", False, True), "insn #4")
gdb.execute("si")
diff --git a/tests/tcg/s390x/hello-s390x-asm.S b/tests/tcg/s390x/hello-s390x-asm.S
index 2e9faa1604..4dbda12d35 100644
--- a/tests/tcg/s390x/hello-s390x-asm.S
+++ b/tests/tcg/s390x/hello-s390x-asm.S
@@ -8,7 +8,7 @@ _start:
/* puts("Hello, World!"); */
lghi %r2,1
larl %r3,foo
-lghi %r4,foo_end-foo
+lgrl %r4,foo_len
svc 4
/* exit(0); */
@@ -18,3 +18,5 @@ svc 1
.align 2
foo: .asciz "Hello, World!\n"
foo_end:
+.align 8
+foo_len: .quad foo_end-foo
diff --git a/tests/tcg/s390x/larl.c b/tests/tcg/s390x/larl.c
new file mode 100644
index 0000000000..7c95f89be7
--- /dev/null
+++ b/tests/tcg/s390x/larl.c
@@ -0,0 +1,21 @@
+/*
+ * Test the LARL instruction.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#include <stdlib.h>
+
+int main(void)
+{
+ long algfi = (long)main;
+ long larl;
+
+ /*
+ * The compiler may emit larl for the C addition, so compute the expected
+ * value using algfi.
+ */
+ asm("algfi %[r],0xd0000000" : [r] "+r" (algfi) : : "cc");
+ asm("larl %[r],main+0xd0000000" : [r] "=r" (larl));
+
+ return algfi == larl ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/tests/tcg/s390x/lra.S b/tests/tcg/s390x/lra.S
new file mode 100644
index 0000000000..79ab86f36b
--- /dev/null
+++ b/tests/tcg/s390x/lra.S
@@ -0,0 +1,19 @@
+ .org 0x200 /* lowcore padding */
+ .globl _start
+_start:
+ lgrl %r1,initial_r1
+ lra %r1,0(%r1)
+ cgrl %r1,expected_r1
+ jne 1f
+ lpswe success_psw
+1:
+ lpswe failure_psw
+ .align 8
+initial_r1:
+ .quad 0x8765432112345678
+expected_r1:
+ .quad 0x8765432180000038 /* ASCE type exception */
+success_psw:
+ .quad 0x2000000000000,0xfff /* see is_special_wait_psw() */
+failure_psw:
+ .quad 0x2000000000000,0 /* disabled wait */
diff --git a/tests/tcg/s390x/mdeb.c b/tests/tcg/s390x/mdeb.c
new file mode 100644
index 0000000000..4897d28069
--- /dev/null
+++ b/tests/tcg/s390x/mdeb.c
@@ -0,0 +1,30 @@
+/*
+ * Test the MDEB and MDEBR instructions.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#include <assert.h>
+#include <stdlib.h>
+
+int main(void)
+{
+ union {
+ float f[2];
+ double d;
+ } a;
+ float b;
+
+ a.f[0] = 1.2345;
+ a.f[1] = 999;
+ b = 6.789;
+ asm("mdeb %[a],%[b]" : [a] "+f" (a.d) : [b] "R" (b));
+ assert(a.d > 8.38 && a.d < 8.39);
+
+ a.f[0] = 1.2345;
+ a.f[1] = 999;
+ b = 6.789;
+ asm("mdebr %[a],%[b]" : [a] "+f" (a.d) : [b] "f" (b));
+ assert(a.d > 8.38 && a.d < 8.39);
+
+ return EXIT_SUCCESS;
+}
diff --git a/tests/tcg/s390x/mie3-mvcrl.c b/tests/tcg/s390x/mie3-mvcrl.c
index 93c7b0a290..6d3d049f2c 100644
--- a/tests/tcg/s390x/mie3-mvcrl.c
+++ b/tests/tcg/s390x/mie3-mvcrl.c
@@ -1,29 +1,55 @@
+#include <stdbool.h>
#include <stdint.h>
+#include <stdlib.h>
#include <string.h>
-
-static inline void mvcrl_8(const char *dst, const char *src)
+static void mvcrl(const char *dst, const char *src, size_t len)
{
+ register long r0 asm("r0") = len;
+
asm volatile (
- "llill %%r0, 8\n"
".insn sse, 0xE50A00000000, 0(%[dst]), 0(%[src])"
- : : [dst] "d" (dst), [src] "d" (src)
- : "r0", "memory");
+ : : [dst] "d" (dst), [src] "d" (src), "r" (r0)
+ : "memory");
}
-
-int main(int argc, char *argv[])
+static bool test(void)
{
const char *alpha = "abcdefghijklmnop";
/* array missing 'i' */
- char tstr[17] = "abcdefghjklmnop\0" ;
+ char tstr[17] = "abcdefghjklmnop\0";
/* mvcrl reference use: 'open a hole in an array' */
- mvcrl_8(tstr + 9, tstr + 8);
+ mvcrl(tstr + 9, tstr + 8, 8);
/* place missing 'i' */
tstr[8] = 'i';
- return strncmp(alpha, tstr, 16ul);
+ return strncmp(alpha, tstr, 16ul) == 0;
+}
+
+static bool test_bad_r0(void)
+{
+ char src[256] = { 0 };
+
+ /*
+ * PoP says: Bits 32-55 of general register 0 should contain zeros;
+ * otherwise, the program may not operate compatibly in the future.
+ *
+ * Try it anyway in order to check whether this would crash QEMU itself.
+ */
+ mvcrl(src, src, (size_t)-1);
+
+ return true;
+}
+
+int main(void)
+{
+ bool ok = true;
+
+ ok &= test();
+ ok &= test_bad_r0();
+
+ return ok ? EXIT_SUCCESS : EXIT_FAILURE;
}