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 | |
29 | QemuOptsList 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 | |
51 | typedef 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 | |
60 | static SemihostingConfig semihosting; |
61 | static const char *semihost_chardev; |
62 | |
63 | bool semihosting_enabled(void) |
64 | { |
65 | return semihosting.enabled; |
66 | } |
67 | |
68 | SemihostingTarget semihosting_get_target(void) |
69 | { |
70 | return semihosting.target; |
71 | } |
72 | |
73 | const char *semihosting_get_arg(int i) |
74 | { |
75 | if (i >= semihosting.argc) { |
76 | return NULL; |
77 | } |
78 | return semihosting.argv[i]; |
79 | } |
80 | |
81 | int semihosting_get_argc(void) |
82 | { |
83 | return semihosting.argc; |
84 | } |
85 | |
86 | const 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 | |
94 | static 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[] */ |
110 | void 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 | |
125 | Chardev *semihosting_get_chardev(void) |
126 | { |
127 | return semihosting.chardev; |
128 | } |
129 | |
130 | void qemu_semihosting_enable(void) |
131 | { |
132 | semihosting.enabled = true; |
133 | semihosting.target = SEMIHOSTING_TARGET_AUTO; |
134 | } |
135 | |
136 | int 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 | |
175 | void 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 | |