123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206 |
- /*
- * GDB Syscall Handling
- *
- * GDB can execute syscalls on the guests behalf, currently used by
- * the various semihosting extensions.
- *
- * Copyright (c) 2003-2005 Fabrice Bellard
- * Copyright (c) 2023 Linaro Ltd
- *
- * SPDX-License-Identifier: LGPL-2.0-or-later
- */
- #include "qemu/osdep.h"
- #include "qemu/error-report.h"
- #include "semihosting/semihost.h"
- #include "system/runstate.h"
- #include "gdbstub/user.h"
- #include "gdbstub/syscalls.h"
- #include "gdbstub/commands.h"
- #include "trace.h"
- #include "internals.h"
- /* Syscall specific state */
- typedef struct {
- char syscall_buf[256];
- gdb_syscall_complete_cb current_syscall_cb;
- } GDBSyscallState;
- static GDBSyscallState gdbserver_syscall_state;
- /*
- * Return true if there is a GDB currently connected to the stub
- * and attached to a CPU
- */
- static bool gdb_attached(void)
- {
- return gdbserver_state.init && gdbserver_state.c_cpu;
- }
- static enum {
- GDB_SYS_UNKNOWN,
- GDB_SYS_ENABLED,
- GDB_SYS_DISABLED,
- } gdb_syscall_mode;
- /* Decide if either remote gdb syscalls or native file IO should be used. */
- int use_gdb_syscalls(void)
- {
- SemihostingTarget target = semihosting_get_target();
- if (target == SEMIHOSTING_TARGET_NATIVE) {
- /* -semihosting-config target=native */
- return false;
- } else if (target == SEMIHOSTING_TARGET_GDB) {
- /* -semihosting-config target=gdb */
- return true;
- }
- /* -semihosting-config target=auto */
- /* On the first call check if gdb is connected and remember. */
- if (gdb_syscall_mode == GDB_SYS_UNKNOWN) {
- gdb_syscall_mode = gdb_attached() ? GDB_SYS_ENABLED : GDB_SYS_DISABLED;
- }
- return gdb_syscall_mode == GDB_SYS_ENABLED;
- }
- /* called when the stub detaches */
- void gdb_disable_syscalls(void)
- {
- gdb_syscall_mode = GDB_SYS_DISABLED;
- }
- void gdb_syscall_reset(void)
- {
- gdbserver_syscall_state.current_syscall_cb = NULL;
- }
- bool gdb_handled_syscall(void)
- {
- if (gdbserver_syscall_state.current_syscall_cb) {
- gdb_put_packet(gdbserver_syscall_state.syscall_buf);
- return true;
- }
- return false;
- }
- /*
- * Send a gdb syscall request.
- * This accepts limited printf-style format specifiers, specifically:
- * %x - target_ulong argument printed in hex.
- * %lx - 64-bit argument printed in hex.
- * %s - string pointer (target_ulong) and length (int) pair.
- */
- void gdb_do_syscall(gdb_syscall_complete_cb cb, const char *fmt, ...)
- {
- char *p, *p_end;
- va_list va;
- if (!gdb_attached()) {
- return;
- }
- gdbserver_syscall_state.current_syscall_cb = cb;
- va_start(va, fmt);
- p = gdbserver_syscall_state.syscall_buf;
- p_end = p + sizeof(gdbserver_syscall_state.syscall_buf);
- *(p++) = 'F';
- while (*fmt) {
- if (*fmt == '%') {
- uint64_t i64;
- uint32_t i32;
- fmt++;
- switch (*fmt++) {
- case 'x':
- i32 = va_arg(va, uint32_t);
- p += snprintf(p, p_end - p, "%" PRIx32, i32);
- break;
- case 'l':
- if (*(fmt++) != 'x') {
- goto bad_format;
- }
- i64 = va_arg(va, uint64_t);
- p += snprintf(p, p_end - p, "%" PRIx64, i64);
- break;
- case 's':
- i64 = va_arg(va, uint64_t);
- i32 = va_arg(va, uint32_t);
- p += snprintf(p, p_end - p, "%" PRIx64 "/%x" PRIx32, i64, i32);
- break;
- default:
- bad_format:
- error_report("gdbstub: Bad syscall format string '%s'",
- fmt - 1);
- break;
- }
- } else {
- *(p++) = *(fmt++);
- }
- }
- *p = 0;
- va_end(va);
- gdb_syscall_handling(gdbserver_syscall_state.syscall_buf);
- }
- /*
- * GDB Command Handlers
- */
- void gdb_handle_file_io(GArray *params, void *user_ctx)
- {
- if (params->len >= 1 && gdbserver_syscall_state.current_syscall_cb) {
- uint64_t ret;
- int err;
- ret = gdb_get_cmd_param(params, 0)->val_ull;
- if (params->len >= 2) {
- err = gdb_get_cmd_param(params, 1)->val_ull;
- } else {
- err = 0;
- }
- /* Convert GDB error numbers back to host error numbers. */
- #define E(X) case GDB_E##X: err = E##X; break
- switch (err) {
- case 0:
- break;
- E(PERM);
- E(NOENT);
- E(INTR);
- E(BADF);
- E(ACCES);
- E(FAULT);
- E(BUSY);
- E(EXIST);
- E(NODEV);
- E(NOTDIR);
- E(ISDIR);
- E(INVAL);
- E(NFILE);
- E(MFILE);
- E(FBIG);
- E(NOSPC);
- E(SPIPE);
- E(ROFS);
- E(NAMETOOLONG);
- default:
- err = EINVAL;
- break;
- }
- #undef E
- gdbserver_syscall_state.current_syscall_cb(gdbserver_state.c_cpu,
- ret, err);
- gdbserver_syscall_state.current_syscall_cb = NULL;
- }
- if (params->len >= 3 && gdb_get_cmd_param(params, 2)->opcode == (uint8_t)'C') {
- gdb_put_packet("T02");
- return;
- }
- gdb_continue();
- }
|