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