From f1672e6f2b6642f9bf5042df6713ce8e4a0f00c6 Mon Sep 17 00:00:00 2001 From: Alex Bennée Date: Mon, 13 May 2019 14:43:57 +0100 Subject: semihosting: move semihosting configuration into its own directory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In preparation for having some more common semihosting code let's excise the current config magic from vl.c into its own file. We shall later add more conditionals to the build configurations so we can avoid building this if we don't need it. Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson --- hw/semihosting/config.c | 160 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 160 insertions(+) create mode 100644 hw/semihosting/config.c (limited to 'hw/semihosting/config.c') diff --git a/hw/semihosting/config.c b/hw/semihosting/config.c new file mode 100644 index 0000000000..f1d3fe1e4c --- /dev/null +++ b/hw/semihosting/config.c @@ -0,0 +1,160 @@ +/* + * 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" + +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 */ } + }, +}; + +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[] */ +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, " "); + } +} + +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"); + 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; +} + -- cgit 1.4.1 From 4e7f9032cf9bba8558b0fd5ab6a1366d6d7b8ee0 Mon Sep 17 00:00:00 2001 From: Alex Bennée Date: Tue, 14 May 2019 15:30:14 +0100 Subject: semihosting: enable chardev backed output for console MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It will be useful for a number of use-cases to be able to re-direct output to a file like we do with serial output. This does the wiring to allow us to treat then semihosting console like just another character output device. Signed-off-by: Alex Bennée --- hw/semihosting/config.c | 26 ++++++++++++++++++++++++++ hw/semihosting/console.c | 9 ++++++++- include/hw/semihosting/semihost.h | 7 +++++++ qemu-options.hx | 6 ++++-- stubs/semihost.c | 4 ++++ vl.c | 2 ++ 6 files changed, 51 insertions(+), 3 deletions(-) (limited to 'hw/semihosting/config.c') diff --git a/hw/semihosting/config.c b/hw/semihosting/config.c index f1d3fe1e4c..2a8e7e1045 100644 --- a/hw/semihosting/config.c +++ b/hw/semihosting/config.c @@ -23,6 +23,7 @@ #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", @@ -35,6 +36,9 @@ QemuOptsList qemu_semihosting_config_opts = { }, { .name = "target", .type = QEMU_OPT_STRING, + }, { + .name = "chardev", + .type = QEMU_OPT_STRING, }, { .name = "arg", .type = QEMU_OPT_STRING, @@ -46,12 +50,14 @@ QemuOptsList qemu_semihosting_config_opts = { 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) { @@ -115,6 +121,11 @@ void semihosting_arg_fallback(const char *file, const char *cmd) } } +Chardev *semihosting_get_chardev(void) +{ + return semihosting.chardev; +} + void qemu_semihosting_enable(void) { semihosting.enabled = true; @@ -132,6 +143,8 @@ int qemu_semihosting_config_options(const char *optarg) 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; @@ -158,3 +171,16 @@ int qemu_semihosting_config_options(const char *optarg) 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 index 01826bd687..466ea6dade 100644 --- a/hw/semihosting/console.c +++ b/hw/semihosting/console.c @@ -17,13 +17,20 @@ #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) { - return write(STDERR_FILENO, s, 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); + } } /* diff --git a/include/hw/semihosting/semihost.h b/include/hw/semihosting/semihost.h index 07ea40a322..60fc42d851 100644 --- a/include/hw/semihosting/semihost.h +++ b/include/hw/semihosting/semihost.h @@ -51,6 +51,11 @@ static inline const char *semihosting_get_cmdline(void) { return NULL; } + +static inline Chardev *semihosting_get_chardev(void) +{ + return NULL; +} #else /* !CONFIG_USER_ONLY */ bool semihosting_enabled(void); SemihostingTarget semihosting_get_target(void); @@ -58,9 +63,11 @@ const char *semihosting_get_arg(int i); int semihosting_get_argc(void); const char *semihosting_get_cmdline(void); 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 /* SEMIHOST_H */ 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/semihost.c b/stubs/semihost.c index 1a4e88e532..4d5b3c0653 100644 --- a/stubs/semihost.c +++ b/stubs/semihost.c @@ -64,3 +64,7 @@ const char *semihosting_get_cmdline(void) void semihosting_arg_fallback(const char *file, const char *cmd) { } + +void qemu_semihosting_connect_chardevs(void) +{ +} diff --git a/vl.c b/vl.c index be8963b270..2e69c9fef2 100644 --- a/vl.c +++ b/vl.c @@ -4170,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"), -- cgit 1.4.1