summaryrefslogtreecommitdiffstats
path: root/cpukit/libdebugger/rtems-debugger-arm.c
diff options
context:
space:
mode:
Diffstat (limited to 'cpukit/libdebugger/rtems-debugger-arm.c')
-rw-r--r--cpukit/libdebugger/rtems-debugger-arm.c1193
1 files changed, 1193 insertions, 0 deletions
diff --git a/cpukit/libdebugger/rtems-debugger-arm.c b/cpukit/libdebugger/rtems-debugger-arm.c
new file mode 100644
index 0000000000..945dc9c2d9
--- /dev/null
+++ b/cpukit/libdebugger/rtems-debugger-arm.c
@@ -0,0 +1,1193 @@
+/*
+ * Copyright (c) 2016 Chris Johns <chrisj@rtems.org>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#define TARGET_DEBUG 0
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <errno.h>
+#include <inttypes.h>
+#include <stdlib.h>
+
+#include <rtems.h>
+#include <rtems/score/threadimpl.h>
+
+#include <libcpu/arm-cp15.h>
+#include <bsp/linker-symbols.h>
+
+#include "rtems-debugger-target.h"
+#include "rtems-debugger-threads.h"
+
+#if TARGET_DEBUG
+#include <rtems/bspIo.h>
+#endif
+
+/*
+ * The ARM has 2 interrupt bits.
+ */
+#define CPSR_IRQ_DISABLE 0x80 /* IIQ disabled when 1 */
+#define CPSR_FIQ_DISABLE 0x40 /* FIQ disabled when 1 */
+#define CPSR_INTS_MASK (CPSR_IRQ_DISABLE | CPSR_FIQ_DISABLE)
+
+/*
+ * Software breakpoint block size.
+ */
+#define RTEMS_DEBUGGER_SWBREAK_NUM 64
+
+/*
+ * Number of registers.
+ */
+#define RTEMS_DEBUGGER_NUMREGS 26
+
+/*
+ * Number of bytes per register.
+ */
+#define RTEMS_DEBUGGER_REGBYTES 4
+
+/*
+ * Number of bytes of registers.
+ */
+#define RTEMS_DEBUGGER_NUMREGBYTES \
+ (RTEMS_DEBUGGER_NUMREGS * RTEMS_DEBUGGER_REGBYTES)
+
+/*
+ * Debugger registers layout.
+ */
+#define REG_R0 0
+#define REG_R1 1
+#define REG_R2 2
+#define REG_R3 3
+#define REG_R4 4
+#define REG_R5 5
+#define REG_R6 6
+#define REG_R7 7
+#define REG_R8 8
+#define REG_R9 9
+#define REG_R10 10
+#define REG_R11 11
+#define REG_R12 12
+#define REG_SP 13
+#define REG_LR 14
+#define REG_PC 15
+#define REG_CPSR 25
+
+/**
+ * The various status registers.
+ */
+#if defined(ARM_MULTILIB_ARCH_V4)
+ #define FRAME_SR frame->register_cpsr
+#elif defined(ARM_MULTILIB_ARCH_V7M)
+ #define FRAME_SR frame->register_xpsr
+#else
+ #error ARM architecture is not supported.
+#endif
+
+/**
+ * The breakpoint.
+ */
+#ifdef __thumb__
+ static const uint8_t breakpoint[2] = { 0x55, 0xbe };
+#else
+ static const uint8_t breakpoint[4] = { 0x75, 0xe0a, 0x20, 0xe1 };
+#endif
+
+/**
+ * Target lock.
+ */
+RTEMS_INTERRUPT_LOCK_DEFINE(static, target_lock, "target_lock")
+
+/**
+ * The init value for the text section.
+ */
+static uint32_t text_section_flags;
+
+/**
+ * Is a session active?
+ */
+static bool debug_session_active;
+
+/*
+ * ARM debug hardware.
+ */
+static int debug_version;
+static int debug_revision;;
+static int hw_breakpoints;
+static int hw_watchpoints;
+
+/**
+ * Hardware break and watch points.
+ */
+typedef struct
+{
+ bool enabled;
+ bool loaded;
+ void* address;
+ size_t length;
+ CPU_Exception_frame* frame;
+ uint32_t control;
+ uint32_t value;
+} arm_debug_hwbreak;
+
+#define ARM_HW_BREAKPOINT_MAX (16)
+#define ARM_HW_WATCHPOINT_MAX (16)
+
+/*
+ * Types of break points. Only the 2 we use listed.
+ */
+#define ARM_HW_BP_UNLINKED_INSTR_MATCH (0x00)
+#define ARM_HW_BP_UNLINKED_INSTR_MISMATCH (0x04)
+
+/*
+ * Privilege levels.
+ */
+#define ARM_HW_BP_PRIV_PL0_SUP_SYS (0x00)
+#define ARM_HW_BP_PRIV_PL1_ONLY (0x01)
+#define ARM_HW_BP_PRIV_PL0_ONLY (0x02)
+#define ARM_HW_BP_PRIV_ALL_MODES (0x03)
+
+static arm_debug_hwbreak hw_breaks[ARM_HW_BREAKPOINT_MAX];
+//static arm_debug_hwbreak hw_watches[ARM_HW_WATCHPOINT_MAX];
+
+#if TARGET_DEBUG
+static void target_printk(const char* format, ...) RTEMS_PRINTFLIKE(1, 2);
+static void
+target_printk(const char* format, ...)
+{
+ va_list ap;
+ va_start(ap, format);
+ vprintk(format, ap);
+ va_end(ap);
+}
+static const char*
+mode_label(int mode)
+{
+ switch (mode) {
+ case 0x10:
+ return "USR";
+ case 0x11:
+ return "FIQ";
+ case 0x12:
+ return "IRQ";
+ case 0x13:
+ return "SVC";
+ case 0x16:
+ return "MON";
+ case 0x17:
+ return "ABT";
+ case 0x1a:
+ return "HYP";
+ case 0x1b:
+ return "UND";
+ case 0x1f:
+ return "SYS";
+ }
+ return "---";
+}
+#else
+#define target_printk(_fmt, ...)
+#define mode_labels(_m) NULL
+#endif
+
+/*
+ * Read and write a CP14 register.
+ *
+ * The software debug event registers are not easy to program because there are
+ * up to 32 registers and the instructions have to assembler for each of the 32
+ * registers, you cannot program it. This means there is a switch table to do
+ * this.
+ */
+#define ARM_CP14_INSTR(_opc, _val, _CRn, _CRm, _opc2) \
+ #_opc " p14, 0, %[" #_val "], c" #_CRn ", c" #_CRm ", " #_opc2 "\n"
+
+#define ARM_CP14_WRITE(_val, _CRn, _CRm, _opc2) \
+ do { \
+ ARM_SWITCH_REGISTERS; \
+ asm volatile( \
+ ARM_SWITCH_TO_ARM \
+ ARM_CP14_INSTR(mcr, val, _CRn, _CRm, _opc2) \
+ ARM_SWITCH_BACK \
+ : ARM_SWITCH_OUTPUT : [val] "r" (_val)); \
+ } while (0)
+
+#define ARM_CP14_READ(_val, _CRn, _CRm, _opc2) \
+ do { \
+ ARM_SWITCH_REGISTERS; \
+ asm volatile( \
+ ARM_SWITCH_TO_ARM \
+ ARM_CP14_INSTR(mrc, val, _CRn, _CRm, _opc2) \
+ ARM_SWITCH_BACK \
+ : [val] "=&r" (_val) ARM_SWITCH_ADDITIONAL_OUTPUT); \
+ } while (0)
+
+static int
+arm_debug_probe(rtems_debugger_target* target)
+{
+ #define ID_VALUE(_i, _h, _l) ((_i >> _l) & ((1 << ((_h - _l) + 1)) -1))
+ uint32_t val;
+ const char const* vl = "[Invalid version]";
+ const char const* labels[] = {
+ "ARMv6 [v6]",
+ "ARMv6 [v6.1]",
+ "ARMv7 [v7, all CP14 registers]",
+ "ARMv7 [v7, baseline CP14 registers]",
+ "ARMv7 [v7.1]"
+ };
+ ARM_CP14_READ(val, 0, 0, 0);
+ debug_version = ID_VALUE(val, 19, 16);
+ if (debug_version < 1 || debug_version > 5) {
+ rtems_debugger_printf("rtems-db: arm debug: (v%d.%d) not supported\n",
+ debug_version, debug_revision);
+ errno = EIO;
+ return -1;
+ }
+ vl = labels[debug_version - 1];
+ debug_revision = ID_VALUE(val, 3, 0);
+ hw_breakpoints = ID_VALUE(val, 27, 24);
+ hw_watchpoints = ID_VALUE(val, 31, 28);
+ rtems_debugger_printf("rtems-db: arm debug: (v%d.%d) %s breakpoints:%d watchpoints:%d\n",
+ debug_version, debug_revision, vl,
+ hw_breakpoints, hw_watchpoints);
+ ARM_CP14_READ(val, 0, 1, 0);
+ if ((val & (1 << 15)) == 0) {
+ switch (debug_version) {
+ case 1:
+ case 2:
+ ARM_CP14_WRITE(val | (1 << 15), 0, 1, 0);
+ break;
+ case 3:
+ case 4:
+ case 5:
+ default:
+ ARM_CP14_WRITE(val | (1 << 15), 0, 2, 2);
+ break;
+ }
+ ARM_CP14_READ(val, 0, 1, 0);
+ if ((val & (1 << 15)) == 0) {
+ rtems_debugger_printf("rtems-db: arm debug: cannot enter monitor mode\n");
+ errno = EIO;
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static inline void
+arm_debug_break_setup(arm_debug_hwbreak* bp,
+ uint32_t address,
+ uint32_t type,
+ uint32_t byte_address_select,
+ uint32_t privilege)
+{
+ bp->control = (((type & 0xf) << 20) |
+ ((byte_address_select & 0xf) << 5) |
+ ((privilege & 0x3) << 1) | 1);
+ bp->value = (intptr_t) address;
+}
+
+static void
+arm_debug_break_write_control(int bp, uint32_t control)
+{
+ switch (bp) {
+ case 0:
+ ARM_CP14_WRITE(control, 0, 0, 5);
+ break;
+ case 1:
+ ARM_CP14_WRITE(control, 0, 1, 5);
+ break;
+ case 2:
+ ARM_CP14_WRITE(control, 0, 2, 5);
+ break;
+ case 3:
+ ARM_CP14_WRITE(control, 0, 3, 5);
+ break;
+ case 4:
+ ARM_CP14_WRITE(control, 0, 4, 5);
+ break;
+ case 5:
+ ARM_CP14_WRITE(control, 0, 5, 5);
+ break;
+ case 6:
+ ARM_CP14_WRITE(control, 0, 6, 5);
+ break;
+ case 7:
+ ARM_CP14_WRITE(control, 0, 7, 5);
+ break;
+ case 8:
+ ARM_CP14_WRITE(control, 0, 8, 5);
+ break;
+ case 9:
+ ARM_CP14_WRITE(control, 0, 9, 5);
+ break;
+ case 10:
+ ARM_CP14_WRITE(control, 0, 10, 5);
+ break;
+ case 11:
+ ARM_CP14_WRITE(control, 0, 11, 5);
+ break;
+ case 12:
+ ARM_CP14_WRITE(control, 0, 12, 5);
+ break;
+ case 13:
+ ARM_CP14_WRITE(control, 0, 13, 5);
+ break;
+ case 14:
+ ARM_CP14_WRITE(control, 0, 14, 5);
+ break;
+ case 15:
+ ARM_CP14_WRITE(control, 0, 15, 5);
+ break;
+ }
+}
+
+static void
+arm_debug_break_write_value(int bp, uint32_t value)
+{
+ switch (bp) {
+ case 0:
+ ARM_CP14_WRITE(value, 0, 0, 4);
+ break;
+ case 1:
+ ARM_CP14_WRITE(value, 0, 1, 4);
+ break;
+ case 2:
+ ARM_CP14_WRITE(value, 0, 2, 4);
+ break;
+ case 3:
+ ARM_CP14_WRITE(value, 0, 3, 4);
+ break;
+ case 4:
+ ARM_CP14_WRITE(value, 0, 4, 4);
+ break;
+ case 5:
+ ARM_CP14_WRITE(value, 0, 5, 4);
+ break;
+ case 6:
+ ARM_CP14_WRITE(value, 0, 6, 4);
+ break;
+ case 7:
+ ARM_CP14_WRITE(value, 0, 7, 4);
+ break;
+ case 8:
+ ARM_CP14_WRITE(value, 0, 8, 4);
+ break;
+ case 9:
+ ARM_CP14_WRITE(value, 0, 9, 4);
+ break;
+ case 10:
+ ARM_CP14_WRITE(value, 0, 10, 4);
+ break;
+ case 11:
+ ARM_CP14_WRITE(value, 0, 11, 4);
+ break;
+ case 12:
+ ARM_CP14_WRITE(value, 0, 12, 4);
+ break;
+ case 13:
+ ARM_CP14_WRITE(value, 0, 13, 4);
+ break;
+ case 14:
+ ARM_CP14_WRITE(value, 0, 14, 4);
+ break;
+ case 15:
+ ARM_CP14_WRITE(value, 0, 15, 4);
+ break;
+ }
+}
+
+static void
+arm_debug_break_clear(void)
+{
+ arm_debug_hwbreak* bp = &hw_breaks[0];
+ int i;
+ for (i = 0; i < hw_breakpoints; ++i, ++bp) {
+ bp->enabled = false;
+ bp->loaded = false;
+ }
+}
+
+static void
+arm_debug_break_load(void)
+{
+ arm_debug_hwbreak* bp = &hw_breaks[0];
+ int i;
+ for (i = 0; i < hw_breakpoints; ++i, ++bp) {
+ if (bp->enabled && !bp->loaded) {
+ bp->loaded = true;
+ target_printk("]]} hwbp: %i: v:%08lx c:%08lx l:%08x\n",
+ i, bp->value, bp->control, bp->length);
+ arm_debug_break_write_value(i, bp->value);
+ arm_debug_break_write_control(i, bp->control);
+ }
+ }
+}
+
+static void
+arm_debug_break_unload(void)
+{
+ arm_debug_hwbreak* bp = &hw_breaks[0];
+ int i;
+ for (i = 0; i < hw_breakpoints; ++i, ++bp) {
+ bp->loaded = false;
+ arm_debug_break_write_control(i, 0);
+ }
+}
+
+#if NOT_USED_BUT_KEEPING
+static size_t
+arm_debug_break_length(void* pc)
+{
+ arm_debug_hwbreak* bp = &hw_breaks[0];
+ int i;
+
+ for (i = 0; i < hw_breakpoints; ++i, ++bp) {
+ if (bp->enabled && bp->address == pc) {
+ return bp->length;
+ }
+ }
+ return sizeof(DB_UINT);
+}
+#endif
+
+int
+rtems_debugger_target_configure(rtems_debugger_target* target)
+{
+ target->capabilities = (RTEMS_DEBUGGER_TARGET_CAP_SWBREAK);
+ target->reg_num = RTEMS_DEBUGGER_NUMREGS;
+ target->reg_size = sizeof(uint32_t);
+ target->breakpoint = &breakpoint[0];
+ target->breakpoint_size = sizeof(breakpoint);
+ return arm_debug_probe(target);
+}
+
+static void
+target_exception(CPU_Exception_frame* frame)
+{
+#if TARGET_DEBUG
+ uint32_t ifsr = arm_cp15_get_instruction_fault_status();
+#endif
+
+ target_printk("[} frame = %08lx sig=%d vector=%x ifsr=%08lx pra=%08x\n",
+ (uint32_t) frame,
+ rtems_debugger_target_exception_to_signal(frame),
+ frame->vector, ifsr, (intptr_t) frame->register_pc);
+
+ if ((FRAME_SR & (1 << 5)) == 0)
+ frame->register_pc = (void*) ((intptr_t) frame->register_pc - 8);
+ else
+ frame->register_pc = (void*) ((intptr_t) frame->register_pc - 4);
+
+ target_printk("[} R0 = %08" PRIx32 " R1 = %08" PRIx32 \
+ " R2 = %08" PRIx32 " R3 = %08" PRIx32 "\n",
+ frame->register_r0, frame->register_r1,
+ frame->register_r2, frame->register_r3);
+ target_printk("[} R4 = %08" PRIx32 " R5 = %08" PRIx32 \
+ " R6 = %08" PRIx32 " R7 = %08" PRIx32 "\n",
+ frame->register_r4, frame->register_r5,
+ frame->register_r6, frame->register_r7);
+ target_printk("[} R8 = %08" PRIx32 " R9 = %08" PRIx32 \
+ " R10 = %08" PRIx32 " R11 = %08" PRIx32 "\n",
+ frame->register_r8, frame->register_r9,
+ frame->register_r10, frame->register_r11);
+ target_printk("[} R12 = %08" PRIx32 " SP = %08" PRIx32 \
+ " LR = %08" PRIxPTR " PC = %08" PRIxPTR "\n", \
+ frame->register_r12, frame->register_sp,
+ (intptr_t) frame->register_lr, (intptr_t) frame->register_pc);
+ target_printk("[} CPSR = %08" PRIx32 " %c%c%c%c%c%c%c%c%c%c%c" \
+ " GE:%" PRIx32 " IT:%02" PRIx32 " M:%" PRIx32 " %s\n",
+ FRAME_SR,
+ (FRAME_SR & (1 << 31)) != 0 ? 'N' : '-',
+ (FRAME_SR & (1 << 30)) != 0 ? 'Z' : '-',
+ (FRAME_SR & (1 << 29)) != 0 ? 'C' : '-',
+ (FRAME_SR & (1 << 28)) != 0 ? 'V' : '-',
+ (FRAME_SR & (1 << 27)) != 0 ? 'Q' : '-',
+ (FRAME_SR & (1 << 24)) != 0 ? 'J' : '-',
+ (FRAME_SR & (1 << 9)) != 0 ? 'E' : '-',
+ (FRAME_SR & (1 << 8)) != 0 ? 'A' : '-',
+ (FRAME_SR & (1 << 7)) != 0 ? 'I' : '-',
+ (FRAME_SR & (1 << 6)) != 0 ? 'F' : '-',
+ (FRAME_SR & (1 << 5)) != 0 ? 'T' : '-',
+ ((FRAME_SR >> (25 - 5)) & (0x3 << 5)) | ((FRAME_SR >> 10) & 0x1f),
+ (FRAME_SR >> 16) & 0xf,
+ FRAME_SR & 0x1f, mode_label(FRAME_SR & 0x1f));
+
+ arm_debug_break_clear();
+
+ if (!debug_session_active)
+ _ARM_Exception_default(frame);
+
+ switch (rtems_debugger_target_exception(frame)) {
+ case rtems_debugger_target_exc_consumed:
+ default:
+ break;
+ case rtems_debugger_target_exc_step:
+ FRAME_SR |= CPSR_INTS_MASK;
+ break;
+ case rtems_debugger_target_exc_cascade:
+ target_printk("rtems-db: unhandled exception: cascading\n");
+ _ARM_Exception_default(frame);
+ break;
+ }
+
+ target_printk("[} resuming frame = %08lx PC = %08" PRIxPTR " CPSR = %08" PRIx32 "\n",
+ (uint32_t) frame, (intptr_t) frame->register_pc, FRAME_SR);
+}
+
+/**
+ * If thumb build of code switch the asm to thumb as required.
+ */
+#ifdef __thumb__
+ #define ASM_ARM_MODE ".align 2\nbx pc\n.arm\n"
+ #define ASM_THUMB_MODE "add %[arm_switch_reg], pc, #1\nbx %[arm_switch_reg]\n.thumb\n"
+ #define ARM_SWITCH_REG uint32_t arm_switch_reg
+#else
+ #define ASM_ARM_MODE
+ #define ASM_THUMB_MODE
+ #define ARM_SWITCH_REG
+#endif
+
+/**
+ * Exception stack frame size.
+ *
+ * The size is the exception stack frame plus the CPSR from the exception. We
+ * save the CPSR and restore it when we exit the exception.
+ */
+#define EXCEPTION_FRAME_SIZE (sizeof(CPU_Exception_frame) + sizeof(uint32_t))
+
+/**
+ * Exception stack frame FPU offsets and sizes.
+ */
+#define EXCEPTION_FRAME_FPU_SIZE ARM_VFP_CONTEXT_SIZE
+#define EXCEPTION_FRAME_FPU_OFFSET ARM_EXCEPTION_FRAME_VFP_CONTEXT_OFFSET
+
+/**
+ * Exception entry.
+ *
+ * We have switched from svc (or even user) to an exception mode. Save the
+ * current CPSR and create an exception frame on the exception's stack and then
+ * copy it to the thread's stack. Switch back to the thread's context and mode
+ * to handle the exception to avoid any stack checks thinking the stack is
+ * blown. This lets the thread be suspended.
+ *
+ * The entry is in two parts, the exception mode entry and the trhead mode
+ * entry. This lets us disable any hardware breakpoint support. We need to do
+ * this because it is enabled in PL0 mode.
+ *
+ * Note, the code currently assumes cp15 has been set up to match the
+ * instruction set being used.
+ */
+#define EXCEPTION_ENTRY_EXC() \
+ __asm__ volatile( \
+ ASM_ARM_MODE \
+ "sub sp, %[frame_size]\n" /* alloc the frame and CPSR */ \
+ "stm sp, {r0-r12}\n" /* store r0-r12 */ \
+ "sub sp, #4\n" \
+ "str lr, [sp]\n" /* save the link reg */ \
+ ASM_THUMB_MODE \
+ : [arm_switch_reg] "=&r" (arm_switch_reg) \
+ : [frame_size] "i" (EXCEPTION_FRAME_SIZE) \
+ : "memory")
+
+/*
+ * FPU entry. Conditionally D16 or D32 support.
+ */
+#ifdef ARM_MULTILIB_VFP
+#ifdef ARM_MULTILIB_VFP_D32
+#define FPU_ENTRY_VFP_D32 \
+ "vstmia r5!, {d16-d31}\n"
+#else /* ARM_MULTILIB_VFP_D32 */
+#define FPU_ENTRY_VFP_D32 \
+ "mov r3, #0\n" \
+ "mov r4, #0\n" \
+ "adds r6, r5, #128\n" \
+ "3:\n" \
+ "stmia r5!, {r3-r4}\n" \
+ "cmp r5, r6\n" \
+ "bne 3b\n"
+#endif /* ARM_MULTILIB_VFP_D32 */
+#define EXCEPTION_ENTRY_FPU(frame_fpu_size) \
+ "sub sp, %[frame_fpu_size]\n" /* size includes alignment size */ \
+ "add r5, sp, #4\n" /* up to align down */ \
+ "bic r5, r5, #7\n" /* align the FPU frame */ \
+ "str r5, [r2]\n" /* store the FPU frame pointer */ \
+ "vmrs r3, FPEXC\n" \
+ "vmrs r4, FPSCR\n" \
+ "stmia r5!, {r3-r4}\n" \
+ "vstmia r5!, {d0-d15}\n" \
+ FPU_ENTRY_VFP_D32
+#else /* ARM_MULTILIB_VFP */
+#define EXCEPTION_ENTRY_FPU(frame_fpu_size)
+#endif /* ARM_MULTILIB_VFP */
+
+#define EXCEPTION_ENTRY_THREAD(_frame) \
+ __asm__ volatile( \
+ ASM_ARM_MODE \
+ "ldr lr, [sp]\n" /* recover the link reg */ \
+ "add sp, #4\n" \
+ "add r0, sp, %[r0_r12_size]\n" /* get the sp in the frame */ \
+ "mrs r1, spsr\n" /* get the saved sr */ \
+ "mov r6, r1\n" /* stash it for later */ \
+ "bic r1, r1, %[psr_t]\n" /* clear thumb mode, not sure? */ \
+ "orr r1, r1, %[psr_i]\n" /* mask irqs */ \
+ "mrs r2, cpsr\n" /* get the current sr */ \
+ "str r2, [sp, %[frame_cpsr]]\n" /* save for exc return */ \
+ "msr cpsr, r1\n" /* switch to user mode */ \
+ "mov r3, sp\n" /* get the stack pointer */ \
+ "mov r4, lr\n" /* get the link reg */ \
+ "msr cpsr, r2\n" /* back to exc mode */ \
+ "mov r5, lr\n" /* get the PRA */ \
+ "stm r0, {r3-r6}\n" /* save into the frame */ \
+ "sub r4, r3, %[frame_size]\n" /* destination address */ \
+ "mov r6, r4\n" /* save the frame */ \
+ "sub r4, #1\n" /* one before the start */ \
+ "add r3, #1\n" /* one past the end */ \
+ "sub r5, sp, #1\n" /* source address */ \
+ "1:\n" \
+ "ldrb r0, [r5, #1]!\n" /* get a byte */ \
+ "strb r0, [r4, #1]!\n" /* put the byte */ \
+ "cmp r3, r4\n" /* the end? */ \
+ "bne 1b\n" \
+ "add sp, %[frame_size]\n" /* free the frame and CPSR */ \
+ "mrs r1, spsr\n" /* get the thread's saved sr */ \
+ "orr r2, r1, %[psr_i]\n" /* mask irqs */ \
+ "msr cpsr, r2\n" /* switch back to the thread's context */ \
+ "sub sp, %[frame_size]\n" /* alloc in the thread stack */ \
+ "mov %[o_frame], sp\n" /* save the frame */ \
+ "add r2, sp, %[o_frame_fpu]\n" /* get the FPU offset */ \
+ "mov r3, #0\n" \
+ "str r3, [r2]\n" /* clear the FPU frame pointer */ \
+ EXCEPTION_ENTRY_FPU(frame_fpu_size) \
+ "msr cpsr, r1\n" /* restore the irq mask */ \
+ ASM_THUMB_MODE \
+ : [arm_switch_reg] "=&r" (arm_switch_reg), \
+ [o_frame] "=r" (_frame) \
+ : [psr_t] "i" (ARM_PSR_T), \
+ [psr_i] "i" (ARM_PSR_I), \
+ [r0_r12_size] "i" (13 * sizeof(uint32_t)), \
+ [frame_cpsr] "i" (EXCEPTION_FRAME_SIZE - sizeof(uint32_t)), \
+ [frame_size] "i" (EXCEPTION_FRAME_SIZE), \
+ [o_frame_fpu] "i" (EXCEPTION_FRAME_FPU_OFFSET), \
+ [frame_fpu_size] "i" (EXCEPTION_FRAME_FPU_SIZE + 4) \
+ : "r0", "r1", "r2", "r3", "r4", "r5", "r6", "memory")
+
+/*
+ * FPU exit. Conditionally D16 or D32 support.
+ */
+#ifdef ARM_MULTILIB_VFP
+#ifdef ARM_MULTILIB_VFP_D32
+#define FPU_EXIT_VFP_D32 \
+ "vldmia r0, {d16-d31}\n"
+#else /* ARM_MULTILIB_VFP_D32 */
+#define FPU_EXIT_VFP_D32
+#endif /* ARM_MULTILIB_VFP_D32 */
+#define EXCEPTION_EXIT_FPU(frame_fpu_size) \
+ "ldmia r0!, {r1-r2}\n" \
+ "vldmia r0!, {d0-d15}\n" \
+ FPU_EXIT_VFP_D32 \
+ "vmsr FPEXC, r1\n" \
+ "vmsr FPSCR, r2\n" \
+ "add sp, %[frame_fpu_size]\n" /* size includes alignment size */
+#else /* ARM_MULTILIB_VFP */
+#define EXCEPTION_EXIT_FPU(frame_fpu_size)
+#endif /* ARM_MULTILIB_VFP */
+
+/**
+ * Exception exit.
+ *
+ * The thread is to be resumed so we are still in the thread's mode. Copy the
+ * exception frame from the thread's stack back to the exception's stack and
+ * restore the thread's context before returning from the exception to the
+ * thread.
+ *
+ * Note, the code currently assumes cp15 has been set up to match the
+ * instruction set being used.
+ */
+#define EXCEPTION_EXIT_THREAD(_frame) \
+ __asm__ volatile( \
+ ASM_ARM_MODE \
+ "mov r0, %[i_frame]\n" /* get the frame */ \
+ "ldr r0, [r0, %[frame_fpu]]\n" /* recover FPU frame pointer */ \
+ EXCEPTION_EXIT_FPU(frame_fpu_size) \
+ "ldr r2, [sp, %[frame_cpsr]]\n" /* recover exc CPSR from thread */ \
+ "mov r0, sp\n" /* get the thread frame pointer */ \
+ "msr cpsr, r2\n" /* switch back to the exc's context */ \
+ "add r3, sp, #1\n" /* get the end */ \
+ "sub sp, %[frame_size]\n" /* alloc the frame */ \
+ "sub r4, sp, #1\n" /* destination address */ \
+ "sub r5, r0, #1\n" /* source address */ \
+ "1:\n" \
+ "ldrb r1, [r5, #1]!\n" /* get a byte */ \
+ "strb r1, [r4, #1]!\n" /* put the byte */ \
+ "cmp r3, r4\n" /* the end? */ \
+ "bne 1b\n" \
+ "add r1, r0, %[r0_r12_size]\n" /* get the sp in the frame */ \
+ "ldm r1, {r3-r6}\n" /* recover from the frame */ \
+ "orr r1, r6, %[psr_i]\n" /* get the thread's psr and mask irqs */ \
+ "msr cpsr, r1\n" /* switch to user mode */ \
+ "mov sp, r3\n" /* set the stack pointer */ \
+ "mov lr, r4\n" /* set the link reg */ \
+ "msr cpsr, r2\n" /* switch back to the exc's context */ \
+ "msr spsr, r6\n" /* set the thread's CPSR */ \
+ "sub sp, #4\n" \
+ "mov lr, r5\n" /* get the PC */ \
+ "str lr, [sp]\n" /* save the link reg */ \
+ ASM_THUMB_MODE \
+ : [arm_switch_reg] "=&r" (arm_switch_reg) \
+ : [psr_i] "i" (ARM_PSR_I), \
+ [r0_r12_size] "i" (13 * sizeof(uint32_t)), \
+ [frame_cpsr] "i" (EXCEPTION_FRAME_SIZE - sizeof(uint32_t)), \
+ [frame_size] "i" (EXCEPTION_FRAME_SIZE), \
+ [frame_fpu] "i" (EXCEPTION_FRAME_FPU_OFFSET), \
+ [frame_fpu_size] "i" (EXCEPTION_FRAME_FPU_SIZE + 4), \
+ [i_frame] "r" (_frame) \
+ : "r0", "r1", "r2", "r3", "r4", "r5", "r6", "memory")
+
+#define EXCEPTION_EXIT_EXC() \
+ __asm__ volatile( \
+ ASM_ARM_MODE \
+ "ldr lr, [sp]\n" /* recover the link reg */ \
+ "add sp, #4\n" \
+ "ldm sp, {r0-r12}\n" /* restore the trhead's context */ \
+ "add sp, %[frame_size]\n" /* free the frame */ \
+ "subs pc, lr, #0\n" /* return from the exc */ \
+ : \
+ : [frame_size] "i" (EXCEPTION_FRAME_SIZE) \
+ : "memory")
+
+
+static void __attribute__((naked))
+target_exception_undefined_instruction(void)
+{
+ CPU_Exception_frame* frame;
+ ARM_SWITCH_REG;
+ EXCEPTION_ENTRY_EXC();
+ arm_debug_break_unload();
+ EXCEPTION_ENTRY_THREAD(frame);
+ frame->vector = 1;
+ target_exception(frame);
+ EXCEPTION_EXIT_THREAD(frame);
+ arm_debug_break_load();
+ EXCEPTION_EXIT_EXC();
+}
+
+static void __attribute__((naked))
+target_exception_supervisor_call(void)
+{
+ CPU_Exception_frame* frame;
+ ARM_SWITCH_REG;
+ /*
+ * The PC offset needs to be review so we move past a svc instruction. This
+ * can then used as a user breakpoint. The issue is this exception is used by
+ * the BKPT instruction in the prefetch abort handler to signal a TRAP.
+ */
+ EXCEPTION_ENTRY_EXC();
+ arm_debug_break_unload();
+ EXCEPTION_ENTRY_THREAD(frame);
+ frame->vector = 2;
+ target_exception(frame);
+ EXCEPTION_EXIT_THREAD(frame);
+ arm_debug_break_load();
+ EXCEPTION_EXIT_EXC();
+}
+
+static void __attribute__((naked))
+target_exception_prefetch_abort(void)
+{
+ CPU_Exception_frame* frame;
+ ARM_SWITCH_REG;
+ EXCEPTION_ENTRY_EXC();
+ arm_debug_break_unload();
+ EXCEPTION_ENTRY_THREAD(frame);
+ if ((arm_cp15_get_instruction_fault_status() & 0x1f) == 0x02)
+ frame->vector = 2;
+ else
+ frame->vector = 3;
+ target_exception(frame);
+ EXCEPTION_EXIT_THREAD(frame);
+ arm_debug_break_load();
+ EXCEPTION_EXIT_EXC();
+}
+
+static void __attribute__((naked))
+target_exception_data_abort(void)
+{
+ CPU_Exception_frame* frame;
+ ARM_SWITCH_REG;
+ EXCEPTION_ENTRY_EXC();
+ arm_debug_break_unload();
+ EXCEPTION_ENTRY_THREAD(frame);
+ frame->vector = 4;
+ target_exception(frame);
+ EXCEPTION_EXIT_THREAD(frame);
+ arm_debug_break_load();
+ EXCEPTION_EXIT_EXC();
+}
+
+int
+rtems_debugger_target_enable(void)
+{
+ rtems_interrupt_lock_context lock_context;
+ void* text_begin;
+ void* text_end;
+ debug_session_active = true;
+ text_begin = &bsp_section_text_begin[0];
+ text_end = &bsp_section_text_end[0];
+ rtems_interrupt_lock_acquire(&target_lock, &lock_context);
+ arm_debug_break_unload();
+ arm_debug_break_clear();
+ text_section_flags =
+ arm_cp15_set_translation_table_entries(text_begin,
+ text_end,
+ ARMV7_MMU_DATA_READ_WRITE_CACHED);
+ arm_cp15_set_exception_handler(ARM_EXCEPTION_UNDEF,
+ target_exception_undefined_instruction);
+ arm_cp15_set_exception_handler(ARM_EXCEPTION_SWI,
+ target_exception_supervisor_call);
+ arm_cp15_set_exception_handler(ARM_EXCEPTION_PREF_ABORT,
+ target_exception_prefetch_abort);
+ arm_cp15_set_exception_handler(ARM_EXCEPTION_DATA_ABORT,
+ target_exception_data_abort);
+ rtems_interrupt_lock_release(&target_lock, &lock_context);
+ return 0;
+}
+
+int
+rtems_debugger_target_disable(void)
+{
+ rtems_interrupt_lock_context lock_context;
+#if DOES_NOT_WORK
+ void* text_begin;
+ void* text_end;
+#endif
+ rtems_interrupt_lock_acquire(&target_lock, &lock_context);
+ debug_session_active = false;
+#if DOES_NOT_WORK
+ text_begin = &bsp_section_text_begin[0];
+ text_end = &bsp_section_text_end[0];
+ arm_cp15_set_translation_table_entries(text_begin,
+ text_end,
+ text_section_flags);
+#endif
+ rtems_interrupt_lock_release(&target_lock, &lock_context);
+ return 0;
+}
+
+int
+rtems_debugger_target_read_regs(rtems_debugger_thread* thread)
+{
+ if (!rtems_debugger_thread_flag(thread,
+ RTEMS_DEBUGGER_THREAD_FLAG_REG_VALID)) {
+ static const uint32_t good_address = (uint32_t) &good_address;
+ uint32_t* regs = &thread->registers[0];
+ int i;
+
+ for (i = 0; i < RTEMS_DEBUGGER_NUMREGS; ++i)
+ regs[i] = (uint32_t) &good_address;
+
+ if (thread->frame) {
+ CPU_Exception_frame* frame = thread->frame;
+
+ /*
+ * Assume interrupts are not masked and if masked set them to the saved
+ * value.
+ */
+ FRAME_SR &= ~CPSR_INTS_MASK;
+
+ if (rtems_debugger_thread_flag(thread,
+ RTEMS_DEBUGGER_THREAD_FLAG_INTS_DISABLED)) {
+ FRAME_SR |=
+ (thread->flags >> RTEMS_DEBUGGER_THREAD_FLAG_TARGET_BASE) & CPSR_INTS_MASK;
+ thread->flags = ~RTEMS_DEBUGGER_THREAD_FLAG_INTS_DISABLED;
+ }
+
+ regs[REG_R0] = frame->register_r0;
+ regs[REG_R1] = frame->register_r1;
+ regs[REG_R2] = frame->register_r2;
+ regs[REG_R3] = frame->register_r3;
+ regs[REG_R4] = frame->register_r4;
+ regs[REG_R5] = frame->register_r5;
+ regs[REG_R6] = frame->register_r6;
+ regs[REG_R7] = frame->register_r7;
+ regs[REG_R8] = frame->register_r8;
+ regs[REG_R9] = frame->register_r9;
+ regs[REG_R10] = frame->register_r10;
+ regs[REG_R11] = frame->register_r11;
+ regs[REG_R12] = frame->register_r12;
+ regs[REG_SP] = frame->register_sp;
+ regs[REG_LR] = (uint32_t) frame->register_lr;
+ regs[REG_PC] = (uint32_t) frame->register_pc;
+ regs[REG_CPSR] = FRAME_SR;
+ /*
+ * Get the signal from the frame.
+ */
+ thread->signal = rtems_debugger_target_exception_to_signal(frame);
+ }
+ else {
+#if defined(ARM_MULTILIB_ARCH_V4)
+ regs[REG_R4] = thread->tcb->Registers.register_r4;
+ regs[REG_R5] = thread->tcb->Registers.register_r5;
+ regs[REG_R6] = thread->tcb->Registers.register_r6;
+ regs[REG_R7] = thread->tcb->Registers.register_r7;
+ regs[REG_R8] = thread->tcb->Registers.register_r8;
+ regs[REG_R9] = thread->tcb->Registers.register_r9;
+ regs[REG_R10] = thread->tcb->Registers.register_r10;
+ regs[REG_R11] = thread->tcb->Registers.register_fp;
+ regs[REG_LR] = (intptr_t) thread->tcb->Registers.register_lr;
+ regs[REG_PC] = (intptr_t) thread->tcb->Registers.register_lr;
+ regs[REG_SP] = (intptr_t) thread->tcb->Registers.register_sp;
+#elif defined(ARM_MULTILIB_ARCH_V7M)
+ regs[REG_R4] = thread->tcb->Registers.register_r4;
+ regs[REG_R5] = thread->tcb->Registers.register_r5;
+ regs[REG_R6] = thread->tcb->Registers.register_r6;
+ regs[REG_R7] = thread->tcb->Registers.register_r7;
+ regs[REG_R8] = thread->tcb->Registers.register_r8;
+ regs[REG_R9] = thread->tcb->Registers.register_r9;
+ regs[REG_R10] = thread->tcb->Registers.register_r10;
+ regs[REG_R11] = thread->tcb->Registers.register_r11;
+ regs[REG_LR] = (intptr_t) thread->tcb->Registers.register_lr;
+ regs[REG_PC] = (intptr_t) thread->tcb->Registers.register_lr;
+ regs[REG_SP] = (intptr_t) thread->tcb->Registers.register_sp;
+#endif
+ /*
+ * Blocked threads have no signal.
+ */
+ thread->signal = 0;
+ }
+
+ thread->flags |= RTEMS_DEBUGGER_THREAD_FLAG_REG_VALID;
+ thread->flags &= ~RTEMS_DEBUGGER_THREAD_FLAG_REG_DIRTY;
+ }
+
+ return 0;
+}
+
+int
+rtems_debugger_target_write_regs(rtems_debugger_thread* thread)
+{
+ if (rtems_debugger_thread_flag(thread,
+ RTEMS_DEBUGGER_THREAD_FLAG_REG_DIRTY)) {
+ uint32_t* regs = &thread->registers[0];
+
+ /*
+ * Only write to debugger controlled threads. Do not touch the registers
+ * for threads blocked in the context switcher.
+ */
+ if (rtems_debugger_thread_flag(thread,
+ RTEMS_DEBUGGER_THREAD_FLAG_DEBUGGING)) {
+ CPU_Exception_frame* frame = thread->frame;
+ frame->register_r0 = regs[REG_R0];
+ frame->register_r1 = regs[REG_R1];
+ frame->register_r2 = regs[REG_R2];
+ frame->register_r3 = regs[REG_R3];
+ frame->register_r4 = regs[REG_R4];
+ frame->register_r5 = regs[REG_R5];
+ frame->register_r6 = regs[REG_R6];
+ frame->register_r7 = regs[REG_R7];
+ frame->register_r8 = regs[REG_R8];
+ frame->register_r9 = regs[REG_R9];
+ frame->register_r10 = regs[REG_R10];
+ frame->register_r11 = regs[REG_R11];
+ frame->register_r12 = regs[REG_R12];
+ frame->register_sp = regs[REG_SP];
+ frame->register_lr = (void*) regs[REG_LR];
+ frame->register_pc = (void*) regs[REG_PC];
+ FRAME_SR = regs[REG_CPSR];
+ }
+ thread->flags &= ~RTEMS_DEBUGGER_THREAD_FLAG_REG_DIRTY;
+ }
+ return 0;
+}
+
+DB_UINT
+rtems_debugger_target_reg_pc(rtems_debugger_thread* thread)
+{
+ int r;
+ r = rtems_debugger_target_read_regs(thread);
+ if (r >= 0) {
+ uint32_t* regs = &thread->registers[0];
+ return regs[REG_PC];
+ }
+ return 0;
+}
+
+DB_UINT
+rtems_debugger_target_frame_pc(CPU_Exception_frame* frame)
+{
+ return (DB_UINT) frame->register_pc;
+}
+
+DB_UINT
+rtems_debugger_target_reg_sp(rtems_debugger_thread* thread)
+{
+ int r;
+ r = rtems_debugger_target_read_regs(thread);
+ if (r >= 0) {
+ uint32_t* regs = &thread->registers[0];
+ return regs[REG_SP];
+ }
+ return 0;
+}
+
+DB_UINT
+rtems_debugger_target_tcb_sp(rtems_debugger_thread* thread)
+{
+ return (DB_UINT) thread->tcb->Registers.register_sp;
+}
+
+int
+rtems_debugger_target_thread_stepping(rtems_debugger_thread* thread)
+{
+ if (rtems_debugger_thread_flag(thread,
+ (RTEMS_DEBUGGER_THREAD_FLAG_STEP |
+ RTEMS_DEBUGGER_THREAD_FLAG_STEPPING))) {
+ /*
+ * Single stepping and range stepping uses hardware debug breakpoint
+ * 0. This is reserved for single stepping.
+ */
+ CPU_Exception_frame* frame = thread->frame;
+ arm_debug_hwbreak* bp = &hw_breaks[0];
+ int i;
+ for (i = 0; i < hw_breakpoints; ++i, ++bp) {
+ if (!bp->enabled) {
+ const uint32_t addr = (intptr_t) frame->register_pc;
+ const bool thumb = (FRAME_SR & (1 << 5)) != 0 ? true : false;
+ uint32_t bas;
+
+ bp->enabled = true;
+ bp->loaded = false;
+ bp->address = frame->register_pc;
+ bp->frame = frame;
+ bp->length = sizeof(uint32_t);
+
+ if (thumb) {
+ uint16_t instr = *((uint16_t*) frame->register_pc);
+ switch (instr & 0xf800) {
+ case 0xe800:
+ case 0xf000:
+ case 0xf800:
+ break;
+ default:
+ bp->length = sizeof(uint16_t);
+ break;
+ }
+ }
+
+ /*
+ * See table C3-2 Effect of byte address selection on Breakpoint
+ * generation and "Instruction address comparisoin programming
+ * examples.
+ */
+ if (thumb) {
+ if ((addr & (1 << 1)) == 0) {
+ bas = 0x3; /* b0011 */
+ }
+ else {
+ bas = 0xc; /* b1100 */
+ }
+ }
+ else {
+ bas = 0xf; /* b1111 */
+ }
+
+ arm_debug_break_setup(bp,
+ addr & ~0x3,
+ ARM_HW_BP_UNLINKED_INSTR_MISMATCH,
+ bas,
+ ARM_HW_BP_PRIV_PL0_SUP_SYS);
+
+ /*
+ * Save the interrupt state before stepping if set.
+ */
+ if ((FRAME_SR & CPSR_INTS_MASK) != 0) {
+ uint32_t int_state;
+ int_state =
+ (frame->register_cpsr & CPSR_INTS_MASK) << RTEMS_DEBUGGER_THREAD_FLAG_TARGET_BASE;
+ thread->flags |= RTEMS_DEBUGGER_THREAD_FLAG_INTS_DISABLED | int_state;
+ }
+ /*
+ * Mask the interrupt when stepping.
+ */
+ FRAME_SR |= CPSR_INTS_MASK;
+ break;
+ }
+ }
+ }
+ return 0;
+}
+
+int
+rtems_debugger_target_exception_to_signal(CPU_Exception_frame* frame)
+{
+ int sig = RTEMS_DEBUGGER_SIGNAL_HUP;
+ switch (frame->vector) {
+ case ARM_EXCEPTION_RESET:
+ case ARM_EXCEPTION_SWI:
+ sig = RTEMS_DEBUGGER_SIGNAL_TRAP;
+ break;
+ case ARM_EXCEPTION_UNDEF:
+ sig = RTEMS_DEBUGGER_SIGNAL_ILL;
+ break;
+ case ARM_EXCEPTION_FIQ:
+ sig = RTEMS_DEBUGGER_SIGNAL_FPE;
+ break;
+ case ARM_EXCEPTION_PREF_ABORT:
+ case ARM_EXCEPTION_DATA_ABORT:
+ sig = RTEMS_DEBUGGER_SIGNAL_SEGV;
+ break;
+ case ARM_EXCEPTION_RESERVED:
+ case ARM_EXCEPTION_IRQ:
+ sig = RTEMS_DEBUGGER_SIGNAL_BUS;
+ break;
+ default:
+ break;
+ }
+ return sig;
+}
+
+int
+rtems_debugger_target_hwbreak_control(rtems_debugger_target_watchpoint wp,
+ bool insert,
+ DB_UINT addr,
+ DB_UINT kind)
+{
+ /*
+ * To do.
+ */
+ return 0;
+}
+
+int
+rtems_debugger_target_cache_sync(rtems_debugger_target_swbreak* swbreak)
+{
+ /*
+ * Flush the data cache and invalidate the instruction cache.
+ */
+ rtems_cache_flush_multiple_data_lines(swbreak->address,
+ sizeof(breakpoint));
+ rtems_cache_invalidate_multiple_instruction_lines(swbreak->address,
+ sizeof(breakpoint));
+ return 0;
+}