semihosting: enable chardev backed output for console

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 <alex.bennee@linaro.org>
This commit is contained in:
Alex Bennée 2019-05-14 15:30:14 +01:00
parent a331c6d774
commit 4e7f9032cf
6 changed files with 51 additions and 3 deletions

View File

@ -23,6 +23,7 @@
#include "qemu/config-file.h" #include "qemu/config-file.h"
#include "qemu/error-report.h" #include "qemu/error-report.h"
#include "hw/semihosting/semihost.h" #include "hw/semihosting/semihost.h"
#include "chardev/char.h"
QemuOptsList qemu_semihosting_config_opts = { QemuOptsList qemu_semihosting_config_opts = {
.name = "semihosting-config", .name = "semihosting-config",
@ -35,6 +36,9 @@ QemuOptsList qemu_semihosting_config_opts = {
}, { }, {
.name = "target", .name = "target",
.type = QEMU_OPT_STRING, .type = QEMU_OPT_STRING,
}, {
.name = "chardev",
.type = QEMU_OPT_STRING,
}, { }, {
.name = "arg", .name = "arg",
.type = QEMU_OPT_STRING, .type = QEMU_OPT_STRING,
@ -46,12 +50,14 @@ QemuOptsList qemu_semihosting_config_opts = {
typedef struct SemihostingConfig { typedef struct SemihostingConfig {
bool enabled; bool enabled;
SemihostingTarget target; SemihostingTarget target;
Chardev *chardev;
const char **argv; const char **argv;
int argc; int argc;
const char *cmdline; /* concatenated argv */ const char *cmdline; /* concatenated argv */
} SemihostingConfig; } SemihostingConfig;
static SemihostingConfig semihosting; static SemihostingConfig semihosting;
static const char *semihost_chardev;
bool semihosting_enabled(void) 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) void qemu_semihosting_enable(void)
{ {
semihosting.enabled = true; semihosting.enabled = true;
@ -132,6 +143,8 @@ int qemu_semihosting_config_options(const char *optarg)
semihosting.enabled = qemu_opt_get_bool(opts, "enable", semihosting.enabled = qemu_opt_get_bool(opts, "enable",
true); true);
const char *target = qemu_opt_get(opts, "target"); 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 (target != NULL) {
if (strcmp("native", target) == 0) { if (strcmp("native", target) == 0) {
semihosting.target = SEMIHOSTING_TARGET_NATIVE; semihosting.target = SEMIHOSTING_TARGET_NATIVE;
@ -158,3 +171,16 @@ int qemu_semihosting_config_options(const char *optarg)
return 0; 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;
}
}

View File

@ -17,13 +17,20 @@
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "cpu.h" #include "cpu.h"
#include "hw/semihosting/semihost.h"
#include "hw/semihosting/console.h" #include "hw/semihosting/console.h"
#include "exec/gdbstub.h" #include "exec/gdbstub.h"
#include "qemu/log.h" #include "qemu/log.h"
#include "chardev/char.h"
int qemu_semihosting_log_out(const char *s, int len) 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); return write(STDERR_FILENO, s, len);
}
} }
/* /*

View File

@ -51,6 +51,11 @@ static inline const char *semihosting_get_cmdline(void)
{ {
return NULL; return NULL;
} }
static inline Chardev *semihosting_get_chardev(void)
{
return NULL;
}
#else /* !CONFIG_USER_ONLY */ #else /* !CONFIG_USER_ONLY */
bool semihosting_enabled(void); bool semihosting_enabled(void);
SemihostingTarget semihosting_get_target(void); SemihostingTarget semihosting_get_target(void);
@ -58,9 +63,11 @@ const char *semihosting_get_arg(int i);
int semihosting_get_argc(void); int semihosting_get_argc(void);
const char *semihosting_get_cmdline(void); const char *semihosting_get_cmdline(void);
void semihosting_arg_fallback(const char *file, const char *cmd); void semihosting_arg_fallback(const char *file, const char *cmd);
Chardev *semihosting_get_chardev(void);
/* for vl.c hooks */ /* for vl.c hooks */
void qemu_semihosting_enable(void); void qemu_semihosting_enable(void);
int qemu_semihosting_config_options(const char *opt); int qemu_semihosting_config_options(const char *opt);
void qemu_semihosting_connect_chardevs(void);
#endif /* CONFIG_USER_ONLY */ #endif /* CONFIG_USER_ONLY */
#endif /* SEMIHOST_H */ #endif /* SEMIHOST_H */

View File

@ -4025,12 +4025,12 @@ STEXI
Enable semihosting mode (ARM, M68K, Xtensa, MIPS, Nios II only). Enable semihosting mode (ARM, M68K, Xtensa, MIPS, Nios II only).
ETEXI ETEXI
DEF("semihosting-config", HAS_ARG, QEMU_OPTION_semihosting_config, 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", " semihosting configuration\n",
QEMU_ARCH_ARM | QEMU_ARCH_M68K | QEMU_ARCH_XTENSA | QEMU_ARCH_LM32 | QEMU_ARCH_ARM | QEMU_ARCH_M68K | QEMU_ARCH_XTENSA | QEMU_ARCH_LM32 |
QEMU_ARCH_MIPS | QEMU_ARCH_NIOS2) QEMU_ARCH_MIPS | QEMU_ARCH_NIOS2)
STEXI 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 @findex -semihosting-config
Enable and configure semihosting (ARM, M68K, Xtensa, MIPS, Nios II only). Enable and configure semihosting (ARM, M68K, Xtensa, MIPS, Nios II only).
@table @option @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}) 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} or to GDB (@code{gdb}). The default is @code{auto}, which means @code{gdb}
during debug sessions and @code{native} otherwise. 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},... @item arg=@var{str1},arg=@var{str2},...
Allows the user to pass input arguments, and can be used multiple times to build 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 up a list. The old-style @code{-kernel}/@code{-append} method of passing a

View File

@ -64,3 +64,7 @@ const char *semihosting_get_cmdline(void)
void semihosting_arg_fallback(const char *file, const char *cmd) void semihosting_arg_fallback(const char *file, const char *cmd)
{ {
} }
void qemu_semihosting_connect_chardevs(void)
{
}

2
vl.c
View File

@ -4170,6 +4170,8 @@ int main(int argc, char **argv, char **envp)
qemu_opts_foreach(qemu_find_opts("chardev"), qemu_opts_foreach(qemu_find_opts("chardev"),
chardev_init_func, NULL, &error_fatal); chardev_init_func, NULL, &error_fatal);
/* now chardevs have been created we may have semihosting to connect */
qemu_semihosting_connect_chardevs();
#ifdef CONFIG_VIRTFS #ifdef CONFIG_VIRTFS
qemu_opts_foreach(qemu_find_opts("fsdev"), qemu_opts_foreach(qemu_find_opts("fsdev"),