]>
Commit | Line | Data |
---|---|---|
f1672e6f AB |
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" | |
6b5fe137 | 25 | #include "semihosting/semihost.h" |
4e7f9032 | 26 | #include "chardev/char.h" |
f1672e6f AB |
27 | |
28 | QemuOptsList qemu_semihosting_config_opts = { | |
29 | .name = "semihosting-config", | |
90c072e0 | 30 | .merge_lists = true, |
f1672e6f AB |
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, | |
5202861b PM |
37 | }, { |
38 | .name = "userspace", | |
39 | .type = QEMU_OPT_BOOL, | |
f1672e6f AB |
40 | }, { |
41 | .name = "target", | |
42 | .type = QEMU_OPT_STRING, | |
4e7f9032 AB |
43 | }, { |
44 | .name = "chardev", | |
45 | .type = QEMU_OPT_STRING, | |
f1672e6f AB |
46 | }, { |
47 | .name = "arg", | |
48 | .type = QEMU_OPT_STRING, | |
49 | }, | |
50 | { /* end of list */ } | |
51 | }, | |
52 | }; | |
53 | ||
54 | typedef struct SemihostingConfig { | |
55 | bool enabled; | |
5202861b | 56 | bool userspace_enabled; |
f1672e6f | 57 | SemihostingTarget target; |
78beee80 | 58 | char **argv; |
f1672e6f AB |
59 | int argc; |
60 | const char *cmdline; /* concatenated argv */ | |
61 | } SemihostingConfig; | |
62 | ||
63 | static SemihostingConfig semihosting; | |
4e7f9032 | 64 | static const char *semihost_chardev; |
f1672e6f | 65 | |
5202861b | 66 | bool semihosting_enabled(bool is_user) |
f1672e6f | 67 | { |
5202861b | 68 | return semihosting.enabled && (!is_user || semihosting.userspace_enabled); |
f1672e6f AB |
69 | } |
70 | ||
71 | SemihostingTarget semihosting_get_target(void) | |
72 | { | |
73 | return semihosting.target; | |
74 | } | |
75 | ||
76 | const char *semihosting_get_arg(int i) | |
77 | { | |
78 | if (i >= semihosting.argc) { | |
79 | return NULL; | |
80 | } | |
81 | return semihosting.argv[i]; | |
82 | } | |
83 | ||
84 | int semihosting_get_argc(void) | |
85 | { | |
86 | return semihosting.argc; | |
87 | } | |
88 | ||
89 | const 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 | ||
97 | static 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 */ | |
78beee80 AB |
105 | s->argv = g_renew(char *, s->argv, s->argc + 1); |
106 | s->argv[s->argc - 1] = g_strdup(val); | |
f1672e6f AB |
107 | s->argv[s->argc] = NULL; |
108 | } | |
109 | return 0; | |
110 | } | |
111 | ||
112 | /* Use strings passed via -kernel/-append to initialize semihosting.argv[] */ | |
113 | void 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 | ||
128 | void qemu_semihosting_enable(void) | |
129 | { | |
130 | semihosting.enabled = true; | |
131 | semihosting.target = SEMIHOSTING_TARGET_AUTO; | |
132 | } | |
133 | ||
134 | int 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); | |
5202861b PM |
144 | semihosting.userspace_enabled = qemu_opt_get_bool(opts, "userspace", |
145 | false); | |
f1672e6f | 146 | const char *target = qemu_opt_get(opts, "target"); |
4e7f9032 AB |
147 | /* setup of chardev is deferred until they are initialised */ |
148 | semihost_chardev = qemu_opt_get(opts, "chardev"); | |
f1672e6f AB |
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 | ||
fb08790b RH |
175 | /* We had to defer this until chardevs were created */ |
176 | void qemu_semihosting_chardev_init(void) | |
4e7f9032 | 177 | { |
fb08790b RH |
178 | Chardev *chr = NULL; |
179 | ||
4e7f9032 | 180 | if (semihost_chardev) { |
fb08790b | 181 | chr = qemu_chr_find(semihost_chardev); |
4e7f9032 AB |
182 | if (chr == NULL) { |
183 | error_report("semihosting chardev '%s' not found", | |
184 | semihost_chardev); | |
185 | exit(1); | |
186 | } | |
4e7f9032 | 187 | } |
fb08790b RH |
188 | |
189 | qemu_semihosting_console_init(chr); | |
4e7f9032 | 190 | } |