From a0d4e9933c57693f99203da358bb8aaa8a5d50d9 Mon Sep 17 00:00:00 2001 From: Chris Johns Date: Fri, 25 Nov 2016 15:13:36 +1100 Subject: cpukit: Add libdebugger, a remote debugger agent for GDB. --- cpukit/libdebugger/Makefile.am | 23 + cpukit/libdebugger/preinstall.am | 9 + cpukit/libdebugger/rtems-debugger-arm.c | 1193 ++++++++++++++ cpukit/libdebugger/rtems-debugger-block.c | 73 + cpukit/libdebugger/rtems-debugger-block.h | 75 + cpukit/libdebugger/rtems-debugger-cmd.c | 208 +++ cpukit/libdebugger/rtems-debugger-i386.c | 421 +++++ cpukit/libdebugger/rtems-debugger-remote-tcp.c | 342 ++++ cpukit/libdebugger/rtems-debugger-remote-tcp.h | 47 + cpukit/libdebugger/rtems-debugger-remote.c | 68 + cpukit/libdebugger/rtems-debugger-remote.h | 71 + cpukit/libdebugger/rtems-debugger-server.c | 1993 ++++++++++++++++++++++++ cpukit/libdebugger/rtems-debugger-server.h | 204 +++ cpukit/libdebugger/rtems-debugger-target.c | 400 +++++ cpukit/libdebugger/rtems-debugger-target.h | 237 +++ cpukit/libdebugger/rtems-debugger-threads.c | 577 +++++++ cpukit/libdebugger/rtems-debugger-threads.h | 272 ++++ cpukit/libdebugger/rtems-debugger.h | 81 + 18 files changed, 6294 insertions(+) create mode 100644 cpukit/libdebugger/Makefile.am create mode 100644 cpukit/libdebugger/preinstall.am create mode 100644 cpukit/libdebugger/rtems-debugger-arm.c create mode 100644 cpukit/libdebugger/rtems-debugger-block.c create mode 100644 cpukit/libdebugger/rtems-debugger-block.h create mode 100644 cpukit/libdebugger/rtems-debugger-cmd.c create mode 100644 cpukit/libdebugger/rtems-debugger-i386.c create mode 100644 cpukit/libdebugger/rtems-debugger-remote-tcp.c create mode 100644 cpukit/libdebugger/rtems-debugger-remote-tcp.h create mode 100644 cpukit/libdebugger/rtems-debugger-remote.c create mode 100644 cpukit/libdebugger/rtems-debugger-remote.h create mode 100644 cpukit/libdebugger/rtems-debugger-server.c create mode 100644 cpukit/libdebugger/rtems-debugger-server.h create mode 100644 cpukit/libdebugger/rtems-debugger-target.c create mode 100644 cpukit/libdebugger/rtems-debugger-target.h create mode 100644 cpukit/libdebugger/rtems-debugger-threads.c create mode 100644 cpukit/libdebugger/rtems-debugger-threads.h create mode 100644 cpukit/libdebugger/rtems-debugger.h (limited to 'cpukit/libdebugger') 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 . 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 +#include +#include + +#include +#include + +#include +#include + +#include "rtems-debugger-target.h" +#include "rtems-debugger-threads.h" + +#if TARGET_DEBUG +#include +#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 . 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 +#include + +#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 . 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 + +#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 . 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 +#include +#include +#include + +#define __need_getopt_newlib +#include + +#include +#include +#include + +#include + +/* + * 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 \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 . 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 +#include +#include + +#include +#include + +#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 +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 = +" \ + \ + \ + i386 \ + \ + \ +"; +#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 . 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 +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#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 . 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 . 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 +#include +#include + +#include +#include +#include + +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 . 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 +#include + +#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 . 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 +#include +#include + +#include +#include +#include + +#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 = ¤t[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 = ¤t[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 = ¤t[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 = ¤t[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 = ¤t[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 = ¤t[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 . 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 +#include + +#include + +#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 . 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 +#include +#include + +#include +#include + +#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 +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 . 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 + +#include + +#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 . 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 +#include +#include + +#include +#include +#include + +#include + +#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 = ¤t[threads->current.level++]; + thread->registers = + ®isters[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 = ¤t[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 = ¤t[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 . 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 + +#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 . 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 + +#include +#include + +#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 -- cgit v1.2.3