summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.editorconfig5
-rw-r--r--.travis.yml2
-rw-r--r--MAINTAINERS8
-rw-r--r--Makefile4
-rw-r--r--Makefile.target16
-rw-r--r--default-configs/arm-softmmu.mak1
-rw-r--r--default-configs/lm32-softmmu.mak2
-rw-r--r--default-configs/m68k-softmmu.mak2
-rw-r--r--default-configs/mips-softmmu-common.mak1
-rw-r--r--default-configs/nios2-softmmu.mak2
-rw-r--r--default-configs/xtensa-softmmu.mak2
-rw-r--r--gdbstub.c2
-rw-r--r--hw/Kconfig1
-rw-r--r--hw/Makefile.objs1
-rw-r--r--hw/mips/mips_malta.c2
-rw-r--r--hw/semihosting/Kconfig3
-rw-r--r--hw/semihosting/Makefile.objs2
-rw-r--r--hw/semihosting/config.c186
-rw-r--r--hw/semihosting/console.c84
-rw-r--r--include/hw/semihosting/console.h38
-rw-r--r--include/hw/semihosting/semihost.h (renamed from include/exec/semihost.h)17
-rw-r--r--include/sysemu/sysemu.h1
-rw-r--r--linux-user/Makefile.objs2
-rw-r--r--linux-user/arm/semihost.c24
-rw-r--r--qemu-options.hx6
-rw-r--r--stubs/Makefile.objs1
-rw-r--r--stubs/semihost.c70
-rw-r--r--target/arm/arm-semi.c96
-rw-r--r--target/arm/helper.c2
-rw-r--r--target/arm/translate-a64.c2
-rw-r--r--target/arm/translate.c2
-rw-r--r--target/lm32/helper.c2
-rw-r--r--target/m68k/op_helper.c2
-rw-r--r--target/mips/Makefile.objs3
-rw-r--r--target/mips/helper.h2
-rw-r--r--target/mips/mips-semi.c14
-rw-r--r--target/mips/translate.c10
-rw-r--r--target/nios2/helper.c2
-rw-r--r--target/xtensa/translate.c2
-rw-r--r--target/xtensa/xtensa-semi.c2
-rw-r--r--tests/docker/dockerfiles/fedora.docker7
-rw-r--r--tests/docker/dockerfiles/ubuntu1804.docker57
-rwxr-xr-xtests/qemu-iotests/check177
-rw-r--r--tests/qemu-iotests/group177
-rw-r--r--tests/tcg/Makefile1
-rw-r--r--tests/tcg/aarch64/Makefile.softmmu-target34
-rw-r--r--tests/tcg/aarch64/system/boot.S239
-rw-r--r--tests/tcg/aarch64/system/kernel.ld24
-rw-r--r--tests/tcg/alpha/Makefile.softmmu-target34
-rw-r--r--tests/tcg/alpha/system/boot.S511
-rw-r--r--tests/tcg/alpha/system/kernel.ld30
-rw-r--r--tests/tcg/i386/Makefile.softmmu-target4
-rw-r--r--tests/tcg/i386/system/memory.c243
-rw-r--r--tests/tcg/minilib/printf.c3
-rw-r--r--tests/tcg/multiarch/system/Makefile.softmmu-target14
-rw-r--r--tests/tcg/multiarch/system/hello.c (renamed from tests/tcg/i386/system/hello.c)0
-rw-r--r--tests/tcg/multiarch/system/memory.c449
-rw-r--r--vl.c128
58 files changed, 2178 insertions, 580 deletions
diff --git a/.editorconfig b/.editorconfig
index 1582883393..df6db65531 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -26,6 +26,11 @@ file_type_emacs = makefile
indent_style = space
indent_size = 4
+[*.{s,S}]
+indent_style = tab
+indent_size = 8
+file_type_emacs = asm
+
[*.{vert,frag}]
file_type_emacs = glsl
diff --git a/.travis.yml b/.travis.yml
index 6fd89b3d91..b053a836a3 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -284,5 +284,5 @@ matrix:
# Run check-tcg against softmmu targets
- env:
- - CONFIG="--target-list=xtensa-softmmu,arm-softmmu"
+ - CONFIG="--target-list=xtensa-softmmu,arm-softmmu,aarch64-softmmu,alpha-softmmu"
- TEST_CMD="make -j3 check-tcg V=1"
diff --git a/MAINTAINERS b/MAINTAINERS
index 8c34d5c34b..1f5f8b7a2c 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -470,6 +470,7 @@ M: Richard Henderson <rth@twiddle.net>
S: Maintained
F: hw/alpha/
F: hw/isa/smc37c669-superio.c
+F: tests/tcg/alpha/system/
ARM Machines
------------
@@ -2563,6 +2564,13 @@ F: docs/pvrdma.txt
F: contrib/rdmacm-mux/*
F: qapi/rdma.json
+Semihosting
+M: Alex Bennée <alex.bennee@linaro.org>
+L: qemu-devel@nongnu.org
+S: Maintained
+F: hw/semihosting/
+F: include/hw/semihosting/
+
Build and test automation
-------------------------
Build and test automation
diff --git a/Makefile b/Makefile
index e02b88bcb1..f0be624f47 100644
--- a/Makefile
+++ b/Makefile
@@ -1009,7 +1009,9 @@ $(filter %.1 %.7 %.8,$(DOCS)): scripts/texi2pod.pl
%/coverage-report.html:
@mkdir -p $*
$(call quiet-command,\
- gcovr -r $(SRC_PATH) --object-directory $(BUILD_PATH) \
+ gcovr -r $(SRC_PATH) \
+ $(foreach t, $(TARGET_DIRS), --object-directory $(BUILD_DIR)/$(t)) \
+ --object-directory $(BUILD_DIR) \
-p --html --html-details -o $@, \
"GEN", "coverage-report.html")
diff --git a/Makefile.target b/Makefile.target
index 4ef4ce5996..ecd856e3a3 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -238,3 +238,19 @@ endif
generated-files-y += config-target.h
Makefile: $(generated-files-y)
+
+# Reports/Analysis
+#
+# The target specific coverage report only cares about target specific
+# blobs and not the shared code.
+#
+
+%/coverage-report.html:
+ @mkdir -p $*
+ $(call quiet-command,\
+ gcovr -r $(SRC_PATH) --object-directory $(CURDIR) \
+ -p --html --html-details -o $@, \
+ "GEN", "coverage-report.html")
+
+.PHONY: coverage-report
+coverage-report: $(CURDIR)/reports/coverage/coverage-report.html
diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak
index f23ecfd5c5..1f2e0e7fde 100644
--- a/default-configs/arm-softmmu.mak
+++ b/default-configs/arm-softmmu.mak
@@ -39,3 +39,4 @@ CONFIG_MICROBIT=y
CONFIG_FSL_IMX25=y
CONFIG_FSL_IMX7=y
CONFIG_FSL_IMX6UL=y
+CONFIG_SEMIHOSTING=y
diff --git a/default-configs/lm32-softmmu.mak b/default-configs/lm32-softmmu.mak
index 6d259665d6..115b3e34c9 100644
--- a/default-configs/lm32-softmmu.mak
+++ b/default-configs/lm32-softmmu.mak
@@ -4,6 +4,8 @@
#
#CONFIG_MILKYMIST_TMU2=n # disabling it actually causes compile-time failures
+CONFIG_SEMIHOSTING=y
+
# Boards:
#
CONFIG_LM32=y
diff --git a/default-configs/m68k-softmmu.mak b/default-configs/m68k-softmmu.mak
index e17495e2a0..4049a8f2ba 100644
--- a/default-configs/m68k-softmmu.mak
+++ b/default-configs/m68k-softmmu.mak
@@ -1,5 +1,7 @@
# Default configuration for m68k-softmmu
+CONFIG_SEMIHOSTING=y
+
# Boards:
#
CONFIG_AN5206=y
diff --git a/default-configs/mips-softmmu-common.mak b/default-configs/mips-softmmu-common.mak
index 8e54a74b7a..e10ac4b20c 100644
--- a/default-configs/mips-softmmu-common.mak
+++ b/default-configs/mips-softmmu-common.mak
@@ -35,6 +35,7 @@ CONFIG_MIPS_CPS=y
CONFIG_MIPS_ITU=y
CONFIG_R4K=y
CONFIG_MALTA=y
+CONFIG_SEMIHOSTING=y
CONFIG_PCNET_PCI=y
CONFIG_MIPSSIM=y
CONFIG_ACPI_SMBUS=y
diff --git a/default-configs/nios2-softmmu.mak b/default-configs/nios2-softmmu.mak
index e130d024e6..1bc4082ea9 100644
--- a/default-configs/nios2-softmmu.mak
+++ b/default-configs/nios2-softmmu.mak
@@ -1,5 +1,7 @@
# Default configuration for nios2-softmmu
+CONFIG_SEMIHOSTING=y
+
# Boards:
#
CONFIG_NIOS2_10M50=y
diff --git a/default-configs/xtensa-softmmu.mak b/default-configs/xtensa-softmmu.mak
index 7e4d1cc097..3aa20a47a7 100644
--- a/default-configs/xtensa-softmmu.mak
+++ b/default-configs/xtensa-softmmu.mak
@@ -1,5 +1,7 @@
# Default configuration for Xtensa
+CONFIG_SEMIHOSTING=y
+
# Boards:
#
CONFIG_XTENSA_SIM=y
diff --git a/gdbstub.c b/gdbstub.c
index b129df4e59..462f89edfe 100644
--- a/gdbstub.c
+++ b/gdbstub.c
@@ -37,7 +37,7 @@
#include "qemu/sockets.h"
#include "sysemu/hw_accel.h"
#include "sysemu/kvm.h"
-#include "exec/semihost.h"
+#include "hw/semihosting/semihost.h"
#include "exec/exec-all.h"
#ifdef CONFIG_USER_ONLY
diff --git a/hw/Kconfig b/hw/Kconfig
index 88b9f15007..195f541e50 100644
--- a/hw/Kconfig
+++ b/hw/Kconfig
@@ -29,6 +29,7 @@ source pci/Kconfig
source rdma/Kconfig
source scsi/Kconfig
source sd/Kconfig
+source semihosting/Kconfig
source smbios/Kconfig
source ssi/Kconfig
source timer/Kconfig
diff --git a/hw/Makefile.objs b/hw/Makefile.objs
index 82aa7fab8e..d770926ba9 100644
--- a/hw/Makefile.objs
+++ b/hw/Makefile.objs
@@ -36,6 +36,7 @@ devices-dirs-$(CONFIG_SOFTMMU) += watchdog/
devices-dirs-$(CONFIG_SOFTMMU) += xen/
devices-dirs-$(CONFIG_MEM_DEVICE) += mem/
devices-dirs-$(CONFIG_SOFTMMU) += smbios/
+devices-dirs-y += semihosting/
devices-dirs-y += core/
common-obj-y += $(devices-dirs-y)
obj-y += $(devices-dirs-y)
diff --git a/hw/mips/mips_malta.c b/hw/mips/mips_malta.c
index aff8464f2a..37ec89b07e 100644
--- a/hw/mips/mips_malta.c
+++ b/hw/mips/mips_malta.c
@@ -55,7 +55,7 @@
#include "qemu/error-report.h"
#include "hw/empty_slot.h"
#include "sysemu/kvm.h"
-#include "exec/semihost.h"
+#include "hw/semihosting/semihost.h"
#include "hw/mips/cps.h"
#define ENVP_ADDR 0x80002000l
diff --git a/hw/semihosting/Kconfig b/hw/semihosting/Kconfig
new file mode 100644
index 0000000000..efe0a30734
--- /dev/null
+++ b/hw/semihosting/Kconfig
@@ -0,0 +1,3 @@
+
+config SEMIHOSTING
+ bool
diff --git a/hw/semihosting/Makefile.objs b/hw/semihosting/Makefile.objs
new file mode 100644
index 0000000000..4ad47c05c0
--- /dev/null
+++ b/hw/semihosting/Makefile.objs
@@ -0,0 +1,2 @@
+obj-$(CONFIG_SEMIHOSTING) += config.o
+obj-$(CONFIG_SEMIHOSTING) += console.o
diff --git a/hw/semihosting/config.c b/hw/semihosting/config.c
new file mode 100644
index 0000000000..2a8e7e1045
--- /dev/null
+++ b/hw/semihosting/config.c
@@ -0,0 +1,186 @@
+/*
+ * Semihosting configuration
+ *
+ * Copyright (c) 2015 Imagination Technologies
+ * Copyright (c) 2019 Linaro Ltd
+ *
+ * This controls the configuration of semihosting for all guest
+ * targets that support it. Architecture specific handling is handled
+ * in target/HW/HW-semi.c
+ *
+ * Semihosting is sightly strange in that it is also supported by some
+ * linux-user targets. However in that use case no configuration of
+ * the outputs and command lines is supported.
+ *
+ * The config module is common to all softmmu targets however as vl.c
+ * needs to link against the helpers.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/option.h"
+#include "qemu/config-file.h"
+#include "qemu/error-report.h"
+#include "hw/semihosting/semihost.h"
+#include "chardev/char.h"
+
+QemuOptsList qemu_semihosting_config_opts = {
+ .name = "semihosting-config",
+ .implied_opt_name = "enable",
+ .head = QTAILQ_HEAD_INITIALIZER(qemu_semihosting_config_opts.head),
+ .desc = {
+ {
+ .name = "enable",
+ .type = QEMU_OPT_BOOL,
+ }, {
+ .name = "target",
+ .type = QEMU_OPT_STRING,
+ }, {
+ .name = "chardev",
+ .type = QEMU_OPT_STRING,
+ }, {
+ .name = "arg",
+ .type = QEMU_OPT_STRING,
+ },
+ { /* end of list */ }
+ },
+};
+
+typedef struct SemihostingConfig {
+ bool enabled;
+ SemihostingTarget target;
+ Chardev *chardev;
+ const char **argv;
+ int argc;
+ const char *cmdline; /* concatenated argv */
+} SemihostingConfig;
+
+static SemihostingConfig semihosting;
+static const char *semihost_chardev;
+
+bool semihosting_enabled(void)
+{
+ return semihosting.enabled;
+}
+
+SemihostingTarget semihosting_get_target(void)
+{
+ return semihosting.target;
+}
+
+const char *semihosting_get_arg(int i)
+{
+ if (i >= semihosting.argc) {
+ return NULL;
+ }
+ return semihosting.argv[i];
+}
+
+int semihosting_get_argc(void)
+{
+ return semihosting.argc;
+}
+
+const char *semihosting_get_cmdline(void)
+{
+ if (semihosting.cmdline == NULL && semihosting.argc > 0) {
+ semihosting.cmdline = g_strjoinv(" ", (gchar **)semihosting.argv);
+ }
+ return semihosting.cmdline;
+}
+
+static int add_semihosting_arg(void *opaque,
+ const char *name, const char *val,
+ Error **errp)
+{
+ SemihostingConfig *s = opaque;
+ if (strcmp(name, "arg") == 0) {
+ s->argc++;
+ /* one extra element as g_strjoinv() expects NULL-terminated array */
+ s->argv = g_realloc(s->argv, (s->argc + 1) * sizeof(void *));
+ s->argv[s->argc - 1] = val;
+ s->argv[s->argc] = NULL;
+ }
+ return 0;
+}
+
+/* Use strings passed via -kernel/-append to initialize semihosting.argv[] */
+void semihosting_arg_fallback(const char *file, const char *cmd)
+{
+ char *cmd_token;
+
+ /* argv[0] */
+ add_semihosting_arg(&semihosting, "arg", file, NULL);
+
+ /* split -append and initialize argv[1..n] */
+ cmd_token = strtok(g_strdup(cmd), " ");
+ while (cmd_token) {
+ add_semihosting_arg(&semihosting, "arg", cmd_token, NULL);
+ cmd_token = strtok(NULL, " ");
+ }
+}
+
+Chardev *semihosting_get_chardev(void)
+{
+ return semihosting.chardev;
+}
+
+void qemu_semihosting_enable(void)
+{
+ semihosting.enabled = true;
+ semihosting.target = SEMIHOSTING_TARGET_AUTO;
+}
+
+int qemu_semihosting_config_options(const char *optarg)
+{
+ QemuOptsList *opt_list = qemu_find_opts("semihosting-config");
+ QemuOpts *opts = qemu_opts_parse_noisily(opt_list, optarg, false);
+
+ semihosting.enabled = true;
+
+ if (opts != NULL) {
+ semihosting.enabled = qemu_opt_get_bool(opts, "enable",
+ true);
+ const char *target = qemu_opt_get(opts, "target");
+ /* setup of chardev is deferred until they are initialised */
+ semihost_chardev = qemu_opt_get(opts, "chardev");
+ if (target != NULL) {
+ if (strcmp("native", target) == 0) {
+ semihosting.target = SEMIHOSTING_TARGET_NATIVE;
+ } else if (strcmp("gdb", target) == 0) {
+ semihosting.target = SEMIHOSTING_TARGET_GDB;
+ } else if (strcmp("auto", target) == 0) {
+ semihosting.target = SEMIHOSTING_TARGET_AUTO;
+ } else {
+ error_report("unsupported semihosting-config %s",
+ optarg);
+ return 1;
+ }
+ } else {
+ semihosting.target = SEMIHOSTING_TARGET_AUTO;
+ }
+ /* Set semihosting argument count and vector */
+ qemu_opt_foreach(opts, add_semihosting_arg,
+ &semihosting, NULL);
+ } else {
+ error_report("unsupported semihosting-config %s", optarg);
+ return 1;
+ }
+
+ return 0;
+}
+
+void qemu_semihosting_connect_chardevs(void)
+{
+ /* We had to defer this until chardevs were created */
+ if (semihost_chardev) {
+ Chardev *chr = qemu_chr_find(semihost_chardev);
+ if (chr == NULL) {
+ error_report("semihosting chardev '%s' not found",
+ semihost_chardev);
+ exit(1);
+ }
+ semihosting.chardev = chr;
+ }
+}
diff --git a/hw/semihosting/console.c b/hw/semihosting/console.c
new file mode 100644
index 0000000000..466ea6dade
--- /dev/null
+++ b/hw/semihosting/console.c
@@ -0,0 +1,84 @@
+/*
+ * Semihosting Console Support
+ *
+ * Copyright (c) 2015 Imagination Technologies
+ * Copyright (c) 2019 Linaro Ltd
+ *
+ * This provides support for outputting to a semihosting console.
+ *
+ * While most semihosting implementations support reading and writing
+ * to arbitrary file descriptors we treat the console as something
+ * specifically for debugging interaction. This means messages can be
+ * re-directed to gdb (if currently being used to debug) or even
+ * re-directed elsewhere.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "cpu.h"
+#include "hw/semihosting/semihost.h"
+#include "hw/semihosting/console.h"
+#include "exec/gdbstub.h"
+#include "qemu/log.h"
+#include "chardev/char.h"
+
+int qemu_semihosting_log_out(const char *s, int len)
+{
+ Chardev *chardev = semihosting_get_chardev();
+ if (chardev) {
+ return qemu_chr_write_all(chardev, (uint8_t *) s, len);
+ } else {
+ return write(STDERR_FILENO, s, len);
+ }
+}
+
+/*
+ * A re-implementation of lock_user_string that we can use locally
+ * instead of relying on softmmu-semi. Hopefully we can deprecate that
+ * in time. We either copy len bytes if specified or until we find a NULL.
+ */
+static GString *copy_user_string(CPUArchState *env, target_ulong addr, int len)
+{
+ CPUState *cpu = ENV_GET_CPU(env);
+ GString *s = g_string_sized_new(len ? len : 128);
+ uint8_t c;
+ bool done;
+
+ do {
+ if (cpu_memory_rw_debug(cpu, addr++, &c, 1, 0) == 0) {
+ s = g_string_append_c(s, c);
+ done = len ? s->len == len : c == 0;
+ } else {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: passed inaccessible address " TARGET_FMT_lx,
+ __func__, addr);
+ done = true;
+ }
+ } while (!done);
+
+ return s;
+}
+
+static void semihosting_cb(CPUState *cs, target_ulong ret, target_ulong err)
+{
+ if (ret == (target_ulong) -1) {
+ qemu_log("%s: gdb console output failed ("TARGET_FMT_ld")",
+ __func__, err);
+ }
+}
+
+int qemu_semihosting_console_out(CPUArchState *env, target_ulong addr, int len)
+{
+ GString *s = copy_user_string(env, addr, len);
+ int out = s->len;
+
+ if (use_gdb_syscalls()) {
+ gdb_do_syscall(semihosting_cb, "write,2,%x,%x", addr, s->len);
+ } else {
+ out = qemu_semihosting_log_out(s->str, s->len);
+ }
+
+ g_string_free(s, true);
+ return out;
+}
diff --git a/include/hw/semihosting/console.h b/include/hw/semihosting/console.h
new file mode 100644
index 0000000000..30e66ae20a
--- /dev/null
+++ b/include/hw/semihosting/console.h
@@ -0,0 +1,38 @@
+/*
+ * Semihosting Console
+ *
+ * Copyright (c) 2019 Linaro Ltd
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef _SEMIHOST_CONSOLE_H_
+#define _SEMIHOST_CONSOLE_H_
+
+/**
+ * qemu_semihosting_console_out:
+ * @env: CPUArchState
+ * @s: host address of guest string
+ * @len: length of string or 0 (string is null terminated)
+ *
+ * Send a guest string to the debug console. This may be the remote
+ * gdb session if a softmmu guest is currently being debugged.
+ *
+ * Returns: number of bytes written.
+ */
+int qemu_semihosting_console_out(CPUArchState *env, target_ulong s, int len);
+
+/**
+ * qemu_semihosting_log_out:
+ * @s: pointer to string
+ * @len: length of string
+ *
+ * Send a string to the debug output. Unlike console_out these strings
+ * can't be sent to a remote gdb instance as they don't exist in guest
+ * memory.
+ *
+ * Returns: number of bytes written
+ */
+int qemu_semihosting_log_out(const char *s, int len);
+
+#endif /* _SEMIHOST_CONSOLE_H_ */
diff --git a/include/exec/semihost.h b/include/hw/semihosting/semihost.h
index 5980939c7b..60fc42d851 100644
--- a/include/exec/semihost.h
+++ b/include/hw/semihosting/semihost.h
@@ -51,12 +51,23 @@ static inline const char *semihosting_get_cmdline(void)
{
return NULL;
}
-#else
+
+static inline Chardev *semihosting_get_chardev(void)
+{
+ return NULL;
+}
+#else /* !CONFIG_USER_ONLY */
bool semihosting_enabled(void);
SemihostingTarget semihosting_get_target(void);
const char *semihosting_get_arg(int i);
int semihosting_get_argc(void);
const char *semihosting_get_cmdline(void);
-#endif
+void semihosting_arg_fallback(const char *file, const char *cmd);
+Chardev *semihosting_get_chardev(void);
+/* for vl.c hooks */
+void qemu_semihosting_enable(void);
+int qemu_semihosting_config_options(const char *opt);
+void qemu_semihosting_connect_chardevs(void);
+#endif /* CONFIG_USER_ONLY */
-#endif
+#endif /* SEMIHOST_H */
diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h
index 5f133cae83..61579ae71e 100644
--- a/include/sysemu/sysemu.h
+++ b/include/sysemu/sysemu.h
@@ -193,5 +193,6 @@ extern QemuOptsList qemu_nic_opts;
extern QemuOptsList qemu_net_opts;
extern QemuOptsList qemu_global_opts;
extern QemuOptsList qemu_mon_opts;
+extern QemuOptsList qemu_semihosting_config_opts;
#endif
diff --git a/linux-user/Makefile.objs b/linux-user/Makefile.objs
index 769b8d8336..285c5dfa17 100644
--- a/linux-user/Makefile.objs
+++ b/linux-user/Makefile.objs
@@ -6,4 +6,6 @@ obj-y = main.o syscall.o strace.o mmap.o signal.o \
obj-$(TARGET_HAS_BFLT) += flatload.o
obj-$(TARGET_I386) += vm86.o
obj-$(TARGET_ARM) += arm/nwfpe/
+obj-$(TARGET_ARM) += arm/semihost.o
+obj-$(TARGET_AARCH64) += arm/semihost.o
obj-$(TARGET_M68K) += m68k-sim.o
diff --git a/linux-user/arm/semihost.c b/linux-user/arm/semihost.c
new file mode 100644
index 0000000000..9554102a85
--- /dev/null
+++ b/linux-user/arm/semihost.c
@@ -0,0 +1,24 @@
+/*
+ * ARM Semihosting Console Support
+ *
+ * Copyright (c) 2019 Linaro Ltd
+ *
+ * Currently ARM is unique in having support for semihosting support
+ * in linux-user. So for now we implement the common console API but
+ * just for arm linux-user.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "cpu.h"
+#include "hw/semihosting/console.h"
+#include "qemu.h"
+
+int qemu_semihosting_console_out(CPUArchState *env, target_ulong addr, int len)
+{
+ void *s = lock_user_string(addr);
+ len = write(STDERR_FILENO, s, len ? len : strlen(s));
+ unlock_user(s, addr, 0);
+ return len;
+}
diff --git a/qemu-options.hx b/qemu-options.hx
index 7ae3373a00..39dc170429 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -4025,12 +4025,12 @@ STEXI
Enable semihosting mode (ARM, M68K, Xtensa, MIPS, Nios II only).
ETEXI
DEF("semihosting-config", HAS_ARG, QEMU_OPTION_semihosting_config,
- "-semihosting-config [enable=on|off][,target=native|gdb|auto][,arg=str[,...]]\n" \
+ "-semihosting-config [enable=on|off][,target=native|gdb|auto][,chardev=id][,arg=str[,...]]\n" \
" semihosting configuration\n",
QEMU_ARCH_ARM | QEMU_ARCH_M68K | QEMU_ARCH_XTENSA | QEMU_ARCH_LM32 |
QEMU_ARCH_MIPS | QEMU_ARCH_NIOS2)
STEXI
-@item -semihosting-config [enable=on|off][,target=native|gdb|auto][,arg=str[,...]]
+@item -semihosting-config [enable=on|off][,target=native|gdb|auto][,chardev=id][,arg=str[,...]]
@findex -semihosting-config
Enable and configure semihosting (ARM, M68K, Xtensa, MIPS, Nios II only).
@table @option
@@ -4038,6 +4038,8 @@ Enable and configure semihosting (ARM, M68K, Xtensa, MIPS, Nios II only).
Defines where the semihosting calls will be addressed, to QEMU (@code{native})
or to GDB (@code{gdb}). The default is @code{auto}, which means @code{gdb}
during debug sessions and @code{native} otherwise.
+@item chardev=@var{str1}
+Send the output to a chardev backend output for native or auto output when not in gdb
@item arg=@var{str1},arg=@var{str2},...
Allows the user to pass input arguments, and can be used multiple times to build
up a list. The old-style @code{-kernel}/@code{-append} method of passing a
diff --git a/stubs/Makefile.objs b/stubs/Makefile.objs
index 73452ad265..9c7393b08c 100644
--- a/stubs/Makefile.objs
+++ b/stubs/Makefile.objs
@@ -40,3 +40,4 @@ stub-obj-y += pci-host-piix.o
stub-obj-y += ram-block.o
stub-obj-y += ramfb.o
stub-obj-y += fw_cfg.o
+stub-obj-$(CONFIG_SOFTMMU) += semihost.o
diff --git a/stubs/semihost.c b/stubs/semihost.c
new file mode 100644
index 0000000000..4d5b3c0653
--- /dev/null
+++ b/stubs/semihost.c
@@ -0,0 +1,70 @@
+/*
+ * Semihosting Stubs for SoftMMU
+ *
+ * Copyright (c) 2019 Linaro Ltd
+ *
+ * Stubs for SoftMMU targets that don't actually do semihosting.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/option.h"
+#include "qemu/error-report.h"
+#include "hw/semihosting/semihost.h"
+
+/* Empty config */
+QemuOptsList qemu_semihosting_config_opts = {
+ .name = "",
+ .head = QTAILQ_HEAD_INITIALIZER(qemu_semihosting_config_opts.head),
+ .desc = {
+ { /* end of list */ }
+ },
+};
+
+/* Queries to config status default to off */
+bool semihosting_enabled(void)
+{
+ return false;
+}
+
+SemihostingTarget semihosting_get_target(void)
+{
+ return SEMIHOSTING_TARGET_AUTO;
+}
+
+/*
+ * All the rest are empty subs. We could g_assert_not_reached() but
+ * that adds extra weight to the final binary. Waste not want not.
+ */
+void qemu_semihosting_enable(void)
+{
+}
+
+int qemu_semihosting_config_options(const char *optarg)
+{
+ return 1;
+}
+
+const char *semihosting_get_arg(int i)
+{
+ return NULL;
+}
+
+int semihosting_get_argc(void)
+{
+ return 0;
+}
+
+const char *semihosting_get_cmdline(void)
+{
+ return NULL;
+}
+
+void semihosting_arg_fallback(const char *file, const char *cmd)
+{
+}
+
+void qemu_semihosting_connect_chardevs(void)
+{
+}
diff --git a/target/arm/arm-semi.c b/target/arm/arm-semi.c
index ddb94e0aba..53e807ab72 100644
--- a/target/arm/arm-semi.c
+++ b/target/arm/arm-semi.c
@@ -2,6 +2,7 @@
* Arm "Angel" semihosting syscalls
*
* Copyright (c) 2005, 2007 CodeSourcery.
+ * Copyright (c) 2019 Linaro
* Written by Paul Brook.
*
* This program is free software; you can redistribute it and/or modify
@@ -16,12 +17,18 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * ARM Semihosting is documented in:
+ * Semihosting for AArch32 and AArch64 Release 2.0
+ * https://static.docs.arm.com/100863/0200/semihosting.pdf
*/
#include "qemu/osdep.h"
#include "cpu.h"
-#include "exec/semihost.h"
+#include "hw/semihosting/semihost.h"
+#include "hw/semihosting/console.h"
+#include "qemu/log.h"
#ifdef CONFIG_USER_ONLY
#include "qemu.h"
@@ -239,6 +246,15 @@ static target_ulong arm_gdb_syscall(ARMCPU *cpu, gdb_syscall_complete_cb cb,
put_user_u64(val, args + (n) * 8) : \
put_user_u32(val, args + (n) * 4))
+/*
+ * Do a semihosting call.
+ *
+ * The specification always says that the "return register" either
+ * returns a specific value or is corrupted, so we don't need to
+ * report to our caller whether we are returning a value or trying to
+ * leave the register unchanged. We use 0xdeadbeef as the return value
+ * when there isn't a defined return value for the call.
+ */
target_ulong do_arm_semihosting(CPUARMState *env)
{
ARMCPU *cpu = arm_env_get_cpu(env);
@@ -299,32 +315,10 @@ target_ulong do_arm_semihosting(CPUARMState *env)
return set_swi_errno(ts, close(arg0));
}
case TARGET_SYS_WRITEC:
- {
- char c;
-
- if (get_user_u8(c, args))
- /* FIXME - should this error code be -TARGET_EFAULT ? */
- return (uint32_t)-1;
- /* Write to debug console. stderr is near enough. */
- if (use_gdb_syscalls()) {
- return arm_gdb_syscall(cpu, arm_semi_cb, "write,2,%x,1", args);
- } else {
- return write(STDERR_FILENO, &c, 1);
- }
- }
+ qemu_semihosting_console_out(env, args, 1);
+ return 0xdeadbeef;
case TARGET_SYS_WRITE0:
- if (!(s = lock_user_string(args)))
- /* FIXME - should this error code be -TARGET_EFAULT ? */
- return (uint32_t)-1;
- len = strlen(s);
- if (use_gdb_syscalls()) {
- return arm_gdb_syscall(cpu, arm_semi_cb, "write,2,%x,%x",
- args, len);
- } else {
- ret = write(STDERR_FILENO, s, len);
- }
- unlock_user(s, args, 0);
- return ret;
+ return qemu_semihosting_console_out(env, args, 0);
case TARGET_SYS_WRITE:
GET_ARG(0);
GET_ARG(1);
@@ -337,13 +331,15 @@ target_ulong do_arm_semihosting(CPUARMState *env)
} else {
s = lock_user(VERIFY_READ, arg1, len, 1);
if (!s) {
- /* FIXME - should this error code be -TARGET_EFAULT ? */
- return (uint32_t)-1;
+ /* Return bytes not written on error */
+ return len;
}
ret = set_swi_errno(ts, write(arg0, s, len));
unlock_user(s, arg1, 0);
- if (ret == (uint32_t)-1)
- return -1;
+ if (ret == (uint32_t)-1) {
+ ret = 0;
+ }
+ /* Return bytes not written */
return len - ret;
}
case TARGET_SYS_READ:
@@ -358,19 +354,21 @@ target_ulong do_arm_semihosting(CPUARMState *env)
} else {
s = lock_user(VERIFY_WRITE, arg1, len, 0);
if (!s) {
- /* FIXME - should this error code be -TARGET_EFAULT ? */
- return (uint32_t)-1;
+ /* return bytes not read */
+ return len;
}
do {
ret = set_swi_errno(ts, read(arg0, s, len));
} while (ret == -1 && errno == EINTR);
unlock_user(s, arg1, len);
- if (ret == (uint32_t)-1)
- return -1;
+ if (ret == (uint32_t)-1) {
+ ret = 0;
+ }
+ /* Return bytes not read */
return len - ret;
}
case TARGET_SYS_READC:
- /* XXX: Read from debug console. Not implemented. */
+ qemu_log_mask(LOG_UNIMP, "%s: SYS_READC not implemented", __func__);
return 0;
case TARGET_SYS_ISTTY:
GET_ARG(0);
@@ -404,7 +402,7 @@ target_ulong do_arm_semihosting(CPUARMState *env)
return buf.st_size;
}
case TARGET_SYS_TMPNAM:
- /* XXX: Not implemented. */
+ qemu_log_mask(LOG_UNIMP, "%s: SYS_TMPNAM not implemented", __func__);
return -1;
case TARGET_SYS_REMOVE:
GET_ARG(0);
@@ -509,14 +507,16 @@ target_ulong do_arm_semihosting(CPUARMState *env)
output_size = ts->info->arg_end - ts->info->arg_start;
if (!output_size) {
- /* We special-case the "empty command line" case (argc==0).
- Just provide the terminating 0. */
+ /*
+ * We special-case the "empty command line" case (argc==0).
+ * Just provide the terminating 0.
+ */
output_size = 1;
}
#endif
if (output_size > input_size) {
- /* Not enough space to store command-line arguments. */
+ /* Not enough space to store command-line arguments. */
return -1;
}
@@ -570,8 +570,10 @@ target_ulong do_arm_semihosting(CPUARMState *env)
GET_ARG(0);
#ifdef CONFIG_USER_ONLY
- /* Some C libraries assume the heap immediately follows .bss, so
- allocate it using sbrk. */
+ /*
+ * Some C libraries assume the heap immediately follows .bss, so
+ * allocate it using sbrk.
+ */
if (!ts->heap_limit) {
abi_ulong ret;
@@ -619,7 +621,8 @@ target_ulong do_arm_semihosting(CPUARMState *env)
}
case TARGET_SYS_EXIT:
if (is_a64(env)) {
- /* The A64 version of this call takes a parameter block,
+ /*
+ * The A64 version of this call takes a parameter block,
* so the application-exit type can return a subcode which
* is the exit status code from the application.
*/
@@ -632,14 +635,17 @@ target_ulong do_arm_semihosting(CPUARMState *env)
ret = 1;
}
} else {
- /* ARM specifies only Stopped_ApplicationExit as normal
- * exit, everything else is considered an error */
+ /*
+ * ARM specifies only Stopped_ApplicationExit as normal
+ * exit, everything else is considered an error
+ */
ret = (args == ADP_Stopped_ApplicationExit) ? 0 : 1;
}
gdb_exit(env, ret);
exit(ret);
case TARGET_SYS_SYNCCACHE:
- /* Clean the D-cache and invalidate the I-cache for the specified
+ /*
+ * Clean the D-cache and invalidate the I-cache for the specified
* virtual address range. This is a nop for us since we don't
* implement caches. This is only present on A64.
*/
diff --git a/target/arm/helper.c b/target/arm/helper.c
index acd23c53ca..719fb92e60 100644
--- a/target/arm/helper.c
+++ b/target/arm/helper.c
@@ -16,7 +16,7 @@
#include "exec/cpu_ldst.h"
#include "arm_ldst.h"
#include <zlib.h> /* For crc32 */
-#include "exec/semihost.h"
+#include "hw/semihosting/semihost.h"
#include "sysemu/cpus.h"
#include "sysemu/kvm.h"
#include "fpu/softfloat.h"
diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c
index 42999c5801..092f0df3c4 100644
--- a/target/arm/translate-a64.c
+++ b/target/arm/translate-a64.c
@@ -29,7 +29,7 @@
#include "qemu/host-utils.h"
#include "qemu/qemu-print.h"
-#include "exec/semihost.h"
+#include "hw/semihosting/semihost.h"
#include "exec/gen-icount.h"
#include "exec/helper-proto.h"
diff --git a/target/arm/translate.c b/target/arm/translate.c
index 298c262825..d240c1b714 100644
--- a/target/arm/translate.c
+++ b/target/arm/translate.c
@@ -30,7 +30,7 @@
#include "qemu/bitops.h"
#include "qemu/qemu-print.h"
#include "arm_ldst.h"
-#include "exec/semihost.h"
+#include "hw/semihosting/semihost.h"
#include "exec/helper-proto.h"
#include "exec/helper-gen.h"
diff --git a/target/lm32/helper.c b/target/lm32/helper.c
index 20ea17ba23..8cd4840052 100644
--- a/target/lm32/helper.c
+++ b/target/lm32/helper.c
@@ -22,7 +22,7 @@
#include "exec/exec-all.h"
#include "qemu/host-utils.h"
#include "sysemu/sysemu.h"
-#include "exec/semihost.h"
+#include "hw/semihosting/semihost.h"
#include "exec/log.h"
bool lm32_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
diff --git a/target/m68k/op_helper.c b/target/m68k/op_helper.c
index 1ecc772b5c..bde2d551ff 100644
--- a/target/m68k/op_helper.c
+++ b/target/m68k/op_helper.c
@@ -21,7 +21,7 @@
#include "exec/helper-proto.h"
#include "exec/exec-all.h"
#include "exec/cpu_ldst.h"
-#include "exec/semihost.h"
+#include "hw/semihosting/semihost.h"
#if defined(CONFIG_USER_ONLY)
diff --git a/target/mips/Makefile.objs b/target/mips/Makefile.objs
index 651f36f517..3448ad5e19 100644
--- a/target/mips/Makefile.objs
+++ b/target/mips/Makefile.objs
@@ -1,4 +1,5 @@
obj-y += translate.o dsp_helper.o op_helper.o lmi_helper.o helper.o cpu.o
-obj-y += gdbstub.o msa_helper.o mips-semi.o
+obj-y += gdbstub.o msa_helper.o
+obj-$(CONFIG_SOFTMMU) += mips-semi.o
obj-$(CONFIG_SOFTMMU) += machine.o cp0_timer.o
obj-$(CONFIG_KVM) += kvm.o
diff --git a/target/mips/helper.h b/target/mips/helper.h
index 2863f603f7..51f0e1c183 100644
--- a/target/mips/helper.h
+++ b/target/mips/helper.h
@@ -2,7 +2,9 @@ DEF_HELPER_3(raise_exception_err, noreturn, env, i32, int)
DEF_HELPER_2(raise_exception, noreturn, env, i32)
DEF_HELPER_1(raise_exception_debug, noreturn, env)
+#ifndef CONFIG_USER_ONLY
DEF_HELPER_1(do_semihosting, void, env)
+#endif
#ifdef TARGET_MIPS64
DEF_HELPER_4(sdl, void, env, tl, tl, int)
diff --git a/target/mips/mips-semi.c b/target/mips/mips-semi.c
index a7aefbaefc..35bdfd7c77 100644
--- a/target/mips/mips-semi.c
+++ b/target/mips/mips-semi.c
@@ -22,7 +22,8 @@
#include "qemu/log.h"
#include "exec/helper-proto.h"
#include "exec/softmmu-semi.h"
-#include "exec/semihost.h"
+#include "hw/semihosting/semihost.h"
+#include "hw/semihosting/console.h"
typedef enum UHIOp {
UHI_exit = 1,
@@ -329,13 +330,12 @@ void helper_do_semihosting(CPUMIPSState *env)
p2 = strstr(p, "%d");
if (p2) {
int char_num = p2 - p;
- char *buf = g_malloc(char_num + 1);
- strncpy(buf, p, char_num);
- buf[char_num] = '\0';
- gpr[2] = printf("%s%d%s", buf, (int)gpr[5], p2 + 2);
- g_free(buf);
+ GString *s = g_string_new_len(p, char_num);
+ g_string_append_printf(s, "%d%s", (int)gpr[5], p2 + 2);
+ gpr[2] = qemu_semihosting_log_out(s->str, s->len);
+ g_string_free(s, true);
} else {
- gpr[2] = printf("%s", p);
+ gpr[2] = qemu_semihosting_log_out(p, strlen(p));
}
FREE_TARGET_STRING(p, gpr[4]);
break;
diff --git a/target/mips/translate.c b/target/mips/translate.c
index dd706ad0b1..70552fe543 100644
--- a/target/mips/translate.c
+++ b/target/mips/translate.c
@@ -32,7 +32,7 @@
#include "exec/helper-proto.h"
#include "exec/helper-gen.h"
-#include "exec/semihost.h"
+#include "hw/semihosting/semihost.h"
#include "target/mips/trace.h"
#include "trace-tcg.h"
@@ -13726,6 +13726,14 @@ static inline bool is_uhi(int sdbbp_code)
#endif
}
+#ifdef CONFIG_USER_ONLY
+/* The above should dead-code away any calls to this..*/
+static inline void gen_helper_do_semihosting(void *env)
+{
+ g_assert_not_reached();
+}
+#endif
+
static int decode_mips16_opc (CPUMIPSState *env, DisasContext *ctx)
{
int rx, ry;
diff --git a/target/nios2/helper.c b/target/nios2/helper.c
index ffb83fc104..57c97bde3c 100644
--- a/target/nios2/helper.c
+++ b/target/nios2/helper.c
@@ -26,7 +26,7 @@
#include "exec/cpu_ldst.h"
#include "exec/log.h"
#include "exec/helper-proto.h"
-#include "exec/semihost.h"
+#include "hw/semihosting/semihost.h"
#if defined(CONFIG_USER_ONLY)
diff --git a/target/xtensa/translate.c b/target/xtensa/translate.c
index 53dce470c1..6f1da87875 100644
--- a/target/xtensa/translate.c
+++ b/target/xtensa/translate.c
@@ -38,7 +38,7 @@
#include "qemu/qemu-print.h"
#include "sysemu/sysemu.h"
#include "exec/cpu_ldst.h"
-#include "exec/semihost.h"
+#include "hw/semihosting/semihost.h"
#include "exec/translator.h"
#include "exec/helper-proto.h"
diff --git a/target/xtensa/xtensa-semi.c b/target/xtensa/xtensa-semi.c
index 5f5ce4f344..38efa3485a 100644
--- a/target/xtensa/xtensa-semi.c
+++ b/target/xtensa/xtensa-semi.c
@@ -29,7 +29,7 @@
#include "cpu.h"
#include "chardev/char-fe.h"
#include "exec/helper-proto.h"
-#include "exec/semihost.h"
+#include "hw/semihosting/semihost.h"
#include "qapi/error.h"
#include "qemu/log.h"
#include "sysemu/sysemu.h"
diff --git a/tests/docker/dockerfiles/fedora.docker b/tests/docker/dockerfiles/fedora.docker
index 69d4a7f5d7..afbba29ada 100644
--- a/tests/docker/dockerfiles/fedora.docker
+++ b/tests/docker/dockerfiles/fedora.docker
@@ -8,6 +8,7 @@ ENV PACKAGES \
bzip2-devel \
ccache \
clang \
+ cyrus-sasl-devel \
device-mapper-multipath-devel \
findutils \
flex \
@@ -23,13 +24,17 @@ ENV PACKAGES \
libaio-devel \
libasan \
libattr-devel \
+ libblockdev-mpath-devel \
libcap-devel \
libcap-ng-devel \
libcurl-devel \
libfdt-devel \
+ libiscsi-devel \
libjpeg-devel \
+ libpmem-devel \
libpng-devel \
librbd-devel \
+ libseccomp-devel \
libssh2-devel \
libubsan \
libusbx-devel \
@@ -74,10 +79,12 @@ ENV PACKAGES \
pixman-devel \
python3 \
PyYAML \
+ rdma-core-devel \
SDL2-devel \
snappy-devel \
sparse \
spice-server-devel \
+ systemd-devel \
systemtap-sdt-devel \
tar \
usbredir-devel \
diff --git a/tests/docker/dockerfiles/ubuntu1804.docker b/tests/docker/dockerfiles/ubuntu1804.docker
new file mode 100644
index 0000000000..2e2900150b
--- /dev/null
+++ b/tests/docker/dockerfiles/ubuntu1804.docker
@@ -0,0 +1,57 @@
+FROM ubuntu:18.04
+ENV PACKAGES flex bison \
+ ccache \
+ clang \
+ gcc \
+ gettext \
+ git \
+ glusterfs-common \
+ libaio-dev \
+ libattr1-dev \
+ libbluetooth-dev \
+ libbrlapi-dev \
+ libbz2-dev \
+ libcacard-dev \
+ libcap-dev \
+ libcap-ng-dev \
+ libcurl4-gnutls-dev \
+ libdrm-dev \
+ libepoxy-dev \
+ libfdt-dev \
+ libgbm-dev \
+ libgtk-3-dev \
+ libibverbs-dev \
+ libiscsi-dev \
+ libjemalloc-dev \
+ libjpeg-turbo8-dev \
+ liblzo2-dev \
+ libncurses5-dev \
+ libncursesw5-dev \
+ libnfs-dev \
+ libnss3-dev \
+ libnuma-dev \
+ libpixman-1-dev \
+ librados-dev \
+ librbd-dev \
+ librdmacm-dev \
+ libsasl2-dev \
+ libsdl2-dev \
+ libseccomp-dev \
+ libsnappy-dev \
+ libspice-protocol-dev \
+ libspice-server-dev \
+ libssh2-1-dev \
+ libusb-1.0-0-dev \
+ libusbredirhost-dev \
+ libvdeplug-dev \
+ libvte-2.91-dev \
+ libxen-dev \
+ make \
+ python-yaml \
+ sparse \
+ texinfo \
+ xfslibs-dev
+RUN apt-get update && \
+ apt-get -y install $PACKAGES
+RUN dpkg -l $PACKAGES | sort > /packages.txt
+ENV FEATURES clang pyyaml sdl2
diff --git a/tests/qemu-iotests/check b/tests/qemu-iotests/check
index 922c5d1d3d..95162c6cf9 100755
--- a/tests/qemu-iotests/check
+++ b/tests/qemu-iotests/check
@@ -27,9 +27,7 @@ bad=""
notrun=""
casenotrun=""
interrupt=true
-
-# by default don't output timestamps
-timestamp=${TIMESTAMP:=false}
+makecheck=false
_init_error()
{
@@ -88,6 +86,22 @@ _full_platform_details()
echo "$os/$platform $host $kernel"
}
+_full_env_details()
+{
+ cat <<EOF
+QEMU -- "$QEMU_PROG" $QEMU_OPTIONS
+QEMU_IMG -- "$QEMU_IMG_PROG" $QEMU_IMG_OPTIONS
+QEMU_IO -- "$QEMU_IO_PROG" $QEMU_IO_OPTIONS
+QEMU_NBD -- "$QEMU_NBD_PROG" $QEMU_NBD_OPTIONS
+IMGFMT -- $FULL_IMGFMT_DETAILS
+IMGPROTO -- $IMGPROTO
+PLATFORM -- $FULL_HOST_DETAILS
+TEST_DIR -- $TEST_DIR
+SOCKET_SCM_HELPER -- $SOCKET_SCM_HELPER
+
+EOF
+}
+
# $1 = prog to look for
set_prog_path()
{
@@ -254,8 +268,8 @@ other options
-misalign misalign memory allocations
-n show me, do not run tests
-o options -o options to pass to qemu-img create/convert
- -T output timestamps
-c mode cache mode
+ -makecheck pretty print output for make check
testlist options
-g group[,group...] include tests from these groups
@@ -403,7 +417,10 @@ testlist options
command -v xxdiff >/dev/null 2>&1 && diff=xxdiff
fi
;;
-
+ -makecheck) # makecheck friendly output
+ makecheck=true
+ xpand=false
+ ;;
-n) # show me, don't do it
showme=true
xpand=false
@@ -416,8 +433,7 @@ testlist options
cachemode=true
xpand=false
;;
- -T) # turn on timestamp output
- timestamp=true
+ -T) # deprecated timestamp option
xpand=false
;;
@@ -633,12 +649,6 @@ _wallclock()
date "+%H %M %S" | awk '{ print $1*3600 + $2*60 + $3 }'
}
-_timestamp()
-{
- now=$(date "+%T")
- printf %s " [$now]"
-}
-
_wrapup()
{
if $showme
@@ -704,23 +714,54 @@ END { if (NR > 0) {
trap "_wrapup; exit \$status" 0 1 2 3 15
+# Report the test start and results. For makecheck we want to pretty
+# print the whole report at the end of the execution.
+# args: $seq, $starttime, $lasttime
+_report_test_start()
+{
+ if ! $makecheck; then
+ if [ -n "$3" ]; then
+ local lasttime=" (last: $3s)"
+ fi
+ printf "%-8s %-10s [%s] %4s%-14s\r" "$1" "..." "$2" "..." "$lasttime"
+ fi
+}
+# args:$seq $status $starttime $lasttime $thistime $details
+_report_test_result()
+{
+ local status lasttime thistime
+ if $makecheck; then
+ if [ -n "$2" ] && [ "$2" != "pass" ]; then
+ status=" [$2]"
+ fi
+ printf " TEST iotest-$IMGFMT: %s%s\n" "$1" "$status"
+ return
+ fi
+
+ if [ -n "$4" ]; then
+ lasttime=" (last: $4s)"
+ fi
+ if [ -n "$5" ]; then
+ thistime=" $5s"
+ fi
+ case "$2" in
+ "pass") status=$(printf "\e[32m%-10s\e[0m" "$2") ;;
+ "fail") status=$(printf "\e[1m\e[31m%-10s\e[0m" "$2") ;;
+ "not run") status=$(printf "\e[33m%-10s\e[0m" "$2") ;;
+ *) status=$(printf "%-10s" "$2") ;;
+ esac
+
+ printf "%-8s %s [%s] [%s] %4s%-14s %s\n" "$1" "$status" "$3" "$(date '+%T')" "$thistime" "$lasttime" "$6"
+}
+
[ -f $TIMESTAMP_FILE ] || touch $TIMESTAMP_FILE
FULL_IMGFMT_DETAILS=$(_full_imgfmt_details)
FULL_HOST_DETAILS=$(_full_platform_details)
-cat <<EOF
-QEMU -- "$QEMU_PROG" $QEMU_OPTIONS
-QEMU_IMG -- "$QEMU_IMG_PROG" $QEMU_IMG_OPTIONS
-QEMU_IO -- "$QEMU_IO_PROG" $QEMU_IO_OPTIONS
-QEMU_NBD -- "$QEMU_NBD_PROG" $QEMU_NBD_OPTIONS
-IMGFMT -- $FULL_IMGFMT_DETAILS
-IMGPROTO -- $IMGPROTO
-PLATFORM -- $FULL_HOST_DETAILS
-TEST_DIR -- $TEST_DIR
-SOCKET_SCM_HELPER -- $SOCKET_SCM_HELPER
-
-EOF
+if ! $makecheck; then
+ _full_env_details
+fi
seq="check"
@@ -728,42 +769,43 @@ seq="check"
for seq in $list
do
- err=false
- printf %s "$seq"
+ err=false # error flag
+ printdiff=false # show diff to reference output?
+ status="" # test result summary
+ results="" # test result details
+
if [ -n "$TESTS_REMAINING_LOG" ] ; then
sed -e "s/$seq//" -e 's/ / /' -e 's/^ *//' $TESTS_REMAINING_LOG > $TESTS_REMAINING_LOG.tmp
mv $TESTS_REMAINING_LOG.tmp $TESTS_REMAINING_LOG
sync
fi
+ lasttime=$(sed -n -e "/^$seq /s/.* //p" <$TIMESTAMP_FILE)
+ starttime=$(date "+%T")
+ _report_test_start $seq $starttime $lasttime
+
if $showme
then
- echo
- continue
+ status="not run"
elif [ -f expunged ] && $expunge && egrep "^$seq([ ]|\$)" expunged >/dev/null
then
- echo " - expunged"
+ status="not run"
+ results="expunged"
rm -f $seq.out.bad
echo "/^$seq\$/d" >>$tmp.expunged
elif [ ! -f "$source_iotests/$seq" ]
then
- echo " - no such test?"
+ status="not run"
+ results="no such test?"
echo "/^$seq\$/d" >>$tmp.expunged
else
# really going to try and run this one
#
rm -f $seq.out.bad
- lasttime=$(sed -n -e "/^$seq /s/.* //p" <$TIMESTAMP_FILE)
- if [ "X$lasttime" != X ]; then
- printf %s " ${lasttime}s ..."
- else
- printf " " # prettier output with timestamps.
- fi
rm -f core $seq.notrun
rm -f $seq.casenotrun
start=$(_wallclock)
- $timestamp && printf %s " [$(date "+%T")]"
if [ "$(head -n 1 "$source_iotests/$seq")" == "#!/usr/bin/env python" ]; then
run_command="$PYTHON $seq"
@@ -781,26 +823,26 @@ do
$run_command >$tmp.out 2>&1)
fi
sts=$?
- $timestamp && _timestamp
stop=$(_wallclock)
if [ -f core ]
then
- printf " [dumped core]"
mv core $seq.core
+ status="fail"
+ results="[dumped core] $seq.core"
err=true
fi
if [ -f $seq.notrun ]
then
- $timestamp || printf " [not run] "
- $timestamp && echo " [not run]" && printf %s " $seq -- "
- cat $seq.notrun
- notrun="$notrun $seq"
+ # overwrites timestamp output
+ status="not run"
+ results="$(cat $seq.notrun)"
else
if [ $sts -ne 0 ]
then
- printf %s " [failed, exit status $sts]"
+ status="fail"
+ results=$(printf %s "[failed, exit status $sts]")
err=true
fi
@@ -821,22 +863,23 @@ do
if [ ! -f "$reference" ]
then
- echo " - no qualified output"
+ status="fail"
+ reason="no qualified output"
err=true
else
if diff -w "$reference" $tmp.out >/dev/null 2>&1
then
- echo ""
- if $err
- then
- :
- else
- echo "$seq $(expr $stop - $start)" >>$tmp.time
+ if ! $err; then
+ status="pass"
+ thistime=$(expr $stop - $start)
+ echo "$seq $thistime" >>$tmp.time
fi
else
- echo " - output mismatch (see $seq.out.bad)"
mv $tmp.out $seq.out.bad
$diff -w "$reference" "$PWD"/$seq.out.bad
+ status="fail"
+ results="output mismatch (see $seq.out.bad)"
+ printdiff=true
err=true
fi
fi
@@ -850,13 +893,27 @@ do
# come here for each test, except when $showme is true
#
- if $err
- then
- bad="$bad $seq"
- n_bad=$(expr $n_bad + 1)
- quick=false
- fi
- [ -f $seq.notrun ] || try=$(expr $try + 1)
+ _report_test_result $seq "$status" "$starttime" "$lasttime" "$thistime" "$results"
+ case "$status" in
+ "pass")
+ try=$(expr $try + 1)
+ ;;
+ "fail")
+ try=$(expr $try + 1)
+ if $makecheck; then
+ _full_env_details
+ fi
+ if $printdiff; then
+ $diff -w "$reference" "$PWD"/$seq.out.bad
+ fi
+ bad="$bad $seq"
+ n_bad=$(expr $n_bad + 1)
+ quick=false
+ ;;
+ "not run")
+ notrun="$notrun $seq"
+ ;;
+ esac
seq="after_$seq"
done
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
index 52b7c16e15..2c74deec00 100644
--- a/tests/qemu-iotests/group
+++ b/tests/qemu-iotests/group
@@ -1,8 +1,21 @@
#
# QA groups control file
# Defines test groups
+#
+# Some notes about the groups:
+#
# - do not start group names with a digit
#
+# - quick : Tests in this group should finish within some few seconds.
+#
+# - img : Tests in this group can be used to excercise the qemu-img tool.
+#
+# - auto : Tests in this group are used during "make check" and should be
+# runnable in any case. That means they should run with every QEMU binary
+# (also non-x86), with every QEMU configuration (i.e. must not fail if
+# an optional feature is not compiled in - but reporting a "skip" is ok),
+# and work all kind of host filesystems and users (e.g. "nobody" or "root").
+#
#
# test-group association ... one line per test
@@ -32,11 +45,11 @@
023 rw auto
024 rw backing auto quick
025 rw auto quick
-026 rw blkdbg auto
+026 rw blkdbg
027 rw auto quick
-028 rw backing auto quick
+028 rw backing quick
029 rw auto quick
-030 rw auto backing
+030 rw backing
031 rw auto quick
032 rw auto quick
033 rw auto quick
@@ -46,35 +59,35 @@
037 rw auto backing quick
038 rw auto backing quick
039 rw auto quick
-040 rw auto
-041 rw auto backing
+040 rw
+041 rw backing
042 rw auto quick
043 rw auto backing
-044 rw auto
-045 rw auto quick
+044 rw
+045 rw quick
046 rw auto aio quick
047 rw auto quick
048 img auto quick
049 rw auto
050 rw auto backing quick
-051 rw auto
+051 rw
052 rw auto backing quick
053 rw auto quick
054 rw auto quick
-055 rw auto
-056 rw auto backing
-057 rw auto
-058 rw auto quick
+055 rw
+056 rw backing
+057 rw
+058 rw quick
059 rw auto quick
060 rw auto quick
061 rw auto
062 rw auto quick
063 rw auto quick
064 rw auto quick
-065 rw auto quick
+065 rw quick
066 rw auto quick
-067 rw auto quick
-068 rw auto quick
+067 rw quick
+068 rw quick
069 rw auto quick
070 rw auto quick
071 rw auto quick
@@ -91,18 +104,18 @@
082 rw auto quick
083 rw auto
084 img auto quick
-085 rw auto
+085 rw
086 rw auto quick
-087 rw auto quick
+087 rw quick
088 rw auto quick
089 rw auto quick
090 rw auto quick
091 rw auto migration
092 rw auto quick
-093 auto
+093 throttle
094 rw auto quick
-095 rw auto quick
-096 rw auto quick
+095 rw quick
+096 rw quick
097 rw auto backing
098 rw auto backing quick
099 rw auto quick
@@ -118,60 +131,60 @@
109 rw auto
110 rw auto backing quick
111 rw auto quick
-112 rw auto
+112 rw
113 rw auto quick
114 rw auto quick
-115 rw auto
+115 rw
116 rw auto quick
117 rw auto
-118 rw auto
+118 rw
119 rw auto quick
120 rw auto quick
-121 rw auto
+121 rw
122 rw auto
123 rw auto quick
-124 rw auto backing
-125 rw auto
+124 rw backing
+125 rw
126 rw auto backing
-127 rw auto backing quick
+127 rw backing quick
128 rw auto quick
-129 rw auto quick
+129 rw quick
130 rw auto quick
131 rw auto quick
-132 rw auto quick
+132 rw quick
133 auto quick
134 rw auto quick
135 rw auto
-136 rw auto
+136 rw
137 rw auto
138 rw auto quick
-139 rw auto quick
+139 rw quick
140 rw auto quick
141 rw auto quick
142 auto
143 auto quick
-144 rw auto quick
-145 auto quick
+144 rw quick
+145 quick
146 auto quick
-147 auto
-148 rw auto quick
-149 rw auto sudo
+147 img
+148 rw quick
+149 rw sudo
150 rw auto quick
-151 rw auto
-152 rw auto quick
-153 rw auto quick
+151 rw
+152 rw quick
+153 rw quick
154 rw auto backing quick
-155 rw auto
+155 rw
156 rw auto quick
-157 auto
+157 quick
158 rw auto quick
159 rw auto quick
160 rw auto quick
161 rw auto quick
-162 auto quick
-163 rw auto
-165 rw auto quick
-169 rw auto quick migration
+162 quick
+163 rw
+165 rw quick
+169 rw quick migration
170 rw auto quick
171 rw auto quick
172 auto
@@ -180,74 +193,74 @@
175 auto quick
176 rw auto backing
177 rw auto quick
-178 auto
+178 img
179 rw auto quick
181 rw auto migration
-182 rw auto quick
-183 rw auto migration
+182 rw quick
+183 rw migration
184 rw auto quick
-185 rw auto
+185 rw
186 rw auto
187 rw auto
-188 rw auto quick
-189 rw auto
+188 rw quick
+189 rw
190 rw auto quick
191 rw auto
192 rw auto quick
-194 rw auto migration quick
+194 rw migration quick
195 rw auto quick
-196 rw auto quick migration
+196 rw quick migration
197 rw auto quick
-198 rw auto
-199 rw auto migration
-200 rw auto
+198 rw
+199 rw migration
+200 rw
201 rw auto migration
-202 rw auto quick
-203 rw auto migration
-204 rw auto quick
-205 rw auto quick
-206 rw auto
+202 rw quick
+203 rw migration
+204 rw quick
+205 rw quick
+206 rw
207 rw auto
-208 rw auto quick
-209 rw auto quick
+208 rw quick
+209 rw quick
210 rw auto
211 rw auto quick
212 rw auto quick
213 rw auto quick
214 rw auto
215 rw auto quick
-216 rw auto quick
+216 rw quick
217 rw auto quick
-218 rw auto quick
-219 rw auto
+218 rw quick
+219 rw
220 rw auto
221 rw auto quick
-222 rw auto quick
-223 rw auto quick
-224 rw auto quick
+222 rw quick
+223 rw quick
+224 rw quick
225 rw auto quick
226 auto quick
-227 auto quick
-228 rw auto quick
+227 quick
+228 rw quick
229 auto quick
231 auto quick
-232 auto quick
+232 quick
233 auto quick
-234 auto quick migration
-235 auto quick
-236 auto quick
+234 quick migration
+235 quick
+236 quick
237 rw auto quick
-238 auto quick
+238 quick
239 rw auto quick
-240 auto quick
+240 quick
241 rw auto quick
-242 rw auto quick
+242 rw quick
243 rw auto quick
244 rw auto quick
-245 rw auto
-246 rw auto quick
-247 rw auto quick
-248 rw auto quick
+245 rw
+246 rw quick
+247 rw quick
+248 rw quick
249 rw auto quick
252 rw auto backing quick
253 rw auto quick
diff --git a/tests/tcg/Makefile b/tests/tcg/Makefile
index 1cdd628e96..6fa63cc8d5 100644
--- a/tests/tcg/Makefile
+++ b/tests/tcg/Makefile
@@ -96,6 +96,7 @@ else
# build options for bare programs are usually pretty different. They
# are expected to provide their own build recipes.
-include $(SRC_PATH)/tests/tcg/minilib/Makefile.target
+-include $(SRC_PATH)/tests/tcg/multiarch/system/Makefile.softmmu-target
-include $(SRC_PATH)/tests/tcg/$(TARGET_BASE_ARCH)/Makefile.softmmu-target
ifneq ($(TARGET_BASE_ARCH),$(TARGET_NAME))
-include $(SRC_PATH)/tests/tcg/$(TARGET_NAME)/Makefile.softmmu-target
diff --git a/tests/tcg/aarch64/Makefile.softmmu-target b/tests/tcg/aarch64/Makefile.softmmu-target
new file mode 100644
index 0000000000..2e560e4d08
--- /dev/null
+++ b/tests/tcg/aarch64/Makefile.softmmu-target
@@ -0,0 +1,34 @@
+#
+# Aarch64 system tests
+#
+
+AARCH64_SYSTEM_SRC=$(SRC_PATH)/tests/tcg/aarch64/system
+VPATH+=$(AARCH64_SYSTEM_SRC)
+
+# These objects provide the basic boot code and helper functions for all tests
+CRT_OBJS=boot.o
+
+AARCH64_TEST_SRCS=$(wildcard $(AARCH64_SYSTEM_SRC)/*.c)
+AARCH64_TESTS = $(patsubst $(AARCH64_SYSTEM_SRC)/%.c, %, $(AARCH64_TEST_SRCS))
+
+CRT_PATH=$(AARCH64_SYSTEM_SRC)
+LINK_SCRIPT=$(AARCH64_SYSTEM_SRC)/kernel.ld
+LDFLAGS=-Wl,-T$(LINK_SCRIPT)
+TESTS+=$(AARCH64_TESTS) $(MULTIARCH_TESTS)
+CFLAGS+=-nostdlib -ggdb -O0 $(MINILIB_INC)
+LDFLAGS+=-static -nostdlib $(CRT_OBJS) $(MINILIB_OBJS) -lgcc
+
+# building head blobs
+.PRECIOUS: $(CRT_OBJS)
+
+%.o: $(CRT_PATH)/%.S
+ $(CC) $(CFLAGS) -x assembler-with-cpp -c $< -o $@
+
+# Build and link the tests
+%: %.c $(LINK_SCRIPT) $(CRT_OBJS) $(MINILIB_OBJS)
+ $(CC) $(CFLAGS) $< -o $@ $(LDFLAGS)
+
+memory: CFLAGS+=-DCHECK_UNALIGNED=1
+
+# Running
+QEMU_OPTS+=-M virt -cpu max -display none -semihosting-config enable=on,target=native,chardev=output -kernel
diff --git a/tests/tcg/aarch64/system/boot.S b/tests/tcg/aarch64/system/boot.S
new file mode 100644
index 0000000000..b14e94f332
--- /dev/null
+++ b/tests/tcg/aarch64/system/boot.S
@@ -0,0 +1,239 @@
+/*
+ * Minimal AArch64 system boot code.
+ *
+ * Copyright Linaro Ltd 2019
+ *
+ * Loosely based on the newlib/libgloss setup stubs. Using semihosting
+ * for serial output and exit functions.
+ */
+
+/*
+ * Semihosting interface on ARM AArch64
+ * See "Semihosting for AArch32 and AArch64 Relase 2.0" by ARM
+ * w0 - semihosting call number
+ * x1 - semihosting parameter
+ */
+#define semihosting_call hlt 0xf000
+#define SYS_WRITEC 0x03 /* character to debug channel */
+#define SYS_WRITE0 0x04 /* string to debug channel */
+#define SYS_EXIT 0x18
+
+ .align 12
+
+ .macro ventry label
+ .align 7
+ b \label
+ .endm
+
+vector_table:
+ /* Current EL with SP0. */
+ ventry curr_sp0_sync /* Synchronous */
+ ventry curr_sp0_irq /* Irq/vIRQ */
+ ventry curr_sp0_fiq /* Fiq/vFIQ */
+ ventry curr_sp0_serror /* SError/VSError */
+
+ /* Current EL with SPx. */
+ ventry curr_spx_sync /* Synchronous */
+ ventry curr_spx_irq /* IRQ/vIRQ */
+ ventry curr_spx_fiq /* FIQ/vFIQ */
+ ventry curr_spx_serror /* SError/VSError */
+
+ /* Lower EL using AArch64. */
+ ventry lower_a64_sync /* Synchronous */
+ ventry lower_a64_irq /* IRQ/vIRQ */
+ ventry lower_a64_fiq /* FIQ/vFIQ */
+ ventry lower_a64_serror /* SError/VSError */
+
+ /* Lower EL using AArch32. */
+ ventry lower_a32_sync /* Synchronous */
+ ventry lower_a32_irq /* IRQ/vIRQ */
+ ventry lower_a32_fiq /* FIQ/vFIQ */
+ ventry lower_a32_serror /* SError/VSError */
+
+ .text
+ .align 4
+
+ /* Common vector handling for now */
+curr_sp0_sync:
+curr_sp0_irq:
+curr_sp0_fiq:
+curr_sp0_serror:
+curr_spx_sync:
+curr_spx_irq:
+curr_spx_fiq:
+curr_spx_serror:
+lower_a64_sync:
+lower_a64_irq:
+lower_a64_fiq:
+lower_a64_serror:
+lower_a32_sync:
+lower_a32_irq:
+lower_a32_fiq:
+lower_a32_serror:
+ mov x0, SYS_WRITE0
+ adr x1, .error
+ semihosting_call
+ mov x0, SYS_EXIT
+ mov x1, 1
+ semihosting_call
+ /* never returns */
+
+ .section .rodata
+.error:
+ .string "Terminated by exception.\n"
+
+ .text
+ .align 4
+ .global __start
+__start:
+ /* Installs a table of exception vectors to catch and handle all
+ exceptions by terminating the process with a diagnostic. */
+ adr x0, vector_table
+ msr vbar_el1, x0
+
+ /* Page table setup (identity mapping). */
+ adrp x0, ttb
+ add x0, x0, :lo12:ttb
+ msr ttbr0_el1, x0
+
+ /*
+ * Setup a flat address mapping page-tables. Stage one simply
+ * maps RAM to the first Gb. The stage2 tables have two 2mb
+ * translation block entries covering a series of adjacent
+ * 4k pages.
+ */
+
+ /* Stage 1 entry: indexed by IA[38:30] */
+ adr x1, . /* phys address */
+ bic x1, x1, #(1 << 30) - 1 /* 1GB alignment*/
+ add x2, x0, x1, lsr #(30 - 3) /* offset in l1 page table */
+
+ /* point to stage 2 table [47:12] */
+ adrp x0, ttb_stage2
+ orr x1, x0, #3 /* ptr to stage 2 */
+ str x1, [x2]
+
+ /* Stage 2 entries: indexed by IA[29:21] */
+ ldr x5, =(((1 << 9) - 1) << 21)
+
+ /* First block: .text/RO/execute enabled */
+ adr x1, . /* phys address */
+ bic x1, x1, #(1 << 21) - 1 /* 2mb block alignment */
+ and x4, x1, x5 /* IA[29:21] */
+ add x2, x0, x4, lsr #(21 - 3) /* offset in l2 page table */
+ ldr x3, =0x401 /* attr(AF, block) */
+ orr x1, x1, x3
+ str x1, [x2] /* 1st 2mb (.text & rodata) */
+
+ /* Second block: .data/RW/no execute */
+ adrp x1, .data
+ add x1, x1, :lo12:.data
+ bic x1, x1, #(1 << 21) - 1 /* 2mb block alignment */
+ and x4, x1, x5 /* IA[29:21] */
+ add x2, x0, x4, lsr #(21 - 3) /* offset in l2 page table */
+ ldr x3, =(3 << 53) | 0x401 /* attr(AF, NX, block) */
+ orr x1, x1, x3
+ str x1, [x2] /* 2nd 2mb (.data & .bss)*/
+
+ /* Setup/enable the MMU. */
+
+ /*
+ * TCR_EL1 - Translation Control Registers
+ *
+ * IPS[34:32] = 40-bit PA, 1TB
+ * TG0[14:15] = b00 => 4kb granuale
+ * ORGN0[11:10] = Outer: Normal, WB Read-Alloc No Write-Alloc Cacheable
+ * IRGN0[9:8] = Inner: Normal, WB Read-Alloc No Write-Alloc Cacheable
+ * T0SZ[5:0] = 2^(64 - 25)
+ *
+ * The size of T0SZ controls what the initial lookup level. It
+ * would be nice to start at level 2 but unfortunatly for a
+ * flat-mapping on the virt machine we need to handle IA's
+ * with at least 1gb range to see RAM. So we start with a
+ * level 1 lookup.
+ */
+ ldr x0, = (2 << 32) | 25 | (3 << 10) | (3 << 8)
+ msr tcr_el1, x0
+
+ mov x0, #0xee /* Inner/outer cacheable WB */
+ msr mair_el1, x0
+ isb
+
+ /*
+ * SCTLR_EL1 - System Control Register
+ *
+ * WXN[19] = 0 = no effect, Write does not imply XN (execute never)
+ * I[12] = Instruction cachability control
+ * SA[3] = SP alignment check
+ * C[2] = Data cachability control
+ * M[0] = 1, enable stage 1 address translation for EL0/1
+ */
+ mrs x0, sctlr_el1
+ ldr x1, =0x100d /* bits I(12) SA(3) C(2) M(0) */
+ bic x0, x0, #(1 << 1) /* clear bit A(1) */
+ bic x0, x0, #(1 << 19) /* clear WXN */
+ orr x0, x0, x1 /* set bits */
+
+ dsb sy
+ msr sctlr_el1, x0
+ isb
+
+ /*
+ * Enable FP registers. The standard C pre-amble will be
+ * saving these and A-profile compilers will use AdvSIMD
+ * registers unless we tell it not to.
+ */
+ mrs x0, cpacr_el1
+ orr x0, x0, #(3 << 20)
+ msr cpacr_el1, x0
+
+ /* Setup some stack space and enter the test code.
+ * Assume everthing except the return value is garbage when we
+ * return, we won't need it.
+ */
+ adrp x0, stack_end
+ add x0, x0, :lo12:stack_end
+ mov sp, x0
+ bl main
+
+ /* pass return value to sys exit */
+ mov x1, x0
+ ldr x0, =0x20026 /* ADP_Stopped_ApplicationExit */
+ stp x0, x1, [sp, #-16]!
+ mov x1, sp
+ mov x0, SYS_EXIT
+ semihosting_call
+ /* never returns */
+
+ /*
+ * Helper Functions
+ */
+
+ /* Output a single character to serial port */
+ .global __sys_outc
+__sys_outc:
+ stp x0, x1, [sp, #-16]!
+ /* pass address of c on stack */
+ mov x1, sp
+ mov x0, SYS_WRITEC
+ semihosting_call
+ ldp x0, x1, [sp], #16
+ ret
+
+ .data
+ .align 12
+
+ /* Translation table
+ * @4k granuale: 9 bit lookup, 512 entries
+ */
+ttb:
+ .space 4096, 0
+
+ .align 12
+ttb_stage2:
+ .space 4096, 0
+
+ .align 12
+stack:
+ .space 65536, 0
+stack_end:
diff --git a/tests/tcg/aarch64/system/kernel.ld b/tests/tcg/aarch64/system/kernel.ld
new file mode 100644
index 0000000000..7b3a76dcbf
--- /dev/null
+++ b/tests/tcg/aarch64/system/kernel.ld
@@ -0,0 +1,24 @@
+ENTRY(__start)
+
+SECTIONS
+{
+ /* virt machine, RAM starts at 1gb */
+ . = (1 << 30);
+ .text : {
+ *(.text)
+ }
+ .rodata : {
+ *(.rodata)
+ }
+ /* align r/w section to next 2mb */
+ . = ALIGN(1 << 21);
+ .data : {
+ *(.data)
+ }
+ .bss : {
+ *(.bss)
+ }
+ /DISCARD/ : {
+ *(.ARM.attributes)
+ }
+}
diff --git a/tests/tcg/alpha/Makefile.softmmu-target b/tests/tcg/alpha/Makefile.softmmu-target
new file mode 100644
index 0000000000..3c0f34cc69
--- /dev/null
+++ b/tests/tcg/alpha/Makefile.softmmu-target
@@ -0,0 +1,34 @@
+#
+# Alpha system tests
+#
+
+ALPHA_SYSTEM_SRC=$(SRC_PATH)/tests/tcg/alpha/system
+VPATH+=$(ALPHA_SYSTEM_SRC)
+
+# These objects provide the basic boot code and helper functions for all tests
+CRT_OBJS=boot.o
+
+ALPHA_TEST_SRCS=$(wildcard $(ALPHA_SYSTEM_SRC)/*.c)
+ALPHA_TESTS = $(patsubst $(ALPHA_SYSTEM_SRC)/%.c, %, $(ALPHA_TEST_SRCS))
+
+CRT_PATH=$(ALPHA_SYSTEM_SRC)
+LINK_SCRIPT=$(ALPHA_SYSTEM_SRC)/kernel.ld
+LDFLAGS=-Wl,-T$(LINK_SCRIPT)
+TESTS+=$(ALPHA_TESTS) $(MULTIARCH_TESTS)
+CFLAGS+=-nostdlib -g -O1 -mcpu=ev6 $(MINILIB_INC)
+LDFLAGS+=-static -nostdlib $(CRT_OBJS) $(MINILIB_OBJS) -lgcc
+
+# building head blobs
+.PRECIOUS: $(CRT_OBJS)
+
+%.o: $(CRT_PATH)/%.S
+ $(CC) $(CFLAGS) -x assembler-with-cpp -c $< -o $@
+
+# Build and link the tests
+%: %.c $(LINK_SCRIPT) $(CRT_OBJS) $(MINILIB_OBJS)
+ $(CC) $(CFLAGS) $< -o $@ $(LDFLAGS)
+
+memory: CFLAGS+=-DCHECK_UNALIGNED=0
+
+# Running
+QEMU_OPTS+=-serial chardev:output -kernel
diff --git a/tests/tcg/alpha/system/boot.S b/tests/tcg/alpha/system/boot.S
new file mode 100644
index 0000000000..9791b1ef7c
--- /dev/null
+++ b/tests/tcg/alpha/system/boot.S
@@ -0,0 +1,511 @@
+/*
+ * Minimal Alpha system boot code.
+ *
+ * Copyright Linaro Ltd 2019
+ */
+
+ .set noat
+ .set nomacro
+ .arch ev6
+ .text
+
+.macro load_pci_io reg
+ /* For typhoon, this is
+ * 0xfffffc0000000000 -- kseg identity map
+ * + 0x10000000000 -- typhoon pio base
+ * + 0x1fc000000 -- typhoon pchip0 pci base
+ * = 0xfffffd01fc000000
+ */
+ ldah \reg, -3 /* ff..fd0000 */
+ lda \reg, 0x1fc(\reg) /* ff..fd01fc */
+ sll \reg, 24, \reg
+.endm
+
+#define com1Rbr 0x3f8
+#define com1Thr 0x3f8
+#define com1Ier 0x3f9
+#define com1Iir 0x3fa
+#define com1Lcr 0x3fb
+#define com1Mcr 0x3fc
+#define com1Lsr 0x3fd
+#define com1Msr 0x3fe
+#define com1Scr 0x3ff
+#define com1Dll 0x3f8
+#define com1Dlm 0x3f9
+
+#define PAL_halt 0
+#define PAL_wrent 52
+#define PAL_wrkgp 55
+
+ .text
+ .p2align 4
+ .globl _start
+ .ent _start
+_start:
+ br $gp, .+4
+ ldah $gp, 0($gp) !gpdisp!1
+ lda $gp, 0($gp) !gpdisp!1
+
+ ldah $sp, $stack_end($gp) !gprelhigh
+ lda $sp, $stack_end($sp) !gprellow
+
+ /* Install kernel gp for exception handlers. */
+ mov $gp, $16
+ call_pal PAL_wrkgp
+
+ /* Install exception handlers. */
+ ldah $16, entInt($gp) !gprelhigh
+ lda $16, entInt($16) !gprellow
+ lda $17, 0
+ call_pal PAL_wrent
+
+ ldah $16, entArith($gp) !gprelhigh
+ lda $16, entArith($16) !gprellow
+ lda $17, 1
+ call_pal PAL_wrent
+
+ ldah $16, entMM($gp) !gprelhigh
+ lda $16, entMM($16) !gprellow
+ lda $17, 2
+ call_pal PAL_wrent
+
+ ldah $16, entIF($gp) !gprelhigh
+ lda $16, entIF($16) !gprellow
+ lda $17, 3
+ call_pal PAL_wrent
+
+ ldah $16, entUna($gp) !gprelhigh
+ lda $16, entUna($16) !gprellow
+ lda $17, 4
+ call_pal PAL_wrent
+
+ ldah $16, entSys($gp) !gprelhigh
+ lda $16, entSys($16) !gprellow
+ lda $17, 5
+ call_pal PAL_wrent
+
+ /*
+ * Initialize COM1.
+ */
+ load_pci_io $1
+ lda $2, 0x87 /* outb(0x87, com1Lcr); */
+ stb $2, com1Lcr($1)
+ stb $31, com1Dlm($1) /* outb(0, com1Dlm); */
+ lda $2, 3 /* baudconst 3 => 56000 */
+ stb $2, com1Dll($1) /* outb(baudconst, com1Dll); */
+ lda $2, 0x07
+ stb $2, com1Lcr($1) /* outb(0x07, com1Lcr) */
+ lda $2, 0x0f
+ stb $2, com1Mcr($1) /* outb(0x0f, com1Mcr) */
+
+ bsr $26, main !samegp
+
+ /* fall through to _exit */
+ .end _start
+
+ .globl _exit
+ .ent _exit
+_exit:
+ .frame $sp, 0, $26, 0
+ .prologue 0
+
+ /* We cannot return an error code. */
+ call_pal PAL_halt
+ .end _exit
+
+/*
+ * We have received an exception that we don't handle. Log and exit.
+ */
+ .ent log_exit
+log_exit:
+entInt:
+entArith:
+entMM:
+entIF:
+entUna:
+entSys:
+ ldah $16, $errormsg($gp) !gprelhigh
+ lda $16, $errormsg($16) !gprellow
+ bsr $26, __sys_outs !samegp
+ bsr $26, _exit !samegp
+ .end log_exit
+
+ .section .rodata
+$errormsg:
+ .string "Terminated by exception.\n"
+ .previous
+
+ /*
+ * Helper Functions
+ */
+
+ /* Output a single character to serial port */
+ .global __sys_outc
+ .ent __sys_outc
+__sys_outc:
+ .frame $sp, 0, $26, 0
+ .prologue 0
+
+ load_pci_io $1
+
+ /*
+ * while ((inb(com1Lsr) & 0x20) == 0)
+ * continue;
+ */
+1: ldbu $0, com1Lsr($1)
+ and $0, 0x20, $0
+ beq $0, 1b
+
+ /* outb(c, com1Thr); */
+ stb $16, com1Thr($1)
+ ret
+ .end __sys_outc
+
+ /* Output a nul-terminated string to serial port */
+ .global __sys_outs
+ .ent __sys_outs
+__sys_outs:
+ .frame $sp, 0, $26, 0
+ .prologue 0
+
+ load_pci_io $1
+
+ ldbu $2, 0($16)
+ beq $2, 9f
+
+ /*
+ * while ((inb(com1Lsr) & 0x20) == 0)
+ * continue;
+ */
+1: ldbu $0, com1Lsr($1)
+ and $0, 0x20, $0
+ beq $0, 1b
+
+ /* outb(c, com1Thr); */
+ stb $2, com1Thr($1)
+
+ addq $16, 1, $16
+ ldbu $2, 0($16)
+ bne $2, 1b
+
+9: ret
+ .end __sys_outs
+
+/*
+ * Division routines that are normally in libc.
+ *
+ * These do not follow the C calling convention. Arguments are in $24+$25,
+ * the result is in $27. Register $28 may be clobbered; everything else
+ * must be saved.
+ *
+ * We store the remainder in $28, so that we can share code.
+ *
+ * We do not signal divide by zero.
+ */
+
+/*
+ * Unsigned 64-bit division.
+ */
+
+ .globl __divqu
+ .ent __divqu
+__divqu:
+ .frame $sp, 48, $23
+ subq $sp, 48, $sp
+ stq $0, 0($sp)
+ stq $1, 8($sp)
+ stq $2, 16($sp)
+ stq $3, 24($sp)
+ stq $4, 32($sp)
+ .prologue 0
+
+#define mask $0
+#define divisor $1
+#define compare $2
+#define tmp1 $3
+#define tmp2 $4
+#define quotient $27
+#define modulus $28
+
+ mov $24, modulus
+ mov $25, divisor
+ mov $31, quotient
+ mov 1, mask
+ beq $25, 9f
+
+ /* Shift left until divisor >= modulus. */
+1: cmpult divisor, modulus, compare
+ blt divisor, 2f
+ addq divisor, divisor, divisor
+ addq mask, mask, mask
+ bne compare, 1b
+
+2: addq quotient, mask, tmp2
+ srl mask, 1, mask
+ cmpule divisor, modulus, compare
+ subq modulus, divisor, tmp1
+ cmovne compare, tmp2, quotient
+ srl divisor, 1, divisor
+ cmovne compare, tmp1, modulus
+ bne mask, 2b
+
+9: ldq $0, 0($sp)
+ ldq $1, 8($sp)
+ ldq $2, 16($sp)
+ ldq $3, 24($sp)
+ ldq $4, 32($sp)
+ addq $sp, 48, $sp
+ ret $31, ($23), 1
+
+#undef mask
+#undef divisor
+#undef compare
+#undef tmp1
+#undef tmp2
+#undef quotient
+#undef modulus
+
+ .end __divqu
+
+/*
+ * Unsigned 64-bit remainder.
+ * Note that __divqu above leaves the result in $28.
+ */
+
+ .globl __remqu
+ .ent __remqu
+__remqu:
+ .frame $sp, 16, $23
+ subq $sp, 16, $sp
+ stq $23, 0($sp)
+ .prologue 0
+
+ bsr $23, __divqu
+
+ ldq $23, 0($sp)
+ mov $28, $27
+ addq $sp, 16, $sp
+ ret $31, ($23), 1
+ .end __remqu
+
+/*
+ * Signed 64-bit division.
+ */
+
+ .globl __divqs
+ .ent __divqs
+__divqs:
+ .prologue 0
+
+ /* Common case: both arguments are positive. */
+ bis $24, $25, $28
+ bge $28, __divqu
+
+ /* At least one argument is negative. */
+ subq $sp, 32, $sp
+ stq $23, 0($sp)
+ stq $24, 8($sp)
+ stq $25, 16($sp)
+
+ /* Compute absolute values. */
+ subq $31, $24, $28
+ cmovlt $24, $28, $24
+ subq $31, $25, $28
+ cmovlt $25, $28, $25
+
+ bsr $23, __divqu
+
+ ldq $24, 8($sp)
+ ldq $25, 16($sp)
+
+ /* -a / b = a / -b = -(a / b) */
+ subq $31, $27, $23
+ xor $24, $25, $28
+ cmovlt $28, $23, $27
+
+ ldq $23, 0($sp)
+ addq $sp, 32, $sp
+ ret $31, ($23), 1
+ .end __divqs
+
+/*
+ * Signed 64-bit remainder.
+ */
+
+ .globl __remqs
+ .ent __remqs
+__remqs:
+ .prologue 0
+
+ /* Common case: both arguments are positive. */
+ bis $24, $25, $28
+ bge $28, __remqu
+
+ /* At least one argument is negative. */
+ subq $sp, 32, $sp
+ stq $23, 0($sp)
+ stq $24, 8($sp)
+ stq $25, 16($sp)
+
+ /* Compute absolute values. */
+ subq $31, $24, $28
+ cmovlt $24, $28, $24
+ subq $31, $25, $28
+ cmovlt $25, $28, $25
+
+ bsr $23, __divqu
+
+ ldq $23, 0($sp)
+ ldq $24, 8($sp)
+ ldq $25, 16($sp)
+
+ /* -a % b = -(a % b); a % -b = a % b. */
+ subq $31, $28, $27
+ cmovge $24, $28, $27
+
+ addq $sp, 32, $sp
+ ret $31, ($23), 1
+ .end __remqs
+
+/*
+ * Unsigned 32-bit division.
+ */
+
+ .globl __divlu
+ .ent __divlu
+__divlu:
+ .frame $sp, 32, $23
+ subq $sp, 32, $sp
+ stq $23, 0($sp)
+ stq $24, 8($sp)
+ stq $25, 16($sp)
+ .prologue 0
+
+ /* Zero extend and use the 64-bit routine. */
+ zap $24, 0xf0, $24
+ zap $25, 0xf0, $25
+ bsr $23, __divqu
+
+ addl $27, 0, $27
+ ldq $23, 0($sp)
+ ldq $24, 8($sp)
+ ldq $25, 16($sp)
+ addq $sp, 32, $sp
+ ret $31, ($23), 1
+ .end __divlu
+
+/*
+ * Unsigned 32-bit remainder.
+ */
+
+ .globl __remlu
+ .ent __remlu
+__remlu:
+ .frame $sp, 32, $23
+ subq $sp, 32, $sp
+ stq $23, 0($sp)
+ stq $24, 8($sp)
+ stq $25, 16($sp)
+ .prologue 0
+
+ /* Zero extend and use the 64-bit routine. */
+ zap $24, 0xf0, $24
+ zap $25, 0xf0, $25
+ bsr $23, __divqu
+
+ /* Recall that the remainder is returned in $28. */
+ addl $28, 0, $27
+ ldq $23, 0($sp)
+ ldq $24, 8($sp)
+ ldq $25, 16($sp)
+ addq $sp, 32, $sp
+ ret $31, ($23), 1
+ .end __remlu
+
+/*
+ * Signed 32-bit division.
+ */
+
+ .globl __divls
+ .ent __divls
+__divls:
+ .frame $sp, 32, $23
+ subq $sp, 32, $sp
+ stq $23, 0($sp)
+ stq $24, 8($sp)
+ stq $25, 16($sp)
+ .prologue 0
+
+ /* Sign extend. */
+ addl $24, 0, $24
+ addl $25, 0, $25
+
+ /* Compute absolute values. */
+ subq $31, $24, $28
+ cmovlt $24, $28, $24
+ subq $31, $25, $28
+ cmovlt $25, $28, $25
+
+ bsr $23, __divqu
+
+ ldq $24, 8($sp)
+ ldq $25, 16($sp)
+
+ /* Negate the unsigned result, if necessary. */
+ xor $24, $25, $28
+ subl $31, $27, $23
+ addl $27, 0, $27
+ addl $28, 0, $28
+ cmovlt $28, $23, $27
+
+ ldq $23, 0($sp)
+ addq $sp, 32, $sp
+ ret $31, ($23), 1
+ .end __divls
+
+/*
+ * Signed 32-bit remainder.
+ */
+
+ .globl __remls
+ .ent __remls
+__remls:
+ .frame $sp, 32, $23
+ subq $sp, 32, $sp
+ stq $23, 0($sp)
+ stq $24, 8($sp)
+ stq $25, 16($sp)
+ .prologue 0
+
+ /* Sign extend. */
+ addl $24, 0, $24
+ addl $25, 0, $25
+
+ /* Compute absolute values. */
+ subq $31, $24, $28
+ cmovlt $24, $28, $24
+ subq $31, $25, $28
+ cmovlt $25, $28, $25
+
+ bsr $23, __divqu
+
+ ldq $23, 0($sp)
+ ldq $24, 8($sp)
+ ldq $25, 16($sp)
+
+ /* Negate the unsigned result, if necessary. */
+ subl $31, $28, $27
+ addl $28, 0, $28
+ cmovge $24, $28, $27
+
+ addq $sp, 32, $sp
+ ret $31, ($23), 1
+ .end __remls
+
+ .data
+ .p2align 4
+stack:
+ .skip 65536
+$stack_end:
+ .type stack,@object
+ .size stack, . - stack
diff --git a/tests/tcg/alpha/system/kernel.ld b/tests/tcg/alpha/system/kernel.ld
new file mode 100644
index 0000000000..d2ac6ecfeb
--- /dev/null
+++ b/tests/tcg/alpha/system/kernel.ld
@@ -0,0 +1,30 @@
+ENTRY(_start)
+
+SECTIONS
+{
+ /* Linux kernel legacy start address. */
+ . = 0xfffffc0000310000;
+ _text = .;
+ .text : {
+ *(.text)
+ }
+ .rodata : {
+ *(.rodata)
+ }
+ _etext = .;
+
+ . = ALIGN(8192);
+ _data = .;
+ .got : {
+ *(.got)
+ }
+ .data : {
+ *(.sdata)
+ *(.data)
+ }
+ _edata = .;
+ .bss : {
+ *(.bss)
+ }
+ _end = .;
+}
diff --git a/tests/tcg/i386/Makefile.softmmu-target b/tests/tcg/i386/Makefile.softmmu-target
index 53c9c5ece0..e1f98177aa 100644
--- a/tests/tcg/i386/Makefile.softmmu-target
+++ b/tests/tcg/i386/Makefile.softmmu-target
@@ -27,7 +27,7 @@ CFLAGS+=-m32
LINK_SCRIPT=$(I386_SYSTEM_SRC)/kernel.ld
LDFLAGS=-Wl,-T$(LINK_SCRIPT) -Wl,-melf_i386
# FIXME: move to common once x86_64 is bootstrapped
-TESTS+=$(X86_TESTS)
+TESTS+=$(X86_TESTS) $(MULTIARCH_TESTS)
endif
CFLAGS+=-nostdlib -ggdb -O0 $(MINILIB_INC)
LDFLAGS+=-static -nostdlib $(CRT_OBJS) $(MINILIB_OBJS) -lgcc
@@ -42,5 +42,7 @@ LDFLAGS+=-static -nostdlib $(CRT_OBJS) $(MINILIB_OBJS) -lgcc
%: %.c $(LINK_SCRIPT) $(CRT_OBJS) $(MINILIB_OBJS)
$(CC) $(CFLAGS) $< -o $@ $(LDFLAGS)
+memory: CFLAGS+=-DCHECK_UNALIGNED=1
+
# Running
QEMU_OPTS+=-device isa-debugcon,chardev=output -device isa-debug-exit,iobase=0xf4,iosize=0x4 -kernel
diff --git a/tests/tcg/i386/system/memory.c b/tests/tcg/i386/system/memory.c
deleted file mode 100644
index a7a0a8e978..0000000000
--- a/tests/tcg/i386/system/memory.c
+++ /dev/null
@@ -1,243 +0,0 @@
-/*
- * Memory Test
- *
- * This is intended to test the softmmu code and ensure we properly
- * behave across normal and unaligned accesses across several pages.
- * We are not replicating memory tests for stuck bits and other
- * hardware level failures but looking for issues with different size
- * accesses when:
-
- *
- */
-
-#include <inttypes.h>
-#include <minilib.h>
-
-#define TEST_SIZE (4096 * 4) /* 4 pages */
-
-static uint8_t test_data[TEST_SIZE];
-
-static void pdot(int count)
-{
- if (count % 128 == 0) {
- ml_printf(".");
- }
-}
-
-
-/*
- * Fill the data with ascending value bytes. As x86 is a LE machine we
- * write in ascending order and then read and high byte should either
- * be zero or higher than the lower bytes.
- */
-
-static void init_test_data_u8(void)
-{
- uint8_t count = 0, *ptr = &test_data[0];
- int i;
-
- ml_printf("Filling test area with u8:");
- for (i = 0; i < TEST_SIZE; i++) {
- *ptr++ = count++;
- pdot(i);
- }
- ml_printf("done\n");
-}
-
-static void init_test_data_u16(int offset)
-{
- uint8_t count = 0;
- uint16_t word, *ptr = (uint16_t *) &test_data[0];
- const int max = (TEST_SIZE - offset) / sizeof(word);
- int i;
-
- ml_printf("Filling test area with u16 (offset %d):", offset);
-
- /* Leading zeros */
- for (i = 0; i < offset; i++) {
- *ptr = 0;
- }
-
- ptr = (uint16_t *) &test_data[offset];
- for (i = 0; i < max; i++) {
- uint8_t high, low;
- low = count++;
- high = count++;
- word = (high << 8) | low;
- *ptr++ = word;
- pdot(i);
- }
- ml_printf("done\n");
-}
-
-static void init_test_data_u32(int offset)
-{
- uint8_t count = 0;
- uint32_t word, *ptr = (uint32_t *) &test_data[0];
- const int max = (TEST_SIZE - offset) / sizeof(word);
- int i;
-
- ml_printf("Filling test area with u32 (offset %d):", offset);
-
- /* Leading zeros */
- for (i = 0; i < offset; i++) {
- *ptr = 0;
- }
-
- ptr = (uint32_t *) &test_data[offset];
- for (i = 0; i < max; i++) {
- uint8_t b1, b2, b3, b4;
- b4 = count++;
- b3 = count++;
- b2 = count++;
- b1 = count++;
- word = (b1 << 24) | (b2 << 16) | (b3 << 8) | b4;
- *ptr++ = word;
- pdot(i);
- }
- ml_printf("done\n");
-}
-
-
-static int read_test_data_u16(int offset)
-{
- uint16_t word, *ptr = (uint16_t *)&test_data[offset];
- int i;
- const int max = (TEST_SIZE - offset) / sizeof(word);
-
- ml_printf("Reading u16 from %#lx (offset %d):", ptr, offset);
-
- for (i = 0; i < max; i++) {
- uint8_t high, low;
- word = *ptr++;
- high = (word >> 8) & 0xff;
- low = word & 0xff;
- if (high < low && high != 0) {
- ml_printf("Error %d < %d\n", high, low);
- return 1;
- } else {
- pdot(i);
- }
-
- }
- ml_printf("done\n");
- return 0;
-}
-
-static int read_test_data_u32(int offset)
-{
- uint32_t word, *ptr = (uint32_t *)&test_data[offset];
- int i;
- const int max = (TEST_SIZE - offset) / sizeof(word);
-
- ml_printf("Reading u32 from %#lx (offset %d):", ptr, offset);
-
- for (i = 0; i < max; i++) {
- uint8_t b1, b2, b3, b4;
- word = *ptr++;
-
- b1 = word >> 24 & 0xff;
- b2 = word >> 16 & 0xff;
- b3 = word >> 8 & 0xff;
- b4 = word & 0xff;
-
- if ((b1 < b2 && b1 != 0) ||
- (b2 < b3 && b2 != 0) ||
- (b3 < b4 && b3 != 0)) {
- ml_printf("Error %d, %d, %d, %d", b1, b2, b3, b4);
- return 2;
- } else {
- pdot(i);
- }
- }
- ml_printf("done\n");
- return 0;
-}
-
-static int read_test_data_u64(int offset)
-{
- uint64_t word, *ptr = (uint64_t *)&test_data[offset];
- int i;
- const int max = (TEST_SIZE - offset) / sizeof(word);
-
- ml_printf("Reading u64 from %#lx (offset %d):", ptr, offset);
-
- for (i = 0; i < max; i++) {
- uint8_t b1, b2, b3, b4, b5, b6, b7, b8;
- word = *ptr++;
-
- b1 = ((uint64_t) (word >> 56)) & 0xff;
- b2 = ((uint64_t) (word >> 48)) & 0xff;
- b3 = ((uint64_t) (word >> 40)) & 0xff;
- b4 = (word >> 32) & 0xff;
- b5 = (word >> 24) & 0xff;
- b6 = (word >> 16) & 0xff;
- b7 = (word >> 8) & 0xff;
- b8 = (word >> 0) & 0xff;
-
- if ((b1 < b2 && b1 != 0) ||
- (b2 < b3 && b2 != 0) ||
- (b3 < b4 && b3 != 0) ||
- (b4 < b5 && b4 != 0) ||
- (b5 < b6 && b5 != 0) ||
- (b6 < b7 && b6 != 0) ||
- (b7 < b8 && b7 != 0)) {
- ml_printf("Error %d, %d, %d, %d, %d, %d, %d, %d",
- b1, b2, b3, b4, b5, b6, b7, b8);
- return 2;
- } else {
- pdot(i);
- }
- }
- ml_printf("done\n");
- return 0;
-}
-
-/* Read the test data and verify at various offsets */
-int do_reads(void)
-{
- int r = 0;
- int off = 0;
-
- while (r == 0 && off < 8) {
- r = read_test_data_u16(off);
- r |= read_test_data_u32(off);
- r |= read_test_data_u64(off);
- off++;
- }
-
- return r;
-}
-
-int main(void)
-{
- int i, r = 0;
-
-
- init_test_data_u8();
- r = do_reads();
- if (r) {
- return r;
- }
-
- for (i = 0; i < 8; i++) {
- init_test_data_u16(i);
-
- r = do_reads();
- if (r) {
- return r;
- }
- }
-
- for (i = 0; i < 8; i++) {
- init_test_data_u32(i);
-
- r = do_reads();
- if (r) {
- return r;
- }
- }
-
- ml_printf("Test complete: %s\n", r == 0 ? "PASSED" : "FAILED");
- return r;
-}
diff --git a/tests/tcg/minilib/printf.c b/tests/tcg/minilib/printf.c
index 121620cb16..10472b4f58 100644
--- a/tests/tcg/minilib/printf.c
+++ b/tests/tcg/minilib/printf.c
@@ -119,6 +119,9 @@ void ml_printf(const char *fmt, ...)
str = va_arg(ap, char*);
print_str(str);
break;
+ case 'c':
+ __sys_outc(va_arg(ap, int));
+ break;
case '%':
__sys_outc(*fmt);
break;
diff --git a/tests/tcg/multiarch/system/Makefile.softmmu-target b/tests/tcg/multiarch/system/Makefile.softmmu-target
new file mode 100644
index 0000000000..db4bbeda44
--- /dev/null
+++ b/tests/tcg/multiarch/system/Makefile.softmmu-target
@@ -0,0 +1,14 @@
+# -*- Mode: makefile -*-
+#
+# Multiarch system tests
+#
+# We just collect the tests together here and rely on the actual guest
+# architecture to add to the test dependancies and deal with the
+# complications of building.
+#
+
+MULTIARCH_SYSTEM_SRC=$(SRC_PATH)/tests/tcg/multiarch/system
+VPATH+=$(MULTIARCH_SYSTEM_SRC)
+
+MULTIARCH_TEST_SRCS=$(wildcard $(MULTIARCH_SYSTEM_SRC)/*.c)
+MULTIARCH_TESTS = $(patsubst $(MULTIARCH_SYSTEM_SRC)/%.c, %, $(MULTIARCH_TEST_SRCS))
diff --git a/tests/tcg/i386/system/hello.c b/tests/tcg/multiarch/system/hello.c
index 821dc0ef09..821dc0ef09 100644
--- a/tests/tcg/i386/system/hello.c
+++ b/tests/tcg/multiarch/system/hello.c
diff --git a/tests/tcg/multiarch/system/memory.c b/tests/tcg/multiarch/system/memory.c
new file mode 100644
index 0000000000..dc1d8a98ff
--- /dev/null
+++ b/tests/tcg/multiarch/system/memory.c
@@ -0,0 +1,449 @@
+/*
+ * Memory Test
+ *
+ * This is intended to test the softmmu code and ensure we properly
+ * behave across normal and unaligned accesses across several pages.
+ * We are not replicating memory tests for stuck bits and other
+ * hardware level failures but looking for issues with different size
+ * accesses when access is:
+ *
+ * - unaligned at various sizes (if -DCHECK_UNALIGNED set)
+ * - spanning a (softmmu) page
+ * - sign extension when loading
+ */
+
+#include <inttypes.h>
+#include <stdbool.h>
+#include <minilib.h>
+
+#ifndef CHECK_UNALIGNED
+# error "Target does not specify CHECK_UNALIGNED"
+#endif
+
+#define PAGE_SIZE 4096 /* nominal 4k "pages" */
+#define TEST_SIZE (PAGE_SIZE * 4) /* 4 pages */
+
+#define ARRAY_SIZE(x) ((sizeof(x) / sizeof((x)[0])))
+
+__attribute__((aligned(PAGE_SIZE)))
+static uint8_t test_data[TEST_SIZE];
+
+typedef void (*init_ufn) (int offset);
+typedef bool (*read_ufn) (int offset);
+typedef bool (*read_sfn) (int offset, bool nf);
+
+static void pdot(int count)
+{
+ if (count % 128 == 0) {
+ ml_printf(".");
+ }
+}
+
+/*
+ * Helper macros for shift/extract so we can keep our endian handling
+ * in one place.
+ */
+#define BYTE_SHIFT(b, pos) ((uint64_t)b << (pos * 8))
+#define BYTE_EXTRACT(b, pos) ((b >> (pos * 8)) & 0xff)
+
+/*
+ * Fill the data with ascending value bytes.
+ *
+ * Currently we only support Little Endian machines so write in
+ * ascending address order. When we read higher address bytes should
+ * either be zero or higher than the lower bytes.
+ */
+
+static void init_test_data_u8(int unused_offset)
+{
+ uint8_t count = 0, *ptr = &test_data[0];
+ int i;
+ (void)(unused_offset);
+
+ ml_printf("Filling test area with u8:");
+ for (i = 0; i < TEST_SIZE; i++) {
+ *ptr++ = count++;
+ pdot(i);
+ }
+ ml_printf("done\n");
+}
+
+/*
+ * Full the data with alternating positive and negative bytes. This
+ * should mean for reads larger than a byte all subsequent reads will
+ * stay either negative or positive. We never write 0.
+ */
+
+static inline uint8_t get_byte(int index, bool neg)
+{
+ return neg ? (0xff << (index % 7)) : (0xff >> ((index % 6) + 1));
+}
+
+static void init_test_data_s8(bool neg_first)
+{
+ uint8_t top, bottom, *ptr = &test_data[0];
+ int i;
+
+ ml_printf("Filling test area with s8 pairs (%s):",
+ neg_first ? "neg first" : "pos first");
+ for (i = 0; i < TEST_SIZE / 2; i++) {
+ *ptr++ = get_byte(i, neg_first);
+ *ptr++ = get_byte(i, !neg_first);
+ pdot(i);
+ }
+ ml_printf("done\n");
+}
+
+/*
+ * Zero the first few bytes of the test data in preparation for
+ * new offset values.
+ */
+static void reset_start_data(int offset)
+{
+ uint32_t *ptr = (uint32_t *) &test_data[0];
+ int i;
+ for (i = 0; i < offset; i++) {
+ *ptr++ = 0;
+ }
+}
+
+static void init_test_data_u16(int offset)
+{
+ uint8_t count = 0;
+ uint16_t word, *ptr = (uint16_t *) &test_data[offset];
+ const int max = (TEST_SIZE - offset) / sizeof(word);
+ int i;
+
+ ml_printf("Filling test area with u16 (offset %d, %p):", offset, ptr);
+
+ reset_start_data(offset);
+
+ for (i = 0; i < max; i++) {
+ uint8_t low = count++, high = count++;
+ word = BYTE_SHIFT(high, 1) | BYTE_SHIFT(low, 0);
+ *ptr++ = word;
+ pdot(i);
+ }
+ ml_printf("done @ %p\n", ptr);
+}
+
+static void init_test_data_u32(int offset)
+{
+ uint8_t count = 0;
+ uint32_t word, *ptr = (uint32_t *) &test_data[offset];
+ const int max = (TEST_SIZE - offset) / sizeof(word);
+ int i;
+
+ ml_printf("Filling test area with u32 (offset %d, %p):", offset, ptr);
+
+ reset_start_data(offset);
+
+ for (i = 0; i < max; i++) {
+ uint8_t b4 = count++, b3 = count++;
+ uint8_t b2 = count++, b1 = count++;
+ word = BYTE_SHIFT(b1, 3) | BYTE_SHIFT(b2, 2) | BYTE_SHIFT(b3, 1) | b4;
+ *ptr++ = word;
+ pdot(i);
+ }
+ ml_printf("done @ %p\n", ptr);
+}
+
+static void init_test_data_u64(int offset)
+{
+ uint8_t count = 0;
+ uint64_t word, *ptr = (uint64_t *) &test_data[offset];
+ const int max = (TEST_SIZE - offset) / sizeof(word);
+ int i;
+
+ ml_printf("Filling test area with u64 (offset %d, %p):", offset, ptr);
+
+ reset_start_data(offset);
+
+ for (i = 0; i < max; i++) {
+ uint8_t b8 = count++, b7 = count++;
+ uint8_t b6 = count++, b5 = count++;
+ uint8_t b4 = count++, b3 = count++;
+ uint8_t b2 = count++, b1 = count++;
+ word = BYTE_SHIFT(b1, 7) | BYTE_SHIFT(b2, 6) | BYTE_SHIFT(b3, 5) |
+ BYTE_SHIFT(b4, 4) | BYTE_SHIFT(b5, 3) | BYTE_SHIFT(b6, 2) |
+ BYTE_SHIFT(b7, 1) | b8;
+ *ptr++ = word;
+ pdot(i);
+ }
+ ml_printf("done @ %p\n", ptr);
+}
+
+static bool read_test_data_u16(int offset)
+{
+ uint16_t word, *ptr = (uint16_t *)&test_data[offset];
+ int i;
+ const int max = (TEST_SIZE - offset) / sizeof(word);
+
+ ml_printf("Reading u16 from %#lx (offset %d):", ptr, offset);
+
+ for (i = 0; i < max; i++) {
+ uint8_t high, low;
+ word = *ptr++;
+ high = (word >> 8) & 0xff;
+ low = word & 0xff;
+ if (high < low && high != 0) {
+ ml_printf("Error %d < %d\n", high, low);
+ return false;
+ } else {
+ pdot(i);
+ }
+
+ }
+ ml_printf("done @ %p\n", ptr);
+ return true;
+}
+
+static bool read_test_data_u32(int offset)
+{
+ uint32_t word, *ptr = (uint32_t *)&test_data[offset];
+ int i;
+ const int max = (TEST_SIZE - offset) / sizeof(word);
+
+ ml_printf("Reading u32 from %#lx (offset %d):", ptr, offset);
+
+ for (i = 0; i < max; i++) {
+ uint8_t b1, b2, b3, b4;
+ word = *ptr++;
+
+ b1 = word >> 24 & 0xff;
+ b2 = word >> 16 & 0xff;
+ b3 = word >> 8 & 0xff;
+ b4 = word & 0xff;
+
+ if ((b1 < b2 && b1 != 0) ||
+ (b2 < b3 && b2 != 0) ||
+ (b3 < b4 && b3 != 0)) {
+ ml_printf("Error %d, %d, %d, %d", b1, b2, b3, b4);
+ return false;
+ } else {
+ pdot(i);
+ }
+ }
+ ml_printf("done @ %p\n", ptr);
+ return true;
+}
+
+static bool read_test_data_u64(int offset)
+{
+ uint64_t word, *ptr = (uint64_t *)&test_data[offset];
+ int i;
+ const int max = (TEST_SIZE - offset) / sizeof(word);
+
+ ml_printf("Reading u64 from %#lx (offset %d):", ptr, offset);
+
+ for (i = 0; i < max; i++) {
+ uint8_t b1, b2, b3, b4, b5, b6, b7, b8;
+ word = *ptr++;
+
+ b1 = ((uint64_t) (word >> 56)) & 0xff;
+ b2 = ((uint64_t) (word >> 48)) & 0xff;
+ b3 = ((uint64_t) (word >> 40)) & 0xff;
+ b4 = (word >> 32) & 0xff;
+ b5 = (word >> 24) & 0xff;
+ b6 = (word >> 16) & 0xff;
+ b7 = (word >> 8) & 0xff;
+ b8 = (word >> 0) & 0xff;
+
+ if ((b1 < b2 && b1 != 0) ||
+ (b2 < b3 && b2 != 0) ||
+ (b3 < b4 && b3 != 0) ||
+ (b4 < b5 && b4 != 0) ||
+ (b5 < b6 && b5 != 0) ||
+ (b6 < b7 && b6 != 0) ||
+ (b7 < b8 && b7 != 0)) {
+ ml_printf("Error %d, %d, %d, %d, %d, %d, %d, %d",
+ b1, b2, b3, b4, b5, b6, b7, b8);
+ return false;
+ } else {
+ pdot(i);
+ }
+ }
+ ml_printf("done @ %p\n", ptr);
+ return true;
+}
+
+/* Read the test data and verify at various offsets */
+read_ufn read_ufns[] = { read_test_data_u16,
+ read_test_data_u32,
+ read_test_data_u64 };
+
+bool do_unsigned_reads(void)
+{
+ int i;
+ bool ok = true;
+
+ for (i = 0; i < ARRAY_SIZE(read_ufns) && ok; i++) {
+#if CHECK_UNALIGNED
+ int off;
+ for (off = 0; off < 8 && ok; off++) {
+ ok = read_ufns[i](off);
+ }
+#else
+ ok = read_ufns[i](0);
+#endif
+ }
+
+ return ok;
+}
+
+static bool do_unsigned_test(init_ufn fn)
+{
+#if CHECK_UNALIGNED
+ bool ok = true;
+ int i;
+ for (i = 0; i < 8 && ok; i++) {
+ fn(i);
+ ok = do_unsigned_reads();
+ }
+#else
+ fn(0);
+ return do_unsigned_reads();
+#endif
+}
+
+/*
+ * We need to ensure signed data is read into a larger data type to
+ * ensure that sign extension is working properly.
+ */
+
+static bool read_test_data_s8(int offset, bool neg_first)
+{
+ int8_t *ptr = (int8_t *)&test_data[offset];
+ int i;
+ const int max = (TEST_SIZE - offset) / 2;
+
+ ml_printf("Reading s8 pairs from %#lx (offset %d):", ptr, offset);
+
+ for (i = 0; i < max; i++) {
+ int16_t first, second;
+ bool ok;
+ first = *ptr++;
+ second = *ptr++;
+
+ if (neg_first && first < 0 && second > 0) {
+ pdot(i);
+ } else if (!neg_first && first > 0 && second < 0) {
+ pdot(i);
+ } else {
+ ml_printf("Error %d %c %d\n", first, neg_first ? '<' : '>', second);
+ return false;
+ }
+ }
+ ml_printf("done @ %p\n", ptr);
+ return true;
+}
+
+static bool read_test_data_s16(int offset, bool neg_first)
+{
+ int16_t *ptr = (int16_t *)&test_data[offset];
+ int i;
+ const int max = (TEST_SIZE - offset) / (sizeof(*ptr));
+
+ ml_printf("Reading s16 from %#lx (offset %d, %s):", ptr,
+ offset, neg_first ? "neg" : "pos");
+
+ for (i = 0; i < max; i++) {
+ int32_t data = *ptr++;
+
+ if (neg_first && data < 0) {
+ pdot(i);
+ } else if (data > 0) {
+ pdot(i);
+ } else {
+ ml_printf("Error %d %c 0\n", data, neg_first ? '<' : '>');
+ return false;
+ }
+ }
+ ml_printf("done @ %p\n", ptr);
+ return true;
+}
+
+static bool read_test_data_s32(int offset, bool neg_first)
+{
+ int32_t *ptr = (int32_t *)&test_data[offset];
+ int i;
+ const int max = (TEST_SIZE - offset) / (sizeof(int32_t));
+
+ ml_printf("Reading s32 from %#lx (offset %d, %s):",
+ ptr, offset, neg_first ? "neg" : "pos");
+
+ for (i = 0; i < max; i++) {
+ int64_t data = *ptr++;
+
+ if (neg_first && data < 0) {
+ pdot(i);
+ } else if (data > 0) {
+ pdot(i);
+ } else {
+ ml_printf("Error %d %c 0\n", data, neg_first ? '<' : '>');
+ return false;
+ }
+ }
+ ml_printf("done @ %p\n", ptr);
+ return true;
+}
+
+/*
+ * Read the test data and verify at various offsets
+ *
+ * For everything except bytes all our reads should be either positive
+ * or negative depending on what offset we are reading from. Currently
+ * we only handle LE systems.
+ */
+read_sfn read_sfns[] = { read_test_data_s8,
+ read_test_data_s16,
+ read_test_data_s32 };
+
+bool do_signed_reads(bool neg_first)
+{
+ int i;
+ bool ok = true;
+
+ for (i = 0; i < ARRAY_SIZE(read_sfns) && ok; i++) {
+#if CHECK_UNALIGNED
+ int off;
+ for (off = 0; off < 8 && ok; off++) {
+ bool nf = i == 0 ? neg_first ^ (off & 1) : !(neg_first ^ (off & 1));
+ ok = read_sfns[i](off, nf);
+ }
+#else
+ ok = read_sfns[i](0, i == 0 ? neg_first : !neg_first);
+#endif
+ }
+
+ return ok;
+}
+
+init_ufn init_ufns[] = { init_test_data_u8,
+ init_test_data_u16,
+ init_test_data_u32,
+ init_test_data_u64 };
+
+int main(void)
+{
+ int i;
+ bool ok = true;
+
+ /* Run through the unsigned tests first */
+ for (i = 0; i < ARRAY_SIZE(init_ufns) && ok; i++) {
+ ok = do_unsigned_test(init_ufns[i]);
+ }
+
+ if (ok) {
+ init_test_data_s8(false);
+ ok = do_signed_reads(false);
+ }
+
+ if (ok) {
+ init_test_data_s8(true);
+ ok = do_signed_reads(true);
+ }
+
+ ml_printf("Test complete: %s\n", ok ? "PASSED" : "FAILED");
+ return ok ? 0 : -1;
+}
diff --git a/vl.c b/vl.c
index 5550bd7693..2e69c9fef2 100644
--- a/vl.c
+++ b/vl.c
@@ -116,7 +116,7 @@ int main(int argc, char **argv)
#include "qapi/opts-visitor.h"
#include "qapi/clone-visitor.h"
#include "qom/object_interfaces.h"
-#include "exec/semihost.h"
+#include "hw/semihosting/semihost.h"
#include "crypto/init.h"
#include "sysemu/replay.h"
#include "qapi/qapi-events-run-state.h"
@@ -501,25 +501,6 @@ static QemuOptsList qemu_icount_opts = {
},
};
-static QemuOptsList qemu_semihosting_config_opts = {
- .name = "semihosting-config",
- .implied_opt_name = "enable",
- .head = QTAILQ_HEAD_INITIALIZER(qemu_semihosting_config_opts.head),
- .desc = {
- {
- .name = "enable",
- .type = QEMU_OPT_BOOL,
- }, {
- .name = "target",
- .type = QEMU_OPT_STRING,
- }, {
- .name = "arg",
- .type = QEMU_OPT_STRING,
- },
- { /* end of list */ }
- },
-};
-
static QemuOptsList qemu_fw_cfg_opts = {
.name = "fw_cfg",
.implied_opt_name = "name",
@@ -1351,80 +1332,6 @@ static void configure_msg(QemuOpts *opts)
enable_timestamp_msg = qemu_opt_get_bool(opts, "timestamp", true);
}
-/***********************************************************/
-/* Semihosting */
-
-typedef struct SemihostingConfig {
- bool enabled;
- SemihostingTarget target;
- const char **argv;
- int argc;
- const char *cmdline; /* concatenated argv */
-} SemihostingConfig;
-
-static SemihostingConfig semihosting;
-
-bool semihosting_enabled(void)
-{
- return semihosting.enabled;
-}
-
-SemihostingTarget semihosting_get_target(void)
-{
- return semihosting.target;
-}
-
-const char *semihosting_get_arg(int i)
-{
- if (i >= semihosting.argc) {
- return NULL;
- }
- return semihosting.argv[i];
-}
-
-int semihosting_get_argc(void)
-{
- return semihosting.argc;
-}
-
-const char *semihosting_get_cmdline(void)
-{
- if (semihosting.cmdline == NULL && semihosting.argc > 0) {
- semihosting.cmdline = g_strjoinv(" ", (gchar **)semihosting.argv);
- }
- return semihosting.cmdline;
-}
-
-static int add_semihosting_arg(void *opaque,
- const char *name, const char *val,
- Error **errp)
-{
- SemihostingConfig *s = opaque;
- if (strcmp(name, "arg") == 0) {
- s->argc++;
- /* one extra element as g_strjoinv() expects NULL-terminated array */
- s->argv = g_realloc(s->argv, (s->argc + 1) * sizeof(void *));
- s->argv[s->argc - 1] = val;
- s->argv[s->argc] = NULL;
- }
- return 0;
-}
-
-/* Use strings passed via -kernel/-append to initialize semihosting.argv[] */
-static inline void semihosting_arg_fallback(const char *file, const char *cmd)
-{
- char *cmd_token;
-
- /* argv[0] */
- add_semihosting_arg(&semihosting, "arg", file, NULL);
-
- /* split -append and initialize argv[1..n] */
- cmd_token = strtok(g_strdup(cmd), " ");
- while (cmd_token) {
- add_semihosting_arg(&semihosting, "arg", cmd_token, NULL);
- cmd_token = strtok(NULL, " ");
- }
-}
/* Now we still need this for compatibility with XEN. */
bool has_igd_gfx_passthru;
@@ -3743,37 +3650,10 @@ int main(int argc, char **argv, char **envp)
nb_option_roms++;
break;
case QEMU_OPTION_semihosting:
- semihosting.enabled = true;
- semihosting.target = SEMIHOSTING_TARGET_AUTO;
+ qemu_semihosting_enable();
break;
case QEMU_OPTION_semihosting_config:
- semihosting.enabled = true;
- opts = qemu_opts_parse_noisily(qemu_find_opts("semihosting-config"),
- optarg, false);
- if (opts != NULL) {
- semihosting.enabled = qemu_opt_get_bool(opts, "enable",
- true);
- const char *target = qemu_opt_get(opts, "target");
- if (target != NULL) {
- if (strcmp("native", target) == 0) {
- semihosting.target = SEMIHOSTING_TARGET_NATIVE;
- } else if (strcmp("gdb", target) == 0) {
- semihosting.target = SEMIHOSTING_TARGET_GDB;
- } else if (strcmp("auto", target) == 0) {
- semihosting.target = SEMIHOSTING_TARGET_AUTO;
- } else {
- error_report("unsupported semihosting-config %s",
- optarg);
- exit(1);
- }
- } else {
- semihosting.target = SEMIHOSTING_TARGET_AUTO;
- }
- /* Set semihosting argument count and vector */
- qemu_opt_foreach(opts, add_semihosting_arg,
- &semihosting, NULL);
- } else {
- error_report("unsupported semihosting-config %s", optarg);
+ if (qemu_semihosting_config_options(optarg) != 0) {
exit(1);
}
break;
@@ -4290,6 +4170,8 @@ int main(int argc, char **argv, char **envp)
qemu_opts_foreach(qemu_find_opts("chardev"),
chardev_init_func, NULL, &error_fatal);
+ /* now chardevs have been created we may have semihosting to connect */
+ qemu_semihosting_connect_chardevs();
#ifdef CONFIG_VIRTFS
qemu_opts_foreach(qemu_find_opts("fsdev"),