/* * Copyright (c) 2016-2022 Chris Johns . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #define TARGET_DEBUG 0 #define ARM_DUMP_ROM_TABLES 0 #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include "rtems-debugger-target.h" #include "rtems-debugger-threads.h" #if TARGET_DEBUG #include #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 #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_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; } int rtems_debugger_target_enable(void) { rtems_interrupt_lock_context lock_context; arm_debug_break_unload(); arm_debug_break_clear_all(); 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); 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; }