semihosting: add --semihosting-config arg sub-argument

Add new "arg" sub-argument to the --semihosting-config allowing the user
to pass multiple input arguments separately. It is required for example
by UHI semihosting to construct argc and argv.

Also, update ARM semihosting to support new option (at the moment it is
the only target which cares about arguments).

If the semihosting is enabled and no semihosting args have been specified,
then fall back to -kernel/-append. The -append string is split on whitespace
before initializing semihosting.argv[1..n]; this is different from what
QEMU MIPS machines' pseudo-bootloaders do (i.e. argv[1] contains the whole
-append), but is more intuitive from UHI user's point of view and Linux
kernel just does not care as it concatenates argv[1..n] into single cmdline
string anyway.

Signed-off-by: Leon Alrae <leon.alrae@imgtec.com>
Message-id: 1434643256-16858-3-git-send-email-leon.alrae@imgtec.com
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Leon Alrae 2015-06-19 14:17:45 +01:00 committed by Peter Maydell
parent cfe67cef48
commit a59d31a1eb
4 changed files with 103 additions and 12 deletions

View File

@ -36,9 +36,27 @@ static inline SemihostingTarget semihosting_get_target(void)
{ {
return SEMIHOSTING_TARGET_AUTO; return SEMIHOSTING_TARGET_AUTO;
} }
static inline const char *semihosting_get_arg(int i)
{
return NULL;
}
static inline int semihosting_get_argc(void)
{
return 0;
}
static inline const char *semihosting_get_cmdline(void)
{
return NULL;
}
#else #else
bool semihosting_enabled(void); bool semihosting_enabled(void);
SemihostingTarget semihosting_get_target(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 #endif
#endif #endif

View File

@ -3351,14 +3351,25 @@ STEXI
Enable semihosting mode (ARM, M68K, Xtensa only). Enable semihosting mode (ARM, M68K, Xtensa 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 semihosting configuration\n", "-semihosting-config [enable=on|off][,target=native|gdb|auto][,arg=str[,...]]\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)
STEXI STEXI
@item -semihosting-config [enable=on|off,]target=native|gdb|auto @item -semihosting-config [enable=on|off][,target=native|gdb|auto][,arg=str[,...]]
@findex -semihosting-config @findex -semihosting-config
Enable semihosting and define where the semihosting calls will be addressed, Enable and configure semihosting (ARM, M68K, Xtensa only).
to QEMU (@code{native}) or to GDB (@code{gdb}). The default is @code{auto}, which means @table @option
@code{gdb} during debug sessions and @code{native} otherwise (ARM, M68K, Xtensa only). @item target=@code{native|gdb|auto}
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 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
command line is still supported for backward compatibility. If both the
@code{--semihosting-config arg} and the @code{-kernel}/@code{-append} are
specified, the former is passed to semihosting as it always takes precedence.
@end table
ETEXI ETEXI
DEF("old-param", 0, QEMU_OPTION_old_param, DEF("old-param", 0, QEMU_OPTION_old_param,
"-old-param old param mode\n", QEMU_ARCH_ARM) "-old-param old param mode\n", QEMU_ARCH_ARM)

View File

@ -27,6 +27,7 @@
#include <time.h> #include <time.h>
#include "cpu.h" #include "cpu.h"
#include "exec/semihost.h"
#ifdef CONFIG_USER_ONLY #ifdef CONFIG_USER_ONLY
#include "qemu.h" #include "qemu.h"
@ -440,10 +441,7 @@ uint32_t do_arm_semihosting(CPUARMState *env)
input_size = arg1; input_size = arg1;
/* Compute the size of the output string. */ /* Compute the size of the output string. */
#if !defined(CONFIG_USER_ONLY) #if !defined(CONFIG_USER_ONLY)
output_size = strlen(ts->boot_info->kernel_filename) output_size = strlen(semihosting_get_cmdline()) + 1;
+ 1 /* Separating space. */
+ strlen(ts->boot_info->kernel_cmdline)
+ 1; /* Terminating null byte. */
#else #else
unsigned int i; unsigned int i;
@ -474,9 +472,7 @@ uint32_t do_arm_semihosting(CPUARMState *env)
/* Copy the command-line arguments. */ /* Copy the command-line arguments. */
#if !defined(CONFIG_USER_ONLY) #if !defined(CONFIG_USER_ONLY)
pstrcpy(output_buffer, output_size, ts->boot_info->kernel_filename); pstrcpy(output_buffer, output_size, semihosting_get_cmdline());
pstrcat(output_buffer, output_size, " ");
pstrcat(output_buffer, output_size, ts->boot_info->kernel_cmdline);
#else #else
if (output_size == 1) { if (output_size == 1) {
/* Empty command-line. */ /* Empty command-line. */

66
vl.c
View File

@ -488,6 +488,9 @@ static QemuOptsList qemu_semihosting_config_opts = {
}, { }, {
.name = "target", .name = "target",
.type = QEMU_OPT_STRING, .type = QEMU_OPT_STRING,
}, {
.name = "arg",
.type = QEMU_OPT_STRING,
}, },
{ /* end of list */ } { /* end of list */ }
}, },
@ -1251,6 +1254,9 @@ static void configure_msg(QemuOpts *opts)
typedef struct SemihostingConfig { typedef struct SemihostingConfig {
bool enabled; bool enabled;
SemihostingTarget target; SemihostingTarget target;
const char **argv;
int argc;
const char *cmdline; /* concatenated argv */
} SemihostingConfig; } SemihostingConfig;
static SemihostingConfig semihosting; static SemihostingConfig semihosting;
@ -1265,6 +1271,58 @@ SemihostingTarget semihosting_get_target(void)
return semihosting.target; 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, " ");
}
}
/***********************************************************/ /***********************************************************/
/* USB devices */ /* USB devices */
@ -3669,6 +3727,9 @@ int main(int argc, char **argv, char **envp)
} else { } else {
semihosting.target = SEMIHOSTING_TARGET_AUTO; semihosting.target = SEMIHOSTING_TARGET_AUTO;
} }
/* Set semihosting argument count and vector */
qemu_opt_foreach(opts, add_semihosting_arg,
&semihosting, NULL);
} else { } else {
fprintf(stderr, "Unsupported semihosting-config %s\n", fprintf(stderr, "Unsupported semihosting-config %s\n",
optarg); optarg);
@ -4237,6 +4298,11 @@ int main(int argc, char **argv, char **envp)
exit(1); exit(1);
} }
if (semihosting_enabled() && !semihosting_get_argc() && kernel_filename) {
/* fall back to the -kernel/-append */
semihosting_arg_fallback(kernel_filename, kernel_cmdline);
}
os_set_line_buffering(); os_set_line_buffering();
#ifdef CONFIG_SPICE #ifdef CONFIG_SPICE