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:
Alex Bennée 2023-03-02 18:57:57 -08:00
parent 4ea5fe997d
commit c566080cd3
17 changed files with 387 additions and 288 deletions

View File

@ -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"

View File

@ -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;

View File

@ -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.

View File

@ -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'))

View File

@ -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
View 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();
}

View File

@ -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"

View File

@ -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
View 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_ */

View File

@ -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

View File

@ -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"

View File

@ -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

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"