summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChris Johns <chrisj@rtems.org>2016-11-25 15:13:36 +1100
committerChris Johns <chrisj@rtems.org>2016-11-29 08:50:40 +1100
commita0d4e9933c57693f99203da358bb8aaa8a5d50d9 (patch)
treed17a98faf93185d2baa18fba6ee97b0e2916b2e4
parentarm/zynq: Wait for the UART TX FIFO to empty on reset. (diff)
downloadrtems-a0d4e9933c57693f99203da358bb8aaa8a5d50d9.tar.bz2
cpukit: Add libdebugger, a remote debugger agent for GDB.
-rw-r--r--cpukit/Makefile.am9
-rw-r--r--cpukit/configure.ac12
-rw-r--r--cpukit/libdebugger/Makefile.am23
-rw-r--r--cpukit/libdebugger/preinstall.am9
-rw-r--r--cpukit/libdebugger/rtems-debugger-arm.c1193
-rw-r--r--cpukit/libdebugger/rtems-debugger-block.c73
-rw-r--r--cpukit/libdebugger/rtems-debugger-block.h75
-rw-r--r--cpukit/libdebugger/rtems-debugger-cmd.c208
-rw-r--r--cpukit/libdebugger/rtems-debugger-i386.c421
-rw-r--r--cpukit/libdebugger/rtems-debugger-remote-tcp.c342
-rw-r--r--cpukit/libdebugger/rtems-debugger-remote-tcp.h47
-rw-r--r--cpukit/libdebugger/rtems-debugger-remote.c68
-rw-r--r--cpukit/libdebugger/rtems-debugger-remote.h71
-rw-r--r--cpukit/libdebugger/rtems-debugger-server.c1993
-rw-r--r--cpukit/libdebugger/rtems-debugger-server.h204
-rw-r--r--cpukit/libdebugger/rtems-debugger-target.c400
-rw-r--r--cpukit/libdebugger/rtems-debugger-target.h237
-rw-r--r--cpukit/libdebugger/rtems-debugger-threads.c577
-rw-r--r--cpukit/libdebugger/rtems-debugger-threads.h272
-rw-r--r--cpukit/libdebugger/rtems-debugger.h81
-rw-r--r--cpukit/preinstall.am18
-rw-r--r--cpukit/wrapup/Makefile.am4
-rw-r--r--testsuites/libtests/Makefile.am4
-rw-r--r--testsuites/libtests/configure.ac12
-rw-r--r--testsuites/libtests/debugger01/Makefile.am21
-rw-r--r--testsuites/libtests/debugger01/debugger01.doc18
-rw-r--r--testsuites/libtests/debugger01/debugger01.scn188
-rw-r--r--testsuites/libtests/debugger01/init.c53
-rw-r--r--testsuites/libtests/debugger01/remote.c321
-rw-r--r--testsuites/libtests/debugger01/system.h37
30 files changed, 6991 insertions, 0 deletions
diff --git a/cpukit/Makefile.am b/cpukit/Makefile.am
index 6ca5e1652e..73bf35d8ba 100644
--- a/cpukit/Makefile.am
+++ b/cpukit/Makefile.am
@@ -18,6 +18,7 @@ SUBDIRS += libmd
SUBDIRS += libgnat
SUBDIRS += libdl
SUBDIRS += libstdthreads
+SUBDIRS += libdebugger
SUBDIRS += wrapup
SUBDIRS += zlib
@@ -99,6 +100,14 @@ include_rtems_rtl_HEADERS += libdl/rtl-indirect-ptr.h libdl/rtl-sym.h libdl/rtl-
include_rtems_rtl_HEADERS += libdl/rap.h libdl/rap-shell.h
endif
+if LIBDEBUGGER
+include_rtems_HEADERS += libdebugger/rtems-debugger.h
+include_rtems_debuggerdir = $(includedir)/rtems/debugger
+include_rtems_debugger_HEADERS =
+include_rtems_debugger_HEADERS += libdebugger/rtems-debugger-server.h
+include_rtems_debugger_HEADERS += libdebugger/rtems-debugger-remote.h
+endif
+
include_rtems_HEADERS += include/rtems/bspIo.h
include_rtems_HEADERS += include/rtems/print.h
include_rtems_HEADERS += include/rtems/printer.h
diff --git a/cpukit/configure.ac b/cpukit/configure.ac
index 5a6c2e66bf..3ebf5960e7 100644
--- a/cpukit/configure.ac
+++ b/cpukit/configure.ac
@@ -396,6 +396,17 @@ esac
AM_CONDITIONAL(LIBDL,[test x"$HAVE_LIBDL" = x"yes"])
AC_MSG_RESULT([$HAVE_LIBDL])
+# Filter debugger to only build for architectures that have a target backend
+AC_MSG_CHECKING([whether CPU supports libdebugger])
+case $RTEMS_CPU in
+ arm | i386)
+ HAVE_LIBDEBUGGER=yes ;;
+ *)
+ HAVE_LIBDEBUGGER=no ;;
+esac
+AM_CONDITIONAL(LIBDEBUGGER,[test x"$HAVE_LIBDEBUGGER" = x"yes"])
+AC_MSG_RESULT([$HAVE_LIBDEBUGGER])
+
AC_MSG_CHECKING([whether CPU supports SHA])
case $RTEMS_CPU in
m32c)
@@ -474,6 +485,7 @@ libi2c/Makefile
libmd/Makefile
libdl/Makefile
libstdthreads/Makefile
+libdebugger/Makefile
zlib/Makefile
ftpd/Makefile
telnetd/Makefile
diff --git a/cpukit/libdebugger/Makefile.am b/cpukit/libdebugger/Makefile.am
new file mode 100644
index 0000000000..72a89447d7
--- /dev/null
+++ b/cpukit/libdebugger/Makefile.am
@@ -0,0 +1,23 @@
+if LIBDEBUGGER
+
+include $(top_srcdir)/automake/compile.am
+
+noinst_LIBRARIES = libdebugger.a
+libdebugger_a_SOURCES = \
+ rtems-debugger-block.c \
+ rtems-debugger-cmd.c \
+ rtems-debugger-remote.c \
+ rtems-debugger-server.c \
+ rtems-debugger-target.c \
+ rtems-debugger-threads.c \
+ rtems-debugger-@RTEMS_CPU@.c
+
+if LIBNETWORKING
+libdebugger_a_SOURCES += \
+ rtems-debugger-remote-tcp.c
+endif
+
+endif
+
+include $(srcdir)/preinstall.am
+include $(top_srcdir)/automake/local.am
diff --git a/cpukit/libdebugger/preinstall.am b/cpukit/libdebugger/preinstall.am
new file mode 100644
index 0000000000..695eb30912
--- /dev/null
+++ b/cpukit/libdebugger/preinstall.am
@@ -0,0 +1,9 @@
+## Automatically generated by ampolish3 - Do not edit
+
+if AMPOLISH3
+$(srcdir)/preinstall.am: Makefile.am
+ $(AMPOLISH3) $(srcdir)/Makefile.am > $(srcdir)/preinstall.am
+endif
+
+if LIBDEBUGGER
+endif
diff --git a/cpukit/libdebugger/rtems-debugger-arm.c b/cpukit/libdebugger/rtems-debugger-arm.c
new file mode 100644
index 0000000000..945dc9c2d9
--- /dev/null
+++ b/cpukit/libdebugger/rtems-debugger-arm.c
@@ -0,0 +1,1193 @@
+/*
+ * Copyright (c) 2016 Chris Johns <chrisj@rtems.org>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#define TARGET_DEBUG 0
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <errno.h>
+#include <inttypes.h>
+#include <stdlib.h>
+
+#include <rtems.h>
+#include <rtems/score/threadimpl.h>
+
+#include <libcpu/arm-cp15.h>
+#include <bsp/linker-symbols.h>
+
+#include "rtems-debugger-target.h"
+#include "rtems-debugger-threads.h"
+
+#if TARGET_DEBUG
+#include <rtems/bspIo.h>
+#endif
+
+/*
+ * The ARM has 2 interrupt bits.
+ */
+#define CPSR_IRQ_DISABLE 0x80 /* IIQ disabled when 1 */
+#define CPSR_FIQ_DISABLE 0x40 /* FIQ disabled when 1 */
+#define CPSR_INTS_MASK (CPSR_IRQ_DISABLE | CPSR_FIQ_DISABLE)
+
+/*
+ * Software breakpoint block size.
+ */
+#define RTEMS_DEBUGGER_SWBREAK_NUM 64
+
+/*
+ * Number of registers.
+ */
+#define RTEMS_DEBUGGER_NUMREGS 26
+
+/*
+ * Number of bytes per register.
+ */
+#define RTEMS_DEBUGGER_REGBYTES 4
+
+/*
+ * Number of bytes of registers.
+ */
+#define RTEMS_DEBUGGER_NUMREGBYTES \
+ (RTEMS_DEBUGGER_NUMREGS * RTEMS_DEBUGGER_REGBYTES)
+
+/*
+ * Debugger registers layout.
+ */
+#define REG_R0 0
+#define REG_R1 1
+#define REG_R2 2
+#define REG_R3 3
+#define REG_R4 4
+#define REG_R5 5
+#define REG_R6 6
+#define REG_R7 7
+#define REG_R8 8
+#define REG_R9 9
+#define REG_R10 10
+#define REG_R11 11
+#define REG_R12 12
+#define REG_SP 13
+#define REG_LR 14
+#define REG_PC 15
+#define REG_CPSR 25
+
+/**
+ * The various status registers.
+ */
+#if defined(ARM_MULTILIB_ARCH_V4)
+ #define FRAME_SR frame->register_cpsr
+#elif defined(ARM_MULTILIB_ARCH_V7M)
+ #define FRAME_SR frame->register_xpsr
+#else
+ #error ARM architecture is not supported.
+#endif
+
+/**
+ * The breakpoint.
+ */
+#ifdef __thumb__
+ static const uint8_t breakpoint[2] = { 0x55, 0xbe };
+#else
+ static const uint8_t breakpoint[4] = { 0x75, 0xe0a, 0x20, 0xe1 };
+#endif
+
+/**
+ * Target lock.
+ */
+RTEMS_INTERRUPT_LOCK_DEFINE(static, target_lock, "target_lock")
+
+/**
+ * The init value for the text section.
+ */
+static uint32_t text_section_flags;
+
+/**
+ * Is a session active?
+ */
+static bool debug_session_active;
+
+/*
+ * ARM debug hardware.
+ */
+static int debug_version;
+static int debug_revision;;
+static int hw_breakpoints;
+static int hw_watchpoints;
+
+/**
+ * Hardware break and watch points.
+ */
+typedef struct
+{
+ bool enabled;
+ bool loaded;
+ void* address;
+ size_t length;
+ CPU_Exception_frame* frame;
+ uint32_t control;
+ uint32_t value;
+} arm_debug_hwbreak;
+
+#define ARM_HW_BREAKPOINT_MAX (16)
+#define ARM_HW_WATCHPOINT_MAX (16)
+
+/*
+ * Types of break points. Only the 2 we use listed.
+ */
+#define ARM_HW_BP_UNLINKED_INSTR_MATCH (0x00)
+#define ARM_HW_BP_UNLINKED_INSTR_MISMATCH (0x04)
+
+/*
+ * Privilege levels.
+ */
+#define ARM_HW_BP_PRIV_PL0_SUP_SYS (0x00)
+#define ARM_HW_BP_PRIV_PL1_ONLY (0x01)
+#define ARM_HW_BP_PRIV_PL0_ONLY (0x02)
+#define ARM_HW_BP_PRIV_ALL_MODES (0x03)
+
+static arm_debug_hwbreak hw_breaks[ARM_HW_BREAKPOINT_MAX];
+//static arm_debug_hwbreak hw_watches[ARM_HW_WATCHPOINT_MAX];
+
+#if TARGET_DEBUG
+static void target_printk(const char* format, ...) RTEMS_PRINTFLIKE(1, 2);
+static void
+target_printk(const char* format, ...)
+{
+ va_list ap;
+ va_start(ap, format);
+ vprintk(format, ap);
+ va_end(ap);
+}
+static const char*
+mode_label(int mode)
+{
+ switch (mode) {
+ case 0x10:
+ return "USR";
+ case 0x11:
+ return "FIQ";
+ case 0x12:
+ return "IRQ";
+ case 0x13:
+ return "SVC";
+ case 0x16:
+ return "MON";
+ case 0x17:
+ return "ABT";
+ case 0x1a:
+ return "HYP";
+ case 0x1b:
+ return "UND";
+ case 0x1f:
+ return "SYS";
+ }
+ return "---";
+}
+#else
+#define target_printk(_fmt, ...)
+#define mode_labels(_m) NULL
+#endif
+
+/*
+ * Read and write a CP14 register.
+ *
+ * The software debug event registers are not easy to program because there are
+ * up to 32 registers and the instructions have to assembler for each of the 32
+ * registers, you cannot program it. This means there is a switch table to do
+ * this.
+ */
+#define ARM_CP14_INSTR(_opc, _val, _CRn, _CRm, _opc2) \
+ #_opc " p14, 0, %[" #_val "], c" #_CRn ", c" #_CRm ", " #_opc2 "\n"
+
+#define ARM_CP14_WRITE(_val, _CRn, _CRm, _opc2) \
+ do { \
+ ARM_SWITCH_REGISTERS; \
+ asm volatile( \
+ ARM_SWITCH_TO_ARM \
+ ARM_CP14_INSTR(mcr, val, _CRn, _CRm, _opc2) \
+ ARM_SWITCH_BACK \
+ : ARM_SWITCH_OUTPUT : [val] "r" (_val)); \
+ } while (0)
+
+#define ARM_CP14_READ(_val, _CRn, _CRm, _opc2) \
+ do { \
+ ARM_SWITCH_REGISTERS; \
+ asm volatile( \
+ ARM_SWITCH_TO_ARM \
+ ARM_CP14_INSTR(mrc, val, _CRn, _CRm, _opc2) \
+ ARM_SWITCH_BACK \
+ : [val] "=&r" (_val) ARM_SWITCH_ADDITIONAL_OUTPUT); \
+ } while (0)
+
+static int
+arm_debug_probe(rtems_debugger_target* target)
+{
+ #define ID_VALUE(_i, _h, _l) ((_i >> _l) & ((1 << ((_h - _l) + 1)) -1))
+ uint32_t val;
+ const char const* vl = "[Invalid version]";
+ const char const* labels[] = {
+ "ARMv6 [v6]",
+ "ARMv6 [v6.1]",
+ "ARMv7 [v7, all CP14 registers]",
+ "ARMv7 [v7, baseline CP14 registers]",
+ "ARMv7 [v7.1]"
+ };
+ ARM_CP14_READ(val, 0, 0, 0);
+ debug_version = ID_VALUE(val, 19, 16);
+ if (debug_version < 1 || debug_version > 5) {
+ rtems_debugger_printf("rtems-db: arm debug: (v%d.%d) not supported\n",
+ debug_version, debug_revision);
+ errno = EIO;
+ return -1;
+ }
+ vl = labels[debug_version - 1];
+ debug_revision = ID_VALUE(val, 3, 0);
+ hw_breakpoints = ID_VALUE(val, 27, 24);
+ hw_watchpoints = ID_VALUE(val, 31, 28);
+ rtems_debugger_printf("rtems-db: arm debug: (v%d.%d) %s breakpoints:%d watchpoints:%d\n",
+ debug_version, debug_revision, vl,
+ hw_breakpoints, hw_watchpoints);
+ ARM_CP14_READ(val, 0, 1, 0);
+ if ((val & (1 << 15)) == 0) {
+ switch (debug_version) {
+ case 1:
+ case 2:
+ ARM_CP14_WRITE(val | (1 << 15), 0, 1, 0);
+ break;
+ case 3:
+ case 4:
+ case 5:
+ default:
+ ARM_CP14_WRITE(val | (1 << 15), 0, 2, 2);
+ break;
+ }
+ ARM_CP14_READ(val, 0, 1, 0);
+ if ((val & (1 << 15)) == 0) {
+ rtems_debugger_printf("rtems-db: arm debug: cannot enter monitor mode\n");
+ errno = EIO;
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static inline void
+arm_debug_break_setup(arm_debug_hwbreak* bp,
+ uint32_t address,
+ uint32_t type,
+ uint32_t byte_address_select,
+ uint32_t privilege)
+{
+ bp->control = (((type & 0xf) << 20) |
+ ((byte_address_select & 0xf) << 5) |
+ ((privilege & 0x3) << 1) | 1);
+ bp->value = (intptr_t) address;
+}
+
+static void
+arm_debug_break_write_control(int bp, uint32_t control)
+{
+ switch (bp) {
+ case 0:
+ ARM_CP14_WRITE(control, 0, 0, 5);
+ break;
+ case 1:
+ ARM_CP14_WRITE(control, 0, 1, 5);
+ break;
+ case 2:
+ ARM_CP14_WRITE(control, 0, 2, 5);
+ break;
+ case 3:
+ ARM_CP14_WRITE(control, 0, 3, 5);
+ break;
+ case 4:
+ ARM_CP14_WRITE(control, 0, 4, 5);
+ break;
+ case 5:
+ ARM_CP14_WRITE(control, 0, 5, 5);
+ break;
+ case 6:
+ ARM_CP14_WRITE(control, 0, 6, 5);
+ break;
+ case 7:
+ ARM_CP14_WRITE(control, 0, 7, 5);
+ break;
+ case 8:
+ ARM_CP14_WRITE(control, 0, 8, 5);
+ break;
+ case 9:
+ ARM_CP14_WRITE(control, 0, 9, 5);
+ break;
+ case 10:
+ ARM_CP14_WRITE(control, 0, 10, 5);
+ break;
+ case 11:
+ ARM_CP14_WRITE(control, 0, 11, 5);
+ break;
+ case 12:
+ ARM_CP14_WRITE(control, 0, 12, 5);
+ break;
+ case 13:
+ ARM_CP14_WRITE(control, 0, 13, 5);
+ break;
+ case 14:
+ ARM_CP14_WRITE(control, 0, 14, 5);
+ break;
+ case 15:
+ ARM_CP14_WRITE(control, 0, 15, 5);
+ break;
+ }
+}
+
+static void
+arm_debug_break_write_value(int bp, uint32_t value)
+{
+ switch (bp) {
+ case 0:
+ ARM_CP14_WRITE(value, 0, 0, 4);
+ break;
+ case 1:
+ ARM_CP14_WRITE(value, 0, 1, 4);
+ break;
+ case 2:
+ ARM_CP14_WRITE(value, 0, 2, 4);
+ break;
+ case 3:
+ ARM_CP14_WRITE(value, 0, 3, 4);
+ break;
+ case 4:
+ ARM_CP14_WRITE(value, 0, 4, 4);
+ break;
+ case 5:
+ ARM_CP14_WRITE(value, 0, 5, 4);
+ break;
+ case 6:
+ ARM_CP14_WRITE(value, 0, 6, 4);
+ break;
+ case 7:
+ ARM_CP14_WRITE(value, 0, 7, 4);
+ break;
+ case 8:
+ ARM_CP14_WRITE(value, 0, 8, 4);
+ break;
+ case 9:
+ ARM_CP14_WRITE(value, 0, 9, 4);
+ break;
+ case 10:
+ ARM_CP14_WRITE(value, 0, 10, 4);
+ break;
+ case 11:
+ ARM_CP14_WRITE(value, 0, 11, 4);
+ break;
+ case 12:
+ ARM_CP14_WRITE(value, 0, 12, 4);
+ break;
+ case 13:
+ ARM_CP14_WRITE(value, 0, 13, 4);
+ break;
+ case 14:
+ ARM_CP14_WRITE(value, 0, 14, 4);
+ break;
+ case 15:
+ ARM_CP14_WRITE(value, 0, 15, 4);
+ break;
+ }
+}
+
+static void
+arm_debug_break_clear(void)
+{
+ arm_debug_hwbreak* bp = &hw_breaks[0];
+ int i;
+ for (i = 0; i < hw_breakpoints; ++i, ++bp) {
+ bp->enabled = false;
+ bp->loaded = false;
+ }
+}
+
+static void
+arm_debug_break_load(void)
+{
+ arm_debug_hwbreak* bp = &hw_breaks[0];
+ int i;
+ for (i = 0; i < hw_breakpoints; ++i, ++bp) {
+ if (bp->enabled && !bp->loaded) {
+ bp->loaded = true;
+ target_printk("]]} hwbp: %i: v:%08lx c:%08lx l:%08x\n",
+ i, bp->value, bp->control, bp->length);
+ arm_debug_break_write_value(i, bp->value);
+ arm_debug_break_write_control(i, bp->control);
+ }
+ }
+}
+
+static void
+arm_debug_break_unload(void)
+{
+ arm_debug_hwbreak* bp = &hw_breaks[0];
+ int i;
+ for (i = 0; i < hw_breakpoints; ++i, ++bp) {
+ bp->loaded = false;
+ arm_debug_break_write_control(i, 0);
+ }
+}
+
+#if NOT_USED_BUT_KEEPING
+static size_t
+arm_debug_break_length(void* pc)
+{
+ arm_debug_hwbreak* bp = &hw_breaks[0];
+ int i;
+
+ for (i = 0; i < hw_breakpoints; ++i, ++bp) {
+ if (bp->enabled && bp->address == pc) {
+ return bp->length;
+ }
+ }
+ return sizeof(DB_UINT);
+}
+#endif
+
+int
+rtems_debugger_target_configure(rtems_debugger_target* target)
+{
+ target->capabilities = (RTEMS_DEBUGGER_TARGET_CAP_SWBREAK);
+ target->reg_num = RTEMS_DEBUGGER_NUMREGS;
+ target->reg_size = sizeof(uint32_t);
+ target->breakpoint = &breakpoint[0];
+ target->breakpoint_size = sizeof(breakpoint);
+ return arm_debug_probe(target);
+}
+
+static void
+target_exception(CPU_Exception_frame* frame)
+{
+#if TARGET_DEBUG
+ uint32_t ifsr = arm_cp15_get_instruction_fault_status();
+#endif
+
+ target_printk("[} frame = %08lx sig=%d vector=%x ifsr=%08lx pra=%08x\n",
+ (uint32_t) frame,
+ rtems_debugger_target_exception_to_signal(frame),
+ frame->vector, ifsr, (intptr_t) frame->register_pc);
+
+ if ((FRAME_SR & (1 << 5)) == 0)
+ frame->register_pc = (void*) ((intptr_t) frame->register_pc - 8);
+ else
+ frame->register_pc = (void*) ((intptr_t) frame->register_pc - 4);
+
+ target_printk("[} R0 = %08" PRIx32 " R1 = %08" PRIx32 \
+ " R2 = %08" PRIx32 " R3 = %08" PRIx32 "\n",
+ frame->register_r0, frame->register_r1,
+ frame->register_r2, frame->register_r3);
+ target_printk("[} R4 = %08" PRIx32 " R5 = %08" PRIx32 \
+ " R6 = %08" PRIx32 " R7 = %08" PRIx32 "\n",
+ frame->register_r4, frame->register_r5,
+ frame->register_r6, frame->register_r7);
+ target_printk("[} R8 = %08" PRIx32 " R9 = %08" PRIx32 \
+ " R10 = %08" PRIx32 " R11 = %08" PRIx32 "\n",
+ frame->register_r8, frame->register_r9,
+ frame->register_r10, frame->register_r11);
+ target_printk("[} R12 = %08" PRIx32 " SP = %08" PRIx32 \
+ " LR = %08" PRIxPTR " PC = %08" PRIxPTR "\n", \
+ frame->register_r12, frame->register_sp,
+ (intptr_t) frame->register_lr, (intptr_t) frame->register_pc);
+ target_printk("[} CPSR = %08" PRIx32 " %c%c%c%c%c%c%c%c%c%c%c" \
+ " GE:%" PRIx32 " IT:%02" PRIx32 " M:%" PRIx32 " %s\n",
+ FRAME_SR,
+ (FRAME_SR & (1 << 31)) != 0 ? 'N' : '-',
+ (FRAME_SR & (1 << 30)) != 0 ? 'Z' : '-',
+ (FRAME_SR & (1 << 29)) != 0 ? 'C' : '-',
+ (FRAME_SR & (1 << 28)) != 0 ? 'V' : '-',
+ (FRAME_SR & (1 << 27)) != 0 ? 'Q' : '-',
+ (FRAME_SR & (1 << 24)) != 0 ? 'J' : '-',
+ (FRAME_SR & (1 << 9)) != 0 ? 'E' : '-',
+ (FRAME_SR & (1 << 8)) != 0 ? 'A' : '-',
+ (FRAME_SR & (1 << 7)) != 0 ? 'I' : '-',
+ (FRAME_SR & (1 << 6)) != 0 ? 'F' : '-',
+ (FRAME_SR & (1 << 5)) != 0 ? 'T' : '-',
+ ((FRAME_SR >> (25 - 5)) & (0x3 << 5)) | ((FRAME_SR >> 10) & 0x1f),
+ (FRAME_SR >> 16) & 0xf,
+ FRAME_SR & 0x1f, mode_label(FRAME_SR & 0x1f));
+
+ arm_debug_break_clear();
+
+ if (!debug_session_active)
+ _ARM_Exception_default(frame);
+
+ switch (rtems_debugger_target_exception(frame)) {
+ case rtems_debugger_target_exc_consumed:
+ default:
+ break;
+ case rtems_debugger_target_exc_step:
+ FRAME_SR |= CPSR_INTS_MASK;
+ break;
+ case rtems_debugger_target_exc_cascade:
+ target_printk("rtems-db: unhandled exception: cascading\n");
+ _ARM_Exception_default(frame);
+ break;
+ }
+
+ target_printk("[} resuming frame = %08lx PC = %08" PRIxPTR " CPSR = %08" PRIx32 "\n",
+ (uint32_t) frame, (intptr_t) frame->register_pc, FRAME_SR);
+}
+
+/**
+ * If thumb build of code switch the asm to thumb as required.
+ */
+#ifdef __thumb__
+ #define ASM_ARM_MODE ".align 2\nbx pc\n.arm\n"
+ #define ASM_THUMB_MODE "add %[arm_switch_reg], pc, #1\nbx %[arm_switch_reg]\n.thumb\n"
+ #define ARM_SWITCH_REG uint32_t arm_switch_reg
+#else
+ #define ASM_ARM_MODE
+ #define ASM_THUMB_MODE
+ #define ARM_SWITCH_REG
+#endif
+
+/**
+ * Exception stack frame size.
+ *
+ * The size is the exception stack frame plus the CPSR from the exception. We
+ * save the CPSR and restore it when we exit the exception.
+ */
+#define EXCEPTION_FRAME_SIZE (sizeof(CPU_Exception_frame) + sizeof(uint32_t))
+
+/**
+ * Exception stack frame FPU offsets and sizes.
+ */
+#define EXCEPTION_FRAME_FPU_SIZE ARM_VFP_CONTEXT_SIZE
+#define EXCEPTION_FRAME_FPU_OFFSET ARM_EXCEPTION_FRAME_VFP_CONTEXT_OFFSET
+
+/**
+ * Exception entry.
+ *
+ * We have switched from svc (or even user) to an exception mode. Save the
+ * current CPSR and create an exception frame on the exception's stack and then
+ * copy it to the thread's stack. Switch back to the thread's context and mode
+ * to handle the exception to avoid any stack checks thinking the stack is
+ * blown. This lets the thread be suspended.
+ *
+ * The entry is in two parts, the exception mode entry and the trhead mode
+ * entry. This lets us disable any hardware breakpoint support. We need to do
+ * this because it is enabled in PL0 mode.
+ *
+ * Note, the code currently assumes cp15 has been set up to match the
+ * instruction set being used.
+ */
+#define EXCEPTION_ENTRY_EXC() \
+ __asm__ volatile( \
+ ASM_ARM_MODE \
+ "sub sp, %[frame_size]\n" /* alloc the frame and CPSR */ \
+ "stm sp, {r0-r12}\n" /* store r0-r12 */ \
+ "sub sp, #4\n" \
+ "str lr, [sp]\n" /* save the link reg */ \
+ ASM_THUMB_MODE \
+ : [arm_switch_reg] "=&r" (arm_switch_reg) \
+ : [frame_size] "i" (EXCEPTION_FRAME_SIZE) \
+ : "memory")
+
+/*
+ * FPU entry. Conditionally D16 or D32 support.
+ */
+#ifdef ARM_MULTILIB_VFP
+#ifdef ARM_MULTILIB_VFP_D32
+#define FPU_ENTRY_VFP_D32 \
+ "vstmia r5!, {d16-d31}\n"
+#else /* ARM_MULTILIB_VFP_D32 */
+#define FPU_ENTRY_VFP_D32 \
+ "mov r3, #0\n" \
+ "mov r4, #0\n" \
+ "adds r6, r5, #128\n" \
+ "3:\n" \
+ "stmia r5!, {r3-r4}\n" \
+ "cmp r5, r6\n" \
+ "bne 3b\n"
+#endif /* ARM_MULTILIB_VFP_D32 */
+#define EXCEPTION_ENTRY_FPU(frame_fpu_size) \
+ "sub sp, %[frame_fpu_size]\n" /* size includes alignment size */ \
+ "add r5, sp, #4\n" /* up to align down */ \
+ "bic r5, r5, #7\n" /* align the FPU frame */ \
+ "str r5, [r2]\n" /* store the FPU frame pointer */ \
+ "vmrs r3, FPEXC\n" \
+ "vmrs r4, FPSCR\n" \
+ "stmia r5!, {r3-r4}\n" \
+ "vstmia r5!, {d0-d15}\n" \
+ FPU_ENTRY_VFP_D32
+#else /* ARM_MULTILIB_VFP */
+#define EXCEPTION_ENTRY_FPU(frame_fpu_size)
+#endif /* ARM_MULTILIB_VFP */
+
+#define EXCEPTION_ENTRY_THREAD(_frame) \
+ __asm__ volatile( \
+ ASM_ARM_MODE \
+ "ldr lr, [sp]\n" /* recover the link reg */ \
+ "add sp, #4\n" \
+ "add r0, sp, %[r0_r12_size]\n" /* get the sp in the frame */ \
+ "mrs r1, spsr\n" /* get the saved sr */ \
+ "mov r6, r1\n" /* stash it for later */ \
+ "bic r1, r1, %[psr_t]\n" /* clear thumb mode, not sure? */ \
+ "orr r1, r1, %[psr_i]\n" /* mask irqs */ \
+ "mrs r2, cpsr\n" /* get the current sr */ \
+ "str r2, [sp, %[frame_cpsr]]\n" /* save for exc return */ \
+ "msr cpsr, r1\n" /* switch to user mode */ \
+ "mov r3, sp\n" /* get the stack pointer */ \
+ "mov r4, lr\n" /* get the link reg */ \
+ "msr cpsr, r2\n" /* back to exc mode */ \
+ "mov r5, lr\n" /* get the PRA */ \
+ "stm r0, {r3-r6}\n" /* save into the frame */ \
+ "sub r4, r3, %[frame_size]\n" /* destination address */ \
+ "mov r6, r4\n" /* save the frame */ \
+ "sub r4, #1\n" /* one before the start */ \
+ "add r3, #1\n" /* one past the end */ \
+ "sub r5, sp, #1\n" /* source address */ \
+ "1:\n" \
+ "ldrb r0, [r5, #1]!\n" /* get a byte */ \
+ "strb r0, [r4, #1]!\n" /* put the byte */ \
+ "cmp r3, r4\n" /* the end? */ \
+ "bne 1b\n" \
+ "add sp, %[frame_size]\n" /* free the frame and CPSR */ \
+ "mrs r1, spsr\n" /* get the thread's saved sr */ \
+ "orr r2, r1, %[psr_i]\n" /* mask irqs */ \
+ "msr cpsr, r2\n" /* switch back to the thread's context */ \
+ "sub sp, %[frame_size]\n" /* alloc in the thread stack */ \
+ "mov %[o_frame], sp\n" /* save the frame */ \
+ "add r2, sp, %[o_frame_fpu]\n" /* get the FPU offset */ \
+ "mov r3, #0\n" \
+ "str r3, [r2]\n" /* clear the FPU frame pointer */ \
+ EXCEPTION_ENTRY_FPU(frame_fpu_size) \
+ "msr cpsr, r1\n" /* restore the irq mask */ \
+ ASM_THUMB_MODE \
+ : [arm_switch_reg] "=&r" (arm_switch_reg), \
+ [o_frame] "=r" (_frame) \
+ : [psr_t] "i" (ARM_PSR_T), \
+ [psr_i] "i" (ARM_PSR_I), \
+ [r0_r12_size] "i" (13 * sizeof(uint32_t)), \
+ [frame_cpsr] "i" (EXCEPTION_FRAME_SIZE - sizeof(uint32_t)), \
+ [frame_size] "i" (EXCEPTION_FRAME_SIZE), \
+ [o_frame_fpu] "i" (EXCEPTION_FRAME_FPU_OFFSET), \
+ [frame_fpu_size] "i" (EXCEPTION_FRAME_FPU_SIZE + 4) \
+ : "r0", "r1", "r2", "r3", "r4", "r5", "r6", "memory")
+
+/*
+ * FPU exit. Conditionally D16 or D32 support.
+ */
+#ifdef ARM_MULTILIB_VFP
+#ifdef ARM_MULTILIB_VFP_D32
+#define FPU_EXIT_VFP_D32 \
+ "vldmia r0, {d16-d31}\n"
+#else /* ARM_MULTILIB_VFP_D32 */
+#define FPU_EXIT_VFP_D32
+#endif /* ARM_MULTILIB_VFP_D32 */
+#define EXCEPTION_EXIT_FPU(frame_fpu_size) \
+ "ldmia r0!, {r1-r2}\n" \
+ "vldmia r0!, {d0-d15}\n" \
+ FPU_EXIT_VFP_D32 \
+ "vmsr FPEXC, r1\n" \
+ "vmsr FPSCR, r2\n" \
+ "add sp, %[frame_fpu_size]\n" /* size includes alignment size */
+#else /* ARM_MULTILIB_VFP */
+#define EXCEPTION_EXIT_FPU(frame_fpu_size)
+#endif /* ARM_MULTILIB_VFP */
+
+/**
+ * Exception exit.
+ *
+ * The thread is to be resumed so we are still in the thread's mode. Copy the
+ * exception frame from the thread's stack back to the exception's stack and
+ * restore the thread's context before returning from the exception to the
+ * thread.
+ *
+ * Note, the code currently assumes cp15 has been set up to match the
+ * instruction set being used.
+ */
+#define EXCEPTION_EXIT_THREAD(_frame) \
+ __asm__ volatile( \
+ ASM_ARM_MODE \
+ "mov r0, %[i_frame]\n" /* get the frame */ \
+ "ldr r0, [r0, %[frame_fpu]]\n" /* recover FPU frame pointer */ \
+ EXCEPTION_EXIT_FPU(frame_fpu_size) \
+ "ldr r2, [sp, %[frame_cpsr]]\n" /* recover exc CPSR from thread */ \
+ "mov r0, sp\n" /* get the thread frame pointer */ \
+ "msr cpsr, r2\n" /* switch back to the exc's context */ \
+ "add r3, sp, #1\n" /* get the end */ \
+ "sub sp, %[frame_size]\n" /* alloc the frame */ \
+ "sub r4, sp, #1\n" /* destination address */ \
+ "sub r5, r0, #1\n" /* source address */ \
+ "1:\n" \
+ "ldrb r1, [r5, #1]!\n" /* get a byte */ \
+ "strb r1, [r4, #1]!\n" /* put the byte */ \
+ "cmp r3, r4\n" /* the end? */ \
+ "bne 1b\n" \
+ "add r1, r0, %[r0_r12_size]\n" /* get the sp in the frame */ \
+ "ldm r1, {r3-r6}\n" /* recover from the frame */ \
+ "orr r1, r6, %[psr_i]\n" /* get the thread's psr and mask irqs */ \
+ "msr cpsr, r1\n" /* switch to user mode */ \
+ "mov sp, r3\n" /* set the stack pointer */ \
+ "mov lr, r4\n" /* set the link reg */ \
+ "msr cpsr, r2\n" /* switch back to the exc's context */ \
+ "msr spsr, r6\n" /* set the thread's CPSR */ \
+ "sub sp, #4\n" \
+ "mov lr, r5\n" /* get the PC */ \
+ "str lr, [sp]\n" /* save the link reg */ \
+ ASM_THUMB_MODE \
+ : [arm_switch_reg] "=&r" (arm_switch_reg) \
+ : [psr_i] "i" (ARM_PSR_I), \
+ [r0_r12_size] "i" (13 * sizeof(uint32_t)), \
+ [frame_cpsr] "i" (EXCEPTION_FRAME_SIZE - sizeof(uint32_t)), \
+ [frame_size] "i" (EXCEPTION_FRAME_SIZE), \
+ [frame_fpu] "i" (EXCEPTION_FRAME_FPU_OFFSET), \
+ [frame_fpu_size] "i" (EXCEPTION_FRAME_FPU_SIZE + 4), \
+ [i_frame] "r" (_frame) \
+ : "r0", "r1", "r2", "r3", "r4", "r5", "r6", "memory")
+
+#define EXCEPTION_EXIT_EXC() \
+ __asm__ volatile( \
+ ASM_ARM_MODE \
+ "ldr lr, [sp]\n" /* recover the link reg */ \
+ "add sp, #4\n" \
+ "ldm sp, {r0-r12}\n" /* restore the trhead's context */ \
+ "add sp, %[frame_size]\n" /* free the frame */ \
+ "subs pc, lr, #0\n" /* return from the exc */ \
+ : \
+ : [frame_size] "i" (EXCEPTION_FRAME_SIZE) \
+ : "memory")
+
+
+static void __attribute__((naked))
+target_exception_undefined_instruction(void)
+{
+ CPU_Exception_frame* frame;
+ ARM_SWITCH_REG;
+ EXCEPTION_ENTRY_EXC();
+ arm_debug_break_unload();
+ EXCEPTION_ENTRY_THREAD(frame);
+ frame->vector = 1;
+ target_exception(frame);
+ EXCEPTION_EXIT_THREAD(frame);
+ arm_debug_break_load();
+ EXCEPTION_EXIT_EXC();
+}
+
+static void __attribute__((naked))
+target_exception_supervisor_call(void)
+{
+ CPU_Exception_frame* frame;
+ ARM_SWITCH_REG;
+ /*
+ * The PC offset needs to be review so we move past a svc instruction. This
+ * can then used as a user breakpoint. The issue is this exception is used by
+ * the BKPT instruction in the prefetch abort handler to signal a TRAP.
+ */
+ EXCEPTION_ENTRY_EXC();
+ arm_debug_break_unload();
+ EXCEPTION_ENTRY_THREAD(frame);
+ frame->vector = 2;
+ target_exception(frame);
+ EXCEPTION_EXIT_THREAD(frame);
+ arm_debug_break_load();
+ EXCEPTION_EXIT_EXC();
+}
+
+static void __attribute__((naked))
+target_exception_prefetch_abort(void)
+{
+ CPU_Exception_frame* frame;
+ ARM_SWITCH_REG;
+ EXCEPTION_ENTRY_EXC();
+ arm_debug_break_unload();
+ EXCEPTION_ENTRY_THREAD(frame);
+ if ((arm_cp15_get_instruction_fault_status() & 0x1f) == 0x02)
+ frame->vector = 2;
+ else
+ frame->vector = 3;
+ target_exception(frame);
+ EXCEPTION_EXIT_THREAD(frame);
+ arm_debug_break_load();
+ EXCEPTION_EXIT_EXC();
+}
+
+static void __attribute__((naked))
+target_exception_data_abort(void)
+{
+ CPU_Exception_frame* frame;
+ ARM_SWITCH_REG;
+ EXCEPTION_ENTRY_EXC();
+ arm_debug_break_unload();
+ EXCEPTION_ENTRY_THREAD(frame);
+ frame->vector = 4;
+ target_exception(frame);
+ EXCEPTION_EXIT_THREAD(frame);
+ arm_debug_break_load();
+ EXCEPTION_EXIT_EXC();
+}
+
+int
+rtems_debugger_target_enable(void)
+{
+ rtems_interrupt_lock_context lock_context;
+ void* text_begin;
+ void* text_end;
+ debug_session_active = true;
+ text_begin = &bsp_section_text_begin[0];
+ text_end = &bsp_section_text_end[0];
+ rtems_interrupt_lock_acquire(&target_lock, &lock_context);
+ arm_debug_break_unload();
+ arm_debug_break_clear();
+ text_section_flags =
+ arm_cp15_set_translation_table_entries(text_begin,
+ text_end,
+ ARMV7_MMU_DATA_READ_WRITE_CACHED);
+ arm_cp15_set_exception_handler(ARM_EXCEPTION_UNDEF,
+ target_exception_undefined_instruction);
+ arm_cp15_set_exception_handler(ARM_EXCEPTION_SWI,
+ target_exception_supervisor_call);
+ arm_cp15_set_exception_handler(ARM_EXCEPTION_PREF_ABORT,
+ target_exception_prefetch_abort);
+ arm_cp15_set_exception_handler(ARM_EXCEPTION_DATA_ABORT,
+ target_exception_data_abort);
+ rtems_interrupt_lock_release(&target_lock, &lock_context);
+ return 0;
+}
+
+int
+rtems_debugger_target_disable(void)
+{
+ rtems_interrupt_lock_context lock_context;
+#if DOES_NOT_WORK
+ void* text_begin;
+ void* text_end;
+#endif
+ rtems_interrupt_lock_acquire(&target_lock, &lock_context);
+ debug_session_active = false;
+#if DOES_NOT_WORK
+ text_begin = &bsp_section_text_begin[0];
+ text_end = &bsp_section_text_end[0];
+ arm_cp15_set_translation_table_entries(text_begin,
+ text_end,
+ text_section_flags);
+#endif
+ rtems_interrupt_lock_release(&target_lock, &lock_context);
+ return 0;
+}
+
+int
+rtems_debugger_target_read_regs(rtems_debugger_thread* thread)
+{
+ if (!rtems_debugger_thread_flag(thread,
+ RTEMS_DEBUGGER_THREAD_FLAG_REG_VALID)) {
+ static const uint32_t good_address = (uint32_t) &good_address;
+ uint32_t* regs = &thread->registers[0];
+ int i;
+
+ for (i = 0; i < RTEMS_DEBUGGER_NUMREGS; ++i)
+ regs[i] = (uint32_t) &good_address;
+
+ if (thread->frame) {
+ CPU_Exception_frame* frame = thread->frame;
+
+ /*
+ * Assume interrupts are not masked and if masked set them to the saved
+ * value.
+ */
+ FRAME_SR &= ~CPSR_INTS_MASK;
+
+ if (rtems_debugger_thread_flag(thread,
+ RTEMS_DEBUGGER_THREAD_FLAG_INTS_DISABLED)) {
+ FRAME_SR |=
+ (thread->flags >> RTEMS_DEBUGGER_THREAD_FLAG_TARGET_BASE) & CPSR_INTS_MASK;
+ thread->flags = ~RTEMS_DEBUGGER_THREAD_FLAG_INTS_DISABLED;
+ }
+
+ regs[REG_R0] = frame->register_r0;
+ regs[REG_R1] = frame->register_r1;
+ regs[REG_R2] = frame->register_r2;
+ regs[REG_R3] = frame->register_r3;
+ regs[REG_R4] = frame->register_r4;
+ regs[REG_R5] = frame->register_r5;
+ regs[REG_R6] = frame->register_r6;
+ regs[REG_R7] = frame->register_r7;
+ regs[REG_R8] = frame->register_r8;
+ regs[REG_R9] = frame->register_r9;
+ regs[REG_R10] = frame->register_r10;
+ regs[REG_R11] = frame->register_r11;
+ regs[REG_R12] = frame->register_r12;
+ regs[REG_SP] = frame->register_sp;
+ regs[REG_LR] = (uint32_t) frame->register_lr;
+ regs[REG_PC] = (uint32_t) frame->register_pc;
+ regs[REG_CPSR] = FRAME_SR;
+ /*
+ * Get the signal from the frame.
+ */
+ thread->signal = rtems_debugger_target_exception_to_signal(frame);
+ }
+ else {
+#if defined(ARM_MULTILIB_ARCH_V4)
+ regs[REG_R4] = thread->tcb->Registers.register_r4;
+ regs[REG_R5] = thread->tcb->Registers.register_r5;
+ regs[REG_R6] = thread->tcb->Registers.register_r6;
+ regs[REG_R7] = thread->tcb->Registers.register_r7;
+ regs[REG_R8] = thread->tcb->Registers.register_r8;
+ regs[REG_R9] = thread->tcb->Registers.register_r9;
+ regs[REG_R10] = thread->tcb->Registers.register_r10;
+ regs[REG_R11] = thread->tcb->Registers.register_fp;
+ regs[REG_LR] = (intptr_t) thread->tcb->Registers.register_lr;
+ regs[REG_PC] = (intptr_t) thread->tcb->Registers.register_lr;
+ regs[REG_SP] = (intptr_t) thread->tcb->Registers.register_sp;
+#elif defined(ARM_MULTILIB_ARCH_V7M)
+ regs[REG_R4] = thread->tcb->Registers.register_r4;
+ regs[REG_R5] = thread->tcb->Registers.register_r5;
+ regs[REG_R6] = thread->tcb->Registers.register_r6;
+ regs[REG_R7] = thread->tcb->Registers.register_r7;
+ regs[REG_R8] = thread->tcb->Registers.register_r8;
+ regs[REG_R9] = thread->tcb->Registers.register_r9;
+ regs[REG_R10] = thread->tcb->Registers.register_r10;
+ regs[REG_R11] = thread->tcb->Registers.register_r11;
+ regs[REG_LR] = (intptr_t) thread->tcb->Registers.register_lr;
+ regs[REG_PC] = (intptr_t) thread->tcb->Registers.register_lr;
+ regs[REG_SP] = (intptr_t) thread->tcb->Registers.register_sp;
+#endif
+ /*
+ * Blocked threads have no signal.
+ */
+ thread->signal = 0;
+ }
+
+ thread->flags |= RTEMS_DEBUGGER_THREAD_FLAG_REG_VALID;
+ thread->flags &= ~RTEMS_DEBUGGER_THREAD_FLAG_REG_DIRTY;
+ }
+
+ return 0;
+}
+
+int
+rtems_debugger_target_write_regs(rtems_debugger_thread* thread)
+{
+ if (rtems_debugger_thread_flag(thread,
+ RTEMS_DEBUGGER_THREAD_FLAG_REG_DIRTY)) {
+ uint32_t* regs = &thread->registers[0];
+
+ /*
+ * Only write to debugger controlled threads. Do not touch the registers
+ * for threads blocked in the context switcher.
+ */
+ if (rtems_debugger_thread_flag(thread,
+ RTEMS_DEBUGGER_THREAD_FLAG_DEBUGGING)) {
+ CPU_Exception_frame* frame = thread->frame;
+ frame->register_r0 = regs[REG_R0];
+ frame->register_r1 = regs[REG_R1];
+ frame->register_r2 = regs[REG_R2];
+ frame->register_r3 = regs[REG_R3];
+ frame->register_r4 = regs[REG_R4];
+ frame->register_r5 = regs[REG_R5];
+ frame->register_r6 = regs[REG_R6];
+ frame->register_r7 = regs[REG_R7];
+ frame->register_r8 = regs[REG_R8];
+ frame->register_r9 = regs[REG_R9];
+ frame->register_r10 = regs[REG_R10];
+ frame->register_r11 = regs[REG_R11];
+ frame->register_r12 = regs[REG_R12];
+ frame->register_sp = regs[REG_SP];
+ frame->register_lr = (void*) regs[REG_LR];
+ frame->register_pc = (void*) regs[REG_PC];
+ FRAME_SR = regs[REG_CPSR];
+ }
+ thread->flags &= ~RTEMS_DEBUGGER_THREAD_FLAG_REG_DIRTY;
+ }
+ return 0;
+}
+
+DB_UINT
+rtems_debugger_target_reg_pc(rtems_debugger_thread* thread)
+{
+ int r;
+ r = rtems_debugger_target_read_regs(thread);
+ if (r >= 0) {
+ uint32_t* regs = &thread->registers[0];
+ return regs[REG_PC];
+ }
+ return 0;
+}
+
+DB_UINT
+rtems_debugger_target_frame_pc(CPU_Exception_frame* frame)
+{
+ return (DB_UINT) frame->register_pc;
+}
+
+DB_UINT
+rtems_debugger_target_reg_sp(rtems_debugger_thread* thread)
+{
+ int r;
+ r = rtems_debugger_target_read_regs(thread);
+ if (r >= 0) {
+ uint32_t* regs = &thread->registers[0];
+ return regs[REG_SP];
+ }
+ return 0;
+}
+
+DB_UINT
+rtems_debugger_target_tcb_sp(rtems_debugger_thread* thread)
+{
+ return (DB_UINT) thread->tcb->Registers.register_sp;
+}
+
+int
+rtems_debugger_target_thread_stepping(rtems_debugger_thread* thread)
+{
+ if (rtems_debugger_thread_flag(thread,
+ (RTEMS_DEBUGGER_THREAD_FLAG_STEP |
+ RTEMS_DEBUGGER_THREAD_FLAG_STEPPING))) {
+ /*
+ * Single stepping and range stepping uses hardware debug breakpoint
+ * 0. This is reserved for single stepping.
+ */
+ CPU_Exception_frame* frame = thread->frame;
+ arm_debug_hwbreak* bp = &hw_breaks[0];
+ int i;
+ for (i = 0; i < hw_breakpoints; ++i, ++bp) {
+ if (!bp->enabled) {
+ const uint32_t addr = (intptr_t) frame->register_pc;
+ const bool thumb = (FRAME_SR & (1 << 5)) != 0 ? true : false;
+ uint32_t bas;
+
+ bp->enabled = true;
+ bp->loaded = false;
+ bp->address = frame->register_pc;
+ bp->frame = frame;
+ bp->length = sizeof(uint32_t);
+
+ if (thumb) {
+ uint16_t instr = *((uint16_t*) frame->register_pc);
+ switch (instr & 0xf800) {
+ case 0xe800:
+ case 0xf000:
+ case 0xf800:
+ break;
+ default:
+ bp->length = sizeof(uint16_t);
+ break;
+ }
+ }
+
+ /*
+ * See table C3-2 Effect of byte address selection on Breakpoint
+ * generation and "Instruction address comparisoin programming
+ * examples.
+ */
+ if (thumb) {
+ if ((addr & (1 << 1)) == 0) {
+ bas = 0x3; /* b0011 */
+ }
+ else {
+ bas = 0xc; /* b1100 */
+ }
+ }
+ else {
+ bas = 0xf; /* b1111 */
+ }
+
+ arm_debug_break_setup(bp,
+ addr & ~0x3,
+ ARM_HW_BP_UNLINKED_INSTR_MISMATCH,
+ bas,
+ ARM_HW_BP_PRIV_PL0_SUP_SYS);
+
+ /*
+ * Save the interrupt state before stepping if set.
+ */
+ if ((FRAME_SR & CPSR_INTS_MASK) != 0) {
+ uint32_t int_state;
+ int_state =
+ (frame->register_cpsr & CPSR_INTS_MASK) << RTEMS_DEBUGGER_THREAD_FLAG_TARGET_BASE;
+ thread->flags |= RTEMS_DEBUGGER_THREAD_FLAG_INTS_DISABLED | int_state;
+ }
+ /*
+ * Mask the interrupt when stepping.
+ */
+ FRAME_SR |= CPSR_INTS_MASK;
+ break;
+ }
+ }
+ }
+ return 0;
+}
+
+int
+rtems_debugger_target_exception_to_signal(CPU_Exception_frame* frame)
+{
+ int sig = RTEMS_DEBUGGER_SIGNAL_HUP;
+ switch (frame->vector) {
+ case ARM_EXCEPTION_RESET:
+ case ARM_EXCEPTION_SWI:
+ sig = RTEMS_DEBUGGER_SIGNAL_TRAP;
+ break;
+ case ARM_EXCEPTION_UNDEF:
+ sig = RTEMS_DEBUGGER_SIGNAL_ILL;
+ break;
+ case ARM_EXCEPTION_FIQ:
+ sig = RTEMS_DEBUGGER_SIGNAL_FPE;
+ break;
+ case ARM_EXCEPTION_PREF_ABORT:
+ case ARM_EXCEPTION_DATA_ABORT:
+ sig = RTEMS_DEBUGGER_SIGNAL_SEGV;
+ break;
+ case ARM_EXCEPTION_RESERVED:
+ case ARM_EXCEPTION_IRQ:
+ sig = RTEMS_DEBUGGER_SIGNAL_BUS;
+ break;
+ default:
+ break;
+ }
+ return sig;
+}
+
+int
+rtems_debugger_target_hwbreak_control(rtems_debugger_target_watchpoint wp,
+ bool insert,
+ DB_UINT addr,
+ DB_UINT kind)
+{
+ /*
+ * To do.
+ */
+ return 0;
+}
+
+int
+rtems_debugger_target_cache_sync(rtems_debugger_target_swbreak* swbreak)
+{
+ /*
+ * Flush the data cache and invalidate the instruction cache.
+ */
+ rtems_cache_flush_multiple_data_lines(swbreak->address,
+ sizeof(breakpoint));
+ rtems_cache_invalidate_multiple_instruction_lines(swbreak->address,
+ sizeof(breakpoint));
+ return 0;
+}
diff --git a/cpukit/libdebugger/rtems-debugger-block.c b/cpukit/libdebugger/rtems-debugger-block.c
new file mode 100644
index 0000000000..918e321ca6
--- /dev/null
+++ b/cpukit/libdebugger/rtems-debugger-block.c
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2016 Chris Johns <chrisj@rtems.org>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+
+#include "rtems-debugger-block.h"
+
+int
+rtems_debugger_block_create(rtems_debugger_block* block,
+ size_t step,
+ size_t size)
+{
+ int r = 0;
+ block->level = 0;
+ block->step = step;
+ block->count = step;
+ block->size = size;
+ block->block = calloc(block->count, block->size);
+ if (block->block == NULL)
+ errno = ENOMEM;
+ return r;
+}
+
+int
+rtems_debugger_block_destroy(rtems_debugger_block* block)
+{
+ free(block->block);
+ block->block = NULL;
+ block->level = 0;
+ block->count = 0;
+ block->size = 0;
+ return 0;
+}
+
+int
+rtems_debugger_block_resize(rtems_debugger_block* block)
+{
+ int r = 0;
+ if (block->level >= block->count) {
+ block->count += block->step;
+ block->block = realloc(block->block, block->count * block->size);
+ if (block->block == NULL) {
+ block->level = 0;
+ block->count = 0;
+ errno = ENOMEM;
+ r = -1;
+ }
+ }
+ return r;
+}
diff --git a/cpukit/libdebugger/rtems-debugger-block.h b/cpukit/libdebugger/rtems-debugger-block.h
new file mode 100644
index 0000000000..c1c7fab2ac
--- /dev/null
+++ b/cpukit/libdebugger/rtems-debugger-block.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2016 Chris Johns <chrisj@rtems.org>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Debugger for RTEMS.
+ */
+
+#ifndef _RTEMS_DEBUGGER_BLOCK_h
+#define _RTEMS_DEBUGGER_BLOCK_h
+
+#include <rtems/rtems-debugger.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/**
+ * DB server block manages a block of re-sizable memory. The block only
+ * grows. As more threads enter the system the block becomes the peak and then
+ * sits at that level.
+ */
+typedef struct rtems_debugger_block
+{
+ void* block; /**< The block of memory. */
+ size_t step; /**< The step size the block is increased by. */
+ size_t size; /**< The size of the elements in the block. */
+ size_t count; /**< The number of elements in the block. */
+ size_t level; /**< The usage level in the block. */
+} rtems_debugger_block;
+
+/**
+ * Create a block.
+ */
+extern int rtems_debugger_block_create(rtems_debugger_block* block,
+ size_t step,
+ size_t size);
+
+/**
+ * Destroy a block.
+ */
+extern int rtems_debugger_block_destroy(rtems_debugger_block* block);
+
+/**
+ * Resize a block.
+ */
+extern int rtems_debugger_block_resize(rtems_debugger_block* block);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+
+#endif
diff --git a/cpukit/libdebugger/rtems-debugger-cmd.c b/cpukit/libdebugger/rtems-debugger-cmd.c
new file mode 100644
index 0000000000..e292065e64
--- /dev/null
+++ b/cpukit/libdebugger/rtems-debugger-cmd.c
@@ -0,0 +1,208 @@
+/*
+ * Copyright (c) 2016 Chris Johns <chrisj@rtems.org>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <errno.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#define __need_getopt_newlib
+#include <getopt.h>
+
+#include <rtems.h>
+#include <rtems/printer.h>
+#include <rtems/shell.h>
+
+#include <rtems/rtems-debugger.h>
+
+/*
+ * Debugger command for the RTEMS shell.
+ */
+
+static int rtems_shell_main_debugger(int argc, char *argv[])
+{
+ if (argc == 1) {
+ printf("RTEMS debugger is %srunning\n", rtems_debugger_running() ? "" : "not ");
+ return 0;
+ }
+
+ if (strcasecmp(argv[1], "start") == 0) {
+ rtems_printer printer;
+ const char* remote = "tcp";
+ const char* device = "1122";
+ int timeout = RTEMS_DEBUGGER_TIMEOUT;
+ rtems_task_priority priority = 1;
+ bool verbose = false;
+ struct getopt_data data;
+ char* end;
+ int r;
+
+ if (rtems_debugger_running()) {
+ printf("error: debugger already running.\n");
+ return 1;
+ }
+
+ memset(&data, 0, sizeof(data));
+
+ rtems_print_printer_fprintf(&printer, stdout);
+
+ argv += 1;
+ argc -= 1;
+
+ while (true) {
+ int c;
+
+ c = getopt_r(argc, argv, "vR:d:t:P:l:", &data);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'v':
+ verbose = true;
+ break;
+ case 'R':
+ remote = data.optarg;
+ break;
+ case 'd':
+ device = data.optarg;
+ break;
+ case 't':
+ timeout = strtoul(data.optarg, &end, 10);
+ if (timeout == 0 || *end != '\0') {
+ printf("error: invalid timeout: %s\n", data.optarg);
+ return 1;
+ }
+ break;
+ case 'P':
+ priority = strtoul(data.optarg, &end, 10);
+ if (priority == 0 || *end != '\0') {
+ printf("error: invalid priority: %s\n", data.optarg);
+ return 1;
+ }
+ break;
+ case 'l':
+ if (strcasecmp(data.optarg, "stdout") == 0)
+ rtems_print_printer_fprintf(&printer, stdout);
+ else if (strcasecmp(data.optarg, "stderr") == 0)
+ rtems_print_printer_fprintf(&printer, stderr);
+ else if (strcasecmp(data.optarg, "kernel") == 0)
+ rtems_print_printer_printk(&printer);
+ else {
+ printf("error: unknown printer (stdout, stderr, kernel): %s\n", data.optarg);
+ return 1;
+ }
+ break;
+ default:
+ case '?':
+ if (data.optarg != NULL)
+ printf("error: unknown option: %s\n", data.optarg);
+ else
+ printf("error: invalid start command\n");
+ return 1;
+ }
+ }
+
+ printf("RTEMS Debugger start: remote=%s device=%s priority=%" PRIu32 "\n",
+ remote, device, priority);
+
+ r = rtems_debugger_start(remote, device, timeout, priority, &printer);
+ if (r < 0) {
+ printf("debugger start failed\n");
+ return 1;
+ }
+
+ rtems_debugger_set_verbose(verbose);
+ }
+ else if (strcasecmp(argv[1], "stop") == 0) {
+ int r;
+
+ if (!rtems_debugger_running()) {
+ printf("error: debugger not running.\n");
+ return 1;
+ }
+
+ r = rtems_debugger_stop();
+ if (r < 0) {
+ printf("debugger stop failed\n");
+ return 1;
+ }
+ }
+ else if (strcasecmp(argv[1], "remote-debug") == 0) {
+ int r;
+
+ if (!rtems_debugger_running()) {
+ printf("error: debugger not running.\n");
+ return 1;
+ }
+
+ if (argc == 3 && strcasecmp(argv[2], "on") == 0) {
+ r = rtems_debugger_remote_debug(true);
+ if (r < 0) {
+ printf("debugger remote-debug on failed\n");
+ return 1;
+ }
+ }
+ else if (argc == 3 && strcasecmp(argv[2], "off") == 0) {
+ r = rtems_debugger_remote_debug(false);
+ if (r < 0) {
+ printf("debugger remote-debug off failed\n");
+ return 1;
+ }
+ }
+ else {
+ printf("debugger remote-debug: not on or off\n");
+ return 1;
+ }
+ }
+ else if (strcasecmp(argv[1], "help") == 0) {
+ printf("debugger [start/stop/help] ...\n" \
+ " start -v -R remote -d device -t secs -P priority -l [stdout,stderr,kernel]\n" \
+ " stop\n" \
+ " remote-debug <on/off>\n" \
+ " help\n");
+ }
+ else {
+ printf("error: unknown command: %s\n", argv[1]);
+ return 1;
+ }
+
+ return 0;
+}
+
+rtems_shell_cmd_t rtems_shell_DEBUGGER_Command = {
+ "debugger", /* name */
+ "debugger [start/stop] [options ...]", /* usage */
+ "misc", /* topic */
+ rtems_shell_main_debugger, /* command */
+ NULL, /* alias */
+ NULL, /* next */
+ 0755,
+ 0,
+ 0
+};
diff --git a/cpukit/libdebugger/rtems-debugger-i386.c b/cpukit/libdebugger/rtems-debugger-i386.c
new file mode 100644
index 0000000000..901ba85322
--- /dev/null
+++ b/cpukit/libdebugger/rtems-debugger-i386.c
@@ -0,0 +1,421 @@
+/*
+ * Copyright (c) 2016 Chris Johns <chrisj@rtems.org>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#define TARGET_DEBUG 1
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <errno.h>
+#include <inttypes.h>
+#include <stdlib.h>
+
+#include <rtems.h>
+#include <rtems/score/threadimpl.h>
+
+#include "rtems-debugger-target.h"
+#include "rtems-debugger-threads.h"
+
+/*
+ * Hardware breakpoints. Limited by hardware
+ */
+#define RTEMS_DEBUGGER_HWBREAK_NUM 4
+
+/*
+ * Number of registers.
+ */
+#define RTEMS_DEBUGGER_NUMREGS 16
+
+/*
+ * Number of bytes per register.
+ */
+#define RTEMS_DEBUGGER_REGBYTES 4
+
+/*
+ * Number of bytes of registers.
+ */
+#define RTEMS_DEBUGGER_NUMREGBYTES \
+ (RTEMS_DEBUGGER_NUMREGS * RTEMS_DEBUGGER_REGBYTES)
+
+/*
+ * Debugger registers layout.
+ */
+#define REG_EAX 0
+#define REG_ECX 1
+#define REG_EDX 2
+#define REG_EBX 3
+#define REG_ESP 4
+#define REG_EBP 5
+#define REG_ESI 6
+#define REG_EDI 7
+#define REG_PC 8
+#define REG_EIP REG_PC
+#define REG_PS 9
+#define REG_EFLAGS REG_PS
+#define REG_CS 10
+#define REG_SS 11
+#define REG_DS 12
+#define REG_ES 13
+#define REG_FS 14
+#define REG_GS 15
+
+/**
+ * The int 3 opcode.
+ */
+#define TARGET_BKPT 0xcc
+
+static const uint8_t breakpoint[1] = { TARGET_BKPT };
+
+/*
+ * Get a copy of a register.
+ */
+#define GET_REG(_r, _v) asm volatile("pushl %%" #_r "; popl %0" : "=rm" (_v))
+
+/*
+ * Get a copy of a segment register.
+ */
+#define GET_SEG_REG(_r, _v) \
+ do { \
+ int _i; \
+ GET_REG(_r, _i); \
+ _v = _i & 0xffff; \
+ } while (0)
+
+/**
+ * Target lock.
+ */
+RTEMS_INTERRUPT_LOCK_DEFINE(static, target_lock, "target_lock")
+
+/**
+ * The orginal exception handler.
+ */
+static void (*orig_currentExcHandler)(CPU_Exception_frame* frame);
+
+#if TARGET_DEBUG
+#include <rtems/bspIo.h>
+static void target_printk(const char* format, ...) RTEMS_PRINTFLIKE(1, 2);
+static void
+target_printk(const char* format, ...)
+{
+ va_list ap;
+ va_start(ap, format);
+ vprintk(format, ap);
+ va_end(ap);
+}
+#else
+#define target_printk(_fmt, ...)
+#endif
+
+#if TODO
+/**
+ * Target description.
+ */
+static const char* const target_xml =
+"<?xml version=\"1.0\"> \
+ <!DOCTYPE target SYSTEM \"gdb-target.dtd\"> \
+ <target version=\"1.0\"> \
+ <architecture>i386</architecture> \
+ <xi:include href=\"32bit-core.xml\"/> \
+ <xi:include href=\"32bit-sse.xml\"/> \
+</target>";
+#endif
+
+int
+rtems_debugger_target_configure(rtems_debugger_target* target)
+{
+ target->capabilities = (RTEMS_DEBUGGER_TARGET_CAP_SWBREAK);
+ target->reg_num = RTEMS_DEBUGGER_NUMREGS;
+ target->reg_size = sizeof(uint32_t);
+ target->breakpoint = &breakpoint[0];
+ target->breakpoint_size = sizeof(breakpoint);
+ return 0;
+}
+
+static void
+target_exception(CPU_Exception_frame* frame)
+{
+ target_printk("[} frame = %08lx sig=%d (%lx)\n",
+ (uint32_t) frame,
+ rtems_debugger_target_exception_to_signal(frame),
+ frame->idtIndex);
+ target_printk("[} EAX = %" PRIx32 " EBX = %" PRIx32 \
+ " ECX = %" PRIx32 " EDX = %" PRIx32 "\n",
+ frame->eax, frame->ebx, frame->ecx, frame->edx);
+ target_printk("[} ESI = %" PRIx32 " EDI = %" PRIx32 \
+ " EBP = %" PRIx32 " ESP = %" PRIx32 "\n",
+ frame->esi, frame->edi, frame->ebp, frame->esp0);
+ target_printk("[} EIP = %" PRIx32"\n", frame->eip);
+
+ frame->eflags &= ~EFLAGS_TRAP;
+
+ switch (rtems_debugger_target_exception(frame)) {
+ case rtems_debugger_target_exc_consumed:
+ default:
+ break;
+ case rtems_debugger_target_exc_step:
+ frame->eflags |= EFLAGS_TRAP;
+ break;
+ case rtems_debugger_target_exc_cascade:
+ orig_currentExcHandler(frame);
+ break;
+ }
+}
+
+int
+rtems_debugger_target_enable(void)
+{
+ rtems_interrupt_lock_context lock_context;
+ rtems_interrupt_lock_acquire(&target_lock, &lock_context);
+ if (orig_currentExcHandler == NULL) {
+ orig_currentExcHandler = _currentExcHandler;
+ _currentExcHandler = target_exception;
+ }
+ rtems_interrupt_lock_release(&target_lock, &lock_context);
+ return 0;
+}
+
+int
+rtems_debugger_target_disable(void)
+{
+ rtems_interrupt_lock_context lock_context;
+ rtems_interrupt_lock_acquire(&target_lock, &lock_context);
+ if (orig_currentExcHandler != NULL)
+ _currentExcHandler = orig_currentExcHandler;
+ rtems_interrupt_lock_release(&target_lock, &lock_context);
+ return 0;
+}
+
+int
+rtems_debugger_target_read_regs(rtems_debugger_thread* thread)
+{
+ if (!rtems_debugger_thread_flag(thread,
+ RTEMS_DEBUGGER_THREAD_FLAG_REG_VALID)) {
+ uint32_t* regs = &thread->registers[0];
+ size_t i;
+
+ for (i = 0; i < rtems_debugger_target_reg_num(); ++i)
+ regs[i] = 0xdeaddead;
+
+ if (thread->frame) {
+ CPU_Exception_frame* frame = thread->frame;
+ regs[REG_EAX] = frame->eax;
+ regs[REG_ECX] = frame->ecx;
+ regs[REG_EDX] = frame->edx;
+ regs[REG_EBX] = frame->ebx;
+ regs[REG_ESP] = frame->esp0;
+ regs[REG_EBP] = frame->ebp;
+ regs[REG_ESI] = frame->esi;
+ regs[REG_EDI] = frame->edi;
+ regs[REG_EIP] = frame->eip;
+ regs[REG_EFLAGS] = frame->eflags;
+ regs[REG_CS] = frame->cs;
+
+ /*
+ * Get the signal from the frame.
+ */
+ thread->signal = rtems_debugger_target_exception_to_signal(frame);
+ }
+ else {
+ regs[REG_EBX] = thread->tcb->Registers.ebx;
+ regs[REG_ESI] = thread->tcb->Registers.esi;
+ regs[REG_EDI] = thread->tcb->Registers.edi;
+ regs[REG_EFLAGS] = thread->tcb->Registers.eflags;
+ regs[REG_ESP] = (intptr_t) thread->tcb->Registers.esp;
+ regs[REG_EBP] = (intptr_t) thread->tcb->Registers.ebp;
+ regs[REG_EIP] = *((DB_UINT*) thread->tcb->Registers.esp);
+ regs[REG_EAX] = (intptr_t) thread;
+
+ GET_SEG_REG(CS, regs[REG_CS]);
+
+ /*
+ * Blocked threads have no signal.
+ */
+ thread->signal = 0;
+ }
+
+ GET_SEG_REG(SS, regs[REG_SS]);
+ GET_SEG_REG(DS, regs[REG_DS]);
+ GET_SEG_REG(ES, regs[REG_ES]);
+ GET_SEG_REG(FS, regs[REG_FS]);
+ GET_SEG_REG(GS, regs[REG_GS]);
+
+ thread->flags |= RTEMS_DEBUGGER_THREAD_FLAG_REG_VALID;
+ thread->flags &= ~RTEMS_DEBUGGER_THREAD_FLAG_REG_DIRTY;
+ }
+
+ return 0;
+}
+
+int
+rtems_debugger_target_write_regs(rtems_debugger_thread* thread)
+{
+ if (rtems_debugger_thread_flag(thread,
+ RTEMS_DEBUGGER_THREAD_FLAG_REG_DIRTY)) {
+ uint32_t* regs = &thread->registers[0];
+
+ /*
+ * Only write to debugger controlled threads. Do not touch the registers
+ * for threads blocked in the context switcher.
+ */
+ if (rtems_debugger_thread_flag(thread,
+ RTEMS_DEBUGGER_THREAD_FLAG_DEBUGGING)) {
+ CPU_Exception_frame* frame = thread->frame;
+ frame->eax = regs[REG_EAX];
+ frame->ecx = regs[REG_ECX];
+ frame->edx = regs[REG_EDX];
+ frame->ebx = regs[REG_EBX];
+ frame->esp0 = regs[REG_ESP];
+ frame->ebp = regs[REG_EBP];
+ frame->esi = regs[REG_ESI];
+ frame->edi = regs[REG_EDI];
+ frame->eip = regs[REG_EIP];
+ frame->eflags = regs[REG_EFLAGS];
+ frame->cs = regs[REG_CS];
+ }
+ thread->flags &= ~RTEMS_DEBUGGER_THREAD_FLAG_REG_DIRTY;
+ }
+ return 0;
+}
+
+DB_UINT
+rtems_debugger_target_reg_pc(rtems_debugger_thread* thread)
+{
+ int r;
+ r = rtems_debugger_target_read_regs(thread);
+ if (r >= 0) {
+ uint32_t* regs = &thread->registers[0];
+ return regs[REG_EIP];
+ }
+ return 0;
+}
+
+DB_UINT
+rtems_debugger_target_frame_pc(CPU_Exception_frame* frame)
+{
+ return (DB_UINT) frame->eip;
+}
+
+DB_UINT
+rtems_debugger_target_reg_sp(rtems_debugger_thread* thread)
+{
+ int r;
+ r = rtems_debugger_target_read_regs(thread);
+ if (r >= 0) {
+ uint32_t* regs = &thread->registers[0];
+ return regs[REG_ESP];
+ }
+ return 0;
+}
+
+DB_UINT
+rtems_debugger_target_tcb_sp(rtems_debugger_thread* thread)
+{
+ return (DB_UINT) thread->tcb->Registers.esp;
+}
+
+int
+rtems_debugger_target_thread_stepping(rtems_debugger_thread* thread)
+{
+ if (rtems_debugger_thread_flag(thread,
+ (RTEMS_DEBUGGER_THREAD_FLAG_STEP |
+ RTEMS_DEBUGGER_THREAD_FLAG_STEPPING))) {
+ CPU_Exception_frame* frame = thread->frame;
+ /*
+ * Single step instructions with interrupts masked to avoid stepping into
+ * an interrupt handler.
+ */
+ if ((frame->eflags & EFLAGS_INTR_ENABLE) == 0)
+ thread->flags |= RTEMS_DEBUGGER_THREAD_FLAG_INTS_DISABLED;
+ else
+ frame->eflags &= ~EFLAGS_INTR_ENABLE;
+ frame->eflags |= EFLAGS_TRAP;
+ }
+ return 0;
+}
+
+int
+rtems_debugger_target_exception_to_signal(CPU_Exception_frame* frame)
+{
+ int sig = RTEMS_DEBUGGER_SIGNAL_HUP;
+ switch (frame->idtIndex) {
+ case 1: /* debug exception */
+ case 3: /* breakpoint int3 */
+ sig = RTEMS_DEBUGGER_SIGNAL_TRAP;
+ break;
+ case 4: /* int overflow */
+ case 5: /* out-of-bounds */
+ sig = RTEMS_DEBUGGER_SIGNAL_URG;
+ break;
+ case 6: /* invalid opcode */
+ sig = RTEMS_DEBUGGER_SIGNAL_ILL;
+ break;
+ case 8: /* double fault */
+ case 16: /* fp error */
+ sig = RTEMS_DEBUGGER_SIGNAL_EMT;
+ break;
+ case 0: /* divide by zero */
+ case 7: /* FPU not avail. */
+ sig = RTEMS_DEBUGGER_SIGNAL_FPE;
+ break;
+ case 9: /* i387 seg overr. */
+ case 10: /* Invalid TSS */
+ case 11: /* seg. not pres. */
+ case 12: /* stack except. */
+ case 13: /* general prot. */
+ case 14: /* page fault */
+ case 17: /* alignment check */
+ sig = RTEMS_DEBUGGER_SIGNAL_SEGV;
+ break;
+ case 2: /* NMI */
+ case 18: /* machine check */
+ sig = RTEMS_DEBUGGER_SIGNAL_BUS;
+ break;
+ default:
+ break;
+ }
+ return sig;
+}
+
+int
+rtems_debugger_target_hwbreak_control(rtems_debugger_target_watchpoint wp,
+ bool insert,
+ DB_UINT addr,
+ DB_UINT kind)
+{
+ /*
+ * To do.
+ */
+ return 0;
+}
+
+int
+rtems_debugger_target_cache_sync(rtems_debugger_target_swbreak* swbreak)
+{
+ /*
+ * Nothing to do on an i386.
+ */
+ return 0;
+}
diff --git a/cpukit/libdebugger/rtems-debugger-remote-tcp.c b/cpukit/libdebugger/rtems-debugger-remote-tcp.c
new file mode 100644
index 0000000000..ded51d604e
--- /dev/null
+++ b/cpukit/libdebugger/rtems-debugger-remote-tcp.c
@@ -0,0 +1,342 @@
+/*
+ * Copyright (c) 2016 Chris Johns <chrisj@rtems.org>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <rtems/rtems-debugger.h>
+#include <rtems/debugger/rtems-debugger-server.h>
+#include <rtems/debugger/rtems-debugger-remote.h>
+
+#include "rtems-debugger-remote-tcp.h"
+
+/**
+ * Debugger default server port. 'RT' as ASCII.
+ */
+#define RTEMS_DB_PORT_DEFAULT (8284)
+
+/**
+ * TCP Remote data.
+ */
+typedef struct
+{
+ int fd;
+ int port;
+} rtems_debugger_remote_tcp;
+
+static rtems_debugger_remote_tcp*
+tcp_remote(rtems_debugger_remote* remote)
+{
+ rtems_debugger_remote_tcp* tcp = NULL;
+ rtems_debugger_lock();
+ if (remote != NULL && remote->data != NULL)
+ tcp = (rtems_debugger_remote_tcp*) remote->data;
+ rtems_debugger_unlock();
+ return tcp;
+}
+
+static int
+tcp_remote_begin(rtems_debugger_remote* remote, const char* device)
+{
+ rtems_debugger_remote_tcp* tcp;
+ int port;
+ char* end;
+
+ rtems_debugger_lock();
+
+ /*
+ * Parse the port number.
+ */
+ port = strtoul(device, &end, 10);
+ if (port == 0 || *end != '\0') {
+ rtems_debugger_printf("error: rtems-db: tcp remote: invalid port: %s\n", device);
+ return -1;
+ }
+
+ tcp = malloc(sizeof(rtems_debugger_remote_tcp));
+ if (tcp == NULL) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ remote->data = tcp;
+
+ tcp->fd = -1;
+ tcp->port = port;
+
+ rtems_debugger_unlock();
+
+ return 0;
+}
+
+static int
+tcp_remote_end(rtems_debugger_remote* remote)
+{
+ rtems_debugger_lock();
+
+ if (remote != NULL && remote->data != NULL) {
+ rtems_debugger_remote_tcp* tcp = (rtems_debugger_remote_tcp*) remote->data;
+ if (tcp != NULL) {
+ if (tcp->fd >= 0)
+ close(tcp->fd);
+ free(tcp);
+ remote->data = NULL;
+ }
+ }
+
+ rtems_debugger_unlock();
+
+ return 0;
+}
+
+static int
+tcp_remote_connect(rtems_debugger_remote* remote)
+{
+ int ld;
+ struct sockaddr_in addr;
+ socklen_t opt;
+ socklen_t len;
+ bool running;
+ struct timeval timeout;
+ rtems_debugger_remote_tcp* tcp = tcp_remote(remote);
+ int r;
+
+ if (rtems_debugger_verbose())
+ rtems_debugger_printf("error: rtems-db: tcp remote: connect\n");
+
+ ld = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (ld < 0) {
+ rtems_debugger_printf("error: rtems-db: tcp remote: socket: (%d) %s\n",
+ errno, strerror(errno));
+ return -1;
+ }
+
+ opt = 1;
+ r = setsockopt(ld,
+ SOL_SOCKET,
+ SO_REUSEADDR,
+ (char *) &opt,
+ sizeof(opt));
+ if (r < 0) {
+ close(ld);
+ rtems_debugger_printf("error: rtems-db: tcp remote: setsocket: reuseaddr: (%d) %s\n",
+ errno, strerror(errno));
+ return -1;
+ }
+
+ addr.sin_family = PF_INET;
+ addr.sin_port = htons(tcp->port);
+ addr.sin_addr.s_addr = INADDR_ANY;
+
+ r = bind(ld, (struct sockaddr *) &addr, sizeof(addr));
+ if (r < 0) {
+ close(ld);
+ rtems_debugger_printf("error: rtems-db: tcp remote: bind: (%d) %s\n",
+ errno, strerror(errno));
+ return -1;
+ }
+
+ /*
+ * Backlog of 1 connection.
+ */
+ r = listen(ld, 1);
+ if (r < 0) {
+ close(ld);
+ rtems_debugger_printf("error: rtems-db: tcp remote: listen: (%d) %s\n",
+ errno, strerror(errno));
+ return -1;
+ }
+
+ /*
+ * Use a random port if the port is 0.
+ */
+ if (tcp->port == 0) {
+ len = sizeof(addr);
+ r = getsockname(ld, (struct sockaddr *) &addr, &len);
+ if (r < 0 || len < sizeof(addr)) {
+ close(ld);
+ rtems_debugger_printf("error: rtems-db: tcp remote: getsockname: (%d) %s\n",
+ errno, strerror(errno));
+ return -1;
+ }
+ tcp->port = ntohs(addr.sin_port);
+ }
+
+ rtems_debugger_printf("rtems-db: tcp remote: listing on port: %d\n",
+ tcp->port);
+
+ len = sizeof(addr);
+ tcp->fd = accept(ld, (struct sockaddr *) &addr, &len);
+
+ running = rtems_debugger_server_running();
+
+ close(ld);
+
+ if (tcp->fd < 0) {
+ /*
+ * EBADF means the socket has been closed, ignore it.
+ */
+ if (errno != EBADF)
+ rtems_debugger_printf("error: rtems-db: accept: (%d) %s\n",
+ errno, strerror(errno));
+ return -1;
+ }
+
+ if (!running) {
+ close(tcp->fd);
+ errno = EIO;
+ return -1;
+ }
+
+ opt = 1;
+ r = setsockopt(tcp->fd,
+ SOL_SOCKET, SO_KEEPALIVE,
+ (char*) &opt,
+ sizeof(opt));
+ if (r < 0) {
+ int errno_ = errno;
+ close(tcp->fd);
+ rtems_debugger_printf("error: rtems-db: tcp remote: set keepalive: (%d) %s\n",
+ errno, strerror(errno));
+ errno = errno_;
+ return -1;
+ }
+
+ opt = 1;
+ r = setsockopt(tcp->fd,
+ IPPROTO_TCP, TCP_NODELAY,
+ (char*) &opt, sizeof(opt));
+ if (r < 0) {
+ int errno_ = errno;
+ close(tcp->fd);
+ rtems_debugger_printf("error: rtems-db: tcp remote: set no-delay: (%d) %s\n",
+ errno, strerror(errno));
+ errno = errno_;
+ return -1;
+ }
+
+ timeout.tv_sec = rtems_debugger->timeout;
+ timeout.tv_usec = 0;
+
+ r = setsockopt(tcp->fd,
+ SOL_SOCKET, SO_RCVTIMEO,
+ (char*) &timeout, sizeof(timeout));
+ if (r < 0) {
+ int errno_ = errno;
+ close(tcp->fd);
+ rtems_debugger_printf("error: rtems-db: tcp remote: set rcv-timeout: (%d) %s\n",
+ errno, strerror(errno));
+ errno = errno_;
+ return -1;
+ }
+
+ rtems_debugger_printf("rtems-db: tcp remote: connect host: %s\n",
+ inet_ntoa(addr.sin_addr));
+
+ return 0;
+}
+
+static int
+tcp_remote_disconnect(rtems_debugger_remote* remote)
+{
+ rtems_debugger_remote_tcp* tcp;
+
+ rtems_debugger_lock();
+
+ rtems_debugger_printf("rtems-db: tcp remote: disconnect host\n");
+
+ tcp = (rtems_debugger_remote_tcp*) remote->data;
+ close(tcp->fd);
+
+ rtems_debugger_unlock();
+
+ return 0;
+}
+
+static bool
+tcp_remote_isconnected(rtems_debugger_remote* remote)
+{
+ rtems_debugger_remote_tcp* tcp = tcp_remote(remote);
+ return tcp != NULL && tcp->fd >= 0;
+}
+
+static ssize_t
+tcp_remote_receive(rtems_debugger_remote* remote,
+ void* buf,
+ size_t nbytes)
+{
+ rtems_debugger_remote_tcp* tcp = tcp_remote(remote);
+ ssize_t len;
+ if (tcp != NULL) {
+ len = read(tcp->fd, buf, nbytes);
+ }
+ else {
+ errno = EIO;
+ len = -1;
+ }
+ return len;
+}
+
+static ssize_t
+tcp_remote_send(rtems_debugger_remote* remote,
+ const void* buf,
+ size_t nbytes)
+{
+ rtems_debugger_remote_tcp* tcp = tcp_remote(remote);
+ ssize_t len;
+ if (tcp != NULL) {
+ len = write(tcp->fd, buf, nbytes);
+ }
+ else {
+ errno = EIO;
+ len = -1;
+ }
+ return len;
+}
+
+static rtems_debugger_remote remote_tcp =
+{
+ .name = "tcp",
+ .begin = tcp_remote_begin,
+ .end = tcp_remote_end,
+ .connect = tcp_remote_connect,
+ .disconnect = tcp_remote_disconnect,
+ .isconnected = tcp_remote_isconnected,
+ .read = tcp_remote_receive,
+ .write = tcp_remote_send
+};
+
+int
+rtems_debugger_register_tcp_remote(void)
+{
+ return rtems_debugger_remote_register(&remote_tcp);
+}
diff --git a/cpukit/libdebugger/rtems-debugger-remote-tcp.h b/cpukit/libdebugger/rtems-debugger-remote-tcp.h
new file mode 100644
index 0000000000..d9d7feeb48
--- /dev/null
+++ b/cpukit/libdebugger/rtems-debugger-remote-tcp.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2016 Chris Johns <chrisj@rtems.org>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Debugger for RTEMS.
+ */
+
+#ifndef _RTEMS_DEBUGGER_REMOTE_TCP_h
+#define _RTEMS_DEBUGGER_REMOTE_TCP_h
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/**
+ * Register a remote with the server.
+ */
+int rtems_debugger_register_tcp_remote(void);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+
+#endif
diff --git a/cpukit/libdebugger/rtems-debugger-remote.c b/cpukit/libdebugger/rtems-debugger-remote.c
new file mode 100644
index 0000000000..f9a104db07
--- /dev/null
+++ b/cpukit/libdebugger/rtems-debugger-remote.c
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2016 Chris Johns <chrisj@rtems.org>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <rtems/rtems-debugger.h>
+#include <rtems/debugger/rtems-debugger-server.h>
+#include <rtems/debugger/rtems-debugger-remote.h>
+
+static rtems_debugger_remote* remotes[4];
+
+int
+rtems_debugger_remote_register(rtems_debugger_remote* remote)
+{
+ size_t r;
+ if (remote->begin == NULL || remote->end == NULL ||
+ remote->connect == NULL || remote->disconnect == NULL ||
+ remote->isconnected == NULL ||
+ remote->read == NULL || remote->write == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+ for (r = 0; r < RTEMS_DEBUGGER_NUMOF(remotes); ++r) {
+ if (remotes[r] == NULL) {
+ remotes[r] = remote;
+ return 0;
+ }
+ }
+ errno = ENOSPC;
+ return -1;
+}
+
+rtems_debugger_remote*
+rtems_debugger_remote_find(const char* name)
+{
+ size_t r;
+ for (r = 0; r < RTEMS_DEBUGGER_NUMOF(remotes); ++r) {
+ if (remotes[r] != NULL) {
+ if (strcasecmp(name, remotes[r]->name) == 0)
+ return remotes[r];
+ }
+ }
+ return NULL;
+}
diff --git a/cpukit/libdebugger/rtems-debugger-remote.h b/cpukit/libdebugger/rtems-debugger-remote.h
new file mode 100644
index 0000000000..3ea2b14b1e
--- /dev/null
+++ b/cpukit/libdebugger/rtems-debugger-remote.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2016 Chris Johns <chrisj@rtems.org>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Debugger for RTEMS.
+ */
+
+#ifndef _RTEMS_DEBUGGER_REMOTE_h
+#define _RTEMS_DEBUGGER_REMOTE_h
+
+#include <rtems/rtems-debugger.h>
+#include <rtems/debugger/rtems-debugger-server.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/**
+ * Debugger remote.
+ */
+struct rtems_debugger_remote
+{
+ const char* name;
+ int (*begin)(rtems_debugger_remote* remote, const char* device);
+ int (*end)(rtems_debugger_remote* remote);
+ int (*connect)(rtems_debugger_remote* remote);
+ int (*disconnect)(rtems_debugger_remote* remote);
+ bool (*isconnected)(rtems_debugger_remote* remote);
+ ssize_t (*read)(rtems_debugger_remote* remote, void* buf, size_t nbytes);
+ ssize_t (*write)(rtems_debugger_remote* remote, const void* buf, size_t nbytes);
+ void* data;
+};
+
+/**
+ * Register a remote with the server.
+ */
+int rtems_debugger_remote_register(rtems_debugger_remote* remote);
+
+/**
+ * Find a remote by name.
+ */
+rtems_debugger_remote* rtems_debugger_remote_find(const char* name);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+
+#endif
diff --git a/cpukit/libdebugger/rtems-debugger-server.c b/cpukit/libdebugger/rtems-debugger-server.c
new file mode 100644
index 0000000000..8c20cf6fd4
--- /dev/null
+++ b/cpukit/libdebugger/rtems-debugger-server.c
@@ -0,0 +1,1993 @@
+/*
+ * Copyright (c) 2016 Chris Johns <chrisj@rtems.org>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <rtems/rtems-debugger.h>
+#include <rtems/debugger/rtems-debugger-server.h>
+#include <rtems/debugger/rtems-debugger-remote.h>
+
+#include "rtems-debugger-target.h"
+#include "rtems-debugger-threads.h"
+
+/*
+ * GDB Debugger Remote Server for RTEMS.
+ */
+
+/*
+ * Hack to void including bsp.h. The reset needs a better API.
+ */
+extern void bsp_reset(void);
+
+/*
+ * Command lookup table.
+ */
+typedef int (*rtems_debugger_command)(uint8_t* buffer, int size);
+
+typedef struct rtems_debugger_packet
+{
+ const char const* label;
+ rtems_debugger_command command;
+} rtems_debugger_packet;
+
+/**
+ * Common error strings.
+ */
+static const char const* r_OK = "OK";
+static const char const* r_E01 = "E01";
+
+/*
+ * Global Debugger.
+ *
+ * The server instance is allocated on the heap so memory is only used then the
+ * server is running. A global is used because:
+ *
+ * 1. There can only be a single instance at once.
+ * 2. The backend's need access to the data and holding pointers in the TCB
+ * for each thread is mess.
+ * 3. The code is smaller and faster.
+ */
+rtems_debugger_server* rtems_debugger;
+
+int
+rtems_debugger_printf(const char* format, ...)
+{
+ int len;
+ va_list ap;
+ va_start(ap, format);
+ len = rtems_vprintf(&rtems_debugger->printer, format, ap);
+ va_end(ap);
+ return len;
+}
+
+bool
+rtems_debugger_verbose(void)
+{
+ return rtems_debugger_server_flag(RTEMS_DEBUGGER_FLAG_VERBOSE);
+}
+
+static inline int
+hex_decode(uint8_t ch)
+{
+ int i;
+ if (ch >= '0' && ch <= '9')
+ i = (int) (ch - '0');
+ else if (ch >= 'a' && ch <= 'f')
+ i = (int) (ch - 'a') + 10;
+ else if (ch >= 'A' && ch <= 'F')
+ i = (int) (ch - 'A') + 10;
+ else
+ i = -1;
+ return i;
+}
+
+static inline uint8_t
+hex_encode(int val)
+{
+ return "0123456789abcdef"[val & 0xf];
+}
+
+static inline DB_UINT
+hex_decode_uint(const uint8_t* data)
+{
+ DB_UINT ui = 0;
+ size_t i;
+ if (data[0] == '-') {
+ if (data[1] == '1')
+ ui = (DB_UINT) -1;
+ }
+ else {
+ for (i = 0; i < (sizeof(ui) * 2); ++i) {
+ int v = hex_decode(data[i]);
+ if (v < 0)
+ break;
+ ui = (ui << 4) | v;
+ }
+ }
+ return ui;
+}
+
+static inline int
+hex_decode_int(const uint8_t* data)
+{
+ return (int) hex_decode_uint(data);
+}
+
+static bool
+thread_id_decode(const char* data, DB_UINT* pid, DB_UINT* tid)
+{
+ bool is_extended = false;
+ if (*data == 'p') {
+ is_extended = true;
+ ++data;
+ }
+ *pid = *tid = hex_decode_uint((const uint8_t*) data);
+ if (is_extended) {
+ const char* stop = strchr(data, '.');
+ if (stop != NULL) {
+ *tid = hex_decode_uint((const uint8_t*) stop + 1);
+ }
+ }
+ return is_extended;
+}
+
+static inline bool
+check_pid(DB_UINT pid)
+{
+ return pid == 0|| rtems_debugger->pid == (pid_t) pid;
+}
+
+int
+rtems_debugger_lock(void)
+{
+ if (rtems_debugger->lock != 0) {
+ rtems_status_code sc;
+ sc = rtems_semaphore_obtain(rtems_debugger->lock, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
+ if (sc != RTEMS_SUCCESSFUL) {
+ rtems_debugger_printf("error: rtems-db: lock: %s\n",
+ rtems_status_text(sc));
+ return -1;
+ }
+ }
+ return 0;
+}
+
+int
+rtems_debugger_unlock(void)
+{
+ if (rtems_debugger->lock != 0) {
+ rtems_status_code sc;
+ sc = rtems_semaphore_release(rtems_debugger->lock);
+ if (sc != RTEMS_SUCCESSFUL) {
+ rtems_debugger_printf("error: rtems-db: unlock: %s\n",
+ rtems_status_text(sc));
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static int
+rtems_debugger_lock_create(void)
+{
+ #define LOCK_ATTRIBUTES \
+ RTEMS_PRIORITY | RTEMS_INHERIT_PRIORITY | RTEMS_BINARY_SEMAPHORE
+ rtems_status_code sc;
+ sc = rtems_semaphore_create(rtems_build_name('G', 'D', 'B', 's'),
+ 1,
+ LOCK_ATTRIBUTES,
+ 0,
+ &rtems_debugger->lock);
+ if (sc != RTEMS_SUCCESSFUL) {
+ rtems_debugger_printf("error: rtems-db: sema create: %s\n",
+ rtems_status_text(sc));
+ errno = EIO;
+ return -1;
+ }
+ return 0;
+}
+
+static int
+rtems_debugger_lock_destroy(void)
+{
+ rtems_debugger_lock();
+ if (rtems_debugger->lock != 0) {
+ rtems_status_code sc;
+ rtems_semaphore_release(rtems_debugger->lock);
+ sc = rtems_semaphore_delete(rtems_debugger->lock);
+ rtems_debugger->lock = 0;
+ if (sc != RTEMS_SUCCESSFUL) {
+ rtems_debugger_printf("error: rtems-db: sema delete: %s\n",
+ rtems_status_text(sc));
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static int
+rtems_debugger_task_create(const char* name,
+ rtems_task_priority priority,
+ size_t stack_size,
+ rtems_task_entry entry_point,
+ rtems_task_argument argument,
+ rtems_id* id)
+{
+ rtems_name tname;
+ rtems_status_code sc;
+
+ tname = rtems_build_name(name[0], name[1], name[2], name[3]);
+
+ sc = rtems_task_create (tname,
+ priority,
+ stack_size,
+ RTEMS_FLOATING_POINT | RTEMS_LOCAL,
+ RTEMS_PREEMPT | RTEMS_NO_ASR,
+ id);
+ if (sc != RTEMS_SUCCESSFUL) {
+ *id = 0;
+ rtems_debugger_printf("error: rtems-db: thread create: %s: %s\n",
+ name, rtems_status_text(sc));
+ errno = EIO;
+ return -1;
+ }
+
+ sc = rtems_task_start(*id, entry_point, argument);
+ if (sc != RTEMS_SUCCESSFUL) {
+ rtems_debugger_printf("error: rtems-db: thread start: %s: %s\n",
+ name, rtems_status_text(sc));
+ rtems_task_delete(*id);
+ *id = 0;
+ errno = EIO;
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+rtems_debugger_task_destroy(const char* name,
+ rtems_id id,
+ volatile bool* finished,
+ int timeout)
+{
+ while (timeout) {
+ bool has_finished;
+ rtems_debugger_lock();
+ has_finished = *finished;
+ rtems_debugger_unlock();
+
+ if (has_finished)
+ break;
+
+ usleep(RTEMS_DEBUGGER_POLL_WAIT);
+ if (timeout < RTEMS_DEBUGGER_POLL_WAIT)
+ timeout = 0;
+ else
+ timeout -= RTEMS_DEBUGGER_POLL_WAIT;
+ }
+
+ if (timeout == 0) {
+ rtems_debugger_printf("rtems-db: %s not stopping, killing\n", name);
+ rtems_task_delete(id);
+ }
+ return 0;
+}
+
+bool
+rtems_debugger_server_running(void)
+{
+ bool running;
+ rtems_debugger_lock();
+ running = rtems_debugger->server_running;
+ rtems_debugger_unlock();
+ return running;
+}
+
+rtems_debugger_remote*
+rtems_debugger_remote_handle(void)
+{
+ rtems_debugger_remote* remote;
+ rtems_debugger_lock();
+ remote = rtems_debugger->remote;
+ rtems_debugger_unlock();
+ return remote;
+}
+
+bool
+rtems_debugger_connected(void)
+{
+ bool isconnected = false;
+ rtems_debugger_lock();
+ if (rtems_debugger->remote != NULL)
+ isconnected = rtems_debugger->remote->isconnected(rtems_debugger->remote);
+ rtems_debugger_unlock();
+ return isconnected;
+}
+
+bool
+rtems_debugger_server_events_running(void)
+{
+ bool running;
+ rtems_debugger_lock();
+ running = rtems_debugger->events_running;
+ rtems_debugger_unlock();
+ return running;
+}
+
+int
+rtems_debugger_server_events_wake(void)
+{
+ rtems_status_code sc;
+ int r = 0;
+ sc = rtems_event_send(rtems_debugger->events_task, RTEMS_EVENT_1);
+ if (sc != RTEMS_SUCCESSFUL) {
+ rtems_debugger_printf("error: rtems-db: event send: %s\n",
+ rtems_status_text(sc));
+ errno = EIO;
+ r = -1;
+ }
+ return r;
+}
+
+static int
+rtems_debugger_server_events_wait(void)
+{
+ rtems_event_set out = 0;
+ rtems_status_code sc;
+ int r = 0;
+ rtems_debugger_unlock();
+ while (true) {
+ sc = rtems_event_receive(RTEMS_EVENT_1,
+ RTEMS_EVENT_ALL | RTEMS_WAIT,
+ RTEMS_NO_TIMEOUT,
+ &out);
+ if (sc != RTEMS_SUCCESSFUL) {
+ rtems_debugger_printf("error: rtems-db: event receive: %s\n",
+ rtems_status_text(sc));
+ errno = EIO;
+ r = -1;
+ break;
+ }
+ if (out == RTEMS_EVENT_1)
+ break;
+ }
+ rtems_debugger_lock();
+ return r;
+}
+
+static int
+rtems_debugger_remote_connect(void)
+{
+ rtems_debugger_remote* remote = rtems_debugger_remote_handle();
+ if (remote == NULL) {
+ errno = EIO;
+ return -1;
+ }
+ if (!remote->isconnected(remote))
+ return remote->connect(remote);
+}
+
+static int
+rtems_debugger_remote_disconnect(void)
+{
+ rtems_debugger_remote* remote = rtems_debugger_remote_handle();
+ if (remote == NULL) {
+ errno = EIO;
+ return -1;
+ }
+ if (remote->isconnected(remote))
+ return remote->disconnect(remote);
+}
+
+static int
+rtems_debugger_remote_receive(uint8_t* buffer, size_t size)
+{
+ rtems_debugger_remote* remote = rtems_debugger_remote_handle();
+ ssize_t len = remote->read(remote, buffer, size);
+ if (len < 0 && errno != EAGAIN)
+ rtems_debugger_printf("rtems-db: read: (%d) %s\n",
+ errno, strerror(errno));
+ return (int) len;
+}
+
+static int
+rtems_debugger_remote_send(void)
+{
+ const uint8_t* buffer = rtems_debugger->output;
+ ssize_t size = rtems_debugger->output_level;
+
+ if (rtems_debugger->output_level > RTEMS_DEBUGGER_BUFFER_SIZE) {
+ rtems_debugger_printf("rtems-db: write too big: %d\n",
+ (int) rtems_debugger->output_level);
+ return -1;
+ }
+
+ if (rtems_debugger->remote_debug) {
+ size_t i = 0;
+ rtems_debugger_printf("rtems-db: put:%4zu: ", rtems_debugger->output_level);
+ while (i < rtems_debugger->output_level)
+ rtems_debugger_printf("%c", (char) rtems_debugger->output[i++]);
+ rtems_debugger_printf("\n");
+ }
+
+ while (size) {
+ rtems_debugger_remote* remote = rtems_debugger_remote_handle();
+ ssize_t w;
+ if (remote == NULL) {
+ errno = EIO;
+ return -1;
+ }
+ w = remote->write(remote, buffer, size);
+ if (w < 0 && errno != EINTR) {
+ rtems_debugger_printf("rtems-db: write: (%d) %s\n",
+ errno, strerror(errno));
+ break;
+ }
+ else {
+ size -= w;
+ buffer += w;
+ }
+ }
+
+ return (int) rtems_debugger->output_level;
+}
+
+static int
+rtems_debugger_remote_send_ack(void)
+{
+ rtems_debugger->output[0] = '+';
+ rtems_debugger->output_level = 1;
+ return rtems_debugger_remote_send();
+}
+
+static int
+rtems_debugger_remote_send_nack(void)
+{
+ rtems_debugger->output[0] = '-';
+ rtems_debugger->output_level = 1;
+ return rtems_debugger_remote_send();
+}
+
+static int
+rtems_debugger_remote_packet_in(void)
+{
+ uint8_t buf[256];
+ uint8_t state;
+ int in = 0;
+ uint8_t csum = 0;
+ uint8_t rx_csum = 0;
+ bool junk = false;
+ bool escaped = false;
+ bool remote_debug_header = true;
+
+ /*
+ * States:
+ * 'H' : Looking for the start character '$', '-' or '+'.
+ * 'P' : Looking for the checksum character '#' else buffer data.
+ * '1' : Looking for the first checksum character.
+ * '2' : Looking for the second checksum character.
+ * 'F' : Finished.
+ */
+
+ state = 'H';
+
+ while (state != 'F') {
+ int r;
+ int i;
+
+ rtems_debugger_unlock();
+
+ r = rtems_debugger_remote_receive(buf, sizeof(buf));
+
+ rtems_debugger_lock();
+
+ if (r <= 0) {
+ /*
+ * Timeout?
+ */
+ if (r < 0 && errno == EAGAIN) {
+ if (rtems_debugger->ack_pending) {
+ rtems_debugger_remote_send();
+ }
+ continue;
+ }
+ if (r == 0)
+ rtems_debugger_printf("rtems-db: remote disconnected\n");
+ return -1;
+ }
+
+ i = 0;
+
+ while (i < r) {
+ uint8_t c = buf[i++];
+
+ if (rtems_debugger->remote_debug && remote_debug_header) {
+ rtems_debugger_printf("rtems-db: get:%4d: ", r);
+ remote_debug_header = false;
+ }
+
+ if (rtems_debugger->remote_debug)
+ rtems_debugger_printf("%c", c);
+
+ switch (state) {
+ case 'H':
+ switch (c) {
+ case '+':
+ if (rtems_debugger->remote_debug) {
+ rtems_debugger_printf(" [[ACK%s]]\n",
+ rtems_debugger->ack_pending ? "" : "?");
+ remote_debug_header = true;
+ }
+ rtems_debugger->ack_pending = false;
+ break;
+ case '-':
+ if (rtems_debugger->remote_debug) {
+ rtems_debugger_printf(" [[NACK]]\n");
+ remote_debug_header = true;
+ }
+ /*
+ * Resend.
+ */
+ rtems_debugger_remote_send();
+ break;
+ case '$':
+ state = 'P';
+ csum = 0;
+ in = 0;
+ if (junk && rtems_debugger->remote_debug) {
+ rtems_debugger_printf("\b [[junk dropped]]\nrtems-db: get: : $");
+ remote_debug_header = false;
+ }
+ break;
+ case '\x3':
+ if (rtems_debugger->remote_debug)
+ rtems_debugger_printf("^C [[BREAK]]\n");
+ rtems_debugger->ack_pending = false;
+ rtems_debugger->input[0] = '^';
+ rtems_debugger->input[1] = 'C';
+ rtems_debugger->input[2] = '\0';
+ return 2;
+ default:
+ junk = true;
+ break;
+ }
+ break;
+ case 'P':
+ if (c == '{' && !escaped) {
+ escaped = true;
+ }
+ else if (c == '$' && !escaped) {
+ csum = 0;
+ in = 0;
+ if (rtems_debugger->remote_debug) {
+ rtems_debugger_printf("\n");
+ remote_debug_header = true;
+ }
+ }
+ else if (c == '#' && !escaped) {
+ rtems_debugger->input[in] = '\0';
+ rx_csum = 0;
+ state = '1';
+ }
+ else {
+ if (in >= (RTEMS_DEBUGGER_BUFFER_SIZE - 1)) {
+ rtems_debugger_printf("rtems-db: input buffer overflow\n");
+ return -1;
+ }
+ csum += c;
+ rtems_debugger->input[in++] = c;
+ }
+ break;
+ case '1':
+ rx_csum = (rx_csum << 4) | (uint8_t) hex_decode(c);
+ state = '2';
+ break;
+ case '2':
+ rx_csum = (rx_csum << 4) | (uint8_t) hex_decode(c);
+ if (csum == rx_csum) {
+ state = 'F';
+ if (rtems_debugger->remote_debug)
+ rtems_debugger_printf("\n");
+ rtems_debugger_remote_send_ack();
+ }
+ else {
+ if (rtems_debugger->remote_debug) {
+ rtems_debugger_printf(" [[invalid checksum]]\n");
+ remote_debug_header = true;
+ rtems_debugger_remote_send_nack();
+ }
+ state = 'H';
+ }
+ break;
+ case 'F':
+ if (rtems_debugger->remote_debug)
+ rtems_debugger_printf(" [[extra data: 0x%02x]]", (int) c);
+ break;
+ default:
+ rtems_debugger_printf("rtems-db: bad state\n");
+ rtems_debugger_remote_send_nack();
+ return -1;
+ }
+ }
+ }
+
+ return in;
+}
+
+static int
+rtems_debugger_remote_packet_in_hex(uint8_t* addr,
+ const char* data,
+ size_t size)
+{
+ size_t i;
+ for (i = 0; i < size; ++i) {
+ *addr = (hex_decode(*data++) << 4);
+ *addr++ |= hex_decode(*data++);
+ }
+ return 0;
+}
+
+#if KEEP_INCASE
+static void
+remote_packet_out_rewind(size_t size)
+{
+ size_t i = 0;
+ while (rtems_debugger->output_level > 0 && i < size) {
+ if (rtems_debugger->output_level > 1) {
+ if (rtems_debugger->output[rtems_debugger->output_level - 1] == '}') {
+ --rtems_debugger->output_level;
+ }
+ }
+ --rtems_debugger->output_level;
+ --i;
+ }
+}
+#endif
+
+static int
+remote_packet_out_append_buffer(const char* buffer, size_t size)
+{
+ size_t ol = rtems_debugger->output_level;
+ size_t i = 0;
+ while (i < size) {
+ char c = buffer[i++];
+ if (c == '#' || c == '$') {
+ if (rtems_debugger->output_level >= (RTEMS_DEBUGGER_BUFFER_SIZE - 1)) {
+ rtems_debugger->output_level = ol;
+ rtems_debugger_printf("rtems-db: output overflow\n");
+ return -1;
+ }
+ rtems_debugger->output[rtems_debugger->output_level++] = '}';
+ c ^= 0x20;
+ }
+ if (rtems_debugger->output_level >= (RTEMS_DEBUGGER_BUFFER_SIZE - 1)) {
+ rtems_debugger->output_level = ol;
+ rtems_debugger_printf("rtems-db: output overflow\n");
+ return -1;
+ }
+ rtems_debugger->output[rtems_debugger->output_level++] = c;
+ }
+ return 0;
+}
+
+static int
+remote_packet_out_append_hex(const uint8_t* data, size_t size)
+{
+ size_t ol = rtems_debugger->output_level;
+ size_t i = 0;
+ while (i < size) {
+ uint8_t byte = data[i++];
+ if (rtems_debugger->output_level >= (RTEMS_DEBUGGER_BUFFER_SIZE - 2)) {
+ rtems_debugger->output_level = ol;
+ rtems_debugger_printf("rtems-db: output overflow\n");
+ return -1;
+ }
+ rtems_debugger->output[rtems_debugger->output_level++] = hex_encode(byte >> 4);
+ rtems_debugger->output[rtems_debugger->output_level++] = hex_encode(byte);
+ }
+ return 0;
+}
+
+static int
+remote_packet_out_append_str(const char* str)
+{
+ return remote_packet_out_append_buffer(str, strlen(str));
+}
+
+static int
+remote_packet_out_append_vprintf(const char* fmt, va_list ap)
+{
+ int len;
+ char buffer[64];
+ len = vsnprintf(buffer, sizeof(buffer), fmt, ap);
+ return remote_packet_out_append_buffer(buffer, len);
+}
+
+static int
+remote_packet_out_append(const char* fmt, ...)
+{
+ va_list ap;
+ int r;
+ va_start(ap, fmt);
+ r = remote_packet_out_append_vprintf(fmt, ap);
+ va_end(ap);
+ return r;
+}
+
+static void
+remote_packet_out_reset(void)
+{
+ rtems_debugger->output_level = 1;
+ rtems_debugger->output[0] = '$';
+}
+
+static int
+remote_packet_out_buffer(const char* buffer, size_t size)
+{
+ remote_packet_out_reset();
+ return remote_packet_out_append_buffer(buffer, size);
+}
+
+static int
+remote_packet_out_str(const char* str)
+{
+ remote_packet_out_reset();
+ return remote_packet_out_append_buffer(str, strlen(str));
+}
+
+static int
+remote_packet_out(const char* fmt, ...)
+{
+ va_list ap;
+ int r;
+ va_start(ap, fmt);
+ remote_packet_out_reset();
+ r = remote_packet_out_append_vprintf(fmt, ap);
+ va_end(ap);
+ return r;
+}
+
+static int
+remote_packet_out_send(void)
+{
+ uint8_t csum = 0;
+ size_t i = 1;
+
+ if (rtems_debugger->output_level >= (RTEMS_DEBUGGER_BUFFER_SIZE - 3)) {
+ rtems_debugger_printf("rtems-db: output overflow\n");
+ return -1;
+ }
+
+ while (i < rtems_debugger->output_level) {
+ csum += rtems_debugger->output[i++];
+ }
+
+ rtems_debugger->output[rtems_debugger->output_level++] = '#';
+ rtems_debugger->output[rtems_debugger->output_level++] = hex_encode((csum >> 4) & 0xf);
+ rtems_debugger->output[rtems_debugger->output_level++] = hex_encode(csum & 0xf);
+
+ rtems_debugger->ack_pending = true;;
+
+ return rtems_debugger_remote_send();
+}
+
+static int
+remote_packet_dispatch(const rtems_debugger_packet* packet,
+ size_t packets,
+ uint8_t* buffer,
+ int size)
+{
+ const rtems_debugger_packet* p;
+ size_t i;
+ int r = -1;
+ for (i = 0, p = &packet[0]; i < packets; ++i, ++p) {
+ if (strncmp(p->label,
+ (const char*) &buffer[0],
+ strlen(p->label)) == 0) {
+ r = p->command(buffer, size);
+ break;
+ }
+ }
+ if (r < 0) {
+ remote_packet_out_buffer("", 0);
+ remote_packet_out_send();
+ }
+ return 0;
+}
+
+static int
+remote_detach(uint8_t* buffer, int size)
+{
+ remote_packet_out_str(r_OK);
+ remote_packet_out_send();
+ rtems_debugger_remote_disconnect();
+ return 0;
+}
+
+static int
+remote_ut_features(uint8_t* buffer, int size)
+{
+ return -1;
+}
+
+static int
+remote_ut_osdata(uint8_t* buffer, int size)
+{
+ return -1;
+}
+
+static const rtems_debugger_packet uninterpreted_transfer[] = {
+ { .label = "qXfer:features",
+ .command = remote_ut_features },
+ { .label = "qXfer:osdata",
+ .command = remote_ut_osdata },
+};
+
+#define REMOTE_UNINTERPRETED_TRANSFERS \
+ RTEMS_DEBUGGER_NUMOF(uninterpreted_transfer)
+
+static int
+remote_gq_uninterpreted_transfer(uint8_t* buffer, int size)
+{
+ return remote_packet_dispatch(uninterpreted_transfer,
+ REMOTE_UNINTERPRETED_TRANSFERS,
+ buffer, size);
+}
+
+static int
+remote_gq_thread_info_subsequent(uint8_t* buffer, int size)
+{
+ rtems_debugger_threads* threads = rtems_debugger->threads;
+ if (threads->next >= threads->current.level)
+ remote_packet_out_str("l");
+ else {
+ rtems_debugger_thread* current;
+ const char* format = "p%d.%08lx";
+ current = rtems_debugger_thread_current(threads);
+ remote_packet_out_str("m");
+ while (threads->next < threads->current.level) {
+ int r;
+ r = remote_packet_out_append(format,
+ rtems_debugger->pid,
+ current[threads->next].id);
+ if (r < 0)
+ break;
+ format = ",p%d.%08lx";
+ ++threads->next;
+ }
+ }
+ remote_packet_out_send();
+ return 0;
+}
+
+static int
+remote_gq_thread_info_first(uint8_t* buffer, int size)
+{
+ rtems_debugger->threads->next = 0;
+ return remote_gq_thread_info_subsequent(buffer, size);
+}
+
+static int
+remote_gq_thread_extra_info(uint8_t* buffer, int size)
+{
+ const char* comma;
+ remote_packet_out_reset();
+ comma = strchr((const char*) buffer, ',');
+ if (comma != NULL) {
+ DB_UINT pid = 0;
+ DB_UINT tid = 0;
+ bool extended;
+ extended = thread_id_decode(comma + 1, &pid, &tid);
+ if (!extended || (extended && check_pid(pid))) {
+ int r;
+ r = rtems_debugger_thread_find_index(tid);
+ if (r >= 0) {
+ rtems_debugger_threads* threads = rtems_debugger->threads;
+ rtems_debugger_thread* current;
+ rtems_debugger_thread* thread;
+ char buf[128];
+ char str[32];
+ size_t l;
+ current = rtems_debugger_thread_current(threads);
+ thread = &current[r];
+ l = snprintf(buf, sizeof(buf),
+ "%4s (%08lx), ", thread->name, thread->id);
+ remote_packet_out_append_hex((const uint8_t*) buf, l);
+ l = snprintf(buf, sizeof(buf),
+ "priority(c:%3d r:%3d), ",
+ rtems_debugger_thread_current_priority(thread),
+ rtems_debugger_thread_real_priority(thread));
+ remote_packet_out_append_hex((const uint8_t*) buf, l);
+ l = snprintf(buf, sizeof(buf),
+ "stack(s:%6lu a:%p), ",
+ rtems_debugger_thread_stack_size(thread),
+ rtems_debugger_thread_stack_area(thread));
+ remote_packet_out_append_hex((const uint8_t*) buf, l);
+ rtems_debugger_thread_state_str(thread, str, sizeof(str));
+ l = snprintf(buf, sizeof(buf), "state(%s)", str);
+ remote_packet_out_append_hex((const uint8_t*) buf, l);
+ }
+ }
+ }
+ remote_packet_out_send();
+ return 0;
+}
+
+static int
+remote_gq_supported(uint8_t* buffer, int size)
+{
+ uint32_t capabilities = rtems_debugger_target_capabilities();
+ const char* p;
+ bool swbreak = false;
+ bool hwbreak = false;
+ bool vCont = false;
+ bool no_resumed = false;
+ bool multiprocess = false;
+ remote_packet_out("qSupported:PacketSize=%d;QNonStop-",
+ RTEMS_DEBUGGER_BUFFER_SIZE);
+ p = strchr((const char*) buffer, ':');
+ if (p != NULL)
+ ++p;
+ while (p != NULL && p != '\0') {
+ bool echo = false;
+ char* sc;
+ sc = strchr(p, ';');
+ if (sc != NULL) {
+ *sc++ = '\0';
+ }
+ if (strcmp(p, "swbreak+") == 0 &&
+ !swbreak && (capabilities & RTEMS_DEBUGGER_TARGET_CAP_SWBREAK) != 0) {
+ swbreak = true;
+ echo = true;
+ }
+ if (strcmp(p, "hwbreak+") == 0 &&
+ !hwbreak && (capabilities & RTEMS_DEBUGGER_TARGET_CAP_HWBREAK) != 0) {
+ hwbreak = true;
+ echo = true;
+ }
+ if (!vCont && strcmp(p, "vContSupported+") == 0) {
+ rtems_debugger->flags |= RTEMS_DEBUGGER_FLAG_VCONT;
+ vCont = true;
+ echo = true;
+ }
+ if (!no_resumed && strcmp(p, "no-resumed+") == 0) {
+ no_resumed = true;
+ echo = true;
+ }
+ if (!multiprocess && strcmp(p, "multiprocess+") == 0) {
+ rtems_debugger->flags |= RTEMS_DEBUGGER_FLAG_MULTIPROCESS;
+ multiprocess = true;
+ echo = true;
+ }
+
+ if (echo) {
+ remote_packet_out_append_str(";");
+ remote_packet_out_append_str(p);
+ }
+ else if (strncmp(p, "xmlRegisters", sizeof("xmlRegisters") - 1) == 0) {
+ /* ignore */
+ }
+ else {
+ remote_packet_out_append_str(";");
+ remote_packet_out_append_buffer(p, strlen(p) - 1);
+ remote_packet_out_append_str("-");
+ }
+ p = sc;
+ }
+ if (!swbreak && (capabilities & RTEMS_DEBUGGER_TARGET_CAP_SWBREAK) != 0) {
+ remote_packet_out_append_str("swbreak+;");
+ }
+ if (!hwbreak && (capabilities & RTEMS_DEBUGGER_TARGET_CAP_HWBREAK) != 0) {
+ remote_packet_out_append_str("hwbreak+;");
+ }
+ if (!vCont) {
+ remote_packet_out_append_str("vContSupported+;");
+ }
+ if (!no_resumed) {
+ remote_packet_out_append_str("no-resumed+;");
+ }
+ if (!multiprocess) {
+ remote_packet_out_append_str("multiprocess+;");
+ }
+ remote_packet_out_send();
+ return 0;
+}
+
+static int
+remote_gq_attached(uint8_t* buffer, int size)
+{
+ const char const* response = "1";
+ const char* colon = strchr((const char*) buffer, ':');
+ if (colon != NULL) {
+ DB_UINT pid = hex_decode_uint((const uint8_t*) colon + 1);
+ if ((pid_t) pid != rtems_debugger->pid)
+ response = r_E01;
+ }
+ remote_packet_out_str(response);
+ remote_packet_out_send();
+ return 0;
+}
+
+static const rtems_debugger_packet general_query[] = {
+ { .label = "qfThreadInfo",
+ .command = remote_gq_thread_info_first },
+ { .label = "qsThreadInfo",
+ .command = remote_gq_thread_info_subsequent },
+ { .label = "qThreadExtraInfo",
+ .command = remote_gq_thread_extra_info },
+ { .label = "qSupported",
+ .command = remote_gq_supported },
+ { .label = "qAttached",
+ .command = remote_gq_attached },
+ { .label = "qXfer",
+ .command = remote_gq_uninterpreted_transfer },
+};
+
+#define REMOTE_GENERAL_QUERIES RTEMS_DEBUGGER_NUMOF(general_query)
+
+static int
+remote_general_query(uint8_t* buffer, int size)
+{
+ return remote_packet_dispatch(general_query, REMOTE_GENERAL_QUERIES,
+ buffer, size);
+}
+
+static int
+remote_gs_non_stop(uint8_t* buffer, int size)
+{
+ const char const* response = r_E01;
+ char* p = strchr((char*) buffer, ':');
+ if (p != NULL) {
+ ++p;
+ response = r_OK;
+ if (*p == '0') {
+ rtems_debugger->flags &= ~RTEMS_DEBUGGER_FLAG_NON_STOP;
+ }
+ else if (*p == '1') {
+ rtems_debugger->flags |= RTEMS_DEBUGGER_FLAG_NON_STOP;
+ }
+ else
+ response = r_E01;
+ }
+ remote_packet_out_str(response);
+ remote_packet_out_send();
+ return 0;
+}
+
+static const rtems_debugger_packet general_set[] = {
+ { .label = "QNonStop",
+ .command = remote_gs_non_stop },
+};
+
+#define REMOTE_GENERAL_SETS RTEMS_DEBUGGER_NUMOF(general_set)
+
+static int
+remote_general_set(uint8_t* buffer, int size)
+{
+ return remote_packet_dispatch(general_set, REMOTE_GENERAL_SETS,
+ buffer, size);
+}
+
+static int
+remote_v_stopped(uint8_t* buffer, int size)
+{
+ rtems_debugger_threads* threads = rtems_debugger->threads;
+ if (threads->next >= threads->stopped.level)
+ remote_packet_out_str(r_OK);
+ else {
+ rtems_id* stopped;
+ remote_packet_out("T%02x", rtems_debugger->signal);
+ stopped = rtems_debugger_thread_stopped(threads);
+ while (threads->next < threads->stopped.level) {
+ int r;
+ r = remote_packet_out_append("thread:p%d.%08lx;",
+ rtems_debugger->pid,
+ stopped[threads->next]);
+ if (r < 0)
+ break;
+ ++threads->next;
+ }
+ }
+ remote_packet_out_send();
+ return 0;
+}
+
+static int
+remote_stop_reason(uint8_t* buffer, int size)
+{
+ rtems_debugger->threads->next = 0;
+ return remote_v_stopped(buffer, size);
+}
+
+static int
+remote_v_continue(uint8_t* buffer, int size)
+{
+ buffer += 5;
+
+ if (buffer[0] == '?') {
+ /*
+ * You need to supply 'c' and 'C' or GDB says vCont is not supported. As
+ * Sammy-J says "Silly GDB".
+ */
+ remote_packet_out_str("vCont;c;C;s;r;");
+ }
+ else {
+ const char* semi = (const char*) &buffer[0];
+ bool resume = false;
+ bool ok = true;
+ while (ok && semi != NULL) {
+ const char* colon = strchr(semi + 1, ':');
+ const char action = *(semi + 1);
+ DB_UINT pid = 0;
+ DB_UINT tid = 0;
+ bool extended;
+ if (colon != NULL) {
+ int r = -1;
+ extended = thread_id_decode(colon + 1, &pid, &tid);
+ if (extended || check_pid(pid)) {
+ rtems_debugger_threads* threads = rtems_debugger->threads;
+ rtems_debugger_thread* thread = NULL;
+ int index = 0;
+ if (tid != (DB_UINT) -1) {
+ rtems_debugger_thread* current;
+ current = rtems_debugger_thread_current(threads);
+ index = rtems_debugger_thread_find_index(tid);
+ if (index >= 0)
+ thread = &current[index];
+ }
+ switch (action) {
+ case 'c':
+ case 'C':
+ if (tid == (DB_UINT) -1) {
+ r = rtems_debugger_thread_continue_all();
+ }
+ else if (thread != NULL) {
+ r = rtems_debugger_thread_continue(thread);
+ }
+ if (r == 0)
+ resume = true;
+ break;
+ case 's':
+ if (thread != NULL) {
+ r = rtems_debugger_thread_step(thread);
+ if (r == 0)
+ resume = true;
+ }
+ break;
+ case 'r':
+ /*
+ * Range to step around inside: `r start,end`.
+ */
+ if (thread != NULL) {
+ const char* comma;
+ comma = strchr(semi + 2, ',');
+ if (comma != NULL) {
+ DB_UINT start;
+ DB_UINT end;
+ start = hex_decode_uint((const uint8_t*) semi + 2);
+ end = hex_decode_uint((const uint8_t*) comma + 1);
+ r = rtems_debugger_thread_stepping(thread, start, end);
+ if (r == 0)
+ resume = true;
+ }
+ else {
+ ok = false;
+ }
+ }
+ break;
+ default:
+ ok = false;
+ break;
+ }
+ if (r < 0)
+ ok = false;
+ }
+ }
+ else {
+ ok = false;
+ }
+ semi = strchr(semi + 1, ';');
+ }
+
+ if (ok)
+ remote_packet_out_str(r_OK);
+ else
+ remote_packet_out_str(r_E01);
+
+ if (resume)
+ rtems_debugger_thread_system_resume(false);
+ }
+
+ remote_packet_out_send();
+
+ return 0;
+}
+
+static int
+remote_v_kill(uint8_t* buffer, int size)
+{
+ rtems_debugger->flags |= RTEMS_DEBUGGER_FLAG_RESET;
+ return remote_detach(buffer, size);
+}
+
+static const rtems_debugger_packet v_packets[] = {
+ { .label = "vCont",
+ .command = remote_v_continue },
+ { .label = "vStopped",
+ .command = remote_v_stopped },
+ { .label = "vKill",
+ .command = remote_v_kill },
+};
+
+#define REMOTE_V_PACKETS RTEMS_DEBUGGER_NUMOF(v_packets)
+
+static int
+remote_v_packets(uint8_t* buffer, int size)
+{
+ return remote_packet_dispatch(v_packets, REMOTE_V_PACKETS,
+ buffer, size);
+}
+
+static int
+remote_thread_select(uint8_t* buffer, int size)
+{
+ const char const* response = r_OK;
+ int* index = NULL;
+
+ if (buffer[1] == 'g')
+ index = &rtems_debugger->threads->selector_gen;
+ else if (buffer[1] == 'c')
+ index = &rtems_debugger->threads->selector_cont;
+ else
+ response = r_E01;
+
+ if (index != NULL) {
+ DB_UINT pid = 0;
+ DB_UINT tid = 0;
+ bool extended;
+ extended = thread_id_decode((const char*) &buffer[2], &pid, &tid);
+ if (extended && !check_pid(pid)) {
+ response = r_E01;
+ }
+ else {
+ if (tid == 0 || tid == (DB_UINT) -1)
+ *index = (int) tid;
+ else {
+ int r;
+ r = rtems_debugger_thread_find_index(tid);
+ if (r < 0) {
+ response = r_E01;
+ *index = -1;
+ }
+ else
+ *index = r;
+ }
+ }
+ }
+
+ remote_packet_out_str(response);
+ remote_packet_out_send();
+ return 0;
+}
+
+static int
+remote_thread_alive(uint8_t* buffer, int size)
+{
+ const char const* response = r_E01;
+ DB_UINT pid = 0;
+ DB_UINT tid = 0;
+ bool extended;
+ extended = thread_id_decode((const char*) &buffer[1], &pid, &tid);
+ if (!extended || (extended && check_pid(pid))) {
+ int r;
+ r = rtems_debugger_thread_find_index(tid);
+ if (r >= 0)
+ response = r_OK;
+ }
+ remote_packet_out_str(response);
+ remote_packet_out_send();
+ return 0;
+}
+
+static int
+remote_argc_argv(uint8_t* buffer, int size)
+{
+ return -1;
+}
+
+static int
+remote_continue_at(uint8_t* buffer, int size)
+{
+ if (!rtems_debugger_server_flag(RTEMS_DEBUGGER_FLAG_VCONT)) {
+ char* vCont_c = "vCont;c:p1.-1";
+ return remote_v_continue((uint8_t*) vCont_c, strlen(vCont_c));
+ }
+ return -1;
+}
+
+static int
+remote_read_general_regs(uint8_t* buffer, int size)
+{
+ rtems_debugger_threads* threads = rtems_debugger->threads;
+ bool ok = false;
+ int r;
+ if (threads->selector_gen >= 0 &&
+ threads->selector_gen < (int) threads->current.level) {
+ rtems_debugger_thread* current;
+ rtems_debugger_thread* thread;
+ current = rtems_debugger_thread_current(threads);
+ thread = &current[threads->selector_gen];
+ r = rtems_debugger_target_read_regs(thread);
+ if (r >= 0) {
+ remote_packet_out_reset();
+ r = remote_packet_out_append_hex((const uint8_t*) &thread->registers[0],
+ rtems_debugger_target_reg_size());
+ if (r >= 0)
+ ok = true;
+ }
+ }
+ if (!ok)
+ remote_packet_out_str(r_E01);
+ remote_packet_out_send();
+ return 0;
+}
+
+static int
+remote_write_general_regs(uint8_t* buffer, int size)
+{
+ rtems_debugger_threads* threads = rtems_debugger->threads;
+ size_t reg_size = rtems_debugger_target_reg_size();
+ bool ok = false;
+ int r;
+ if (threads->selector_gen >= 0 &&
+ threads->selector_gen < (int) threads->current.level &&
+ ((size - 1) / 2) == (int) reg_size) {
+ rtems_debugger_thread* current;
+ rtems_debugger_thread* thread;
+ current = rtems_debugger_thread_current(threads);
+ thread = &current[threads->selector_gen];
+ r = rtems_debugger_target_read_regs(thread);
+ if (r >= 0) {
+ r = rtems_debugger_remote_packet_in_hex((uint8_t*) &thread->registers[0],
+ (const char*) &buffer[1],
+ reg_size);
+ if (r >= 0) {
+ thread->flags |= RTEMS_DEBUGGER_THREAD_FLAG_REG_DIRTY;
+ ok = true;
+ }
+ }
+ }
+ if (!ok)
+ remote_packet_out_str(r_E01);
+ remote_packet_out_send();
+ return 0;
+}
+
+static int
+remote_read_reg(uint8_t* buffer, int size)
+{
+ rtems_debugger_threads* threads = rtems_debugger->threads;
+ bool ok = false;
+ int r;
+ if (threads->selector_gen >= 0
+ && threads->selector_gen < (int) threads->current.level) {
+ size_t reg = hex_decode_int(&buffer[1]);
+ if (reg < rtems_debugger_target_reg_num()) {
+ rtems_debugger_thread* current;
+ rtems_debugger_thread* thread;
+ current = rtems_debugger_thread_current(threads);
+ thread = &current[threads->selector_gen];
+ r = rtems_debugger_target_read_regs(thread);
+ if (r >= 0) {
+ const uint8_t* addr = (const uint8_t*) &thread->registers[reg];
+ remote_packet_out_reset();
+ r = remote_packet_out_append_hex(addr, sizeof(thread->registers[0]));
+ if (r >= 0)
+ ok = true;
+ }
+ }
+ }
+ if (!ok)
+ remote_packet_out_str(r_E01);
+ remote_packet_out_send();
+ return 0;
+}
+
+static int
+remote_write_reg(uint8_t* buffer, int size)
+{
+ rtems_debugger_threads* threads = rtems_debugger->threads;
+ const char const* response = r_E01;
+ if (threads->selector_gen >= 0
+ && threads->selector_gen < (int) threads->current.level) {
+ const char* equals;
+ equals = strchr((const char*) buffer, '=');
+ if (equals != NULL) {
+ size_t reg = hex_decode_int(&buffer[1]);
+ if (reg < rtems_debugger_target_reg_num()) {
+ rtems_debugger_thread* current;
+ rtems_debugger_thread* thread;
+ int r;
+ current = rtems_debugger_thread_current(threads);
+ thread = &current[threads->selector_gen];
+ r = rtems_debugger_target_read_regs(thread);
+ if (r >= 0) {
+ uint8_t* addr = (uint8_t*) &thread->registers[reg];
+ r = rtems_debugger_remote_packet_in_hex(addr,
+ equals + 1,
+ sizeof(thread->registers[reg]));
+ if (r == 0) {
+ thread->flags |= RTEMS_DEBUGGER_THREAD_FLAG_REG_DIRTY;
+ response = r_OK;
+ }
+ }
+ }
+ }
+ }
+ remote_packet_out_str(response);
+ remote_packet_out_send();
+ return 0;
+}
+
+static int
+remote_read_memory(uint8_t* buffer, int size)
+{
+ const char* comma;
+ comma = strchr((const char*) buffer, ',');
+ if (comma == NULL)
+ remote_packet_out_str(r_E01);
+ else {
+ DB_UINT addr;
+ DB_UINT length;
+ int r;
+ addr = hex_decode_uint(&buffer[1]);
+ length = hex_decode_uint((const uint8_t*) comma + 1);
+ remote_packet_out_reset();
+ r = rtems_debugger_target_start_memory_access();
+ if (r == 0) {
+ /*
+ * There should be specific target access for 8, 16, 32 and 64 bit reads.
+ */
+ r = remote_packet_out_append_hex((const uint8_t*) addr, length);
+ }
+ rtems_debugger_target_end_memory_access();
+ if (r < 0)
+ remote_packet_out_str(r_E01);
+ }
+ remote_packet_out_send();
+ return 0;
+}
+
+static int
+remote_write_memory(uint8_t* buffer, int size)
+{
+ const char const* response = r_E01;
+ const char* comma;
+ const char* colon;
+ comma = strchr((const char*) buffer, ',');
+ colon = strchr((const char*) buffer, ':');
+ if (comma != NULL && colon != NULL) {
+ DB_UINT addr;
+ DB_UINT length;
+ int r;
+ addr = hex_decode_uint(&buffer[1]);
+ length = hex_decode_uint((const uint8_t*) comma + 1);
+ r = rtems_debugger_target_start_memory_access();
+ if (r == 0) {
+ r = rtems_debugger_remote_packet_in_hex((uint8_t*) addr,
+ colon + 1,
+ length);
+ }
+ rtems_debugger_target_end_memory_access();
+ if (r == 0)
+ response = r_OK;
+ }
+ remote_packet_out_str(response);
+ remote_packet_out_send();
+ return 0;
+}
+
+static int
+remote_single_step(uint8_t* buffer, int size)
+{
+ if (!rtems_debugger_server_flag(RTEMS_DEBUGGER_FLAG_VCONT)) {
+ rtems_debugger_threads* threads = rtems_debugger->threads;
+ if (threads != NULL && rtems_debugger_thread_current(threads) != NULL) {
+ rtems_debugger_thread* current;
+ char vCont_s[32];
+ current = rtems_debugger_thread_current(threads);
+ snprintf(vCont_s, sizeof(vCont_s), "vCont;s:p1.%08lx;c:p1.-1",
+ current[threads->selector_cont].id);
+ return remote_v_continue((uint8_t*) vCont_s, strlen(vCont_s));
+ }
+ remote_packet_out_str(r_E01);
+ remote_packet_out_send();
+ return 0;
+ }
+ return -1;
+}
+
+static int
+remote_breakpoints(bool insert, uint8_t* buffer, int size)
+{
+ const char* comma1;
+ int r = -1;
+ comma1 = strchr((const char*) buffer, ',');
+ if (comma1 != NULL) {
+ const char* comma2;
+ comma2 = strchr(comma1 + 1, ',');
+ if (comma2 != NULL) {
+ uint32_t capabilities;
+ DB_UINT addr;
+ DB_UINT kind;
+ addr = hex_decode_uint((const uint8_t*) comma1 + 1);
+ kind = hex_decode_uint((const uint8_t*)comma2 + 1);
+ capabilities = rtems_debugger_target_capabilities();
+ switch (buffer[1]) {
+ case '0':
+ if ((capabilities & RTEMS_DEBUGGER_TARGET_CAP_SWBREAK) != 0) {
+ r = rtems_debugger_target_swbreak_control(insert, addr, kind);
+ }
+ break;
+ case '1': /* execute */
+ case '2': /* write */
+ case '3': /* read */
+ case '4': /* access */
+ if ((capabilities & RTEMS_DEBUGGER_TARGET_CAP_HWWATCH) != 0) {
+ rtems_debugger_target_watchpoint type;
+ switch (buffer[1]) {
+ case '1':
+ type = rtems_debugger_target_hw_execute;
+ break;
+ case '2':
+ type = rtems_debugger_target_hw_write;
+ break;
+ case '3':
+ type = rtems_debugger_target_hw_read;
+ break;
+ case '4':
+ default:
+ type = rtems_debugger_target_hw_read_write;
+ break;
+ }
+ r = rtems_debugger_target_hwbreak_control(type, insert, addr, kind);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ remote_packet_out_str(r < 0 ? r_E01 : r_OK);
+ remote_packet_out_send();
+ return 0;
+}
+
+static int
+remote_insert_breakpoint(uint8_t* buffer, int size)
+{
+ return remote_breakpoints(true, buffer, size);
+}
+
+static int
+remote_remove_breakpoint(uint8_t* buffer, int size)
+{
+ return remote_breakpoints(false, buffer, size);
+}
+
+static int
+remote_break(uint8_t* buffer, int size)
+{
+ int r;
+ r = rtems_debugger_thread_system_suspend();
+ if (r < 0) {
+ rtems_debugger_printf("error: rtems-db: suspend all on break\n");
+ }
+ return remote_stop_reason(buffer, size);
+}
+
+static const rtems_debugger_packet packets[] = {
+ { .label = "q",
+ .command = remote_general_query },
+ { .label = "Q",
+ .command = remote_general_set },
+ { .label = "v",
+ .command = remote_v_packets },
+ { .label = "H",
+ .command = remote_thread_select },
+ { .label = "T",
+ .command = remote_thread_alive },
+ { .label = "?",
+ .command = remote_stop_reason },
+ { .label = "A",
+ .command = remote_argc_argv },
+ { .label = "c",
+ .command = remote_continue_at },
+ { .label = "g",
+ .command = remote_read_general_regs },
+ { .label = "G",
+ .command = remote_write_general_regs },
+ { .label = "p",
+ .command = remote_read_reg },
+ { .label = "P",
+ .command = remote_write_reg },
+ { .label = "m",
+ .command = remote_read_memory },
+ { .label = "M",
+ .command = remote_write_memory },
+ { .label = "s",
+ .command = remote_single_step },
+ { .label = "Z",
+ .command = remote_insert_breakpoint },
+ { .label = "z",
+ .command = remote_remove_breakpoint },
+ { .label = "D",
+ .command = remote_detach },
+ { .label = "k",
+ .command = remote_v_kill },
+ { .label = "r",
+ .command = remote_v_kill },
+ { .label = "R",
+ .command = remote_v_kill },
+ { .label = "^C",
+ .command = remote_break },
+};
+
+#define REMOTE_PACKETS RTEMS_DEBUGGER_NUMOF(packets)
+
+static int
+remote_packets(uint8_t* buffer, size_t size)
+{
+ return remote_packet_dispatch(packets, REMOTE_PACKETS,
+ buffer, size);
+}
+
+static void
+rtems_debugger_events(rtems_task_argument arg)
+{
+ int r;
+
+ if (rtems_debugger_verbose())
+ rtems_debugger_printf("rtems-db: events running\n");
+
+ /*
+ * Hold the lock until the thread blocks waiting for an event.
+ */
+ rtems_debugger_lock();
+
+ rtems_debugger_target_enable();
+
+ while (rtems_debugger_server_events_running()) {
+ r = rtems_debugger_server_events_wait();
+ if (r < 0)
+ break;
+ if (!rtems_debugger_server_events_running())
+ break;
+ r = rtems_debugger_thread_system_suspend();
+ if (r < 0)
+ break;
+ r = remote_stop_reason(NULL, 0);
+ if (r < 0)
+ break;
+ }
+
+ if (r < 0)
+ rtems_debugger_printf("rtems-db: error in events\n");
+
+ rtems_debugger_target_disable();
+
+ rtems_debugger->events_running = false;
+ rtems_debugger->events_finished = true;
+
+ rtems_debugger_unlock();
+
+ if (rtems_debugger_verbose())
+ rtems_debugger_printf("rtems-db: events finishing\n");
+
+ rtems_task_delete(RTEMS_SELF);
+}
+
+static int
+rtems_debugger_session(void)
+{
+ int r;
+ int rr;
+
+ if (rtems_debugger_verbose())
+ rtems_debugger_printf("rtems-db: remote running\n");
+
+ /*
+ * Hold the lock until the thread blocks on the remote input.
+ */
+ rtems_debugger_lock();
+
+ r = rtems_debugger_target_create();
+ if (r < 0) {
+ rtems_debugger_thread_destroy();
+ rtems_debugger_unlock();
+ return r;
+ }
+
+ r = rtems_debugger_thread_create();
+ if (r < 0) {
+ rtems_debugger_unlock();
+ return r;
+ }
+
+ rtems_debugger->events_running = true;
+ rtems_debugger->events_finished = false;
+
+ r = rtems_debugger_task_create("DBSe",
+ rtems_debugger->priority,
+ RTEMS_DEBUGGER_STACKSIZE,
+ rtems_debugger_events,
+ 0,
+ &rtems_debugger->events_task);
+ if (r < 0) {
+ rtems_debugger_target_destroy();
+ rtems_debugger_thread_destroy();
+ rtems_debugger_unlock();
+ return r;
+ }
+
+ while (rtems_debugger_server_running() &&
+ rtems_debugger_connected()) {
+ r = rtems_debugger_remote_packet_in();
+ if (r < 0)
+ break;
+ if (r > 0) {
+ remote_packets(&rtems_debugger->input[0], r);
+ }
+ }
+
+ rtems_debugger->events_running = false;
+ rtems_debugger_server_events_wake();
+
+ rtems_debugger_unlock();
+
+ rr = rtems_debugger_task_destroy("DBSe",
+ rtems_debugger->events_task,
+ &rtems_debugger->events_finished,
+ RTEMS_DEBUGGER_TIMEOUT_STOP);
+ if (rr < 0 && r == 0)
+ r = rr;
+
+ rtems_debugger_lock();
+
+ rr = rtems_debugger_target_destroy();
+ if (rr < 0 && r == 0)
+ r = rr;
+
+ rr = rtems_debugger_thread_destroy();
+ if (rr < 0 && r == 0)
+ r = rr;
+
+ if (rtems_debugger_server_flag(RTEMS_DEBUGGER_FLAG_RESET)) {
+ rtems_debugger_printf("rtems-db: shutdown\n");
+ rtems_fatal_error_occurred(1122);
+ }
+
+ rtems_debugger->flags = 0;
+ rtems_debugger->ack_pending = false;
+
+ rtems_debugger_unlock();
+
+ if (rtems_debugger_verbose())
+ rtems_debugger_printf("rtems-db: remote finishing\n");
+
+ return r;
+}
+
+static int
+rtems_debugger_create(const char* remote,
+ const char* device,
+ rtems_task_priority priority,
+ int timeout,
+ const rtems_printer* printer)
+{
+ int r;
+
+ if (rtems_debugger != NULL) {
+ rtems_printf(printer, "error: rtems-db: create: already active\n");
+ errno = EEXIST;
+ return -1;
+ }
+
+ rtems_debugger = malloc(sizeof(rtems_debugger_server));
+ if (rtems_debugger == NULL) {
+ rtems_printf(printer, "error: rtems-db: create: no memory\n");
+ errno = ENOMEM;
+ return -1;
+ }
+
+ memset(rtems_debugger, 0, sizeof(rtems_debugger_server));
+
+ /*
+ * These do not change with a session.
+ */
+ rtems_debugger->priority = priority;
+ rtems_debugger->timeout = timeout;
+ rtems_debugger->printer = *printer;
+ rtems_debugger->pid = getpid();
+ rtems_debugger->remote_debug = false;
+
+ rtems_debugger->remote = rtems_debugger_remote_find(remote);
+ if (rtems_debugger->remote== NULL) {
+ rtems_printf(printer, "error: rtems-db: remote not found: %s\n", remote);
+ free(rtems_debugger);
+ rtems_debugger = NULL;
+ return -1;
+ }
+
+ r = rtems_debugger->remote->begin(rtems_debugger->remote, device);
+ if (r < 0) {
+ rtems_printf(printer, "error: rtems-db: remote begin: %s: %s\n",
+ rtems_debugger->remote->name, strerror(errno));
+ free(rtems_debugger);
+ rtems_debugger = NULL;
+ }
+
+ /*
+ * Reset at the end of the session.
+ */
+ rtems_debugger->flags = 0;
+ rtems_debugger->ack_pending = false;
+
+ r = rtems_debugger_lock_create();
+ if (r < 0) {
+ free(rtems_debugger);
+ rtems_debugger = NULL;
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+rtems_debugger_destroy(void)
+{
+ int r;
+ int rr;
+
+ rtems_debugger_lock();
+ rtems_debugger->server_running = false;
+ rtems_debugger_unlock();
+
+ r = rtems_debugger_remote_disconnect();
+
+ rr = rtems_debugger->remote->end(rtems_debugger->remote);
+ if (rr < 0 && r == 0)
+ r = rr;
+
+ rr = rtems_debugger_task_destroy("DBSr",
+ rtems_debugger->server_task,
+ &rtems_debugger->server_finished,
+ RTEMS_DEBUGGER_TIMEOUT_STOP);
+ if (rr < 0 && r == 0)
+ r = rr;
+
+ rr = rtems_debugger_lock_destroy();
+ if (rr < 0 && r == 0)
+ r = rr;
+
+ free(rtems_debugger);
+ rtems_debugger = NULL;
+
+ return r;
+}
+
+static void
+rtems_debugger_main(rtems_task_argument arg)
+{
+ int r;
+
+ rtems_debugger_printf("rtems-db: remote running\n");
+
+ while (rtems_debugger_server_running()) {
+ r = rtems_debugger_remote_connect();
+ if (r < 0)
+ break;
+ rtems_debugger_session();
+ rtems_debugger_remote_disconnect();
+ }
+
+ rtems_debugger_printf("rtems-db: remote finishing\n");
+
+ rtems_debugger_lock();
+ rtems_debugger->server_running = false;
+ rtems_debugger->server_finished = true;
+ rtems_debugger_unlock();
+
+ rtems_task_delete(RTEMS_SELF);
+}
+
+int
+rtems_debugger_start(const char* remote,
+ const char* device,
+ int timeout,
+ rtems_task_priority priority,
+ const rtems_printer* printer)
+{
+ int r;
+
+ r = rtems_debugger_create(remote, device, priority, timeout, printer);
+ if (r < 0)
+ return -1;
+
+ rtems_debugger_lock();
+ rtems_debugger->server_running = true;
+ rtems_debugger->server_finished = false;
+ rtems_debugger_unlock();
+
+ r = rtems_debugger_task_create("DBSs",
+ priority,
+ RTEMS_DEBUGGER_STACKSIZE,
+ rtems_debugger_main,
+ 0,
+ &rtems_debugger->server_task);
+ if (r < 0) {
+ rtems_debugger_destroy();
+ return -1;
+ }
+
+ return 0;
+}
+
+int
+rtems_debugger_stop(void)
+{
+ return rtems_debugger_destroy();
+}
+
+bool
+rtems_debugger_running(void)
+{
+ return rtems_debugger != NULL;
+}
+
+void
+rtems_debugger_set_verbose(bool on)
+{
+ if (rtems_debugger_running()) {
+ if (on)
+ rtems_debugger->flags |= RTEMS_DEBUGGER_FLAG_VERBOSE;
+ else
+ rtems_debugger->flags &= ~RTEMS_DEBUGGER_FLAG_VERBOSE;
+ }
+}
+
+int
+rtems_debugger_remote_debug(bool state)
+{
+ rtems_debugger_lock();
+ rtems_debugger->remote_debug = state;
+ rtems_debugger_printf("rtems-db: remote-debug is %s\n",
+ rtems_debugger->remote_debug ? "on" : "off");
+ rtems_debugger_unlock();
+ return 0;
+}
diff --git a/cpukit/libdebugger/rtems-debugger-server.h b/cpukit/libdebugger/rtems-debugger-server.h
new file mode 100644
index 0000000000..0941933ba9
--- /dev/null
+++ b/cpukit/libdebugger/rtems-debugger-server.h
@@ -0,0 +1,204 @@
+/*
+ * Copyright (c) 2016 Chris Johns <chrisj@rtems.org>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Debugger for RTEMS.
+ */
+
+#ifndef _RTEMS_DEBUGGER_SERVER_h
+#define _RTEMS_DEBUGGER_SERVER_h
+
+#include <rtems.h>
+#include <rtems/printer.h>
+
+#include <rtems/rtems-debugger.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/**
+ * Debugger NUMOF macro.
+ */
+#define RTEMS_DEBUGGER_NUMOF(_d) (sizeof(_d) / sizeof(_d[0]))
+
+/**
+ * Machine specific size. Here for 64bit support.
+ */
+#define DB_UINT uint32_t
+
+/*
+ * Debugger signals.
+ */
+#define RTEMS_DEBUGGER_SIGNAL_HUP 1 /* Hangup */
+#define RTEMS_DEBUGGER_SIGNAL_INT 2 /* Interrupt */
+#define RTEMS_DEBUGGER_SIGNAL_QUIT 3 /* Quit */
+#define RTEMS_DEBUGGER_SIGNAL_ILL 4 /* Illegal instruction */
+#define RTEMS_DEBUGGER_SIGNAL_TRAP 5 /* Trace/breakpoint trap */
+#define RTEMS_DEBUGGER_SIGNAL_ABRT 6 /* Aborted */
+#define RTEMS_DEBUGGER_SIGNAL_EMT 7 /* Emulation trap */
+#define RTEMS_DEBUGGER_SIGNAL_FPE 8 /* Arithmetic exception */
+#define RTEMS_DEBUGGER_SIGNAL_KILL 9 /* Killed */
+#define RTEMS_DEBUGGER_SIGNAL_BUS 10 /* Bus error */
+#define RTEMS_DEBUGGER_SIGNAL_SEGV 11 /* Segmentation fault */
+#define RTEMS_DEBUGGER_SIGNAL_SYS 12 /* Bad system call */
+#define RTEMS_DEBUGGER_SIGNAL_PIPE 13 /* Broken pipe */
+#define RTEMS_DEBUGGER_SIGNAL_ALRM 14 /* Alarm clock */
+#define RTEMS_DEBUGGER_SIGNAL_TERM 15 /* Terminated */
+#define RTEMS_DEBUGGER_SIGNAL_URG 16 /* Urgent I/O condition */
+#define RTEMS_DEBUGGER_SIGNAL_STOP 17 /* Stopped (signal) */
+#define RTEMS_DEBUGGER_SIGNAL_TSTP 18 /* Stopped (user) */
+#define RTEMS_DEBUGGER_SIGNAL_CONT 19 /* Continued */
+
+/**
+ * Timeout period for the Debugger task to stop in usecs.
+ */
+#define RTEMS_DEBUGGER_TIMEOUT_STOP (5 * 1000 * 1000)
+
+/**
+ * Debugger poll wait timeout in usecs.
+ */
+#define RTEMS_DEBUGGER_POLL_WAIT (10000)
+
+/**
+ * Debugger task stack size.
+ */
+#define RTEMS_DEBUGGER_STACKSIZE (16 * 1024)
+
+/**
+ * Debugger output buffer size.
+ */
+#define RTEMS_DEBUGGER_BUFFER_SIZE (4 * 1024)
+
+/**
+ * Debugger flags.
+ */
+#define RTEMS_DEBUGGER_FLAG_VERBOSE (1 << 0)
+#define RTEMS_DEBUGGER_FLAG_RESET (1 << 1)
+#define RTEMS_DEBUGGER_FLAG_NON_STOP (1 << 2)
+#define RTEMS_DEBUGGER_FLAG_VCONT (1 << 3)
+#define RTEMS_DEBUGGER_FLAG_MULTIPROCESS (1 << 4)
+
+/**
+ * Forward decl for the threads and targets.
+ */
+typedef struct rtems_debugger_remote rtems_debugger_remote;
+typedef struct rtems_debugger_threads rtems_debugger_threads;
+typedef struct rtems_debugger_target rtems_debugger_target;
+
+/**
+ * Debugger data.
+ */
+typedef struct
+{
+ int port;
+ int timeout;
+ rtems_task_priority priority;
+ rtems_id lock;
+ rtems_id lock_output;
+ rtems_debugger_remote* remote;
+ rtems_id server_task;
+ volatile bool server_running;
+ volatile bool server_finished;
+ rtems_id events_task;
+ volatile bool events_running;
+ volatile bool events_finished;
+ rtems_printer printer;
+ uint32_t flags;
+ pid_t pid;
+ bool remote_debug;
+ bool ack_pending;
+ size_t output_level;
+ uint8_t input[RTEMS_DEBUGGER_BUFFER_SIZE];
+ uint8_t output[RTEMS_DEBUGGER_BUFFER_SIZE];
+ rtems_debugger_threads* threads;
+ int signal;
+ rtems_debugger_target* target;
+} rtems_debugger_server;
+
+/**
+ * Debugger global variable.
+ */
+extern rtems_debugger_server* rtems_debugger;
+
+/**
+ * Debug server printer.
+ */
+extern int rtems_debugger_printf(const char* format, ...) RTEMS_PRINTFLIKE(1, 2);
+
+/**
+ * Lock the Debugger.
+ */
+extern int rtems_debugger_lock(void);
+
+/**
+ * Unlock the Debugger.
+ */
+extern int rtems_debugger_unlock(void);
+
+/**
+ * Is the server still running?
+ */
+bool rtems_debugger_server_running(void);
+
+/**
+ * Get the remote handle from the debugger.
+ */
+rtems_debugger_remote* rtems_debugger_remote_handle(void);
+
+/**
+ * Is the debugger connected?
+ */
+bool rtems_debugger_connected(void);
+
+/**
+ * Is the debugger events thread runnins?
+ */
+bool rtems_debugger_server_events_running(void);
+
+/**
+ * Wake events thread in the debug server.
+ */
+extern int rtems_debugger_server_events_wake(void);
+
+/**
+ * Check if verbose is on.
+ */
+extern bool rtems_debugger_verbose(void);
+
+/**
+ * Check a flag.
+ */
+static inline bool rtems_debugger_server_flag(uint32_t mask)
+{
+ return (rtems_debugger->flags & mask) != 0;
+}
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+
+#endif
diff --git a/cpukit/libdebugger/rtems-debugger-target.c b/cpukit/libdebugger/rtems-debugger-target.c
new file mode 100644
index 0000000000..cd1565463e
--- /dev/null
+++ b/cpukit/libdebugger/rtems-debugger-target.c
@@ -0,0 +1,400 @@
+/*
+ * Copyright (c) 2016 Chris Johns <chrisj@rtems.org>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#define TARGET_DEBUG 0
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <errno.h>
+#include <inttypes.h>
+#include <stdlib.h>
+
+#include <rtems.h>
+#include <rtems/score/threadimpl.h>
+
+#include "rtems-debugger-target.h"
+#include "rtems-debugger-threads.h"
+
+/**
+ * Frame signature.
+ */
+#define TARGET_FRAME_MAGIC_NUM (2)
+#define TARGET_FRAME_MAGIC 0xdeadbeef, 0xb2107016
+static const uint32_t
+ frame_magic[TARGET_FRAME_MAGIC_NUM] = { TARGET_FRAME_MAGIC };
+
+#if TARGET_DEBUG
+#include <rtems/bspIo.h>
+static void target_printk(const char* format, ...) RTEMS_PRINTFLIKE(1, 2);
+static void
+target_printk(const char* format, ...)
+{
+ va_list ap;
+ va_start(ap, format);
+ vprintk(format, ap);
+ va_end(ap);
+}
+#else
+#define target_printk(_fmt, ...)
+#endif
+
+int
+rtems_debugger_target_create(void)
+{
+ if (rtems_debugger->target == NULL) {
+ rtems_debugger_target* target;
+ int r;
+
+ target = calloc(1, sizeof(rtems_debugger_target));
+ if (target == NULL) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ r = rtems_debugger_target_configure(target);
+ if (r < 0) {
+ free(target);
+ return -1;
+ }
+
+ if (target->breakpoint_size > RTEMS_DEBUGGER_TARGET_SWBREAK_MAX_SIZE) {
+ free(target);
+ rtems_debugger_printf("error: rtems-db: target: breakpoint size too big\n");
+ return -1;
+ }
+
+ r = rtems_debugger_block_create(&target->swbreaks,
+ RTEMS_DEBUGGER_TARGET_SWBREAK_NUM,
+ sizeof(rtems_debugger_target_swbreak));
+ if (r < 0) {
+ free(target);
+ return -1;
+ }
+
+ rtems_debugger->target = target;
+ }
+
+ return 0;
+}
+
+int
+rtems_debugger_target_destroy(void)
+{
+ if (rtems_debugger->target != NULL) {
+ rtems_debugger_target* target = rtems_debugger->target;
+ rtems_debugger_target_swbreak_remove();
+ rtems_debugger_target_disable();
+ rtems_debugger_block_destroy(&target->swbreaks);
+ free(target);
+ rtems_debugger->target = NULL;
+ }
+ return 0;
+}
+
+uint32_t
+rtems_debugger_target_capabilities(void)
+{
+ if (rtems_debugger->target != NULL)
+
+ return rtems_debugger->target->capabilities;
+ return 0;
+}
+
+size_t
+rtems_debugger_target_reg_num(void)
+{
+ if (rtems_debugger->target != NULL)
+ return rtems_debugger->target->reg_num;
+ return 0;
+}
+
+size_t
+rtems_debugger_target_reg_size(void)
+{
+ if (rtems_debugger->target != NULL)
+ return rtems_debugger->target->reg_num * rtems_debugger->target->reg_size;
+ return 0;
+}
+
+int
+rtems_debugger_target_swbreak_control(bool insert, DB_UINT addr, DB_UINT kind)
+{
+ rtems_debugger_target* target = rtems_debugger->target;
+ rtems_debugger_target_swbreak* swbreaks = target->swbreaks.block;
+ size_t swbreak_size;
+ uint8_t* loc = (void*) addr;
+ size_t i;
+ int r;
+
+ if (target == NULL || swbreaks == NULL || kind != target->breakpoint_size) {
+ errno = EIO;
+ return -1;
+ }
+
+ swbreak_size =
+ sizeof(rtems_debugger_target_swbreak) + target->breakpoint_size;
+
+ for (i = 0; i < target->swbreaks.level; ++i) {
+ if (loc == swbreaks[i].address) {
+ size_t remaining;
+ if (!insert) {
+ --target->swbreaks.level;
+ remaining = (target->swbreaks.level - i) * swbreak_size;
+ memmove(&swbreaks[i], &swbreaks[i + 1], remaining);
+ }
+ return 0;
+ }
+ }
+
+ if (!insert)
+ return 0;
+
+ r = rtems_debugger_block_resize(&target->swbreaks);
+ if (r < 0)
+ return -1;
+
+ swbreaks = target->swbreaks.block;
+
+ swbreaks[target->swbreaks.level].address = loc;
+ if (target->breakpoint_size > 4)
+ memcpy(&swbreaks[target->swbreaks.level].contents[0],
+ loc,
+ target->breakpoint_size);
+ else {
+ uint8_t* contents = &swbreaks[target->swbreaks.level].contents[0];
+ switch (target->breakpoint_size) {
+ case 4:
+ contents[3] = loc[3];
+ case 3:
+ contents[2] = loc[2];
+ case 2:
+ contents[1] = loc[1];
+ case 1:
+ contents[0] = loc[0];
+ break;
+ }
+ }
+ ++target->swbreaks.level;
+
+ return 0;
+}
+
+int
+rtems_debugger_target_swbreak_insert(void)
+{
+ rtems_debugger_target* target = rtems_debugger->target;
+ int r = -1;
+ if (target != NULL && target->swbreaks.block != NULL) {
+ rtems_debugger_target_swbreak* swbreaks = target->swbreaks.block;
+ size_t i;
+ r = 0;
+ for (i = 0; i < target->swbreaks.level; ++i) {
+ uint8_t* loc = swbreaks[i].address;
+ if (rtems_debugger_verbose())
+ rtems_debugger_printf("rtems-db: bp: in: %p\n", swbreaks[i].address);
+ if (target->breakpoint_size > 4)
+ memcpy(loc, &target->breakpoint[0], target->breakpoint_size);
+ else {
+ switch (target->breakpoint_size) {
+ case 4:
+ loc[3] = target->breakpoint[3];
+ case 3:
+ loc[2] = target->breakpoint[2];
+ case 2:
+ loc[1] = target->breakpoint[1];
+ case 1:
+ loc[0] = target->breakpoint[0];
+ break;
+ }
+ }
+ r = rtems_debugger_target_cache_sync(&swbreaks[i]);
+ }
+ }
+ return r;
+}
+
+int
+rtems_debugger_target_swbreak_remove(void)
+{
+ rtems_debugger_target* target = rtems_debugger->target;
+ int r = -1;
+ if (target != NULL && target->swbreaks.block != NULL) {
+ rtems_debugger_target* target = rtems_debugger->target;
+ rtems_debugger_target_swbreak* swbreaks = target->swbreaks.block;
+ size_t i;
+ r = 0;
+ for (i = 0; i < target->swbreaks.level; ++i) {
+ uint8_t* loc = swbreaks[i].address;
+ uint8_t* contents = &swbreaks[i].contents[0];
+ if (rtems_debugger_verbose())
+ rtems_debugger_printf("rtems-db: bp: out: %p\n", swbreaks[i].address);
+ if (target->breakpoint_size > 4)
+ memcpy(loc, contents, target->breakpoint_size);
+ else {
+ switch (target->breakpoint_size) {
+ case 4:
+ loc[3] = contents[3];
+ case 3:
+ loc[2] = contents[2];
+ case 2:
+ loc[1] = contents[1];
+ case 1:
+ loc[0] = contents[0];
+ break;
+ }
+ }
+ r = rtems_debugger_target_cache_sync(&swbreaks[i]);
+ }
+ }
+ return r;
+}
+
+rtems_debugger_target_exc_action
+rtems_debugger_target_exception(CPU_Exception_frame* frame)
+{
+ volatile const uint32_t magic[3] = {
+ (uint32_t) frame, TARGET_FRAME_MAGIC
+ };
+
+ (void) magic;
+
+ if (!rtems_interrupt_is_in_progress()) {
+ rtems_debugger_threads* threads = rtems_debugger->threads;
+ Thread_Control* thread = _Thread_Executing;
+ rtems_id* excludes;
+ const rtems_id tid = thread->Object.id;
+ DB_UINT pc;
+ const rtems_debugger_thread_stepper* stepper;
+ size_t i;
+
+ target_printk("[} tid:%08" PRIx32 ": thread:%08" PRIxPTR
+ " frame:%08" PRIxPTR "\n",
+ tid, (intptr_t) thread, (intptr_t) frame);
+
+ /*
+ * If the thread is the debugger recover.
+ */
+ if (tid == rtems_debugger->server_task) {
+ if (rtems_debugger->target->memory_access) {
+ target_printk("[} server access fault\n");
+ rtems_debugger->target->memory_access = true;
+ longjmp(rtems_debugger->target->access_return, -1);
+ }
+ target_printk("[} server exception\n");
+ return rtems_debugger_target_exc_cascade;
+ }
+
+ /*
+ * See if the thread is excluded.
+ */
+ excludes = rtems_debugger_thread_excludes(threads);
+ for (i = 0; i < threads->excludes.level; ++i) {
+ if (tid == excludes[i]) {
+ /*
+ * We do nothing with this condition and cascade the exception.
+ *
+ * @todo: if this is a hwbreak carry on, if this is a swbreak replace
+ * the contents of the instruction, step then return the
+ * swbreak's contents.
+ */
+ target_printk("[} tid:%08lx: excluded\n", tid);
+ return rtems_debugger_target_exc_cascade;
+ }
+ }
+
+ /*
+ * See if the thread is inside the stepping a range.
+ */
+ pc = rtems_debugger_target_frame_pc(frame);
+ stepper = rtems_debugger_thread_is_stepping(tid, pc);
+ if (stepper != NULL) {
+ stepper->thread->frame = frame;
+ rtems_debugger_target_thread_stepping(stepper->thread);
+ target_printk("[} tid:%08lx: stepping\n", tid);
+ return rtems_debugger_target_exc_step;
+ }
+
+ target_printk("[} tid:%08lx: suspending\n", tid);
+
+ /*
+ * Tag the thread as being debugged, wake the debug server's event thread,
+ * then suspend this thread.
+ */
+ _Thread_Set_state(thread, STATES_DEBUGGER);
+ rtems_debugger_server_events_wake();
+ rtems_task_suspend(tid);
+
+ target_printk("[} tid:%08lx: resuming\n", tid);
+
+ return rtems_debugger_target_exc_consumed;
+ }
+
+ target_printk("[} cascade, in interrupt\n");
+
+ return rtems_debugger_target_exc_cascade;
+}
+
+int
+rtems_debugger_target_set_exception_frame(rtems_debugger_thread* thread)
+{
+ int r = 0;
+ thread->frame = NULL;
+ thread->flags &= ~RTEMS_DEBUGGER_THREAD_FLAG_DEBUGGING;
+ if ((thread->tcb->current_state & STATES_DEBUGGER) != 0) {
+ CPU_Exception_frame* frame = NULL;
+ DB_UINT* sp;
+ int i;
+ sp = (DB_UINT*) rtems_debugger_target_tcb_sp(thread);
+ for (i = 0; i < 128; ++i) {
+ if (sp[i] == frame_magic[0] && sp[i + 1] == frame_magic[1]) {
+ frame = (CPU_Exception_frame*) sp[i + 2];
+ break;
+ }
+ }
+ _Thread_Clear_state(thread->tcb, STATES_DEBUGGER);
+ thread->frame = frame;
+ if (frame != NULL)
+ thread->flags |= RTEMS_DEBUGGER_THREAD_FLAG_DEBUGGING;
+ else
+ r = -1;
+ }
+ return r;
+}
+
+int
+rtems_debugger_target_start_memory_access(void)
+{
+ rtems_debugger_target* target = rtems_debugger->target;
+ target->memory_access = true;
+ return setjmp(target->access_return);
+}
+
+void
+rtems_debugger_target_end_memory_access(void)
+{
+ rtems_debugger->target->memory_access = false;
+}
diff --git a/cpukit/libdebugger/rtems-debugger-target.h b/cpukit/libdebugger/rtems-debugger-target.h
new file mode 100644
index 0000000000..82230e8d8b
--- /dev/null
+++ b/cpukit/libdebugger/rtems-debugger-target.h
@@ -0,0 +1,237 @@
+/*
+ * Copyright (c) 2016 Chris Johns <chrisj@rtems.org>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Debugger for RTEMS.
+ */
+
+#ifndef _RTEMS_DEBUGGER_TARGET_h
+#define _RTEMS_DEBUGGER_TARGET_h
+
+#include <setjmp.h>
+
+#include <rtems/rtems-debugger.h>
+
+#include "rtems-debugger-threads.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/*
+ * Software breakpoint block size.
+ */
+#define RTEMS_DEBUGGER_TARGET_SWBREAK_NUM 64
+
+/**
+ * Target capabilities mask.
+ */
+#define RTEMS_DEBUGGER_TARGET_CAP_SWBREAK (1 << 0)
+#define RTEMS_DEBUGGER_TARGET_CAP_HWBREAK (1 << 1)
+#define RTEMS_DEBUGGER_TARGET_CAP_HWWATCH (1 << 2)
+
+/**
+ * Types of hardware breakpoints.
+ */
+typedef enum rtems_debugger_target_watchpoint
+{
+ rtems_debugger_target_hw_read,
+ rtems_debugger_target_hw_write,
+ rtems_debugger_target_hw_read_write,
+ rtems_debugger_target_hw_execute
+} rtems_debugger_target_watchpoint;
+
+/**
+ * Target exception actions.
+ */
+typedef enum rtems_debugger_target_exc_action
+{
+ rtems_debugger_target_exc_consumed, /*<< The exception has been consumed. */
+ rtems_debugger_target_exc_cascade, /*<< Cascade to a previous handler. */
+ rtems_debugger_target_exc_step, /*<< Step an instruction. */
+} rtems_debugger_target_exc_action;
+
+/**
+ * Memory breakpoint. We use thumb mode BKPT which is 2 bytes.
+ */
+#define RTEMS_DEBUGGER_TARGET_SWBREAK_MAX_SIZE (4)
+typedef struct rtems_debugger_target_swbreak {
+ void* address;
+ uint8_t contents[RTEMS_DEBUGGER_TARGET_SWBREAK_MAX_SIZE];
+} rtems_debugger_target_swbreak;
+
+/**
+ * The target data.
+ */
+typedef struct rtems_debugger_target {
+ int capabilities; /*<< The capabilities to report. */
+ size_t reg_num; /*<< The number of registers. */
+ size_t reg_size; /*<< The size of a register. */
+ const uint8_t* breakpoint; /*<< The breakpoint instruction(s). */
+ size_t breakpoint_size; /*<< The breakpoint size. */
+ rtems_debugger_block swbreaks; /*<< The software breakpoint block. */
+ bool memory_access; /*<< Accessing target memory. */
+ jmp_buf access_return; /*<< Return from an access fault. */
+} rtems_debugger_target;
+
+/**
+ * Create the target.
+ */
+extern int rtems_debugger_target_create(void);
+
+/**
+ * Destroy the target.
+ */
+extern int rtems_debugger_target_destroy(void);
+
+/**
+ * Configure the target. This is architecture specific.
+ */
+extern int rtems_debugger_target_configure(rtems_debugger_target* target);
+
+/**
+ * Enable the target.
+ */
+extern int rtems_debugger_target_enable(void);
+
+/**
+ * Disable the target.
+ */
+extern int rtems_debugger_target_disable(void);
+
+/**
+ * Return the capabilities mask for the target.
+ */
+extern uint32_t rtems_debugger_target_capabilities(void);
+
+/**
+ * Return the number of regisers.
+ */
+extern size_t rtems_debugger_target_reg_num(void);
+
+/**
+ * Return the size of the regisers in bytes.
+ */
+extern size_t rtems_debugger_target_reg_size(void);
+
+/**
+ * Read the regosters.
+ */
+extern int rtems_debugger_target_read_regs(rtems_debugger_thread* thread);
+
+/**
+ * Write the regosters.
+ */
+extern int rtems_debugger_target_write_regs(rtems_debugger_thread* thread);
+
+/**
+ * Return the thread's program counter (PC).
+ */
+extern DB_UINT rtems_debugger_target_reg_pc(rtems_debugger_thread* thread);
+
+/**
+ * Return the frame's program counter (PC).
+ */
+extern DB_UINT rtems_debugger_target_frame_pc(CPU_Exception_frame* frame);
+
+/**
+ * Return the thread's stack pointer (SP).
+ */
+extern DB_UINT rtems_debugger_target_reg_sp(rtems_debugger_thread* thread);
+
+/**
+ * Return the thread's TCB stack pointer (SP).
+ */
+extern DB_UINT rtems_debugger_target_tcb_sp(rtems_debugger_thread* thread);
+
+/**
+ * The thread is stepping. Setup the thread to step an instruction.
+ */
+extern int rtems_debugger_target_thread_stepping(rtems_debugger_thread* thread);
+
+/**
+ * Return the signal for the exception.
+ */
+extern int rtems_debugger_target_exception_to_signal(CPU_Exception_frame* frame);
+
+/**
+ * Software breakpoints. These are also referred to as memory breakpoints.
+ */
+extern int rtems_debugger_target_swbreak_control(bool insert,
+ DB_UINT addr,
+ DB_UINT kind);
+
+/**
+ * Insert software breakpoints into the memory.
+ */
+extern int rtems_debugger_target_swbreak_insert(void);
+
+/**
+ * Remove software breakpoints from the memory.
+ */
+extern int rtems_debugger_target_swbreak_remove(void);
+
+/**
+ * Hardware breakpoints.
+ */
+extern int rtems_debugger_target_hwbreak_control(rtems_debugger_target_watchpoint type,
+ bool insert,
+ DB_UINT addr,
+ DB_UINT kind);
+
+/**
+ * Target exception processor.
+ */
+extern rtems_debugger_target_exc_action
+rtems_debugger_target_exception(CPU_Exception_frame* frame);
+
+/**
+ * Set the thread's exception stack frame pointer.
+ */
+extern int rtems_debugger_target_set_exception_frame(rtems_debugger_thread* thread);
+
+/**
+ * Target instruction cache sync. This depends on the target but it normally
+ * means a data cache flush and an instruction cache invalidate.
+ */
+extern int rtems_debugger_target_cache_sync(rtems_debugger_target_swbreak* swbreak);
+
+/**
+ * Start a target memory access. If 0 is return the access can proceed and if
+ * -1 is return the access has failed.
+ */
+extern int rtems_debugger_target_start_memory_access(void);
+
+/**
+ * End a target memory access.
+ */
+extern void rtems_debugger_target_end_memory_access(void);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+
+#endif
diff --git a/cpukit/libdebugger/rtems-debugger-threads.c b/cpukit/libdebugger/rtems-debugger-threads.c
new file mode 100644
index 0000000000..89c1b9eb13
--- /dev/null
+++ b/cpukit/libdebugger/rtems-debugger-threads.c
@@ -0,0 +1,577 @@
+/*
+ * Copyright (c) 2016 Chris Johns <chrisj@rtems.org>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <rtems.h>
+#include <rtems/score/statesimpl.h>
+#include <rtems/score/threadimpl.h>
+
+#include <rtems/debugger/rtems-debugger-server.h>
+
+#include "rtems-debugger-target.h"
+#include "rtems-debugger-threads.h"
+
+static const char const* excludes_defaults[] =
+{
+ "TIME",
+ "_BSD",
+ "IRQS",
+ "DBSs",
+ "DBSe",
+};
+
+static void
+rtems_debugger_thread_free(rtems_debugger_threads* threads)
+{
+ rtems_debugger_block_destroy(&threads->current);
+ rtems_debugger_block_destroy(&threads->registers);
+ rtems_debugger_block_destroy(&threads->excludes);
+ rtems_debugger_block_destroy(&threads->stopped);
+ rtems_debugger_block_destroy(&threads->steppers);
+ threads->next = 0;
+}
+
+int
+rtems_debugger_thread_create(void)
+{
+ rtems_debugger_threads* threads;
+ int r;
+
+ threads = calloc(1, sizeof(rtems_debugger_threads));
+ if (threads == NULL) {
+ errno = ENOMEM;
+ rtems_debugger_printf("error: rtems-db: thread: threads alloc: (%d) %s\n",
+ errno, strerror(errno));
+ return -1;
+ }
+
+ r = rtems_debugger_block_create(&threads->current,
+ RTEMS_DEBUGGER_THREAD_BLOCK_SIZE,
+ sizeof(rtems_debugger_thread));
+ if (r < 0) {
+ rtems_debugger_thread_free(threads);
+ free(threads);
+ rtems_debugger_printf("error: rtems-db: thread: current alloc: (%d) %s\n",
+ errno, strerror(errno));
+ return -1;
+ }
+
+ r = rtems_debugger_block_create(&threads->registers,
+ RTEMS_DEBUGGER_THREAD_BLOCK_SIZE,
+ rtems_debugger_target_reg_size());
+ if (r < 0) {
+ rtems_debugger_thread_free(threads);
+ free(threads);
+ rtems_debugger_printf("error: rtems-db: thread: registers alloc: (%d) %s\n",
+ errno, strerror(errno));
+ return -1;
+ }
+
+ r = rtems_debugger_block_create(&threads->excludes,
+ RTEMS_DEBUGGER_THREAD_BLOCK_SIZE,
+ sizeof(rtems_id));
+ if (r < 0) {
+ rtems_debugger_thread_free(threads);
+ free(threads);
+ rtems_debugger_printf("error: rtems-db: thread: exlcudes alloc: (%d) %s\n",
+ errno, strerror(errno));
+ return -1;
+ }
+
+ r = rtems_debugger_block_create(&threads->stopped,
+ RTEMS_DEBUGGER_THREAD_BLOCK_SIZE,
+ sizeof(rtems_id));
+ if (r < 0) {
+ rtems_debugger_thread_free(threads);
+ free(threads);
+ rtems_debugger_printf("error: rtems-db: thread: stopped alloc: (%d) %s\n",
+ errno, strerror(errno));
+ return -1;
+ }
+
+ r = rtems_debugger_block_create(&threads->steppers,
+ RTEMS_DEBUGGER_THREAD_BLOCK_SIZE,
+ sizeof(rtems_debugger_thread_stepper));
+ if (r < 0) {
+ rtems_debugger_thread_free(threads);
+ free(threads);
+ rtems_debugger_printf("error: rtems-db: thread: steppers alloc: (%d) %s\n",
+ errno, strerror(errno));
+ return -1;
+ }
+
+ rtems_debugger->threads = threads;
+
+ return rtems_debugger_thread_system_suspend();
+}
+
+int
+rtems_debugger_thread_destroy(void)
+{
+ rtems_debugger_threads* threads = rtems_debugger->threads;
+ rtems_debugger_thread_system_resume(true);
+ rtems_debugger_thread_free(threads);
+ free(threads);
+ rtems_debugger->threads = NULL;
+ return 0;
+}
+
+int
+rtems_debugger_thread_find_index(rtems_id id)
+{
+ rtems_debugger_threads* threads = rtems_debugger->threads;
+ rtems_debugger_thread* current = rtems_debugger_thread_current(threads);
+ int r = -1;
+ if (threads != NULL) {
+ size_t i;
+ for (i = 0; i < threads->current.level; ++i) {
+ if (id == 0 || current[i].id == id) {
+ r = i;
+ break;
+ }
+ }
+ }
+ return r;
+}
+
+static bool
+snapshot_thread(rtems_tcb* tcb, void* arg)
+{
+ rtems_debugger_threads* threads = rtems_debugger->threads;
+ rtems_id id = tcb->Object.id;
+ char name[RTEMS_DEBUGGER_THREAD_NAME_SIZE];
+ bool exclude = false;
+ size_t i;
+
+ /*
+ * The only time the threads pointer is NULL is a realloc error so we stop
+ * processing threads. There is no way to stop the iterator.
+ */
+ if (rtems_debugger_thread_current(threads) == NULL)
+ return true;
+
+ /*
+ * Filter the threads.
+ */
+ switch (rtems_object_id_get_api(id)) {
+ case OBJECTS_NO_API:
+ case OBJECTS_INTERNAL_API:
+ exclude = true;
+ break;
+ default:
+ rtems_object_get_name(id, sizeof(name), (char*) &name[0]);
+ for (i = 0; i < RTEMS_DEBUGGER_NUMOF(excludes_defaults); ++i) {
+ if (strcmp(excludes_defaults[i], name) == 0) {
+ exclude = true;
+ break;
+ }
+ }
+ break;
+ }
+
+ if (exclude) {
+ rtems_id* excludes;
+ int r;
+ r = rtems_debugger_block_resize(&threads->excludes);
+ if (r < 0) {
+ rtems_debugger_thread_free(threads);
+ return true;
+ }
+ excludes = rtems_debugger_thread_excludes(threads);
+ excludes[threads->excludes.level++] = id;
+ }
+ else {
+ rtems_debugger_thread* current;
+ DB_UINT* registers;
+ rtems_debugger_thread* thread;
+ int r;
+
+ r = rtems_debugger_block_resize(&threads->current);
+ if (r < 0) {
+ rtems_debugger_thread_free(threads);
+ return true;
+ }
+ r = rtems_debugger_block_resize(&threads->registers);
+ if (r < 0) {
+ rtems_debugger_thread_free(threads);
+ return true;
+ }
+
+ current = rtems_debugger_thread_current(threads);
+ registers = rtems_debugger_thread_registers(threads);
+
+ thread = &current[threads->current.level++];
+ thread->registers =
+ &registers[threads->registers.level++ * rtems_debugger_target_reg_num()];
+
+ thread->tcb = tcb;
+ thread->id = id;
+ thread->flags = 0;
+ thread->signal = 0;
+ thread->frame = NULL;
+ memcpy((void*) &thread->name[0], &name[0], sizeof(thread->name));
+
+ /*
+ * See if there is a valid exception stack frame and if the thread is being
+ * debugged.
+ */
+ r = rtems_debugger_target_set_exception_frame(thread);
+ if (r < 0) {
+ rtems_debugger_printf("error: rtems-db: thread: snap: %08lx: not valid frame\n",
+ id);
+ }
+
+ /*
+ * Read the target registers into the thread register array.
+ */
+ rtems_debugger_target_read_regs(thread);
+
+ /*
+ * Debugger threads are stopped for breakpoint, segv or other errors have
+ * the RTEMS_DEBUGGER_THREAD_FLAG_DEBUGGING set.
+ */
+ if (rtems_debugger_thread_flag(thread,
+ RTEMS_DEBUGGER_THREAD_FLAG_DEBUGGING)) {
+ rtems_id* stopped;
+ r = rtems_debugger_block_resize(&threads->stopped);
+ if (r < 0) {
+ rtems_debugger_thread_free(threads);
+ return true;
+ }
+ stopped = rtems_debugger_thread_stopped(threads);
+ stopped[threads->stopped.level++] = id;
+ }
+ else {
+ rtems_status_code sc;
+ sc = rtems_task_suspend(id);
+ if (sc != RTEMS_SUCCESSFUL && sc != RTEMS_ALREADY_SUSPENDED) {
+ rtems_debugger_printf("error: rtems-db: thread: suspend: %08lx: %s\n",
+ id, rtems_status_text(sc));
+ r = -1;
+ }
+ }
+
+ if (rtems_debugger_server_flag(RTEMS_DEBUGGER_FLAG_VERBOSE))
+ rtems_debugger_printf("rtems-db: sys: thd: %08lx: signal: %d\n",
+ id, thread->signal);
+
+ /*
+ * Pick up the first non-zero signal.
+ */
+ if (rtems_debugger->signal == 0) {
+ rtems_debugger->signal = thread->signal;
+ }
+ }
+
+ return false;
+}
+
+int
+rtems_debugger_thread_system_suspend(void)
+{
+ rtems_debugger_threads* threads = rtems_debugger->threads;
+ int r = -1;
+ if (threads != NULL && rtems_debugger_thread_current(threads) != NULL) {
+ if (rtems_debugger_verbose())
+ rtems_debugger_printf("rtems-db: sys: : suspending\n");
+ r = rtems_debugger_target_swbreak_remove();
+ if (r == 0) {
+ rtems_debugger_thread* current;
+ threads->current.level = 0;
+ threads->registers.level = 0;
+ threads->stopped.level = 0;
+ threads->excludes.level = 0;
+ threads->steppers.level = 0;
+ rtems_task_iterate(snapshot_thread, NULL);
+ current = rtems_debugger_thread_current(threads);
+ if (current == NULL) {
+ rtems_debugger_printf("error: rtems-db: thread: snapshot: (%d) %s\n",
+ errno, strerror(errno));
+ r = -1;
+ }
+ else {
+ rtems_id* stopped;
+ /*
+ * If there are no stopped threads pick the first one in the current
+ * table and return that.
+ */
+ threads->selector_gen = 0;
+ threads->selector_cont = 0;
+ stopped = rtems_debugger_thread_stopped(threads);
+ if (threads->stopped.level == 0 && threads->current.level > 0) {
+ stopped[threads->stopped.level++] = current[0].id;
+ }
+ if (threads->stopped.level > 0) {
+ threads->selector_gen =
+ rtems_debugger_thread_find_index(stopped[0]);
+ if (threads->selector_gen < 0)
+ threads->selector_gen = 0;
+ }
+ }
+ }
+ else {
+ errno = EIO;
+ }
+ }
+ return r;
+}
+
+int
+rtems_debugger_thread_system_resume(bool detaching)
+{
+ rtems_debugger_threads* threads = rtems_debugger->threads;
+ rtems_debugger_thread* current;
+ int r = 0;
+ current = rtems_debugger_thread_current(threads);
+ if (threads != NULL && current != NULL) {
+ size_t i;
+ if (rtems_debugger_verbose())
+ rtems_debugger_printf("rtems-db: sys: : resuming\n");
+ if (!detaching)
+ r = rtems_debugger_target_swbreak_insert();
+ if (r == 0) {
+ for (i = 0; i < threads->current.level; ++i) {
+ rtems_debugger_thread* thread = &current[i];
+ rtems_status_code sc;
+ int rr;
+ /*
+ * Check if resuming, which is continuing, a step, or stepping a range.
+ */
+ if (detaching ||
+ rtems_debugger_thread_flag(thread,
+ RTEMS_DEBUGGER_THREAD_FLAG_RESUME)) {
+ if (!detaching) {
+ rr = rtems_debugger_target_write_regs(thread);
+ if (rr < 0 && r == 0)
+ r = rr;
+ if (rtems_debugger_thread_flag(thread,
+ RTEMS_DEBUGGER_THREAD_FLAG_STEP_INSTR)) {
+ rr = rtems_debugger_target_thread_stepping(thread);
+ if (rr < 0 && r == 0)
+ r = rr;
+ }
+ }
+ sc = rtems_task_resume(thread->id);
+ if (sc != RTEMS_SUCCESSFUL) {
+ rtems_debugger_printf("error: rtems-db: thread: resume: %08lx: %s\n",
+ thread->id, rtems_status_text(sc));
+ }
+ thread->flags &= ~(RTEMS_DEBUGGER_THREAD_FLAG_CONTINUE |
+ RTEMS_DEBUGGER_THREAD_FLAG_STEP);
+ thread->signal = 0;
+ }
+ }
+ /*
+ * Excludes are not cleared so the exception handler can find the
+ * excluded thread.
+ */
+ threads->current.level = 0;
+ threads->registers.level = 0;
+ threads->stopped.level = 0;
+ }
+ else {
+ r = -1;
+ errno = EIO;
+ }
+ }
+ return r;
+}
+
+int
+rtems_debugger_thread_continue(rtems_debugger_thread* thread)
+{
+ thread->flags |= RTEMS_DEBUGGER_THREAD_FLAG_CONTINUE;
+ return 0;
+}
+
+int
+rtems_debugger_thread_continue_all(void)
+{
+ rtems_debugger_threads* threads = rtems_debugger->threads;
+ rtems_debugger_thread* current;
+ int r = 0;
+ current = rtems_debugger_thread_current(threads);
+ if (threads != NULL && current != NULL) {
+ size_t i;
+ for (i = 0; i < threads->current.level; ++i) {
+ rtems_debugger_thread* thread = &current[i];
+ int r;
+ r = rtems_debugger_thread_continue(thread);
+ if (r < 0)
+ break;
+ }
+ }
+ else {
+ r = -1;
+ errno = EIO;
+ }
+ return r;
+}
+
+int
+rtems_debugger_thread_step(rtems_debugger_thread* thread)
+{
+ thread->flags |= RTEMS_DEBUGGER_THREAD_FLAG_STEP;
+ return 0;
+}
+
+int
+rtems_debugger_thread_stepping(rtems_debugger_thread* thread,
+ DB_UINT start,
+ DB_UINT end)
+{
+ /* add lock */
+ rtems_debugger_threads* threads = rtems_debugger->threads;
+ rtems_debugger_thread_stepper* stepper;
+ int r;
+ /*
+ * The resize will automatically extend the block when we are full. The
+ * steppers are cleared in suspend by setting the level to 0.
+ */
+ r = rtems_debugger_block_resize(&threads->steppers);
+ if (r < 0) {
+ rtems_debugger_thread_free(threads);
+ return -1;
+ }
+ stepper = rtems_debugger_thread_steppers(threads);
+ stepper = &stepper[threads->steppers.level];
+ stepper->thread = thread;
+ stepper->start = start;
+ stepper->end = end;
+ threads->steppers.level++;
+ thread->flags |= RTEMS_DEBUGGER_THREAD_FLAG_STEPPING;
+ return 0;
+}
+
+const rtems_debugger_thread_stepper*
+rtems_debugger_thread_is_stepping(rtems_id id, DB_UINT pc)
+{
+ /* add lock */
+ rtems_debugger_threads* threads = rtems_debugger->threads;
+ rtems_debugger_thread_stepper* stepper;
+ size_t i;
+ stepper = rtems_debugger_thread_steppers(threads);
+ for (i = 0; i < threads->steppers.level; ++i, ++stepper) {
+ if (stepper->thread->id == id) {
+ if (pc == stepper->start || (pc > stepper->start && pc < stepper->end))
+ return stepper;
+ break;
+ }
+ }
+ return NULL;
+}
+
+int
+rtems_debugger_thread_current_priority(rtems_debugger_thread* thread)
+{
+ return _Thread_Get_priority(thread->tcb);
+}
+
+int
+rtems_debugger_thread_real_priority(rtems_debugger_thread* thread)
+{
+ return thread->tcb->Real_priority.priority;
+}
+
+int
+rtems_debugger_thread_state(rtems_debugger_thread* thread)
+{
+ return thread->tcb->current_state;
+}
+
+int
+rtems_debugger_thread_state_str(rtems_debugger_thread* thread,
+ char* buffer,
+ size_t size)
+{
+ struct mapper {
+ const char const* label;
+ DB_UINT mask;
+ };
+ const struct mapper map[] = {
+ { "DELAY", STATES_DELAYING },
+ { "DORM", STATES_DORMANT },
+ { "LIFE", STATES_LIFE_IS_CHANGING },
+ { "SUSP", STATES_SUSPENDED },
+ { "Wbar", STATES_WAITING_FOR_BARRIER },
+ { "Wbuf", STATES_WAITING_FOR_BUFFER },
+ { "Wcvar", STATES_WAITING_FOR_CONDITION_VARIABLE },
+ { "Wevnt", STATES_WAITING_FOR_EVENT },
+ { "Wisig", STATES_INTERRUPTIBLE_BY_SIGNAL },
+ { "Wjatx", STATES_WAITING_FOR_JOIN_AT_EXIT },
+ { "Wjoin", STATES_WAITING_FOR_JOIN },
+ { "Wmsg" , STATES_WAITING_FOR_MESSAGE },
+ { "Wmutex", STATES_WAITING_FOR_MUTEX },
+ { "WRATE", STATES_WAITING_FOR_PERIOD },
+ { "Wrpc", STATES_WAITING_FOR_RPC_REPLY },
+ { "Wrwlk", STATES_WAITING_FOR_RWLOCK },
+ { "Wseg", STATES_WAITING_FOR_SEGMENT },
+ { "Wsem", STATES_WAITING_FOR_SEMAPHORE },
+ { "Wsig", STATES_WAITING_FOR_SIGNAL },
+ { "Wslcnd", STATES_WAITING_FOR_SYS_LOCK_CONDITION },
+ { "Wslftx", STATES_WAITING_FOR_SYS_LOCK_FUTEX },
+ { "Wslmtx", STATES_WAITING_FOR_SYS_LOCK_MUTEX },
+ { "Wslsem", STATES_WAITING_FOR_SYS_LOCK_SEMAPHORE },
+ { "Wsysev", STATES_WAITING_FOR_SYSTEM_EVENT },
+ { "Wtime", STATES_WAITING_FOR_TIME },
+ { "Wwkup", STATES_WAITING_FOR_BSD_WAKEUP },
+ { "ZOMBI", STATES_ZOMBIE },
+ };
+ DB_UINT state = thread->tcb->current_state;
+ if (state == STATES_READY) {
+ strcpy(buffer, "READY");
+ }
+ else {
+ char* start = buffer;
+ size_t i;
+ buffer[0] = '\0';
+ buffer[size - 1] = '\0';
+ for (i = 0; size > 0 && i < RTEMS_DEBUGGER_NUMOF(map); ++i) {
+ if ((map[i].mask & state) != 0) {
+ size_t l = snprintf(buffer, size - 1, "%s ", map[i].label);
+ buffer += l;
+ size -= l;
+ }
+ }
+ if (buffer != start)
+ *(buffer - 1) = '\0';
+ }
+ return 0;
+}
+
+unsigned long
+rtems_debugger_thread_stack_size(rtems_debugger_thread* thread)
+{
+ return thread->tcb->Start.Initial_stack.size;
+}
+
+void*
+rtems_debugger_thread_stack_area(rtems_debugger_thread* thread)
+{
+ return thread->tcb->Start.Initial_stack.area;
+}
diff --git a/cpukit/libdebugger/rtems-debugger-threads.h b/cpukit/libdebugger/rtems-debugger-threads.h
new file mode 100644
index 0000000000..563b402d83
--- /dev/null
+++ b/cpukit/libdebugger/rtems-debugger-threads.h
@@ -0,0 +1,272 @@
+/*
+ * Copyright (c) 2016 Chris Johns <chrisj@rtems.org>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Debugger for RTEMS.
+ */
+
+#ifndef _RTEMS_DEBUGGER_THREADS_h
+#define _RTEMS_DEBUGGER_THREADS_h
+
+#include <rtems/debugger/rtems-debugger-server.h>
+
+#include "rtems-debugger-block.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/**
+ * Debugger thread name size, fixed size. ASCIIZ format.
+ */
+#define RTEMS_DEBUGGER_THREAD_NAME_SIZE (5)
+
+/**
+ * Debugger thread allocation block size.
+ */
+#define RTEMS_DEBUGGER_THREAD_BLOCK_SIZE (32)
+
+/**
+ * Debugger thread flags.
+ */
+#define RTEMS_DEBUGGER_THREAD_FLAG_DEBUGGING (1 << 0)
+#define RTEMS_DEBUGGER_THREAD_FLAG_REG_VALID (1 << 1)
+#define RTEMS_DEBUGGER_THREAD_FLAG_REG_DIRTY (1 << 2)
+#define RTEMS_DEBUGGER_THREAD_FLAG_CONTINUE (1 << 3)
+#define RTEMS_DEBUGGER_THREAD_FLAG_STEP (1 << 4)
+#define RTEMS_DEBUGGER_THREAD_FLAG_STEPPING (1 << 5)
+#define RTEMS_DEBUGGER_THREAD_FLAG_INTS_DISABLED (1 << 6)
+/* Target specific flags for use by the target backend. */
+#define RTEMS_DEBUGGER_THREAD_FLAG_TARGET_BASE (24)
+#define RTEMS_DEBUGGER_THREAD_FLAG_TARGET_MASK (0xff << RTEMS_DEBUGGER_THREAD_FLAG_TARGET_BASE)
+
+/**
+ * Resume this thread.
+ */
+#define RTEMS_DEBUGGER_THREAD_FLAG_RESUME \
+ (RTEMS_DEBUGGER_THREAD_FLAG_CONTINUE | \
+ RTEMS_DEBUGGER_THREAD_FLAG_STEP | \
+ RTEMS_DEBUGGER_THREAD_FLAG_STEPPING)
+
+/**
+ * Step an instruction.
+ */
+#define RTEMS_DEBUGGER_THREAD_FLAG_STEP_INSTR \
+ (RTEMS_DEBUGGER_THREAD_FLAG_STEP | \
+ RTEMS_DEBUGGER_THREAD_FLAG_STEPPING)
+
+/**
+ * Debugger thread.
+ */
+typedef struct rtems_debugger_thread
+{
+ uint32_t flags;
+ const char name[RTEMS_DEBUGGER_THREAD_NAME_SIZE];
+ Thread_Control* tcb;
+ rtems_id id;
+ int cpu;
+ DB_UINT* registers;
+ int signal;
+ void* frame;
+} rtems_debugger_thread;
+
+/**
+ * Debugger stepping thread. This is a thread that steps while inside an
+ * address range.
+ */
+typedef struct rtems_debugger_thread_stepper
+{
+ rtems_debugger_thread* thread;
+ DB_UINT start;
+ DB_UINT end;
+} rtems_debugger_thread_stepper;
+
+/**
+ * Debugger thread control.
+ */
+struct rtems_debugger_threads
+{
+ rtems_debugger_block current; /**< The threads currently available. */
+ rtems_debugger_block registers; /**< The threads that have stopped. */
+ rtems_debugger_block excludes; /**< The threads we cannot touch. */
+ rtems_debugger_block stopped; /**< The threads that have stopped. */
+ rtems_debugger_block steppers; /**< The threads that are stepping. */
+ size_t next; /**< An iterator. */
+ int selector_gen; /**< General thread selector. */
+ int selector_cont; /**< Continue thread selector. */
+};
+
+/**
+ * Create the thread support.
+ */
+extern int rtems_debugger_thread_create(void);
+
+/**
+ * Destroy the thread support.
+ */
+extern int rtems_debugger_thread_destroy(void);
+
+/**
+ * Find the index in the thread table for the ID.
+ */
+extern int rtems_debugger_thread_find_index(rtems_id id);
+
+/**
+ * Suspend the system.
+ */
+extern int rtems_debugger_thread_system_suspend(void);
+
+/**
+ * Resume the system.
+ */
+extern int rtems_debugger_thread_system_resume(bool detaching);
+
+/**
+ * Continue all threads.
+ */
+extern int rtems_debugger_thread_continue_all(void);
+
+/**
+ * Continue a thread.
+ */
+extern int rtems_debugger_thread_continue(rtems_debugger_thread* thread);
+
+/**
+ * Step a thread.
+ */
+extern int rtems_debugger_thread_step(rtems_debugger_thread* thread);
+
+/**
+ * Thread is stepping so record the details.
+ */
+extern int rtems_debugger_thread_stepping(rtems_debugger_thread* thread,
+ DB_UINT start,
+ DB_UINT end);
+
+/**
+ * Thread's PC in the stepping range? Returns the stepper is in range else
+ * NULL.
+ */
+extern const rtems_debugger_thread_stepper*
+rtems_debugger_thread_is_stepping(rtems_id id, DB_UINT pc);
+
+/**
+ * Return the thread's current priority/
+ */
+extern int rtems_debugger_thread_current_priority(rtems_debugger_thread* thread);
+
+/**
+ * Return the thread's real priority.
+ */
+extern int rtems_debugger_thread_real_priority(rtems_debugger_thread* thread);
+
+/**
+ * Return the thread's state.
+ */
+extern int rtems_debugger_thread_state(rtems_debugger_thread* thread);
+
+/**
+ * Return the thread's state.
+ */
+//extern bool rtems_debugger_thread_state_debugger(rtems_debugger_thread* thread);
+
+/**
+ * Return a string of the thread's state.
+ */
+extern int rtems_debugger_thread_state_str(rtems_debugger_thread* thread,
+ char* buffer,
+ size_t size);
+
+/**
+ * Return the thread's stack size.
+ */
+extern unsigned long rtems_debugger_thread_stack_size(rtems_debugger_thread* thread);
+
+/**
+ * Return the thread's stack area address.
+ */
+extern void* rtems_debugger_thread_stack_area(rtems_debugger_thread* thread);
+
+/**
+ * Check a thread's flag and return true if any of the bits in the mask are
+ * set.
+ */
+static inline bool
+rtems_debugger_thread_flag(rtems_debugger_thread* thread,
+ uint32_t mask)
+{
+ return (thread->flags & mask) != 0;
+}
+
+/**
+ * Get the current threads.
+ */
+static inline rtems_debugger_thread*
+rtems_debugger_thread_current(rtems_debugger_threads* threads)
+{
+ return threads->current.block;
+}
+
+/**
+ * Get the registers.
+ */
+static inline DB_UINT*
+rtems_debugger_thread_registers(rtems_debugger_threads* threads)
+{
+ return threads->registers.block;
+}
+
+/**
+ * Get the excludes.
+ */
+static inline rtems_id*
+rtems_debugger_thread_excludes(rtems_debugger_threads* threads)
+{
+ return threads->excludes.block;
+}
+
+/**
+ * Get the stopped.
+ */
+static inline rtems_id*
+rtems_debugger_thread_stopped(rtems_debugger_threads* threads)
+{
+ return threads->stopped.block;
+}
+
+/**
+ * Get the steppers.
+ */
+static inline rtems_debugger_thread_stepper*
+rtems_debugger_thread_steppers(rtems_debugger_threads* threads)
+{
+ return threads->steppers.block;
+}
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif
diff --git a/cpukit/libdebugger/rtems-debugger.h b/cpukit/libdebugger/rtems-debugger.h
new file mode 100644
index 0000000000..1fc8b3d522
--- /dev/null
+++ b/cpukit/libdebugger/rtems-debugger.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2016 Chris Johns <chrisj@rtems.org>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Debugger for RTEMS.
+ */
+
+#ifndef _RTEMS_DEBUGGER_h
+#define _RTEMS_DEBUGGER_h
+
+#include <stdbool.h>
+
+#include <rtems.h>
+#include <rtems/printer.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/**
+ * Timeout period for an ack
+ */
+#define RTEMS_DEBUGGER_TIMEOUT (3)
+
+/**
+ * Start the Debugger.
+ */
+extern int rtems_debugger_start(const char* remote,
+ const char* device,
+ int timeout,
+ rtems_task_priority priority,
+ const rtems_printer* printer);
+
+/**
+ * Stop the Debugger.
+ */
+extern int rtems_debugger_stop(void);
+
+/**
+ * Is the Debugger running?.
+ */
+extern bool rtems_debugger_running(void);
+
+/**
+ * Verbose control.
+ */
+extern void rtems_debugger_set_verbose(bool on);
+
+/**
+ * Control remote debug printing.
+ */
+extern int rtems_debugger_remote_debug(bool state);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+
+#endif
diff --git a/cpukit/preinstall.am b/cpukit/preinstall.am
index 9514390daa..f4b6529080 100644
--- a/cpukit/preinstall.am
+++ b/cpukit/preinstall.am
@@ -233,6 +233,24 @@ $(PROJECT_INCLUDE)/rtems/rtl/rap-shell.h: libdl/rap-shell.h $(PROJECT_INCLUDE)/r
$(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/rtl/rap-shell.h
PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/rtl/rap-shell.h
endif
+if LIBDEBUGGER
+$(PROJECT_INCLUDE)/rtems/rtems-debugger.h: libdebugger/rtems-debugger.h $(PROJECT_INCLUDE)/rtems/$(dirstamp)
+ $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/rtems-debugger.h
+PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/rtems-debugger.h
+
+$(PROJECT_INCLUDE)/rtems/debugger/$(dirstamp):
+ @$(MKDIR_P) $(PROJECT_INCLUDE)/rtems/debugger
+ @: > $(PROJECT_INCLUDE)/rtems/debugger/$(dirstamp)
+PREINSTALL_DIRS += $(PROJECT_INCLUDE)/rtems/debugger/$(dirstamp)
+
+$(PROJECT_INCLUDE)/rtems/debugger/rtems-debugger-server.h: libdebugger/rtems-debugger-server.h $(PROJECT_INCLUDE)/rtems/debugger/$(dirstamp)
+ $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/debugger/rtems-debugger-server.h
+PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/debugger/rtems-debugger-server.h
+
+$(PROJECT_INCLUDE)/rtems/debugger/rtems-debugger-remote.h: libdebugger/rtems-debugger-remote.h $(PROJECT_INCLUDE)/rtems/debugger/$(dirstamp)
+ $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/debugger/rtems-debugger-remote.h
+PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/debugger/rtems-debugger-remote.h
+endif
$(PROJECT_INCLUDE)/rtems/bspIo.h: include/rtems/bspIo.h $(PROJECT_INCLUDE)/rtems/$(dirstamp)
$(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/bspIo.h
PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/bspIo.h
diff --git a/cpukit/wrapup/Makefile.am b/cpukit/wrapup/Makefile.am
index 768a9037ac..47ffd2498c 100644
--- a/cpukit/wrapup/Makefile.am
+++ b/cpukit/wrapup/Makefile.am
@@ -95,6 +95,10 @@ if LIBDL
TMP_LIBS += ../libdl/libdl.a
endif
+if LIBDEBUGGER
+TMP_LIBS += ../libdebugger/libdebugger.a
+endif
+
librtemscpu.a: $(TMP_LIBS)
rm -f $@
$(MKDIR_P) $(ARCH)
diff --git a/testsuites/libtests/Makefile.am b/testsuites/libtests/Makefile.am
index c00916a143..9ff4394f9e 100644
--- a/testsuites/libtests/Makefile.am
+++ b/testsuites/libtests/Makefile.am
@@ -53,5 +53,9 @@ _SUBDIRS += dl04 dl05
endif
endif
+if DEBUGGERTESTS
+_SUBDIRS += debugger01
+endif
+
include $(top_srcdir)/../automake/test-subdirs.am
include $(top_srcdir)/../automake/local.am
diff --git a/testsuites/libtests/configure.ac b/testsuites/libtests/configure.ac
index 430c509640..8e74b3e979 100644
--- a/testsuites/libtests/configure.ac
+++ b/testsuites/libtests/configure.ac
@@ -80,6 +80,17 @@ AS_IF([test x"$TEST_LIBDL" = x"yes"],[
AM_CONDITIONAL(DLTESTS,[test x"$TEST_LIBDL" = x"yes"])
+# Must match the list in cpukit.
+AC_MSG_CHECKING([whether CPU supports libdebugger])
+case $RTEMS_CPU in
+ arm | i386)
+ TEST_LIBDEBUGGER=yes ;;
+ *)
+ TEST_LIBDEBUGGER=no ;;
+esac
+AC_MSG_RESULT([$TEST_LIBDEBUGGER])
+AM_CONDITIONAL(DEBUGGERTESTS,[test x"$TEST_LIBDEBUGGER" = x"yes"])
+
# Explicitly list all Makefiles here
AC_CONFIG_FILES([Makefile
networking01/Makefile
@@ -169,5 +180,6 @@ math/Makefile
mathf/Makefile
mathl/Makefile
complex/Makefile
+debugger01/Makefile
])
AC_OUTPUT
diff --git a/testsuites/libtests/debugger01/Makefile.am b/testsuites/libtests/debugger01/Makefile.am
new file mode 100644
index 0000000000..d0bf0f8c14
--- /dev/null
+++ b/testsuites/libtests/debugger01/Makefile.am
@@ -0,0 +1,21 @@
+
+rtems_tests_PROGRAMS = debugger01
+debugger01_SOURCES = init.c remote.c system.h
+
+dist_rtems_tests_DATA = debugger01.scn
+
+include $(RTEMS_ROOT)/make/custom/@RTEMS_BSP@.cfg
+include $(top_srcdir)/../automake/compile.am
+include $(top_srcdir)/../automake/leaf.am
+
+
+AM_CPPFLAGS += -I$(top_srcdir)/../support/include
+
+LINK_OBJS = $(debugger01_OBJECTS)
+LINK_LIBS = $(debugger01_LDLIBS)
+
+debugger01$(EXEEXT): $(debugger01_OBJECTS) $(debugger01_DEPENDENCIES)
+ @rm -f debugger01$(EXEEXT)
+ $(make-exe)
+
+include $(top_srcdir)/../automake/local.am
diff --git a/testsuites/libtests/debugger01/debugger01.doc b/testsuites/libtests/debugger01/debugger01.doc
new file mode 100644
index 0000000000..2cdd48bf4d
--- /dev/null
+++ b/testsuites/libtests/debugger01/debugger01.doc
@@ -0,0 +1,18 @@
+# Copyright (c) 2016 Chris Johns <chrisj@rtems.org>
+#
+# The license and distribution terms for this file may be
+# found in the file LICENSE in this distribution or at
+# http://www.rtems.org/license/LICENSE.
+#
+
+This file describes the directives and concepts tested by this test set.
+
+test set name: debugger01
+
+directives:
+
+ rtems_debugger_start
+
+concepts:
+
++ Starts a debugging session with the test remote backend.
diff --git a/testsuites/libtests/debugger01/debugger01.scn b/testsuites/libtests/debugger01/debugger01.scn
new file mode 100644
index 0000000000..7d221fd98b
--- /dev/null
+++ b/testsuites/libtests/debugger01/debugger01.scn
@@ -0,0 +1,188 @@
+
+*** BEGIN OF TEST DEBUGGER01 ***
+error: rtems-db: remote not found: test
+error: rtems-db: test remote: begin
+rtems-db: remote-debug is on
+rtems-db: remote running
+rtems-db: test remote: isconnected: not-connected
+error: rtems-db: test remote: connect
+rtems-db: remote running
+rtems-db: arm debug: (v3.0) ARMv7 [v7, all CP14 registers] breakpoints:5
+watchpoints:3
+rtems-db: sys: : suspending
+rtems-db: sys: thd: 0a010001: signal: 0
+rtems-db: test remote: isconnected: connected
+ remote: rx: message=0 length=1
+ remote: rx: +
+rtems-db: get: 1: + [[ACK?]]
+ remote: rx: message=1 length=5
+ remote: rx: xxxxx
+ remote: rx: message=2 length=5
+ remote: rx: $x#aa
+rtems-db: get: 5: xxxxx [[junk dropped]]
+rtems-db: get: : $x#aa [[invalid checksum]]
+rtems-db: put: 1: -
+ remote: tx: message=0 length=1
+ remote: tx: -
+ remote: rx: message=3 length=141
+ remote: rx: $qSupported:multiprocess+;swbreak+;hwbreak+;qRelocInsn+;fork-events+;vfork-events+;exec-events+;vContSupported+;QThreadEvents+;no-resumed+#df
+rtems-db: get: 141: [[junk dropped]]
+rtems-db: get: : $qSupported:multiprocess+;swbreak+;hwbreak+;qRelocInsn+;fork-events+;vfork-events+;exec-events+;vContSupported+;QThreadEvents+;no-resumed+#df
+rtems-db: put: 1: +
+ remote: tx: message=1 length=1
+ remote: tx: +
+rtems-db: put: 167: $qSupported:PacketSize=4096;QNonStop-;multiprocess+;swbreak+;hwbreak-;qRelocInsn-;fork-events-;vfork-events-;exec-events-;vContSupported+;QThreadEvents-;no-resumed+#b3
+ remote: tx: message=2 length=167
+ remote: tx: $qSupported:PacketSize=4096;QNonStop-;multiprocess+;swbreak+;hwbreak-;qRelocInsn-;fork-events-;vfork-events-;exec-events-;vContSupported+;QThreadEvents-;no-resumed+#b3
+rtems-db: test remote: isconnected: connected
+ remote: rx: message=4 length=19
+ remote: rx: $vMustReplyEmpty#3a
+rtems-db: get: 19: $vMustReplyEmpty#3a
+rtems-db: put: 1: +
+ remote: tx: message=3 length=1
+ remote: tx: +
+rtems-db: put: 4: $#00
+ remote: tx: message=4 length=4
+ remote: tx: $#00
+rtems-db: test remote: isconnected: connected
+ remote: rx: message=5 length=1
+ remote: rx: +
+rtems-db: get: 1: + [[ACK]]
+ remote: rx: message=6 length=10
+ remote: rx: $Hgp0.0#ad
+rtems-db: get: 10: $Hgp0.0#ad
+rtems-db: put: 1: +
+ remote: tx: message=5 length=1
+ remote: tx: +
+rtems-db: put: 6: $OK#9a
+ remote: tx: message=6 length=6
+ remote: tx: $OK#9a
+rtems-db: test remote: isconnected: connected
+ remote: rx: message=7 length=1
+ remote: rx: +
+rtems-db: get: 1: + [[ACK]]
+ remote: rx: message=8 length=12
+ remote: rx: $qTStatus#49
+rtems-db: get: 12: $qTStatus#49
+rtems-db: put: 1: +
+ remote: tx: message=7 length=1
+ remote: tx: +
+rtems-db: put: 4: $#00
+ remote: tx: message=8 length=4
+ remote: tx: $#00
+rtems-db: test remote: isconnected: connected
+ remote: rx: message=9 length=1
+ remote: rx: +
+rtems-db: get: 1: + [[ACK]]
+ remote: rx: message=10 length=5
+ remote: rx: $?#3f
+rtems-db: get: 5: $?#3f
+rtems-db: put: 1: +
+ remote: tx: message=9 length=1
+ remote: tx: +
+rtems-db: put: 26: $T00thread:p1.0a010001;#23
+ remote: tx: message=10 length=26
+ remote: tx: $T00thread:p1.0a010001;#23
+rtems-db: test remote: isconnected: connected
+ remote: rx: message=11 length=1
+ remote: rx: +
+rtems-db: get: 1: + [[ACK]]
+ remote: rx: message=12 length=16
+ remote: rx: $qfThreadInfo#bb
+rtems-db: get: 16: $qfThreadInfo#bb
+rtems-db: put: 1: +
+ remote: tx: message=11 length=1
+ remote: tx: +
+rtems-db: put: 16: $mp1.0a010001#ef
+ remote: tx: message=12 length=2
+ remote: tx: $mp1.0a010001#ef
+rtems-db: test remote: isconnected: connected
+ remote: rx: message=13 length=1
+ remote: rx: +
+rtems-db: get: 1: + [[ACK]]
+ remote: rx: message=14 length=16
+ remote: rx: $qsThreadInfo#c8
+rtems-db: get: 16: $qsThreadInfo#c8
+rtems-db: put: 1: +
+ remote: tx: message=13 length=1
+ remote: tx: +
+rtems-db: put: 5: $l#6c
+ remote: tx: message=14 length=5
+ remote: tx: $l#6c
+rtems-db: test remote: isconnected: connected
+ remote: rx: message=15 length=1
+ remote: rx: -
+rtems-db: get: 1: - [[NACK]]
+rtems-db: put: 5: $l#6c
+ remote: tx: message=15 length=5
+ remote: tx: $l#6c
+ remote: rx: message=16 length=1
+ remote: rx: +
+rtems-db: get: 1: + [[ACK]]
+ remote: rx: message=17 length=15
+ remote: rx: $qAttached:1#fa
+rtems-db: get: 15: $qAttached:1#fa
+rtems-db: put: 1: +
+ remote: tx: message=16 length=1
+ remote: tx: +
+rtems-db: put: 5: $1#31
+ remote: tx: message=17 length=5
+ remote: tx: $1#31
+rtems-db: test remote: isconnected: connected
+ remote: rx: message=18 length=1
+ remote: rx: +
+rtems-db: get: 1: + [[ACK]]
+ remote: rx: message=19 length=8
+ remote: rx: $Hc-1#09
+rtems-db: get: 8: $Hc-1#09
+rtems-db: put: 1: +
+ remote: tx: message=18 length=1
+ remote: tx: +
+rtems-db: put: 6: $OK#9a
+ remote: tx: message=19 length=6
+ remote: tx: $OK#9a
+rtems-db: test remote: isconnected: connected
+ remote: rx: message=20 length=1
+ remote: rx: +
+rtems-db: get: 1: + [[ACK]]
+ remote: rx: message=21 length=12
+ remote: rx: $qOffsets#4b
+rtems-db: get: 12: $qOffsets#4b
+rtems-db: put: 1: +
+ remote: tx: message=20 length=1
+ remote: tx: +
+rtems-db: put: 4: $#00
+ remote: tx: message=21 length=4
+ remote: tx: $#00
+rtems-db: test remote: isconnected: connected
+ remote: rx: message=22 length=1
+ remote: rx: +
+rtems-db: get: 1: + [[ACK]]
+ remote: rx: message=23 length=6
+ remote: rx: $g#67+
+rtems-db: get: 6: $g#67
+rtems-db: put: 1: +
+ remote: tx: message=22 length=1
+ remote: tx: +
++ [[extra data: 0x2b]]rtems-db: put: 212: $cc381200cc381200cc381200cc3812004c162000a83f2000704520004c16200040362000481620000000000020ee1100cc38120028a2200089dc100089dc1000cc381200cc381200cc381200cc381200cc381200cc381200cc381200cc381200cc381200cc381200#95
+ remote: tx: message=23 length=2
+ remote: tx: $cc381200cc381200cc381200cc3812004c162000a83f2000704520004c16200040362000481620000000000020ee1100cc38120028a2200089dc100089dc1000cc381200cc381200cc381200cc381200cc381200cc381200cc381200cc381200cc3812g00cc381200#95
+rtems-db: test remote: isconnected: connected
+ remote: rx: message=24 length=7
+ remote: rx: $D;1#b0
+rtems-db: get: 7: $D;1#b0
+rtems-db: put: 1: +
+ remote: tx: message=24 length=1
+ remote: tx: +
+rtems-db: put: 6: $OK#9a
+ remote: tx: message=25 length=6
+ remote: tx: $OK#9a
+rtems-db: test remote: isconnected: connected
+rtems-db: test remote: disconnect host
+rtems-db: test remote: isconnected: not-connected
+rtems-db: events running
+rtems-db: events finishing
+rtems-db: sys: : resuming
+rtems-db: test remote: isconnected: not-connected
+rtems-db: test remote: isconnected: not-connected
+*** END OF TEST DEBUGGER01 ***
diff --git a/testsuites/libtests/debugger01/init.c b/testsuites/libtests/debugger01/init.c
new file mode 100644
index 0000000000..e243d424a5
--- /dev/null
+++ b/testsuites/libtests/debugger01/init.c
@@ -0,0 +1,53 @@
+/*
+ * Debugger test.
+ *
+ * Copyright (c) 2016 Chris Johns (chrisj@rtems.org)
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#define CONFIGURE_INIT
+#include "system.h"
+
+#include "tmacros.h"
+
+#include <rtems/rtems-debugger.h>
+
+#include "system.h"
+
+static void test(void)
+{
+ rtems_printer printer;
+
+ rtems_print_printer_fprintf(&printer, stdout);
+ rtems_test_assert(rtems_debugger_start("test", "something",
+ 3, 10, &printer) < 0);
+ rtems_test_assert(rtems_debugger_register_test_remote() == 0);
+ rtems_test_assert(rtems_debugger_start("test", "something",
+ 3, 10, &printer) == 0);
+ rtems_debugger_set_verbose(true);
+ rtems_test_assert(rtems_debugger_remote_debug(true) == 0);
+
+ /*
+ * This thread is suspended when the debugger is active until the debugger
+ * disconnects.
+ */
+ sleep(1);
+}
+
+const char rtems_test_name[] = "DEBUGGER01";
+
+rtems_task Init(rtems_task_argument argument)
+{
+ TEST_BEGIN();
+
+ test();
+
+ TEST_END();
+}
diff --git a/testsuites/libtests/debugger01/remote.c b/testsuites/libtests/debugger01/remote.c
new file mode 100644
index 0000000000..1c8c4d09be
--- /dev/null
+++ b/testsuites/libtests/debugger01/remote.c
@@ -0,0 +1,321 @@
+/*
+ * Debugger test remote.
+ *
+ * Copyright (c) 2016 Chris Johns (chrisj@rtems.org)
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.
+ */
+
+#ifdef HAVE_CONFIG_H
+ #include "config.h"
+#endif
+
+#include "tmacros.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <rtems/rtems-debugger.h>
+#include <rtems/debugger/rtems-debugger-server.h>
+#include <rtems/debugger/rtems-debugger-remote.h>
+
+#include "system.h"
+
+/**
+ * Remote data.
+ */
+typedef struct
+{
+ int connect_count;
+ bool connected;
+ size_t out;
+ size_t in;
+} rtems_debugger_remote_test;
+
+static const char* out[] =
+{
+ "+",
+ "xxxxx",
+ "$x#aa",
+
+ "$qSupported:multiprocess+;swbreak+;hwbreak+;qRelocInsn+;fork-events+;"
+ "vfork-events+;exec-events+;vContSupported+;QThreadEvents+;no-resumed+#df",
+
+ "$vMustReplyEmpty#3a",
+ "+",
+
+ "$Hgp0.0#ad",
+ "+",
+
+ "$qTStatus#49",
+ "+",
+
+ "$?#3f",
+ "+",
+
+ "$qfThreadInfo#bb",
+ "+",
+
+ "$qsThreadInfo#c8",
+ "-",
+ "+",
+
+ "$qAttached:1#fa",
+ "+",
+
+ "$Hc-1#09",
+ "+",
+
+ "$qOffsets#4b",
+ "+",
+
+ "$g#67"
+ "+",
+
+ "$D;1#b0",
+ "+"
+};
+
+static const char* in[] =
+{
+ /* 0 */
+ "-",
+
+ /* 1 */
+ "+",
+ "$qSupported:PacketSize=4096;QNonStop-;multiprocess+;swbreak+;hwbreak-;"
+ "qRelocInsn-;fork-events-;vfork-events-;exec-events-;vContSupported+;"
+ "QThreadEvents-;no-resumed+#b3",
+
+ /* 3 */
+ "+",
+ "$#00",
+
+ /* 5 */
+ "+",
+ "$OK#9a",
+
+ /* 7 */
+ "+",
+ "$#00",
+
+ /* 9 */
+ "+",
+ "$T00thread:p1.0a010001;#23",
+
+ /* 11 */
+ "+",
+ "**",
+
+ /* 13 */
+ "+",
+ "$l#6c",
+ "$l#6c",
+
+ /* 16 */
+ "+",
+ "$1#31",
+
+ /* 18 */
+ "+",
+ "$OK#9a",
+
+ /* 20 */
+ "+",
+ "$#00",
+
+ /* 22 */
+ "+",
+ "**",
+
+ "+",
+ "$OK#9a"
+};
+
+static int
+test_remote_begin(rtems_debugger_remote* remote, const char* device)
+{
+ rtems_debugger_remote_test* test;
+
+ rtems_debugger_printf("error: rtems-db: test remote: begin\n");
+
+ rtems_debugger_lock();
+
+ /*
+ * Check the device.
+ */
+ rtems_test_assert(strcmp(device, "something") == 0);
+
+ test = malloc(sizeof(rtems_debugger_remote_test));
+ rtems_test_assert(test != NULL);
+
+ remote->data = test;
+
+ test->connect_count = 0;
+ test->connected = false;
+ test->out = 0;
+ test->in = 0;
+
+ rtems_debugger_unlock();
+
+ return 0;
+}
+
+static int
+test_remote_end(rtems_debugger_remote* remote)
+{
+ rtems_debugger_remote_test* test;
+
+ rtems_debugger_printf("error: rtems-db: test remote: end\n");
+
+ rtems_debugger_lock();
+
+ rtems_test_assert(remote != NULL);
+ rtems_test_assert(remote->data != NULL);
+ test = (rtems_debugger_remote_test*) remote->data;
+
+ test->connected = false;
+
+ free(test);
+
+ rtems_debugger_unlock();
+
+ return 0;
+}
+
+static int
+test_remote_connect(rtems_debugger_remote* remote)
+{
+ rtems_debugger_remote_test* test;
+
+ rtems_test_assert(remote != NULL);
+ rtems_test_assert(remote->data != NULL);
+ test = (rtems_debugger_remote_test*) remote->data;
+
+ if (test->connect_count > 0) {
+ rtems_event_set out = 0;
+ rtems_test_assert(rtems_event_receive(RTEMS_EVENT_1,
+ RTEMS_EVENT_ALL | RTEMS_WAIT,
+ RTEMS_NO_TIMEOUT,
+ &out) == RTEMS_SUCCESSFUL);
+ }
+
+ rtems_debugger_printf("error: rtems-db: test remote: connect\n");
+
+ ++test->connect_count;
+ test->connected = true;
+ test->out = 0;
+ test->in = 0;
+
+ return 0;
+}
+
+static int
+test_remote_disconnect(rtems_debugger_remote* remote)
+{
+ rtems_debugger_remote_test* test;
+
+ rtems_test_assert(remote != NULL);
+ rtems_test_assert(remote->data != NULL);
+ test = (rtems_debugger_remote_test*) remote->data;
+
+ rtems_debugger_printf("rtems-db: test remote: disconnect host\n");
+
+ rtems_debugger_lock();
+
+ rtems_test_assert(test->connected == true);
+
+ test->connected = false;
+
+ rtems_debugger_unlock();
+
+ return 0;
+}
+
+static bool
+test_remote_isconnected(rtems_debugger_remote* remote)
+{
+ rtems_debugger_remote_test* test;
+ bool isconnected;
+ rtems_test_assert(remote != NULL);
+ rtems_test_assert(remote->data != NULL);
+ test = (rtems_debugger_remote_test*) remote->data;
+ isconnected = test != NULL && test->connected;
+ rtems_debugger_printf("rtems-db: test remote: isconnected: %s\n",
+ isconnected ? "connected" : "not-connected");
+ return isconnected;
+}
+
+static void
+test_remote_print(const char* label, const char* buf, size_t size)
+{
+ printf(" remote: %s: ", label);
+ while (size-- > 0) {
+ printf("%c", *buf++);
+ }
+ printf("\n");
+}
+
+static ssize_t
+test_remote_receive(rtems_debugger_remote* remote,
+ void* buf,
+ size_t nbytes)
+{
+ rtems_debugger_remote_test* test;
+ size_t len;
+ rtems_test_assert(remote != NULL);
+ rtems_test_assert(remote->data != NULL);
+ test = (rtems_debugger_remote_test*) remote->data;
+ rtems_test_assert(test->out < RTEMS_DEBUGGER_NUMOF(out));
+ len = strlen(out[test->out]);
+ printf(" remote: rx: message=%zu length=%zu\n", test->out, len);
+ test_remote_print("rx", out[test->out], len);
+ rtems_test_assert(len < nbytes);
+ memcpy(buf, out[test->out++], len);
+ return len;
+}
+
+static ssize_t
+test_remote_send(rtems_debugger_remote* remote,
+ const void* buf,
+ size_t nbytes)
+{
+ rtems_debugger_remote_test* test;
+ size_t len;
+ bool no_match;
+ rtems_test_assert(remote != NULL);
+ rtems_test_assert(remote->data != NULL);
+ test = (rtems_debugger_remote_test*) remote->data;
+ rtems_test_assert(test->in < RTEMS_DEBUGGER_NUMOF(in));
+ len = strlen(in[test->in]);
+ no_match = len == 2 && strcmp(in[test->in], "**") == 0;
+ printf(" remote: tx: message=%zu length=%zu\n", test->in, len);
+ if (!no_match)
+ rtems_test_assert(len == nbytes);
+ test_remote_print("tx", buf, nbytes);
+ if (!no_match)
+ rtems_test_assert(memcmp(buf, in[test->in], nbytes) == 0);
+ test->in++;
+ return nbytes;
+}
+
+static rtems_debugger_remote remote_test =
+{
+ .name = "test",
+ .begin = test_remote_begin,
+ .end = test_remote_end,
+ .connect = test_remote_connect,
+ .disconnect = test_remote_disconnect,
+ .isconnected = test_remote_isconnected,
+ .read = test_remote_receive,
+ .write = test_remote_send
+};
+
+int
+rtems_debugger_register_test_remote(void)
+{
+ return rtems_debugger_remote_register(&remote_test);
+}
diff --git a/testsuites/libtests/debugger01/system.h b/testsuites/libtests/debugger01/system.h
new file mode 100644
index 0000000000..c38283c33f
--- /dev/null
+++ b/testsuites/libtests/debugger01/system.h
@@ -0,0 +1,37 @@
+/* system.h
+ *
+ * This include file contains information that is included in every
+ * function in the test set.
+ *
+ * COPYRIGHT (c) 1989-1999.
+ * On-Line Applications Research Corporation (OAR).
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.
+ */
+
+#include <bsp.h>
+#include <tmacros.h>
+
+/* functions */
+
+rtems_task Init(rtems_task_argument argument);
+
+int rtems_debugger_register_test_remote(void);
+void test_wake(void);
+
+/* configuration information */
+
+#define CONFIGURE_APPLICATION_NEEDS_CONSOLE_DRIVER
+#define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER
+
+#define CONFIGURE_MAXIMUM_USER_EXTENSIONS 1
+#define CONFIGURE_MAXIMUM_TASKS 10
+#define CONFIGURE_MAXIMUM_SEMAPHORES 10
+
+#define CONFIGURE_RTEMS_INIT_TASKS_TABLE
+
+#include <rtems/confdefs.h>
+
+/* end of include file */