text: /*
   * 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"
  #include "sysemu/sysemu.h"
  
  QemuOptsList qemu_semihosting_config_opts = {
      .name = "semihosting-config",
      .implied_opt_name = "enable",
      .head = QTAILQ_HEAD_INITIALIZER(qemu_semihosting_config_opts.head),
      .desc = {
          {
              .name = "enable",
              .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;
      SemihostingTarget target;
      Chardev *chardev;
      const char **argv;
      int argc;
      const char *cmdline; /* concatenated argv */
  } SemihostingConfig;
  
  static SemihostingConfig semihosting;
  static const char *semihost_chardev;
  
  bool semihosting_enabled(void)
  {
      return semihosting.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_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[] */
  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, " ");
      }
  }
  
  Chardev *semihosting_get_chardev(void)
  {
      return semihosting.chardev;
  }
  
  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);
          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;
  }
  
  void qemu_semihosting_connect_chardevs(void)
  {
      /* We had to defer this until chardevs were created */
      if (semihost_chardev) {
          Chardev *chr = qemu_chr_find(semihost_chardev);
          if (chr == NULL) {
              error_report("semihosting chardev '%s' not found",
                           semihost_chardev);
              exit(1);
          }
          semihosting.chardev = chr;
      }
  }
  