mirror of
https://github.com/qemu/qemu.git
synced 2024-11-25 03:43:37 +08:00
5202861b20
Currently our semihosting implementations generally prohibit use of semihosting calls in system emulation from the guest userspace. This is a very long standing behaviour justified originally "to provide some semblance of security" (since code with access to the semihosting ABI can do things like read and write arbitrary files on the host system). However, it is sometimes useful to be able to run trusted guest code which performs semihosting calls from guest userspace, notably for test code. Add a command line suboption to the existing semihosting-config option group so that you can explicitly opt in to semihosting from guest userspace with -semihosting-config userspace=on (There is no equivalent option for the user-mode emulator, because there by definition all code runs in userspace and has access to semihosting already.) This commit adds the infrastructure for the command line option and adds a bool 'is_user' parameter to the function semihosting_userspace_enabled() that target code can use to check whether it should be permitting the semihosting call for userspace. It mechanically makes all the callsites pass 'false', so they continue checking "is semihosting enabled in general". Subsequent commits will make each target that implements semihosting honour the userspace=on option by passing the correct value and removing whatever "don't do this for userspace" checking they were doing by hand. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Acked-by: Alex Bennée <alex.bennee@linaro.org> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-Id: <20220822141230.3658237-2-peter.maydell@linaro.org> Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
191 lines
5.4 KiB
C
191 lines
5.4 KiB
C
/*
|
|
* 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 "semihosting/semihost.h"
|
|
#include "chardev/char.h"
|
|
|
|
QemuOptsList qemu_semihosting_config_opts = {
|
|
.name = "semihosting-config",
|
|
.merge_lists = true,
|
|
.implied_opt_name = "enable",
|
|
.head = QTAILQ_HEAD_INITIALIZER(qemu_semihosting_config_opts.head),
|
|
.desc = {
|
|
{
|
|
.name = "enable",
|
|
.type = QEMU_OPT_BOOL,
|
|
}, {
|
|
.name = "userspace",
|
|
.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;
|
|
bool userspace_enabled;
|
|
SemihostingTarget target;
|
|
char **argv;
|
|
int argc;
|
|
const char *cmdline; /* concatenated argv */
|
|
} SemihostingConfig;
|
|
|
|
static SemihostingConfig semihosting;
|
|
static const char *semihost_chardev;
|
|
|
|
bool semihosting_enabled(bool is_user)
|
|
{
|
|
return semihosting.enabled && (!is_user || semihosting.userspace_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_renew(char *, s->argv, s->argc + 1);
|
|
s->argv[s->argc - 1] = g_strdup(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);
|
|
semihosting.userspace_enabled = qemu_opt_get_bool(opts, "userspace",
|
|
false);
|
|
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;
|
|
}
|
|
|
|
/* We had to defer this until chardevs were created */
|
|
void qemu_semihosting_chardev_init(void)
|
|
{
|
|
Chardev *chr = NULL;
|
|
|
|
if (semihost_chardev) {
|
|
chr = qemu_chr_find(semihost_chardev);
|
|
if (chr == NULL) {
|
|
error_report("semihosting chardev '%s' not found",
|
|
semihost_chardev);
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
qemu_semihosting_console_init(chr);
|
|
}
|