xref: /qemu/semihosting/config.c (revision 5db05230)
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 slightly 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 system 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 "semihosting/semihost.h"
26 #include "chardev/char.h"
27 
28 QemuOptsList 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         { /* end of list */ }
51     },
52 };
53 
54 typedef struct SemihostingConfig {
55     bool enabled;
56     bool userspace_enabled;
57     SemihostingTarget target;
58     char **argv;
59     int argc;
60     const char *cmdline; /* concatenated argv */
61 } SemihostingConfig;
62 
63 static SemihostingConfig semihosting;
64 static const char *semihost_chardev;
65 
66 bool semihosting_enabled(bool is_user)
67 {
68     return semihosting.enabled && (!is_user || semihosting.userspace_enabled);
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 */
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 /* 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     g_autofree char *cmd_dup = g_strdup(cmd);
117 
118     /* argv[0] */
119     add_semihosting_arg(&semihosting, "arg", file, NULL);
120 
121     /* split -append and initialize argv[1..n] */
122     cmd_token = strtok(cmd_dup, " ");
123     while (cmd_token) {
124         add_semihosting_arg(&semihosting, "arg", cmd_token, NULL);
125         cmd_token = strtok(NULL, " ");
126     }
127 }
128 
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 *optstr)
136 {
137     QemuOptsList *opt_list = qemu_find_opts("semihosting-config");
138     QemuOpts *opts = qemu_opts_parse_noisily(opt_list, optstr, false);
139 
140     semihosting.enabled = true;
141 
142     if (opts != NULL) {
143         semihosting.enabled = qemu_opt_get_bool(opts, "enable",
144                                                 true);
145         semihosting.userspace_enabled = qemu_opt_get_bool(opts, "userspace",
146                                                           false);
147         const char *target = qemu_opt_get(opts, "target");
148         /* setup of chardev is deferred until they are initialised */
149         semihost_chardev = qemu_opt_get(opts, "chardev");
150         if (target != NULL) {
151             if (strcmp("native", target) == 0) {
152                 semihosting.target = SEMIHOSTING_TARGET_NATIVE;
153             } else if (strcmp("gdb", target) == 0) {
154                 semihosting.target = SEMIHOSTING_TARGET_GDB;
155             } else  if (strcmp("auto", target) == 0) {
156                 semihosting.target = SEMIHOSTING_TARGET_AUTO;
157             } else {
158                 error_report("unsupported semihosting-config %s",
159                              optstr);
160                 return 1;
161             }
162         } else {
163             semihosting.target = SEMIHOSTING_TARGET_AUTO;
164         }
165         /* Set semihosting argument count and vector */
166         qemu_opt_foreach(opts, add_semihosting_arg,
167                          &semihosting, NULL);
168     } else {
169         error_report("unsupported semihosting-config %s", optstr);
170         return 1;
171     }
172 
173     return 0;
174 }
175 
176 /* We had to defer this until chardevs were created */
177 void qemu_semihosting_chardev_init(void)
178 {
179     Chardev *chr = NULL;
180 
181     if (semihost_chardev) {
182         chr = qemu_chr_find(semihost_chardev);
183         if (chr == NULL) {
184             error_report("semihosting chardev '%s' not found",
185                          semihost_chardev);
186             exit(1);
187         }
188     }
189 
190     qemu_semihosting_console_init(chr);
191 }
192