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#include "sysemu/sysemu.h"
28
29QemuOptsList qemu_semihosting_config_opts = {
30 .name = "semihosting-config",
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 = "target",
39 .type = QEMU_OPT_STRING,
40 }, {
41 .name = "chardev",
42 .type = QEMU_OPT_STRING,
43 }, {
44 .name = "arg",
45 .type = QEMU_OPT_STRING,
46 },
47 { /* end of list */ }
48 },
49};
50
51typedef struct SemihostingConfig {
52 bool enabled;
53 SemihostingTarget target;
54 Chardev *chardev;
55 const char **argv;
56 int argc;
57 const char *cmdline; /* concatenated argv */
58} SemihostingConfig;
59
60static SemihostingConfig semihosting;
61static const char *semihost_chardev;
62
63bool semihosting_enabled(void)
64{
65 return semihosting.enabled;
66}
67
68SemihostingTarget semihosting_get_target(void)
69{
70 return semihosting.target;
71}
72
73const char *semihosting_get_arg(int i)
74{
75 if (i >= semihosting.argc) {
76 return NULL;
77 }
78 return semihosting.argv[i];
79}
80
81int semihosting_get_argc(void)
82{
83 return semihosting.argc;
84}
85
86const char *semihosting_get_cmdline(void)
87{
88 if (semihosting.cmdline == NULL && semihosting.argc > 0) {
89 semihosting.cmdline = g_strjoinv(" ", (gchar **)semihosting.argv);
90 }
91 return semihosting.cmdline;
92}
93
94static int add_semihosting_arg(void *opaque,
95 const char *name, const char *val,
96 Error **errp)
97{
98 SemihostingConfig *s = opaque;
99 if (strcmp(name, "arg") == 0) {
100 s->argc++;
101 /* one extra element as g_strjoinv() expects NULL-terminated array */
102 s->argv = g_realloc(s->argv, (s->argc + 1) * sizeof(void *));
103 s->argv[s->argc - 1] = val;
104 s->argv[s->argc] = NULL;
105 }
106 return 0;
107}
108
109/* Use strings passed via -kernel/-append to initialize semihosting.argv[] */
110void semihosting_arg_fallback(const char *file, const char *cmd)
111{
112 char *cmd_token;
113
114 /* argv[0] */
115 add_semihosting_arg(&semihosting, "arg", file, NULL);
116
117 /* split -append and initialize argv[1..n] */
118 cmd_token = strtok(g_strdup(cmd), " ");
119 while (cmd_token) {
120 add_semihosting_arg(&semihosting, "arg", cmd_token, NULL);
121 cmd_token = strtok(NULL, " ");
122 }
123}
124
125Chardev *semihosting_get_chardev(void)
126{
127 return semihosting.chardev;
128}
129
130void qemu_semihosting_enable(void)
131{
132 semihosting.enabled = true;
133 semihosting.target = SEMIHOSTING_TARGET_AUTO;
134}
135
136int qemu_semihosting_config_options(const char *optarg)
137{
138 QemuOptsList *opt_list = qemu_find_opts("semihosting-config");
139 QemuOpts *opts = qemu_opts_parse_noisily(opt_list, optarg, false);
140
141 semihosting.enabled = true;
142
143 if (opts != NULL) {
144 semihosting.enabled = qemu_opt_get_bool(opts, "enable",
145 true);
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
175void qemu_semihosting_connect_chardevs(void)
176{
177 /* We had to defer this until chardevs were created */
178 if (semihost_chardev) {
179 Chardev *chr = qemu_chr_find(semihost_chardev);
180 if (chr == NULL) {
181 error_report("semihosting chardev '%s' not found",
182 semihost_chardev);
183 exit(1);
184 }
185 semihosting.chardev = chr;
186 }
187}
188