1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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 { }
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;
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
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
113void semihosting_arg_fallback(const char *file, const char *cmd)
114{
115 char *cmd_token;
116
117
118 add_semihosting_arg(&semihosting, "arg", file, NULL);
119
120
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
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
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
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