1c566080cSAlex Bennée /*
2c566080cSAlex Bennée * GDB Syscall Handling
3c566080cSAlex Bennée *
4c566080cSAlex Bennée * GDB can execute syscalls on the guests behalf, currently used by
52d3d2517SRichard Henderson * the various semihosting extensions.
6c566080cSAlex Bennée *
7c566080cSAlex Bennée * Copyright (c) 2003-2005 Fabrice Bellard
8c566080cSAlex Bennée * Copyright (c) 2023 Linaro Ltd
9c566080cSAlex Bennée *
10*b14d0649SPhilippe Mathieu-Daudé * SPDX-License-Identifier: LGPL-2.0-or-later
11c566080cSAlex Bennée */
12c566080cSAlex Bennée
13c566080cSAlex Bennée #include "qemu/osdep.h"
14c566080cSAlex Bennée #include "qemu/error-report.h"
15c566080cSAlex Bennée #include "semihosting/semihost.h"
16c566080cSAlex Bennée #include "sysemu/runstate.h"
17c566080cSAlex Bennée #include "gdbstub/user.h"
18c566080cSAlex Bennée #include "gdbstub/syscalls.h"
19133f202bSGustavo Romero #include "gdbstub/commands.h"
20c566080cSAlex Bennée #include "trace.h"
21c566080cSAlex Bennée #include "internals.h"
22c566080cSAlex Bennée
23c566080cSAlex Bennée /* Syscall specific state */
24c566080cSAlex Bennée typedef struct {
25c566080cSAlex Bennée char syscall_buf[256];
26c566080cSAlex Bennée gdb_syscall_complete_cb current_syscall_cb;
27c566080cSAlex Bennée } GDBSyscallState;
28c566080cSAlex Bennée
29c566080cSAlex Bennée static GDBSyscallState gdbserver_syscall_state;
30c566080cSAlex Bennée
31c566080cSAlex Bennée /*
32c566080cSAlex Bennée * Return true if there is a GDB currently connected to the stub
33c566080cSAlex Bennée * and attached to a CPU
34c566080cSAlex Bennée */
gdb_attached(void)35c566080cSAlex Bennée static bool gdb_attached(void)
36c566080cSAlex Bennée {
37c566080cSAlex Bennée return gdbserver_state.init && gdbserver_state.c_cpu;
38c566080cSAlex Bennée }
39c566080cSAlex Bennée
40c566080cSAlex Bennée static enum {
41c566080cSAlex Bennée GDB_SYS_UNKNOWN,
42c566080cSAlex Bennée GDB_SYS_ENABLED,
43c566080cSAlex Bennée GDB_SYS_DISABLED,
44c566080cSAlex Bennée } gdb_syscall_mode;
45c566080cSAlex Bennée
46c566080cSAlex Bennée /* Decide if either remote gdb syscalls or native file IO should be used. */
use_gdb_syscalls(void)47c566080cSAlex Bennée int use_gdb_syscalls(void)
48c566080cSAlex Bennée {
49c566080cSAlex Bennée SemihostingTarget target = semihosting_get_target();
50c566080cSAlex Bennée if (target == SEMIHOSTING_TARGET_NATIVE) {
51c566080cSAlex Bennée /* -semihosting-config target=native */
52c566080cSAlex Bennée return false;
53c566080cSAlex Bennée } else if (target == SEMIHOSTING_TARGET_GDB) {
54c566080cSAlex Bennée /* -semihosting-config target=gdb */
55c566080cSAlex Bennée return true;
56c566080cSAlex Bennée }
57c566080cSAlex Bennée
58c566080cSAlex Bennée /* -semihosting-config target=auto */
59c566080cSAlex Bennée /* On the first call check if gdb is connected and remember. */
60c566080cSAlex Bennée if (gdb_syscall_mode == GDB_SYS_UNKNOWN) {
61c566080cSAlex Bennée gdb_syscall_mode = gdb_attached() ? GDB_SYS_ENABLED : GDB_SYS_DISABLED;
62c566080cSAlex Bennée }
63c566080cSAlex Bennée return gdb_syscall_mode == GDB_SYS_ENABLED;
64c566080cSAlex Bennée }
65c566080cSAlex Bennée
66c566080cSAlex Bennée /* called when the stub detaches */
gdb_disable_syscalls(void)67c566080cSAlex Bennée void gdb_disable_syscalls(void)
68c566080cSAlex Bennée {
69c566080cSAlex Bennée gdb_syscall_mode = GDB_SYS_DISABLED;
70c566080cSAlex Bennée }
71c566080cSAlex Bennée
gdb_syscall_reset(void)72c566080cSAlex Bennée void gdb_syscall_reset(void)
73c566080cSAlex Bennée {
74c566080cSAlex Bennée gdbserver_syscall_state.current_syscall_cb = NULL;
75c566080cSAlex Bennée }
76c566080cSAlex Bennée
gdb_handled_syscall(void)77c566080cSAlex Bennée bool gdb_handled_syscall(void)
78c566080cSAlex Bennée {
79c566080cSAlex Bennée if (gdbserver_syscall_state.current_syscall_cb) {
80c566080cSAlex Bennée gdb_put_packet(gdbserver_syscall_state.syscall_buf);
81c566080cSAlex Bennée return true;
82c566080cSAlex Bennée }
83c566080cSAlex Bennée
84c566080cSAlex Bennée return false;
85c566080cSAlex Bennée }
86c566080cSAlex Bennée
87c566080cSAlex Bennée /*
88c566080cSAlex Bennée * Send a gdb syscall request.
89c566080cSAlex Bennée * This accepts limited printf-style format specifiers, specifically:
90c566080cSAlex Bennée * %x - target_ulong argument printed in hex.
91c566080cSAlex Bennée * %lx - 64-bit argument printed in hex.
92c566080cSAlex Bennée * %s - string pointer (target_ulong) and length (int) pair.
93c566080cSAlex Bennée */
gdb_do_syscall(gdb_syscall_complete_cb cb,const char * fmt,...)942f70f2d7SRichard Henderson void gdb_do_syscall(gdb_syscall_complete_cb cb, const char *fmt, ...)
95c566080cSAlex Bennée {
962f70f2d7SRichard Henderson char *p, *p_end;
972f70f2d7SRichard Henderson va_list va;
98c566080cSAlex Bennée
99c566080cSAlex Bennée if (!gdb_attached()) {
100c566080cSAlex Bennée return;
101c566080cSAlex Bennée }
102c566080cSAlex Bennée
103c566080cSAlex Bennée gdbserver_syscall_state.current_syscall_cb = cb;
1042f70f2d7SRichard Henderson va_start(va, fmt);
105131f387dSAlex Bennée
1062f70f2d7SRichard Henderson p = gdbserver_syscall_state.syscall_buf;
1072f70f2d7SRichard Henderson p_end = p + sizeof(gdbserver_syscall_state.syscall_buf);
108c566080cSAlex Bennée *(p++) = 'F';
109c566080cSAlex Bennée while (*fmt) {
110c566080cSAlex Bennée if (*fmt == '%') {
1112f70f2d7SRichard Henderson uint64_t i64;
1120820a075SRichard Henderson uint32_t i32;
1132f70f2d7SRichard Henderson
114c566080cSAlex Bennée fmt++;
115c566080cSAlex Bennée switch (*fmt++) {
116c566080cSAlex Bennée case 'x':
1170820a075SRichard Henderson i32 = va_arg(va, uint32_t);
1180820a075SRichard Henderson p += snprintf(p, p_end - p, "%" PRIx32, i32);
119c566080cSAlex Bennée break;
120c566080cSAlex Bennée case 'l':
121c566080cSAlex Bennée if (*(fmt++) != 'x') {
122c566080cSAlex Bennée goto bad_format;
123c566080cSAlex Bennée }
124c566080cSAlex Bennée i64 = va_arg(va, uint64_t);
125c566080cSAlex Bennée p += snprintf(p, p_end - p, "%" PRIx64, i64);
126c566080cSAlex Bennée break;
127c566080cSAlex Bennée case 's':
1280820a075SRichard Henderson i64 = va_arg(va, uint64_t);
1290820a075SRichard Henderson i32 = va_arg(va, uint32_t);
1300820a075SRichard Henderson p += snprintf(p, p_end - p, "%" PRIx64 "/%x" PRIx32, i64, i32);
131c566080cSAlex Bennée break;
132c566080cSAlex Bennée default:
133c566080cSAlex Bennée bad_format:
134c566080cSAlex Bennée error_report("gdbstub: Bad syscall format string '%s'",
135c566080cSAlex Bennée fmt - 1);
136c566080cSAlex Bennée break;
137c566080cSAlex Bennée }
138c566080cSAlex Bennée } else {
139c566080cSAlex Bennée *(p++) = *(fmt++);
140c566080cSAlex Bennée }
141c566080cSAlex Bennée }
142c566080cSAlex Bennée *p = 0;
143131f387dSAlex Bennée
144c566080cSAlex Bennée va_end(va);
1452f70f2d7SRichard Henderson gdb_syscall_handling(gdbserver_syscall_state.syscall_buf);
146c566080cSAlex Bennée }
147c566080cSAlex Bennée
148c566080cSAlex Bennée /*
149c566080cSAlex Bennée * GDB Command Handlers
150c566080cSAlex Bennée */
151c566080cSAlex Bennée
gdb_handle_file_io(GArray * params,void * user_ctx)152c566080cSAlex Bennée void gdb_handle_file_io(GArray *params, void *user_ctx)
153c566080cSAlex Bennée {
154c566080cSAlex Bennée if (params->len >= 1 && gdbserver_syscall_state.current_syscall_cb) {
155c566080cSAlex Bennée uint64_t ret;
156c566080cSAlex Bennée int err;
157c566080cSAlex Bennée
158133f202bSGustavo Romero ret = gdb_get_cmd_param(params, 0)->val_ull;
159c566080cSAlex Bennée if (params->len >= 2) {
160133f202bSGustavo Romero err = gdb_get_cmd_param(params, 1)->val_ull;
161c566080cSAlex Bennée } else {
162c566080cSAlex Bennée err = 0;
163c566080cSAlex Bennée }
164c566080cSAlex Bennée
165c566080cSAlex Bennée /* Convert GDB error numbers back to host error numbers. */
166c566080cSAlex Bennée #define E(X) case GDB_E##X: err = E##X; break
167c566080cSAlex Bennée switch (err) {
168c566080cSAlex Bennée case 0:
169c566080cSAlex Bennée break;
170c566080cSAlex Bennée E(PERM);
171c566080cSAlex Bennée E(NOENT);
172c566080cSAlex Bennée E(INTR);
173c566080cSAlex Bennée E(BADF);
174c566080cSAlex Bennée E(ACCES);
175c566080cSAlex Bennée E(FAULT);
176c566080cSAlex Bennée E(BUSY);
177c566080cSAlex Bennée E(EXIST);
178c566080cSAlex Bennée E(NODEV);
179c566080cSAlex Bennée E(NOTDIR);
180c566080cSAlex Bennée E(ISDIR);
181c566080cSAlex Bennée E(INVAL);
182c566080cSAlex Bennée E(NFILE);
183c566080cSAlex Bennée E(MFILE);
184c566080cSAlex Bennée E(FBIG);
185c566080cSAlex Bennée E(NOSPC);
186c566080cSAlex Bennée E(SPIPE);
187c566080cSAlex Bennée E(ROFS);
188c566080cSAlex Bennée E(NAMETOOLONG);
189c566080cSAlex Bennée default:
190c566080cSAlex Bennée err = EINVAL;
191c566080cSAlex Bennée break;
192c566080cSAlex Bennée }
193c566080cSAlex Bennée #undef E
194c566080cSAlex Bennée
195c566080cSAlex Bennée gdbserver_syscall_state.current_syscall_cb(gdbserver_state.c_cpu,
196c566080cSAlex Bennée ret, err);
197c566080cSAlex Bennée gdbserver_syscall_state.current_syscall_cb = NULL;
198c566080cSAlex Bennée }
199c566080cSAlex Bennée
200133f202bSGustavo Romero if (params->len >= 3 && gdb_get_cmd_param(params, 2)->opcode == (uint8_t)'C') {
201c566080cSAlex Bennée gdb_put_packet("T02");
202c566080cSAlex Bennée return;
203c566080cSAlex Bennée }
204c566080cSAlex Bennée
205c566080cSAlex Bennée gdb_continue();
206c566080cSAlex Bennée }
207