qemu/semihosting/config.c
<<
>>
Prefs
   1/*
   2 * Semihosting configuration
   3 *
   4 * Copyright (c) 2015 Imagination Technologies
   5 * Copyright (c) 2019 Linaro Ltd
   6 *
   7 * This controls the configuration of semihosting for all guest
   8 * targets that support it. Architecture specific handling is handled
   9 * in target/HW/HW-semi.c
  10 *
  11 * Semihosting is sightly strange in that it is also supported by some
  12 * linux-user targets. However in that use case no configuration of
  13 * the outputs and command lines is supported.
  14 *
  15 * The config module is common to all softmmu targets however as vl.c
  16 * needs to link against the helpers.
  17 *
  18 * SPDX-License-Identifier: GPL-2.0-or-later
  19 */
  20
  21#include "qemu/osdep.h"
  22#include "qemu/option.h"
  23#include "qemu/config-file.h"
  24#include "qemu/error-report.h"
  25#include "semihosting/semihost.h"
  26#include "chardev/char.h"
  27
  28QemuOptsList qemu_semihosting_config_opts = {
  29    .name = "semihosting-config",
  30    .merge_lists = true,
  31    .implied_opt_name = "enable",
  32    .head = QTAILQ_HEAD_INITIALIZER(qemu_semihosting_config_opts.head),
  33    .desc = {
  34        {
  35            .name = "enable",
  36            .type = QEMU_OPT_BOOL,
  37        }, {
  38            .name = "userspace",
  39            .type = QEMU_OPT_BOOL,
  40        }, {
  41            .name = "target",
  42            .type = QEMU_OPT_STRING,
  43        }, {
  44            .name = "chardev",
  45            .type = QEMU_OPT_STRING,
  46        }, {
  47            .name = "arg",
  48            .type = QEMU_OPT_STRING,
  49        },
  50        { /* end of list */ }
  51    },
  52};
  53
  54typedef struct SemihostingConfig {
  55    bool enabled;
  56    bool userspace_enabled;
  57    SemihostingTarget target;
  58    char **argv;
  59    int argc;
  60    const char *cmdline; /* concatenated argv */
  61} SemihostingConfig;
  62
  63static SemihostingConfig semihosting;
  64static const char *semihost_chardev;
  65
  66bool semihosting_enabled(bool is_user)
  67{
  68    return semihosting.enabled && (!is_user || semihosting.userspace_enabled);
  69}
  70
  71SemihostingTarget semihosting_get_target(void)
  72{
  73    return semihosting.target;
  74}
  75
  76const char *semihosting_get_arg(int i)
  77{
  78    if (i >= semihosting.argc) {
  79        return NULL;
  80    }
  81    return semihosting.argv[i];
  82}
  83
  84int semihosting_get_argc(void)
  85{
  86    return semihosting.argc;
  87}
  88
  89const char *semihosting_get_cmdline(void)
  90{
  91    if (semihosting.cmdline == NULL && semihosting.argc > 0) {
  92        semihosting.cmdline = g_strjoinv(" ", (gchar **)semihosting.argv);
  93    }
  94    return semihosting.cmdline;
  95}
  96
  97static int add_semihosting_arg(void *opaque,
  98                               const char *name, const char *val,
  99                               Error **errp)
 100{
 101    SemihostingConfig *s = opaque;
 102    if (strcmp(name, "arg") == 0) {
 103        s->argc++;
 104        /* one extra element as g_strjoinv() expects NULL-terminated array */
 105        s->argv = g_renew(char *, s->argv, s->argc + 1);
 106        s->argv[s->argc - 1] = g_strdup(val);
 107        s->argv[s->argc] = NULL;
 108    }
 109    return 0;
 110}
 111
 112/* Use strings passed via -kernel/-append to initialize semihosting.argv[] */
 113void semihosting_arg_fallback(const char *file, const char *cmd)
 114{
 115    char *cmd_token;
 116
 117    /* argv[0] */
 118    add_semihosting_arg(&semihosting, "arg", file, NULL);
 119
 120    /* split -append and initialize argv[1..n] */
 121    cmd_token = strtok(g_strdup(cmd), " ");
 122    while (cmd_token) {
 123        add_semihosting_arg(&semihosting, "arg", cmd_token, NULL);
 124        cmd_token = strtok(NULL, " ");
 125    }
 126}
 127
 128void qemu_semihosting_enable(void)
 129{
 130    semihosting.enabled = true;
 131    semihosting.target = SEMIHOSTING_TARGET_AUTO;
 132}
 133
 134int qemu_semihosting_config_options(const char *optarg)
 135{
 136    QemuOptsList *opt_list = qemu_find_opts("semihosting-config");
 137    QemuOpts *opts = qemu_opts_parse_noisily(opt_list, optarg, false);
 138
 139    semihosting.enabled = true;
 140
 141    if (opts != NULL) {
 142        semihosting.enabled = qemu_opt_get_bool(opts, "enable",
 143                                                true);
 144        semihosting.userspace_enabled = qemu_opt_get_bool(opts, "userspace",
 145                                                          false);
 146        const char *target = qemu_opt_get(opts, "target");
 147        /* setup of chardev is deferred until they are initialised */
 148        semihost_chardev = qemu_opt_get(opts, "chardev");
 149        if (target != NULL) {
 150            if (strcmp("native", target) == 0) {
 151                semihosting.target = SEMIHOSTING_TARGET_NATIVE;
 152            } else if (strcmp("gdb", target) == 0) {
 153                semihosting.target = SEMIHOSTING_TARGET_GDB;
 154            } else  if (strcmp("auto", target) == 0) {
 155                semihosting.target = SEMIHOSTING_TARGET_AUTO;
 156            } else {
 157                error_report("unsupported semihosting-config %s",
 158                             optarg);
 159                return 1;
 160            }
 161        } else {
 162            semihosting.target = SEMIHOSTING_TARGET_AUTO;
 163        }
 164        /* Set semihosting argument count and vector */
 165        qemu_opt_foreach(opts, add_semihosting_arg,
 166                         &semihosting, NULL);
 167    } else {
 168        error_report("unsupported semihosting-config %s", optarg);
 169        return 1;
 170    }
 171
 172    return 0;
 173}
 174
 175/* We had to defer this until chardevs were created */
 176void qemu_semihosting_chardev_init(void)
 177{
 178    Chardev *chr = NULL;
 179
 180    if (semihost_chardev) {
 181        chr = qemu_chr_find(semihost_chardev);
 182        if (chr == NULL) {
 183            error_report("semihosting chardev '%s' not found",
 184                         semihost_chardev);
 185            exit(1);
 186        }
 187    }
 188
 189    qemu_semihosting_console_init(chr);
 190}
 191