/*
* Copyright (c) 2016-2019 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-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.
*/
#define NEEDS_THUMB_SWITCH !ARM_THUMB_ONLY && defined(__thumb__)
#if NEEDS_THUMB_SWITCH
#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_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"
#else
#define ARM_SWITCH_REG
#define ARM_SWITCH_REG_ASM
#define ARM_SWITCH_REG_ASM_L
#define ASM_ARM_MODE
#define ASM_THUMB_MODE
#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.
*/
static int debug_version;
static void* debug_registers;
static int debug_revision;
static bool debug_disable_ints;
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];
/*
* 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) \
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_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); _ARM_Data_synchronization_barrier()
#define ARM_MMAP_READ(reg) *ARM_MMAP_ADDR(reg)
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(1004, 0xC5ACCE55);
}
/*
* Are we already in debug mode?
*/
val = ARM_MMAP_READ(34);
if ((val & (1 << 15)) == 0) {
rtems_debugger_printf("rtems-db: arm debug: enable debug mode\n");
val = ARM_MMAP_READ(34);
ARM_MMAP_WRITE(34, ARM_MMAP_READ(34) | (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(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)
{
if (bp < 15) {
if (debug_registers != NULL) {
ARM_MMAP_WRITE(80 + bp, control);
}
else {
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)
{
if (bp < 15) {
if (debug_registers != NULL) {
ARM_MMAP_WRITE(64 + bp, value);
}
else {
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(34);
}
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(34, 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 = true;
}
static void
arm_debug_commit_interrupt_disable(void)
{
if (debug_disable_ints) {
arm_debug_dbgdscr_write(arm_debug_dbgdscr_read() | (1 << 11));
debug_disable_ints = false;
}
}
static void
arm_debug_enable_interrupts(void)
{
arm_debug_dbgdscr_write(arm_debug_dbgdscr_read() & ~(1 << 11));
}
static void
arm_debug_break_clear(void)
{
rtems_interrupt_lock_context lock_context;
arm_debug_hwbreak* bp = &hw_breaks[0];
int i;
rtems_interrupt_lock_acquire(&target_lock, &lock_context);
for (i = 0; i < hw_breakpoints; ++i, ++bp) {
bp->enabled = false;
bp->loaded = false;
}
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
}
/*
* You can only load the hardware breaks points when in the SVC mode or the
* single step inverted break point will trigger.
*/
static void
arm_debug_break_load(void)
{
rtems_interrupt_lock_context lock_context;
arm_debug_hwbreak* bp = &hw_breaks[0];
int i;
rtems_interrupt_lock_acquire(&target_lock, &lock_context);
if (bp->enabled && !bp->loaded) {
arm_debug_set_context_id(0xdead1111);
arm_debug_break_write_value(0, bp->value);
arm_debug_break_write_control(0, bp->control);
}
++bp;
for (i = 1; i < hw_breakpoints; ++i, ++bp) {
if (bp->enabled && !bp->loaded) {
bp->loaded = true;
arm_debug_break_write_value(i, bp->value);
arm_debug_break_write_control(i, bp->control);
}
}
rtems_interrupt_lock_release(&target_lock, &lock_context);
}
static void
arm_debug_break_unload(void)
{
rtems_interrupt_lock_context lock_context;
arm_debug_hwbreak* bp = &hw_breaks[0];
int i;
rtems_interrupt_lock_acquire(&target_lock, &lock_context);
arm_debug_set_context_id(0);
for (i = 0; i < hw_breakpoints; ++i, ++bp) {
bp->loaded = false;
arm_debug_break_write_control(i, 0);
}
rtems_interrupt_lock_release(&target_lock, &lock_context);
}
static void
arm_debug_break_dump(void)
{
#if TARGET_DEBUG
arm_debug_hwbreak* bp = &hw_breaks[0];
int i;
for (i = 0; i < hw_breakpoints; ++i, ++bp) {
if (bp->enabled) {
target_printk("[} bp: %d: control: %08x addr: %08x\n",
i, bp->control, bp->value);
}
}
#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();
#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" \
" ifsr=%08" PRIx32 " pra=%08x\n",
(uint32_t) frame,
rtems_debugger_target_exception_to_signal(frame),
frame->vector, mvector, dbgdscr, arm_moe_label(moe),
ifsr, (intptr_t) frame->register_pc);
frame->register_pc = (void*) ((intptr_t) frame->register_pc - exc_offset);
target_print_frame(frame);
arm_debug_enable_interrupts();
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:
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);
arm_debug_break_dump();
}
/**
* 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_ASM \
: [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 ARM_CLEAR_THUMB_MODE "bic r1, r1, %[psr_t]\n" /* clear thumb */
#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 */ \
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 */ \
"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 */ \
ASM_THUMB_MODE \
: ARM_SWITCH_REG_ASM_L \
[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" \
"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 */ \
"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_ASM \
: [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 thread'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")
/*
* 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_REG;
EXCEPTION_ENTRY_EXC();
EXCEPTION_ENTRY_THREAD(frame);
longjmp(unlock_abort_jmpbuf, -1);
}
#endif
static void __attribute__((naked))
target_exception_undefined_instruction(void)
{
CPU_Exception_frame* frame;
ARM_SWITCH_REG;
EXCEPTION_ENTRY_EXC();
arm_debug_break_unload();
arm_debug_enable_interrupts();
EXCEPTION_ENTRY_THREAD(frame);
frame->vector = 1;
target_exception(frame);
EXCEPTION_EXIT_THREAD(frame);
arm_debug_commit_interrupt_disable();
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 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_EXC();
arm_debug_break_unload();
arm_debug_enable_interrupts();
EXCEPTION_ENTRY_THREAD(frame);
frame->vector = 2;
target_exception(frame);
EXCEPTION_EXIT_THREAD(frame);
arm_debug_commit_interrupt_disable();
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();
arm_debug_enable_interrupts();
EXCEPTION_ENTRY_THREAD(frame);
frame->vector = 3;
target_exception(frame);
EXCEPTION_EXIT_THREAD(frame);
arm_debug_commit_interrupt_disable();
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();
arm_debug_enable_interrupts();
EXCEPTION_ENTRY_THREAD(frame);
frame->vector = 4;
target_exception(frame);
EXCEPTION_EXIT_THREAD(frame);
arm_debug_commit_interrupt_disable();
arm_debug_break_load();
EXCEPTION_EXIT_EXC();
}
#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;
}
int
rtems_debugger_target_enable(void)
{
rtems_interrupt_lock_context lock_context;
debug_session_active = true;
arm_debug_break_unload();
arm_debug_break_clear();
rtems_interrupt_lock_acquire(&target_lock, &lock_context);
rtems_debugger_target_set_mmu();
rtems_debugger_target_set_vectors();
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
arm_debug_break_unload();
arm_debug_break_clear();
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;
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;
arm_debug_hwbreak* bp = &hw_breaks[0];
target_printk("[} stepping: %s\n", bp->enabled ? "yes" : "no");
if (!bp->enabled) {
const uint32_t addr = (intptr_t) frame->register_pc;
const bool thumb = (FRAME_SR(frame) & (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 comparision 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);
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)
{
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)
{
/*
* 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_instruction_sync_after_code_change(swbreak->address,
sizeof(breakpoint));
return 0;
}