xref: /qemu/semihosting/config.c (revision 7cebff0d)
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 "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