gdbstub: move syscall handling to new file
Our GDB syscall support is the last chunk of code that needs target specific support so move it to a new file. We take the opportunity to move the syscall state into its own singleton instance and add in a few helpers for the main gdbstub to interact with the module. I also moved the gdb_exit() declaration into syscalls.h as it feels pretty related and most of the callers of it treat it as such. Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-Id: <20230302190846.2593720-22-alex.bennee@linaro.org> Message-Id: <20230303025805.625589-22-richard.henderson@linaro.org>
This commit is contained in:
parent
4ea5fe997d
commit
c566080cd3
@ -38,6 +38,8 @@
|
|||||||
#include <sys/sysctl.h>
|
#include <sys/sysctl.h>
|
||||||
#include <utime.h>
|
#include <utime.h>
|
||||||
|
|
||||||
|
#include "include/gdbstub/syscalls.h"
|
||||||
|
|
||||||
#include "qemu.h"
|
#include "qemu.h"
|
||||||
#include "signal-common.h"
|
#include "signal-common.h"
|
||||||
#include "user/syscall-trace.h"
|
#include "user/syscall-trace.h"
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
#include "qemu/module.h"
|
#include "qemu/module.h"
|
||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
#include "exec/gdbstub.h"
|
#include "exec/gdbstub.h"
|
||||||
|
#include "gdbstub/syscalls.h"
|
||||||
#ifdef CONFIG_USER_ONLY
|
#ifdef CONFIG_USER_ONLY
|
||||||
#include "gdbstub/user.h"
|
#include "gdbstub/user.h"
|
||||||
#else
|
#else
|
||||||
@ -38,7 +39,6 @@
|
|||||||
|
|
||||||
#include "sysemu/hw_accel.h"
|
#include "sysemu/hw_accel.h"
|
||||||
#include "sysemu/runstate.h"
|
#include "sysemu/runstate.h"
|
||||||
#include "semihosting/semihost.h"
|
|
||||||
#include "exec/exec-all.h"
|
#include "exec/exec-all.h"
|
||||||
#include "exec/replay-core.h"
|
#include "exec/replay-core.h"
|
||||||
#include "exec/tb-flush.h"
|
#include "exec/tb-flush.h"
|
||||||
@ -78,41 +78,6 @@ void gdb_init_gdbserver_state(void)
|
|||||||
|
|
||||||
bool gdb_has_xml;
|
bool gdb_has_xml;
|
||||||
|
|
||||||
/*
|
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* writes 2*len+1 bytes in buf */
|
/* writes 2*len+1 bytes in buf */
|
||||||
void gdb_memtohex(GString *buf, const uint8_t *mem, int len)
|
void gdb_memtohex(GString *buf, const uint8_t *mem, int len)
|
||||||
{
|
{
|
||||||
@ -922,7 +887,7 @@ static void handle_detach(GArray *params, void *user_ctx)
|
|||||||
|
|
||||||
if (!gdbserver_state.c_cpu) {
|
if (!gdbserver_state.c_cpu) {
|
||||||
/* No more process attached */
|
/* No more process attached */
|
||||||
gdb_syscall_mode = GDB_SYS_DISABLED;
|
gdb_disable_syscalls();
|
||||||
gdb_continue();
|
gdb_continue();
|
||||||
}
|
}
|
||||||
gdb_put_packet("OK");
|
gdb_put_packet("OK");
|
||||||
@ -1235,60 +1200,6 @@ static void handle_read_all_regs(GArray *params, void *user_ctx)
|
|||||||
gdb_put_strbuf();
|
gdb_put_strbuf();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handle_file_io(GArray *params, void *user_ctx)
|
|
||||||
{
|
|
||||||
if (params->len >= 1 && gdbserver_state.current_syscall_cb) {
|
|
||||||
uint64_t ret;
|
|
||||||
int err;
|
|
||||||
|
|
||||||
ret = get_param(params, 0)->val_ull;
|
|
||||||
if (params->len >= 2) {
|
|
||||||
err = get_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_state.current_syscall_cb(gdbserver_state.c_cpu, ret, err);
|
|
||||||
gdbserver_state.current_syscall_cb = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (params->len >= 3 && get_param(params, 2)->opcode == (uint8_t)'C') {
|
|
||||||
gdb_put_packet("T02");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
gdb_continue();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_step(GArray *params, void *user_ctx)
|
static void handle_step(GArray *params, void *user_ctx)
|
||||||
{
|
{
|
||||||
@ -1894,7 +1805,7 @@ static int gdb_handle_packet(const char *line_buf)
|
|||||||
case 'F':
|
case 'F':
|
||||||
{
|
{
|
||||||
static const GdbCmdParseEntry file_io_cmd_desc = {
|
static const GdbCmdParseEntry file_io_cmd_desc = {
|
||||||
.handler = handle_file_io,
|
.handler = gdb_handle_file_io,
|
||||||
.cmd = "F",
|
.cmd = "F",
|
||||||
.cmd_startswith = 1,
|
.cmd_startswith = 1,
|
||||||
.schema = "L,L,o0"
|
.schema = "L,L,o0"
|
||||||
@ -2062,88 +1973,6 @@ void gdb_set_stop_cpu(CPUState *cpu)
|
|||||||
gdbserver_state.g_cpu = cpu;
|
gdbserver_state.g_cpu = cpu;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 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_syscallv(gdb_syscall_complete_cb cb, const char *fmt, va_list va)
|
|
||||||
{
|
|
||||||
char *p;
|
|
||||||
char *p_end;
|
|
||||||
target_ulong addr;
|
|
||||||
uint64_t i64;
|
|
||||||
|
|
||||||
if (!gdb_attached()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
gdbserver_state.current_syscall_cb = cb;
|
|
||||||
#ifndef CONFIG_USER_ONLY
|
|
||||||
vm_stop(RUN_STATE_DEBUG);
|
|
||||||
#endif
|
|
||||||
p = &gdbserver_state.syscall_buf[0];
|
|
||||||
p_end = &gdbserver_state.syscall_buf[sizeof(gdbserver_state.syscall_buf)];
|
|
||||||
*(p++) = 'F';
|
|
||||||
while (*fmt) {
|
|
||||||
if (*fmt == '%') {
|
|
||||||
fmt++;
|
|
||||||
switch (*fmt++) {
|
|
||||||
case 'x':
|
|
||||||
addr = va_arg(va, target_ulong);
|
|
||||||
p += snprintf(p, p_end - p, TARGET_FMT_lx, addr);
|
|
||||||
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':
|
|
||||||
addr = va_arg(va, target_ulong);
|
|
||||||
p += snprintf(p, p_end - p, TARGET_FMT_lx "/%x",
|
|
||||||
addr, va_arg(va, int));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
bad_format:
|
|
||||||
error_report("gdbstub: Bad syscall format string '%s'",
|
|
||||||
fmt - 1);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
*(p++) = *(fmt++);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*p = 0;
|
|
||||||
#ifdef CONFIG_USER_ONLY
|
|
||||||
gdb_put_packet(gdbserver_state.syscall_buf);
|
|
||||||
/* Return control to gdb for it to process the syscall request.
|
|
||||||
* Since the protocol requires that gdb hands control back to us
|
|
||||||
* using a "here are the results" F packet, we don't need to check
|
|
||||||
* gdb_handlesig's return value (which is the signal to deliver if
|
|
||||||
* execution was resumed via a continue packet).
|
|
||||||
*/
|
|
||||||
gdb_handlesig(gdbserver_state.c_cpu, 0);
|
|
||||||
#else
|
|
||||||
/* In this case wait to send the syscall packet until notification that
|
|
||||||
the CPU has stopped. This must be done because if the packet is sent
|
|
||||||
now the reply from the syscall request could be received while the CPU
|
|
||||||
is still in the running state, which can cause packets to be dropped
|
|
||||||
and state transition 'T' packets to be sent while the syscall is still
|
|
||||||
being processed. */
|
|
||||||
qemu_cpu_kick(gdbserver_state.c_cpu);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void gdb_do_syscall(gdb_syscall_complete_cb cb, const char *fmt, ...)
|
|
||||||
{
|
|
||||||
va_list va;
|
|
||||||
|
|
||||||
va_start(va, fmt);
|
|
||||||
gdb_do_syscallv(cb, fmt, va);
|
|
||||||
va_end(va);
|
|
||||||
}
|
|
||||||
|
|
||||||
void gdb_read_byte(uint8_t ch)
|
void gdb_read_byte(uint8_t ch)
|
||||||
{
|
{
|
||||||
uint8_t reply;
|
uint8_t reply;
|
||||||
|
@ -61,8 +61,6 @@ typedef struct GDBState {
|
|||||||
bool multiprocess;
|
bool multiprocess;
|
||||||
GDBProcess *processes;
|
GDBProcess *processes;
|
||||||
int process_num;
|
int process_num;
|
||||||
char syscall_buf[256];
|
|
||||||
gdb_syscall_complete_cb current_syscall_cb;
|
|
||||||
GString *str_buf;
|
GString *str_buf;
|
||||||
GByteArray *mem_buf;
|
GByteArray *mem_buf;
|
||||||
int sstep_flags;
|
int sstep_flags;
|
||||||
@ -191,6 +189,12 @@ void gdb_handle_query_attached(GArray *params, void *user_ctx); /* both */
|
|||||||
void gdb_handle_query_qemu_phy_mem_mode(GArray *params, void *user_ctx);
|
void gdb_handle_query_qemu_phy_mem_mode(GArray *params, void *user_ctx);
|
||||||
void gdb_handle_set_qemu_phy_mem_mode(GArray *params, void *user_ctx);
|
void gdb_handle_set_qemu_phy_mem_mode(GArray *params, void *user_ctx);
|
||||||
|
|
||||||
|
/* sycall handling */
|
||||||
|
void gdb_handle_file_io(GArray *params, void *user_ctx);
|
||||||
|
bool gdb_handled_syscall(void);
|
||||||
|
void gdb_disable_syscalls(void);
|
||||||
|
void gdb_syscall_reset(void);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Break/Watch point support - there is an implementation for softmmu
|
* Break/Watch point support - there is an implementation for softmmu
|
||||||
* and user mode.
|
* and user mode.
|
||||||
|
@ -5,6 +5,10 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
specific_ss.add(files('gdbstub.c'))
|
specific_ss.add(files('gdbstub.c'))
|
||||||
|
|
||||||
|
# These have to built to the target ABI
|
||||||
|
specific_ss.add(files('syscalls.c'))
|
||||||
|
|
||||||
softmmu_ss.add(files('softmmu.c'))
|
softmmu_ss.add(files('softmmu.c'))
|
||||||
user_ss.add(files('user.c'))
|
user_ss.add(files('user.c'))
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
#include "qemu/error-report.h"
|
#include "qemu/error-report.h"
|
||||||
#include "qemu/cutils.h"
|
#include "qemu/cutils.h"
|
||||||
#include "exec/gdbstub.h"
|
#include "exec/gdbstub.h"
|
||||||
|
#include "gdbstub/syscalls.h"
|
||||||
#include "exec/hwaddr.h"
|
#include "exec/hwaddr.h"
|
||||||
#include "exec/tb-flush.h"
|
#include "exec/tb-flush.h"
|
||||||
#include "sysemu/cpus.h"
|
#include "sysemu/cpus.h"
|
||||||
@ -113,9 +114,9 @@ static void gdb_vm_state_change(void *opaque, bool running, RunState state)
|
|||||||
if (running || gdbserver_state.state == RS_INACTIVE) {
|
if (running || gdbserver_state.state == RS_INACTIVE) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Is there a GDB syscall waiting to be sent? */
|
/* Is there a GDB syscall waiting to be sent? */
|
||||||
if (gdbserver_state.current_syscall_cb) {
|
if (gdb_handled_syscall()) {
|
||||||
gdb_put_packet(gdbserver_state.syscall_buf);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -384,7 +385,7 @@ int gdbserver_start(const char *device)
|
|||||||
}
|
}
|
||||||
gdbserver_state.state = chr ? RS_IDLE : RS_INACTIVE;
|
gdbserver_state.state = chr ? RS_IDLE : RS_INACTIVE;
|
||||||
gdbserver_system_state.mon_chr = mon_chr;
|
gdbserver_system_state.mon_chr = mon_chr;
|
||||||
gdbserver_state.current_syscall_cb = NULL;
|
gdb_syscall_reset();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
235
gdbstub/syscalls.c
Normal file
235
gdbstub/syscalls.c
Normal file
@ -0,0 +1,235 @@
|
|||||||
|
/*
|
||||||
|
* GDB Syscall Handling
|
||||||
|
*
|
||||||
|
* GDB can execute syscalls on the guests behalf, currently used by
|
||||||
|
* the various semihosting extensions. As this interfaces with a guest
|
||||||
|
* ABI we need to build it per-guest (although in reality its a 32 or
|
||||||
|
* 64 bit target_ulong that is the only difference).
|
||||||
|
*
|
||||||
|
* Copyright (c) 2003-2005 Fabrice Bellard
|
||||||
|
* Copyright (c) 2023 Linaro Ltd
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.0+
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "qemu/osdep.h"
|
||||||
|
#include "qemu/error-report.h"
|
||||||
|
#include "cpu.h"
|
||||||
|
#include "semihosting/semihost.h"
|
||||||
|
#include "sysemu/runstate.h"
|
||||||
|
#include "gdbstub/user.h"
|
||||||
|
#include "gdbstub/syscalls.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_syscallv(gdb_syscall_complete_cb cb, const char *fmt, va_list va)
|
||||||
|
{
|
||||||
|
char *p;
|
||||||
|
char *p_end;
|
||||||
|
target_ulong addr;
|
||||||
|
uint64_t i64;
|
||||||
|
|
||||||
|
if (!gdb_attached()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
gdbserver_syscall_state.current_syscall_cb = cb;
|
||||||
|
#ifndef CONFIG_USER_ONLY
|
||||||
|
vm_stop(RUN_STATE_DEBUG);
|
||||||
|
#endif
|
||||||
|
p = &gdbserver_syscall_state.syscall_buf[0];
|
||||||
|
p_end = &gdbserver_syscall_state.syscall_buf[sizeof(gdbserver_syscall_state.syscall_buf)];
|
||||||
|
*(p++) = 'F';
|
||||||
|
while (*fmt) {
|
||||||
|
if (*fmt == '%') {
|
||||||
|
fmt++;
|
||||||
|
switch (*fmt++) {
|
||||||
|
case 'x':
|
||||||
|
addr = va_arg(va, target_ulong);
|
||||||
|
p += snprintf(p, p_end - p, TARGET_FMT_lx, addr);
|
||||||
|
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':
|
||||||
|
addr = va_arg(va, target_ulong);
|
||||||
|
p += snprintf(p, p_end - p, TARGET_FMT_lx "/%x",
|
||||||
|
addr, va_arg(va, int));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
bad_format:
|
||||||
|
error_report("gdbstub: Bad syscall format string '%s'",
|
||||||
|
fmt - 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
*(p++) = *(fmt++);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*p = 0;
|
||||||
|
#ifdef CONFIG_USER_ONLY
|
||||||
|
gdb_put_packet(gdbserver_syscall_state.syscall_buf);
|
||||||
|
/*
|
||||||
|
* Return control to gdb for it to process the syscall request.
|
||||||
|
* Since the protocol requires that gdb hands control back to us
|
||||||
|
* using a "here are the results" F packet, we don't need to check
|
||||||
|
* gdb_handlesig's return value (which is the signal to deliver if
|
||||||
|
* execution was resumed via a continue packet).
|
||||||
|
*/
|
||||||
|
gdb_handlesig(gdbserver_state.c_cpu, 0);
|
||||||
|
#else
|
||||||
|
/*
|
||||||
|
* In this case wait to send the syscall packet until notification that
|
||||||
|
* the CPU has stopped. This must be done because if the packet is sent
|
||||||
|
* now the reply from the syscall request could be received while the CPU
|
||||||
|
* is still in the running state, which can cause packets to be dropped
|
||||||
|
* and state transition 'T' packets to be sent while the syscall is still
|
||||||
|
* being processed.
|
||||||
|
*/
|
||||||
|
qemu_cpu_kick(gdbserver_state.c_cpu);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void gdb_do_syscall(gdb_syscall_complete_cb cb, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list va;
|
||||||
|
|
||||||
|
va_start(va, fmt);
|
||||||
|
gdb_do_syscallv(cb, fmt, va);
|
||||||
|
va_end(va);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 = get_param(params, 0)->val_ull;
|
||||||
|
if (params->len >= 2) {
|
||||||
|
err = get_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 && get_param(params, 2)->opcode == (uint8_t)'C') {
|
||||||
|
gdb_put_packet("T02");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
gdb_continue();
|
||||||
|
}
|
@ -15,6 +15,7 @@
|
|||||||
#include "exec/hwaddr.h"
|
#include "exec/hwaddr.h"
|
||||||
#include "exec/tb-flush.h"
|
#include "exec/tb-flush.h"
|
||||||
#include "exec/gdbstub.h"
|
#include "exec/gdbstub.h"
|
||||||
|
#include "gdbstub/syscalls.h"
|
||||||
#include "gdbstub/user.h"
|
#include "gdbstub/user.h"
|
||||||
#include "hw/core/cpu.h"
|
#include "hw/core/cpu.h"
|
||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
|
@ -10,98 +10,6 @@
|
|||||||
#define GDB_WATCHPOINT_READ 3
|
#define GDB_WATCHPOINT_READ 3
|
||||||
#define GDB_WATCHPOINT_ACCESS 4
|
#define GDB_WATCHPOINT_ACCESS 4
|
||||||
|
|
||||||
/* For gdb file i/o remote protocol open flags. */
|
|
||||||
#define GDB_O_RDONLY 0
|
|
||||||
#define GDB_O_WRONLY 1
|
|
||||||
#define GDB_O_RDWR 2
|
|
||||||
#define GDB_O_APPEND 8
|
|
||||||
#define GDB_O_CREAT 0x200
|
|
||||||
#define GDB_O_TRUNC 0x400
|
|
||||||
#define GDB_O_EXCL 0x800
|
|
||||||
|
|
||||||
/* For gdb file i/o remote protocol errno values */
|
|
||||||
#define GDB_EPERM 1
|
|
||||||
#define GDB_ENOENT 2
|
|
||||||
#define GDB_EINTR 4
|
|
||||||
#define GDB_EBADF 9
|
|
||||||
#define GDB_EACCES 13
|
|
||||||
#define GDB_EFAULT 14
|
|
||||||
#define GDB_EBUSY 16
|
|
||||||
#define GDB_EEXIST 17
|
|
||||||
#define GDB_ENODEV 19
|
|
||||||
#define GDB_ENOTDIR 20
|
|
||||||
#define GDB_EISDIR 21
|
|
||||||
#define GDB_EINVAL 22
|
|
||||||
#define GDB_ENFILE 23
|
|
||||||
#define GDB_EMFILE 24
|
|
||||||
#define GDB_EFBIG 27
|
|
||||||
#define GDB_ENOSPC 28
|
|
||||||
#define GDB_ESPIPE 29
|
|
||||||
#define GDB_EROFS 30
|
|
||||||
#define GDB_ENAMETOOLONG 91
|
|
||||||
#define GDB_EUNKNOWN 9999
|
|
||||||
|
|
||||||
/* For gdb file i/o remote protocol lseek whence. */
|
|
||||||
#define GDB_SEEK_SET 0
|
|
||||||
#define GDB_SEEK_CUR 1
|
|
||||||
#define GDB_SEEK_END 2
|
|
||||||
|
|
||||||
/* For gdb file i/o stat/fstat. */
|
|
||||||
typedef uint32_t gdb_mode_t;
|
|
||||||
typedef uint32_t gdb_time_t;
|
|
||||||
|
|
||||||
struct gdb_stat {
|
|
||||||
uint32_t gdb_st_dev; /* device */
|
|
||||||
uint32_t gdb_st_ino; /* inode */
|
|
||||||
gdb_mode_t gdb_st_mode; /* protection */
|
|
||||||
uint32_t gdb_st_nlink; /* number of hard links */
|
|
||||||
uint32_t gdb_st_uid; /* user ID of owner */
|
|
||||||
uint32_t gdb_st_gid; /* group ID of owner */
|
|
||||||
uint32_t gdb_st_rdev; /* device type (if inode device) */
|
|
||||||
uint64_t gdb_st_size; /* total size, in bytes */
|
|
||||||
uint64_t gdb_st_blksize; /* blocksize for filesystem I/O */
|
|
||||||
uint64_t gdb_st_blocks; /* number of blocks allocated */
|
|
||||||
gdb_time_t gdb_st_atime; /* time of last access */
|
|
||||||
gdb_time_t gdb_st_mtime; /* time of last modification */
|
|
||||||
gdb_time_t gdb_st_ctime; /* time of last change */
|
|
||||||
} QEMU_PACKED;
|
|
||||||
|
|
||||||
struct gdb_timeval {
|
|
||||||
gdb_time_t tv_sec; /* second */
|
|
||||||
uint64_t tv_usec; /* microsecond */
|
|
||||||
} QEMU_PACKED;
|
|
||||||
|
|
||||||
typedef void (*gdb_syscall_complete_cb)(CPUState *cpu, uint64_t ret, int err);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* gdb_do_syscall:
|
|
||||||
* @cb: function to call when the system call has completed
|
|
||||||
* @fmt: gdb syscall format string
|
|
||||||
* ...: list of arguments to interpolate into @fmt
|
|
||||||
*
|
|
||||||
* Send a GDB syscall request. This function will return immediately;
|
|
||||||
* the callback function will be called later when the remote system
|
|
||||||
* call has completed.
|
|
||||||
*
|
|
||||||
* @fmt should be in the 'call-id,parameter,parameter...' format documented
|
|
||||||
* for the F request packet in the GDB remote protocol. A limited set of
|
|
||||||
* printf-style format specifiers is supported:
|
|
||||||
* %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, ...);
|
|
||||||
/**
|
|
||||||
* gdb_do_syscallv:
|
|
||||||
* @cb: function to call when the system call has completed
|
|
||||||
* @fmt: gdb syscall format string
|
|
||||||
* @va: arguments to interpolate into @fmt
|
|
||||||
*
|
|
||||||
* As gdb_do_syscall, but taking a va_list rather than a variable
|
|
||||||
* argument list.
|
|
||||||
*/
|
|
||||||
void gdb_do_syscallv(gdb_syscall_complete_cb cb, const char *fmt, va_list va);
|
|
||||||
int use_gdb_syscalls(void);
|
|
||||||
|
|
||||||
/* Get or set a register. Returns the size of the register. */
|
/* Get or set a register. Returns the size of the register. */
|
||||||
typedef int (*gdb_get_reg_cb)(CPUArchState *env, GByteArray *buf, int reg);
|
typedef int (*gdb_get_reg_cb)(CPUArchState *env, GByteArray *buf, int reg);
|
||||||
@ -120,16 +28,6 @@ void gdb_register_coprocessor(CPUState *cpu,
|
|||||||
*/
|
*/
|
||||||
int gdbserver_start(const char *port_or_device);
|
int gdbserver_start(const char *port_or_device);
|
||||||
|
|
||||||
/**
|
|
||||||
* gdb_exit: exit gdb session, reporting inferior status
|
|
||||||
* @code: exit code reported
|
|
||||||
*
|
|
||||||
* This closes the session and sends a final packet to GDB reporting
|
|
||||||
* the exit status of the program. It also cleans up any connections
|
|
||||||
* detritus before returning.
|
|
||||||
*/
|
|
||||||
void gdb_exit(int code);
|
|
||||||
|
|
||||||
void gdb_set_stop_cpu(CPUState *cpu);
|
void gdb_set_stop_cpu(CPUState *cpu);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
124
include/gdbstub/syscalls.h
Normal file
124
include/gdbstub/syscalls.h
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
/*
|
||||||
|
* GDB Syscall support
|
||||||
|
*
|
||||||
|
* Copyright (c) 2023 Linaro Ltd
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.0+
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _SYSCALLS_H_
|
||||||
|
#define _SYSCALLS_H_
|
||||||
|
|
||||||
|
/* For gdb file i/o remote protocol open flags. */
|
||||||
|
#define GDB_O_RDONLY 0
|
||||||
|
#define GDB_O_WRONLY 1
|
||||||
|
#define GDB_O_RDWR 2
|
||||||
|
#define GDB_O_APPEND 8
|
||||||
|
#define GDB_O_CREAT 0x200
|
||||||
|
#define GDB_O_TRUNC 0x400
|
||||||
|
#define GDB_O_EXCL 0x800
|
||||||
|
|
||||||
|
/* For gdb file i/o remote protocol errno values */
|
||||||
|
#define GDB_EPERM 1
|
||||||
|
#define GDB_ENOENT 2
|
||||||
|
#define GDB_EINTR 4
|
||||||
|
#define GDB_EBADF 9
|
||||||
|
#define GDB_EACCES 13
|
||||||
|
#define GDB_EFAULT 14
|
||||||
|
#define GDB_EBUSY 16
|
||||||
|
#define GDB_EEXIST 17
|
||||||
|
#define GDB_ENODEV 19
|
||||||
|
#define GDB_ENOTDIR 20
|
||||||
|
#define GDB_EISDIR 21
|
||||||
|
#define GDB_EINVAL 22
|
||||||
|
#define GDB_ENFILE 23
|
||||||
|
#define GDB_EMFILE 24
|
||||||
|
#define GDB_EFBIG 27
|
||||||
|
#define GDB_ENOSPC 28
|
||||||
|
#define GDB_ESPIPE 29
|
||||||
|
#define GDB_EROFS 30
|
||||||
|
#define GDB_ENAMETOOLONG 91
|
||||||
|
#define GDB_EUNKNOWN 9999
|
||||||
|
|
||||||
|
/* For gdb file i/o remote protocol lseek whence. */
|
||||||
|
#define GDB_SEEK_SET 0
|
||||||
|
#define GDB_SEEK_CUR 1
|
||||||
|
#define GDB_SEEK_END 2
|
||||||
|
|
||||||
|
/* For gdb file i/o stat/fstat. */
|
||||||
|
typedef uint32_t gdb_mode_t;
|
||||||
|
typedef uint32_t gdb_time_t;
|
||||||
|
|
||||||
|
struct gdb_stat {
|
||||||
|
uint32_t gdb_st_dev; /* device */
|
||||||
|
uint32_t gdb_st_ino; /* inode */
|
||||||
|
gdb_mode_t gdb_st_mode; /* protection */
|
||||||
|
uint32_t gdb_st_nlink; /* number of hard links */
|
||||||
|
uint32_t gdb_st_uid; /* user ID of owner */
|
||||||
|
uint32_t gdb_st_gid; /* group ID of owner */
|
||||||
|
uint32_t gdb_st_rdev; /* device type (if inode device) */
|
||||||
|
uint64_t gdb_st_size; /* total size, in bytes */
|
||||||
|
uint64_t gdb_st_blksize; /* blocksize for filesystem I/O */
|
||||||
|
uint64_t gdb_st_blocks; /* number of blocks allocated */
|
||||||
|
gdb_time_t gdb_st_atime; /* time of last access */
|
||||||
|
gdb_time_t gdb_st_mtime; /* time of last modification */
|
||||||
|
gdb_time_t gdb_st_ctime; /* time of last change */
|
||||||
|
} QEMU_PACKED;
|
||||||
|
|
||||||
|
struct gdb_timeval {
|
||||||
|
gdb_time_t tv_sec; /* second */
|
||||||
|
uint64_t tv_usec; /* microsecond */
|
||||||
|
} QEMU_PACKED;
|
||||||
|
|
||||||
|
typedef void (*gdb_syscall_complete_cb)(CPUState *cpu, uint64_t ret, int err);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gdb_do_syscall:
|
||||||
|
* @cb: function to call when the system call has completed
|
||||||
|
* @fmt: gdb syscall format string
|
||||||
|
* ...: list of arguments to interpolate into @fmt
|
||||||
|
*
|
||||||
|
* Send a GDB syscall request. This function will return immediately;
|
||||||
|
* the callback function will be called later when the remote system
|
||||||
|
* call has completed.
|
||||||
|
*
|
||||||
|
* @fmt should be in the 'call-id,parameter,parameter...' format documented
|
||||||
|
* for the F request packet in the GDB remote protocol. A limited set of
|
||||||
|
* printf-style format specifiers is supported:
|
||||||
|
* %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, ...);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gdb_do_syscallv:
|
||||||
|
* @cb: function to call when the system call has completed
|
||||||
|
* @fmt: gdb syscall format string
|
||||||
|
* @va: arguments to interpolate into @fmt
|
||||||
|
*
|
||||||
|
* As gdb_do_syscall, but taking a va_list rather than a variable
|
||||||
|
* argument list.
|
||||||
|
*/
|
||||||
|
void gdb_do_syscallv(gdb_syscall_complete_cb cb, const char *fmt, va_list va);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* use_gdb_syscalls() - report if GDB should be used for syscalls
|
||||||
|
*
|
||||||
|
* This is mostly driven by the semihosting mode the user configures
|
||||||
|
* but assuming GDB is allowed by that we report true if GDB is
|
||||||
|
* connected to the stub.
|
||||||
|
*/
|
||||||
|
int use_gdb_syscalls(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gdb_exit: exit gdb session, reporting inferior status
|
||||||
|
* @code: exit code reported
|
||||||
|
*
|
||||||
|
* This closes the session and sends a final packet to GDB reporting
|
||||||
|
* the exit status of the program. It also cleans up any connections
|
||||||
|
* detritus before returning.
|
||||||
|
*/
|
||||||
|
void gdb_exit(int code);
|
||||||
|
|
||||||
|
#endif /* _SYSCALLS_H_ */
|
@ -18,7 +18,7 @@
|
|||||||
*/
|
*/
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "accel/tcg/perf.h"
|
#include "accel/tcg/perf.h"
|
||||||
#include "exec/gdbstub.h"
|
#include "gdbstub/syscalls.h"
|
||||||
#include "qemu.h"
|
#include "qemu.h"
|
||||||
#include "user-internals.h"
|
#include "user-internals.h"
|
||||||
#ifdef CONFIG_GPROF
|
#ifdef CONFIG_GPROF
|
||||||
|
@ -34,6 +34,7 @@
|
|||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "qemu/timer.h"
|
#include "qemu/timer.h"
|
||||||
#include "exec/gdbstub.h"
|
#include "exec/gdbstub.h"
|
||||||
|
#include "gdbstub/syscalls.h"
|
||||||
#include "semihosting/semihost.h"
|
#include "semihosting/semihost.h"
|
||||||
#include "semihosting/console.h"
|
#include "semihosting/console.h"
|
||||||
#include "semihosting/common-semi.h"
|
#include "semihosting/common-semi.h"
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "exec/gdbstub.h"
|
#include "gdbstub/syscalls.h"
|
||||||
#include "semihosting/semihost.h"
|
#include "semihosting/semihost.h"
|
||||||
#include "semihosting/guestfd.h"
|
#include "semihosting/guestfd.h"
|
||||||
#ifdef CONFIG_USER_ONLY
|
#ifdef CONFIG_USER_ONLY
|
||||||
|
@ -7,8 +7,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "exec/gdbstub.h"
|
|
||||||
#include "cpu.h"
|
#include "cpu.h"
|
||||||
|
#include "gdbstub/syscalls.h"
|
||||||
#include "semihosting/guestfd.h"
|
#include "semihosting/guestfd.h"
|
||||||
#include "semihosting/syscalls.h"
|
#include "semihosting/syscalls.h"
|
||||||
#include "semihosting/console.h"
|
#include "semihosting/console.h"
|
||||||
|
@ -30,7 +30,7 @@
|
|||||||
#include "crypto/cipher.h"
|
#include "crypto/cipher.h"
|
||||||
#include "crypto/init.h"
|
#include "crypto/init.h"
|
||||||
#include "exec/cpu-common.h"
|
#include "exec/cpu-common.h"
|
||||||
#include "exec/gdbstub.h"
|
#include "gdbstub/syscalls.h"
|
||||||
#include "hw/boards.h"
|
#include "hw/boards.h"
|
||||||
#include "migration/misc.h"
|
#include "migration/misc.h"
|
||||||
#include "migration/postcopy-ram.h"
|
#include "migration/postcopy-ram.h"
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
|
|
||||||
#include "cpu.h"
|
#include "cpu.h"
|
||||||
#include "exec/gdbstub.h"
|
#include "gdbstub/syscalls.h"
|
||||||
#include "gdbstub/helpers.h"
|
#include "gdbstub/helpers.h"
|
||||||
#include "semihosting/syscalls.h"
|
#include "semihosting/syscalls.h"
|
||||||
#include "semihosting/softmmu-uaccess.h"
|
#include "semihosting/softmmu-uaccess.h"
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "cpu.h"
|
#include "cpu.h"
|
||||||
#include "qemu/log.h"
|
#include "qemu/log.h"
|
||||||
#include "exec/gdbstub.h"
|
#include "gdbstub/syscalls.h"
|
||||||
#include "gdbstub/helpers.h"
|
#include "gdbstub/helpers.h"
|
||||||
#include "semihosting/softmmu-uaccess.h"
|
#include "semihosting/softmmu-uaccess.h"
|
||||||
#include "semihosting/semihost.h"
|
#include "semihosting/semihost.h"
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
|
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "cpu.h"
|
#include "cpu.h"
|
||||||
#include "exec/gdbstub.h"
|
#include "gdbstub/syscalls.h"
|
||||||
#include "gdbstub/helpers.h"
|
#include "gdbstub/helpers.h"
|
||||||
#include "semihosting/syscalls.h"
|
#include "semihosting/syscalls.h"
|
||||||
#include "semihosting/softmmu-uaccess.h"
|
#include "semihosting/softmmu-uaccess.h"
|
||||||
|
Loading…
Reference in New Issue
Block a user