aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2023-03-09 16:54:51 +0000
committerPeter Maydell <peter.maydell@linaro.org>2023-03-09 16:54:51 +0000
commitb1224d8395de9be79c8cddbc2f60d29d9f1c10fe (patch)
treee1ed07b1201271f34307f27dde9f0fa7fe405b86
parent66a6aa8f9a56a6317e074b1f5e269fecdf4ad782 (diff)
parent412ae12647d1086c713e13841fd25d10d5418c7f (diff)
Merge tag 'pull-gdbstub-070323-3' of https://gitlab.com/stsquad/qemu into staging
gdbstub refactor: - split user and softmmu code - use cleaner headers for tb_flush, target_ulong - probe for gdb multiarch support at configure - make syscall handling target independent - add update guest debug of accel ops # -----BEGIN PGP SIGNATURE----- # # iQEzBAABCgAdFiEEZoWumedRZ7yvyN81+9DbCVqeKkQFAmQHomMACgkQ+9DbCVqe # KkTtFAf/aEyKY0iUNxtB4/oV1L2VnLaZi+iKoZT4RQgrhOhzr5WV6/3/V05cw1RJ # SIwcl8wB4gowYILs44eM/Qzcixiugl++2rvM4YVXiQyWKzkH6sY4X2iFuPGTwHLp # y+E7RM77QNS7M9xYaVkdsQawnbsgjG67wZKbb88aaekFEn61UuDg1V2Nqa2ICy7Y # /8yGIB2ixDfXOF0z4g8NOG44BXTDBtJbcEzf5GMz6D4HGnPZUbENy1nT0OcBk3zK # PqKPNkPFZ360pqA9MtougjZ3xTBb7Afe9nRRMquV2RoFmkkY2otSjdPBFQu5GBlm # NyTXEzjIQ6tCZlbS0eqdPVrUHHUx9g== # =Al36 # -----END PGP SIGNATURE----- # gpg: Signature made Tue 07 Mar 2023 20:45:23 GMT # gpg: using RSA key 6685AE99E75167BCAFC8DF35FBD0DB095A9E2A44 # gpg: Good signature from "Alex Bennée (Master Work Key) <alex.bennee@linaro.org>" [full] # Primary key fingerprint: 6685 AE99 E751 67BC AFC8 DF35 FBD0 DB09 5A9E 2A44 * tag 'pull-gdbstub-070323-3' of https://gitlab.com/stsquad/qemu: (30 commits) gdbstub: move update guest debug to accel ops gdbstub: Build syscall.c once stubs: split semihosting_get_target from system only stubs gdbstub: Adjust gdb_do_syscall to only use uint32_t and uint64_t gdbstub: Remove gdb_do_syscallv gdbstub: split out softmmu/user specifics for syscall handling include: split target_long definition from cpu-defs testing: probe gdb for supported architectures ahead of time gdbstub: only compile gdbstub twice for whole build gdbstub: move syscall handling to new file gdbstub: move register helpers into standalone include gdbstub: don't use target_ulong while handling registers gdbstub: fix address type of gdb_set_cpu_pc gdbstub: specialise stub_can_reverse gdbstub: introduce gdb_get_max_cpus gdbstub: specialise target_memory_rw_debug gdbstub: specialise handle_query_attached gdbstub: abstract target specific details from gdb_put_packet_binary gdbstub: rationalise signal mapping in softmmu gdbstub: move chunks of user code into own files ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
-rw-r--r--MAINTAINERS4
-rw-r--r--accel/kvm/kvm-accel-ops.c8
-rw-r--r--accel/stubs/tcg-stub.c1
-rw-r--r--accel/tcg/tb-maint.c1
-rw-r--r--accel/tcg/translate-all.c1
-rw-r--r--bsd-user/freebsd/os-syscall.c2
-rw-r--r--bsd-user/main.c1
-rw-r--r--bsd-user/signal.c1
-rwxr-xr-xconfigure8
-rw-r--r--cpu.c12
-rw-r--r--gdbstub/gdbstub.c1655
-rw-r--r--gdbstub/internals.h212
-rw-r--r--gdbstub/meson.build34
-rw-r--r--gdbstub/softmmu.c603
-rw-r--r--gdbstub/syscalls.c205
-rw-r--r--gdbstub/trace-events4
-rw-r--r--gdbstub/user-target.c283
-rw-r--r--gdbstub/user.c423
-rw-r--r--hw/ppc/spapr_hcall.c1
-rw-r--r--include/exec/cpu-defs.h19
-rw-r--r--include/exec/exec-all.h1
-rw-r--r--include/exec/gdbstub.h208
-rw-r--r--include/exec/target_long.h42
-rw-r--r--include/exec/tb-flush.h26
-rw-r--r--include/gdbstub/helpers.h103
-rw-r--r--include/gdbstub/syscalls.h113
-rw-r--r--include/gdbstub/user.h43
-rw-r--r--include/sysemu/accel-ops.h1
-rw-r--r--linux-user/exit.c2
-rw-r--r--linux-user/main.c1
-rw-r--r--linux-user/signal.c2
-rw-r--r--linux-user/user-internals.h1
-rw-r--r--plugins/core.c1
-rw-r--r--plugins/loader.c2
-rwxr-xr-xscripts/probe-gdb-support.py88
-rw-r--r--semihosting/arm-compat-semi.c1
-rw-r--r--semihosting/guestfd.c2
-rw-r--r--semihosting/syscalls.c37
-rw-r--r--softmmu/runstate.c2
-rw-r--r--stubs/meson.build1
-rw-r--r--stubs/semihost-all.c17
-rw-r--r--stubs/semihost.c5
-rw-r--r--target/alpha/gdbstub.c2
-rw-r--r--target/alpha/sys_helper.c1
-rw-r--r--target/arm/gdbstub.c1
-rw-r--r--target/arm/gdbstub64.c2
-rw-r--r--target/arm/tcg/helper-a64.c2
-rw-r--r--target/arm/tcg/m_helper.c1
-rw-r--r--target/avr/gdbstub.c2
-rw-r--r--target/cris/gdbstub.c2
-rw-r--r--target/hexagon/gdbstub.c2
-rw-r--r--target/hppa/gdbstub.c2
-rw-r--r--target/i386/gdbstub.c2
-rw-r--r--target/i386/whpx/whpx-all.c2
-rw-r--r--target/loongarch/gdbstub.c1
-rw-r--r--target/m68k/gdbstub.c2
-rw-r--r--target/m68k/helper.c1
-rw-r--r--target/m68k/m68k-semi.c3
-rw-r--r--target/microblaze/gdbstub.c2
-rw-r--r--target/mips/gdbstub.c2
-rw-r--r--target/mips/tcg/sysemu/mips-semi.c3
-rw-r--r--target/nios2/cpu.c2
-rw-r--r--target/nios2/nios2-semi.c3
-rw-r--r--target/openrisc/gdbstub.c2
-rw-r--r--target/openrisc/interrupt.c2
-rw-r--r--target/openrisc/mmu.c2
-rw-r--r--target/ppc/cpu_init.c2
-rw-r--r--target/ppc/gdbstub.c1
-rw-r--r--target/riscv/csr.c1
-rw-r--r--target/riscv/gdbstub.c1
-rw-r--r--target/rx/gdbstub.c2
-rw-r--r--target/s390x/gdbstub.c1
-rw-r--r--target/s390x/helper.c2
-rw-r--r--target/sh4/gdbstub.c2
-rw-r--r--target/sparc/gdbstub.c2
-rw-r--r--target/tricore/gdbstub.c2
-rw-r--r--target/xtensa/core-dc232b.c2
-rw-r--r--target/xtensa/core-dc233c.c2
-rw-r--r--target/xtensa/core-de212.c2
-rw-r--r--target/xtensa/core-de233_fpu.c2
-rw-r--r--target/xtensa/core-dsp3400.c2
-rw-r--r--target/xtensa/core-fsf.c2
-rw-r--r--target/xtensa/core-lx106.c2
-rw-r--r--target/xtensa/core-sample_controller.c2
-rw-r--r--target/xtensa/core-test_kc705_be.c2
-rw-r--r--target/xtensa/core-test_mmuhifi_c3.c2
-rw-r--r--target/xtensa/gdbstub.c2
-rw-r--r--target/xtensa/helper.c2
-rwxr-xr-xtarget/xtensa/import_core.sh2
-rw-r--r--tests/tcg/aarch64/Makefile.target2
-rw-r--r--tests/tcg/multiarch/Makefile.target5
-rw-r--r--tests/tcg/multiarch/system/Makefile.softmmu-target6
-rw-r--r--tests/tcg/s390x/Makefile.target2
93 files changed, 2462 insertions, 1818 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index 640deb2895..3f24bc2787 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -136,6 +136,8 @@ F: docs/devel/decodetree.rst
F: docs/devel/tcg*
F: include/exec/cpu*.h
F: include/exec/exec-all.h
+F: include/exec/tb-flush.h
+F: include/exec/target_long.h
F: include/exec/helper*.h
F: include/sysemu/cpus.h
F: include/sysemu/tcg.h
@@ -2751,9 +2753,11 @@ S: Maintained
F: docs/system/gdb.rst
F: gdbstub/*
F: include/exec/gdbstub.h
+F: include/gdbstub/*
F: gdb-xml/
F: tests/tcg/multiarch/gdbstub/
F: scripts/feature_to_c.sh
+F: scripts/probe-gdb-support.py
Memory API
M: Paolo Bonzini <pbonzini@redhat.com>
diff --git a/accel/kvm/kvm-accel-ops.c b/accel/kvm/kvm-accel-ops.c
index fbf4fe3497..457eafa380 100644
--- a/accel/kvm/kvm-accel-ops.c
+++ b/accel/kvm/kvm-accel-ops.c
@@ -86,6 +86,13 @@ static bool kvm_cpus_are_resettable(void)
return !kvm_enabled() || kvm_cpu_check_are_resettable();
}
+#ifdef KVM_CAP_SET_GUEST_DEBUG
+static int kvm_update_guest_debug_ops(CPUState *cpu)
+{
+ return kvm_update_guest_debug(cpu, 0);
+}
+#endif
+
static void kvm_accel_ops_class_init(ObjectClass *oc, void *data)
{
AccelOpsClass *ops = ACCEL_OPS_CLASS(oc);
@@ -99,6 +106,7 @@ static void kvm_accel_ops_class_init(ObjectClass *oc, void *data)
ops->synchronize_pre_loadvm = kvm_cpu_synchronize_pre_loadvm;
#ifdef KVM_CAP_SET_GUEST_DEBUG
+ ops->update_guest_debug = kvm_update_guest_debug_ops;
ops->supports_guest_debug = kvm_supports_guest_debug;
ops->insert_breakpoint = kvm_insert_breakpoint;
ops->remove_breakpoint = kvm_remove_breakpoint;
diff --git a/accel/stubs/tcg-stub.c b/accel/stubs/tcg-stub.c
index 96af23dc5d..813695b402 100644
--- a/accel/stubs/tcg-stub.c
+++ b/accel/stubs/tcg-stub.c
@@ -11,6 +11,7 @@
*/
#include "qemu/osdep.h"
+#include "exec/tb-flush.h"
#include "exec/exec-all.h"
void tb_flush(CPUState *cpu)
diff --git a/accel/tcg/tb-maint.c b/accel/tcg/tb-maint.c
index efefa08ee1..7246c1c46b 100644
--- a/accel/tcg/tb-maint.c
+++ b/accel/tcg/tb-maint.c
@@ -22,6 +22,7 @@
#include "exec/cputlb.h"
#include "exec/log.h"
#include "exec/exec-all.h"
+#include "exec/tb-flush.h"
#include "exec/translate-all.h"
#include "sysemu/tcg.h"
#include "tcg/tcg.h"
diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c
index a5bea8f99c..74deb18bd0 100644
--- a/accel/tcg/translate-all.c
+++ b/accel/tcg/translate-all.c
@@ -47,6 +47,7 @@
#include "exec/cputlb.h"
#include "exec/translate-all.h"
#include "exec/translator.h"
+#include "exec/tb-flush.h"
#include "qemu/bitmap.h"
#include "qemu/qemu-print.h"
#include "qemu/main-loop.h"
diff --git a/bsd-user/freebsd/os-syscall.c b/bsd-user/freebsd/os-syscall.c
index 179a20c304..c8f998ecec 100644
--- a/bsd-user/freebsd/os-syscall.c
+++ b/bsd-user/freebsd/os-syscall.c
@@ -38,6 +38,8 @@
#include <sys/sysctl.h>
#include <utime.h>
+#include "include/gdbstub/syscalls.h"
+
#include "qemu.h"
#include "signal-common.h"
#include "user/syscall-trace.h"
diff --git a/bsd-user/main.c b/bsd-user/main.c
index 41290e16f9..89f225dead 100644
--- a/bsd-user/main.c
+++ b/bsd-user/main.c
@@ -44,6 +44,7 @@
#include "trace/control.h"
#include "crypto/init.h"
#include "qemu/guest-random.h"
+#include "gdbstub/user.h"
#include "host-os.h"
#include "target_arch_cpu.h"
diff --git a/bsd-user/signal.c b/bsd-user/signal.c
index 58a5386395..f4e078ee1d 100644
--- a/bsd-user/signal.c
+++ b/bsd-user/signal.c
@@ -21,6 +21,7 @@
#include "qemu/osdep.h"
#include "qemu/log.h"
#include "qemu.h"
+#include "gdbstub/user.h"
#include "signal-common.h"
#include "trace.h"
#include "hw/core/tcg-cpu-ops.h"
diff --git a/configure b/configure
index 7290493729..05bed4f4a1 100755
--- a/configure
+++ b/configure
@@ -230,6 +230,7 @@ stack_protector=""
safe_stack=""
use_containers="yes"
gdb_bin=$(command -v "gdb-multiarch" || command -v "gdb")
+gdb_arches=""
if test -e "$source_path/.git"
then
@@ -2396,6 +2397,7 @@ if test -n "$gdb_bin"; then
gdb_version=$($gdb_bin --version | head -n 1)
if version_ge ${gdb_version##* } 9.1; then
echo "HAVE_GDB_BIN=$gdb_bin" >> $config_host_mak
+ gdb_arches=$("$source_path/scripts/probe-gdb-support.py" $gdb_bin)
else
gdb_bin=""
fi
@@ -2520,6 +2522,12 @@ for target in $target_list; do
write_target_makefile "build-tcg-tests-$target" >> "$config_target_mak"
echo "BUILD_STATIC=$build_static" >> "$config_target_mak"
echo "QEMU=$PWD/$qemu" >> "$config_target_mak"
+
+ # will GDB work with these binaries?
+ if test "${gdb_arches#*$arch}" != "$gdb_arches"; then
+ echo "HOST_GDB_SUPPORTS_ARCH=y" >> "$config_target_mak"
+ fi
+
echo "run-tcg-tests-$target: $qemu\$(EXESUF)" >> Makefile.prereqs
tcg_tests_targets="$tcg_tests_targets $target"
fi
diff --git a/cpu.c b/cpu.c
index 2e9f931249..567b23af46 100644
--- a/cpu.c
+++ b/cpu.c
@@ -31,11 +31,12 @@
#include "hw/core/sysemu-cpu-ops.h"
#include "exec/address-spaces.h"
#endif
+#include "sysemu/cpus.h"
#include "sysemu/tcg.h"
-#include "sysemu/kvm.h"
#include "exec/replay-core.h"
#include "exec/cpu-common.h"
#include "exec/exec-all.h"
+#include "exec/tb-flush.h"
#include "exec/translate-all.h"
#include "exec/log.h"
#include "hw/core/accel-cpu.h"
@@ -325,9 +326,14 @@ void cpu_single_step(CPUState *cpu, int enabled)
{
if (cpu->singlestep_enabled != enabled) {
cpu->singlestep_enabled = enabled;
- if (kvm_enabled()) {
- kvm_update_guest_debug(cpu, 0);
+
+#if !defined(CONFIG_USER_ONLY)
+ const AccelOpsClass *ops = cpus_get_accel();
+ if (ops->update_guest_debug) {
+ ops->update_guest_debug(cpu);
}
+#endif
+
trace_breakpoint_singlestep(cpu->cpu_index, enabled);
}
}
diff --git a/gdbstub/gdbstub.c b/gdbstub/gdbstub.c
index fb9c49e0fd..d9e9bf9294 100644
--- a/gdbstub/gdbstub.c
+++ b/gdbstub/gdbstub.c
@@ -24,298 +24,26 @@
*/
#include "qemu/osdep.h"
-#include "qapi/error.h"
-#include "qemu/error-report.h"
#include "qemu/ctype.h"
#include "qemu/cutils.h"
#include "qemu/module.h"
#include "trace.h"
#include "exec/gdbstub.h"
+#include "gdbstub/syscalls.h"
#ifdef CONFIG_USER_ONLY
-#include "qemu.h"
+#include "gdbstub/user.h"
#else
-#include "monitor/monitor.h"
-#include "chardev/char.h"
-#include "chardev/char-fe.h"
#include "hw/cpu/cluster.h"
#include "hw/boards.h"
#endif
-#define MAX_PACKET_LENGTH 4096
-
-#include "qemu/sockets.h"
#include "sysemu/hw_accel.h"
#include "sysemu/runstate.h"
-#include "semihosting/semihost.h"
-#include "exec/exec-all.h"
#include "exec/replay-core.h"
+#include "exec/hwaddr.h"
#include "internals.h"
-#ifdef CONFIG_USER_ONLY
-#define GDB_ATTACHED "0"
-#else
-#define GDB_ATTACHED "1"
-#endif
-
-#ifndef CONFIG_USER_ONLY
-static int phy_memory_mode;
-#endif
-
-static inline int target_memory_rw_debug(CPUState *cpu, target_ulong addr,
- uint8_t *buf, int len, bool is_write)
-{
- CPUClass *cc;
-
-#ifndef CONFIG_USER_ONLY
- if (phy_memory_mode) {
- if (is_write) {
- cpu_physical_memory_write(addr, buf, len);
- } else {
- cpu_physical_memory_read(addr, buf, len);
- }
- return 0;
- }
-#endif
-
- cc = CPU_GET_CLASS(cpu);
- if (cc->memory_rw_debug) {
- return cc->memory_rw_debug(cpu, addr, buf, len, is_write);
- }
- return cpu_memory_rw_debug(cpu, addr, buf, len, is_write);
-}
-
-/* Return the GDB index for a given vCPU state.
- *
- * For user mode this is simply the thread id. In system mode GDB
- * numbers CPUs from 1 as 0 is reserved as an "any cpu" index.
- */
-static inline int cpu_gdb_index(CPUState *cpu)
-{
-#if defined(CONFIG_USER_ONLY)
- TaskState *ts = (TaskState *) cpu->opaque;
- return ts ? ts->ts_tid : -1;
-#else
- return cpu->cpu_index + 1;
-#endif
-}
-
-enum {
- GDB_SIGNAL_0 = 0,
- GDB_SIGNAL_INT = 2,
- GDB_SIGNAL_QUIT = 3,
- GDB_SIGNAL_TRAP = 5,
- GDB_SIGNAL_ABRT = 6,
- GDB_SIGNAL_ALRM = 14,
- GDB_SIGNAL_IO = 23,
- GDB_SIGNAL_XCPU = 24,
- GDB_SIGNAL_UNKNOWN = 143
-};
-
-#ifdef CONFIG_USER_ONLY
-
-/* Map target signal numbers to GDB protocol signal numbers and vice
- * versa. For user emulation's currently supported systems, we can
- * assume most signals are defined.
- */
-
-static int gdb_signal_table[] = {
- 0,
- TARGET_SIGHUP,
- TARGET_SIGINT,
- TARGET_SIGQUIT,
- TARGET_SIGILL,
- TARGET_SIGTRAP,
- TARGET_SIGABRT,
- -1, /* SIGEMT */
- TARGET_SIGFPE,
- TARGET_SIGKILL,
- TARGET_SIGBUS,
- TARGET_SIGSEGV,
- TARGET_SIGSYS,
- TARGET_SIGPIPE,
- TARGET_SIGALRM,
- TARGET_SIGTERM,
- TARGET_SIGURG,
- TARGET_SIGSTOP,
- TARGET_SIGTSTP,
- TARGET_SIGCONT,
- TARGET_SIGCHLD,
- TARGET_SIGTTIN,
- TARGET_SIGTTOU,
- TARGET_SIGIO,
- TARGET_SIGXCPU,
- TARGET_SIGXFSZ,
- TARGET_SIGVTALRM,
- TARGET_SIGPROF,
- TARGET_SIGWINCH,
- -1, /* SIGLOST */
- TARGET_SIGUSR1,
- TARGET_SIGUSR2,
-#ifdef TARGET_SIGPWR
- TARGET_SIGPWR,
-#else
- -1,
-#endif
- -1, /* SIGPOLL */
- -1,
- -1,
- -1,
- -1,
- -1,
- -1,
- -1,
- -1,
- -1,
- -1,
- -1,
-#ifdef __SIGRTMIN
- __SIGRTMIN + 1,
- __SIGRTMIN + 2,
- __SIGRTMIN + 3,
- __SIGRTMIN + 4,
- __SIGRTMIN + 5,
- __SIGRTMIN + 6,
- __SIGRTMIN + 7,
- __SIGRTMIN + 8,
- __SIGRTMIN + 9,
- __SIGRTMIN + 10,
- __SIGRTMIN + 11,
- __SIGRTMIN + 12,
- __SIGRTMIN + 13,
- __SIGRTMIN + 14,
- __SIGRTMIN + 15,
- __SIGRTMIN + 16,
- __SIGRTMIN + 17,
- __SIGRTMIN + 18,
- __SIGRTMIN + 19,
- __SIGRTMIN + 20,
- __SIGRTMIN + 21,
- __SIGRTMIN + 22,
- __SIGRTMIN + 23,
- __SIGRTMIN + 24,
- __SIGRTMIN + 25,
- __SIGRTMIN + 26,
- __SIGRTMIN + 27,
- __SIGRTMIN + 28,
- __SIGRTMIN + 29,
- __SIGRTMIN + 30,
- __SIGRTMIN + 31,
- -1, /* SIGCANCEL */
- __SIGRTMIN,
- __SIGRTMIN + 32,
- __SIGRTMIN + 33,
- __SIGRTMIN + 34,
- __SIGRTMIN + 35,
- __SIGRTMIN + 36,
- __SIGRTMIN + 37,
- __SIGRTMIN + 38,
- __SIGRTMIN + 39,
- __SIGRTMIN + 40,
- __SIGRTMIN + 41,
- __SIGRTMIN + 42,
- __SIGRTMIN + 43,
- __SIGRTMIN + 44,
- __SIGRTMIN + 45,
- __SIGRTMIN + 46,
- __SIGRTMIN + 47,
- __SIGRTMIN + 48,
- __SIGRTMIN + 49,
- __SIGRTMIN + 50,
- __SIGRTMIN + 51,
- __SIGRTMIN + 52,
- __SIGRTMIN + 53,
- __SIGRTMIN + 54,
- __SIGRTMIN + 55,
- __SIGRTMIN + 56,
- __SIGRTMIN + 57,
- __SIGRTMIN + 58,
- __SIGRTMIN + 59,
- __SIGRTMIN + 60,
- __SIGRTMIN + 61,
- __SIGRTMIN + 62,
- __SIGRTMIN + 63,
- __SIGRTMIN + 64,
- __SIGRTMIN + 65,
- __SIGRTMIN + 66,
- __SIGRTMIN + 67,
- __SIGRTMIN + 68,
- __SIGRTMIN + 69,
- __SIGRTMIN + 70,
- __SIGRTMIN + 71,
- __SIGRTMIN + 72,
- __SIGRTMIN + 73,
- __SIGRTMIN + 74,
- __SIGRTMIN + 75,
- __SIGRTMIN + 76,
- __SIGRTMIN + 77,
- __SIGRTMIN + 78,
- __SIGRTMIN + 79,
- __SIGRTMIN + 80,
- __SIGRTMIN + 81,
- __SIGRTMIN + 82,
- __SIGRTMIN + 83,
- __SIGRTMIN + 84,
- __SIGRTMIN + 85,
- __SIGRTMIN + 86,
- __SIGRTMIN + 87,
- __SIGRTMIN + 88,
- __SIGRTMIN + 89,
- __SIGRTMIN + 90,
- __SIGRTMIN + 91,
- __SIGRTMIN + 92,
- __SIGRTMIN + 93,
- __SIGRTMIN + 94,
- __SIGRTMIN + 95,
- -1, /* SIGINFO */
- -1, /* UNKNOWN */
- -1, /* DEFAULT */
- -1,
- -1,
- -1,
- -1,
- -1,
- -1
-#endif
-};
-#else
-/* In system mode we only need SIGINT and SIGTRAP; other signals
- are not yet supported. */
-
-enum {
- TARGET_SIGINT = 2,
- TARGET_SIGTRAP = 5
-};
-
-static int gdb_signal_table[] = {
- -1,
- -1,
- TARGET_SIGINT,
- -1,
- -1,
- TARGET_SIGTRAP
-};
-#endif
-
-#ifdef CONFIG_USER_ONLY
-static int target_signal_to_gdb (int sig)
-{
- int i;
- for (i = 0; i < ARRAY_SIZE (gdb_signal_table); i++)
- if (gdb_signal_table[i] == sig)
- return i;
- return GDB_SIGNAL_UNKNOWN;
-}
-#endif
-
-static int gdb_signal_to_target (int sig)
-{
- if (sig < ARRAY_SIZE (gdb_signal_table))
- return gdb_signal_table[sig];
- else
- return -1;
-}
-
typedef struct GDBRegisterState {
int base_reg;
int num_regs;
@@ -325,56 +53,9 @@ typedef struct GDBRegisterState {
struct GDBRegisterState *next;
} GDBRegisterState;
-typedef struct GDBProcess {
- uint32_t pid;
- bool attached;
-
- char target_xml[1024];
-} GDBProcess;
-
-enum RSState {
- RS_INACTIVE,
- RS_IDLE,
- RS_GETLINE,
- RS_GETLINE_ESC,
- RS_GETLINE_RLE,
- RS_CHKSUM1,
- RS_CHKSUM2,
-};
-typedef struct GDBState {
- bool init; /* have we been initialised? */
- CPUState *c_cpu; /* current CPU for step/continue ops */
- CPUState *g_cpu; /* current CPU for other ops */
- CPUState *query_cpu; /* for q{f|s}ThreadInfo */
- enum RSState state; /* parsing state */
- char line_buf[MAX_PACKET_LENGTH];
- int line_buf_index;
- int line_sum; /* running checksum */
- int line_csum; /* checksum at the end of the packet */
- GByteArray *last_packet;
- int signal;
-#ifdef CONFIG_USER_ONLY
- int fd;
- char *socket_path;
- int running_state;
-#else
- CharBackend chr;
- Chardev *mon_chr;
-#endif
- bool multiprocess;
- GDBProcess *processes;
- int process_num;
- char syscall_buf[256];
- gdb_syscall_complete_cb current_syscall_cb;
- GString *str_buf;
- GByteArray *mem_buf;
- int sstep_flags;
- int supported_sstep_flags;
-} GDBState;
-
-static GDBState gdbserver_state;
+GDBState gdbserver_state;
-static void init_gdbserver_state(void)
+void gdb_init_gdbserver_state(void)
{
g_assert(!gdbserver_state.init);
memset(&gdbserver_state, 0, sizeof(GDBState));
@@ -393,211 +74,10 @@ static void init_gdbserver_state(void)
gdbserver_state.sstep_flags &= gdbserver_state.supported_sstep_flags;
}
-#ifndef CONFIG_USER_ONLY
-static void reset_gdbserver_state(void)
-{
- g_free(gdbserver_state.processes);
- gdbserver_state.processes = NULL;
- gdbserver_state.process_num = 0;
-}
-#endif
-
bool gdb_has_xml;
-#ifdef CONFIG_USER_ONLY
-
-static int get_char(void)
-{
- uint8_t ch;
- int ret;
-
- for(;;) {
- ret = recv(gdbserver_state.fd, &ch, 1, 0);
- if (ret < 0) {
- if (errno == ECONNRESET)
- gdbserver_state.fd = -1;
- if (errno != EINTR)
- return -1;
- } else if (ret == 0) {
- close(gdbserver_state.fd);
- gdbserver_state.fd = -1;
- return -1;
- } else {
- break;
- }
- }
- return ch;
-}
-#endif
-
-/*
- * 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;
-}
-
-static bool stub_can_reverse(void)
-{
-#ifdef CONFIG_USER_ONLY
- return false;
-#else
- return replay_mode == REPLAY_MODE_PLAY;
-#endif
-}
-
-/* Resume execution. */
-static inline void gdb_continue(void)
-{
-
-#ifdef CONFIG_USER_ONLY
- gdbserver_state.running_state = 1;
- trace_gdbstub_op_continue();
-#else
- if (!runstate_needs_reset()) {
- trace_gdbstub_op_continue();
- vm_start();
- }
-#endif
-}
-
-/*
- * Resume execution, per CPU actions. For user-mode emulation it's
- * equivalent to gdb_continue.
- */
-static int gdb_continue_partial(char *newstates)
-{
- CPUState *cpu;
- int res = 0;
-#ifdef CONFIG_USER_ONLY
- /*
- * This is not exactly accurate, but it's an improvement compared to the
- * previous situation, where only one CPU would be single-stepped.
- */
- CPU_FOREACH(cpu) {
- if (newstates[cpu->cpu_index] == 's') {
- trace_gdbstub_op_stepping(cpu->cpu_index);
- cpu_single_step(cpu, gdbserver_state.sstep_flags);
- }
- }
- gdbserver_state.running_state = 1;
-#else
- int flag = 0;
-
- if (!runstate_needs_reset()) {
- bool step_requested = false;
- CPU_FOREACH(cpu) {
- if (newstates[cpu->cpu_index] == 's') {
- step_requested = true;
- break;
- }
- }
-
- if (vm_prepare_start(step_requested)) {
- return 0;
- }
-
- CPU_FOREACH(cpu) {
- switch (newstates[cpu->cpu_index]) {
- case 0:
- case 1:
- break; /* nothing to do here */
- case 's':
- trace_gdbstub_op_stepping(cpu->cpu_index);
- cpu_single_step(cpu, gdbserver_state.sstep_flags);
- cpu_resume(cpu);
- flag = 1;
- break;
- case 'c':
- trace_gdbstub_op_continue_cpu(cpu->cpu_index);
- cpu_resume(cpu);
- flag = 1;
- break;
- default:
- res = -1;
- break;
- }
- }
- }
- if (flag) {
- qemu_clock_enable(QEMU_CLOCK_VIRTUAL, true);
- }
-#endif
- return res;
-}
-
-static void put_buffer(const uint8_t *buf, int len)
-{
-#ifdef CONFIG_USER_ONLY
- int ret;
-
- while (len > 0) {
- ret = send(gdbserver_state.fd, buf, len, 0);
- if (ret < 0) {
- if (errno != EINTR)
- return;
- } else {
- buf += ret;
- len -= ret;
- }
- }
-#else
- /* XXX this blocks entire thread. Rewrite to use
- * qemu_chr_fe_write and background I/O callbacks */
- qemu_chr_fe_write_all(&gdbserver_state.chr, buf, len);
-#endif
-}
-
-static inline int fromhex(int v)
-{
- if (v >= '0' && v <= '9')
- return v - '0';
- else if (v >= 'A' && v <= 'F')
- return v - 'A' + 10;
- else if (v >= 'a' && v <= 'f')
- return v - 'a' + 10;
- else
- return 0;
-}
-
-static inline int tohex(int v)
-{
- if (v < 10)
- return v + '0';
- else
- return v - 10 + 'a';
-}
-
/* writes 2*len+1 bytes in buf */
-static void memtohex(GString *buf, const uint8_t *mem, int len)
+void gdb_memtohex(GString *buf, const uint8_t *mem, int len)
{
int i, c;
for(i = 0; i < len; i++) {
@@ -608,7 +88,7 @@ static void memtohex(GString *buf, const uint8_t *mem, int len)
g_string_append_c(buf, '\0');
}
-static void hextomem(GByteArray *mem, const char *buf, int len)
+void gdb_hextomem(GByteArray *mem, const char *buf, int len)
{
int i;
@@ -653,7 +133,7 @@ static void hexdump(const char *buf, int len,
}
/* return -1 if error, 0 if OK */
-static int put_packet_binary(const char *buf, int len, bool dump)
+int gdb_put_packet_binary(const char *buf, int len, bool dump)
{
int csum, i;
uint8_t footer[3];
@@ -677,37 +157,31 @@ static int put_packet_binary(const char *buf, int len, bool dump)
footer[2] = tohex((csum) & 0xf);
g_byte_array_append(gdbserver_state.last_packet, footer, 3);
- put_buffer(gdbserver_state.last_packet->data,
+ gdb_put_buffer(gdbserver_state.last_packet->data,
gdbserver_state.last_packet->len);
-#ifdef CONFIG_USER_ONLY
- i = get_char();
- if (i < 0)
- return -1;
- if (i == '+')
+ if (gdb_got_immediate_ack()) {
break;
-#else
- break;
-#endif
+ }
}
return 0;
}
/* return -1 if error, 0 if OK */
-static int put_packet(const char *buf)
+int gdb_put_packet(const char *buf)
{
trace_gdbstub_io_reply(buf);
- return put_packet_binary(buf, strlen(buf), false);
+ return gdb_put_packet_binary(buf, strlen(buf), false);
}
-static void put_strbuf(void)
+void gdb_put_strbuf(void)
{
- put_packet(gdbserver_state.str_buf->str);
+ gdb_put_packet(gdbserver_state.str_buf->str);
}
/* Encode data using the encoding for 'x' packets. */
-static void memtox(GString *buf, const char *mem, int len)
+void gdb_memtox(GString *buf, const char *mem, int len)
{
char c;
@@ -764,7 +238,7 @@ static CPUState *find_cpu(uint32_t thread_id)
CPUState *cpu;
CPU_FOREACH(cpu) {
- if (cpu_gdb_index(cpu) == thread_id) {
+ if (gdb_get_cpu_index(cpu) == thread_id) {
return cpu;
}
}
@@ -818,7 +292,7 @@ static CPUState *gdb_next_attached_cpu(CPUState *cpu)
}
/* Return the first attached cpu */
-static CPUState *gdb_first_attached_cpu(void)
+CPUState *gdb_first_attached_cpu(void)
{
CPUState *cpu = first_cpu;
GDBProcess *process = gdb_get_cpu_process(cpu);
@@ -1024,7 +498,7 @@ static void gdb_process_breakpoint_remove_all(GDBProcess *p)
}
-static void gdb_set_cpu_pc(target_ulong pc)
+static void gdb_set_cpu_pc(vaddr pc)
{
CPUState *cpu = gdbserver_state.c_cpu;
@@ -1032,23 +506,16 @@ static void gdb_set_cpu_pc(target_ulong pc)
cpu_set_pc(cpu, pc);
}
-static void gdb_append_thread_id(CPUState *cpu, GString *buf)
+void gdb_append_thread_id(CPUState *cpu, GString *buf)
{
if (gdbserver_state.multiprocess) {
g_string_append_printf(buf, "p%02x.%02x",
- gdb_get_cpu_pid(cpu), cpu_gdb_index(cpu));
+ gdb_get_cpu_pid(cpu), gdb_get_cpu_index(cpu));
} else {
- g_string_append_printf(buf, "%02x", cpu_gdb_index(cpu));
+ g_string_append_printf(buf, "%02x", gdb_get_cpu_index(cpu));
}
}
-typedef enum GDBThreadIdKind {
- GDB_ONE_THREAD = 0,
- GDB_ALL_THREADS, /* One process, all threads */
- GDB_ALL_PROCESSES,
- GDB_READ_THREAD_ERR
-} GDBThreadIdKind;
-
static GDBThreadIdKind read_thread_id(const char *buf, const char **end_buf,
uint32_t *pid, uint32_t *tid)
{
@@ -1111,16 +578,7 @@ static int gdb_handle_vcont(const char *p)
GDBProcess *process;
CPUState *cpu;
GDBThreadIdKind kind;
-#ifdef CONFIG_USER_ONLY
- int max_cpus = 1; /* global variable max_cpus exists only in system mode */
-
- CPU_FOREACH(cpu) {
- max_cpus = max_cpus <= cpu->cpu_index ? cpu->cpu_index + 1 : max_cpus;
- }
-#else
- MachineState *ms = MACHINE(qdev_get_machine());
- unsigned int max_cpus = ms->smp.max_cpus;
-#endif
+ unsigned int max_cpus = gdb_get_max_cpus();
/* uninitialised CPUs stay 0 */
newstates = g_new0(char, max_cpus);
@@ -1229,20 +687,6 @@ out:
return res;
}
-typedef union GdbCmdVariant {
- const char *data;
- uint8_t opcode;
- unsigned long val_ul;
- unsigned long long val_ull;
- struct {
- GDBThreadIdKind kind;
- uint32_t pid;
- uint32_t tid;
- } thread_id;
-} GdbCmdVariant;
-
-#define get_param(p, i) (&g_array_index(p, GdbCmdVariant, i))
-
static const char *cmd_next_param(const char *param, const char delimiter)
{
static const char all_delimiters[] = ",;:=";
@@ -1409,7 +853,7 @@ static void run_cmd_parser(const char *data, const GdbCmdParseEntry *cmd)
/* In case there was an error during the command parsing we must
* send a NULL packet to indicate the command is not supported */
if (process_string_cmd(NULL, data, cmd, 1)) {
- put_packet("");
+ gdb_put_packet("");
}
}
@@ -1420,7 +864,7 @@ static void handle_detach(GArray *params, void *user_ctx)
if (gdbserver_state.multiprocess) {
if (!params->len) {
- put_packet("E22");
+ gdb_put_packet("E22");
return;
}
@@ -1441,10 +885,10 @@ static void handle_detach(GArray *params, void *user_ctx)
if (!gdbserver_state.c_cpu) {
/* No more process attached */
- gdb_syscall_mode = GDB_SYS_DISABLED;
+ gdb_disable_syscalls();
gdb_continue();
}
- put_packet("OK");
+ gdb_put_packet("OK");
}
static void handle_thread_alive(GArray *params, void *user_ctx)
@@ -1452,23 +896,23 @@ static void handle_thread_alive(GArray *params, void *user_ctx)
CPUState *cpu;
if (!params->len) {
- put_packet("E22");
+ gdb_put_packet("E22");
return;
}
if (get_param(params, 0)->thread_id.kind == GDB_READ_THREAD_ERR) {
- put_packet("E22");
+ gdb_put_packet("E22");
return;
}
cpu = gdb_get_cpu(get_param(params, 0)->thread_id.pid,
get_param(params, 0)->thread_id.tid);
if (!cpu) {
- put_packet("E22");
+ gdb_put_packet("E22");
return;
}
- put_packet("OK");
+ gdb_put_packet("OK");
}
static void handle_continue(GArray *params, void *user_ctx)
@@ -1505,24 +949,24 @@ static void handle_set_thread(GArray *params, void *user_ctx)
CPUState *cpu;
if (params->len != 2) {
- put_packet("E22");
+ gdb_put_packet("E22");
return;
}
if (get_param(params, 1)->thread_id.kind == GDB_READ_THREAD_ERR) {
- put_packet("E22");
+ gdb_put_packet("E22");
return;
}
if (get_param(params, 1)->thread_id.kind != GDB_ONE_THREAD) {
- put_packet("OK");
+ gdb_put_packet("OK");
return;
}
cpu = gdb_get_cpu(get_param(params, 1)->thread_id.pid,
get_param(params, 1)->thread_id.tid);
if (!cpu) {
- put_packet("E22");
+ gdb_put_packet("E22");
return;
}
@@ -1533,14 +977,14 @@ static void handle_set_thread(GArray *params, void *user_ctx)
switch (get_param(params, 0)->opcode) {
case 'c':
gdbserver_state.c_cpu = cpu;
- put_packet("OK");
+ gdb_put_packet("OK");
break;
case 'g':
gdbserver_state.g_cpu = cpu;
- put_packet("OK");
+ gdb_put_packet("OK");
break;
default:
- put_packet("E22");
+ gdb_put_packet("E22");
break;
}
}
@@ -1550,7 +994,7 @@ static void handle_insert_bp(GArray *params, void *user_ctx)
int res;
if (params->len != 3) {
- put_packet("E22");
+ gdb_put_packet("E22");
return;
}
@@ -1559,14 +1003,14 @@ static void handle_insert_bp(GArray *params, void *user_ctx)
get_param(params, 1)->val_ull,
get_param(params, 2)->val_ull);
if (res >= 0) {
- put_packet("OK");
+ gdb_put_packet("OK");
return;
} else if (res == -ENOSYS) {
- put_packet("");
+ gdb_put_packet("");
return;
}
- put_packet("E22");
+ gdb_put_packet("E22");
}
static void handle_remove_bp(GArray *params, void *user_ctx)
@@ -1574,7 +1018,7 @@ static void handle_remove_bp(GArray *params, void *user_ctx)
int res;
if (params->len != 3) {
- put_packet("E22");
+ gdb_put_packet("E22");
return;
}
@@ -1583,14 +1027,14 @@ static void handle_remove_bp(GArray *params, void *user_ctx)
get_param(params, 1)->val_ull,
get_param(params, 2)->val_ull);
if (res >= 0) {
- put_packet("OK");
+ gdb_put_packet("OK");
return;
} else if (res == -ENOSYS) {
- put_packet("");
+ gdb_put_packet("");
return;
}
- put_packet("E22");
+ gdb_put_packet("E22");
}
/*
@@ -1609,20 +1053,20 @@ static void handle_set_reg(GArray *params, void *user_ctx)
int reg_size;
if (!gdb_has_xml) {
- put_packet("");
+ gdb_put_packet("");
return;
}
if (params->len != 2) {
- put_packet("E22");
+ gdb_put_packet("E22");
return;
}
reg_size = strlen(get_param(params, 1)->data) / 2;
- hextomem(gdbserver_state.mem_buf, get_param(params, 1)->data, reg_size);
+ gdb_hextomem(gdbserver_state.mem_buf, get_param(params, 1)->data, reg_size);
gdb_write_register(gdbserver_state.g_cpu, gdbserver_state.mem_buf->data,
get_param(params, 0)->val_ull);
- put_packet("OK");
+ gdb_put_packet("OK");
}
static void handle_get_reg(GArray *params, void *user_ctx)
@@ -1630,12 +1074,12 @@ static void handle_get_reg(GArray *params, void *user_ctx)
int reg_size;
if (!gdb_has_xml) {
- put_packet("");
+ gdb_put_packet("");
return;
}
if (!params->len) {
- put_packet("E14");
+ gdb_put_packet("E14");
return;
}
@@ -1643,75 +1087,77 @@ static void handle_get_reg(GArray *params, void *user_ctx)
gdbserver_state.mem_buf,
get_param(params, 0)->val_ull);
if (!reg_size) {
- put_packet("E14");
+ gdb_put_packet("E14");
return;
} else {
g_byte_array_set_size(gdbserver_state.mem_buf, reg_size);
}
- memtohex(gdbserver_state.str_buf, gdbserver_state.mem_buf->data, reg_size);
- put_strbuf();
+ gdb_memtohex(gdbserver_state.str_buf,
+ gdbserver_state.mem_buf->data, reg_size);
+ gdb_put_strbuf();
}
static void handle_write_mem(GArray *params, void *user_ctx)
{
if (params->len != 3) {
- put_packet("E22");
+ gdb_put_packet("E22");
return;
}
- /* hextomem() reads 2*len bytes */
+ /* gdb_hextomem() reads 2*len bytes */
if (get_param(params, 1)->val_ull >
strlen(get_param(params, 2)->data) / 2) {
- put_packet("E22");
+ gdb_put_packet("E22");
return;
}
- hextomem(gdbserver_state.mem_buf, get_param(params, 2)->data,
- get_param(params, 1)->val_ull);
- if (target_memory_rw_debug(gdbserver_state.g_cpu,
- get_param(params, 0)->val_ull,
- gdbserver_state.mem_buf->data,
- gdbserver_state.mem_buf->len, true)) {
- put_packet("E14");
+ gdb_hextomem(gdbserver_state.mem_buf, get_param(params, 2)->data,
+ get_param(params, 1)->val_ull);
+ if (gdb_target_memory_rw_debug(gdbserver_state.g_cpu,
+ get_param(params, 0)->val_ull,
+ gdbserver_state.mem_buf->data,
+ gdbserver_state.mem_buf->len, true)) {
+ gdb_put_packet("E14");
return;
}
- put_packet("OK");
+ gdb_put_packet("OK");
}
static void handle_read_mem(GArray *params, void *user_ctx)
{
if (params->len != 2) {
- put_packet("E22");
+ gdb_put_packet("E22");
return;
}
- /* memtohex() doubles the required space */
+ /* gdb_memtohex() doubles the required space */
if (get_param(params, 1)->val_ull > MAX_PACKET_LENGTH / 2) {
- put_packet("E22");
+ gdb_put_packet("E22");
return;
}
g_byte_array_set_size(gdbserver_state.mem_buf,
get_param(params, 1)->val_ull);
- if (target_memory_rw_debug(gdbserver_state.g_cpu,
- get_param(params, 0)->val_ull,
- gdbserver_state.mem_buf->data,
- gdbserver_state.mem_buf->len, false)) {
- put_packet("E14");
+ if (gdb_target_memory_rw_debug(gdbserver_state.g_cpu,
+ get_param(params, 0)->val_ull,
+ gdbserver_state.mem_buf->data,
+ gdbserver_state.mem_buf->len, false)) {
+ gdb_put_packet("E14");
return;
}
- memtohex(gdbserver_state.str_buf, gdbserver_state.mem_buf->data,
+ gdb_memtohex(gdbserver_state.str_buf, gdbserver_state.mem_buf->data,
gdbserver_state.mem_buf->len);
- put_strbuf();
+ gdb_put_strbuf();
}
static void handle_write_all_regs(GArray *params, void *user_ctx)
{
- target_ulong addr, len;
+ int reg_id;
+ size_t len;
uint8_t *registers;
int reg_size;
@@ -1721,94 +1167,42 @@ static void handle_write_all_regs(GArray *params, void *user_ctx)
cpu_synchronize_state(gdbserver_state.g_cpu);
len = strlen(get_param(params, 0)->data) / 2;
- hextomem(gdbserver_state.mem_buf, get_param(params, 0)->data, len);
+ gdb_hextomem(gdbserver_state.mem_buf, get_param(params, 0)->data, len);
registers = gdbserver_state.mem_buf->data;
- for (addr = 0; addr < gdbserver_state.g_cpu->gdb_num_g_regs && len > 0;
- addr++) {
- reg_size = gdb_write_register(gdbserver_state.g_cpu, registers, addr);
+ for (reg_id = 0;
+ reg_id < gdbserver_state.g_cpu->gdb_num_g_regs && len > 0;
+ reg_id++) {
+ reg_size = gdb_write_register(gdbserver_state.g_cpu, registers, reg_id);
len -= reg_size;
registers += reg_size;
}
- put_packet("OK");
+ gdb_put_packet("OK");
}
static void handle_read_all_regs(GArray *params, void *user_ctx)
{
- target_ulong addr, len;
+ int reg_id;
+ size_t len;
cpu_synchronize_state(gdbserver_state.g_cpu);
g_byte_array_set_size(gdbserver_state.mem_buf, 0);
len = 0;
- for (addr = 0; addr < gdbserver_state.g_cpu->gdb_num_g_regs; addr++) {
+ for (reg_id = 0; reg_id < gdbserver_state.g_cpu->gdb_num_g_regs; reg_id++) {
len += gdb_read_register(gdbserver_state.g_cpu,
gdbserver_state.mem_buf,
- addr);
+ reg_id);
}
g_assert(len == gdbserver_state.mem_buf->len);
- memtohex(gdbserver_state.str_buf, gdbserver_state.mem_buf->data, len);
- put_strbuf();
+ gdb_memtohex(gdbserver_state.str_buf, gdbserver_state.mem_buf->data, len);
+ 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') {
- put_packet("T02");
- return;
- }
-
- gdb_continue();
-}
static void handle_step(GArray *params, void *user_ctx)
{
if (params->len) {
- gdb_set_cpu_pc((target_ulong)get_param(params, 0)->val_ull);
+ gdb_set_cpu_pc(get_param(params, 0)->val_ull);
}
cpu_single_step(gdbserver_state.c_cpu, gdbserver_state.sstep_flags);
@@ -1817,8 +1211,8 @@ static void handle_step(GArray *params, void *user_ctx)
static void handle_backward(GArray *params, void *user_ctx)
{
- if (!stub_can_reverse()) {
- put_packet("E22");
+ if (!gdb_can_reverse()) {
+ gdb_put_packet("E22");
}
if (params->len == 1) {
switch (get_param(params, 0)->opcode) {
@@ -1826,26 +1220,26 @@ static void handle_backward(GArray *params, void *user_ctx)
if (replay_reverse_step()) {
gdb_continue();
} else {
- put_packet("E14");
+ gdb_put_packet("E14");
}
return;
case 'c':
if (replay_reverse_continue()) {
gdb_continue();
} else {
- put_packet("E14");
+ gdb_put_packet("E14");
}
return;
}
}
/* Default invalid command */
- put_packet("");
+ gdb_put_packet("");
}
static void handle_v_cont_query(GArray *params, void *user_ctx)
{
- put_packet("vCont;c;C;s;S");
+ gdb_put_packet("vCont;c;C;s;S");
}
static void handle_v_cont(GArray *params, void *user_ctx)
@@ -1858,9 +1252,9 @@ static void handle_v_cont(GArray *params, void *user_ctx)
res = gdb_handle_vcont(get_param(params, 0)->data);
if ((res == -EINVAL) || (res == -ERANGE)) {
- put_packet("E22");
+ gdb_put_packet("E22");
} else if (res) {
- put_packet("");
+ gdb_put_packet("");
}
}
@@ -1892,13 +1286,13 @@ static void handle_v_attach(GArray *params, void *user_ctx)
gdb_append_thread_id(cpu, gdbserver_state.str_buf);
g_string_append_c(gdbserver_state.str_buf, ';');
cleanup:
- put_strbuf();
+ gdb_put_strbuf();
}
static void handle_v_kill(GArray *params, void *user_ctx)
{
/* Kill the target */
- put_packet("OK");
+ gdb_put_packet("OK");
error_report("QEMU: Terminated via GDBstub");
gdb_exit(0);
exit(0);
@@ -1939,7 +1333,7 @@ static void handle_v_commands(GArray *params, void *user_ctx)
if (process_string_cmd(NULL, get_param(params, 0)->data,
gdb_v_commands_table,
ARRAY_SIZE(gdb_v_commands_table))) {
- put_packet("");
+ gdb_put_packet("");
}
}
@@ -1957,7 +1351,7 @@ static void handle_query_qemu_sstepbits(GArray *params, void *user_ctx)
SSTEP_NOTIMER);
}
- put_strbuf();
+ gdb_put_strbuf();
}
static void handle_set_qemu_sstep(GArray *params, void *user_ctx)
@@ -1971,19 +1365,19 @@ static void handle_set_qemu_sstep(GArray *params, void *user_ctx)
new_sstep_flags = get_param(params, 0)->val_ul;
if (new_sstep_flags & ~gdbserver_state.supported_sstep_flags) {
- put_packet("E22");
+ gdb_put_packet("E22");
return;
}
gdbserver_state.sstep_flags = new_sstep_flags;
- put_packet("OK");
+ gdb_put_packet("OK");
}
static void handle_query_qemu_sstep(GArray *params, void *user_ctx)
{
g_string_printf(gdbserver_state.str_buf, "0x%x",
gdbserver_state.sstep_flags);
- put_strbuf();
+ gdb_put_strbuf();
}
static void handle_query_curr_tid(GArray *params, void *user_ctx)
@@ -2000,19 +1394,19 @@ static void handle_query_curr_tid(GArray *params, void *user_ctx)
cpu = get_first_cpu_in_process(process);
g_string_assign(gdbserver_state.str_buf, "QC");
gdb_append_thread_id(cpu, gdbserver_state.str_buf);
- put_strbuf();
+ gdb_put_strbuf();
}
static void handle_query_threads(GArray *params, void *user_ctx)
{
if (!gdbserver_state.query_cpu) {
- put_packet("l");
+ gdb_put_packet("l");
return;
}
g_string_assign(gdbserver_state.str_buf, "m");
gdb_append_thread_id(gdbserver_state.query_cpu, gdbserver_state.str_buf);
- put_strbuf();
+ gdb_put_strbuf();
gdbserver_state.query_cpu = gdb_next_attached_cpu(gdbserver_state.query_cpu);
}
@@ -2029,7 +1423,7 @@ static void handle_query_thread_extra(GArray *params, void *user_ctx)
if (!params->len ||
get_param(params, 0)->thread_id.kind == GDB_READ_THREAD_ERR) {
- put_packet("E22");
+ gdb_put_packet("E22");
return;
}
@@ -2054,51 +1448,9 @@ static void handle_query_thread_extra(GArray *params, void *user_ctx)
cpu->halted ? "halted " : "running");
}
trace_gdbstub_op_extra_info(rs->str);
- memtohex(gdbserver_state.str_buf, (uint8_t *)rs->str, rs->len);
- put_strbuf();
-}
-
-#ifdef CONFIG_USER_ONLY
-static void handle_query_offsets(GArray *params, void *user_ctx)
-{
- TaskState *ts;
-
- ts = gdbserver_state.c_cpu->opaque;
- g_string_printf(gdbserver_state.str_buf,
- "Text=" TARGET_ABI_FMT_lx
- ";Data=" TARGET_ABI_FMT_lx
- ";Bss=" TARGET_ABI_FMT_lx,
- ts->info->code_offset,
- ts->info->data_offset,
- ts->info->data_offset);
- put_strbuf();
-}
-#else
-static void handle_query_rcmd(GArray *params, void *user_ctx)
-{
- const guint8 zero = 0;
- int len;
-
- if (!params->len) {
- put_packet("E22");
- return;
- }
-
- len = strlen(get_param(params, 0)->data);
- if (len % 2) {
- put_packet("E01");
- return;
- }
-
- g_assert(gdbserver_state.mem_buf->len == 0);
- len = len / 2;
- hextomem(gdbserver_state.mem_buf, get_param(params, 0)->data, len);
- g_byte_array_append(gdbserver_state.mem_buf, &zero, 1);
- qemu_chr_be_write(gdbserver_state.mon_chr, gdbserver_state.mem_buf->data,
- gdbserver_state.mem_buf->len);
- put_packet("OK");
+ gdb_memtohex(gdbserver_state.str_buf, (uint8_t *)rs->str, rs->len);
+ gdb_put_strbuf();
}
-#endif
static void handle_query_supported(GArray *params, void *user_ctx)
{
@@ -2110,7 +1462,7 @@ static void handle_query_supported(GArray *params, void *user_ctx)
g_string_append(gdbserver_state.str_buf, ";qXfer:features:read+");
}
- if (stub_can_reverse()) {
+ if (gdb_can_reverse()) {
g_string_append(gdbserver_state.str_buf,
";ReverseStep+;ReverseContinue+");
}
@@ -2127,7 +1479,7 @@ static void handle_query_supported(GArray *params, void *user_ctx)
}
g_string_append(gdbserver_state.str_buf, ";vContSupported+;multiprocess+");
- put_strbuf();
+ gdb_put_strbuf();
}
static void handle_query_xfer_features(GArray *params, void *user_ctx)
@@ -2139,14 +1491,14 @@ static void handle_query_xfer_features(GArray *params, void *user_ctx)
const char *p;
if (params->len < 3) {
- put_packet("E22");
+ gdb_put_packet("E22");
return;
}
process = gdb_get_cpu_process(gdbserver_state.g_cpu);
cc = CPU_GET_CLASS(gdbserver_state.g_cpu);
if (!cc->gdb_core_xml_file) {
- put_packet("");
+ gdb_put_packet("");
return;
}
@@ -2154,7 +1506,7 @@ static void handle_query_xfer_features(GArray *params, void *user_ctx)
p = get_param(params, 0)->data;
xml = get_feature_xml(p, &p, process);
if (!xml) {
- put_packet("E00");
+ gdb_put_packet("E00");
return;
}
@@ -2162,7 +1514,7 @@ static void handle_query_xfer_features(GArray *params, void *user_ctx)
len = get_param(params, 2)->val_ul;
total_len = strlen(xml);
if (addr > total_len) {
- put_packet("E00");
+ gdb_put_packet("E00");
return;
}
@@ -2172,67 +1524,15 @@ static void handle_query_xfer_features(GArray *params, void *user_ctx)
if (len < total_len - addr) {
g_string_assign(gdbserver_state.str_buf, "m");
- memtox(gdbserver_state.str_buf, xml + addr, len);
- } else {
- g_string_assign(gdbserver_state.str_buf, "l");
- memtox(gdbserver_state.str_buf, xml + addr, total_len - addr);
- }
-
- put_packet_binary(gdbserver_state.str_buf->str,
- gdbserver_state.str_buf->len, true);
-}
-
-#if defined(CONFIG_USER_ONLY) && defined(CONFIG_LINUX_USER)
-static void handle_query_xfer_auxv(GArray *params, void *user_ctx)
-{
- TaskState *ts;
- unsigned long offset, len, saved_auxv, auxv_len;
-
- if (params->len < 2) {
- put_packet("E22");
- return;
- }
-
- offset = get_param(params, 0)->val_ul;
- len = get_param(params, 1)->val_ul;
- ts = gdbserver_state.c_cpu->opaque;
- saved_auxv = ts->info->saved_auxv;
- auxv_len = ts->info->auxv_len;
-
- if (offset >= auxv_len) {
- put_packet("E00");
- return;
- }
-
- if (len > (MAX_PACKET_LENGTH - 5) / 2) {
- len = (MAX_PACKET_LENGTH - 5) / 2;
- }
-
- if (len < auxv_len - offset) {
- g_string_assign(gdbserver_state.str_buf, "m");
+ gdb_memtox(gdbserver_state.str_buf, xml + addr, len);
} else {
g_string_assign(gdbserver_state.str_buf, "l");
- len = auxv_len - offset;
+ gdb_memtox(gdbserver_state.str_buf, xml + addr, total_len - addr);
}
- g_byte_array_set_size(gdbserver_state.mem_buf, len);
- if (target_memory_rw_debug(gdbserver_state.g_cpu, saved_auxv + offset,
- gdbserver_state.mem_buf->data, len, false)) {
- put_packet("E14");
- return;
- }
-
- memtox(gdbserver_state.str_buf,
- (const char *)gdbserver_state.mem_buf->data, len);
- put_packet_binary(gdbserver_state.str_buf->str,
+ gdb_put_packet_binary(gdbserver_state.str_buf->str,
gdbserver_state.str_buf->len, true);
}
-#endif
-
-static void handle_query_attached(GArray *params, void *user_ctx)
-{
- put_packet(GDB_ATTACHED);
-}
static void handle_query_qemu_supported(GArray *params, void *user_ctx)
{
@@ -2240,32 +1540,8 @@ static void handle_query_qemu_supported(GArray *params, void *user_ctx)
#ifndef CONFIG_USER_ONLY
g_string_append(gdbserver_state.str_buf, ";PhyMemMode");
#endif
- put_strbuf();
-}
-
-#ifndef CONFIG_USER_ONLY
-static void handle_query_qemu_phy_mem_mode(GArray *params,
- void *user_ctx)
-{
- g_string_printf(gdbserver_state.str_buf, "%d", phy_memory_mode);
- put_strbuf();
-}
-
-static void handle_set_qemu_phy_mem_mode(GArray *params, void *user_ctx)
-{
- if (!params->len) {
- put_packet("E22");
- return;
- }
-
- if (!get_param(params, 0)->val_ul) {
- phy_memory_mode = 0;
- } else {
- phy_memory_mode = 1;
- }
- put_packet("OK");
+ gdb_put_strbuf();
}
-#endif
static const GdbCmdParseEntry gdb_gen_query_set_common_table[] = {
/* Order is important if has same prefix */
@@ -2306,12 +1582,12 @@ static const GdbCmdParseEntry gdb_gen_query_table[] = {
},
#ifdef CONFIG_USER_ONLY
{
- .handler = handle_query_offsets,
+ .handler = gdb_handle_query_offsets,
.cmd = "Offsets",
},
#else
{
- .handler = handle_query_rcmd,
+ .handler = gdb_handle_query_rcmd,
.cmd = "Rcmd,",
.cmd_startswith = 1,
.schema = "s0"
@@ -2334,21 +1610,21 @@ static const GdbCmdParseEntry gdb_gen_query_table[] = {
.cmd_startswith = 1,
.schema = "s:l,l0"
},
-#if defined(CONFIG_USER_ONLY) && defined(CONFIG_LINUX_USER)
+#if defined(CONFIG_USER_ONLY) && defined(CONFIG_LINUX)
{
- .handler = handle_query_xfer_auxv,
+ .handler = gdb_handle_query_xfer_auxv,
.cmd = "Xfer:auxv:read::",
.cmd_startswith = 1,
.schema = "l,l0"
},
#endif
{
- .handler = handle_query_attached,
+ .handler = gdb_handle_query_attached,
.cmd = "Attached:",
.cmd_startswith = 1
},
{
- .handler = handle_query_attached,
+ .handler = gdb_handle_query_attached,
.cmd = "Attached",
},
{
@@ -2357,7 +1633,7 @@ static const GdbCmdParseEntry gdb_gen_query_table[] = {
},
#ifndef CONFIG_USER_ONLY
{
- .handler = handle_query_qemu_phy_mem_mode,
+ .handler = gdb_handle_query_qemu_phy_mem_mode,
.cmd = "qemu.PhyMemMode",
},
#endif
@@ -2373,7 +1649,7 @@ static const GdbCmdParseEntry gdb_gen_set_table[] = {
},
#ifndef CONFIG_USER_ONLY
{
- .handler = handle_set_qemu_phy_mem_mode,
+ .handler = gdb_handle_set_qemu_phy_mem_mode,
.cmd = "qemu.PhyMemMode:",
.cmd_startswith = 1,
.schema = "l0"
@@ -2396,7 +1672,7 @@ static void handle_gen_query(GArray *params, void *user_ctx)
if (process_string_cmd(NULL, get_param(params, 0)->data,
gdb_gen_query_table,
ARRAY_SIZE(gdb_gen_query_table))) {
- put_packet("");
+ gdb_put_packet("");
}
}
@@ -2415,7 +1691,7 @@ static void handle_gen_set(GArray *params, void *user_ctx)
if (process_string_cmd(NULL, get_param(params, 0)->data,
gdb_gen_set_table,
ARRAY_SIZE(gdb_gen_set_table))) {
- put_packet("");
+ gdb_put_packet("");
}
}
@@ -2424,7 +1700,7 @@ static void handle_target_halt(GArray *params, void *user_ctx)
g_string_printf(gdbserver_state.str_buf, "T%02xthread:", GDB_SIGNAL_TRAP);
gdb_append_thread_id(gdbserver_state.c_cpu, gdbserver_state.str_buf);
g_string_append_c(gdbserver_state.str_buf, ';');
- put_strbuf();
+ gdb_put_strbuf();
/*
* Remove all the breakpoints when this query is issued,
* because gdb is doing an initial connect and the state
@@ -2441,7 +1717,7 @@ static int gdb_handle_packet(const char *line_buf)
switch (line_buf[0]) {
case '!':
- put_packet("OK");
+ gdb_put_packet("OK");
break;
case '?':
{
@@ -2527,7 +1803,7 @@ static int gdb_handle_packet(const char *line_buf)
case 'F':
{
static const GdbCmdParseEntry file_io_cmd_desc = {
- .handler = handle_file_io,
+ .handler = gdb_handle_file_io,
.cmd = "F",
.cmd_startswith = 1,
.schema = "L,L,o0"
@@ -2668,7 +1944,7 @@ static int gdb_handle_packet(const char *line_buf)
break;
default:
/* put empty packet */
- put_packet("");
+ gdb_put_packet("");
break;
}
@@ -2695,183 +1971,7 @@ void gdb_set_stop_cpu(CPUState *cpu)
gdbserver_state.g_cpu = cpu;
}
-#ifndef CONFIG_USER_ONLY
-static void gdb_vm_state_change(void *opaque, bool running, RunState state)
-{
- CPUState *cpu = gdbserver_state.c_cpu;
- g_autoptr(GString) buf = g_string_new(NULL);
- g_autoptr(GString) tid = g_string_new(NULL);
- const char *type;
- int ret;
-
- if (running || gdbserver_state.state == RS_INACTIVE) {
- return;
- }
- /* Is there a GDB syscall waiting to be sent? */
- if (gdbserver_state.current_syscall_cb) {
- put_packet(gdbserver_state.syscall_buf);
- return;
- }
-
- if (cpu == NULL) {
- /* No process attached */
- return;
- }
-
- gdb_append_thread_id(cpu, tid);
-
- switch (state) {
- case RUN_STATE_DEBUG:
- if (cpu->watchpoint_hit) {
- switch (cpu->watchpoint_hit->flags & BP_MEM_ACCESS) {
- case BP_MEM_READ:
- type = "r";
- break;
- case BP_MEM_ACCESS:
- type = "a";
- break;
- default:
- type = "";
- break;
- }
- trace_gdbstub_hit_watchpoint(type, cpu_gdb_index(cpu),
- (target_ulong)cpu->watchpoint_hit->vaddr);
- g_string_printf(buf, "T%02xthread:%s;%swatch:" TARGET_FMT_lx ";",
- GDB_SIGNAL_TRAP, tid->str, type,
- (target_ulong)cpu->watchpoint_hit->vaddr);
- cpu->watchpoint_hit = NULL;
- goto send_packet;
- } else {
- trace_gdbstub_hit_break();
- }
- tb_flush(cpu);
- ret = GDB_SIGNAL_TRAP;
- break;
- case RUN_STATE_PAUSED:
- trace_gdbstub_hit_paused();
- ret = GDB_SIGNAL_INT;
- break;
- case RUN_STATE_SHUTDOWN:
- trace_gdbstub_hit_shutdown();
- ret = GDB_SIGNAL_QUIT;
- break;
- case RUN_STATE_IO_ERROR:
- trace_gdbstub_hit_io_error();
- ret = GDB_SIGNAL_IO;
- break;
- case RUN_STATE_WATCHDOG:
- trace_gdbstub_hit_watchdog();
- ret = GDB_SIGNAL_ALRM;
- break;
- case RUN_STATE_INTERNAL_ERROR:
- trace_gdbstub_hit_internal_error();
- ret = GDB_SIGNAL_ABRT;
- break;
- case RUN_STATE_SAVE_VM:
- case RUN_STATE_RESTORE_VM:
- return;
- case RUN_STATE_FINISH_MIGRATE:
- ret = GDB_SIGNAL_XCPU;
- break;
- default:
- trace_gdbstub_hit_unknown(state);
- ret = GDB_SIGNAL_UNKNOWN;
- break;
- }
- gdb_set_stop_cpu(cpu);
- g_string_printf(buf, "T%02xthread:%s;", ret, tid->str);
-
-send_packet:
- put_packet(buf->str);
-
- /* disable single step if it was enabled */
- cpu_single_step(cpu, 0);
-}
-#endif
-
-/* 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
- 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);
-}
-
-static void gdb_read_byte(uint8_t ch)
+void gdb_read_byte(uint8_t ch)
{
uint8_t reply;
@@ -2881,7 +1981,7 @@ static void gdb_read_byte(uint8_t ch)
of a new command then abandon the previous response. */
if (ch == '-') {
trace_gdbstub_err_got_nack();
- put_buffer(gdbserver_state.last_packet->data,
+ gdb_put_buffer(gdbserver_state.last_packet->data,
gdbserver_state.last_packet->len);
} else if (ch == '+') {
trace_gdbstub_io_got_ack();
@@ -3003,12 +2103,12 @@ static void gdb_read_byte(uint8_t ch)
trace_gdbstub_err_checksum_incorrect(gdbserver_state.line_sum, gdbserver_state.line_csum);
/* send NAK reply */
reply = '-';
- put_buffer(&reply, 1);
+ gdb_put_buffer(&reply, 1);
gdbserver_state.state = RS_IDLE;
} else {
/* send ACK reply */
reply = '+';
- put_buffer(&reply, 1);
+ gdb_put_buffer(&reply, 1);
gdbserver_state.state = gdb_handle_packet(gdbserver_state.line_buf);
}
break;
@@ -3018,39 +2118,12 @@ static void gdb_read_byte(uint8_t ch)
}
}
-/* Tell the remote gdb that the process has exited. */
-void gdb_exit(int code)
-{
- char buf[4];
-
- if (!gdbserver_state.init) {
- return;
- }
-#ifdef CONFIG_USER_ONLY
- if (gdbserver_state.socket_path) {
- unlink(gdbserver_state.socket_path);
- }
- if (gdbserver_state.fd < 0) {
- return;
- }
-#endif
-
- trace_gdbstub_op_exiting((uint8_t)code);
-
- snprintf(buf, sizeof(buf), "W%02x", (uint8_t)code);
- put_packet(buf);
-
-#ifndef CONFIG_USER_ONLY
- qemu_chr_fe_deinit(&gdbserver_state.chr, true);
-#endif
-}
-
/*
* Create the process that will contain all the "orphan" CPUs (that are not
* part of a CPU cluster). Note that if this process contains no CPUs, it won't
* be attachable and thus will be invisible to the user.
*/
-static void create_default_process(GDBState *s)
+void gdb_create_default_process(GDBState *s)
{
GDBProcess *process;
int max_pid = 0;
@@ -3070,447 +2143,3 @@ static void create_default_process(GDBState *s)
process->target_xml[0] = '\0';
}
-#ifdef CONFIG_USER_ONLY
-int
-gdb_handlesig(CPUState *cpu, int sig)
-{
- char buf[256];
- int n;
-
- if (!gdbserver_state.init || gdbserver_state.fd < 0) {
- return sig;
- }
-
- /* disable single step if it was enabled */
- cpu_single_step(cpu, 0);
- tb_flush(cpu);
-
- if (sig != 0) {
- gdb_set_stop_cpu(cpu);
- g_string_printf(gdbserver_state.str_buf,
- "T%02xthread:", target_signal_to_gdb(sig));
- gdb_append_thread_id(cpu, gdbserver_state.str_buf);
- g_string_append_c(gdbserver_state.str_buf, ';');
- put_strbuf();
- }
- /* put_packet() might have detected that the peer terminated the
- connection. */
- if (gdbserver_state.fd < 0) {
- return sig;
- }
-
- sig = 0;
- gdbserver_state.state = RS_IDLE;
- gdbserver_state.running_state = 0;
- while (gdbserver_state.running_state == 0) {
- n = read(gdbserver_state.fd, buf, 256);
- if (n > 0) {
- int i;
-
- for (i = 0; i < n; i++) {
- gdb_read_byte(buf[i]);
- }
- } else {
- /* XXX: Connection closed. Should probably wait for another
- connection before continuing. */
- if (n == 0) {
- close(gdbserver_state.fd);
- }
- gdbserver_state.fd = -1;
- return sig;
- }
- }
- sig = gdbserver_state.signal;
- gdbserver_state.signal = 0;
- return sig;
-}
-
-/* Tell the remote gdb that the process has exited due to SIG. */
-void gdb_signalled(CPUArchState *env, int sig)
-{
- char buf[4];
-
- if (!gdbserver_state.init || gdbserver_state.fd < 0) {
- return;
- }
-
- snprintf(buf, sizeof(buf), "X%02x", target_signal_to_gdb(sig));
- put_packet(buf);
-}
-
-static void gdb_accept_init(int fd)
-{
- init_gdbserver_state();
- create_default_process(&gdbserver_state);
- gdbserver_state.processes[0].attached = true;
- gdbserver_state.c_cpu = gdb_first_attached_cpu();
- gdbserver_state.g_cpu = gdbserver_state.c_cpu;
- gdbserver_state.fd = fd;
- gdb_has_xml = false;
-}
-
-static bool gdb_accept_socket(int gdb_fd)
-{
- int fd;
-
- for(;;) {
- fd = accept(gdb_fd, NULL, NULL);
- if (fd < 0 && errno != EINTR) {
- perror("accept socket");
- return false;
- } else if (fd >= 0) {
- qemu_set_cloexec(fd);
- break;
- }
- }
-
- gdb_accept_init(fd);
- return true;
-}
-
-static int gdbserver_open_socket(const char *path)
-{
- struct sockaddr_un sockaddr = {};
- int fd, ret;
-
- fd = socket(AF_UNIX, SOCK_STREAM, 0);
- if (fd < 0) {
- perror("create socket");
- return -1;
- }
-
- sockaddr.sun_family = AF_UNIX;
- pstrcpy(sockaddr.sun_path, sizeof(sockaddr.sun_path) - 1, path);
- ret = bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr));
- if (ret < 0) {
- perror("bind socket");
- close(fd);
- return -1;
- }
- ret = listen(fd, 1);
- if (ret < 0) {
- perror("listen socket");
- close(fd);
- return -1;
- }
-
- return fd;
-}
-
-static bool gdb_accept_tcp(int gdb_fd)
-{
- struct sockaddr_in sockaddr = {};
- socklen_t len;
- int fd;
-
- for(;;) {
- len = sizeof(sockaddr);
- fd = accept(gdb_fd, (struct sockaddr *)&sockaddr, &len);
- if (fd < 0 && errno != EINTR) {
- perror("accept");
- return false;
- } else if (fd >= 0) {
- qemu_set_cloexec(fd);
- break;
- }
- }
-
- /* set short latency */
- if (socket_set_nodelay(fd)) {
- perror("setsockopt");
- close(fd);
- return false;
- }
-
- gdb_accept_init(fd);
- return true;
-}
-
-static int gdbserver_open_port(int port)
-{
- struct sockaddr_in sockaddr;
- int fd, ret;
-
- fd = socket(PF_INET, SOCK_STREAM, 0);
- if (fd < 0) {
- perror("socket");
- return -1;
- }
- qemu_set_cloexec(fd);
-
- socket_set_fast_reuse(fd);
-
- sockaddr.sin_family = AF_INET;
- sockaddr.sin_port = htons(port);
- sockaddr.sin_addr.s_addr = 0;
- ret = bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr));
- if (ret < 0) {
- perror("bind");
- close(fd);
- return -1;
- }
- ret = listen(fd, 1);
- if (ret < 0) {
- perror("listen");
- close(fd);
- return -1;
- }
-
- return fd;
-}
-
-int gdbserver_start(const char *port_or_path)
-{
- int port = g_ascii_strtoull(port_or_path, NULL, 10);
- int gdb_fd;
-
- if (port > 0) {
- gdb_fd = gdbserver_open_port(port);
- } else {
- gdb_fd = gdbserver_open_socket(port_or_path);
- }
-
- if (gdb_fd < 0) {
- return -1;
- }
-
- if (port > 0 && gdb_accept_tcp(gdb_fd)) {
- return 0;
- } else if (gdb_accept_socket(gdb_fd)) {
- gdbserver_state.socket_path = g_strdup(port_or_path);
- return 0;
- }
-
- /* gone wrong */
- close(gdb_fd);
- return -1;
-}
-
-/* Disable gdb stub for child processes. */
-void gdbserver_fork(CPUState *cpu)
-{
- if (!gdbserver_state.init || gdbserver_state.fd < 0) {
- return;
- }
- close(gdbserver_state.fd);
- gdbserver_state.fd = -1;
- cpu_breakpoint_remove_all(cpu, BP_GDB);
- cpu_watchpoint_remove_all(cpu, BP_GDB);
-}
-#else
-static int gdb_chr_can_receive(void *opaque)
-{
- /* We can handle an arbitrarily large amount of data.
- Pick the maximum packet size, which is as good as anything. */
- return MAX_PACKET_LENGTH;
-}
-
-static void gdb_chr_receive(void *opaque, const uint8_t *buf, int size)
-{
- int i;
-
- for (i = 0; i < size; i++) {
- gdb_read_byte(buf[i]);
- }
-}
-
-static void gdb_chr_event(void *opaque, QEMUChrEvent event)
-{
- int i;
- GDBState *s = (GDBState *) opaque;
-
- switch (event) {
- case CHR_EVENT_OPENED:
- /* Start with first process attached, others detached */
- for (i = 0; i < s->process_num; i++) {
- s->processes[i].attached = !i;
- }
-
- s->c_cpu = gdb_first_attached_cpu();
- s->g_cpu = s->c_cpu;
-
- vm_stop(RUN_STATE_PAUSED);
- replay_gdb_attached();
- gdb_has_xml = false;
- break;
- default:
- break;
- }
-}
-
-static int gdb_monitor_write(Chardev *chr, const uint8_t *buf, int len)
-{
- g_autoptr(GString) hex_buf = g_string_new("O");
- memtohex(hex_buf, buf, len);
- put_packet(hex_buf->str);
- return len;
-}
-
-#ifndef _WIN32
-static void gdb_sigterm_handler(int signal)
-{
- if (runstate_is_running()) {
- vm_stop(RUN_STATE_PAUSED);
- }
-}
-#endif
-
-static void gdb_monitor_open(Chardev *chr, ChardevBackend *backend,
- bool *be_opened, Error **errp)
-{
- *be_opened = false;
-}
-
-static void char_gdb_class_init(ObjectClass *oc, void *data)
-{
- ChardevClass *cc = CHARDEV_CLASS(oc);
-
- cc->internal = true;
- cc->open = gdb_monitor_open;
- cc->chr_write = gdb_monitor_write;
-}
-
-#define TYPE_CHARDEV_GDB "chardev-gdb"
-
-static const TypeInfo char_gdb_type_info = {
- .name = TYPE_CHARDEV_GDB,
- .parent = TYPE_CHARDEV,
- .class_init = char_gdb_class_init,
-};
-
-static int find_cpu_clusters(Object *child, void *opaque)
-{
- if (object_dynamic_cast(child, TYPE_CPU_CLUSTER)) {
- GDBState *s = (GDBState *) opaque;
- CPUClusterState *cluster = CPU_CLUSTER(child);
- GDBProcess *process;
-
- s->processes = g_renew(GDBProcess, s->processes, ++s->process_num);
-
- process = &s->processes[s->process_num - 1];
-
- /*
- * GDB process IDs -1 and 0 are reserved. To avoid subtle errors at
- * runtime, we enforce here that the machine does not use a cluster ID
- * that would lead to PID 0.
- */
- assert(cluster->cluster_id != UINT32_MAX);
- process->pid = cluster->cluster_id + 1;
- process->attached = false;
- process->target_xml[0] = '\0';
-
- return 0;
- }
-
- return object_child_foreach(child, find_cpu_clusters, opaque);
-}
-
-static int pid_order(const void *a, const void *b)
-{
- GDBProcess *pa = (GDBProcess *) a;
- GDBProcess *pb = (GDBProcess *) b;
-
- if (pa->pid < pb->pid) {
- return -1;
- } else if (pa->pid > pb->pid) {
- return 1;
- } else {
- return 0;
- }
-}
-
-static void create_processes(GDBState *s)
-{
- object_child_foreach(object_get_root(), find_cpu_clusters, s);
-
- if (gdbserver_state.processes) {
- /* Sort by PID */
- qsort(gdbserver_state.processes, gdbserver_state.process_num, sizeof(gdbserver_state.processes[0]), pid_order);
- }
-
- create_default_process(s);
-}
-
-int gdbserver_start(const char *device)
-{
- trace_gdbstub_op_start(device);
-
- char gdbstub_device_name[128];
- Chardev *chr = NULL;
- Chardev *mon_chr;
-
- if (!first_cpu) {
- error_report("gdbstub: meaningless to attach gdb to a "
- "machine without any CPU.");
- return -1;
- }
-
- if (!gdb_supports_guest_debug()) {
- error_report("gdbstub: current accelerator doesn't support guest debugging");
- return -1;
- }
-
- if (!device)
- return -1;
- if (strcmp(device, "none") != 0) {
- if (strstart(device, "tcp:", NULL)) {
- /* enforce required TCP attributes */
- snprintf(gdbstub_device_name, sizeof(gdbstub_device_name),
- "%s,wait=off,nodelay=on,server=on", device);
- device = gdbstub_device_name;
- }
-#ifndef _WIN32
- else if (strcmp(device, "stdio") == 0) {
- struct sigaction act;
-
- memset(&act, 0, sizeof(act));
- act.sa_handler = gdb_sigterm_handler;
- sigaction(SIGINT, &act, NULL);
- }
-#endif
- /*
- * FIXME: it's a bit weird to allow using a mux chardev here
- * and implicitly setup a monitor. We may want to break this.
- */
- chr = qemu_chr_new_noreplay("gdb", device, true, NULL);
- if (!chr)
- return -1;
- }
-
- if (!gdbserver_state.init) {
- init_gdbserver_state();
-
- qemu_add_vm_change_state_handler(gdb_vm_state_change, NULL);
-
- /* Initialize a monitor terminal for gdb */
- mon_chr = qemu_chardev_new(NULL, TYPE_CHARDEV_GDB,
- NULL, NULL, &error_abort);
- monitor_init_hmp(mon_chr, false, &error_abort);
- } else {
- qemu_chr_fe_deinit(&gdbserver_state.chr, true);
- mon_chr = gdbserver_state.mon_chr;
- reset_gdbserver_state();
- }
-
- create_processes(&gdbserver_state);
-
- if (chr) {
- qemu_chr_fe_init(&gdbserver_state.chr, chr, &error_abort);
- qemu_chr_fe_set_handlers(&gdbserver_state.chr, gdb_chr_can_receive,
- gdb_chr_receive, gdb_chr_event,
- NULL, &gdbserver_state, NULL, true);
- }
- gdbserver_state.state = chr ? RS_IDLE : RS_INACTIVE;
- gdbserver_state.mon_chr = mon_chr;
- gdbserver_state.current_syscall_cb = NULL;
-
- return 0;
-}
-
-static void register_types(void)
-{
- type_register_static(&char_gdb_type_info);
-}
-
-type_init(register_types);
-#endif
diff --git a/gdbstub/internals.h b/gdbstub/internals.h
index b23999f951..94ddff4495 100644
--- a/gdbstub/internals.h
+++ b/gdbstub/internals.h
@@ -6,14 +6,220 @@
* SPDX-License-Identifier: GPL-2.0-or-later
*/
-#ifndef _INTERNALS_H_
-#define _INTERNALS_H_
+#ifndef GDBSTUB_INTERNALS_H
+#define GDBSTUB_INTERNALS_H
#include "exec/cpu-common.h"
+#define MAX_PACKET_LENGTH 4096
+
+/*
+ * Shared structures and definitions
+ */
+
+enum {
+ GDB_SIGNAL_0 = 0,
+ GDB_SIGNAL_INT = 2,
+ GDB_SIGNAL_QUIT = 3,
+ GDB_SIGNAL_TRAP = 5,
+ GDB_SIGNAL_ABRT = 6,
+ GDB_SIGNAL_ALRM = 14,
+ GDB_SIGNAL_IO = 23,
+ GDB_SIGNAL_XCPU = 24,
+ GDB_SIGNAL_UNKNOWN = 143
+};
+
+typedef struct GDBProcess {
+ uint32_t pid;
+ bool attached;
+
+ char target_xml[1024];
+} GDBProcess;
+
+enum RSState {
+ RS_INACTIVE,
+ RS_IDLE,
+ RS_GETLINE,
+ RS_GETLINE_ESC,
+ RS_GETLINE_RLE,
+ RS_CHKSUM1,
+ RS_CHKSUM2,
+};
+
+typedef struct GDBState {
+ bool init; /* have we been initialised? */
+ CPUState *c_cpu; /* current CPU for step/continue ops */
+ CPUState *g_cpu; /* current CPU for other ops */
+ CPUState *query_cpu; /* for q{f|s}ThreadInfo */
+ enum RSState state; /* parsing state */
+ char line_buf[MAX_PACKET_LENGTH];
+ int line_buf_index;
+ int line_sum; /* running checksum */
+ int line_csum; /* checksum at the end of the packet */
+ GByteArray *last_packet;
+ int signal;
+ bool multiprocess;
+ GDBProcess *processes;
+ int process_num;
+ GString *str_buf;
+ GByteArray *mem_buf;
+ int sstep_flags;
+ int supported_sstep_flags;
+} GDBState;
+
+/* lives in main gdbstub.c */
+extern GDBState gdbserver_state;
+
+/*
+ * Inline utility function, convert from int to hex and back
+ */
+
+static inline int fromhex(int v)
+{
+ if (v >= '0' && v <= '9') {
+ return v - '0';
+ } else if (v >= 'A' && v <= 'F') {
+ return v - 'A' + 10;
+ } else if (v >= 'a' && v <= 'f') {
+ return v - 'a' + 10;
+ } else {
+ return 0;
+ }
+}
+
+static inline int tohex(int v)
+{
+ if (v < 10) {
+ return v + '0';
+ } else {
+ return v - 10 + 'a';
+ }
+}
+
+/*
+ * Connection helpers for both softmmu and user backends
+ */
+
+void gdb_put_strbuf(void);
+int gdb_put_packet(const char *buf);
+int gdb_put_packet_binary(const char *buf, int len, bool dump);
+void gdb_hextomem(GByteArray *mem, const char *buf, int len);
+void gdb_memtohex(GString *buf, const uint8_t *mem, int len);
+void gdb_memtox(GString *buf, const char *mem, int len);
+void gdb_read_byte(uint8_t ch);
+
+/*
+ * Packet acknowledgement - we handle this slightly differently
+ * between user and softmmu mode, mainly to deal with the differences
+ * between the flexible chardev and the direct fd approaches.
+ *
+ * We currently don't support a negotiated QStartNoAckMode
+ */
+
+/**
+ * gdb_got_immediate_ack() - check ok to continue
+ *
+ * Returns true to continue, false to re-transmit for user only, the
+ * softmmu stub always returns true.
+ */
+bool gdb_got_immediate_ack(void);
+/* utility helpers */
+CPUState *gdb_first_attached_cpu(void);
+void gdb_append_thread_id(CPUState *cpu, GString *buf);
+int gdb_get_cpu_index(CPUState *cpu);
+unsigned int gdb_get_max_cpus(void); /* both */
+bool gdb_can_reverse(void); /* softmmu, stub for user */
+
+void gdb_create_default_process(GDBState *s);
+
+/* signal mapping, common for softmmu, specialised for user-mode */
+int gdb_signal_to_target(int sig);
+int gdb_target_signal_to_gdb(int sig);
+
+int gdb_get_char(void); /* user only */
+
+/**
+ * gdb_continue() - handle continue in mode specific way.
+ */
+void gdb_continue(void);
+
+/**
+ * gdb_continue_partial() - handle partial continue in mode specific way.
+ */
+int gdb_continue_partial(char *newstates);
+
+/*
+ * Helpers with separate softmmu and user implementations
+ */
+void gdb_put_buffer(const uint8_t *buf, int len);
+
+/*
+ * Command handlers - either specialised or softmmu or user only
+ */
+void gdb_init_gdbserver_state(void);
+
+typedef enum GDBThreadIdKind {
+ GDB_ONE_THREAD = 0,
+ GDB_ALL_THREADS, /* One process, all threads */
+ GDB_ALL_PROCESSES,
+ GDB_READ_THREAD_ERR
+} GDBThreadIdKind;
+
+typedef union GdbCmdVariant {
+ const char *data;
+ uint8_t opcode;
+ unsigned long val_ul;
+ unsigned long long val_ull;
+ struct {
+ GDBThreadIdKind kind;
+ uint32_t pid;
+ uint32_t tid;
+ } thread_id;
+} GdbCmdVariant;
+
+#define get_param(p, i) (&g_array_index(p, GdbCmdVariant, i))
+
+void gdb_handle_query_rcmd(GArray *params, void *user_ctx); /* softmmu */
+void gdb_handle_query_offsets(GArray *params, void *user_ctx); /* user */
+void gdb_handle_query_xfer_auxv(GArray *params, void *user_ctx); /*user */
+
+void gdb_handle_query_attached(GArray *params, void *user_ctx); /* both */
+
+/* softmmu only */
+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);
+
+/* 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);
+
+/* user/softmmu specific syscall handling */
+void gdb_syscall_handling(const char *syscall_packet);
+
+/*
+ * Break/Watch point support - there is an implementation for softmmu
+ * and user mode.
+ */
bool gdb_supports_guest_debug(void);
int gdb_breakpoint_insert(CPUState *cs, int type, vaddr addr, vaddr len);
int gdb_breakpoint_remove(CPUState *cs, int type, vaddr addr, vaddr len);
void gdb_breakpoint_remove_all(CPUState *cs);
-#endif /* _INTERNALS_H_ */
+/**
+ * gdb_target_memory_rw_debug() - handle debug access to memory
+ * @cs: CPUState
+ * @addr: nominal address, could be an entire physical address
+ * @buf: data
+ * @len: length of access
+ * @is_write: is it a write operation
+ *
+ * This function is specialised depending on the mode we are running
+ * in. For softmmu guests we can switch the interpretation of the
+ * address to a physical address.
+ */
+int gdb_target_memory_rw_debug(CPUState *cs, hwaddr addr,
+ uint8_t *buf, int len, bool is_write);
+
+#endif /* GDBSTUB_INTERNALS_H */
diff --git a/gdbstub/meson.build b/gdbstub/meson.build
index fc895a2c39..bd5c5cd67d 100644
--- a/gdbstub/meson.build
+++ b/gdbstub/meson.build
@@ -4,6 +4,34 @@
# types such as hwaddr.
#
-specific_ss.add(files('gdbstub.c'))
-softmmu_ss.add(files('softmmu.c'))
-user_ss.add(files('user.c'))
+# We need to build the core gdb code via a library to be able to tweak
+# cflags so:
+
+gdb_user_ss = ss.source_set()
+gdb_softmmu_ss = ss.source_set()
+
+# We build two versions of gdbstub, one for each mode
+gdb_user_ss.add(files('gdbstub.c', 'user.c'))
+gdb_softmmu_ss.add(files('gdbstub.c', 'softmmu.c'))
+
+gdb_user_ss = gdb_user_ss.apply(config_host, strict: false)
+gdb_softmmu_ss = gdb_softmmu_ss.apply(config_host, strict: false)
+
+libgdb_user = static_library('gdb_user',
+ gdb_user_ss.sources() + genh,
+ name_suffix: 'fa',
+ c_args: '-DCONFIG_USER_ONLY')
+
+libgdb_softmmu = static_library('gdb_softmmu',
+ gdb_softmmu_ss.sources() + genh,
+ name_suffix: 'fa')
+
+gdb_user = declare_dependency(link_whole: libgdb_user)
+user_ss.add(gdb_user)
+gdb_softmmu = declare_dependency(link_whole: libgdb_softmmu)
+softmmu_ss.add(gdb_softmmu)
+
+common_ss.add(files('syscalls.c'))
+
+# The user-target is specialised by the guest
+specific_ss.add(when: 'CONFIG_USER_ONLY', if_true: files('user-target.c'))
diff --git a/gdbstub/softmmu.c b/gdbstub/softmmu.c
index 129575e510..22ecd09d04 100644
--- a/gdbstub/softmmu.c
+++ b/gdbstub/softmmu.c
@@ -4,16 +4,617 @@
* Debug integration depends on support from the individual
* accelerators so most of this involves calling the ops helpers.
*
+ * Copyright (c) 2003-2005 Fabrice Bellard
* Copyright (c) 2022 Linaro Ltd
*
- * SPDX-License-Identifier: GPL-2.0-or-later
+ * SPDX-License-Identifier: LGPL-2.0+
*/
#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu/error-report.h"
+#include "qemu/cutils.h"
#include "exec/gdbstub.h"
+#include "gdbstub/syscalls.h"
+#include "exec/hwaddr.h"
+#include "exec/tb-flush.h"
#include "sysemu/cpus.h"
+#include "sysemu/runstate.h"
+#include "sysemu/replay.h"
+#include "hw/core/cpu.h"
+#include "hw/cpu/cluster.h"
+#include "hw/boards.h"
+#include "chardev/char.h"
+#include "chardev/char-fe.h"
+#include "monitor/monitor.h"
+#include "trace.h"
#include "internals.h"
+/* System emulation specific state */
+typedef struct {
+ CharBackend chr;
+ Chardev *mon_chr;
+} GDBSystemState;
+
+GDBSystemState gdbserver_system_state;
+
+static void reset_gdbserver_state(void)
+{
+ g_free(gdbserver_state.processes);
+ gdbserver_state.processes = NULL;
+ gdbserver_state.process_num = 0;
+}
+
+/*
+ * Return the GDB index for a given vCPU state.
+ *
+ * In system mode GDB numbers CPUs from 1 as 0 is reserved as an "any
+ * cpu" index.
+ */
+int gdb_get_cpu_index(CPUState *cpu)
+{
+ return cpu->cpu_index + 1;
+}
+
+/*
+ * We check the status of the last message in the chardev receive code
+ */
+bool gdb_got_immediate_ack(void)
+{
+ return true;
+}
+
+/*
+ * GDB Connection management. For system emulation we do all of this
+ * via our existing Chardev infrastructure which allows us to support
+ * network and unix sockets.
+ */
+
+void gdb_put_buffer(const uint8_t *buf, int len)
+{
+ /*
+ * XXX this blocks entire thread. Rewrite to use
+ * qemu_chr_fe_write and background I/O callbacks
+ */
+ qemu_chr_fe_write_all(&gdbserver_system_state.chr, buf, len);
+}
+
+static void gdb_chr_event(void *opaque, QEMUChrEvent event)
+{
+ int i;
+ GDBState *s = (GDBState *) opaque;
+
+ switch (event) {
+ case CHR_EVENT_OPENED:
+ /* Start with first process attached, others detached */
+ for (i = 0; i < s->process_num; i++) {
+ s->processes[i].attached = !i;
+ }
+
+ s->c_cpu = gdb_first_attached_cpu();
+ s->g_cpu = s->c_cpu;
+
+ vm_stop(RUN_STATE_PAUSED);
+ replay_gdb_attached();
+ gdb_has_xml = false;
+ break;
+ default:
+ break;
+ }
+}
+
+/*
+ * In softmmu mode we stop the VM and 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.
+ */
+void gdb_syscall_handling(const char *syscall_packet)
+{
+ vm_stop(RUN_STATE_DEBUG);
+ qemu_cpu_kick(gdbserver_state.c_cpu);
+}
+
+static void gdb_vm_state_change(void *opaque, bool running, RunState state)
+{
+ CPUState *cpu = gdbserver_state.c_cpu;
+ g_autoptr(GString) buf = g_string_new(NULL);
+ g_autoptr(GString) tid = g_string_new(NULL);
+ const char *type;
+ int ret;
+
+ if (running || gdbserver_state.state == RS_INACTIVE) {
+ return;
+ }
+
+ /* Is there a GDB syscall waiting to be sent? */
+ if (gdb_handled_syscall()) {
+ return;
+ }
+
+ if (cpu == NULL) {
+ /* No process attached */
+ return;
+ }
+
+ gdb_append_thread_id(cpu, tid);
+
+ switch (state) {
+ case RUN_STATE_DEBUG:
+ if (cpu->watchpoint_hit) {
+ switch (cpu->watchpoint_hit->flags & BP_MEM_ACCESS) {
+ case BP_MEM_READ:
+ type = "r";
+ break;
+ case BP_MEM_ACCESS:
+ type = "a";
+ break;
+ default:
+ type = "";
+ break;
+ }
+ trace_gdbstub_hit_watchpoint(type,
+ gdb_get_cpu_index(cpu),
+ cpu->watchpoint_hit->vaddr);
+ g_string_printf(buf, "T%02xthread:%s;%swatch:%" VADDR_PRIx ";",
+ GDB_SIGNAL_TRAP, tid->str, type,
+ cpu->watchpoint_hit->vaddr);
+ cpu->watchpoint_hit = NULL;
+ goto send_packet;
+ } else {
+ trace_gdbstub_hit_break();
+ }
+ tb_flush(cpu);
+ ret = GDB_SIGNAL_TRAP;
+ break;
+ case RUN_STATE_PAUSED:
+ trace_gdbstub_hit_paused();
+ ret = GDB_SIGNAL_INT;
+ break;
+ case RUN_STATE_SHUTDOWN:
+ trace_gdbstub_hit_shutdown();
+ ret = GDB_SIGNAL_QUIT;
+ break;
+ case RUN_STATE_IO_ERROR:
+ trace_gdbstub_hit_io_error();
+ ret = GDB_SIGNAL_IO;
+ break;
+ case RUN_STATE_WATCHDOG:
+ trace_gdbstub_hit_watchdog();
+ ret = GDB_SIGNAL_ALRM;
+ break;
+ case RUN_STATE_INTERNAL_ERROR:
+ trace_gdbstub_hit_internal_error();
+ ret = GDB_SIGNAL_ABRT;
+ break;
+ case RUN_STATE_SAVE_VM:
+ case RUN_STATE_RESTORE_VM:
+ return;
+ case RUN_STATE_FINISH_MIGRATE:
+ ret = GDB_SIGNAL_XCPU;
+ break;
+ default:
+ trace_gdbstub_hit_unknown(state);
+ ret = GDB_SIGNAL_UNKNOWN;
+ break;
+ }
+ gdb_set_stop_cpu(cpu);
+ g_string_printf(buf, "T%02xthread:%s;", ret, tid->str);
+
+send_packet:
+ gdb_put_packet(buf->str);
+
+ /* disable single step if it was enabled */
+ cpu_single_step(cpu, 0);
+}
+
+#ifndef _WIN32
+static void gdb_sigterm_handler(int signal)
+{
+ if (runstate_is_running()) {
+ vm_stop(RUN_STATE_PAUSED);
+ }
+}
+#endif
+
+static int gdb_monitor_write(Chardev *chr, const uint8_t *buf, int len)
+{
+ g_autoptr(GString) hex_buf = g_string_new("O");
+ gdb_memtohex(hex_buf, buf, len);
+ gdb_put_packet(hex_buf->str);
+ return len;
+}
+
+static void gdb_monitor_open(Chardev *chr, ChardevBackend *backend,
+ bool *be_opened, Error **errp)
+{
+ *be_opened = false;
+}
+
+static void char_gdb_class_init(ObjectClass *oc, void *data)
+{
+ ChardevClass *cc = CHARDEV_CLASS(oc);
+
+ cc->internal = true;
+ cc->open = gdb_monitor_open;
+ cc->chr_write = gdb_monitor_write;
+}
+
+#define TYPE_CHARDEV_GDB "chardev-gdb"
+
+static const TypeInfo char_gdb_type_info = {
+ .name = TYPE_CHARDEV_GDB,
+ .parent = TYPE_CHARDEV,
+ .class_init = char_gdb_class_init,
+};
+
+static int gdb_chr_can_receive(void *opaque)
+{
+ /*
+ * We can handle an arbitrarily large amount of data.
+ * Pick the maximum packet size, which is as good as anything.
+ */
+ return MAX_PACKET_LENGTH;
+}
+
+static void gdb_chr_receive(void *opaque, const uint8_t *buf, int size)
+{
+ int i;
+
+ for (i = 0; i < size; i++) {
+ gdb_read_byte(buf[i]);
+ }
+}
+
+static int find_cpu_clusters(Object *child, void *opaque)
+{
+ if (object_dynamic_cast(child, TYPE_CPU_CLUSTER)) {
+ GDBState *s = (GDBState *) opaque;
+ CPUClusterState *cluster = CPU_CLUSTER(child);
+ GDBProcess *process;
+
+ s->processes = g_renew(GDBProcess, s->processes, ++s->process_num);
+
+ process = &s->processes[s->process_num - 1];
+
+ /*
+ * GDB process IDs -1 and 0 are reserved. To avoid subtle errors at
+ * runtime, we enforce here that the machine does not use a cluster ID
+ * that would lead to PID 0.
+ */
+ assert(cluster->cluster_id != UINT32_MAX);
+ process->pid = cluster->cluster_id + 1;
+ process->attached = false;
+ process->target_xml[0] = '\0';
+
+ return 0;
+ }
+
+ return object_child_foreach(child, find_cpu_clusters, opaque);
+}
+
+static int pid_order(const void *a, const void *b)
+{
+ GDBProcess *pa = (GDBProcess *) a;
+ GDBProcess *pb = (GDBProcess *) b;
+
+ if (pa->pid < pb->pid) {
+ return -1;
+ } else if (pa->pid > pb->pid) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+static void create_processes(GDBState *s)
+{
+ object_child_foreach(object_get_root(), find_cpu_clusters, s);
+
+ if (gdbserver_state.processes) {
+ /* Sort by PID */
+ qsort(gdbserver_state.processes,
+ gdbserver_state.process_num,
+ sizeof(gdbserver_state.processes[0]),
+ pid_order);
+ }
+
+ gdb_create_default_process(s);
+}
+
+int gdbserver_start(const char *device)
+{
+ trace_gdbstub_op_start(device);
+
+ char gdbstub_device_name[128];
+ Chardev *chr = NULL;
+ Chardev *mon_chr;
+
+ if (!first_cpu) {
+ error_report("gdbstub: meaningless to attach gdb to a "
+ "machine without any CPU.");
+ return -1;
+ }
+
+ if (!gdb_supports_guest_debug()) {
+ error_report("gdbstub: current accelerator doesn't "
+ "support guest debugging");
+ return -1;
+ }
+
+ if (!device) {
+ return -1;
+ }
+ if (strcmp(device, "none") != 0) {
+ if (strstart(device, "tcp:", NULL)) {
+ /* enforce required TCP attributes */
+ snprintf(gdbstub_device_name, sizeof(gdbstub_device_name),
+ "%s,wait=off,nodelay=on,server=on", device);
+ device = gdbstub_device_name;
+ }
+#ifndef _WIN32
+ else if (strcmp(device, "stdio") == 0) {
+ struct sigaction act;
+
+ memset(&act, 0, sizeof(act));
+ act.sa_handler = gdb_sigterm_handler;
+ sigaction(SIGINT, &act, NULL);
+ }
+#endif
+ /*
+ * FIXME: it's a bit weird to allow using a mux chardev here
+ * and implicitly setup a monitor. We may want to break this.
+ */
+ chr = qemu_chr_new_noreplay("gdb", device, true, NULL);
+ if (!chr) {
+ return -1;
+ }
+ }
+
+ if (!gdbserver_state.init) {
+ gdb_init_gdbserver_state();
+
+ qemu_add_vm_change_state_handler(gdb_vm_state_change, NULL);
+
+ /* Initialize a monitor terminal for gdb */
+ mon_chr = qemu_chardev_new(NULL, TYPE_CHARDEV_GDB,
+ NULL, NULL, &error_abort);
+ monitor_init_hmp(mon_chr, false, &error_abort);
+ } else {
+ qemu_chr_fe_deinit(&gdbserver_system_state.chr, true);
+ mon_chr = gdbserver_system_state.mon_chr;
+ reset_gdbserver_state();
+ }
+
+ create_processes(&gdbserver_state);
+
+ if (chr) {
+ qemu_chr_fe_init(&gdbserver_system_state.chr, chr, &error_abort);
+ qemu_chr_fe_set_handlers(&gdbserver_system_state.chr,
+ gdb_chr_can_receive,
+ gdb_chr_receive, gdb_chr_event,
+ NULL, &gdbserver_state, NULL, true);
+ }
+ gdbserver_state.state = chr ? RS_IDLE : RS_INACTIVE;
+ gdbserver_system_state.mon_chr = mon_chr;
+ gdb_syscall_reset();
+
+ return 0;
+}
+
+static void register_types(void)
+{
+ type_register_static(&char_gdb_type_info);
+}
+
+type_init(register_types);
+
+/* Tell the remote gdb that the process has exited. */
+void gdb_exit(int code)
+{
+ char buf[4];
+
+ if (!gdbserver_state.init) {
+ return;
+ }
+
+ trace_gdbstub_op_exiting((uint8_t)code);
+
+ snprintf(buf, sizeof(buf), "W%02x", (uint8_t)code);
+ gdb_put_packet(buf);
+
+ qemu_chr_fe_deinit(&gdbserver_system_state.chr, true);
+}
+
+/*
+ * Memory access
+ */
+static int phy_memory_mode;
+
+int gdb_target_memory_rw_debug(CPUState *cpu, hwaddr addr,
+ uint8_t *buf, int len, bool is_write)
+{
+ CPUClass *cc;
+
+ if (phy_memory_mode) {
+ if (is_write) {
+ cpu_physical_memory_write(addr, buf, len);
+ } else {
+ cpu_physical_memory_read(addr, buf, len);
+ }
+ return 0;
+ }
+
+ cc = CPU_GET_CLASS(cpu);
+ if (cc->memory_rw_debug) {
+ return cc->memory_rw_debug(cpu, addr, buf, len, is_write);
+ }
+
+ return cpu_memory_rw_debug(cpu, addr, buf, len, is_write);
+}
+
+/*
+ * cpu helpers
+ */
+
+unsigned int gdb_get_max_cpus(void)
+{
+ MachineState *ms = MACHINE(qdev_get_machine());
+ return ms->smp.max_cpus;
+}
+
+bool gdb_can_reverse(void)
+{
+ return replay_mode == REPLAY_MODE_PLAY;
+}
+
+/*
+ * Softmmu specific command helpers
+ */
+
+void gdb_handle_query_qemu_phy_mem_mode(GArray *params,
+ void *user_ctx)
+{
+ g_string_printf(gdbserver_state.str_buf, "%d", phy_memory_mode);
+ gdb_put_strbuf();
+}
+
+void gdb_handle_set_qemu_phy_mem_mode(GArray *params, void *user_ctx)
+{
+ if (!params->len) {
+ gdb_put_packet("E22");
+ return;
+ }
+
+ if (!get_param(params, 0)->val_ul) {
+ phy_memory_mode = 0;
+ } else {
+ phy_memory_mode = 1;
+ }
+ gdb_put_packet("OK");
+}
+
+void gdb_handle_query_rcmd(GArray *params, void *user_ctx)
+{
+ const guint8 zero = 0;
+ int len;
+
+ if (!params->len) {
+ gdb_put_packet("E22");
+ return;
+ }
+
+ len = strlen(get_param(params, 0)->data);
+ if (len % 2) {
+ gdb_put_packet("E01");
+ return;
+ }
+
+ g_assert(gdbserver_state.mem_buf->len == 0);
+ len = len / 2;
+ gdb_hextomem(gdbserver_state.mem_buf, get_param(params, 0)->data, len);
+ g_byte_array_append(gdbserver_state.mem_buf, &zero, 1);
+ qemu_chr_be_write(gdbserver_system_state.mon_chr,
+ gdbserver_state.mem_buf->data,
+ gdbserver_state.mem_buf->len);
+ gdb_put_packet("OK");
+}
+
+/*
+ * Execution state helpers
+ */
+
+void gdb_handle_query_attached(GArray *params, void *user_ctx)
+{
+ gdb_put_packet("1");
+}
+
+void gdb_continue(void)
+{
+ if (!runstate_needs_reset()) {
+ trace_gdbstub_op_continue();
+ vm_start();
+ }
+}
+
+/*
+ * Resume execution, per CPU actions.
+ */
+int gdb_continue_partial(char *newstates)
+{
+ CPUState *cpu;
+ int res = 0;
+ int flag = 0;
+
+ if (!runstate_needs_reset()) {
+ bool step_requested = false;
+ CPU_FOREACH(cpu) {
+ if (newstates[cpu->cpu_index] == 's') {
+ step_requested = true;
+ break;
+ }
+ }
+
+ if (vm_prepare_start(step_requested)) {
+ return 0;
+ }
+
+ CPU_FOREACH(cpu) {
+ switch (newstates[cpu->cpu_index]) {
+ case 0:
+ case 1:
+ break; /* nothing to do here */
+ case 's':
+ trace_gdbstub_op_stepping(cpu->cpu_index);
+ cpu_single_step(cpu, gdbserver_state.sstep_flags);
+ cpu_resume(cpu);
+ flag = 1;
+ break;
+ case 'c':
+ trace_gdbstub_op_continue_cpu(cpu->cpu_index);
+ cpu_resume(cpu);
+ flag = 1;
+ break;
+ default:
+ res = -1;
+ break;
+ }
+ }
+ }
+ if (flag) {
+ qemu_clock_enable(QEMU_CLOCK_VIRTUAL, true);
+ }
+ return res;
+}
+
+/*
+ * Signal Handling - in system mode we only need SIGINT and SIGTRAP; other
+ * signals are not yet supported.
+ */
+
+enum {
+ TARGET_SIGINT = 2,
+ TARGET_SIGTRAP = 5
+};
+
+int gdb_signal_to_target(int sig)
+{
+ switch (sig) {
+ case 2:
+ return TARGET_SIGINT;
+ case 5:
+ return TARGET_SIGTRAP;
+ default:
+ return -1;
+ }
+}
+
+/*
+ * Break/Watch point helpers
+ */
+
bool gdb_supports_guest_debug(void)
{
const AccelOpsClass *ops = cpus_get_accel();
diff --git a/gdbstub/syscalls.c b/gdbstub/syscalls.c
new file mode 100644
index 0000000000..02e3a8f74c
--- /dev/null
+++ b/gdbstub/syscalls.c
@@ -0,0 +1,205 @@
+/*
+ * 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+
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/error-report.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_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 = 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();
+}
diff --git a/gdbstub/trace-events b/gdbstub/trace-events
index 03f0c303bf..0c18a4d70a 100644
--- a/gdbstub/trace-events
+++ b/gdbstub/trace-events
@@ -7,7 +7,6 @@ gdbstub_op_continue(void) "Continuing all CPUs"
gdbstub_op_continue_cpu(int cpu_index) "Continuing CPU %d"
gdbstub_op_stepping(int cpu_index) "Stepping CPU %d"
gdbstub_op_extra_info(const char *info) "Thread extra info: %s"
-gdbstub_hit_watchpoint(const char *type, int cpu_gdb_index, uint64_t vaddr) "Watchpoint hit, type=\"%s\" cpu=%d, vaddr=0x%" PRIx64 ""
gdbstub_hit_internal_error(void) "RUN_STATE_INTERNAL_ERROR"
gdbstub_hit_break(void) "RUN_STATE_DEBUG"
gdbstub_hit_paused(void) "RUN_STATE_PAUSED"
@@ -27,3 +26,6 @@ gdbstub_err_invalid_repeat(uint8_t ch) "got invalid RLE count: 0x%02x"
gdbstub_err_invalid_rle(void) "got invalid RLE sequence"
gdbstub_err_checksum_invalid(uint8_t ch) "got invalid command checksum digit: 0x%02x"
gdbstub_err_checksum_incorrect(uint8_t expected, uint8_t got) "got command packet with incorrect checksum, expected=0x%02x, received=0x%02x"
+
+# softmmu.c
+gdbstub_hit_watchpoint(const char *type, int cpu_gdb_index, uint64_t vaddr) "Watchpoint hit, type=\"%s\" cpu=%d, vaddr=0x%" PRIx64 ""
diff --git a/gdbstub/user-target.c b/gdbstub/user-target.c
new file mode 100644
index 0000000000..fa0e59ec9a
--- /dev/null
+++ b/gdbstub/user-target.c
@@ -0,0 +1,283 @@
+/*
+ * Target specific user-mode handling
+ *
+ * Copyright (c) 2003-2005 Fabrice Bellard
+ * Copyright (c) 2022 Linaro Ltd
+ *
+ * SPDX-License-Identifier: LGPL-2.0+
+ */
+
+#include "qemu/osdep.h"
+#include "exec/gdbstub.h"
+#include "qemu.h"
+#include "internals.h"
+
+/*
+ * Map target signal numbers to GDB protocol signal numbers and vice
+ * versa. For user emulation's currently supported systems, we can
+ * assume most signals are defined.
+ */
+
+static int gdb_signal_table[] = {
+ 0,
+ TARGET_SIGHUP,
+ TARGET_SIGINT,
+ TARGET_SIGQUIT,
+ TARGET_SIGILL,
+ TARGET_SIGTRAP,
+ TARGET_SIGABRT,
+ -1, /* SIGEMT */
+ TARGET_SIGFPE,
+ TARGET_SIGKILL,
+ TARGET_SIGBUS,
+ TARGET_SIGSEGV,
+ TARGET_SIGSYS,
+ TARGET_SIGPIPE,
+ TARGET_SIGALRM,
+ TARGET_SIGTERM,
+ TARGET_SIGURG,
+ TARGET_SIGSTOP,
+ TARGET_SIGTSTP,
+ TARGET_SIGCONT,
+ TARGET_SIGCHLD,
+ TARGET_SIGTTIN,
+ TARGET_SIGTTOU,
+ TARGET_SIGIO,
+ TARGET_SIGXCPU,
+ TARGET_SIGXFSZ,
+ TARGET_SIGVTALRM,
+ TARGET_SIGPROF,
+ TARGET_SIGWINCH,
+ -1, /* SIGLOST */
+ TARGET_SIGUSR1,
+ TARGET_SIGUSR2,
+#ifdef TARGET_SIGPWR
+ TARGET_SIGPWR,
+#else
+ -1,
+#endif
+ -1, /* SIGPOLL */
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+#ifdef __SIGRTMIN
+ __SIGRTMIN + 1,
+ __SIGRTMIN + 2,
+ __SIGRTMIN + 3,
+ __SIGRTMIN + 4,
+ __SIGRTMIN + 5,
+ __SIGRTMIN + 6,
+ __SIGRTMIN + 7,
+ __SIGRTMIN + 8,
+ __SIGRTMIN + 9,
+ __SIGRTMIN + 10,
+ __SIGRTMIN + 11,
+ __SIGRTMIN + 12,
+ __SIGRTMIN + 13,
+ __SIGRTMIN + 14,
+ __SIGRTMIN + 15,
+ __SIGRTMIN + 16,
+ __SIGRTMIN + 17,
+ __SIGRTMIN + 18,
+ __SIGRTMIN + 19,
+ __SIGRTMIN + 20,
+ __SIGRTMIN + 21,
+ __SIGRTMIN + 22,
+ __SIGRTMIN + 23,
+ __SIGRTMIN + 24,
+ __SIGRTMIN + 25,
+ __SIGRTMIN + 26,
+ __SIGRTMIN + 27,
+ __SIGRTMIN + 28,
+ __SIGRTMIN + 29,
+ __SIGRTMIN + 30,
+ __SIGRTMIN + 31,
+ -1, /* SIGCANCEL */
+ __SIGRTMIN,
+ __SIGRTMIN + 32,
+ __SIGRTMIN + 33,
+ __SIGRTMIN + 34,
+ __SIGRTMIN + 35,
+ __SIGRTMIN + 36,
+ __SIGRTMIN + 37,
+ __SIGRTMIN + 38,
+ __SIGRTMIN + 39,
+ __SIGRTMIN + 40,
+ __SIGRTMIN + 41,
+ __SIGRTMIN + 42,
+ __SIGRTMIN + 43,
+ __SIGRTMIN + 44,
+ __SIGRTMIN + 45,
+ __SIGRTMIN + 46,
+ __SIGRTMIN + 47,
+ __SIGRTMIN + 48,
+ __SIGRTMIN + 49,
+ __SIGRTMIN + 50,
+ __SIGRTMIN + 51,
+ __SIGRTMIN + 52,
+ __SIGRTMIN + 53,
+ __SIGRTMIN + 54,
+ __SIGRTMIN + 55,
+ __SIGRTMIN + 56,
+ __SIGRTMIN + 57,
+ __SIGRTMIN + 58,
+ __SIGRTMIN + 59,
+ __SIGRTMIN + 60,
+ __SIGRTMIN + 61,
+ __SIGRTMIN + 62,
+ __SIGRTMIN + 63,
+ __SIGRTMIN + 64,
+ __SIGRTMIN + 65,
+ __SIGRTMIN + 66,
+ __SIGRTMIN + 67,
+ __SIGRTMIN + 68,
+ __SIGRTMIN + 69,
+ __SIGRTMIN + 70,
+ __SIGRTMIN + 71,
+ __SIGRTMIN + 72,
+ __SIGRTMIN + 73,
+ __SIGRTMIN + 74,
+ __SIGRTMIN + 75,
+ __SIGRTMIN + 76,
+ __SIGRTMIN + 77,
+ __SIGRTMIN + 78,
+ __SIGRTMIN + 79,
+ __SIGRTMIN + 80,
+ __SIGRTMIN + 81,
+ __SIGRTMIN + 82,
+ __SIGRTMIN + 83,
+ __SIGRTMIN + 84,
+ __SIGRTMIN + 85,
+ __SIGRTMIN + 86,
+ __SIGRTMIN + 87,
+ __SIGRTMIN + 88,
+ __SIGRTMIN + 89,
+ __SIGRTMIN + 90,
+ __SIGRTMIN + 91,
+ __SIGRTMIN + 92,
+ __SIGRTMIN + 93,
+ __SIGRTMIN + 94,
+ __SIGRTMIN + 95,
+ -1, /* SIGINFO */
+ -1, /* UNKNOWN */
+ -1, /* DEFAULT */
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1
+#endif
+};
+
+int gdb_signal_to_target(int sig)
+{
+ if (sig < ARRAY_SIZE(gdb_signal_table)) {
+ return gdb_signal_table[sig];
+ } else {
+ return -1;
+ }
+}
+
+int gdb_target_signal_to_gdb(int sig)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(gdb_signal_table); i++) {
+ if (gdb_signal_table[i] == sig) {
+ return i;
+ }
+ }
+ return GDB_SIGNAL_UNKNOWN;
+}
+
+int gdb_get_cpu_index(CPUState *cpu)
+{
+ TaskState *ts = (TaskState *) cpu->opaque;
+ return ts ? ts->ts_tid : -1;
+}
+
+/*
+ * User-mode specific command helpers
+ */
+
+void gdb_handle_query_offsets(GArray *params, void *user_ctx)
+{
+ TaskState *ts;
+
+ ts = gdbserver_state.c_cpu->opaque;
+ g_string_printf(gdbserver_state.str_buf,
+ "Text=" TARGET_ABI_FMT_lx
+ ";Data=" TARGET_ABI_FMT_lx
+ ";Bss=" TARGET_ABI_FMT_lx,
+ ts->info->code_offset,
+ ts->info->data_offset,
+ ts->info->data_offset);
+ gdb_put_strbuf();
+}
+
+#if defined(CONFIG_LINUX)
+/* Partial user only duplicate of helper in gdbstub.c */
+static inline int target_memory_rw_debug(CPUState *cpu, target_ulong addr,
+ uint8_t *buf, int len, bool is_write)
+{
+ CPUClass *cc;
+ cc = CPU_GET_CLASS(cpu);
+ if (cc->memory_rw_debug) {
+ return cc->memory_rw_debug(cpu, addr, buf, len, is_write);
+ }
+ return cpu_memory_rw_debug(cpu, addr, buf, len, is_write);
+}
+
+void gdb_handle_query_xfer_auxv(GArray *params, void *user_ctx)
+{
+ TaskState *ts;
+ unsigned long offset, len, saved_auxv, auxv_len;
+
+ if (params->len < 2) {
+ gdb_put_packet("E22");
+ return;
+ }
+
+ offset = get_param(params, 0)->val_ul;
+ len = get_param(params, 1)->val_ul;
+ ts = gdbserver_state.c_cpu->opaque;
+ saved_auxv = ts->info->saved_auxv;
+ auxv_len = ts->info->auxv_len;
+
+ if (offset >= auxv_len) {
+ gdb_put_packet("E00");
+ return;
+ }
+
+ if (len > (MAX_PACKET_LENGTH - 5) / 2) {
+ len = (MAX_PACKET_LENGTH - 5) / 2;
+ }
+
+ if (len < auxv_len - offset) {
+ g_string_assign(gdbserver_state.str_buf, "m");
+ } else {
+ g_string_assign(gdbserver_state.str_buf, "l");
+ len = auxv_len - offset;
+ }
+
+ g_byte_array_set_size(gdbserver_state.mem_buf, len);
+ if (target_memory_rw_debug(gdbserver_state.g_cpu, saved_auxv + offset,
+ gdbserver_state.mem_buf->data, len, false)) {
+ gdb_put_packet("E14");
+ return;
+ }
+
+ gdb_memtox(gdbserver_state.str_buf,
+ (const char *)gdbserver_state.mem_buf->data, len);
+ gdb_put_packet_binary(gdbserver_state.str_buf->str,
+ gdbserver_state.str_buf->len, true);
+}
+#endif
diff --git a/gdbstub/user.c b/gdbstub/user.c
index 484bd8f461..80488b6bb9 100644
--- a/gdbstub/user.c
+++ b/gdbstub/user.c
@@ -3,16 +3,423 @@
*
* We know for user-mode we are using TCG so we can call stuff directly.
*
+ * Copyright (c) 2003-2005 Fabrice Bellard
* Copyright (c) 2022 Linaro Ltd
*
- * SPDX-License-Identifier: GPL-2.0-or-later
+ * SPDX-License-Identifier: LGPL-2.0+
*/
#include "qemu/osdep.h"
+#include "qemu/cutils.h"
+#include "qemu/sockets.h"
+#include "exec/hwaddr.h"
+#include "exec/tb-flush.h"
#include "exec/gdbstub.h"
+#include "gdbstub/syscalls.h"
+#include "gdbstub/user.h"
#include "hw/core/cpu.h"
+#include "trace.h"
#include "internals.h"
+/* User-mode specific state */
+typedef struct {
+ int fd;
+ char *socket_path;
+ int running_state;
+} GDBUserState;
+
+static GDBUserState gdbserver_user_state;
+
+int gdb_get_char(void)
+{
+ uint8_t ch;
+ int ret;
+
+ for (;;) {
+ ret = recv(gdbserver_user_state.fd, &ch, 1, 0);
+ if (ret < 0) {
+ if (errno == ECONNRESET) {
+ gdbserver_user_state.fd = -1;
+ }
+ if (errno != EINTR) {
+ return -1;
+ }
+ } else if (ret == 0) {
+ close(gdbserver_user_state.fd);
+ gdbserver_user_state.fd = -1;
+ return -1;
+ } else {
+ break;
+ }
+ }
+ return ch;
+}
+
+bool gdb_got_immediate_ack(void)
+{
+ int i;
+
+ i = gdb_get_char();
+ if (i < 0) {
+ /* no response, continue anyway */
+ return true;
+ }
+
+ if (i == '+') {
+ /* received correctly, continue */
+ return true;
+ }
+
+ /* anything else, including '-' then try again */
+ return false;
+}
+
+void gdb_put_buffer(const uint8_t *buf, int len)
+{
+ int ret;
+
+ while (len > 0) {
+ ret = send(gdbserver_user_state.fd, buf, len, 0);
+ if (ret < 0) {
+ if (errno != EINTR) {
+ return;
+ }
+ } else {
+ buf += ret;
+ len -= ret;
+ }
+ }
+}
+
+/* Tell the remote gdb that the process has exited. */
+void gdb_exit(int code)
+{
+ char buf[4];
+
+ if (!gdbserver_state.init) {
+ return;
+ }
+ if (gdbserver_user_state.socket_path) {
+ unlink(gdbserver_user_state.socket_path);
+ }
+ if (gdbserver_user_state.fd < 0) {
+ return;
+ }
+
+ trace_gdbstub_op_exiting((uint8_t)code);
+
+ snprintf(buf, sizeof(buf), "W%02x", (uint8_t)code);
+ gdb_put_packet(buf);
+}
+
+int gdb_handlesig(CPUState *cpu, int sig)
+{
+ char buf[256];
+ int n;
+
+ if (!gdbserver_state.init || gdbserver_user_state.fd < 0) {
+ return sig;
+ }
+
+ /* disable single step if it was enabled */
+ cpu_single_step(cpu, 0);
+ tb_flush(cpu);
+
+ if (sig != 0) {
+ gdb_set_stop_cpu(cpu);
+ g_string_printf(gdbserver_state.str_buf,
+ "T%02xthread:", gdb_target_signal_to_gdb(sig));
+ gdb_append_thread_id(cpu, gdbserver_state.str_buf);
+ g_string_append_c(gdbserver_state.str_buf, ';');
+ gdb_put_strbuf();
+ }
+ /*
+ * gdb_put_packet() might have detected that the peer terminated the
+ * connection.
+ */
+ if (gdbserver_user_state.fd < 0) {
+ return sig;
+ }
+
+ sig = 0;
+ gdbserver_state.state = RS_IDLE;
+ gdbserver_user_state.running_state = 0;
+ while (gdbserver_user_state.running_state == 0) {
+ n = read(gdbserver_user_state.fd, buf, 256);
+ if (n > 0) {
+ int i;
+
+ for (i = 0; i < n; i++) {
+ gdb_read_byte(buf[i]);
+ }
+ } else {
+ /*
+ * XXX: Connection closed. Should probably wait for another
+ * connection before continuing.
+ */
+ if (n == 0) {
+ close(gdbserver_user_state.fd);
+ }
+ gdbserver_user_state.fd = -1;
+ return sig;
+ }
+ }
+ sig = gdbserver_state.signal;
+ gdbserver_state.signal = 0;
+ return sig;
+}
+
+/* Tell the remote gdb that the process has exited due to SIG. */
+void gdb_signalled(CPUArchState *env, int sig)
+{
+ char buf[4];
+
+ if (!gdbserver_state.init || gdbserver_user_state.fd < 0) {
+ return;
+ }
+
+ snprintf(buf, sizeof(buf), "X%02x", gdb_target_signal_to_gdb(sig));
+ gdb_put_packet(buf);
+}
+
+static void gdb_accept_init(int fd)
+{
+ gdb_init_gdbserver_state();
+ gdb_create_default_process(&gdbserver_state);
+ gdbserver_state.processes[0].attached = true;
+ gdbserver_state.c_cpu = gdb_first_attached_cpu();
+ gdbserver_state.g_cpu = gdbserver_state.c_cpu;
+ gdbserver_user_state.fd = fd;
+ gdb_has_xml = false;
+}
+
+static bool gdb_accept_socket(int gdb_fd)
+{
+ int fd;
+
+ for (;;) {
+ fd = accept(gdb_fd, NULL, NULL);
+ if (fd < 0 && errno != EINTR) {
+ perror("accept socket");
+ return false;
+ } else if (fd >= 0) {
+ qemu_set_cloexec(fd);
+ break;
+ }
+ }
+
+ gdb_accept_init(fd);
+ return true;
+}
+
+static int gdbserver_open_socket(const char *path)
+{
+ struct sockaddr_un sockaddr = {};
+ int fd, ret;
+
+ fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (fd < 0) {
+ perror("create socket");
+ return -1;
+ }
+
+ sockaddr.sun_family = AF_UNIX;
+ pstrcpy(sockaddr.sun_path, sizeof(sockaddr.sun_path) - 1, path);
+ ret = bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr));
+ if (ret < 0) {
+ perror("bind socket");
+ close(fd);
+ return -1;
+ }
+ ret = listen(fd, 1);
+ if (ret < 0) {
+ perror("listen socket");
+ close(fd);
+ return -1;
+ }
+
+ return fd;
+}
+
+static bool gdb_accept_tcp(int gdb_fd)
+{
+ struct sockaddr_in sockaddr = {};
+ socklen_t len;
+ int fd;
+
+ for (;;) {
+ len = sizeof(sockaddr);
+ fd = accept(gdb_fd, (struct sockaddr *)&sockaddr, &len);
+ if (fd < 0 && errno != EINTR) {
+ perror("accept");
+ return false;
+ } else if (fd >= 0) {
+ qemu_set_cloexec(fd);
+ break;
+ }
+ }
+
+ /* set short latency */
+ if (socket_set_nodelay(fd)) {
+ perror("setsockopt");
+ close(fd);
+ return false;
+ }
+
+ gdb_accept_init(fd);
+ return true;
+}
+
+static int gdbserver_open_port(int port)
+{
+ struct sockaddr_in sockaddr;
+ int fd, ret;
+
+ fd = socket(PF_INET, SOCK_STREAM, 0);
+ if (fd < 0) {
+ perror("socket");
+ return -1;
+ }
+ qemu_set_cloexec(fd);
+
+ socket_set_fast_reuse(fd);
+
+ sockaddr.sin_family = AF_INET;
+ sockaddr.sin_port = htons(port);
+ sockaddr.sin_addr.s_addr = 0;
+ ret = bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr));
+ if (ret < 0) {
+ perror("bind");
+ close(fd);
+ return -1;
+ }
+ ret = listen(fd, 1);
+ if (ret < 0) {
+ perror("listen");
+ close(fd);
+ return -1;
+ }
+
+ return fd;
+}
+
+int gdbserver_start(const char *port_or_path)
+{
+ int port = g_ascii_strtoull(port_or_path, NULL, 10);
+ int gdb_fd;
+
+ if (port > 0) {
+ gdb_fd = gdbserver_open_port(port);
+ } else {
+ gdb_fd = gdbserver_open_socket(port_or_path);
+ }
+
+ if (gdb_fd < 0) {
+ return -1;
+ }
+
+ if (port > 0 && gdb_accept_tcp(gdb_fd)) {
+ return 0;
+ } else if (gdb_accept_socket(gdb_fd)) {
+ gdbserver_user_state.socket_path = g_strdup(port_or_path);
+ return 0;
+ }
+
+ /* gone wrong */
+ close(gdb_fd);
+ return -1;
+}
+
+/* Disable gdb stub for child processes. */
+void gdbserver_fork(CPUState *cpu)
+{
+ if (!gdbserver_state.init || gdbserver_user_state.fd < 0) {
+ return;
+ }
+ close(gdbserver_user_state.fd);
+ gdbserver_user_state.fd = -1;
+ cpu_breakpoint_remove_all(cpu, BP_GDB);
+ /* no cpu_watchpoint_remove_all for user-mode */
+}
+
+/*
+ * Execution state helpers
+ */
+
+void gdb_handle_query_attached(GArray *params, void *user_ctx)
+{
+ gdb_put_packet("0");
+}
+
+void gdb_continue(void)
+{
+ gdbserver_user_state.running_state = 1;
+ trace_gdbstub_op_continue();
+}
+
+/*
+ * Resume execution, for user-mode emulation it's equivalent to
+ * gdb_continue.
+ */
+int gdb_continue_partial(char *newstates)
+{
+ CPUState *cpu;
+ int res = 0;
+ /*
+ * This is not exactly accurate, but it's an improvement compared to the
+ * previous situation, where only one CPU would be single-stepped.
+ */
+ CPU_FOREACH(cpu) {
+ if (newstates[cpu->cpu_index] == 's') {
+ trace_gdbstub_op_stepping(cpu->cpu_index);
+ cpu_single_step(cpu, gdbserver_state.sstep_flags);
+ }
+ }
+ gdbserver_user_state.running_state = 1;
+ return res;
+}
+
+/*
+ * Memory access helpers
+ */
+int gdb_target_memory_rw_debug(CPUState *cpu, hwaddr addr,
+ uint8_t *buf, int len, bool is_write)
+{
+ CPUClass *cc;
+
+ cc = CPU_GET_CLASS(cpu);
+ if (cc->memory_rw_debug) {
+ return cc->memory_rw_debug(cpu, addr, buf, len, is_write);
+ }
+ return cpu_memory_rw_debug(cpu, addr, buf, len, is_write);
+}
+
+/*
+ * cpu helpers
+ */
+
+unsigned int gdb_get_max_cpus(void)
+{
+ CPUState *cpu;
+ unsigned int max_cpus = 1;
+
+ CPU_FOREACH(cpu) {
+ max_cpus = max_cpus <= cpu->cpu_index ? cpu->cpu_index + 1 : max_cpus;
+ }
+
+ return max_cpus;
+}
+
+/* replay not supported for user-mode */
+bool gdb_can_reverse(void)
+{
+ return false;
+}
+
+/*
+ * Break/Watch point helpers
+ */
+
bool gdb_supports_guest_debug(void)
{
/* user-mode == TCG == supported */
@@ -65,3 +472,17 @@ void gdb_breakpoint_remove_all(CPUState *cs)
{
cpu_breakpoint_remove_all(cs, BP_GDB);
}
+
+/*
+ * For user-mode syscall support we send the system call immediately
+ * and then 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).
+ */
+void gdb_syscall_handling(const char *syscall_packet)
+{
+ gdb_put_packet(syscall_packet);
+ gdb_handlesig(gdbserver_state.c_cpu, 0);
+}
diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c
index 925ff523cc..ec4def62f8 100644
--- a/hw/ppc/spapr_hcall.c
+++ b/hw/ppc/spapr_hcall.c
@@ -8,6 +8,7 @@
#include "qemu/module.h"
#include "qemu/error-report.h"
#include "exec/exec-all.h"
+#include "exec/tb-flush.h"
#include "helper_regs.h"
#include "hw/ppc/ppc.h"
#include "hw/ppc/spapr.h"
diff --git a/include/exec/cpu-defs.h b/include/exec/cpu-defs.h
index be920d4208..cd8aa177cc 100644
--- a/include/exec/cpu-defs.h
+++ b/include/exec/cpu-defs.h
@@ -55,24 +55,7 @@
# endif
#endif
-#define TARGET_LONG_SIZE (TARGET_LONG_BITS / 8)
-
-/* target_ulong is the type of a virtual address */
-#if TARGET_LONG_SIZE == 4
-typedef int32_t target_long;
-typedef uint32_t target_ulong;
-#define TARGET_FMT_lx "%08x"
-#define TARGET_FMT_ld "%d"
-#define TARGET_FMT_lu "%u"
-#elif TARGET_LONG_SIZE == 8
-typedef int64_t target_long;
-typedef uint64_t target_ulong;
-#define TARGET_FMT_lx "%016" PRIx64
-#define TARGET_FMT_ld "%" PRId64
-#define TARGET_FMT_lu "%" PRIu64
-#else
-#error TARGET_LONG_SIZE undefined
-#endif
+#include "exec/target_long.h"
#if !defined(CONFIG_USER_ONLY) && defined(CONFIG_TCG)
diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h
index e09254333d..ad9eb6067b 100644
--- a/include/exec/exec-all.h
+++ b/include/exec/exec-all.h
@@ -677,7 +677,6 @@ void tb_invalidate_phys_addr(target_ulong addr);
#else
void tb_invalidate_phys_addr(AddressSpace *as, hwaddr addr, MemTxAttrs attrs);
#endif
-void tb_flush(CPUState *cpu);
void tb_phys_invalidate(TranslationBlock *tb, tb_page_addr_t page_addr);
void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t end);
void tb_set_jmp_target(TranslationBlock *tb, int n, uintptr_t addr);
diff --git a/include/exec/gdbstub.h b/include/exec/gdbstub.h
index f667014888..7d743fe1e9 100644
--- a/include/exec/gdbstub.h
+++ b/include/exec/gdbstub.h
@@ -10,122 +10,7 @@
#define GDB_WATCHPOINT_READ 3
#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;
-
-#ifdef NEED_CPU_H
-#include "cpu.h"
-
-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);
-
-#ifdef CONFIG_USER_ONLY
-/**
- * gdb_handlesig: yield control to gdb
- * @cpu: CPU
- * @sig: if non-zero, the signal number which caused us to stop
- *
- * This function yields control to gdb, when a user-mode-only target
- * needs to stop execution. If @sig is non-zero, then we will send a
- * stop packet to tell gdb that we have stopped because of this signal.
- *
- * This function will block (handling protocol requests from gdb)
- * until gdb tells us to continue target execution. When it does
- * return, the return value is a signal to deliver to the target,
- * or 0 if no signal should be delivered, ie the signal that caused
- * us to stop should be ignored.
- */
-int gdb_handlesig(CPUState *, int);
-void gdb_signalled(CPUArchState *, int);
-void gdbserver_fork(CPUState *);
-#endif
/* 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_set_reg_cb)(CPUArchState *env, uint8_t *buf, int reg);
@@ -133,89 +18,6 @@ void gdb_register_coprocessor(CPUState *cpu,
gdb_get_reg_cb get_reg, gdb_set_reg_cb set_reg,
int num_regs, const char *xml, int g_pos);
-/*
- * The GDB remote protocol transfers values in target byte order. As
- * the gdbstub may be batching up several register values we always
- * append to the array.
- */
-
-static inline int gdb_get_reg8(GByteArray *buf, uint8_t val)
-{
- g_byte_array_append(buf, &val, 1);
- return 1;
-}
-
-static inline int gdb_get_reg16(GByteArray *buf, uint16_t val)
-{
- uint16_t to_word = tswap16(val);
- g_byte_array_append(buf, (uint8_t *) &to_word, 2);
- return 2;
-}
-
-static inline int gdb_get_reg32(GByteArray *buf, uint32_t val)
-{
- uint32_t to_long = tswap32(val);
- g_byte_array_append(buf, (uint8_t *) &to_long, 4);
- return 4;
-}
-
-static inline int gdb_get_reg64(GByteArray *buf, uint64_t val)
-{
- uint64_t to_quad = tswap64(val);
- g_byte_array_append(buf, (uint8_t *) &to_quad, 8);
- return 8;
-}
-
-static inline int gdb_get_reg128(GByteArray *buf, uint64_t val_hi,
- uint64_t val_lo)
-{
- uint64_t to_quad;
-#if TARGET_BIG_ENDIAN
- to_quad = tswap64(val_hi);
- g_byte_array_append(buf, (uint8_t *) &to_quad, 8);
- to_quad = tswap64(val_lo);
- g_byte_array_append(buf, (uint8_t *) &to_quad, 8);
-#else
- to_quad = tswap64(val_lo);
- g_byte_array_append(buf, (uint8_t *) &to_quad, 8);
- to_quad = tswap64(val_hi);
- g_byte_array_append(buf, (uint8_t *) &to_quad, 8);
-#endif
- return 16;
-}
-
-static inline int gdb_get_zeroes(GByteArray *array, size_t len)
-{
- guint oldlen = array->len;
- g_byte_array_set_size(array, oldlen + len);
- memset(array->data + oldlen, 0, len);
-
- return len;
-}
-
-/**
- * gdb_get_reg_ptr: get pointer to start of last element
- * @len: length of element
- *
- * This is a helper function to extract the pointer to the last
- * element for additional processing. Some front-ends do additional
- * dynamic swapping of the elements based on CPU state.
- */
-static inline uint8_t * gdb_get_reg_ptr(GByteArray *buf, int len)
-{
- return buf->data + buf->len - len;
-}
-
-#if TARGET_LONG_BITS == 64
-#define gdb_get_regl(buf, val) gdb_get_reg64(buf, val)
-#define ldtul_p(addr) ldq_p(addr)
-#else
-#define gdb_get_regl(buf, val) gdb_get_reg32(buf, val)
-#define ldtul_p(addr) ldl_p(addr)
-#endif
-
-#endif /* NEED_CPU_H */
-
/**
* gdbserver_start: start the gdb server
* @port_or_device: connection spec for gdb
@@ -226,16 +28,6 @@ static inline uint8_t * gdb_get_reg_ptr(GByteArray *buf, int len)
*/
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);
/**
diff --git a/include/exec/target_long.h b/include/exec/target_long.h
new file mode 100644
index 0000000000..93c9472971
--- /dev/null
+++ b/include/exec/target_long.h
@@ -0,0 +1,42 @@
+/*
+ * Target Long Definitions
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ * Copyright (c) 2023 Linaro Ltd
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef _TARGET_LONG_H_
+#define _TARGET_LONG_H_
+
+/*
+ * Usually this should only be included via cpu-defs.h however for
+ * certain cases where we want to build only two versions of a binary
+ * object we can include directly. However the build-system must
+ * ensure TARGET_LONG_BITS is defined directly.
+ */
+#ifndef TARGET_LONG_BITS
+#error TARGET_LONG_BITS not defined
+#endif
+
+#define TARGET_LONG_SIZE (TARGET_LONG_BITS / 8)
+
+/* target_ulong is the type of a virtual address */
+#if TARGET_LONG_SIZE == 4
+typedef int32_t target_long;
+typedef uint32_t target_ulong;
+#define TARGET_FMT_lx "%08x"
+#define TARGET_FMT_ld "%d"
+#define TARGET_FMT_lu "%u"
+#elif TARGET_LONG_SIZE == 8
+typedef int64_t target_long;
+typedef uint64_t target_ulong;
+#define TARGET_FMT_lx "%016" PRIx64
+#define TARGET_FMT_ld "%" PRId64
+#define TARGET_FMT_lu "%" PRIu64
+#else
+#error TARGET_LONG_SIZE undefined
+#endif
+
+#endif /* _TARGET_LONG_H_ */
diff --git a/include/exec/tb-flush.h b/include/exec/tb-flush.h
new file mode 100644
index 0000000000..d92d06565b
--- /dev/null
+++ b/include/exec/tb-flush.h
@@ -0,0 +1,26 @@
+/*
+ * tb-flush prototype for use by the rest of the system.
+ *
+ * Copyright (c) 2022 Linaro Ltd
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#ifndef _TB_FLUSH_H_
+#define _TB_FLUSH_H_
+
+/**
+ * tb_flush() - flush all translation blocks
+ * @cs: CPUState (must be valid, but treated as anonymous pointer)
+ *
+ * Used to flush all the translation blocks in the system. Sometimes
+ * it is simpler to flush everything than work out which individual
+ * translations are now invalid and ensure they are not called
+ * anymore.
+ *
+ * tb_flush() takes care of running the flush in an exclusive context
+ * if it is not already running in one. This means no guest code will
+ * run until this complete.
+ */
+void tb_flush(CPUState *cs);
+
+#endif /* _TB_FLUSH_H_ */
diff --git a/include/gdbstub/helpers.h b/include/gdbstub/helpers.h
new file mode 100644
index 0000000000..c573aef2dc
--- /dev/null
+++ b/include/gdbstub/helpers.h
@@ -0,0 +1,103 @@
+/*
+ * gdbstub helpers
+ *
+ * These are all used by the various frontends and have to be host
+ * aware to ensure things are store in target order.
+ *
+ * Copyright (c) 2022 Linaro Ltd
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef _GDBSTUB_HELPERS_H_
+#define _GDBSTUB_HELPERS_H_
+
+#ifdef NEED_CPU_H
+#include "cpu.h"
+
+/*
+ * The GDB remote protocol transfers values in target byte order. As
+ * the gdbstub may be batching up several register values we always
+ * append to the array.
+ */
+
+static inline int gdb_get_reg8(GByteArray *buf, uint8_t val)
+{
+ g_byte_array_append(buf, &val, 1);
+ return 1;
+}
+
+static inline int gdb_get_reg16(GByteArray *buf, uint16_t val)
+{
+ uint16_t to_word = tswap16(val);
+ g_byte_array_append(buf, (uint8_t *) &to_word, 2);
+ return 2;
+}
+
+static inline int gdb_get_reg32(GByteArray *buf, uint32_t val)
+{
+ uint32_t to_long = tswap32(val);
+ g_byte_array_append(buf, (uint8_t *) &to_long, 4);
+ return 4;
+}
+
+static inline int gdb_get_reg64(GByteArray *buf, uint64_t val)
+{
+ uint64_t to_quad = tswap64(val);
+ g_byte_array_append(buf, (uint8_t *) &to_quad, 8);
+ return 8;
+}
+
+static inline int gdb_get_reg128(GByteArray *buf, uint64_t val_hi,
+ uint64_t val_lo)
+{
+ uint64_t to_quad;
+#if TARGET_BIG_ENDIAN
+ to_quad = tswap64(val_hi);
+ g_byte_array_append(buf, (uint8_t *) &to_quad, 8);
+ to_quad = tswap64(val_lo);
+ g_byte_array_append(buf, (uint8_t *) &to_quad, 8);
+#else
+ to_quad = tswap64(val_lo);
+ g_byte_array_append(buf, (uint8_t *) &to_quad, 8);
+ to_quad = tswap64(val_hi);
+ g_byte_array_append(buf, (uint8_t *) &to_quad, 8);
+#endif
+ return 16;
+}
+
+static inline int gdb_get_zeroes(GByteArray *array, size_t len)
+{
+ guint oldlen = array->len;
+ g_byte_array_set_size(array, oldlen + len);
+ memset(array->data + oldlen, 0, len);
+
+ return len;
+}
+
+/**
+ * gdb_get_reg_ptr: get pointer to start of last element
+ * @len: length of element
+ *
+ * This is a helper function to extract the pointer to the last
+ * element for additional processing. Some front-ends do additional
+ * dynamic swapping of the elements based on CPU state.
+ */
+static inline uint8_t *gdb_get_reg_ptr(GByteArray *buf, int len)
+{
+ return buf->data + buf->len - len;
+}
+
+#if TARGET_LONG_BITS == 64
+#define gdb_get_regl(buf, val) gdb_get_reg64(buf, val)
+#define ldtul_p(addr) ldq_p(addr)
+#else
+#define gdb_get_regl(buf, val) gdb_get_reg32(buf, val)
+#define ldtul_p(addr) ldl_p(addr)
+#endif
+
+#else
+#error "gdbstub helpers should only be included by target specific code"
+#endif
+
+#endif /* _GDBSTUB_HELPERS_H_ */
diff --git a/include/gdbstub/syscalls.h b/include/gdbstub/syscalls.h
new file mode 100644
index 0000000000..243eaf8ce4
--- /dev/null
+++ b/include/gdbstub/syscalls.h
@@ -0,0 +1,113 @@
+/*
+ * 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, ...);
+
+/**
+ * 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_ */
diff --git a/include/gdbstub/user.h b/include/gdbstub/user.h
new file mode 100644
index 0000000000..d392e510c5
--- /dev/null
+++ b/include/gdbstub/user.h
@@ -0,0 +1,43 @@
+/*
+ * gdbstub user-mode only APIs
+ *
+ * Copyright (c) 2022 Linaro Ltd
+ *
+ * SPDX-License-Identifier: LGPL-2.0+
+ */
+
+#ifndef GDBSTUB_USER_H
+#define GDBSTUB_USER_H
+
+/**
+ * gdb_handlesig() - yield control to gdb
+ * @cpu: CPU
+ * @sig: if non-zero, the signal number which caused us to stop
+ *
+ * This function yields control to gdb, when a user-mode-only target
+ * needs to stop execution. If @sig is non-zero, then we will send a
+ * stop packet to tell gdb that we have stopped because of this signal.
+ *
+ * This function will block (handling protocol requests from gdb)
+ * until gdb tells us to continue target execution. When it does
+ * return, the return value is a signal to deliver to the target,
+ * or 0 if no signal should be delivered, ie the signal that caused
+ * us to stop should be ignored.
+ */
+int gdb_handlesig(CPUState *, int);
+
+/**
+ * gdb_signalled() - inform remote gdb of sig exit
+ * @as: current CPUArchState
+ * @sig: signal number
+ */
+void gdb_signalled(CPUArchState *as, int sig);
+
+/**
+ * gdbserver_fork() - disable gdb stub for child processes.
+ * @cs: CPU
+ */
+void gdbserver_fork(CPUState *cs);
+
+
+#endif /* GDBSTUB_USER_H */
diff --git a/include/sysemu/accel-ops.h b/include/sysemu/accel-ops.h
index 30690c71bd..3c1fab4b1e 100644
--- a/include/sysemu/accel-ops.h
+++ b/include/sysemu/accel-ops.h
@@ -48,6 +48,7 @@ struct AccelOpsClass {
/* gdbstub hooks */
bool (*supports_guest_debug)(void);
+ int (*update_guest_debug)(CPUState *cpu);
int (*insert_breakpoint)(CPUState *cpu, int type, vaddr addr, vaddr len);
int (*remove_breakpoint)(CPUState *cpu, int type, vaddr addr, vaddr len);
void (*remove_all_breakpoints)(CPUState *cpu);
diff --git a/linux-user/exit.c b/linux-user/exit.c
index 607b6da9fc..fd49d76f45 100644
--- a/linux-user/exit.c
+++ b/linux-user/exit.c
@@ -18,7 +18,7 @@
*/
#include "qemu/osdep.h"
#include "accel/tcg/perf.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/syscalls.h"
#include "qemu.h"
#include "user-internals.h"
#ifdef CONFIG_GPROF
diff --git a/linux-user/main.c b/linux-user/main.c
index 4ff30ff980..75dbb52788 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -40,6 +40,7 @@
#include "qemu/plugin.h"
#include "exec/exec-all.h"
#include "exec/gdbstub.h"
+#include "gdbstub/user.h"
#include "tcg/tcg.h"
#include "qemu/timer.h"
#include "qemu/envlist.h"
diff --git a/linux-user/signal.c b/linux-user/signal.c
index 098f3a787d..748a98f3e5 100644
--- a/linux-user/signal.c
+++ b/linux-user/signal.c
@@ -18,7 +18,7 @@
*/
#include "qemu/osdep.h"
#include "qemu/bitops.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/user.h"
#include "hw/core/tcg-cpu-ops.h"
#include <sys/ucontext.h>
diff --git a/linux-user/user-internals.h b/linux-user/user-internals.h
index 3576da413f..9333db4f51 100644
--- a/linux-user/user-internals.h
+++ b/linux-user/user-internals.h
@@ -20,6 +20,7 @@
#include "exec/user/thunk.h"
#include "exec/exec-all.h"
+#include "exec/tb-flush.h"
#include "qemu/log.h"
extern char *exec_path;
diff --git a/plugins/core.c b/plugins/core.c
index e04ffa1ba4..04632886b9 100644
--- a/plugins/core.c
+++ b/plugins/core.c
@@ -24,6 +24,7 @@
#include "exec/cpu-common.h"
#include "exec/exec-all.h"
+#include "exec/tb-flush.h"
#include "exec/helper-proto.h"
#include "tcg/tcg.h"
#include "tcg/tcg-op.h"
diff --git a/plugins/loader.c b/plugins/loader.c
index 88c30bde2d..809f3f9b13 100644
--- a/plugins/loader.c
+++ b/plugins/loader.c
@@ -29,7 +29,7 @@
#include "qemu/plugin.h"
#include "qemu/memalign.h"
#include "hw/core/cpu.h"
-#include "exec/exec-all.h"
+#include "exec/tb-flush.h"
#ifndef CONFIG_USER_ONLY
#include "hw/boards.h"
#endif
diff --git a/scripts/probe-gdb-support.py b/scripts/probe-gdb-support.py
new file mode 100755
index 0000000000..5755255966
--- /dev/null
+++ b/scripts/probe-gdb-support.py
@@ -0,0 +1,88 @@
+#!/usr/bin/env python3
+# coding: utf-8
+#
+# Probe gdb for supported architectures.
+#
+# This is required to support testing of the gdbstub as its hard to
+# handle errors gracefully during the test. Instead this script when
+# passed a GDB binary will probe its architecture support and return a
+# string of supported arches, stripped of guff.
+#
+# Copyright 2023 Linaro Ltd
+#
+# Author: Alex Bennée <alex.bennee@linaro.org>
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or later.
+# See the COPYING file in the top-level directory.
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+import argparse
+import re
+from subprocess import check_output, STDOUT
+
+# mappings from gdb arch to QEMU target
+mappings = {
+ "alpha" : "alpha",
+ "aarch64" : ["aarch64", "aarch64_be"],
+ "armv7": "arm",
+ "armv8-a" : ["aarch64", "aarch64_be"],
+ "avr" : "avr",
+ "cris" : "cris",
+ # no hexagon in upstream gdb
+ "hppa1.0" : "hppa",
+ "i386" : "i386",
+ "i386:x86-64" : "x86_64",
+ "Loongarch64" : "loongarch64",
+ "m68k" : "m68k",
+ "MicroBlaze" : "microblaze",
+ "mips:isa64" : ["mips64", "mips64el"],
+ "nios2" : "nios2",
+ "or1k" : "or1k",
+ "powerpc:common" : "ppc",
+ "powerpc:common64" : ["ppc64", "ppc64le"],
+ "riscv:rv32" : "riscv32",
+ "riscv:rv64" : "riscv64",
+ "s390:64-bit" : "s390x",
+ "sh4" : ["sh4", "sh4eb"],
+ "sparc": "sparc",
+ "sparc:v8plus": "sparc32plus",
+ "sparc:v9a" : "sparc64",
+ # no tricore in upstream gdb
+ "xtensa" : ["xtensa", "xtensaeb"]
+}
+
+def do_probe(gdb):
+ gdb_out = check_output([gdb,
+ "-ex", "set architecture",
+ "-ex", "quit"], stderr=STDOUT)
+
+ m = re.search(r"Valid arguments are (.*)",
+ gdb_out.decode("utf-8"))
+
+ valid_arches = set()
+
+ if m.group(1):
+ for arch in m.group(1).split(", "):
+ if arch in mappings:
+ mapping = mappings[arch]
+ if isinstance(mapping, str):
+ valid_arches.add(mapping)
+ else:
+ for entry in mapping:
+ valid_arches.add(entry)
+
+ return valid_arches
+
+def main() -> None:
+ parser = argparse.ArgumentParser(description='Probe GDB Architectures')
+ parser.add_argument('gdb', help='Path to GDB binary.')
+
+ args = parser.parse_args()
+
+ supported = do_probe(args.gdb)
+
+ print(" ".join(supported))
+
+if __name__ == '__main__':
+ main()
diff --git a/semihosting/arm-compat-semi.c b/semihosting/arm-compat-semi.c
index 62d8bae97f..564fe17f75 100644
--- a/semihosting/arm-compat-semi.c
+++ b/semihosting/arm-compat-semi.c
@@ -34,6 +34,7 @@
#include "qemu/osdep.h"
#include "qemu/timer.h"
#include "exec/gdbstub.h"
+#include "gdbstub/syscalls.h"
#include "semihosting/semihost.h"
#include "semihosting/console.h"
#include "semihosting/common-semi.h"
diff --git a/semihosting/guestfd.c b/semihosting/guestfd.c
index b05c52f26f..acb86b50dd 100644
--- a/semihosting/guestfd.c
+++ b/semihosting/guestfd.c
@@ -9,7 +9,7 @@
*/
#include "qemu/osdep.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/syscalls.h"
#include "semihosting/semihost.h"
#include "semihosting/guestfd.h"
#ifdef CONFIG_USER_ONLY
diff --git a/semihosting/syscalls.c b/semihosting/syscalls.c
index e89992cf90..68899ebb1c 100644
--- a/semihosting/syscalls.c
+++ b/semihosting/syscalls.c
@@ -7,7 +7,8 @@
*/
#include "qemu/osdep.h"
-#include "exec/gdbstub.h"
+#include "cpu.h"
+#include "gdbstub/syscalls.h"
#include "semihosting/guestfd.h"
#include "semihosting/syscalls.h"
#include "semihosting/console.h"
@@ -138,46 +139,48 @@ static void gdb_open(CPUState *cs, gdb_syscall_complete_cb complete,
gdb_open_complete = complete;
gdb_do_syscall(gdb_open_cb, "open,%s,%x,%x",
- fname, len, (target_ulong)gdb_flags, (target_ulong)mode);
+ (uint64_t)fname, (uint32_t)len,
+ (uint32_t)gdb_flags, (uint32_t)mode);
}
static void gdb_close(CPUState *cs, gdb_syscall_complete_cb complete,
GuestFD *gf)
{
- gdb_do_syscall(complete, "close,%x", (target_ulong)gf->hostfd);
+ gdb_do_syscall(complete, "close,%x", (uint32_t)gf->hostfd);
}
static void gdb_read(CPUState *cs, gdb_syscall_complete_cb complete,
GuestFD *gf, target_ulong buf, target_ulong len)
{
- gdb_do_syscall(complete, "read,%x,%x,%x",
- (target_ulong)gf->hostfd, buf, len);
+ gdb_do_syscall(complete, "read,%x,%lx,%lx",
+ (uint32_t)gf->hostfd, (uint64_t)buf, (uint64_t)len);
}
static void gdb_write(CPUState *cs, gdb_syscall_complete_cb complete,
GuestFD *gf, target_ulong buf, target_ulong len)
{
- gdb_do_syscall(complete, "write,%x,%x,%x",
- (target_ulong)gf->hostfd, buf, len);
+ gdb_do_syscall(complete, "write,%x,%lx,%lx",
+ (uint32_t)gf->hostfd, (uint64_t)buf, (uint64_t)len);
}
static void gdb_lseek(CPUState *cs, gdb_syscall_complete_cb complete,
GuestFD *gf, int64_t off, int gdb_whence)
{
gdb_do_syscall(complete, "lseek,%x,%lx,%x",
- (target_ulong)gf->hostfd, off, (target_ulong)gdb_whence);
+ (uint32_t)gf->hostfd, off, (uint32_t)gdb_whence);
}
static void gdb_isatty(CPUState *cs, gdb_syscall_complete_cb complete,
GuestFD *gf)
{
- gdb_do_syscall(complete, "isatty,%x", (target_ulong)gf->hostfd);
+ gdb_do_syscall(complete, "isatty,%x", (uint32_t)gf->hostfd);
}
static void gdb_fstat(CPUState *cs, gdb_syscall_complete_cb complete,
GuestFD *gf, target_ulong addr)
{
- gdb_do_syscall(complete, "fstat,%x,%x", (target_ulong)gf->hostfd, addr);
+ gdb_do_syscall(complete, "fstat,%x,%lx",
+ (uint32_t)gf->hostfd, (uint64_t)addr);
}
static void gdb_stat(CPUState *cs, gdb_syscall_complete_cb complete,
@@ -190,7 +193,8 @@ static void gdb_stat(CPUState *cs, gdb_syscall_complete_cb complete,
return;
}
- gdb_do_syscall(complete, "stat,%s,%x", fname, len, addr);
+ gdb_do_syscall(complete, "stat,%s,%lx",
+ (uint64_t)fname, (uint32_t)len, (uint64_t)addr);
}
static void gdb_remove(CPUState *cs, gdb_syscall_complete_cb complete,
@@ -202,7 +206,7 @@ static void gdb_remove(CPUState *cs, gdb_syscall_complete_cb complete,
return;
}
- gdb_do_syscall(complete, "unlink,%s", fname, len);
+ gdb_do_syscall(complete, "unlink,%s", (uint64_t)fname, (uint32_t)len);
}
static void gdb_rename(CPUState *cs, gdb_syscall_complete_cb complete,
@@ -222,7 +226,9 @@ static void gdb_rename(CPUState *cs, gdb_syscall_complete_cb complete,
return;
}
- gdb_do_syscall(complete, "rename,%s,%s", oname, olen, nname, nlen);
+ gdb_do_syscall(complete, "rename,%s,%s",
+ (uint64_t)oname, (uint32_t)olen,
+ (uint64_t)nname, (uint32_t)nlen);
}
static void gdb_system(CPUState *cs, gdb_syscall_complete_cb complete,
@@ -234,13 +240,14 @@ static void gdb_system(CPUState *cs, gdb_syscall_complete_cb complete,
return;
}
- gdb_do_syscall(complete, "system,%s", cmd, len);
+ gdb_do_syscall(complete, "system,%s", (uint64_t)cmd, (uint32_t)len);
}
static void gdb_gettimeofday(CPUState *cs, gdb_syscall_complete_cb complete,
target_ulong tv_addr, target_ulong tz_addr)
{
- gdb_do_syscall(complete, "gettimeofday,%x,%x", tv_addr, tz_addr);
+ gdb_do_syscall(complete, "gettimeofday,%lx,%lx",
+ (uint64_t)tv_addr, (uint64_t)tz_addr);
}
/*
diff --git a/softmmu/runstate.c b/softmmu/runstate.c
index 9b3611d56d..d1e04586db 100644
--- a/softmmu/runstate.c
+++ b/softmmu/runstate.c
@@ -30,7 +30,7 @@
#include "crypto/cipher.h"
#include "crypto/init.h"
#include "exec/cpu-common.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/syscalls.h"
#include "hw/boards.h"
#include "migration/misc.h"
#include "migration/postcopy-ram.h"
diff --git a/stubs/meson.build b/stubs/meson.build
index 7657467a5d..b2b5956d97 100644
--- a/stubs/meson.build
+++ b/stubs/meson.build
@@ -61,4 +61,5 @@ if have_system
else
stub_ss.add(files('qdev.c'))
endif
+stub_ss.add(files('semihost-all.c'))
stub_ss.add(when: 'CONFIG_VFIO_USER_SERVER', if_false: files('vfio-user-obj.c'))
diff --git a/stubs/semihost-all.c b/stubs/semihost-all.c
new file mode 100644
index 0000000000..a2a1fc9c6f
--- /dev/null
+++ b/stubs/semihost-all.c
@@ -0,0 +1,17 @@
+/*
+ * Semihosting Stubs for all targets
+ *
+ * Copyright (c) 2023 Linaro Ltd
+ *
+ * Stubs for all targets that don't actually do semihosting.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "semihosting/semihost.h"
+
+SemihostingTarget semihosting_get_target(void)
+{
+ return SEMIHOSTING_TARGET_AUTO;
+}
diff --git a/stubs/semihost.c b/stubs/semihost.c
index d65c9fd5dc..aad7a70353 100644
--- a/stubs/semihost.c
+++ b/stubs/semihost.c
@@ -28,11 +28,6 @@ bool semihosting_enabled(bool is_user)
return false;
}
-SemihostingTarget semihosting_get_target(void)
-{
- return SEMIHOSTING_TARGET_AUTO;
-}
-
/*
* All the rest are empty subs. We could g_assert_not_reached() but
* that adds extra weight to the final binary. Waste not want not.
diff --git a/target/alpha/gdbstub.c b/target/alpha/gdbstub.c
index 7db14f4431..0f8fa150f8 100644
--- a/target/alpha/gdbstub.c
+++ b/target/alpha/gdbstub.c
@@ -19,7 +19,7 @@
*/
#include "qemu/osdep.h"
#include "cpu.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/helpers.h"
int alpha_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n)
{
diff --git a/target/alpha/sys_helper.c b/target/alpha/sys_helper.c
index 25f6cb8894..c83c92dd4c 100644
--- a/target/alpha/sys_helper.c
+++ b/target/alpha/sys_helper.c
@@ -20,6 +20,7 @@
#include "qemu/osdep.h"
#include "cpu.h"
#include "exec/exec-all.h"
+#include "exec/tb-flush.h"
#include "exec/helper-proto.h"
#include "sysemu/runstate.h"
#include "sysemu/sysemu.h"
diff --git a/target/arm/gdbstub.c b/target/arm/gdbstub.c
index 3f799f5d05..78105b8078 100644
--- a/target/arm/gdbstub.c
+++ b/target/arm/gdbstub.c
@@ -20,6 +20,7 @@
#include "qemu/osdep.h"
#include "cpu.h"
#include "exec/gdbstub.h"
+#include "gdbstub/helpers.h"
#include "internals.h"
#include "cpregs.h"
diff --git a/target/arm/gdbstub64.c b/target/arm/gdbstub64.c
index 3bee892fb7..ec1e07f139 100644
--- a/target/arm/gdbstub64.c
+++ b/target/arm/gdbstub64.c
@@ -20,7 +20,7 @@
#include "qemu/log.h"
#include "cpu.h"
#include "internals.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/helpers.h"
int aarch64_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n)
{
diff --git a/target/arm/tcg/helper-a64.c b/target/arm/tcg/helper-a64.c
index 0972a4bdd0..c3edf163be 100644
--- a/target/arm/tcg/helper-a64.c
+++ b/target/arm/tcg/helper-a64.c
@@ -20,7 +20,7 @@
#include "qemu/osdep.h"
#include "qemu/units.h"
#include "cpu.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/helpers.h"
#include "exec/helper-proto.h"
#include "qemu/host-utils.h"
#include "qemu/log.h"
diff --git a/target/arm/tcg/m_helper.c b/target/arm/tcg/m_helper.c
index 081fc3f5f7..9758f225d6 100644
--- a/target/arm/tcg/m_helper.c
+++ b/target/arm/tcg/m_helper.c
@@ -9,6 +9,7 @@
#include "qemu/osdep.h"
#include "cpu.h"
#include "internals.h"
+#include "gdbstub/helpers.h"
#include "exec/helper-proto.h"
#include "qemu/main-loop.h"
#include "qemu/bitops.h"
diff --git a/target/avr/gdbstub.c b/target/avr/gdbstub.c
index 1c1b908c92..150344d8b9 100644
--- a/target/avr/gdbstub.c
+++ b/target/avr/gdbstub.c
@@ -19,7 +19,7 @@
*/
#include "qemu/osdep.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/helpers.h"
int avr_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n)
{
diff --git a/target/cris/gdbstub.c b/target/cris/gdbstub.c
index 2418d575b1..25c0ca33a5 100644
--- a/target/cris/gdbstub.c
+++ b/target/cris/gdbstub.c
@@ -19,7 +19,7 @@
*/
#include "qemu/osdep.h"
#include "cpu.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/helpers.h"
int crisv10_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n)
{
diff --git a/target/hexagon/gdbstub.c b/target/hexagon/gdbstub.c
index d152d01bfe..46083da620 100644
--- a/target/hexagon/gdbstub.c
+++ b/target/hexagon/gdbstub.c
@@ -16,7 +16,7 @@
*/
#include "qemu/osdep.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/helpers.h"
#include "cpu.h"
#include "internal.h"
diff --git a/target/hppa/gdbstub.c b/target/hppa/gdbstub.c
index 729c37b2ca..48a514384f 100644
--- a/target/hppa/gdbstub.c
+++ b/target/hppa/gdbstub.c
@@ -19,7 +19,7 @@
#include "qemu/osdep.h"
#include "cpu.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/helpers.h"
int hppa_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n)
{
diff --git a/target/i386/gdbstub.c b/target/i386/gdbstub.c
index 786971284a..ebb000df6a 100644
--- a/target/i386/gdbstub.c
+++ b/target/i386/gdbstub.c
@@ -19,7 +19,7 @@
*/
#include "qemu/osdep.h"
#include "cpu.h"
-#include "exec/gdbstub.h"
+#include "include/gdbstub/helpers.h"
#ifdef TARGET_X86_64
static const int gpr_map[16] = {
diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c
index 3d0c0b375f..52af81683c 100644
--- a/target/i386/whpx/whpx-all.c
+++ b/target/i386/whpx/whpx-all.c
@@ -12,7 +12,7 @@
#include "cpu.h"
#include "exec/address-spaces.h"
#include "exec/ioport.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/helpers.h"
#include "qemu/accel.h"
#include "sysemu/whpx.h"
#include "sysemu/cpus.h"
diff --git a/target/loongarch/gdbstub.c b/target/loongarch/gdbstub.c
index a4d1e28e36..fa3e034d15 100644
--- a/target/loongarch/gdbstub.c
+++ b/target/loongarch/gdbstub.c
@@ -10,6 +10,7 @@
#include "cpu.h"
#include "internals.h"
#include "exec/gdbstub.h"
+#include "gdbstub/helpers.h"
uint64_t read_fcc(CPULoongArchState *env)
{
diff --git a/target/m68k/gdbstub.c b/target/m68k/gdbstub.c
index eb2d030e14..1e5f033a12 100644
--- a/target/m68k/gdbstub.c
+++ b/target/m68k/gdbstub.c
@@ -19,7 +19,7 @@
*/
#include "qemu/osdep.h"
#include "cpu.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/helpers.h"
int m68k_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n)
{
diff --git a/target/m68k/helper.c b/target/m68k/helper.c
index 4621cf2402..3b3a6ea8bd 100644
--- a/target/m68k/helper.c
+++ b/target/m68k/helper.c
@@ -23,6 +23,7 @@
#include "exec/exec-all.h"
#include "exec/gdbstub.h"
#include "exec/helper-proto.h"
+#include "gdbstub/helpers.h"
#include "fpu/softfloat.h"
#include "qemu/qemu-print.h"
diff --git a/target/m68k/m68k-semi.c b/target/m68k/m68k-semi.c
index 87b1314925..88ad9ba814 100644
--- a/target/m68k/m68k-semi.c
+++ b/target/m68k/m68k-semi.c
@@ -20,7 +20,8 @@
#include "qemu/osdep.h"
#include "cpu.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/syscalls.h"
+#include "gdbstub/helpers.h"
#include "semihosting/syscalls.h"
#include "semihosting/softmmu-uaccess.h"
#include "hw/boards.h"
diff --git a/target/microblaze/gdbstub.c b/target/microblaze/gdbstub.c
index 8143fcae88..29ac6e9c0f 100644
--- a/target/microblaze/gdbstub.c
+++ b/target/microblaze/gdbstub.c
@@ -19,7 +19,7 @@
*/
#include "qemu/osdep.h"
#include "cpu.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/helpers.h"
/*
* GDB expects SREGs in the following order:
diff --git a/target/mips/gdbstub.c b/target/mips/gdbstub.c
index f1c2a2cf6d..62d7b72407 100644
--- a/target/mips/gdbstub.c
+++ b/target/mips/gdbstub.c
@@ -20,7 +20,7 @@
#include "qemu/osdep.h"
#include "cpu.h"
#include "internal.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/helpers.h"
#include "fpu_helper.h"
int mips_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n)
diff --git a/target/mips/tcg/sysemu/mips-semi.c b/target/mips/tcg/sysemu/mips-semi.c
index 85f0567a7f..f3735df7b9 100644
--- a/target/mips/tcg/sysemu/mips-semi.c
+++ b/target/mips/tcg/sysemu/mips-semi.c
@@ -20,7 +20,8 @@
#include "qemu/osdep.h"
#include "cpu.h"
#include "qemu/log.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/syscalls.h"
+#include "gdbstub/helpers.h"
#include "semihosting/softmmu-uaccess.h"
#include "semihosting/semihost.h"
#include "semihosting/console.h"
diff --git a/target/nios2/cpu.c b/target/nios2/cpu.c
index cff30823da..bc5cbf81c2 100644
--- a/target/nios2/cpu.c
+++ b/target/nios2/cpu.c
@@ -23,7 +23,7 @@
#include "qapi/error.h"
#include "cpu.h"
#include "exec/log.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/helpers.h"
#include "hw/qdev-properties.h"
static void nios2_cpu_set_pc(CPUState *cs, vaddr value)
diff --git a/target/nios2/nios2-semi.c b/target/nios2/nios2-semi.c
index f76e8588c5..3738774976 100644
--- a/target/nios2/nios2-semi.c
+++ b/target/nios2/nios2-semi.c
@@ -23,7 +23,8 @@
#include "qemu/osdep.h"
#include "cpu.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/syscalls.h"
+#include "gdbstub/helpers.h"
#include "semihosting/syscalls.h"
#include "semihosting/softmmu-uaccess.h"
#include "qemu/log.h"
diff --git a/target/openrisc/gdbstub.c b/target/openrisc/gdbstub.c
index 095bf76c12..d1074a0581 100644
--- a/target/openrisc/gdbstub.c
+++ b/target/openrisc/gdbstub.c
@@ -19,7 +19,7 @@
*/
#include "qemu/osdep.h"
#include "cpu.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/helpers.h"
int openrisc_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n)
{
diff --git a/target/openrisc/interrupt.c b/target/openrisc/interrupt.c
index c31c6f12c4..3887812810 100644
--- a/target/openrisc/interrupt.c
+++ b/target/openrisc/interrupt.c
@@ -21,7 +21,7 @@
#include "qemu/log.h"
#include "cpu.h"
#include "exec/exec-all.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/helpers.h"
#include "qemu/host-utils.h"
#ifndef CONFIG_USER_ONLY
#include "hw/loader.h"
diff --git a/target/openrisc/mmu.c b/target/openrisc/mmu.c
index 0b8afdbacf..603c26715e 100644
--- a/target/openrisc/mmu.c
+++ b/target/openrisc/mmu.c
@@ -22,7 +22,7 @@
#include "qemu/log.h"
#include "cpu.h"
#include "exec/exec-all.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/helpers.h"
#include "qemu/host-utils.h"
#include "hw/loader.h"
diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c
index d62ffe8a6f..0ce2e3c91d 100644
--- a/target/ppc/cpu_init.c
+++ b/target/ppc/cpu_init.c
@@ -20,7 +20,7 @@
#include "qemu/osdep.h"
#include "disas/dis-asm.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/helpers.h"
#include "kvm_ppc.h"
#include "sysemu/cpus.h"
#include "sysemu/hw_accel.h"
diff --git a/target/ppc/gdbstub.c b/target/ppc/gdbstub.c
index 1a0b9ca82c..d2bc1d7c53 100644
--- a/target/ppc/gdbstub.c
+++ b/target/ppc/gdbstub.c
@@ -20,6 +20,7 @@
#include "qemu/osdep.h"
#include "cpu.h"
#include "exec/gdbstub.h"
+#include "gdbstub/helpers.h"
#include "internal.h"
static int ppc_gdb_register_len_apple(int n)
diff --git a/target/riscv/csr.c b/target/riscv/csr.c
index ab566639e5..d522efc0b6 100644
--- a/target/riscv/csr.c
+++ b/target/riscv/csr.c
@@ -25,6 +25,7 @@
#include "time_helper.h"
#include "qemu/main-loop.h"
#include "exec/exec-all.h"
+#include "exec/tb-flush.h"
#include "sysemu/cpu-timers.h"
#include "qemu/guest-random.h"
#include "qapi/error.h"
diff --git a/target/riscv/gdbstub.c b/target/riscv/gdbstub.c
index 6048541606..840d1ec5c6 100644
--- a/target/riscv/gdbstub.c
+++ b/target/riscv/gdbstub.c
@@ -18,6 +18,7 @@
#include "qemu/osdep.h"
#include "exec/gdbstub.h"
+#include "gdbstub/helpers.h"
#include "cpu.h"
struct TypeSize {
diff --git a/target/rx/gdbstub.c b/target/rx/gdbstub.c
index 7eb2059a84..d7e0e6689b 100644
--- a/target/rx/gdbstub.c
+++ b/target/rx/gdbstub.c
@@ -17,7 +17,7 @@
*/
#include "qemu/osdep.h"
#include "cpu.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/helpers.h"
int rx_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n)
{
diff --git a/target/s390x/gdbstub.c b/target/s390x/gdbstub.c
index a5d69d0e0b..0cb69395b4 100644
--- a/target/s390x/gdbstub.c
+++ b/target/s390x/gdbstub.c
@@ -23,6 +23,7 @@
#include "s390x-internal.h"
#include "exec/exec-all.h"
#include "exec/gdbstub.h"
+#include "gdbstub/helpers.h"
#include "qemu/bitops.h"
#include "sysemu/hw_accel.h"
#include "sysemu/tcg.h"
diff --git a/target/s390x/helper.c b/target/s390x/helper.c
index 473c8e51b0..2b363aa959 100644
--- a/target/s390x/helper.c
+++ b/target/s390x/helper.c
@@ -21,7 +21,7 @@
#include "qemu/osdep.h"
#include "cpu.h"
#include "s390x-internal.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/helpers.h"
#include "qemu/timer.h"
#include "hw/s390x/ioinst.h"
#include "hw/s390x/pv.h"
diff --git a/target/sh4/gdbstub.c b/target/sh4/gdbstub.c
index 3488f68e32..d8e199fc06 100644
--- a/target/sh4/gdbstub.c
+++ b/target/sh4/gdbstub.c
@@ -19,7 +19,7 @@
*/
#include "qemu/osdep.h"
#include "cpu.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/helpers.h"
/* Hint: Use "set architecture sh4" in GDB to see fpu registers */
/* FIXME: We should use XML for this. */
diff --git a/target/sparc/gdbstub.c b/target/sparc/gdbstub.c
index 5d1e808e8c..a1c8fdc4d5 100644
--- a/target/sparc/gdbstub.c
+++ b/target/sparc/gdbstub.c
@@ -19,7 +19,7 @@
*/
#include "qemu/osdep.h"
#include "cpu.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/helpers.h"
#ifdef TARGET_ABI32
#define gdb_get_rega(buf, val) gdb_get_reg32(buf, val)
diff --git a/target/tricore/gdbstub.c b/target/tricore/gdbstub.c
index 3a27a7e65d..e8f8e5e6ea 100644
--- a/target/tricore/gdbstub.c
+++ b/target/tricore/gdbstub.c
@@ -18,7 +18,7 @@
*/
#include "qemu/osdep.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/helpers.h"
#define LCX_REGNUM 32
diff --git a/target/xtensa/core-dc232b.c b/target/xtensa/core-dc232b.c
index c982d09c24..9aba2667e3 100644
--- a/target/xtensa/core-dc232b.c
+++ b/target/xtensa/core-dc232b.c
@@ -27,7 +27,7 @@
#include "qemu/osdep.h"
#include "cpu.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/helpers.h"
#include "qemu/host-utils.h"
#include "qemu/timer.h"
diff --git a/target/xtensa/core-dc233c.c b/target/xtensa/core-dc233c.c
index 595ab9a90f..9b0a625063 100644
--- a/target/xtensa/core-dc233c.c
+++ b/target/xtensa/core-dc233c.c
@@ -27,7 +27,7 @@
#include "qemu/osdep.h"
#include "cpu.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/helpers.h"
#include "qemu/host-utils.h"
#include "core-dc233c/core-isa.h"
diff --git a/target/xtensa/core-de212.c b/target/xtensa/core-de212.c
index 50c995ba79..b08fe22e65 100644
--- a/target/xtensa/core-de212.c
+++ b/target/xtensa/core-de212.c
@@ -27,7 +27,7 @@
#include "qemu/osdep.h"
#include "cpu.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/helpers.h"
#include "qemu/host-utils.h"
#include "core-de212/core-isa.h"
diff --git a/target/xtensa/core-de233_fpu.c b/target/xtensa/core-de233_fpu.c
index 41af8057fb..8845cdb592 100644
--- a/target/xtensa/core-de233_fpu.c
+++ b/target/xtensa/core-de233_fpu.c
@@ -27,7 +27,7 @@
#include "qemu/osdep.h"
#include "cpu.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/helpers.h"
#include "qemu/host-utils.h"
#include "core-de233_fpu/core-isa.h"
diff --git a/target/xtensa/core-dsp3400.c b/target/xtensa/core-dsp3400.c
index 81e425c568..c0f94b9e27 100644
--- a/target/xtensa/core-dsp3400.c
+++ b/target/xtensa/core-dsp3400.c
@@ -27,7 +27,7 @@
#include "qemu/osdep.h"
#include "cpu.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/helpers.h"
#include "qemu/host-utils.h"
#include "core-dsp3400/core-isa.h"
diff --git a/target/xtensa/core-fsf.c b/target/xtensa/core-fsf.c
index 3327c50b4f..310be8d61f 100644
--- a/target/xtensa/core-fsf.c
+++ b/target/xtensa/core-fsf.c
@@ -27,7 +27,7 @@
#include "qemu/osdep.h"
#include "cpu.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/helpers.h"
#include "qemu/host-utils.h"
#include "core-fsf/core-isa.h"
diff --git a/target/xtensa/core-lx106.c b/target/xtensa/core-lx106.c
index 7a771d09a6..7f71d088f3 100644
--- a/target/xtensa/core-lx106.c
+++ b/target/xtensa/core-lx106.c
@@ -27,7 +27,7 @@
#include "qemu/osdep.h"
#include "cpu.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/helpers.h"
#include "qemu/host-utils.h"
#include "core-lx106/core-isa.h"
diff --git a/target/xtensa/core-sample_controller.c b/target/xtensa/core-sample_controller.c
index fd5de5576b..8867001aac 100644
--- a/target/xtensa/core-sample_controller.c
+++ b/target/xtensa/core-sample_controller.c
@@ -27,7 +27,7 @@
#include "qemu/osdep.h"
#include "cpu.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/helpers.h"
#include "qemu/host-utils.h"
#include "core-sample_controller/core-isa.h"
diff --git a/target/xtensa/core-test_kc705_be.c b/target/xtensa/core-test_kc705_be.c
index 294c16f2f4..bd082f49aa 100644
--- a/target/xtensa/core-test_kc705_be.c
+++ b/target/xtensa/core-test_kc705_be.c
@@ -27,7 +27,7 @@
#include "qemu/osdep.h"
#include "cpu.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/helpers.h"
#include "qemu/host-utils.h"
#include "core-test_kc705_be/core-isa.h"
diff --git a/target/xtensa/core-test_mmuhifi_c3.c b/target/xtensa/core-test_mmuhifi_c3.c
index c0e5d32d1e..3090dd01ed 100644
--- a/target/xtensa/core-test_mmuhifi_c3.c
+++ b/target/xtensa/core-test_mmuhifi_c3.c
@@ -27,7 +27,7 @@
#include "qemu/osdep.h"
#include "cpu.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/helpers.h"
#include "qemu/host-utils.h"
#include "core-test_mmuhifi_c3/core-isa.h"
diff --git a/target/xtensa/gdbstub.c b/target/xtensa/gdbstub.c
index b6696063e5..4b3bfb7e59 100644
--- a/target/xtensa/gdbstub.c
+++ b/target/xtensa/gdbstub.c
@@ -19,7 +19,7 @@
*/
#include "qemu/osdep.h"
#include "cpu.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/helpers.h"
#include "qemu/log.h"
enum {
diff --git a/target/xtensa/helper.c b/target/xtensa/helper.c
index 2aa9777a8e..dbeb97a953 100644
--- a/target/xtensa/helper.c
+++ b/target/xtensa/helper.c
@@ -29,7 +29,7 @@
#include "qemu/log.h"
#include "cpu.h"
#include "exec/exec-all.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/helpers.h"
#include "exec/helper-proto.h"
#include "qemu/error-report.h"
#include "qemu/qemu-print.h"
diff --git a/target/xtensa/import_core.sh b/target/xtensa/import_core.sh
index b4c15556c2..17dfec8957 100755
--- a/target/xtensa/import_core.sh
+++ b/target/xtensa/import_core.sh
@@ -41,7 +41,7 @@ tar -xf "$OVERLAY" -O binutils/xtensa-modules.c | \
cat <<EOF > "${TARGET}.c"
#include "qemu/osdep.h"
#include "cpu.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/helpers.h"
#include "qemu/host-utils.h"
#include "core-$NAME/core-isa.h"
diff --git a/tests/tcg/aarch64/Makefile.target b/tests/tcg/aarch64/Makefile.target
index db122ab4ff..9e91a20b0d 100644
--- a/tests/tcg/aarch64/Makefile.target
+++ b/tests/tcg/aarch64/Makefile.target
@@ -81,7 +81,7 @@ sha512-vector: sha512.c
TESTS += sha512-vector
-ifneq ($(HAVE_GDB_BIN),)
+ifeq ($(HOST_GDB_SUPPORTS_ARCH),y)
GDB_SCRIPT=$(SRC_PATH)/tests/guest-debug/run-test.py
run-gdbstub-sysregs: sysregs
diff --git a/tests/tcg/multiarch/Makefile.target b/tests/tcg/multiarch/Makefile.target
index ae8b3d7268..373db69648 100644
--- a/tests/tcg/multiarch/Makefile.target
+++ b/tests/tcg/multiarch/Makefile.target
@@ -64,6 +64,7 @@ run-test-mmap-%: test-mmap
$(call run-test, test-mmap-$*, $(QEMU) -p $* $<, $< ($* byte pages))
ifneq ($(HAVE_GDB_BIN),)
+ifeq ($(HOST_GDB_SUPPORTS_ARCH),y)
GDB_SCRIPT=$(SRC_PATH)/tests/guest-debug/run-test.py
run-gdbstub-sha1: sha1
@@ -89,6 +90,10 @@ run-gdbstub-thread-breakpoint: testthread
else
run-gdbstub-%:
+ $(call skip-test, "gdbstub test $*", "no guest arch support")
+endif
+else
+run-gdbstub-%:
$(call skip-test, "gdbstub test $*", "need working gdb")
endif
EXTRA_RUNS += run-gdbstub-sha1 run-gdbstub-qxfer-auxv-read \
diff --git a/tests/tcg/multiarch/system/Makefile.softmmu-target b/tests/tcg/multiarch/system/Makefile.softmmu-target
index 368b64d531..5f432c95f3 100644
--- a/tests/tcg/multiarch/system/Makefile.softmmu-target
+++ b/tests/tcg/multiarch/system/Makefile.softmmu-target
@@ -15,6 +15,7 @@ MULTIARCH_TEST_SRCS=$(wildcard $(MULTIARCH_SYSTEM_SRC)/*.c)
MULTIARCH_TESTS = $(patsubst $(MULTIARCH_SYSTEM_SRC)/%.c, %, $(MULTIARCH_TEST_SRCS))
ifneq ($(HAVE_GDB_BIN),)
+ifeq ($(HOST_GDB_SUPPORTS_ARCH),y)
GDB_SCRIPT=$(SRC_PATH)/tests/guest-debug/run-test.py
run-gdbstub-memory: memory
@@ -26,7 +27,10 @@ run-gdbstub-memory: memory
"-monitor none -display none -chardev file$(COMMA)path=$<.out$(COMMA)id=output $(QEMU_OPTS)" \
--bin $< --test $(MULTIARCH_SRC)/gdbstub/memory.py, \
softmmu gdbstub support)
-
+else
+run-gdbstub-%:
+ $(call skip-test, "gdbstub test $*", "no guest arch support")
+endif
else
run-gdbstub-%:
$(call skip-test, "gdbstub test $*", "need working gdb")
diff --git a/tests/tcg/s390x/Makefile.target b/tests/tcg/s390x/Makefile.target
index 72ad309b27..b7f576f983 100644
--- a/tests/tcg/s390x/Makefile.target
+++ b/tests/tcg/s390x/Makefile.target
@@ -51,7 +51,7 @@ $(Z15_TESTS): CFLAGS+=-march=z15 -O2
TESTS+=$(Z15_TESTS)
endif
-ifneq ($(HAVE_GDB_BIN),)
+ifeq ($(HOST_GDB_SUPPORTS_ARCH),y)
GDB_SCRIPT=$(SRC_PATH)/tests/guest-debug/run-test.py
run-gdbstub-signals-s390x: signals-s390x