blob: 56283b5c3c38a8465858b0464f9a622cdcbe9a69 [file] [log] [blame]
Alex Bennéef1672e62019-05-13 14:43:57 +01001/*
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 *
Michael Tokarev669dcb62023-08-23 08:53:30 +020011 * Semihosting is slightly strange in that it is also supported by some
Alex Bennéef1672e62019-05-13 14:43:57 +010012 * linux-user targets. However in that use case no configuration of
13 * the outputs and command lines is supported.
14 *
Philippe Mathieu-Daudéf14eced2023-10-04 11:06:23 +020015 * The config module is common to all system targets however as vl.c
Alex Bennéef1672e62019-05-13 14:43:57 +010016 * 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"
Philippe Mathieu-Daudé6b5fe132021-03-05 13:54:49 +000025#include "semihosting/semihost.h"
Alex Bennée4e7f9032019-05-14 15:30:14 +010026#include "chardev/char.h"
Alex Bennéef1672e62019-05-13 14:43:57 +010027
28QemuOptsList qemu_semihosting_config_opts = {
29 .name = "semihosting-config",
Peter Maydell90c072e2022-06-10 14:32:36 +010030 .merge_lists = true,
Alex Bennéef1672e62019-05-13 14:43:57 +010031 .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 }, {
Peter Maydell52028612022-08-22 15:12:24 +010038 .name = "userspace",
39 .type = QEMU_OPT_BOOL,
40 }, {
Alex Bennéef1672e62019-05-13 14:43:57 +010041 .name = "target",
42 .type = QEMU_OPT_STRING,
43 }, {
Alex Bennée4e7f9032019-05-14 15:30:14 +010044 .name = "chardev",
45 .type = QEMU_OPT_STRING,
46 }, {
Alex Bennéef1672e62019-05-13 14:43:57 +010047 .name = "arg",
48 .type = QEMU_OPT_STRING,
49 },
50 { /* end of list */ }
51 },
52};
53
54typedef struct SemihostingConfig {
55 bool enabled;
Peter Maydell52028612022-08-22 15:12:24 +010056 bool userspace_enabled;
Alex Bennéef1672e62019-05-13 14:43:57 +010057 SemihostingTarget target;
Alex Bennée78beee82022-03-15 11:46:12 +000058 char **argv;
Alex Bennéef1672e62019-05-13 14:43:57 +010059 int argc;
60 const char *cmdline; /* concatenated argv */
61} SemihostingConfig;
62
63static SemihostingConfig semihosting;
Alex Bennée4e7f9032019-05-14 15:30:14 +010064static const char *semihost_chardev;
Alex Bennéef1672e62019-05-13 14:43:57 +010065
Peter Maydell52028612022-08-22 15:12:24 +010066bool semihosting_enabled(bool is_user)
Alex Bennéef1672e62019-05-13 14:43:57 +010067{
Peter Maydell52028612022-08-22 15:12:24 +010068 return semihosting.enabled && (!is_user || semihosting.userspace_enabled);
Alex Bennéef1672e62019-05-13 14:43:57 +010069}
70
71SemihostingTarget semihosting_get_target(void)
72{
73 return semihosting.target;
74}
75
76const char *semihosting_get_arg(int i)
77{
78 if (i >= semihosting.argc) {
79 return NULL;
80 }
81 return semihosting.argv[i];
82}
83
84int semihosting_get_argc(void)
85{
86 return semihosting.argc;
87}
88
89const 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
97static 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 */
Alex Bennée78beee82022-03-15 11:46:12 +0000105 s->argv = g_renew(char *, s->argv, s->argc + 1);
106 s->argv[s->argc - 1] = g_strdup(val);
Alex Bennéef1672e62019-05-13 14:43:57 +0100107 s->argv[s->argc] = NULL;
108 }
109 return 0;
110}
111
112/* Use strings passed via -kernel/-append to initialize semihosting.argv[] */
113void semihosting_arg_fallback(const char *file, const char *cmd)
114{
115 char *cmd_token;
Matheus Tavares Bernardino2eb71a02023-10-29 14:50:31 +0000116 g_autofree char *cmd_dup = g_strdup(cmd);
Alex Bennéef1672e62019-05-13 14:43:57 +0100117
118 /* argv[0] */
119 add_semihosting_arg(&semihosting, "arg", file, NULL);
120
121 /* split -append and initialize argv[1..n] */
Matheus Tavares Bernardino2eb71a02023-10-29 14:50:31 +0000122 cmd_token = strtok(cmd_dup, " ");
Alex Bennéef1672e62019-05-13 14:43:57 +0100123 while (cmd_token) {
124 add_semihosting_arg(&semihosting, "arg", cmd_token, NULL);
125 cmd_token = strtok(NULL, " ");
126 }
127}
128
129void qemu_semihosting_enable(void)
130{
131 semihosting.enabled = true;
132 semihosting.target = SEMIHOSTING_TARGET_AUTO;
133}
134
Philippe Mathieu-Daudéafb81fe2023-10-04 14:00:12 +0200135int qemu_semihosting_config_options(const char *optstr)
Alex Bennéef1672e62019-05-13 14:43:57 +0100136{
137 QemuOptsList *opt_list = qemu_find_opts("semihosting-config");
Philippe Mathieu-Daudéafb81fe2023-10-04 14:00:12 +0200138 QemuOpts *opts = qemu_opts_parse_noisily(opt_list, optstr, false);
Alex Bennéef1672e62019-05-13 14:43:57 +0100139
140 semihosting.enabled = true;
141
142 if (opts != NULL) {
143 semihosting.enabled = qemu_opt_get_bool(opts, "enable",
144 true);
Peter Maydell52028612022-08-22 15:12:24 +0100145 semihosting.userspace_enabled = qemu_opt_get_bool(opts, "userspace",
146 false);
Alex Bennéef1672e62019-05-13 14:43:57 +0100147 const char *target = qemu_opt_get(opts, "target");
Alex Bennée4e7f9032019-05-14 15:30:14 +0100148 /* setup of chardev is deferred until they are initialised */
149 semihost_chardev = qemu_opt_get(opts, "chardev");
Alex Bennéef1672e62019-05-13 14:43:57 +0100150 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",
Philippe Mathieu-Daudéafb81fe2023-10-04 14:00:12 +0200159 optstr);
Alex Bennéef1672e62019-05-13 14:43:57 +0100160 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 {
Philippe Mathieu-Daudéafb81fe2023-10-04 14:00:12 +0200169 error_report("unsupported semihosting-config %s", optstr);
Alex Bennéef1672e62019-05-13 14:43:57 +0100170 return 1;
171 }
172
173 return 0;
174}
175
Richard Hendersonfb087902022-05-01 16:59:06 -0700176/* We had to defer this until chardevs were created */
177void qemu_semihosting_chardev_init(void)
Alex Bennée4e7f9032019-05-14 15:30:14 +0100178{
Richard Hendersonfb087902022-05-01 16:59:06 -0700179 Chardev *chr = NULL;
180
Alex Bennée4e7f9032019-05-14 15:30:14 +0100181 if (semihost_chardev) {
Richard Hendersonfb087902022-05-01 16:59:06 -0700182 chr = qemu_chr_find(semihost_chardev);
Alex Bennée4e7f9032019-05-14 15:30:14 +0100183 if (chr == NULL) {
184 error_report("semihosting chardev '%s' not found",
185 semihost_chardev);
186 exit(1);
187 }
Alex Bennée4e7f9032019-05-14 15:30:14 +0100188 }
Richard Hendersonfb087902022-05-01 16:59:06 -0700189
190 qemu_semihosting_console_init(chr);
Alex Bennée4e7f9032019-05-14 15:30:14 +0100191}