123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191 |
- /*
- * 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 slightly 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 system 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;
- g_autofree char *cmd_dup = g_strdup(cmd);
- /* argv[0] */
- add_semihosting_arg(&semihosting, "arg", file, NULL);
- /* split -append and initialize argv[1..n] */
- cmd_token = strtok(cmd_dup, " ");
- 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 *optstr)
- {
- QemuOptsList *opt_list = qemu_find_opts("semihosting-config");
- QemuOpts *opts = qemu_opts_parse_noisily(opt_list, optstr, 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",
- optstr);
- 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", optstr);
- 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);
- }
|