]>
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", | |
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, | |
4e7f9032 AB |
39 | }, { |
40 | .name = "chardev", | |
41 | .type = QEMU_OPT_STRING, | |
f1672e6f AB |
42 | }, { |
43 | .name = "arg", | |
44 | .type = QEMU_OPT_STRING, | |
45 | }, | |
46 | { /* end of list */ } | |
47 | }, | |
48 | }; | |
49 | ||
50 | typedef struct SemihostingConfig { | |
51 | bool enabled; | |
52 | SemihostingTarget target; | |
4e7f9032 | 53 | Chardev *chardev; |
78beee80 | 54 | char **argv; |
f1672e6f AB |
55 | int argc; |
56 | const char *cmdline; /* concatenated argv */ | |
57 | } SemihostingConfig; | |
58 | ||
59 | static SemihostingConfig semihosting; | |
4e7f9032 | 60 | static const char *semihost_chardev; |
f1672e6f AB |
61 | |
62 | bool semihosting_enabled(void) | |
63 | { | |
64 | return semihosting.enabled; | |
65 | } | |
66 | ||
67 | SemihostingTarget semihosting_get_target(void) | |
68 | { | |
69 | return semihosting.target; | |
70 | } | |
71 | ||
72 | const char *semihosting_get_arg(int i) | |
73 | { | |
74 | if (i >= semihosting.argc) { | |
75 | return NULL; | |
76 | } | |
77 | return semihosting.argv[i]; | |
78 | } | |
79 | ||
80 | int semihosting_get_argc(void) | |
81 | { | |
82 | return semihosting.argc; | |
83 | } | |
84 | ||
85 | const 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 | ||
93 | static 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 */ | |
78beee80 AB |
101 | s->argv = g_renew(char *, s->argv, s->argc + 1); |
102 | s->argv[s->argc - 1] = g_strdup(val); | |
f1672e6f AB |
103 | s->argv[s->argc] = NULL; |
104 | } | |
105 | return 0; | |
106 | } | |
107 | ||
108 | /* Use strings passed via -kernel/-append to initialize semihosting.argv[] */ | |
109 | void 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 | ||
4e7f9032 AB |
124 | Chardev *semihosting_get_chardev(void) |
125 | { | |
126 | return semihosting.chardev; | |
127 | } | |
128 | ||
f1672e6f AB |
129 | void qemu_semihosting_enable(void) |
130 | { | |
131 | semihosting.enabled = true; | |
132 | semihosting.target = SEMIHOSTING_TARGET_AUTO; | |
133 | } | |
134 | ||
135 | int 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"); | |
4e7f9032 AB |
146 | /* setup of chardev is deferred until they are initialised */ |
147 | semihost_chardev = qemu_opt_get(opts, "chardev"); | |
f1672e6f AB |
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 | ||
4e7f9032 AB |
174 | void 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 | } |