qemu/hw/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 "hw/semihosting/semihost.h"
  26#include "chardev/char.h"
  27
  28QemuOptsList qemu_semihosting_config_opts = {
  29    .name = "semihosting-config",
  30    .implied_opt_name = "enable",
  31    .head = QTAILQ_HEAD_INITIALIZER(qemu_semihosting_config_opts.head),
  32    .desc = {
  33        {
  34            .name = "enable",
  35            .type = QEMU_OPT_BOOL,
  36        }, {
  37            .name = "target",
  38            .type = QEMU_OPT_STRING,
  39        }, {
  40            .name = "chardev",
  41            .type = QEMU_OPT_STRING,
  42        }, {
  43            .name = "arg",
  44            .type = QEMU_OPT_STRING,
  45        },
  46        { /* end of list */ }
  47    },
  48};
  49
  50typedef struct SemihostingConfig {
  51    bool enabled;
  52    SemihostingTarget target;
  53    Chardev *chardev;
  54    const char **argv;
  55    int argc;
  56    const char *cmdline; /* concatenated argv */
  57} SemihostingConfig;
  58
  59static SemihostingConfig semihosting;
  60static const char *semihost_chardev;
  61
  62bool semihosting_enabled(void)
  63{
  64    return semihosting.enabled;
  65}
  66
  67SemihostingTarget semihosting_get_target(void)
  68{
  69    return semihosting.target;
  70}
  71
  72const char *semihosting_get_arg(int i)
  73{
  74    if (i >= semihosting.argc) {
  75        return NULL;
  76    }
  77    return semihosting.argv[i];
  78}
  79
  80int semihosting_get_argc(void)
  81{
  82    return semihosting.argc;
  83}
  84
  85const char *semihosting_get_cmdline(void)
  86{
  87    if (semihosting.cmdline == NULL && semihosting.argc > 0) {
  88        semihosting.cmdline = g_strjoinv(" ", (gchar **)semihosting.argv);
  89    }
  90    return semihosting.cmdline;
  91}
  92
  93static int add_semihosting_arg(void *opaque,
  94                               const char *name, const char *val,
  95                               Error **errp)
  96{
  97    SemihostingConfig *s = opaque;
  98    if (strcmp(name, "arg") == 0) {
  99        s->argc++;
 100        /* one extra element as g_strjoinv() expects NULL-terminated array */
 101        s->argv = g_realloc(s->argv, (s->argc + 1) * sizeof(void *));
 102        s->argv[s->argc - 1] = val;
 103        s->argv[s->argc] = NULL;
 104    }
 105    return 0;
 106}
 107
 108/* Use strings passed via -kernel/-append to initialize semihosting.argv[] */
 109void semihosting_arg_fallback(const char *file, const char *cmd)
 110{
 111    char *cmd_token;
 112
 113    /* argv[0] */
 114    add_semihosting_arg(&semihosting, "arg", file, NULL);
 115
 116    /* split -append and initialize argv[1..n] */
 117    cmd_token = strtok(g_strdup(cmd), " ");
 118    while (cmd_token) {
 119        add_semihosting_arg(&semihosting, "arg", cmd_token, NULL);
 120        cmd_token = strtok(NULL, " ");
 121    }
 122}
 123
 124Chardev *semihosting_get_chardev(void)
 125{
 126    return semihosting.chardev;
 127}
 128
 129void qemu_semihosting_enable(void)
 130{
 131    semihosting.enabled = true;
 132    semihosting.target = SEMIHOSTING_TARGET_AUTO;
 133}
 134
 135int qemu_semihosting_config_options(const char *optarg)
 136{
 137    QemuOptsList *opt_list = qemu_find_opts("semihosting-config");
 138    QemuOpts *opts = qemu_opts_parse_noisily(opt_list, optarg, false);
 139
 140    semihosting.enabled = true;
 141
 142    if (opts != NULL) {
 143        semihosting.enabled = qemu_opt_get_bool(opts, "enable",
 144                                                true);
 145        const char *target = qemu_opt_get(opts, "target");
 146        /* setup of chardev is deferred until they are initialised */
 147        semihost_chardev = qemu_opt_get(opts, "chardev");
 148        if (target != NULL) {
 149            if (strcmp("native", target) == 0) {
 150                semihosting.target = SEMIHOSTING_TARGET_NATIVE;
 151            } else if (strcmp("gdb", target) == 0) {
 152                semihosting.target = SEMIHOSTING_TARGET_GDB;
 153            } else  if (strcmp("auto", target) == 0) {
 154                semihosting.target = SEMIHOSTING_TARGET_AUTO;
 155            } else {
 156                error_report("unsupported semihosting-config %s",
 157                             optarg);
 158                return 1;
 159            }
 160        } else {
 161            semihosting.target = SEMIHOSTING_TARGET_AUTO;
 162        }
 163        /* Set semihosting argument count and vector */
 164        qemu_opt_foreach(opts, add_semihosting_arg,
 165                         &semihosting, NULL);
 166    } else {
 167        error_report("unsupported semihosting-config %s", optarg);
 168        return 1;
 169    }
 170
 171    return 0;
 172}
 173
 174void qemu_semihosting_connect_chardevs(void)
 175{
 176    /* We had to defer this until chardevs were created */
 177    if (semihost_chardev) {
 178        Chardev *chr = qemu_chr_find(semihost_chardev);
 179        if (chr == NULL) {
 180            error_report("semihosting chardev '%s' not found",
 181                         semihost_chardev);
 182            exit(1);
 183        }
 184        semihosting.chardev = chr;
 185    }
 186}
 187