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