xref: /qemu/semihosting/config.c (revision b21e2380)
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 
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,
39         }, {
40             .name = "chardev",
41             .type = QEMU_OPT_STRING,
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;
53     Chardev *chardev;
54     const char **argv;
55     int argc;
56     const char *cmdline; /* concatenated argv */
57 } SemihostingConfig;
58 
59 static SemihostingConfig semihosting;
60 static const char *semihost_chardev;
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 */
101         s->argv = g_realloc(s->argv, (s->argc + 1) * sizeof(void *));
102         s->argv[s->argc - 1] = val;
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 
124 Chardev *semihosting_get_chardev(void)
125 {
126     return semihosting.chardev;
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 *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");
146         /* setup of chardev is deferred until they are initialised */
147         semihost_chardev = qemu_opt_get(opts, "chardev");
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 
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 }
187