mirror of
https://gitlab.rtems.org/rtems/rtos/rtems.git
synced 2025-11-16 12:34:45 +00:00
2310 lines
76 KiB
C
2310 lines
76 KiB
C
/*
|
|
* Copyright (c) 2016-2022 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
|
|
|
|
#define ARM_DUMP_ROM_TABLES 0
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include <errno.h>
|
|
#include <inttypes.h>
|
|
#include <stdlib.h>
|
|
|
|
#include <rtems.h>
|
|
#include <rtems/score/threadimpl.h>
|
|
|
|
#include <rtems/debugger/rtems-debugger-bsp.h>
|
|
|
|
#include "rtems-debugger-smp.h"
|
|
#include "rtems-debugger-target.h"
|
|
#include "rtems-debugger-threads.h"
|
|
|
|
#if TARGET_DEBUG
|
|
#include <rtems/bspIo.h>
|
|
#endif
|
|
|
|
/*
|
|
* ARM Variant controls.
|
|
*/
|
|
#if (__ARM_ARCH >= 7) && \
|
|
(__ARM_ARCH_PROFILE == 'A' || __ARM_ARCH_PROFILE == 'R')
|
|
#define ARM_CP15 1
|
|
#endif
|
|
|
|
#if (__ARM_ARCH >= 7) && \
|
|
(__ARM_ARCH_PROFILE == 'M')
|
|
#define ARM_THUMB_ONLY 1
|
|
#else
|
|
#define ARM_THUMB_ONLY 0
|
|
#endif
|
|
|
|
#if defined(ARM_MULTILIB_ARCH_V4)
|
|
#define ARM_PSR_HAS_INT_MASK 1
|
|
#define ARM_PSR_HAS_THUMB 1
|
|
#else
|
|
#define ARM_PSR_HAS_INT_MASK 0
|
|
#define ARM_PSR_HAS_THUMB 0
|
|
#endif
|
|
|
|
#if ARM_CP15
|
|
#include <libcpu/arm-cp15.h>
|
|
#endif
|
|
|
|
/**
|
|
* If thumb build of code switch the asm to thumb as required.
|
|
*
|
|
* If the variant only supports thumb insturctions disable the support.
|
|
*/
|
|
#if !ARM_THUMB_ONLY && defined(__thumb__)
|
|
#define ARM_SWITCH_REG uint32_t arm_switch_reg
|
|
#define ARM_SWITCH_REG_ASM [arm_switch_reg] "=&r" (arm_switch_reg)
|
|
#define ARM_SWITCH_REG_ASM_L ARM_SWITCH_REG_ASM,
|
|
#define ASM_ARM_ASM ".align 2\n.arm\n"
|
|
#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_THUMB_MODE() __asm__ volatile(ASM_THUMB_MODE : ARM_SWITCH_REG_ASM : :);
|
|
#define ARM_ARM_MODE() __asm__ volatile(ASM_ARM_MODE : : :);
|
|
#else
|
|
#define ARM_SWITCH_REG
|
|
#define ARM_SWITCH_REG_ASM
|
|
#define ARM_SWITCH_REG_ASM_L
|
|
#define ASM_ARM_ASM
|
|
#define ASM_ARM_MODE
|
|
#define ASM_THUMB_MODE
|
|
#define ARM_THUMB_MODE()
|
|
#define ARM_ARM_MODE()
|
|
#endif
|
|
|
|
#ifdef ARM_MULTILIB_HAS_BARRIER_INSTRUCTIONS
|
|
#define ARM_SYNC_INST "isb\n"
|
|
#else
|
|
#define ARM_SYNC_INST
|
|
#endif
|
|
|
|
/*
|
|
* Hack to work around ARMv7-M not having a the T and I bits in the PSR.
|
|
*
|
|
* This needs to be fixed when real support for this ARM variant is added.
|
|
*/
|
|
#if !defined(ARM_PSR_I)
|
|
#define ARM_PSR_I 0
|
|
#endif
|
|
#if !defined(ARM_PSR_T)
|
|
#define ARM_PSR_T 0
|
|
#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 type of register.
|
|
*/
|
|
#define RTEMS_DEBUGGER_REG_BYTES 4
|
|
#define RTEMS_DEBUGGER_FP_REG_BYTES 12
|
|
|
|
/*
|
|
* Debugger registers layout. See arm-core.xml in GDB source.
|
|
*/
|
|
#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_F0 16
|
|
#define REG_F1 17
|
|
#define REG_F2 18
|
|
#define REG_F3 19
|
|
#define REG_F4 20
|
|
#define REG_F5 21
|
|
#define REG_F6 22
|
|
#define REG_F7 23
|
|
#define REG_FPS 24
|
|
#define REG_CPSR 25
|
|
|
|
/**
|
|
* Register offset table with the total as the last entry.
|
|
*
|
|
* Check this table in gdb with the command:
|
|
*
|
|
* maint print registers
|
|
*/
|
|
static const size_t arm_reg_offsets[RTEMS_DEBUGGER_NUMREGS + 1] =
|
|
{
|
|
0, /* REG_R0 4 uint32_t */
|
|
4, /* REG_R1 4 uint32_t */
|
|
8, /* REG_R2 4 uint32_t */
|
|
12, /* REG_R3 4 uint32_t */
|
|
16, /* REG_R4 4 uint32_t */
|
|
20, /* REG_R5 4 uint32_t */
|
|
24, /* REG_R6 4 uint32_t */
|
|
28, /* REG_R7 4 uint32_t */
|
|
32, /* REG_R8 4 uint32_t */
|
|
36, /* REG_R9 4 uint32_t */
|
|
40, /* REG_R10 4 uint32_t */
|
|
44, /* REG_R11 4 uint32_t */
|
|
48, /* REG_R12 4 uint32_t */
|
|
52, /* REG_SP 4 *1 */
|
|
56, /* REG_LR 4 uint32_t */
|
|
60, /* REG_PC 4 *1 */
|
|
64, /* REG_F0 12 _arm_ext */
|
|
76, /* REG_F1 12 _arm_ext */
|
|
88, /* REG_F2 12 _arm_ext */
|
|
100, /* REG_F3 12 _arm_ext */
|
|
112, /* REG_F4 12 _arm_ext */
|
|
124, /* REG_F5 12 _arm_ext */
|
|
136, /* REG_F6 12 _arm_ext */
|
|
148, /* REG_F7 12 _arm_ext */
|
|
160, /* REG_FPS 4 uint32_t */
|
|
164, /* REG_CPSR 4 uint32_t */
|
|
168 /* total size */
|
|
};
|
|
|
|
/*
|
|
* Number of bytes of registers.
|
|
*/
|
|
#define RTEMS_DEBUGGER_NUMREGBYTES arm_reg_offsets[RTEMS_DEBUGGER_NUMREGS]
|
|
|
|
/**
|
|
* The various status registers.
|
|
*/
|
|
#if defined(ARM_MULTILIB_ARCH_V4) || defined(ARM_MULTILIB_ARCH_V6M)
|
|
#define FRAME_SR(_frame) (_frame)->register_cpsr
|
|
#elif defined(ARM_MULTILIB_ARCH_V7M)
|
|
#define FRAME_SR(_frame) (_frame)->register_xpsr
|
|
#else
|
|
#error ARM architecture is not supported.
|
|
#endif
|
|
|
|
/**
|
|
* Print the exception frame.
|
|
*/
|
|
#define EXC_FRAME_PRINT(_out, _prefix, _frame) \
|
|
do { \
|
|
_out(_prefix " 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); \
|
|
_out(_prefix " 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); \
|
|
_out(_prefix " 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); \
|
|
_out(_prefix " 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); \
|
|
_out(_prefix " 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), \
|
|
(FRAME_SR(_frame) & (1 << 31)) != 0 ? 'N' : '-', \
|
|
(FRAME_SR(_frame) & (1 << 30)) != 0 ? 'Z' : '-', \
|
|
(FRAME_SR(_frame) & (1 << 29)) != 0 ? 'C' : '-', \
|
|
(FRAME_SR(_frame) & (1 << 28)) != 0 ? 'V' : '-', \
|
|
(FRAME_SR(_frame) & (1 << 27)) != 0 ? 'Q' : '-', \
|
|
(FRAME_SR(_frame) & (1 << 24)) != 0 ? 'J' : '-', \
|
|
(FRAME_SR(_frame) & (1 << 9)) != 0 ? 'E' : '-', \
|
|
(FRAME_SR(_frame) & (1 << 8)) != 0 ? 'A' : '-', \
|
|
(FRAME_SR(_frame) & (1 << 7)) != 0 ? 'I' : '-', \
|
|
(FRAME_SR(_frame) & (1 << 6)) != 0 ? 'F' : '-', \
|
|
(FRAME_SR(_frame) & (1 << 5)) != 0 ? 'T' : '-', \
|
|
(FRAME_SR(_frame) >> 16) & 0xf, \
|
|
((FRAME_SR(_frame) >> (25 - 5)) & (0x3 << 5)) | ((FRAME_SR(_frame) >> 10) & 0x1f), \
|
|
FRAME_SR(_frame) & 0x1f, arm_mode_label(FRAME_SR(_frame) & 0x1f)); \
|
|
} while (0)
|
|
|
|
/**
|
|
* The breakpoint.
|
|
*/
|
|
#ifdef __thumb__
|
|
static const uint8_t breakpoint[2] = { 0x55, 0xbe };
|
|
#else
|
|
static const uint8_t breakpoint[4] = { 0x75, 0xe0, 0x20, 0xe1 };
|
|
#endif
|
|
|
|
/**
|
|
* Target lock.
|
|
*/
|
|
RTEMS_INTERRUPT_LOCK_DEFINE(static, target_lock, "target_lock")
|
|
|
|
/**
|
|
* An exception offset is added to the return address of the PC on an
|
|
* exception's stack frame. The PC needs to be adjusted.
|
|
*/
|
|
static const size_t exc_offsets[2][5] =
|
|
{
|
|
/* ARM undef_ins sup call pref abt data abt */
|
|
{ 0, 4, 0, 4, 8 },
|
|
/* TMB undef_ins sup call pref abt data abt */
|
|
{ 0, 2, 0, 4, 8 }
|
|
};
|
|
|
|
/**
|
|
* Is a session active?
|
|
*/
|
|
static bool debug_session_active;
|
|
|
|
/*
|
|
* ARM debug hardware. These variables are directly access
|
|
* from assembler so do not change types.
|
|
*/
|
|
static int debug_version;
|
|
static void* debug_registers;
|
|
static int debug_revision;
|
|
static int debug_disable_ints;
|
|
static int hw_breakpoints;
|
|
static int hw_watchpoints;
|
|
|
|
/**
|
|
* Hardware break and watch points.
|
|
*/
|
|
#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)
|
|
|
|
/*
|
|
* A hw breakpoint has DBGBCR and DBGBVR registers. Allocate memory
|
|
* for each.
|
|
*
|
|
* Maintian the values ready to load into the hardware. The loader is
|
|
* a load of the value and then control for enabled BPs.
|
|
*/
|
|
static uint32_t hw_breaks[ARM_HW_BREAKPOINT_MAX * 2];
|
|
|
|
/*
|
|
* The order in the array is important
|
|
*/
|
|
#define ARM_HWB_BCR(_bp) (hw_breaks[((_bp) * 2) + 1])
|
|
#define ARM_HWB_VCR(_bp) (hw_breaks[(_bp) * 2])
|
|
#define ARM_HWB_ENALBED(_bp) ((ARM_HWB_BCR(_bp) & 1) != 0)
|
|
#define ARM_HWB_CLEAR(_bp) ARM_HWB_BCR(_bp) = 0; ARM_HWB_VCR(_bp) = 0
|
|
#define ARM_HWB_CLEAR_ALL() memset(&hw_breaks[0], 0, sizeof(hw_breaks))
|
|
|
|
/*
|
|
* Method of entry (MOE) to debug mode. Bits [5:2] of DBGDSCR.
|
|
*/
|
|
#define ARM_HW_DSCR_MOE_HALT_REQUEST (0x0)
|
|
#define ARM_HW_DSCR_MOE_BREAKPOINT_EVENT (0x1)
|
|
#define ARM_HW_DSCR_MOE_ASYNC_WATCHPOINT (0x2)
|
|
#define ARM_HW_DSCR_MOE_BREAKPOINT_INSTR (0x3)
|
|
#define ARM_HW_DSCR_MOE_EXTERNAL (0x4)
|
|
#define ARM_HW_DSCR_MOE_VECTOR_CATCH_EVENT (0x5)
|
|
#define ARM_HW_DSCR_MOE_OS_UNLOCK_EVENT (0x8)
|
|
#define ARM_HW_DSCR_MOE_SYNC_WATCHPOINT (0xa)
|
|
|
|
/*
|
|
* Use to locally probe and catch exceptions when accessinf suspect addresses.
|
|
*/
|
|
#if ARM_CP15
|
|
static void __attribute__((naked)) arm_debug_unlock_abort(void);
|
|
#endif
|
|
|
|
/*
|
|
* Target debugging support. Use this to debug the backend.
|
|
*/
|
|
#if TARGET_DEBUG
|
|
void rtems_debugger_printk_lock(rtems_interrupt_lock_context* lock_context);
|
|
void rtems_debugger_printk_unlock(rtems_interrupt_lock_context* lock_context);
|
|
|
|
static void target_printk(const char* format, ...) RTEMS_PRINTFLIKE(1, 2);
|
|
static void
|
|
target_printk(const char* format, ...)
|
|
{
|
|
rtems_interrupt_lock_context lock_context;
|
|
va_list ap;
|
|
va_start(ap, format);
|
|
rtems_debugger_printk_lock(&lock_context);
|
|
vprintk(format, ap);
|
|
rtems_debugger_printk_unlock(&lock_context);
|
|
va_end(ap);
|
|
}
|
|
#else
|
|
#define target_printk(_fmt, ...)
|
|
#define mode_labels(_m) NULL
|
|
#endif
|
|
|
|
static const char*
|
|
arm_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 "---";
|
|
}
|
|
|
|
#if TARGET_DEBUG
|
|
static const char*
|
|
arm_moe_label(uint32_t moe)
|
|
{
|
|
switch (moe) {
|
|
case ARM_HW_DSCR_MOE_HALT_REQUEST:
|
|
return "HLT";
|
|
case ARM_HW_DSCR_MOE_BREAKPOINT_EVENT:
|
|
return "BPE";
|
|
case ARM_HW_DSCR_MOE_ASYNC_WATCHPOINT:
|
|
return "AWP";
|
|
case ARM_HW_DSCR_MOE_BREAKPOINT_INSTR:
|
|
return "BPI";
|
|
case ARM_HW_DSCR_MOE_EXTERNAL:
|
|
return "EXT";
|
|
case ARM_HW_DSCR_MOE_VECTOR_CATCH_EVENT:
|
|
return "VCE";
|
|
case ARM_HW_DSCR_MOE_OS_UNLOCK_EVENT:
|
|
return "OUL";
|
|
case ARM_HW_DSCR_MOE_SYNC_WATCHPOINT:
|
|
return "SWP";
|
|
default:
|
|
break;
|
|
}
|
|
return "RSV";
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* CP register access.
|
|
*/
|
|
#define ARM_CP_INSTR(_opc, _cp, _op1, _val, _CRn, _CRm, _op2) \
|
|
#_opc " p" #_cp ", " #_op1 ", %[" #_val "], c" #_CRn ", c" #_CRm ", " #_op2 "\n"
|
|
|
|
#define ARM_CP_WRITE(_cp, _op1, _val, _CRn, _CRm, _op2) \
|
|
do { \
|
|
ARM_SWITCH_REG; \
|
|
asm volatile( \
|
|
ASM_ARM_MODE \
|
|
ARM_CP_INSTR(mcr, _cp, _op1, val, _CRn, _CRm, _op2) \
|
|
ARM_SYNC_INST \
|
|
ASM_THUMB_MODE \
|
|
: ARM_SWITCH_REG_ASM \
|
|
: [val] "r" (_val)); \
|
|
} while (0)
|
|
|
|
#define ARM_CP_READ(_cp, _op1, _val, _CRn, _CRm, _op2) \
|
|
do { \
|
|
ARM_SWITCH_REG; \
|
|
asm volatile( \
|
|
ASM_ARM_MODE \
|
|
ARM_SYNC_INST \
|
|
ARM_CP_INSTR(mrc, _cp, _op1, val, _CRn, _CRm, _op2) \
|
|
ASM_THUMB_MODE \
|
|
: ARM_SWITCH_REG_ASM_L \
|
|
[val] "=&r" (_val)); \
|
|
} while (0)
|
|
|
|
/*
|
|
* CP14 register access.
|
|
*
|
|
* The registers can be access via the core or they can be memory-mapped.
|
|
*/
|
|
|
|
/*
|
|
* 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_WRITE(_val, _CRn, _CRm, _op2) \
|
|
ARM_CP_WRITE(14, 0, _val, _CRn, _CRm, _op2)
|
|
|
|
#define ARM_CP14_READ(_val, _CRn, _CRm, _op2) \
|
|
ARM_CP_READ(14, 0, _val, _CRn, _CRm, _op2)
|
|
|
|
/*
|
|
* Read and write a CP15 register.
|
|
*
|
|
* The Context ID register is a process level context and used to scope
|
|
* hardware break points.
|
|
*/
|
|
#define ARM_CP15_WRITE(_val, _op1, _CRn, _CRm, _op2) \
|
|
ARM_CP_WRITE(15, _op1, _val, _CRn, _CRm, _op2)
|
|
|
|
#define ARM_CP15_READ(_val, _op1, _CRn, _CRm, _op2) \
|
|
ARM_CP_READ(15, _op1, _val, _CRn, _CRm, _op2)
|
|
|
|
/*
|
|
* Read and write a memory mapped debug register. The register number is a word
|
|
* offset from the base address.
|
|
*/
|
|
#define ARM_MMAP_ADDR(reg) \
|
|
(((volatile uint32_t*) debug_registers) + (reg))
|
|
#define ARM_MMAP_WRITE(reg, val) *ARM_MMAP_ADDR(reg) = (val)
|
|
#define ARM_MMAP_READ(reg) *ARM_MMAP_ADDR(reg)
|
|
#define ARM_MMAP_WRITE_SYNC(reg, val) \
|
|
ARM_MMAP_WRITE(reg, val); \
|
|
_ARM_Data_synchronization_barrier(); \
|
|
_ARM_Instruction_synchronization_barrier()
|
|
|
|
/*
|
|
* Debug hardware breakpoint registers.
|
|
*/
|
|
#define ARM_MMAP_DBGDSCR 34
|
|
#define ARM_MMAP_DBGBCR 80
|
|
#define ARM_MMAP_DBGBVR 64
|
|
|
|
static bool
|
|
arm_debug_authentication(uint32_t dbgauthstatus)
|
|
{
|
|
bool granted = (dbgauthstatus & (1 << 0)) != 0;
|
|
rtems_debugger_printf("rtems-db: arm debug: authentication: %s " \
|
|
"(%s %s %s %s %s %s %s %s)\n",
|
|
granted ? "granted" : "denied",
|
|
(dbgauthstatus & (1 << 0)) == 0 ? "-" : "NSE",
|
|
(dbgauthstatus & (1 << 1)) == 0 ? "-" : "NSI",
|
|
(dbgauthstatus & (1 << 2)) == 0 ? "-" : "NSNE",
|
|
(dbgauthstatus & (1 << 3)) == 0 ? "-" : "NSNI",
|
|
(dbgauthstatus & (1 << 4)) == 0 ? "-" : "SE",
|
|
(dbgauthstatus & (1 << 5)) == 0 ? "-" : "SI",
|
|
(dbgauthstatus & (1 << 6)) == 0 ? "-" : "SNE",
|
|
(dbgauthstatus & (1 << 7)) == 0 ? "-" : "SNI");
|
|
return granted;
|
|
}
|
|
|
|
static int
|
|
arm_debug_cp14_enable(rtems_debugger_target* target)
|
|
{
|
|
uint32_t val;
|
|
ARM_CP14_READ(val, 7, 14, 6);
|
|
if (!arm_debug_authentication(val))
|
|
return -1;
|
|
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 5:
|
|
default:
|
|
ARM_CP14_WRITE(val | (1 << 15), 0, 2, 2);
|
|
break;
|
|
case 4:
|
|
rtems_debugger_printf("rtems-db: arm debug: no cp14 access with version 4\n");
|
|
return -1;
|
|
}
|
|
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;
|
|
}
|
|
}
|
|
rtems_debugger_printf("rtems-db: arm debug: using cp14 register access\n");
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* The write access to the software unlock register can cause an abort. Absorb
|
|
* it.
|
|
*/
|
|
static jmp_buf unlock_abort_jmpbuf;
|
|
static size_t arm_debug_retries;
|
|
|
|
static void
|
|
arm_debug_dump_rom_table(uint32_t* rom, size_t depth)
|
|
{
|
|
uint32_t pidr[7];
|
|
uint32_t cidr[4];
|
|
uint32_t memtype;
|
|
uint32_t pidr4_4KB_count;
|
|
size_t r;
|
|
|
|
static const char *table_class[16] = {
|
|
"reserved",
|
|
"ROM table",
|
|
"reserved", "reserved",
|
|
"reserved",
|
|
"reserved",
|
|
"reserved",
|
|
"reserved",
|
|
"reserved",
|
|
"CoreSight component",
|
|
"reserved",
|
|
"Peripheral Test Block",
|
|
"reserved",
|
|
"OptimoDE DESS",
|
|
"Generic IP component",
|
|
"PrimeCell or System component"
|
|
};
|
|
|
|
#define ROM_READ(b_, o_, r_) b_[((o_) / sizeof(uint32_t)) + (r_)]
|
|
|
|
if (depth > 16) {
|
|
rtems_debugger_printf("]] rom: too deep\n");
|
|
return;
|
|
}
|
|
|
|
for (r = 0; r < 4; ++r)
|
|
pidr[r] = ROM_READ(rom, 0xfe0, r) & 0xff;
|
|
for (r = 0; r < 3; ++r)
|
|
pidr[r + 4] = ROM_READ(rom, 0xfd0, r) & 0xff;
|
|
for (r = 0; r < 4; ++r)
|
|
cidr[r] = ROM_READ(rom, 0xff0, r) & 0xff;
|
|
memtype = ROM_READ(rom, 0xfcc, 0);
|
|
|
|
pidr4_4KB_count = pidr[4] & (((1 << (7 - 4)) - 1) >> 4);
|
|
|
|
rtems_debugger_printf("]] rom = %p\n", rom);
|
|
rtems_debugger_printf(" PIDR: %08x %08x %08x %08x %08x %08x %08x\n",
|
|
pidr[0], pidr[1], pidr[2], pidr[3],
|
|
pidr[4], pidr[5], pidr[6]);
|
|
rtems_debugger_printf(" CIDR: %08x %08x %08x %08x\n",
|
|
cidr[0], cidr[1], cidr[2], cidr[3]);
|
|
rtems_debugger_printf(" 4KB count: %u\n", pidr4_4KB_count);
|
|
|
|
if ((memtype & 0x01) != 0)
|
|
rtems_debugger_printf(" MEMTYPE sys memory present on bus\n");
|
|
else
|
|
rtems_debugger_printf(" MEMTYPE sys memory not present: dedicated debug bus\n");
|
|
|
|
/*
|
|
* Read ROM table entries until we get 0
|
|
*/
|
|
for (r = 0; rom[r] != 0; ++r) {
|
|
uint32_t romentry = rom[r];
|
|
uint32_t c_pidr[7];
|
|
uint32_t c_cidr[4];
|
|
uint32_t* c_base;
|
|
uint32_t table_type;
|
|
size_t i;
|
|
|
|
c_base = (uint32_t*) ((intptr_t) rom + (romentry & 0xFFFFF000));
|
|
|
|
/*
|
|
* Read the IDs.
|
|
*/
|
|
for (i = 0; i < 4; ++i)
|
|
c_pidr[i] = ROM_READ(c_base, 0xfe0, i) & 0xff;
|
|
for (i = 0; i < 3; ++i)
|
|
c_pidr[i + 4] = ROM_READ(c_base, 0xfd0, i) & 0xff;
|
|
for (i = 0; i < 4; ++i)
|
|
c_cidr[i] = ROM_READ(c_base, 0xff0, i) & 0xff;
|
|
|
|
table_type = ROM_READ(c_base, 0xfcc, 0);
|
|
|
|
rtems_debugger_printf(" > Base: %p, start: 0x%" PRIx32 "\n",
|
|
c_base,
|
|
/* component may take multiple 4K pages */
|
|
(uint32_t)((intptr_t) c_base - 0x1000 * (c_pidr[4] >> 4)));
|
|
rtems_debugger_printf(" Class is 0x%x, %s\n",
|
|
(c_cidr[1] >> 4) & 0xf, table_class[(c_cidr[1] >> 4) & 0xf]);
|
|
|
|
if (((c_cidr[1] >> 4) & 0x0f) == 1) {
|
|
arm_debug_dump_rom_table(c_base, depth + 1);
|
|
}
|
|
else if (((c_cidr[1] >> 4) & 0x0f) == 9) {
|
|
const char* major = "reserved";
|
|
const char* subtype = "reserved";
|
|
unsigned minor = (table_type >> 4) & 0x0f;
|
|
|
|
switch (table_type & 0x0f) {
|
|
case 0:
|
|
major = "Miscellaneous";
|
|
switch (minor) {
|
|
case 0:
|
|
subtype = "other";
|
|
break;
|
|
case 4:
|
|
subtype = "Validation component";
|
|
break;
|
|
}
|
|
break;
|
|
case 1:
|
|
major = "Trace Sink";
|
|
switch (minor) {
|
|
case 0:
|
|
subtype = "other";
|
|
break;
|
|
case 1:
|
|
subtype = "Port";
|
|
break;
|
|
case 2:
|
|
subtype = "Buffer";
|
|
break;
|
|
case 3:
|
|
subtype = "Router";
|
|
break;
|
|
}
|
|
break;
|
|
case 2:
|
|
major = "Trace Link";
|
|
switch (minor) {
|
|
case 0:
|
|
subtype = "other";
|
|
break;
|
|
case 1:
|
|
subtype = "Funnel, router";
|
|
break;
|
|
case 2:
|
|
subtype = "Filter";
|
|
break;
|
|
case 3:
|
|
subtype = "FIFO, buffer";
|
|
break;
|
|
}
|
|
break;
|
|
case 3:
|
|
major = "Trace Source";
|
|
switch (minor) {
|
|
case 0:
|
|
subtype = "other";
|
|
break;
|
|
case 1:
|
|
subtype = "Processor";
|
|
break;
|
|
case 2:
|
|
subtype = "DSP";
|
|
break;
|
|
case 3:
|
|
subtype = "Engine/Coprocessor";
|
|
break;
|
|
case 4:
|
|
subtype = "Bus";
|
|
break;
|
|
case 6:
|
|
subtype = "Software";
|
|
break;
|
|
}
|
|
break;
|
|
case 4:
|
|
major = "Debug Control";
|
|
switch (minor) {
|
|
case 0:
|
|
subtype = "other";
|
|
break;
|
|
case 1:
|
|
subtype = "Trigger Matrix";
|
|
break;
|
|
case 2:
|
|
subtype = "Debug Auth";
|
|
break;
|
|
case 3:
|
|
subtype = "Power Requestor";
|
|
break;
|
|
}
|
|
break;
|
|
case 5:
|
|
major = "Debug Logic";
|
|
switch (minor) {
|
|
case 0:
|
|
subtype = "other";
|
|
break;
|
|
case 1:
|
|
subtype = "Processor";
|
|
break;
|
|
case 2:
|
|
subtype = "DSP";
|
|
break;
|
|
case 3:
|
|
subtype = "Engine/Coprocessor";
|
|
break;
|
|
case 4:
|
|
subtype = "Bus";
|
|
break;
|
|
case 5:
|
|
subtype = "Memory";
|
|
}
|
|
break;
|
|
case 6:
|
|
major = "Perfomance Monitor";
|
|
switch (minor) {
|
|
case 0:
|
|
subtype = "other";
|
|
break;
|
|
case 1:
|
|
subtype = "Processor";
|
|
break;
|
|
case 2:
|
|
subtype = "DSP";
|
|
break;
|
|
case 3:
|
|
subtype = "Engine/Coprocessor";
|
|
break;
|
|
case 4:
|
|
subtype = "Bus";
|
|
break;
|
|
case 5:
|
|
subtype = "Memory";
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
rtems_debugger_printf(" Type: 0x%02" PRIx32 ", %s, %s\n",
|
|
table_type & 0xff, major, subtype);
|
|
rtems_debugger_printf(" PID[4..0]: %02x %02x %02x %02x %02x\n",
|
|
c_pidr[4], c_pidr[3], c_pidr[2], c_pidr[1], c_pidr[0]);
|
|
|
|
if (((c_cidr[1] >> 4) & 0x0f) == 1) {
|
|
arm_debug_dump_rom_table(c_base, depth + 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static int
|
|
arm_debug_rom_discover(uint32_t* rom, uint32_t comp, uint32_t** addr, int* index)
|
|
{
|
|
size_t r = 0;
|
|
*addr = 0;
|
|
while ((rom[r] & 1) != 0) {
|
|
uint32_t* c_base = (uint32_t*) ((intptr_t) rom + (rom[r] & 0xfffff000));
|
|
uint32_t c_cid1 = c_base[0xff4 / sizeof(uint32_t)];
|
|
uint32_t type;
|
|
if (((c_cid1 >> 4) & 0x0f) == 1) {
|
|
if (arm_debug_rom_discover(c_base, comp, addr, index))
|
|
return true;
|
|
}
|
|
type = c_base[0xfcc / sizeof(uint32_t)] & 0xff;
|
|
if (comp == type) {
|
|
if (*index > 0)
|
|
--(*index);
|
|
else {
|
|
*addr = c_base;
|
|
return true;
|
|
}
|
|
}
|
|
++r;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static int
|
|
arm_debug_mmap_enable(rtems_debugger_target* target, uint32_t dbgdidr)
|
|
{
|
|
uint32_t val;
|
|
int rc = -1;
|
|
|
|
#if ARM_CP15
|
|
void* abort_handler;
|
|
#endif
|
|
|
|
/*
|
|
* File scope as setjmp/longjmp effect the local stack variables.
|
|
*/
|
|
arm_debug_retries = 5;
|
|
|
|
/*
|
|
* The DBGDSAR (DSAR) is a signed offset from DBGDRAR. Both need to
|
|
* be valid for the debug register address to be valid. Read the
|
|
* DBGRAR first.
|
|
*/
|
|
ARM_CP14_READ(val, 1, 0, 0);
|
|
if ((val & 3) == 3) {
|
|
uint32_t* rom = (uint32_t*) (val & 0xfffff000);
|
|
uint32_t* comp_base = NULL;
|
|
int core = (int) _SMP_Get_current_processor();
|
|
|
|
if (ARM_DUMP_ROM_TABLES)
|
|
arm_debug_dump_rom_table(rom, 0);
|
|
|
|
debug_registers = NULL;
|
|
|
|
if (arm_debug_rom_discover(rom, 0x15, &comp_base, &core)) {
|
|
debug_registers = comp_base;
|
|
rtems_debugger_printf("rtems-db: ram debug: ROM Base: %p\n", comp_base);
|
|
} else {
|
|
ARM_CP14_READ(val, 2, 0, 0);
|
|
if ((val & 3) == 3 ) {
|
|
debug_registers = (void*) ((intptr_t) rom + (val & ~3));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (debug_registers == NULL) {
|
|
debug_registers = rtems_debugger_arm_debug_registers();
|
|
if (debug_registers == NULL) {
|
|
rtems_debugger_printf("rtems-db: arm debug: no valid register map\n");
|
|
return -1;
|
|
}
|
|
rtems_debugger_printf("rtems-db: arm debug: BSP Base: %p\n", debug_registers);
|
|
}
|
|
|
|
/*
|
|
* Make sure the memory mapped registers return the same ID.
|
|
*/
|
|
if (ARM_MMAP_READ(0) != dbgdidr) {
|
|
debug_registers = NULL;
|
|
rtems_debugger_printf("rtems-db: arm debug: debug reg map not verified: " \
|
|
"0x%08x\n", ARM_MMAP_READ(0));
|
|
return -1;
|
|
}
|
|
|
|
if (!arm_debug_authentication(ARM_MMAP_READ(1006)))
|
|
return -1;
|
|
|
|
#if ARM_CP15
|
|
abort_handler =
|
|
arm_cp15_set_exception_handler(ARM_EXCEPTION_DATA_ABORT,
|
|
arm_debug_unlock_abort);
|
|
#endif
|
|
|
|
while (arm_debug_retries-- > 0) {
|
|
if (setjmp(unlock_abort_jmpbuf) == 0) {
|
|
/*
|
|
* If there is a software lock and it is locked unlock it.
|
|
*
|
|
* On the TI am335x this can cause a data abort which we catch and retry
|
|
* which seems to make the debug hardware work.
|
|
*/
|
|
if (ARM_MMAP_READ(1005) == 3) {
|
|
ARM_MMAP_WRITE_SYNC(1004, 0xC5ACCE55);
|
|
}
|
|
/*
|
|
* Are we already in debug mode?
|
|
*/
|
|
val = ARM_MMAP_READ(ARM_MMAP_DBGDSCR);
|
|
if ((val & (1 << 15)) == 0) {
|
|
rtems_debugger_printf("rtems-db: arm debug: enable debug mode\n");
|
|
val = ARM_MMAP_READ(ARM_MMAP_DBGDSCR);
|
|
ARM_MMAP_WRITE_SYNC(ARM_MMAP_DBGDSCR,
|
|
ARM_MMAP_READ(ARM_MMAP_DBGDSCR) | (1 << 15));
|
|
arm_debug_retries = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
#if ARM_CP15
|
|
arm_cp15_set_exception_handler(ARM_EXCEPTION_DATA_ABORT, abort_handler);
|
|
#endif
|
|
|
|
if (arm_debug_retries > 0) {
|
|
rtems_debugger_printf("rtems-db: arm debug: using debug register access\n");
|
|
rc = 0;
|
|
}
|
|
else {
|
|
rtems_debugger_printf("rtems-db: arm debug: cannot enter debug mode\n");
|
|
}
|
|
|
|
val = ARM_MMAP_READ(1006);
|
|
|
|
return rc;
|
|
}
|
|
|
|
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* 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]"
|
|
};
|
|
int rc = -1;
|
|
|
|
#if ARM_CP15
|
|
ARM_CP15_READ(val, 0, 0, 0, 0);
|
|
rtems_debugger_printf("rtems-db: arm core: Architecture: %d Variant: %d " \
|
|
"Implementor: %d Part Number: %d Revision: %d\n",
|
|
(val >> 16) & ((1 << (19 - 16 + 1)) - 1),
|
|
(val >> 20) & ((1 << (23 - 20 + 1)) - 1),
|
|
(val >> 24) & ((1 << (31 - 24 + 1)) - 1),
|
|
(val >> 4) & ((1 << (15 - 4 + 1)) - 1),
|
|
(val >> 0) & ((1 << ( 3 - 0 + 1)) - 1));
|
|
#endif
|
|
|
|
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);
|
|
|
|
if (!rtems_debugger_arm_debug_configure())
|
|
return -1;
|
|
|
|
switch (debug_version) {
|
|
case 1:
|
|
case 2:
|
|
case 3:
|
|
case 5:
|
|
default:
|
|
rc = arm_debug_mmap_enable(target, val);
|
|
if (rc != 0)
|
|
rc = arm_debug_cp14_enable(target);
|
|
break;
|
|
case 4:
|
|
rc = arm_debug_mmap_enable(target, val);
|
|
break;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
static inline void
|
|
arm_debug_break_setup(int bp,
|
|
uint32_t address,
|
|
uint32_t type,
|
|
uint32_t byte_address_select,
|
|
uint32_t privilege)
|
|
{
|
|
ARM_HWB_BCR(bp) = (((type & 0xf) << 20) |
|
|
((byte_address_select & 0xf) << 5) |
|
|
((privilege & 0x3) << 1) | 1);
|
|
ARM_HWB_VCR(bp) = (intptr_t) (address & (~3));
|
|
}
|
|
|
|
static void
|
|
arm_debug_break_c14_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_c14_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 uint32_t
|
|
arm_debug_dbgdscr_read(void)
|
|
{
|
|
uint32_t val;
|
|
if (debug_registers != NULL) {
|
|
val = ARM_MMAP_READ(ARM_MMAP_DBGDSCR);
|
|
}
|
|
else {
|
|
ARM_CP14_READ(val, 0, 1, 0);
|
|
}
|
|
return val;
|
|
}
|
|
|
|
static void
|
|
arm_debug_dbgdscr_write(uint32_t val)
|
|
{
|
|
if (debug_registers != NULL) {
|
|
ARM_MMAP_WRITE_SYNC(ARM_MMAP_DBGDSCR, val);
|
|
}
|
|
else {
|
|
ARM_CP14_WRITE(val, 0, 1, 0);
|
|
}
|
|
}
|
|
|
|
static uint32_t
|
|
arm_debug_method_of_entry(void)
|
|
{
|
|
return (arm_debug_dbgdscr_read() >> 2) & 0xf;
|
|
}
|
|
|
|
static void
|
|
arm_debug_disable_interrupts(void)
|
|
{
|
|
debug_disable_ints = 1;
|
|
}
|
|
|
|
static void
|
|
arm_debug_enable_interrupts(void)
|
|
{
|
|
arm_debug_dbgdscr_write(arm_debug_dbgdscr_read() & ~(1 << 11));
|
|
}
|
|
|
|
static void
|
|
arm_debug_break_clear(int bp)
|
|
{
|
|
rtems_interrupt_lock_context lock_context;
|
|
rtems_interrupt_lock_acquire(&target_lock, &lock_context);
|
|
ARM_HWB_CLEAR(bp);
|
|
rtems_interrupt_lock_release(&target_lock, &lock_context);
|
|
}
|
|
|
|
static void
|
|
arm_debug_break_clear_all(void)
|
|
{
|
|
rtems_interrupt_lock_context lock_context;
|
|
rtems_interrupt_lock_acquire(&target_lock, &lock_context);
|
|
ARM_HWB_CLEAR_ALL();
|
|
rtems_interrupt_lock_release(&target_lock, &lock_context);
|
|
}
|
|
|
|
static inline void
|
|
arm_debug_set_context_id(const uint32_t id)
|
|
{
|
|
#if ARM_CP15
|
|
ARM_CP15_WRITE(id, 0, 13, 0, 1);
|
|
#endif
|
|
}
|
|
|
|
static void
|
|
arm_debug_break_unload(void)
|
|
{
|
|
rtems_interrupt_lock_context lock_context;
|
|
int i;
|
|
rtems_interrupt_lock_acquire(&target_lock, &lock_context);
|
|
if (debug_registers != NULL) {
|
|
for (i = 0; i < hw_breakpoints; ++i) {
|
|
ARM_MMAP_WRITE(ARM_MMAP_DBGBCR + i, 0);
|
|
ARM_MMAP_WRITE(ARM_MMAP_DBGBVR + i, 0);
|
|
}
|
|
} else {
|
|
for (i = 0; i < hw_breakpoints; ++i) {
|
|
arm_debug_break_c14_write_control(i, 0);
|
|
arm_debug_break_c14_write_value(i, 0);
|
|
}
|
|
}
|
|
rtems_interrupt_lock_release(&target_lock, &lock_context);
|
|
}
|
|
|
|
static void
|
|
arm_debug_break_exec_enable(int bp, uintptr_t addr, bool thumb, bool step) {
|
|
uint32_t bas;
|
|
|
|
/*
|
|
* See table C3-2 Effect of byte address selection on Breakpoint
|
|
* generation and "Instruction address comparision programming
|
|
* examples.
|
|
*/
|
|
if (thumb) {
|
|
/*
|
|
* Thumb
|
|
*/
|
|
if ((addr & (1 << 1)) == 0) {
|
|
/*
|
|
* Instruction address: DBGBVR[31:2]:00 BAS: 0bxx11 Mismatch: Miss
|
|
*/
|
|
bas = 0x3; /* bxx11 */
|
|
}
|
|
else {
|
|
/*
|
|
* Instruction address: DBGBVR[31:2]:10 BAS: 0b11xx Mismatch: Miss
|
|
*/
|
|
bas = 0xc; /* b11xx */
|
|
}
|
|
}
|
|
else {
|
|
/*
|
|
* ARM
|
|
*
|
|
* Instruction address: DBGBVR[31:2]:00 BAS: 0b1111 Mismatch: Miss
|
|
*/
|
|
bas = 0xf; /* b1111 */
|
|
}
|
|
|
|
target_printk("[} break: addr:%08x bas:%x thumb:%s\n",
|
|
addr, bas, thumb ? "yes" : "no");
|
|
|
|
arm_debug_break_setup(
|
|
bp,
|
|
addr,
|
|
step ? ARM_HW_BP_UNLINKED_INSTR_MISMATCH : ARM_HW_BP_UNLINKED_INSTR_MATCH,
|
|
bas,
|
|
ARM_HW_BP_PRIV_PL0_SUP_SYS);
|
|
}
|
|
|
|
static void
|
|
arm_debug_break_dump(void)
|
|
{
|
|
#if TARGET_DEBUG
|
|
int i;
|
|
for (i = 0; i < hw_breakpoints; ++i) {
|
|
if (ARM_HWB_ENALBED(i)) {
|
|
target_printk("[} bp: %d: control: %08x addr: %08x\n",
|
|
i, ARM_HWB_BCR(i), ARM_HWB_VCR(i));
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#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_offset = arm_reg_offsets;
|
|
target->breakpoint = &breakpoint[0];
|
|
target->breakpoint_size = sizeof(breakpoint);
|
|
return arm_debug_probe(target);
|
|
}
|
|
|
|
static void
|
|
target_print_frame(CPU_Exception_frame* frame)
|
|
{
|
|
EXC_FRAME_PRINT(target_printk, "[} ", frame);
|
|
}
|
|
|
|
static const size_t
|
|
target_exc_offset(CPU_Exception_frame* frame)
|
|
{
|
|
size_t thumb = (FRAME_SR(frame) & (1 << 5)) == 0 ? 0 : 1;
|
|
return exc_offsets[thumb][frame->vector];
|
|
}
|
|
|
|
static void
|
|
target_exception(CPU_Exception_frame* frame)
|
|
{
|
|
#if TARGET_DEBUG
|
|
#if ARM_CP15
|
|
const uint32_t ifsr = arm_cp15_get_instruction_fault_status();
|
|
const uint32_t dfsr = arm_cp15_get_data_fault_status();
|
|
const void* far = arm_cp15_get_fault_address();
|
|
#else
|
|
const uint32_t ifsr = 0;
|
|
#endif
|
|
const uint32_t mvector = frame->vector;
|
|
const uint32_t dbgdscr = arm_debug_dbgdscr_read();
|
|
#endif
|
|
|
|
const uint32_t moe = arm_debug_method_of_entry();
|
|
const size_t exc_offset = target_exc_offset(frame);
|
|
|
|
switch (moe){
|
|
case ARM_HW_DSCR_MOE_BREAKPOINT_EVENT:
|
|
case ARM_HW_DSCR_MOE_BREAKPOINT_INSTR:
|
|
case ARM_HW_DSCR_MOE_ASYNC_WATCHPOINT:
|
|
case ARM_HW_DSCR_MOE_SYNC_WATCHPOINT:
|
|
frame->vector = 2;
|
|
break;
|
|
case ARM_HW_DSCR_MOE_HALT_REQUEST:
|
|
case ARM_HW_DSCR_MOE_EXTERNAL:
|
|
case ARM_HW_DSCR_MOE_VECTOR_CATCH_EVENT:
|
|
case ARM_HW_DSCR_MOE_OS_UNLOCK_EVENT:
|
|
default:
|
|
break;
|
|
}
|
|
|
|
target_printk("[} > frame = %08" PRIx32 \
|
|
" sig=%d vector=%u (%u) dbgdscr=%08" PRIx32 " moe=%s" \
|
|
" far=%p ifsr=%08" PRIx32 " dfsr=%08" PRIx32
|
|
" exc-ret-pc=%08x\n", (uint32_t) frame,
|
|
rtems_debugger_target_exception_to_signal(frame),
|
|
frame->vector, mvector, dbgdscr, arm_moe_label(moe),
|
|
far, ifsr, dfsr, (intptr_t) frame->register_pc);
|
|
|
|
arm_debug_break_dump();
|
|
|
|
frame->register_pc = (void*) ((intptr_t) frame->register_pc - exc_offset);
|
|
|
|
target_print_frame(frame);
|
|
|
|
arm_debug_break_clear(0);
|
|
arm_debug_enable_interrupts();
|
|
|
|
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:
|
|
break;
|
|
case rtems_debugger_target_exc_cascade:
|
|
target_printk("rtems-db: unhandled exception: cascading\n");
|
|
_ARM_Exception_default(frame);
|
|
break;
|
|
}
|
|
|
|
target_printk("[} < resuming frame = %08" PRIx32 \
|
|
" PC = %08" PRIxPTR " CPSR = %08" PRIx32 "\n",
|
|
(uint32_t) frame, (intptr_t) frame->register_pc, FRAME_SR(frame));
|
|
target_print_frame(frame);
|
|
}
|
|
|
|
/**
|
|
* Exception Handlers
|
|
*
|
|
* The entry and exit is all assembler and ARM code. This avoids any
|
|
* compiler related optimisations effecting the various pieces.
|
|
*/
|
|
|
|
/**
|
|
* 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 /* force ARM mode for thumb systems */ \
|
|
"sub sp, %[frame_size]\n" /* alloc the frame and CPSR */ \
|
|
"stm sp, {r0-r12}\n" /* store r0-r12 */ \
|
|
: \
|
|
: [frame_size] "i" (EXCEPTION_FRAME_SIZE) \
|
|
: "cc", "memory")
|
|
|
|
/**
|
|
* Debugger entry
|
|
*
|
|
* Check if using debug registers else use CP14.
|
|
*
|
|
* Set all the break point registers to 0. Enable interrupts.
|
|
*/
|
|
#if ARM_CP15
|
|
#define ARM_HW_BP_UNLOAD(_bp) \
|
|
"cmp r0, #" #_bp "\n" \
|
|
"ble 3f\n" \
|
|
"mcr p14, 0, r1, c0, c" #_bp ", 5\n" \
|
|
"mcr p14, 0, r1, c0, c" #_bp ", 4\n"
|
|
#define ARM_DGB_ENABLE_INTS \
|
|
"mrc p14, 0, r1, c0, c1, 0\n" /* Get the DBGDSCR */ \
|
|
"bic r1, r1, #(1 << 11)\n" /* enable interrupts */ \
|
|
"mcr p14, 0, r1, c0, c1, 0\n" /* Set the DBGDSCR */
|
|
#else
|
|
#define ARM_HW_BP_UNLOAD(_bp)
|
|
#define ARM_DGB_ENABLE_INTS
|
|
#endif
|
|
|
|
#define EXCEPTION_ENTRY_DEBUGGER() \
|
|
__asm__ volatile( \
|
|
/* Set up r0 and r1 */ \
|
|
"movw r0, #:lower16:hw_breakpoints\n" /* get the num hw breaks */ \
|
|
"movt r0, #:upper16:hw_breakpoints\n" \
|
|
"ldr r0, [r0]\n" /* r0 = hw_breakpoints */ \
|
|
"mov r1, #0\n" /* write zero */ \
|
|
/* Check if debug registers are being used */ \
|
|
"movw r2, #:lower16:debug_registers\n" /* get the debug regs */ \
|
|
"movt r2, #:upper16:debug_registers\n" \
|
|
"ldr r2, [r2]\n" /* r2 = debug_registers */ \
|
|
"cmp r2, #0\n" /* NULL? */ \
|
|
"beq 2f\n" /* if NULL use cp14 */ \
|
|
/* Debug registers */ \
|
|
"add r3, r2, %[dbgbvr] - 4\n" /* a3 = DBGBCR0, adjusted */ \
|
|
"add r2, r2, %[dbgbcr] - 4\n" /* a2 = DBGBVR0, adjusted */ \
|
|
"1:\n" \
|
|
"str r1, [r3, #4]!\n" /* Store DBGBVR, pre-indexed, modified */ \
|
|
"str r1, [r2, #4]!\n" /* Store DBGBCR, pre-indexed, modified */ \
|
|
"sub r0, r0, #1\n" /* one less */ \
|
|
"cmp r0, #0\n" /* all done? */ \
|
|
"bne 1b\n" \
|
|
"ldr r1, [r2, %[dbgdscr]]\n" /* Get the DBGDSCR */ \
|
|
"bic r1, r1, #(1 << 11)\n" /* enable interrupts */ \
|
|
"str r1, [r2, %[dbgdscr]]\n" /* Set the DBGDSCR */ \
|
|
"b 4f\n" \
|
|
/* CP14 */ \
|
|
"2:\n" \
|
|
ARM_HW_BP_UNLOAD(0) \
|
|
ARM_HW_BP_UNLOAD(1) \
|
|
ARM_HW_BP_UNLOAD(2) \
|
|
ARM_HW_BP_UNLOAD(3) \
|
|
ARM_HW_BP_UNLOAD(4) \
|
|
ARM_HW_BP_UNLOAD(5) \
|
|
ARM_HW_BP_UNLOAD(6) \
|
|
ARM_HW_BP_UNLOAD(7) \
|
|
ARM_HW_BP_UNLOAD(8) \
|
|
ARM_HW_BP_UNLOAD(9) \
|
|
ARM_HW_BP_UNLOAD(10) \
|
|
ARM_HW_BP_UNLOAD(11) \
|
|
ARM_HW_BP_UNLOAD(12) \
|
|
ARM_HW_BP_UNLOAD(12) \
|
|
ARM_HW_BP_UNLOAD(13) \
|
|
ARM_HW_BP_UNLOAD(14) \
|
|
ARM_HW_BP_UNLOAD(15) \
|
|
"3:\n" \
|
|
ARM_DGB_ENABLE_INTS \
|
|
"4:\n" \
|
|
ARM_SYNC_INST \
|
|
: \
|
|
: [dbgdscr] "i" (ARM_MMAP_DBGDSCR * sizeof(uint32_t)), \
|
|
[dbgbcr] "i" (ARM_MMAP_DBGBCR * sizeof(uint32_t)), \
|
|
[dbgbvr] "i" (ARM_MMAP_DBGBVR * sizeof(uint32_t)) \
|
|
: "cc", "r0", "r1", "r2", "r3", "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" \
|
|
"1:\n" \
|
|
"stmia r5!, {r3-r4}\n" \
|
|
"cmp r5, r6\n" \
|
|
"bne 1b\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 ARM_CLEAR_THUMB_MODE "bic r1, r1, %[psr_t]\n" /* clear thumb */
|
|
|
|
#define EXCEPTION_ENTRY_THREAD(_frame) \
|
|
__asm__ volatile( \
|
|
"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 */ \
|
|
ARM_CLEAR_THUMB_MODE /* clear thumb mode */ \
|
|
"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: sp,lr,pc,cpsr */ \
|
|
"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) \
|
|
"bic r1, r1, %[psr_i]\n" /* clear irq mask, debug checks */ \
|
|
"msr cpsr, r1\n" /* restore the state with irq mask clear */ \
|
|
: \
|
|
[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) \
|
|
: "cc", "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( \
|
|
"mov r0, %[i_frame]\n" /* get the frame */ \
|
|
"ldr r0, [r0, %[frame_fpu]]\n" /* recover aligned FPU frame ptr */ \
|
|
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" \
|
|
"mov r0, %[i_frame]\n" /* get the frame */ \
|
|
"add r1, r0, %[r0_r12_size]\n" /* get the sp in the frame */ \
|
|
"ldm r1, {r3-r6}\n" /* recover sp, lr, pc, cpsr */ \
|
|
"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 */ \
|
|
"mov lr, r5\n" /* get the PC */ \
|
|
: \
|
|
: [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) \
|
|
: "cc", "r0", "r1", "r2", "r3", "r4", "r5", "r6", "memory")
|
|
|
|
/*
|
|
* Debugger exit
|
|
*
|
|
* Check if using debug registers else use CP14.
|
|
*
|
|
* Set all the break point registers to settgins. Disable interrupts
|
|
* if debug_disable_ints is true. Clear debug_disable_ints.
|
|
*/
|
|
#if ARM_CP15
|
|
#define ARM_HW_BP_LOAD(_bp) \
|
|
"cmp r0, #" #_bp "\n" \
|
|
"ble 5f\n" \
|
|
"ldm r1!, {r2-r3}\n" \
|
|
"mcr p14, 0, r2, c0, c" #_bp ", 4\n" /* value */ \
|
|
"mcr p14, 0, r3, c0, c" #_bp ", 5\n" /* control */
|
|
#define ARM_DGB_DISABLE_INTS \
|
|
"mrc p14, 0, r4, c0, c1, 0\n" /* Get the DBGDSCR */ \
|
|
"orr r4, r4, #(1 << 11)\n" /* disable interrupts */ \
|
|
"mcr p14, 0, r4, c0, c1, 0\n" /* Set the DBGDSCR */
|
|
#else
|
|
#define ARM_HW_BP_LOAD(_bp)
|
|
#define ARM_DGB_DISABLE_INTS
|
|
#endif
|
|
|
|
#define EXCEPTION_EXIT_DEBUGGER() \
|
|
__asm__ volatile( \
|
|
/* Set up r0, r1, r4 and r5 */ \
|
|
"movw r0, #:lower16:hw_breakpoints\n" /* get the num hw breaks */ \
|
|
"movt r0, #:upper16:hw_breakpoints\n" \
|
|
"ldr r0, [r0]\n" /* r0 = hw_breakpoints */ \
|
|
"movw r1, #:lower16:hw_breaks\n" /* get the hw_breaks pointer */ \
|
|
"movt r1, #:upper16:hw_breaks\n" \
|
|
"movw r4, #:lower16:debug_disable_ints\n" /* get disable ints */ \
|
|
"movt r4, #:upper16:debug_disable_ints\n" \
|
|
"ldr r5, [r4]\n" \
|
|
"mov r3, #0\n" /* clear debug ints */ \
|
|
"str r3, [r4]\n" \
|
|
/* Check if debug registers are being used */ \
|
|
"movw r2, #:lower16:debug_registers\n" /* get the debug regs */ \
|
|
"movt r2, #:upper16:debug_registers\n" \
|
|
"ldr r2, [r2]\n" /* r2 = debug_registers */ \
|
|
"cmp r2, #0\n" /* NULL? */ \
|
|
"beq 3f\n" /* if NULL use cp14 */ \
|
|
/* Debug registers */ \
|
|
"cmp r5, #0\n" /* false? */ \
|
|
"beq 1f\n" /* if false do not set ints disable */ \
|
|
"ldr r4, [r2, %[dbgdscr]]\n" /* Get the DBGDSCR */ \
|
|
"orr r4, r4, #(1 << 11)\n" /* disable interrupts */ \
|
|
"str r4, [r2, %[dbgdscr]]\n" /* Set the DBGDSCR */ \
|
|
"1:\n" \
|
|
"add r3, r2, %[dbgbvr] - 4\n" /* a3 = DBGBCR0, adjusted */ \
|
|
"add r2, r2, %[dbgbcr] - 4\n" /* a2 = DBGBVR0, adjusted */ \
|
|
"2:\n" \
|
|
"ldm r1!, {r4-r5}\n" /* load vr and cr */ \
|
|
"str r4, [r3, #4]!\n" /* Store DBGBVR, pre-indexed, modified */ \
|
|
"str r5, [r2, #4]!\n" /* Store DBGBCR, pre-indexed, modified */ \
|
|
"sub r0, r0, #1\n" /* one less? */ \
|
|
"cmp r0, #1\n" /* all done? */ \
|
|
"bne 2b\n" \
|
|
"b 5f\n" \
|
|
/* CP14 */ \
|
|
"3:\n" \
|
|
"cmp r5, #0\n" /* false? */ \
|
|
"beq 4f\n" /* if false do not set ints disable */ \
|
|
ARM_DGB_DISABLE_INTS \
|
|
"4:\n" \
|
|
ARM_HW_BP_LOAD(0) \
|
|
ARM_HW_BP_LOAD(1) \
|
|
ARM_HW_BP_LOAD(2) \
|
|
ARM_HW_BP_LOAD(3) \
|
|
ARM_HW_BP_LOAD(4) \
|
|
ARM_HW_BP_LOAD(5) \
|
|
ARM_HW_BP_LOAD(6) \
|
|
ARM_HW_BP_LOAD(7) \
|
|
ARM_HW_BP_LOAD(8) \
|
|
ARM_HW_BP_LOAD(9) \
|
|
ARM_HW_BP_LOAD(10) \
|
|
ARM_HW_BP_LOAD(11) \
|
|
ARM_HW_BP_LOAD(12) \
|
|
ARM_HW_BP_LOAD(13) \
|
|
ARM_HW_BP_LOAD(14) \
|
|
ARM_HW_BP_LOAD(15) \
|
|
"5:\n" \
|
|
ARM_SYNC_INST \
|
|
: \
|
|
: [disints] "X" (debug_disable_ints), /* make the sym available */ \
|
|
[dbgdscr] "i" (ARM_MMAP_DBGDSCR * sizeof(uint32_t)), \
|
|
[dbgbcr] "i" (ARM_MMAP_DBGBCR * sizeof(uint32_t)), \
|
|
[dbgbvr] "i" (ARM_MMAP_DBGBVR * sizeof(uint32_t)) \
|
|
: "cc", "r0", "r1", "r2", "r3", "r4", "r5", "memory")
|
|
|
|
#define EXCEPTION_EXIT_EXC() \
|
|
__asm__ volatile( \
|
|
"ldm sp, {r0-r12}\n" /* restore the thread's context */ \
|
|
"add sp, %[frame_size]\n" /* free the frame */ \
|
|
"subs pc, lr, #0\n" /* return from the exc */ \
|
|
ARM_SYNC_INST \
|
|
: \
|
|
: [frame_size] "i" (EXCEPTION_FRAME_SIZE) \
|
|
: "cc", "memory")
|
|
|
|
#define ARM_PUSH_LR() \
|
|
__asm__ volatile( \
|
|
"push {lr}\n" \
|
|
: : : )
|
|
|
|
#define ARM_POP_LR() \
|
|
__asm__ volatile( \
|
|
"pop {lr}\n" \
|
|
: : : )
|
|
|
|
/*
|
|
* Entry and exit stacks
|
|
*/
|
|
#define EXCEPTION_ENTRY(_frame) \
|
|
EXCEPTION_ENTRY_EXC(); \
|
|
EXCEPTION_ENTRY_DEBUGGER(); \
|
|
EXCEPTION_ENTRY_THREAD(_frame); \
|
|
ARM_THUMB_MODE() \
|
|
ARM_PUSH_LR()
|
|
|
|
#define EXCEPTION_EXIT(_frame) \
|
|
ARM_POP_LR(); \
|
|
ARM_ARM_MODE(); \
|
|
EXCEPTION_EXIT_THREAD(_frame); \
|
|
EXCEPTION_EXIT_DEBUGGER(); \
|
|
EXCEPTION_EXIT_EXC()
|
|
|
|
/*
|
|
* This is used to catch faulting accesses.
|
|
*/
|
|
#if ARM_CP15
|
|
static void __attribute__((naked))
|
|
arm_debug_unlock_abort(void)
|
|
{
|
|
CPU_Exception_frame* frame;
|
|
ARM_SWITCH_REGISTERS;
|
|
EXCEPTION_ENTRY_EXC();
|
|
EXCEPTION_ENTRY_THREAD(frame);
|
|
ARM_SWITCH_BACK;
|
|
longjmp(unlock_abort_jmpbuf, -1);
|
|
}
|
|
#endif
|
|
|
|
static void __attribute__((naked))
|
|
target_exception_undefined_instruction(void)
|
|
{
|
|
CPU_Exception_frame* frame;
|
|
ARM_SWITCH_REG;
|
|
EXCEPTION_ENTRY(frame);
|
|
frame->vector = 1;
|
|
target_exception(frame);
|
|
EXCEPTION_EXIT(frame);
|
|
}
|
|
|
|
static void __attribute__((naked))
|
|
target_exception_supervisor_call(void)
|
|
{
|
|
CPU_Exception_frame* frame;
|
|
ARM_SWITCH_REG;
|
|
/*
|
|
* The PC offset needs to be reviewed so we move past a svc
|
|
* instruction. This can then be 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(frame);
|
|
frame->vector = 2;
|
|
target_exception(frame);
|
|
EXCEPTION_EXIT(frame);
|
|
}
|
|
|
|
static void __attribute__((naked))
|
|
target_exception_prefetch_abort(void)
|
|
{
|
|
CPU_Exception_frame* frame;
|
|
ARM_SWITCH_REG;
|
|
EXCEPTION_ENTRY(frame);
|
|
frame->vector = 3;
|
|
target_exception(frame);
|
|
EXCEPTION_EXIT(frame);
|
|
}
|
|
|
|
static void __attribute__((naked))
|
|
target_exception_data_abort(void)
|
|
{
|
|
CPU_Exception_frame* frame;
|
|
ARM_SWITCH_REG;
|
|
EXCEPTION_ENTRY(frame);
|
|
frame->vector = 4;
|
|
target_exception(frame);
|
|
EXCEPTION_EXIT(frame);
|
|
}
|
|
|
|
#if ARM_CP15
|
|
#if __ARM_ARCH_PROFILE == 'A'
|
|
/**
|
|
* The init value for the text section.
|
|
*/
|
|
static uint32_t text_section_flags;
|
|
|
|
/* Defined by linkcmds.base */
|
|
extern char bsp_section_text_begin[];
|
|
extern char bsp_section_text_end[];
|
|
|
|
static void
|
|
rtems_debugger_target_set_mmu(void)
|
|
{
|
|
void* text_begin;
|
|
void* text_end;
|
|
text_begin = &bsp_section_text_begin[0];
|
|
text_end = &bsp_section_text_end[0];
|
|
target_printk("[} MMU edit: text_begin: %p text_end: %p\n",
|
|
text_begin, text_end);
|
|
text_section_flags =
|
|
arm_cp15_set_translation_table_entries(text_begin,
|
|
text_end,
|
|
ARMV7_MMU_DATA_READ_WRITE_CACHED);
|
|
}
|
|
#else
|
|
static void
|
|
rtems_debugger_target_set_mmu(void)
|
|
{
|
|
}
|
|
#endif
|
|
|
|
static void
|
|
rtems_debugger_target_set_vectors(void)
|
|
{
|
|
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);
|
|
}
|
|
#else
|
|
static void
|
|
rtems_debugger_target_set_vectors(void)
|
|
{
|
|
/*
|
|
* Dummy, please add support for your ARM variant.
|
|
*/
|
|
void* ui = target_exception_undefined_instruction;
|
|
void* sc = target_exception_supervisor_call;
|
|
void* pa = target_exception_prefetch_abort;
|
|
void* da = target_exception_data_abort;
|
|
(void) ui;
|
|
(void) sc;
|
|
(void) pa;
|
|
(void) da;
|
|
}
|
|
|
|
static void
|
|
rtems_debugger_target_set_mmu(void)
|
|
{
|
|
}
|
|
#endif
|
|
|
|
static bool
|
|
rtems_debugger_is_int_reg(size_t reg)
|
|
{
|
|
const size_t size = arm_reg_offsets[reg + 1] - arm_reg_offsets[reg];
|
|
return size == RTEMS_DEBUGGER_REG_BYTES;
|
|
}
|
|
|
|
static void
|
|
rtems_debugger_set_int_reg(rtems_debugger_thread* thread,
|
|
size_t reg,
|
|
const uint32_t value)
|
|
{
|
|
const size_t offset = arm_reg_offsets[reg];
|
|
/*
|
|
* Use memcpy to avoid alignment issues.
|
|
*/
|
|
memcpy(&thread->registers[offset], &value, sizeof(uint32_t));
|
|
}
|
|
|
|
static const uint32_t
|
|
rtems_debugger_get_int_reg(rtems_debugger_thread* thread, size_t reg)
|
|
{
|
|
const size_t offset = arm_reg_offsets[reg];
|
|
uint32_t value;
|
|
memcpy(&value, &thread->registers[offset], sizeof(uint32_t));
|
|
return value;
|
|
}
|
|
|
|
static rtems_task
|
|
rtems_debugger_setup_on_cpu(rtems_task_argument arg)
|
|
{
|
|
(void) arg;
|
|
rtems_debugger_target_set_mmu();
|
|
rtems_debugger_target_set_vectors();
|
|
}
|
|
|
|
int
|
|
rtems_debugger_target_enable(void)
|
|
{
|
|
rtems_status_code sc;
|
|
rtems_status_code error = RTEMS_SUCCESSFUL;
|
|
arm_debug_break_unload();
|
|
arm_debug_break_clear_all();
|
|
sc = rtems_debugger_cpu_run_all(
|
|
rtems_debugger_setup_on_cpu, (rtems_task_argument) &error);
|
|
if (sc != RTEMS_SUCCESSFUL) {
|
|
return -1;
|
|
}
|
|
if (error != RTEMS_SUCCESSFUL) {
|
|
return -1;
|
|
}
|
|
debug_session_active = true;
|
|
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
|
|
arm_debug_break_unload();
|
|
arm_debug_break_clear_all();
|
|
rtems_interrupt_lock_acquire(&target_lock, &lock_context);
|
|
debug_disable_ints = 0;
|
|
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;
|
|
int i;
|
|
|
|
memset(&thread->registers[0], 0, RTEMS_DEBUGGER_NUMREGBYTES);
|
|
|
|
for (i = 0; i < RTEMS_DEBUGGER_NUMREGS; ++i) {
|
|
if (rtems_debugger_is_int_reg(i))
|
|
rtems_debugger_set_int_reg(thread, 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(frame) &= ~CPSR_INTS_MASK;
|
|
|
|
if (rtems_debugger_thread_flag(thread,
|
|
RTEMS_DEBUGGER_THREAD_FLAG_INTS_DISABLED)) {
|
|
FRAME_SR(frame) |=
|
|
(thread->flags >> RTEMS_DEBUGGER_THREAD_FLAG_TARGET_BASE) & CPSR_INTS_MASK;
|
|
thread->flags &= ~RTEMS_DEBUGGER_THREAD_FLAG_INTS_DISABLED;
|
|
}
|
|
|
|
rtems_debugger_set_int_reg(thread, REG_R0, frame->register_r0);
|
|
rtems_debugger_set_int_reg(thread, REG_R1, frame->register_r1);
|
|
rtems_debugger_set_int_reg(thread, REG_R2, frame->register_r2);
|
|
rtems_debugger_set_int_reg(thread, REG_R3, frame->register_r3);
|
|
rtems_debugger_set_int_reg(thread, REG_R4, frame->register_r4);
|
|
rtems_debugger_set_int_reg(thread, REG_R5, frame->register_r5);
|
|
rtems_debugger_set_int_reg(thread, REG_R6, frame->register_r6);
|
|
rtems_debugger_set_int_reg(thread, REG_R7, frame->register_r7);
|
|
rtems_debugger_set_int_reg(thread, REG_R8, frame->register_r8);
|
|
rtems_debugger_set_int_reg(thread, REG_R9, frame->register_r9);
|
|
rtems_debugger_set_int_reg(thread, REG_R10, frame->register_r10);
|
|
rtems_debugger_set_int_reg(thread, REG_R11, frame->register_r11);
|
|
rtems_debugger_set_int_reg(thread, REG_R12, frame->register_r12);
|
|
rtems_debugger_set_int_reg(thread, REG_SP, frame->register_sp);
|
|
rtems_debugger_set_int_reg(thread, REG_LR, (uint32_t) frame->register_lr);
|
|
rtems_debugger_set_int_reg(thread, REG_PC, (uint32_t) frame->register_pc);
|
|
rtems_debugger_set_int_reg(thread, REG_CPSR, FRAME_SR(frame));
|
|
/*
|
|
* Get the signal from the frame.
|
|
*/
|
|
thread->signal = rtems_debugger_target_exception_to_signal(frame);
|
|
}
|
|
else {
|
|
#if defined(ARM_MULTILIB_ARCH_V4)
|
|
rtems_debugger_set_int_reg(thread, REG_R4, thread->tcb->Registers.register_r4);
|
|
rtems_debugger_set_int_reg(thread, REG_R5, thread->tcb->Registers.register_r5);
|
|
rtems_debugger_set_int_reg(thread, REG_R6, thread->tcb->Registers.register_r6);
|
|
rtems_debugger_set_int_reg(thread, REG_R7, thread->tcb->Registers.register_r7);
|
|
rtems_debugger_set_int_reg(thread, REG_R8, thread->tcb->Registers.register_r8);
|
|
rtems_debugger_set_int_reg(thread, REG_R9, thread->tcb->Registers.register_r9);
|
|
rtems_debugger_set_int_reg(thread, REG_R10, thread->tcb->Registers.register_r10);
|
|
rtems_debugger_set_int_reg(thread, REG_R11, thread->tcb->Registers.register_fp);
|
|
rtems_debugger_set_int_reg(thread, REG_LR, (intptr_t) thread->tcb->Registers.register_lr);
|
|
rtems_debugger_set_int_reg(thread, REG_PC, (intptr_t) thread->tcb->Registers.register_lr);
|
|
rtems_debugger_set_int_reg(thread, REG_SP, (intptr_t) thread->tcb->Registers.register_sp);
|
|
#elif defined(ARM_MULTILIB_ARCH_V7M)
|
|
rtems_debugger_set_int_reg(thread, REG_R4, thread->tcb->Registers.register_r4);
|
|
rtems_debugger_set_int_reg(thread, REG_R5, thread->tcb->Registers.register_r5);
|
|
rtems_debugger_set_int_reg(thread, REG_R6, thread->tcb->Registers.register_r6);
|
|
rtems_debugger_set_int_reg(thread, REG_R7, thread->tcb->Registers.register_r7);
|
|
rtems_debugger_set_int_reg(thread, REG_R8, thread->tcb->Registers.register_r8);
|
|
rtems_debugger_set_int_reg(thread, REG_R9, thread->tcb->Registers.register_r9);
|
|
rtems_debugger_set_int_reg(thread, REG_R10, thread->tcb->Registers.register_r10);
|
|
rtems_debugger_set_int_reg(thread, REG_R11, thread->tcb->Registers.register_r11);
|
|
rtems_debugger_set_int_reg(thread, REG_LR, (intptr_t) thread->tcb->Registers.register_lr);
|
|
rtems_debugger_set_int_reg(thread, REG_PC, (intptr_t) thread->tcb->Registers.register_lr);
|
|
rtems_debugger_set_int_reg(thread, 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)) {
|
|
/*
|
|
* Only write to debugger controlled exception threads. Do not touch the
|
|
* registers for threads blocked in the context switcher.
|
|
*/
|
|
if (rtems_debugger_thread_flag(thread,
|
|
RTEMS_DEBUGGER_THREAD_FLAG_EXCEPTION)) {
|
|
CPU_Exception_frame* frame = thread->frame;
|
|
frame->register_r0 = rtems_debugger_get_int_reg(thread, REG_R0);
|
|
frame->register_r1 = rtems_debugger_get_int_reg(thread, REG_R1);
|
|
frame->register_r2 = rtems_debugger_get_int_reg(thread, REG_R2);
|
|
frame->register_r3 = rtems_debugger_get_int_reg(thread, REG_R3);
|
|
frame->register_r4 = rtems_debugger_get_int_reg(thread, REG_R4);
|
|
frame->register_r5 = rtems_debugger_get_int_reg(thread, REG_R5);
|
|
frame->register_r6 = rtems_debugger_get_int_reg(thread, REG_R6);
|
|
frame->register_r7 = rtems_debugger_get_int_reg(thread, REG_R7);
|
|
frame->register_r8 = rtems_debugger_get_int_reg(thread, REG_R8);
|
|
frame->register_r9 = rtems_debugger_get_int_reg(thread, REG_R9);
|
|
frame->register_r10 = rtems_debugger_get_int_reg(thread, REG_R10);
|
|
frame->register_r11 = rtems_debugger_get_int_reg(thread, REG_R11);
|
|
frame->register_r12 = rtems_debugger_get_int_reg(thread, REG_R12);
|
|
frame->register_sp = rtems_debugger_get_int_reg(thread, REG_SP);
|
|
frame->register_lr = (void*) rtems_debugger_get_int_reg(thread, REG_LR);
|
|
frame->register_pc = (void*) rtems_debugger_get_int_reg(thread, REG_PC);
|
|
FRAME_SR(frame) = rtems_debugger_get_int_reg(thread, 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) {
|
|
return rtems_debugger_get_int_reg(thread, 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) {
|
|
return rtems_debugger_get_int_reg(thread, 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_INSTR)) {
|
|
/*
|
|
* Single stepping and range stepping uses hardware debug breakpoint
|
|
* 0. This is reserved for single stepping.
|
|
*/
|
|
CPU_Exception_frame* frame = thread->frame;
|
|
|
|
target_printk("[} stepping: hbp[0] enabled: %s\n", ARM_HWB_ENALBED(0) ? "yes" : "no");
|
|
|
|
if (!ARM_HWB_ENALBED(0)) {
|
|
const uint32_t addr = (intptr_t) frame->register_pc;
|
|
const bool thumb = (FRAME_SR(frame) & (1 << 5)) != 0 ? true : false;
|
|
arm_debug_break_exec_enable(0, addr, thumb, true);
|
|
arm_debug_disable_interrupts();
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
rtems_debugger_target_exception_to_signal(CPU_Exception_frame* frame)
|
|
{
|
|
int sig = RTEMS_DEBUGGER_SIGNAL_HUP;
|
|
#if defined(ARM_MULTILIB_ARCH_V4)
|
|
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;
|
|
}
|
|
#endif
|
|
return sig;
|
|
}
|
|
|
|
void
|
|
rtems_debugger_target_exception_print(CPU_Exception_frame* frame)
|
|
{
|
|
EXC_FRAME_PRINT(rtems_debugger_printf, "", frame);
|
|
}
|
|
|
|
int
|
|
rtems_debugger_target_hwbreak_insert(void)
|
|
{
|
|
/*
|
|
* Do nothing, load on exit of the exception handler.
|
|
*/
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
rtems_debugger_target_hwbreak_remove(void)
|
|
{
|
|
target_printk("[} hbreak: remove: unload\n");
|
|
arm_debug_break_unload();
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
rtems_debugger_target_hwbreak_control(rtems_debugger_target_watchpoint wp,
|
|
bool insert,
|
|
DB_UINT addr,
|
|
DB_UINT kind)
|
|
{
|
|
rtems_interrupt_lock_context lock_context;
|
|
int i;
|
|
if (wp != rtems_debugger_target_hw_execute) {
|
|
errno = EIO;
|
|
return -1;
|
|
}
|
|
rtems_interrupt_lock_acquire(&target_lock, &lock_context);
|
|
for (i = 1; i < hw_breakpoints; ++i) {
|
|
if (insert && !ARM_HWB_ENALBED(i)) {
|
|
arm_debug_break_exec_enable(i, addr, kind == 1, false);
|
|
break;
|
|
} else if (!insert && ARM_HWB_ENALBED(i) && ARM_HWB_VCR(i) == (addr & ~3)) {
|
|
arm_debug_break_clear(i);
|
|
break;
|
|
}
|
|
}
|
|
rtems_interrupt_lock_release(&target_lock, &lock_context);
|
|
if (!insert && i == hw_breakpoints) {
|
|
errno = EIO;
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
rtems_debugger_target_cache_sync(rtems_debugger_target_swbreak* swbreak)
|
|
{
|
|
target_printk("[} cache: sync: %p\n", swbreak->address);
|
|
/*
|
|
* Flush the data cache and invalidate the instruction cache.
|
|
*/
|
|
rtems_cache_flush_multiple_data_lines(swbreak->address,
|
|
sizeof(breakpoint));
|
|
rtems_cache_instruction_sync_after_code_change(swbreak->address,
|
|
sizeof(breakpoint));
|
|
return 0;
|
|
}
|