diff options
Diffstat (limited to 'cpukit/libdebugger')
-rw-r--r-- | cpukit/libdebugger/rtems-debugger-aarch64.c | 1905 | ||||
-rw-r--r-- | cpukit/libdebugger/rtems-debugger-arm.c | 808 | ||||
-rw-r--r-- | cpukit/libdebugger/rtems-debugger-i386.c | 12 | ||||
-rw-r--r-- | cpukit/libdebugger/rtems-debugger-microblaze.c | 1393 | ||||
-rw-r--r-- | cpukit/libdebugger/rtems-debugger-server.c | 104 | ||||
-rw-r--r-- | cpukit/libdebugger/rtems-debugger-target.c | 125 | ||||
-rw-r--r-- | cpukit/libdebugger/rtems-debugger-target.h | 31 | ||||
-rw-r--r-- | cpukit/libdebugger/rtems-debugger-threads.c | 10 | ||||
-rw-r--r-- | cpukit/libdebugger/rtems-debugger-threads.h | 10 |
9 files changed, 4031 insertions, 367 deletions
diff --git a/cpukit/libdebugger/rtems-debugger-aarch64.c b/cpukit/libdebugger/rtems-debugger-aarch64.c new file mode 100644 index 0000000000..9a53da0de9 --- /dev/null +++ b/cpukit/libdebugger/rtems-debugger-aarch64.c @@ -0,0 +1,1905 @@ +/* + * 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 + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <errno.h> +#include <inttypes.h> +#include <stdlib.h> + +/* Defined by linkcmds.base. This should be taken from <bsp/linker-symbols.h> */ +extern char bsp_section_start_begin[]; +extern char bsp_section_start_end[]; +extern char bsp_section_text_begin[]; +extern char bsp_section_text_end[]; +extern char bsp_section_fast_text_begin[]; +extern char bsp_section_fast_text_end[]; + +#include <libcpu/mmu-vmsav8-64.h> + +#include <rtems.h> +#include <rtems/score/aarch64-system-registers.h> +#include <rtems/score/cpu.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 + +/* + * Structure used to manage a task executing a function on available cores on + * a scheduler. + */ +typedef struct { + rtems_id allCPUsBarrier; + rtems_task_entry work_function; + rtems_task_argument arg; + rtems_status_code sc; +} run_across_cpus_context; + +/* + * The function that runs as the body of the task which moves itself among the + * various cores registered to a scheduler. + */ +static rtems_task run_across_cpus_task( rtems_task_argument arg ) +{ + uint32_t released = 0; + rtems_status_code sc; + run_across_cpus_context *ctx = (run_across_cpus_context *) arg; + cpu_set_t set; + cpu_set_t scheduler_set; + rtems_id scheduler_id; + + sc = rtems_task_get_scheduler( RTEMS_SELF, &scheduler_id ); + + if ( sc != RTEMS_SUCCESSFUL ) { + ctx->sc = sc; + rtems_task_exit(); + } + + CPU_ZERO( &scheduler_set ); + sc = rtems_scheduler_get_processor_set( + scheduler_id, + sizeof( scheduler_set ), + &scheduler_set + ); + + if ( sc != RTEMS_SUCCESSFUL ) { + ctx->sc = sc; + rtems_task_exit(); + } + + for ( + int cpu_index = 0; + cpu_index < rtems_scheduler_get_processor_maximum(); + cpu_index++ + ) { + if ( !CPU_ISSET( cpu_index, &scheduler_set ) ) { + continue; + } + + CPU_ZERO( &set ); + CPU_SET( cpu_index, &set ); + sc = rtems_task_set_affinity( RTEMS_SELF, sizeof( set ), &set ); + + if ( sc != RTEMS_SUCCESSFUL ) { + ctx->sc = sc; + rtems_task_exit(); + } + + /* execute task on selected CPU */ + ctx->work_function( ctx->arg ); + } + + sc = rtems_barrier_release( ctx->allCPUsBarrier, &released ); + + if ( sc != RTEMS_SUCCESSFUL ) { + ctx->sc = sc; + } + + rtems_task_exit(); +} + +/* + * The function used to run a provided function with arbitrary argument across + * all cores registered to the current scheduler. This is similar to the Linux + * kernel's on_each_cpu() call and always waits for the task to complete before + * returning. + */ +static rtems_status_code run_across_cpus( + rtems_task_entry task_entry, + rtems_task_argument arg +) +{ + rtems_status_code sc; + rtems_id Task_id; + run_across_cpus_context ctx; + + ctx.work_function = task_entry; + ctx.arg = arg; + ctx.sc = RTEMS_SUCCESSFUL; + + memset( &ctx.allCPUsBarrier, 0, sizeof( ctx.allCPUsBarrier ) ); + sc = rtems_barrier_create( + rtems_build_name( 'B', 'c', 'p', 'u' ), + RTEMS_BARRIER_MANUAL_RELEASE, + 2, + &ctx.allCPUsBarrier + ); + + if ( sc != RTEMS_SUCCESSFUL ) { + return sc; + } + + sc = rtems_task_create( + rtems_build_name( 'T', 'c', 'p', 'u' ), + 1, + RTEMS_MINIMUM_STACK_SIZE * 2, + RTEMS_DEFAULT_MODES, + RTEMS_FLOATING_POINT | RTEMS_DEFAULT_ATTRIBUTES, + &Task_id + ); + + if ( sc != RTEMS_SUCCESSFUL ) { + rtems_barrier_delete( ctx.allCPUsBarrier ); + return sc; + } + + sc = rtems_task_start( + Task_id, + run_across_cpus_task, + ( rtems_task_argument ) & ctx + ); + + if ( sc != RTEMS_SUCCESSFUL ) { + rtems_task_delete( Task_id ); + rtems_barrier_delete( ctx.allCPUsBarrier ); + return sc; + } + + /* wait on task */ + sc = rtems_barrier_wait( ctx.allCPUsBarrier, RTEMS_NO_TIMEOUT ); + + if ( sc != RTEMS_SUCCESSFUL ) { + rtems_task_delete( Task_id ); + rtems_barrier_delete( ctx.allCPUsBarrier ); + return sc; + } + + rtems_barrier_delete( ctx.allCPUsBarrier ); + + if ( ctx.sc != RTEMS_SUCCESSFUL ) { + return ctx.sc; + } + + return sc; +} + +/* + * Number of registers. + */ +#define RTEMS_DEBUGGER_NUMREGS 68 + +/* + * Number of bytes per type of register. + */ +#define RTEMS_DEBUGGER_REG_BYTES 8 + +/* Debugger registers layout. See aarch64-core.xml in GDB source. */ +#define REG_X0 0 +#define REG_X1 1 +#define REG_X2 2 +#define REG_X3 3 +#define REG_X4 4 +#define REG_X5 5 +#define REG_X6 6 +#define REG_X7 7 +#define REG_X8 8 +#define REG_X9 9 +#define REG_X10 10 +#define REG_X11 11 +#define REG_X12 12 +#define REG_X13 13 +#define REG_X14 14 +#define REG_X15 15 +#define REG_X16 16 +#define REG_X17 17 +#define REG_X18 18 +#define REG_X19 19 +#define REG_X20 20 +#define REG_X21 21 +#define REG_X22 22 +#define REG_X23 23 +#define REG_X24 24 +#define REG_X25 25 +#define REG_X26 26 +#define REG_X27 27 +#define REG_X28 28 +#define REG_FP 29 +#define REG_LR 30 +#define REG_SP 31 +/* + * PC isn't a real directly accessible register on AArch64, but is exposed via + * ELR_EL1 in exception context. + */ +#define REG_PC 32 +/* CPSR is defined as 32-bit by GDB */ +#define REG_CPS 33 +/* Debugger registers layout. See aarch64-fpu.xml in GDB source. */ +#define REG_V0 34 +#define REG_V1 35 +#define REG_V2 36 +#define REG_V3 37 +#define REG_V4 38 +#define REG_V5 39 +#define REG_V6 40 +#define REG_V7 41 +#define REG_V8 42 +#define REG_V9 43 +#define REG_V10 44 +#define REG_V11 45 +#define REG_V12 46 +#define REG_V13 47 +#define REG_V14 48 +#define REG_V15 49 +#define REG_V16 50 +#define REG_V17 51 +#define REG_V18 52 +#define REG_V19 53 +#define REG_V20 54 +#define REG_V21 55 +#define REG_V22 56 +#define REG_V23 57 +#define REG_V24 58 +#define REG_V25 59 +#define REG_V26 60 +#define REG_V27 61 +#define REG_V28 62 +#define REG_V29 63 +#define REG_V30 64 +#define REG_V31 65 +/* FPSR and FPCR are defined as 32-bit by GDB */ +#define REG_FPS 66 +#define REG_FPC 67 + +/** + * 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 aarch64_reg_offsets[ RTEMS_DEBUGGER_NUMREGS + 1 ] = { + REG_X0 * 8, + REG_X1 * 8, + REG_X2 * 8, + REG_X3 * 8, + REG_X4 * 8, + REG_X5 * 8, + REG_X6 * 8, + REG_X7 * 8, + REG_X8 * 8, + REG_X9 * 8, + REG_X10 * 8, + REG_X11 * 8, + REG_X12 * 8, + REG_X13 * 8, + REG_X14 * 8, + REG_X15 * 8, + REG_X16 * 8, + REG_X17 * 8, + REG_X18 * 8, + REG_X19 * 8, + REG_X20 * 8, + REG_X21 * 8, + REG_X22 * 8, + REG_X23 * 8, + REG_X24 * 8, + REG_X25 * 8, + REG_X26 * 8, + REG_X27 * 8, + REG_X28 * 8, + REG_FP * 8, + REG_LR * 8, + REG_SP * 8, + REG_PC * 8, + REG_CPS * 8, +/* Floating point registers, CPS is 32-bit */ +#define V0_OFFSET ( REG_CPS * 8 + 4 ) + V0_OFFSET, + V0_OFFSET + 16 * ( REG_V1 - REG_V0 ), + V0_OFFSET + 16 * ( REG_V2 - REG_V0 ), + V0_OFFSET + 16 * ( REG_V3 - REG_V0 ), + V0_OFFSET + 16 * ( REG_V4 - REG_V0 ), + V0_OFFSET + 16 * ( REG_V5 - REG_V0 ), + V0_OFFSET + 16 * ( REG_V6 - REG_V0 ), + V0_OFFSET + 16 * ( REG_V7 - REG_V0 ), + V0_OFFSET + 16 * ( REG_V8 - REG_V0 ), + V0_OFFSET + 16 * ( REG_V9 - REG_V0 ), + V0_OFFSET + 16 * ( REG_V10 - REG_V0 ), + V0_OFFSET + 16 * ( REG_V11 - REG_V0 ), + V0_OFFSET + 16 * ( REG_V12 - REG_V0 ), + V0_OFFSET + 16 * ( REG_V13 - REG_V0 ), + V0_OFFSET + 16 * ( REG_V14 - REG_V0 ), + V0_OFFSET + 16 * ( REG_V15 - REG_V0 ), + V0_OFFSET + 16 * ( REG_V16 - REG_V0 ), + V0_OFFSET + 16 * ( REG_V17 - REG_V0 ), + V0_OFFSET + 16 * ( REG_V18 - REG_V0 ), + V0_OFFSET + 16 * ( REG_V19 - REG_V0 ), + V0_OFFSET + 16 * ( REG_V20 - REG_V0 ), + V0_OFFSET + 16 * ( REG_V21 - REG_V0 ), + V0_OFFSET + 16 * ( REG_V22 - REG_V0 ), + V0_OFFSET + 16 * ( REG_V23 - REG_V0 ), + V0_OFFSET + 16 * ( REG_V24 - REG_V0 ), + V0_OFFSET + 16 * ( REG_V25 - REG_V0 ), + V0_OFFSET + 16 * ( REG_V26 - REG_V0 ), + V0_OFFSET + 16 * ( REG_V27 - REG_V0 ), + V0_OFFSET + 16 * ( REG_V28 - REG_V0 ), + V0_OFFSET + 16 * ( REG_V29 - REG_V0 ), + V0_OFFSET + 16 * ( REG_V30 - REG_V0 ), + V0_OFFSET + 16 * ( REG_V31 - REG_V0 ), +/* FPSR and FPCR are defined as 32-bit by GDB */ +#define FPS_OFFSET ( V0_OFFSET + 16 * ( REG_V31 - REG_V0 ) + 16 ) + FPS_OFFSET, +/* FPC follows FPS */ + FPS_OFFSET + 4, +/* Total size */ + FPS_OFFSET + 8, +}; + +/* + * Number of bytes of registers. + */ +#define RTEMS_DEBUGGER_NUMREGBYTES \ + aarch64_reg_offsets[ RTEMS_DEBUGGER_NUMREGS ] + +/** + * Print the exception frame. + */ +#define EXC_FRAME_PRINT( _out, _prefix, _frame ) \ + do { \ + _out( \ + _prefix " X0 = %016" PRIx64 " X1 = %016" PRIx64 \ + " X2 = %016" PRIx64 " X3 = %016" PRIx64 "\n", \ + _frame->register_x0, \ + _frame->register_x1, \ + _frame->register_x2, \ + _frame->register_x3 \ + ); \ + _out( \ + _prefix " X4 = %016" PRIx64 " X5 = %016" PRIx64 \ + " X6 = %016" PRIx64 " X7 = %016" PRIx64 "\n", \ + _frame->register_x4, \ + _frame->register_x5, \ + _frame->register_x6, \ + _frame->register_x7 \ + ); \ + _out( \ + _prefix " X8 = %016" PRIx64 " X9 = %016" PRIx64 \ + " X10 = %016" PRIx64 " X11 = %016" PRIx64 "\n", \ + _frame->register_x8, \ + _frame->register_x9, \ + _frame->register_x10, \ + _frame->register_x11 \ + ); \ + _out( \ + _prefix " X12 = %016" PRIx64 " X13 = %016" PRIx64 \ + " X14 = %016" PRIx64 " X15 = %016" PRIx64 "\n", \ + _frame->register_x12, \ + _frame->register_x13, \ + _frame->register_x14, \ + _frame->register_x15 \ + ); \ + _out( \ + _prefix " X16 = %016" PRIx64 " X17 = %016" PRIx64 \ + " X18 = %016" PRIx64 " X19 = %016" PRIx64 "\n", \ + _frame->register_x16, \ + _frame->register_x17, \ + _frame->register_x18, \ + _frame->register_x19 \ + ); \ + _out( \ + _prefix " X20 = %016" PRIx64 " X21 = %016" PRIx64 \ + " X22 = %016" PRIx64 " X23 = %016" PRIx64 "\n", \ + _frame->register_x20, \ + _frame->register_x21, \ + _frame->register_x22, \ + _frame->register_x23 \ + ); \ + _out( \ + _prefix " X24 = %016" PRIx64 " X25 = %016" PRIx64 \ + " X26 = %016" PRIx64 " X27 = %016" PRIx64 "\n", \ + _frame->register_x24, \ + _frame->register_x25, \ + _frame->register_x26, \ + _frame->register_x27 \ + ); \ + _out( \ + _prefix " X28 = %016" PRIx64 " FP = %016" PRIx64 \ + " LR = %016" PRIxPTR " SP = %016" PRIxPTR "\n", \ + _frame->register_x28, \ + _frame->register_fp, \ + (intptr_t) _frame->register_lr, \ + (intptr_t) _frame->register_sp \ + ); \ + _out( \ + _prefix " PC = %016" PRIxPTR "\n", \ + (intptr_t) _frame->register_pc \ + ); \ + _out( \ + _prefix " CPSR = %08" PRIx64 " %c%c%c%c%c%c%c%c%c" \ + " M:%" PRIx64 " %s\n", \ + _frame->register_cpsr, \ + ( _frame->register_cpsr & ( 1 << 31 ) ) != 0 ? 'N' : '-', \ + ( _frame->register_cpsr & ( 1 << 30 ) ) != 0 ? 'Z' : '-', \ + ( _frame->register_cpsr & ( 1 << 29 ) ) != 0 ? 'C' : '-', \ + ( _frame->register_cpsr & ( 1 << 28 ) ) != 0 ? 'V' : '-', \ + ( _frame->register_cpsr & ( 1 << 21 ) ) != 0 ? 'S' : '-', \ + ( _frame->register_cpsr & ( 1 << 9 ) ) != 0 ? 'D' : '-', \ + ( _frame->register_cpsr & ( 1 << 8 ) ) != 0 ? 'A' : '-', \ + ( _frame->register_cpsr & ( 1 << 7 ) ) != 0 ? 'I' : '-', \ + ( _frame->register_cpsr & ( 1 << 6 ) ) != 0 ? 'F' : '-', \ + _frame->register_cpsr & 0x1f, \ + aarch64_mode_label( _frame->register_cpsr & 0x1f ) \ + ); \ + } while ( 0 ) + +/** + * The breakpoint instruction. + */ +static const uint8_t breakpoint[ 4 ] = { 0x00, 0x00, 0x20, 0xd4 }; + +/** + * Target lock. + */ +RTEMS_INTERRUPT_LOCK_DEFINE( static, target_lock, "target_lock" ) + +/** + * Is a session active? + */ +static bool debug_session_active; + +/* + * AArch64 debug hardware. + */ +static uint64_t hw_breakpoints; +static uint64_t hw_watchpoints; + +#ifdef HARDWARE_BREAKPOINTS_NOT_USED +/** + * Hardware break and watch points. + */ +typedef struct { + bool enabled; + bool loaded; + void *address; + size_t length; + CPU_Exception_frame *frame; + uint64_t control; + uint64_t value; +} aarch64_debug_hwbreak; + +/* + * AArch64 guarantees that 2-16 breakpoints will be available in: + * DBGBCR<0-15>_EL1 (control) + * BT: BSP_FLD64(val, 20, 23) (breakpoint type, always 0x0 or 0x4, address match or mismatch) + * LBN: BSP_FLD64(val, 16, 19) (linked breakpoint number, always 0x0, not relevant given above) + * SSC: BSP_FLD64(val, 14, 15) (security state control, only 0x0 relevant) + * HMC: BSP_BIT64(13) (higher mode control, only 0x0 relevant) + * BAS: BSP_FLD64(val, 5, 8) (byte address select, always 0xF, other values denote debugging of AArch32 code) + * PMC: BSP_FLD64(val, 1, 2) (privelege mode control, only 0x1 relevant) + * E: BSP_BIT64(0) (enable, 0x1 when in use, 0x0 when disabled) + * DBGBVR<0-15>_EL1 (value, address) + * ID_AA64DFR0_EL1 + * WRPs: BSP_FLD64(val, 20, 23) (watchpoints implemented - 1, 0x0 reserved so minimum 2) + * BRPs: BSP_FLD64(val, 12, 15) (breakpoints implemented - 1, 0x0 reserved so minimum 2) + * DebugVer: BSP_FLD64(val, 0, 3) (0x6 - 8, 0x7 - 8 w/ VHE, 0x8 - 8.2, 0x9 - 8.4) + */ +#define AARCH64_HW_BREAKPOINT_MAX ( 16 ) + +/* + * Types of break points. + */ +#define AARCH64_HW_BP_TYPE_UNLINKED_INSTR_MATCH ( 0x0 << 20 ) +#define AARCH64_HW_BP_TYPE_UNLINKED_INSTR_MISMATCH ( 0x4 << 20 ) + +/* + * Byte Address Select + */ +#define AARCH64_HW_BP_BAS_A64 ( 0xF << 5 ) + +/* + * Privilege level, corresponds to PMC at 2:1 + */ +#define AARCH64_HW_BP_PRIV_EL1 ( 0x1 << 1 ) + +/* + * Breakpoint enable. + */ +#define AARCH64_HW_BP_ENABLE ( 0x1 ) + +static aarch64_debug_hwbreak hw_breaks[ AARCH64_HW_BREAKPOINT_MAX ]; +#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, ... ) +#endif + +static const char *aarch64_mode_label( int mode ) +{ + switch ( mode ) { + case 0x0: + return "EL0t"; + case 0x4: + return "EL1t"; + case 0x5: + return "EL1h"; + } + + return "---"; +} + +static int aarch64_debug_probe( rtems_debugger_target *target ) +{ + int debug_version; + uint64_t val; + const char *vl = "[Invalid version]"; + const char * const labels[] = { + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + "ARMv8.0", + "ARMv8.0+VHE", + "ARMv8.2", + "ARMv8.4" + }; + + val = _AArch64_Read_midr_el1(); + rtems_debugger_printf( + "rtems-db: aarch64 core: Architecture: %" PRIu64 " Variant: %" PRIu64 " " \ + "Implementor: %" PRIu64 " Part Number: %" PRIu64 " Revision: %" PRIu64 "\n", + AARCH64_MIDR_EL1_ARCHITECTURE_GET( val ), + AARCH64_MIDR_EL1_VARIANT_GET( val ), + AARCH64_MIDR_EL1_IMPLEMENTER_GET( val ), + AARCH64_MIDR_EL1_PARTNUM_GET( val ), + AARCH64_MIDR_EL1_REVISION_GET( val ) + ); + + val = _AArch64_Read_id_aa64dfr0_el1(); + + debug_version = AARCH64_ID_AA64DFR0_EL1_DEBUGVER_GET( val ); + + if ( debug_version < 6 || debug_version > 9 ) { + rtems_debugger_printf( + "rtems-db: aarch64 debug: %d not supported\n", + debug_version + ); + errno = EIO; + return -1; + } + + vl = labels[ debug_version ]; + hw_breakpoints = AARCH64_ID_AA64DFR0_EL1_BRPS_GET( val ); + hw_watchpoints = AARCH64_ID_AA64DFR0_EL1_WRPS_GET( val ); + + rtems_debugger_printf( + "rtems-db: aarch64 debug: %s (%d) " \ + "breakpoints:%" PRIu64 " watchpoints:%" PRIu64 "\n", + vl, + debug_version, + hw_breakpoints, + hw_watchpoints + ); + + return 0; +} + +#ifdef HARDWARE_BREAKPOINTS_NOT_USED +static void aarch64_debug_break_write_control( int bp, uint64_t control ) +{ + if ( bp < 15 ) { + switch ( bp ) { + case 0: + _AArch64_Write_dbgbcr0_el1( control ); + break; + case 1: + _AArch64_Write_dbgbcr1_el1( control ); + break; + case 2: + _AArch64_Write_dbgbcr2_el1( control ); + break; + case 3: + _AArch64_Write_dbgbcr3_el1( control ); + break; + case 4: + _AArch64_Write_dbgbcr4_el1( control ); + break; + case 5: + _AArch64_Write_dbgbcr5_el1( control ); + break; + case 6: + _AArch64_Write_dbgbcr6_el1( control ); + break; + case 7: + _AArch64_Write_dbgbcr7_el1( control ); + break; + case 8: + _AArch64_Write_dbgbcr8_el1( control ); + break; + case 9: + _AArch64_Write_dbgbcr9_el1( control ); + break; + case 10: + _AArch64_Write_dbgbcr10_el1( control ); + break; + case 11: + _AArch64_Write_dbgbcr11_el1( control ); + break; + case 12: + _AArch64_Write_dbgbcr12_el1( control ); + break; + case 13: + _AArch64_Write_dbgbcr13_el1( control ); + break; + case 14: + _AArch64_Write_dbgbcr14_el1( control ); + break; + case 15: + _AArch64_Write_dbgbcr15_el1( control ); + break; + } + } +} + +static void aarch64_debug_break_write_value( int bp, uint64_t value ) +{ + if ( bp < 15 ) { + switch ( bp ) { + case 0: + _AArch64_Write_dbgbvr0_el1( value ); + break; + case 1: + _AArch64_Write_dbgbvr1_el1( value ); + break; + case 2: + _AArch64_Write_dbgbvr2_el1( value ); + break; + case 3: + _AArch64_Write_dbgbvr3_el1( value ); + break; + case 4: + _AArch64_Write_dbgbvr4_el1( value ); + break; + case 5: + _AArch64_Write_dbgbvr5_el1( value ); + break; + case 6: + _AArch64_Write_dbgbvr6_el1( value ); + break; + case 7: + _AArch64_Write_dbgbvr7_el1( value ); + break; + case 8: + _AArch64_Write_dbgbvr8_el1( value ); + break; + case 9: + _AArch64_Write_dbgbvr9_el1( value ); + break; + case 10: + _AArch64_Write_dbgbvr10_el1( value ); + break; + case 11: + _AArch64_Write_dbgbvr11_el1( value ); + break; + case 12: + _AArch64_Write_dbgbvr12_el1( value ); + break; + case 13: + _AArch64_Write_dbgbvr13_el1( value ); + break; + case 14: + _AArch64_Write_dbgbvr14_el1( value ); + break; + case 15: + _AArch64_Write_dbgbvr15_el1( value ); + break; + } + } +} + +static inline void aarch64_debug_break_setup( + uint8_t index, + uint64_t address +) +{ + aarch64_debug_hwbreak *bp = &hw_breaks[ index ]; + + bp->control = AARCH64_HW_BP_TYPE_UNLINKED_INSTR_MISMATCH | + AARCH64_HW_BP_BAS_A64 | + AARCH64_HW_BP_PRIV_EL1 | + AARCH64_HW_BP_ENABLE; + uint64_t address_mask = 0x3; + + bp->value = (intptr_t) ( address & ~address_mask ); + aarch64_debug_break_write_value( index, bp->value ); + aarch64_debug_break_write_control( index, bp->control ); +} + +static void aarch64_debug_break_clear( void ) +{ + rtems_interrupt_lock_context lock_context; + aarch64_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 void aarch64_debug_break_load( void ) +{ + rtems_interrupt_lock_context lock_context; + aarch64_debug_hwbreak *bp = &hw_breaks[ 0 ]; + int i; + + rtems_interrupt_lock_acquire( &target_lock, &lock_context ); + + if ( bp->enabled && !bp->loaded ) { + aarch64_debug_set_context_id( 0xdead1111 ); + aarch64_debug_break_write_value( 0, bp->value ); + aarch64_debug_break_write_control( 0, bp->control ); + } + + ++bp; + + for ( i = 1; i < hw_breakpoints; ++i, ++bp ) { + if ( bp->enabled && !bp->loaded ) { + bp->loaded = true; + aarch64_debug_break_write_value( i, bp->value ); + aarch64_debug_break_write_control( i, bp->control ); + } + } + + rtems_interrupt_lock_release( &target_lock, &lock_context ); +} + +static void aarch64_debug_break_unload( void ) +{ + rtems_interrupt_lock_context lock_context; + aarch64_debug_hwbreak *bp = &hw_breaks[ 0 ]; + int i; + + rtems_interrupt_lock_acquire( &target_lock, &lock_context ); + aarch64_debug_set_context_id( 0 ); + + for ( i = 0; i < hw_breakpoints; ++i, ++bp ) { + bp->loaded = false; + aarch64_debug_break_write_control( i, 0 ); + } + + rtems_interrupt_lock_release( &target_lock, &lock_context ); +} + +static void aarch64_debug_break_dump( void ) +{ +#if TARGET_DEBUG + aarch64_debug_hwbreak *bp = &hw_breaks[ 0 ]; + int i; + + for ( i = 0; i < hw_breakpoints; ++i, ++bp ) { + if ( bp->enabled ) { + target_printk( + "[} bp: %d: control: %016" PRIx64 " addr: %016" PRIxPTR "\n", + i, + bp->control, + (uintptr_t) bp->value + ); + } + } + +#endif +} +#endif + +static void aarch64_debug_disable_interrupts( void ) +{ + __asm__ volatile ( "msr DAIFSet, #0x2" ); +} + +static void aarch64_debug_enable_interrupts( void ) +{ + __asm__ volatile ( "msr DAIFClr, #2\n" ); +} + +static void aarch64_debug_disable_debug_exceptions( void ) +{ + __asm__ volatile ( "msr DAIFSet, #0x8" ); +} + +static inline void aarch64_debug_set_context_id( const uint32_t id ) +{ + _AArch64_Write_contextidr_el1( id ); +} + +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 = aarch64_reg_offsets; + target->breakpoint = &breakpoint[ 0 ]; + target->breakpoint_size = sizeof( breakpoint ); + return aarch64_debug_probe( target ); +} + +static void target_print_frame( CPU_Exception_frame *frame ) +{ + EXC_FRAME_PRINT( target_printk, "[} ", frame ); +} + +/* returns true if cascade is required */ +static bool target_exception( CPU_Exception_frame *frame ) +{ + target_printk( + "[} > frame = %016" PRIxPTR \ + " sig=%d" \ + " pra=%016" PRIxPTR "\n" \ + "[} > esr=%016" PRIx64 \ + " far=%016" PRIxPTR "\n", + (uintptr_t) frame, + rtems_debugger_target_exception_to_signal( frame ), + (uintptr_t) frame->register_pc, + (uint64_t) frame->register_syndrome, + (uintptr_t) frame->register_fault_address + ); + + target_print_frame( 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" ); + /* Continue in fatal error handler chain */ + return true; + break; + } + + target_printk( + "[} < resuming frame = %016" PRIxPTR "\n", + (uintptr_t) frame + ); + target_print_frame( frame ); + +#if TARGET_DEBUG + uint64_t mdscr = _AArch64_Read_mdscr_el1(); +#endif + target_printk( + "[} global stepping: %s\n", + mdscr & AARCH64_MDSCR_EL1_SS ? "yes" : "no" + ); + target_printk( + "[} kernel self-debug: %s\n", + mdscr & AARCH64_MDSCR_EL1_KDE ? "yes" : "no" + ); + target_printk( + "[} non-step/non-BRK debug events: %s\n", + mdscr & AARCH64_MDSCR_EL1_MDE ? "yes" : "no" + ); + target_printk( + "[} OSLSR(should be 0x8): 0x%016" PRIx64 "\n", + _AArch64_Read_oslsr_el1() + ); +#ifdef HARDWARE_BREAKPOINTS_NOT_USED + aarch64_debug_break_dump(); +#endif + return false; +} + +#define xstr( a ) str( a ) +#define str( a ) #a +#define FRAME_SIZE_STR xstr( AARCH64_EXCEPTION_FRAME_SIZE ) + +/* + * This block of assembly must have a target branch function because GCC + * requires that SP not accumulate changes across an ASM block. Instead of + * changing the SP, we branch to a new function and never return since it was + * never going to return anyway. + */ +#define SWITCH_STACKS_AND_ALLOC( new_mode, old_frame, jump_target ) \ + __asm__ volatile ( \ + "msr spsel, #" new_mode "\n" /* switch to thread stack */ \ + "sub sp, sp, #" FRAME_SIZE_STR "\n" /* reserve space for CEF */ \ + "mov x0, sp\n" /* Set x0 to the new exception frame */ \ + "mov x1, %[old_frame]\n" /* Set x1 to the old exception frame */ \ + "b " #jump_target "\n" /* Jump to the specified function */ \ + : \ + : [ old_frame ] "r" ( old_frame ) \ + : "x0", "x1" ) + +#define SWITCH_STACKS_AND_ALLOC_WITH_CASCADE( new_mode, app_frame, \ + jump_target ) \ + __asm__ volatile ( \ + "msr spsel, #" new_mode "\n" /* switch to thread stack */ \ + "sub sp, sp, #" FRAME_SIZE_STR "\n" /* reserve space for CEF */ \ + "mov x0, sp\n" /* Set x0 to the new exception frame */ \ + "mov x1, %[app_frame]\n" /* Set x1 to the old exception frame */ \ + "mov x2, %[needs_cascade]\n" /* pass on whether cascade is needed */ \ + "b " #jump_target "\n" /* Jump to the specified function */ \ + : \ + : [ app_frame ] "r" ( app_frame ), \ + [ needs_cascade ] "r" ( needs_cascade ) \ + : "x0", "x1" ) + +/* + * This block does not have an overall effect on SP since the spsel mode change + * preserves the original SP + */ +#define DROP_OLD_FRAME( old_frame, old_mode, new_mode ) \ + __asm__ volatile ( \ + "msr spsel, #" old_mode "\n" /* switch to exception stack */ \ + "mov sp, %0\n" /* Reset SP to the beginning of the CEF */ \ + "add sp, sp, #" FRAME_SIZE_STR "\n" /* release space for CEF on exception stack */ \ + "msr spsel, #" new_mode "\n" /* switch to thread stack */ \ + : \ + : "r" ( old_frame ) ) \ + +#define THREAD_MODE "1" +#define EXCEPTION_MODE "0" + +void target_exception_stack_stage_3( + CPU_Exception_frame *exc_frame, + CPU_Exception_frame *app_frame, + bool needs_cascade +); + +void target_exception_stack_stage_3( + CPU_Exception_frame *exc_frame, + CPU_Exception_frame *app_frame, + bool needs_cascade +) +{ + _AArch64_Exception_frame_copy( exc_frame, app_frame ); + DROP_OLD_FRAME( app_frame, THREAD_MODE, EXCEPTION_MODE ); + + if ( needs_cascade ) { + /* does not return */ + _AArch64_Exception_default( exc_frame ); + } + + /* does not return */ + _CPU_Exception_resume( exc_frame ); +} + +void target_exception_stack_stage_2( + CPU_Exception_frame *app_frame, + CPU_Exception_frame *exc_frame +); + +void target_exception_stack_stage_2( + CPU_Exception_frame *app_frame, + CPU_Exception_frame *exc_frame +) +{ + _AArch64_Exception_frame_copy( app_frame, exc_frame ); + DROP_OLD_FRAME( exc_frame, EXCEPTION_MODE, THREAD_MODE ); + /* breakpoints must be disabled here since other tasks could run that don't have debug masked */ +#ifdef HARDWARE_BREAKPOINTS_NOT_USED + aarch64_debug_break_unload(); +#endif + /* enable interrupts here to allow this thread to be suspended as necessary */ + aarch64_debug_enable_interrupts(); + bool needs_cascade = target_exception( app_frame ); + + /* disable interrupts to return to normal operation */ + aarch64_debug_disable_interrupts(); + /* re-enable breakpoints disabled above */ +#ifdef HARDWARE_BREAKPOINTS_NOT_USED + aarch64_debug_break_load(); +#endif + SWITCH_STACKS_AND_ALLOC_WITH_CASCADE( + EXCEPTION_MODE, + app_frame, + target_exception_stack_stage_3 + ); +} + +/* not allowed to return since it unwinds the stack */ +static void target_exception_thread_stack( CPU_Exception_frame *old_frame ) +{ + SWITCH_STACKS_AND_ALLOC( + THREAD_MODE, + old_frame, + target_exception_stack_stage_2 + ); +} + +static void target_exception_application( CPU_Exception_frame *ef ) +{ + /* Continue in fatal error handler chain */ + if ( !debug_session_active ) { + /* does not return */ + _AArch64_Exception_default( ef ); + } + + /* + * Set CPSR.D to disable single-step operation, this will be cleared before + * the thread is resumed if necessary. + */ + ef->register_cpsr |= AARCH64_DSPSR_EL0_D; + + /* + * Switching to the user stack is not possible if the stack pointer is bad. + * This should be a relatively rare occurrance and signals a severe problem + * with the application code or system. + */ + if ( AARCH64_ESR_EL1_EC_GET( ef->register_syndrome ) == 0x26 ) { + if ( target_exception( ef ) ) { + /* does not return */ + _AArch64_Exception_default( ef ); + } + + /* does not return */ + _CPU_Exception_resume( ef ); + } + + target_exception_thread_stack( ef ); +} + +static void target_exception_kernel( CPU_Exception_frame *ef ) +{ + /* + * If there is a stack alignment problem in exception mode, it really + * shouldn't happen and execution won't even make it this far. + */ + if ( !debug_session_active ) { + /* does not return */ + _AArch64_Exception_default( ef ); + } + + /* + * Set CPSR.D to disable single-step operation, this will be cleared before + * the thread is resumed if necessary. + */ + ef->register_cpsr |= AARCH64_DSPSR_EL0_D; + + if ( target_exception( ef ) ) { + /* does not return */ + _AArch64_Exception_default( ef ); + } + + /* does not return */ + _CPU_Exception_resume( ef ); +} + +static void rtems_debugger_target_set_vectors( void ) +{ + /* Set vectors for both application and kernel modes */ + AArch64_set_exception_handler( + AARCH64_EXCEPTION_SPx_SYNCHRONOUS, + (void *) target_exception_application + ); + AArch64_set_exception_handler( + AARCH64_EXCEPTION_SP0_SYNCHRONOUS, + (void *) target_exception_kernel + ); +} + +static bool rtems_debugger_is_int_reg( size_t reg ) +{ + const size_t size = aarch64_reg_offsets[ reg + 1 ] - + aarch64_reg_offsets[ reg ]; + + return size == RTEMS_DEBUGGER_REG_BYTES; +} + +static void rtems_debugger_set_int_reg( + rtems_debugger_thread *thread, + size_t reg, + const uint64_t value +) +{ + const size_t offset = aarch64_reg_offsets[ reg ]; + + memcpy( &thread->registers[ offset ], &value, sizeof( uint64_t ) ); +} + +static const uint64_t rtems_debugger_get_int_reg( + rtems_debugger_thread *thread, + size_t reg +) +{ + const size_t offset = aarch64_reg_offsets[ reg ]; + uint64_t value; + + memcpy( &value, &thread->registers[ offset ], sizeof( uint64_t ) ); + return value; +} + +static void rtems_debugger_set_halfint_reg( + rtems_debugger_thread *thread, + size_t reg, + const uint32_t value +) +{ + const size_t offset = aarch64_reg_offsets[ reg ]; + + memcpy( &thread->registers[ offset ], &value, sizeof( uint32_t ) ); +} + +static const uint32_t rtems_debugger_get_halfint_reg( + rtems_debugger_thread *thread, + size_t reg +) +{ + const size_t offset = aarch64_reg_offsets[ reg ]; + uint32_t value; + + memcpy( &value, &thread->registers[ offset ], sizeof( uint32_t ) ); + return value; +} + +static void rtems_debugger_set_fp_reg( + rtems_debugger_thread *thread, + size_t reg, + const uint128_t value +) +{ + const size_t offset = aarch64_reg_offsets[ reg ]; + + memcpy( &thread->registers[ offset ], &value, sizeof( uint128_t ) ); +} + +static const uint128_t rtems_debugger_get_fp_reg( + rtems_debugger_thread *thread, + size_t reg +) +{ + const size_t offset = aarch64_reg_offsets[ reg ]; + uint128_t value; + + memcpy( &value, &thread->registers[ offset ], sizeof( uint128_t ) ); + return value; +} + +static rtems_status_code rtems_debugger_target_set_text_writable( + bool writable +) +{ + uintptr_t start_begin = (uintptr_t) bsp_section_start_begin; + uintptr_t start_end = (uintptr_t) bsp_section_start_end; + uintptr_t text_begin = (uintptr_t) bsp_section_text_begin; + uintptr_t text_end = (uintptr_t) bsp_section_text_end; + uintptr_t fast_text_begin = (uintptr_t) bsp_section_fast_text_begin; + uintptr_t fast_text_end = (uintptr_t) bsp_section_fast_text_end; + uint64_t mmu_flags = AARCH64_MMU_CODE_RW_CACHED; + rtems_status_code sc; + + if ( !writable ) { + mmu_flags = AARCH64_MMU_CODE_CACHED; + } + + target_printk( + "[} MMU edit: start_begin: 0x%016" PRIxPTR + " start_end: 0x%016" PRIxPTR "\n", + start_begin, + start_end + ); + sc = aarch64_mmu_map( + start_begin, + start_end - start_begin, + mmu_flags + ); + + if ( sc != RTEMS_SUCCESSFUL ) { + target_printk( "[} MMU edit failed\n" ); + return sc; + } + + target_printk( + "[} MMU edit: text_begin: 0x%016" PRIxPTR + " text_end: 0x%016" PRIxPTR "\n", + text_begin, + text_end + ); + sc = aarch64_mmu_map( + text_begin, + text_end - text_begin, + mmu_flags + ); + + if ( sc != RTEMS_SUCCESSFUL ) { + target_printk( "[} MMU edit failed\n" ); + return sc; + } + + target_printk( + "[} MMU edit: fast_text_begin: 0x%016" PRIxPTR + " fast_text_end: 0x%016" PRIxPTR "\n", + fast_text_begin, + fast_text_end + ); + sc = aarch64_mmu_map( + fast_text_begin, + fast_text_end - fast_text_begin, + mmu_flags + ); + + if ( sc != RTEMS_SUCCESSFUL ) { + target_printk( "[} MMU edit failed\n" ); + } + + return sc; +} + +static rtems_task setup_debugger_on_cpu( rtems_task_argument arg ) +{ + rtems_status_code sc; + rtems_status_code *init_error = (rtems_status_code *) arg; + rtems_interrupt_lock_context lock_context; + + rtems_interrupt_lock_acquire( &target_lock, &lock_context ); + sc = rtems_debugger_target_set_text_writable( true ); + + if ( sc != RTEMS_SUCCESSFUL ) { + *init_error = sc; + } + + rtems_debugger_target_set_vectors(); + + /* enable single-step debugging */ + uint64_t mdscr = _AArch64_Read_mdscr_el1(); + + mdscr |= AARCH64_MDSCR_EL1_SS; + mdscr |= AARCH64_MDSCR_EL1_KDE; + mdscr |= AARCH64_MDSCR_EL1_MDE; + _AArch64_Write_mdscr_el1( mdscr ); + + /* clear the OS lock */ + _AArch64_Write_oslar_el1( 0 ); + rtems_interrupt_lock_release( &target_lock, &lock_context ); +} + +int rtems_debugger_target_enable( void ) +{ + rtems_status_code sc; + rtems_status_code init_error = RTEMS_SUCCESSFUL; + + debug_session_active = true; +#ifdef HARDWARE_BREAKPOINTS_NOT_USED + aarch64_debug_break_unload(); + aarch64_debug_break_clear(); +#endif + aarch64_debug_disable_debug_exceptions(); + sc = run_across_cpus( + setup_debugger_on_cpu, + ( rtems_task_argument ) & init_error + ); + + if ( init_error != RTEMS_SUCCESSFUL ) { + return init_error; + } + + return sc; +} + +static rtems_task teardown_debugger_on_cpu( rtems_task_argument arg ) +{ + rtems_status_code sc; + rtems_status_code *deinit_error = (rtems_status_code *) arg; + rtems_interrupt_lock_context lock_context; + + rtems_interrupt_lock_acquire( &target_lock, &lock_context ); + sc = rtems_debugger_target_set_text_writable( false ); + + if ( sc != RTEMS_SUCCESSFUL ) { + *deinit_error = sc; + } + + /* disable single-step debugging */ + uint64_t mdscr = _AArch64_Read_mdscr_el1(); + + mdscr &= ~AARCH64_MDSCR_EL1_SS; + mdscr &= ~AARCH64_MDSCR_EL1_KDE; + mdscr &= ~AARCH64_MDSCR_EL1_MDE; + _AArch64_Write_mdscr_el1( mdscr ); + + rtems_interrupt_lock_release( &target_lock, &lock_context ); +} + +int rtems_debugger_target_disable( void ) +{ + rtems_status_code sc; + rtems_status_code deinit_error = RTEMS_SUCCESSFUL; + + debug_session_active = false; +#ifdef HARDWARE_BREAKPOINTS_NOT_USED + aarch64_debug_break_unload(); + aarch64_debug_break_clear(); +#endif + sc = run_across_cpus( + teardown_debugger_on_cpu, + ( rtems_task_argument ) & deinit_error + ); + + if ( deinit_error != RTEMS_SUCCESSFUL ) { + return deinit_error; + } + + return sc; +} + +int rtems_debugger_target_read_regs( rtems_debugger_thread *thread ) +{ + if ( + !rtems_debugger_thread_flag( + thread, + RTEMS_DEBUGGER_THREAD_FLAG_REG_VALID + ) + ) { + static const uintptr_t good_address = (uintptr_t) &good_address; + int i; + + memset( &thread->registers[ 0 ], 0, RTEMS_DEBUGGER_NUMREGBYTES ); + + /* set all integer register to a known valid address */ + for ( i = 0; i < RTEMS_DEBUGGER_NUMREGS; ++i ) { + if ( rtems_debugger_is_int_reg( i ) ) { + rtems_debugger_set_int_reg( thread, i, (uintptr_t) &good_address ); + } + } + + if ( thread->frame ) { + CPU_Exception_frame *frame = thread->frame; + + *( (CPU_Exception_frame *) thread->registers ) = *frame; + rtems_debugger_set_int_reg( thread, REG_X0, frame->register_x0 ); + rtems_debugger_set_int_reg( thread, REG_X1, frame->register_x1 ); + rtems_debugger_set_int_reg( thread, REG_X2, frame->register_x2 ); + rtems_debugger_set_int_reg( thread, REG_X3, frame->register_x3 ); + rtems_debugger_set_int_reg( thread, REG_X4, frame->register_x4 ); + rtems_debugger_set_int_reg( thread, REG_X5, frame->register_x5 ); + rtems_debugger_set_int_reg( thread, REG_X6, frame->register_x6 ); + rtems_debugger_set_int_reg( thread, REG_X7, frame->register_x7 ); + rtems_debugger_set_int_reg( thread, REG_X8, frame->register_x8 ); + rtems_debugger_set_int_reg( thread, REG_X9, frame->register_x9 ); + rtems_debugger_set_int_reg( thread, REG_X10, frame->register_x10 ); + rtems_debugger_set_int_reg( thread, REG_X11, frame->register_x11 ); + rtems_debugger_set_int_reg( thread, REG_X12, frame->register_x12 ); + rtems_debugger_set_int_reg( thread, REG_X13, frame->register_x13 ); + rtems_debugger_set_int_reg( thread, REG_X14, frame->register_x14 ); + rtems_debugger_set_int_reg( thread, REG_X15, frame->register_x15 ); + rtems_debugger_set_int_reg( thread, REG_X16, frame->register_x16 ); + rtems_debugger_set_int_reg( thread, REG_X17, frame->register_x17 ); + rtems_debugger_set_int_reg( thread, REG_X18, frame->register_x18 ); + rtems_debugger_set_int_reg( thread, REG_X19, frame->register_x19 ); + rtems_debugger_set_int_reg( thread, REG_X20, frame->register_x20 ); + rtems_debugger_set_int_reg( thread, REG_X21, frame->register_x21 ); + rtems_debugger_set_int_reg( thread, REG_X22, frame->register_x22 ); + rtems_debugger_set_int_reg( thread, REG_X23, frame->register_x23 ); + rtems_debugger_set_int_reg( thread, REG_X24, frame->register_x24 ); + rtems_debugger_set_int_reg( thread, REG_X25, frame->register_x25 ); + rtems_debugger_set_int_reg( thread, REG_X26, frame->register_x26 ); + rtems_debugger_set_int_reg( thread, REG_X27, frame->register_x27 ); + rtems_debugger_set_int_reg( thread, REG_X28, frame->register_x28 ); + rtems_debugger_set_int_reg( thread, REG_FP, frame->register_fp ); + rtems_debugger_set_int_reg( + thread, + REG_LR, + (intptr_t) frame->register_lr + ); + rtems_debugger_set_int_reg( + thread, + REG_SP, + (intptr_t) frame->register_sp + ); + rtems_debugger_set_int_reg( + thread, + REG_PC, + (intptr_t) frame->register_pc + ); + /* GDB considers CPSR to be 32-bit because bits 63:32 are RES0 */ + rtems_debugger_set_halfint_reg( + thread, + REG_CPS, + (uint32_t) frame->register_cpsr + ); + rtems_debugger_set_fp_reg( thread, REG_V0, frame->register_q0 ); + rtems_debugger_set_fp_reg( thread, REG_V1, frame->register_q1 ); + rtems_debugger_set_fp_reg( thread, REG_V2, frame->register_q2 ); + rtems_debugger_set_fp_reg( thread, REG_V3, frame->register_q3 ); + rtems_debugger_set_fp_reg( thread, REG_V4, frame->register_q4 ); + rtems_debugger_set_fp_reg( thread, REG_V5, frame->register_q5 ); + rtems_debugger_set_fp_reg( thread, REG_V6, frame->register_q6 ); + rtems_debugger_set_fp_reg( thread, REG_V7, frame->register_q7 ); + rtems_debugger_set_fp_reg( thread, REG_V8, frame->register_q8 ); + rtems_debugger_set_fp_reg( thread, REG_V9, frame->register_q9 ); + rtems_debugger_set_fp_reg( thread, REG_V10, frame->register_q10 ); + rtems_debugger_set_fp_reg( thread, REG_V11, frame->register_q11 ); + rtems_debugger_set_fp_reg( thread, REG_V12, frame->register_q12 ); + rtems_debugger_set_fp_reg( thread, REG_V13, frame->register_q13 ); + rtems_debugger_set_fp_reg( thread, REG_V14, frame->register_q14 ); + rtems_debugger_set_fp_reg( thread, REG_V15, frame->register_q15 ); + rtems_debugger_set_fp_reg( thread, REG_V16, frame->register_q16 ); + rtems_debugger_set_fp_reg( thread, REG_V17, frame->register_q17 ); + rtems_debugger_set_fp_reg( thread, REG_V18, frame->register_q18 ); + rtems_debugger_set_fp_reg( thread, REG_V19, frame->register_q19 ); + rtems_debugger_set_fp_reg( thread, REG_V20, frame->register_q20 ); + rtems_debugger_set_fp_reg( thread, REG_V21, frame->register_q21 ); + rtems_debugger_set_fp_reg( thread, REG_V22, frame->register_q22 ); + rtems_debugger_set_fp_reg( thread, REG_V23, frame->register_q23 ); + rtems_debugger_set_fp_reg( thread, REG_V24, frame->register_q24 ); + rtems_debugger_set_fp_reg( thread, REG_V25, frame->register_q25 ); + rtems_debugger_set_fp_reg( thread, REG_V26, frame->register_q26 ); + rtems_debugger_set_fp_reg( thread, REG_V27, frame->register_q27 ); + rtems_debugger_set_fp_reg( thread, REG_V28, frame->register_q28 ); + rtems_debugger_set_fp_reg( thread, REG_V29, frame->register_q29 ); + rtems_debugger_set_fp_reg( thread, REG_V30, frame->register_q30 ); + rtems_debugger_set_fp_reg( thread, REG_V31, frame->register_q31 ); + /* GDB considers FPSR and FPCR to be 32-bit because bits 63:32 are RES0 */ + rtems_debugger_set_halfint_reg( thread, REG_FPS, frame->register_fpsr ); + rtems_debugger_set_halfint_reg( thread, REG_FPC, frame->register_fpcr ); + /* + * Get the signal from the frame. + */ + thread->signal = rtems_debugger_target_exception_to_signal( frame ); + } else { + rtems_debugger_set_int_reg( + thread, + REG_X19, + thread->tcb->Registers.register_x19 + ); + rtems_debugger_set_int_reg( + thread, + REG_X20, + thread->tcb->Registers.register_x20 + ); + rtems_debugger_set_int_reg( + thread, + REG_X21, + thread->tcb->Registers.register_x21 + ); + rtems_debugger_set_int_reg( + thread, + REG_X22, + thread->tcb->Registers.register_x22 + ); + rtems_debugger_set_int_reg( + thread, + REG_X23, + thread->tcb->Registers.register_x23 + ); + rtems_debugger_set_int_reg( + thread, + REG_X24, + thread->tcb->Registers.register_x24 + ); + rtems_debugger_set_int_reg( + thread, + REG_X25, + thread->tcb->Registers.register_x25 + ); + rtems_debugger_set_int_reg( + thread, + REG_X26, + thread->tcb->Registers.register_x26 + ); + rtems_debugger_set_int_reg( + thread, + REG_X27, + thread->tcb->Registers.register_x27 + ); + rtems_debugger_set_int_reg( + thread, + REG_X28, + thread->tcb->Registers.register_x28 + ); + rtems_debugger_set_int_reg( + thread, + REG_FP, + 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_SP, + (intptr_t) thread->tcb->Registers.register_sp + ); + rtems_debugger_set_int_reg( + thread, + REG_PC, + (intptr_t) thread->tcb->Registers.register_lr + ); + rtems_debugger_set_int_reg( + thread, + REG_V8, + thread->tcb->Registers.register_d8 + ); + rtems_debugger_set_int_reg( + thread, + REG_V9, + thread->tcb->Registers.register_d9 + ); + rtems_debugger_set_int_reg( + thread, + REG_V10, + thread->tcb->Registers.register_d10 + ); + rtems_debugger_set_int_reg( + thread, + REG_V11, + thread->tcb->Registers.register_d11 + ); + rtems_debugger_set_int_reg( + thread, + REG_V12, + thread->tcb->Registers.register_d12 + ); + rtems_debugger_set_int_reg( + thread, + REG_V13, + thread->tcb->Registers.register_d13 + ); + rtems_debugger_set_int_reg( + thread, + REG_V14, + thread->tcb->Registers.register_d14 + ); + rtems_debugger_set_int_reg( + thread, + REG_V15, + thread->tcb->Registers.register_d15 + ); + /* + * 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_x0 = rtems_debugger_get_int_reg( thread, REG_X0 ); + frame->register_x1 = rtems_debugger_get_int_reg( thread, REG_X1 ); + frame->register_x2 = rtems_debugger_get_int_reg( thread, REG_X2 ); + frame->register_x3 = rtems_debugger_get_int_reg( thread, REG_X3 ); + frame->register_x4 = rtems_debugger_get_int_reg( thread, REG_X4 ); + frame->register_x5 = rtems_debugger_get_int_reg( thread, REG_X5 ); + frame->register_x6 = rtems_debugger_get_int_reg( thread, REG_X6 ); + frame->register_x7 = rtems_debugger_get_int_reg( thread, REG_X7 ); + frame->register_x8 = rtems_debugger_get_int_reg( thread, REG_X8 ); + frame->register_x9 = rtems_debugger_get_int_reg( thread, REG_X9 ); + frame->register_x10 = rtems_debugger_get_int_reg( thread, REG_X10 ); + frame->register_x11 = rtems_debugger_get_int_reg( thread, REG_X11 ); + frame->register_x12 = rtems_debugger_get_int_reg( thread, REG_X12 ); + frame->register_x13 = rtems_debugger_get_int_reg( thread, REG_X13 ); + frame->register_x14 = rtems_debugger_get_int_reg( thread, REG_X14 ); + frame->register_x15 = rtems_debugger_get_int_reg( thread, REG_X15 ); + frame->register_x16 = rtems_debugger_get_int_reg( thread, REG_X16 ); + frame->register_x17 = rtems_debugger_get_int_reg( thread, REG_X17 ); + frame->register_x18 = rtems_debugger_get_int_reg( thread, REG_X18 ); + frame->register_x19 = rtems_debugger_get_int_reg( thread, REG_X19 ); + frame->register_x20 = rtems_debugger_get_int_reg( thread, REG_X20 ); + frame->register_x21 = rtems_debugger_get_int_reg( thread, REG_X21 ); + frame->register_x22 = rtems_debugger_get_int_reg( thread, REG_X22 ); + frame->register_x23 = rtems_debugger_get_int_reg( thread, REG_X23 ); + frame->register_x24 = rtems_debugger_get_int_reg( thread, REG_X24 ); + frame->register_x25 = rtems_debugger_get_int_reg( thread, REG_X25 ); + frame->register_x26 = rtems_debugger_get_int_reg( thread, REG_X26 ); + frame->register_x27 = rtems_debugger_get_int_reg( thread, REG_X27 ); + frame->register_x28 = rtems_debugger_get_int_reg( thread, REG_X28 ); + frame->register_fp = (uintptr_t) rtems_debugger_get_int_reg( + thread, + REG_FP + ); + frame->register_lr = (void *) (uintptr_t) rtems_debugger_get_int_reg( + thread, + REG_LR + ); + frame->register_sp = (uintptr_t) rtems_debugger_get_int_reg( + thread, + REG_SP + ); + frame->register_pc = (void *) (uintptr_t) rtems_debugger_get_int_reg( + thread, + REG_PC + ); + frame->register_cpsr = rtems_debugger_get_halfint_reg( thread, REG_CPS ); + frame->register_q0 = rtems_debugger_get_fp_reg( thread, REG_V0 ); + frame->register_q1 = rtems_debugger_get_fp_reg( thread, REG_V1 ); + frame->register_q2 = rtems_debugger_get_fp_reg( thread, REG_V2 ); + frame->register_q3 = rtems_debugger_get_fp_reg( thread, REG_V3 ); + frame->register_q4 = rtems_debugger_get_fp_reg( thread, REG_V4 ); + frame->register_q5 = rtems_debugger_get_fp_reg( thread, REG_V5 ); + frame->register_q6 = rtems_debugger_get_fp_reg( thread, REG_V6 ); + frame->register_q7 = rtems_debugger_get_fp_reg( thread, REG_V7 ); + frame->register_q8 = rtems_debugger_get_fp_reg( thread, REG_V8 ); + frame->register_q9 = rtems_debugger_get_fp_reg( thread, REG_V9 ); + frame->register_q10 = rtems_debugger_get_fp_reg( thread, REG_V10 ); + frame->register_q11 = rtems_debugger_get_fp_reg( thread, REG_V11 ); + frame->register_q12 = rtems_debugger_get_fp_reg( thread, REG_V12 ); + frame->register_q13 = rtems_debugger_get_fp_reg( thread, REG_V13 ); + frame->register_q14 = rtems_debugger_get_fp_reg( thread, REG_V14 ); + frame->register_q15 = rtems_debugger_get_fp_reg( thread, REG_V15 ); + frame->register_q16 = rtems_debugger_get_fp_reg( thread, REG_V16 ); + frame->register_q17 = rtems_debugger_get_fp_reg( thread, REG_V17 ); + frame->register_q18 = rtems_debugger_get_fp_reg( thread, REG_V18 ); + frame->register_q19 = rtems_debugger_get_fp_reg( thread, REG_V19 ); + frame->register_q20 = rtems_debugger_get_fp_reg( thread, REG_V20 ); + frame->register_q21 = rtems_debugger_get_fp_reg( thread, REG_V21 ); + frame->register_q22 = rtems_debugger_get_fp_reg( thread, REG_V22 ); + frame->register_q23 = rtems_debugger_get_fp_reg( thread, REG_V23 ); + frame->register_q24 = rtems_debugger_get_fp_reg( thread, REG_V24 ); + frame->register_q25 = rtems_debugger_get_fp_reg( thread, REG_V25 ); + frame->register_q26 = rtems_debugger_get_fp_reg( thread, REG_V26 ); + frame->register_q27 = rtems_debugger_get_fp_reg( thread, REG_V27 ); + frame->register_q28 = rtems_debugger_get_fp_reg( thread, REG_V28 ); + frame->register_q29 = rtems_debugger_get_fp_reg( thread, REG_V29 ); + frame->register_q30 = rtems_debugger_get_fp_reg( thread, REG_V30 ); + frame->register_q31 = rtems_debugger_get_fp_reg( thread, REG_V31 ); + frame->register_fpsr = rtems_debugger_get_halfint_reg( thread, REG_FPS ); + frame->register_fpcr = rtems_debugger_get_halfint_reg( thread, REG_FPC ); + } + + thread->flags &= ~RTEMS_DEBUGGER_THREAD_FLAG_REG_DIRTY; + } + + return 0; +} + +uintptr_t 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; +} + +uintptr_t rtems_debugger_target_frame_pc( CPU_Exception_frame *frame ) +{ + return (uintptr_t) frame->register_pc; +} + +uintptr_t 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; +} + +uintptr_t rtems_debugger_target_tcb_sp( rtems_debugger_thread *thread ) +{ + return (uintptr_t) thread->tcb->Registers.register_sp; +} + +int rtems_debugger_target_thread_stepping( rtems_debugger_thread *thread ) +{ + CPU_Exception_frame *frame = thread->frame; + + if ( rtems_debugger_thread_flag( + thread, + RTEMS_DEBUGGER_THREAD_FLAG_STEP_INSTR + ) ) { + /* Especially on first startup, frame isn't guaranteed to be non-NULL */ + if ( frame == NULL ) { + return -1; + } + + /* + * Single stepping uses AArch64-specific single-step mode and does not + * involve hardware breakpoints. + */ + + /* Breakpoint instruction exceptions occur even when D is not set. */ + uint64_t stepping_enabled = + !( frame->register_cpsr & AARCH64_DSPSR_EL0_D ); + + target_printk( "[} stepping: %s\n", stepping_enabled ? "yes" : "no" ); + + /* + * This field is unset by the CPU during the software step process and must + * be set again each time the debugger needs to advance one instruction. If + * this is not set each time, the software step exception will trigger + * before executing an instruction. + */ + frame->register_cpsr |= AARCH64_DSPSR_EL0_SS; + + if ( !stepping_enabled ) { + /* + * Clear CPSR.D to enable single-step operation. The debug mask flag is + * set on taking an exception to prevent unwanted stepping. The way + * single-stepping works will need to change if hardware breakpoints and + * watchpoints are ever used. + */ + frame->register_cpsr &= ~AARCH64_DSPSR_EL0_D; + } + } + + return 0; +} + +int rtems_debugger_target_exception_to_signal( CPU_Exception_frame *frame ) +{ + uint64_t EC = AARCH64_ESR_EL1_EC_GET( frame->register_syndrome ); + + switch ( EC ) { + case 0x1: /* WFI */ + case 0x7: /* SVE/SIMD/FP */ + case 0xa: /* LD64B/ST64B* */ + case 0x15: + case 0x18: /* MSR/MRS/system instruction */ + case 0x19: /* SVE */ + case 0x31: + case 0x33: + case 0x35: + case 0x3c: + return RTEMS_DEBUGGER_SIGNAL_TRAP; + + case 0x2c: + return RTEMS_DEBUGGER_SIGNAL_FPE; + + case 0x21: + case 0x25: + return RTEMS_DEBUGGER_SIGNAL_SEGV; + + default: + /* + * Covers unknown, SP/PC alignment, illegal execution state, and any new + * exception classes that get added. + */ + return RTEMS_DEBUGGER_SIGNAL_ILL; + } +} + +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, these are loaded elsewhere if needed. + */ + return 0; +} + +int rtems_debugger_target_hwbreak_remove( void ) +{ +#ifdef HARDWARE_BREAKPOINTS_NOT_USED + aarch64_debug_break_unload(); +#endif + return 0; +} + +int rtems_debugger_target_hwbreak_control( + rtems_debugger_target_watchpoint wp, + bool insert, + uintptr_t 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; +} diff --git a/cpukit/libdebugger/rtems-debugger-arm.c b/cpukit/libdebugger/rtems-debugger-arm.c index ba01a860c8..cdc615ce64 100644 --- a/cpukit/libdebugger/rtems-debugger-arm.c +++ b/cpukit/libdebugger/rtems-debugger-arm.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2019 Chris Johns <chrisj@rtems.org>. + * Copyright (c) 2016-2022 Chris Johns <chrisj@rtems.org>. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -86,14 +86,26 @@ #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 /* @@ -287,29 +299,19 @@ static const size_t exc_offsets[2][5] = static bool debug_session_active; /* - * ARM debug hardware. + * 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 bool debug_disable_ints; +static int 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) @@ -327,8 +329,23 @@ typedef struct #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]; +/* + * 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. @@ -439,6 +456,7 @@ arm_moe_label(uint32_t moe) 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)); \ @@ -449,6 +467,7 @@ arm_moe_label(uint32_t moe) 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 \ @@ -491,9 +510,21 @@ arm_moe_label(uint32_t moe) * 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) +#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) @@ -902,16 +933,17 @@ arm_debug_mmap_enable(rtems_debugger_target* target, uint32_t dbgdidr) * which seems to make the debug hardware work. */ if (ARM_MMAP_READ(1005) == 3) { - ARM_MMAP_WRITE(1004, 0xC5ACCE55); + ARM_MMAP_WRITE_SYNC(1004, 0xC5ACCE55); } /* * Are we already in debug mode? */ - val = ARM_MMAP_READ(34); + 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(34); - ARM_MMAP_WRITE(34, ARM_MMAP_READ(34) | (1 << 15)); + 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; } } @@ -1002,139 +1034,125 @@ arm_debug_probe(rtems_debugger_target* target) } 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) +arm_debug_break_setup(int 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; + 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_write_control(int bp, uint32_t control) +arm_debug_break_c14_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; - } - } + 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) +arm_debug_break_c14_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; - } - } + 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; } } @@ -1143,7 +1161,7 @@ arm_debug_dbgdscr_read(void) { uint32_t val; if (debug_registers != NULL) { - val = ARM_MMAP_READ(34); + val = ARM_MMAP_READ(ARM_MMAP_DBGDSCR); } else { ARM_CP14_READ(val, 0, 1, 0); @@ -1155,7 +1173,7 @@ static void arm_debug_dbgdscr_write(uint32_t val) { if (debug_registers != NULL) { - ARM_MMAP_WRITE(34, val); + ARM_MMAP_WRITE_SYNC(ARM_MMAP_DBGDSCR, val); } else { ARM_CP14_WRITE(val, 0, 1, 0); @@ -1171,35 +1189,30 @@ arm_debug_method_of_entry(void) static void arm_debug_disable_interrupts(void) { - debug_disable_ints = true; + debug_disable_ints = 1; } static void -arm_debug_commit_interrupt_disable(void) +arm_debug_enable_interrupts(void) { - if (debug_disable_ints) { - arm_debug_dbgdscr_write(arm_debug_dbgdscr_read() | (1 << 11)); - debug_disable_ints = false; - } + arm_debug_dbgdscr_write(arm_debug_dbgdscr_read() & ~(1 << 11)); } static void -arm_debug_enable_interrupts(void) +arm_debug_break_clear(int bp) { - arm_debug_dbgdscr_write(arm_debug_dbgdscr_read() & ~(1 << 11)); + 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(void) +arm_debug_break_clear_all(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; - } + ARM_HWB_CLEAR_ALL(); rtems_interrupt_lock_release(&target_lock, &lock_context); } @@ -1211,58 +1224,81 @@ arm_debug_set_context_id(const uint32_t id) #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) +arm_debug_break_unload(void) { rtems_interrupt_lock_context lock_context; - arm_debug_hwbreak* bp = &hw_breaks[0]; - int i; + 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); + 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_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); +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 */ + } } - rtems_interrupt_lock_release(&target_lock, &lock_context); + 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 - arm_debug_hwbreak* bp = &hw_breaks[0]; int i; - for (i = 0; i < hw_breakpoints; ++i, ++bp) { - if (bp->enabled) { + for (i = 0; i < hw_breakpoints; ++i) { + if (ARM_HWB_ENALBED(i)) { target_printk("[} bp: %d: control: %08x addr: %08x\n", - i, bp->control, bp->value); + i, ARM_HWB_BCR(i), ARM_HWB_VCR(i)); } } #endif @@ -1314,6 +1350,8 @@ 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 @@ -1341,18 +1379,20 @@ target_exception(CPU_Exception_frame* frame) target_printk("[} > frame = %08" PRIx32 \ " sig=%d vector=%u (%u) dbgdscr=%08" PRIx32 " moe=%s" \ - " ifsr=%08" PRIx32 " pra=%08x\n", - (uint32_t) frame, + " 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), - ifsr, (intptr_t) frame->register_pc); + 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(); - arm_debug_break_clear(); if (!debug_session_active) _ARM_Exception_default(frame); @@ -1373,10 +1413,16 @@ target_exception(CPU_Exception_frame* frame) " 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 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 @@ -1408,15 +1454,89 @@ target_exception(CPU_Exception_frame* frame) */ #define EXCEPTION_ENTRY_EXC() \ __asm__ volatile( \ - ASM_ARM_MODE \ + 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 */ \ - "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") + : "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. @@ -1430,10 +1550,10 @@ target_exception(CPU_Exception_frame* frame) "mov r3, #0\n" \ "mov r4, #0\n" \ "adds r6, r5, #128\n" \ - "3:\n" \ + "1:\n" \ "stmia r5!, {r3-r4}\n" \ "cmp r5, r6\n" \ - "bne 3b\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 */ \ @@ -1453,9 +1573,6 @@ target_exception(CPU_Exception_frame* frame) #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 */ \ @@ -1468,7 +1585,7 @@ target_exception(CPU_Exception_frame* frame) "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 */ \ + "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 */ \ @@ -1491,8 +1608,7 @@ target_exception(CPU_Exception_frame* frame) 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), \ @@ -1501,7 +1617,7 @@ target_exception(CPU_Exception_frame* frame) [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") + : "cc", "r0", "r1", "r2", "r3", "r4", "r5", "r6", "memory") /* * FPU exit. Conditionally D16 or D32 support. @@ -1537,9 +1653,8 @@ target_exception(CPU_Exception_frame* frame) */ #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 */ \ + "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 */ \ @@ -1562,11 +1677,8 @@ target_exception(CPU_Exception_frame* frame) "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)), \ @@ -1574,19 +1686,135 @@ target_exception(CPU_Exception_frame* frame) [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") + : "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( \ - 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 */ \ + ARM_SYNC_INST \ : \ : [frame_size] "i" (EXCEPTION_FRAME_SIZE) \ - : "memory") + : "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. @@ -1596,9 +1824,10 @@ static void __attribute__((naked)) arm_debug_unlock_abort(void) { CPU_Exception_frame* frame; - ARM_SWITCH_REG; + ARM_SWITCH_REGISTERS; EXCEPTION_ENTRY_EXC(); EXCEPTION_ENTRY_THREAD(frame); + ARM_SWITCH_BACK; longjmp(unlock_abort_jmpbuf, -1); } #endif @@ -1608,16 +1837,10 @@ 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); + EXCEPTION_ENTRY(frame); frame->vector = 1; target_exception(frame); - EXCEPTION_EXIT_THREAD(frame); - arm_debug_commit_interrupt_disable(); - arm_debug_break_load(); - EXCEPTION_EXIT_EXC(); + EXCEPTION_EXIT(frame); } static void __attribute__((naked)) @@ -1631,16 +1854,10 @@ target_exception_supervisor_call(void) * 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); + EXCEPTION_ENTRY(frame); frame->vector = 2; target_exception(frame); - EXCEPTION_EXIT_THREAD(frame); - arm_debug_commit_interrupt_disable(); - arm_debug_break_load(); - EXCEPTION_EXIT_EXC(); + EXCEPTION_EXIT(frame); } static void __attribute__((naked)) @@ -1648,16 +1865,10 @@ 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); + EXCEPTION_ENTRY(frame); frame->vector = 3; target_exception(frame); - EXCEPTION_EXIT_THREAD(frame); - arm_debug_commit_interrupt_disable(); - arm_debug_break_load(); - EXCEPTION_EXIT_EXC(); + EXCEPTION_EXIT(frame); } static void __attribute__((naked)) @@ -1665,16 +1876,10 @@ 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); + EXCEPTION_ENTRY(frame); frame->vector = 4; target_exception(frame); - EXCEPTION_EXIT_THREAD(frame); - arm_debug_commit_interrupt_disable(); - arm_debug_break_load(); - EXCEPTION_EXIT_EXC(); + EXCEPTION_EXIT(frame); } #if ARM_CP15 @@ -1776,13 +1981,13 @@ int rtems_debugger_target_enable(void) { rtems_interrupt_lock_context lock_context; - debug_session_active = true; arm_debug_break_unload(); - arm_debug_break_clear(); + 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; } @@ -1795,8 +2000,9 @@ rtems_debugger_target_disable(void) void* text_end; #endif arm_debug_break_unload(); - arm_debug_break_clear(); + 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]; @@ -1979,57 +2185,13 @@ rtems_debugger_target_thread_stepping(rtems_debugger_thread* thread) * 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"); + target_printk("[} stepping: hbp[0] enabled: %s\n", ARM_HWB_ENALBED(0) ? "yes" : "no"); - if (!bp->enabled) { + 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; - 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_break_exec_enable(0, addr, thumb, true); arm_debug_disable_interrupts(); } } @@ -2085,6 +2247,7 @@ rtems_debugger_target_hwbreak_insert(void) int rtems_debugger_target_hwbreak_remove(void) { + target_printk("[} hbreak: remove: unload\n"); arm_debug_break_unload(); return 0; } @@ -2095,15 +2258,34 @@ rtems_debugger_target_hwbreak_control(rtems_debugger_target_watchpoint wp, DB_UINT addr, DB_UINT kind) { - /* - * To do. - */ + 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. */ diff --git a/cpukit/libdebugger/rtems-debugger-i386.c b/cpukit/libdebugger/rtems-debugger-i386.c index a2396e5f30..02e29c25a1 100644 --- a/cpukit/libdebugger/rtems-debugger-i386.c +++ b/cpukit/libdebugger/rtems-debugger-i386.c @@ -376,7 +376,7 @@ rtems_debugger_target_write_regs(rtems_debugger_thread* thread) return 0; } -DB_UINT +uintptr_t rtems_debugger_target_reg_pc(rtems_debugger_thread* thread) { int r; @@ -387,13 +387,13 @@ rtems_debugger_target_reg_pc(rtems_debugger_thread* thread) return 0; } -DB_UINT +uintptr_t rtems_debugger_target_frame_pc(CPU_Exception_frame* frame) { - return (DB_UINT) frame->eip; + return (uintptr_t) frame->eip; } -DB_UINT +uintptr_t rtems_debugger_target_reg_sp(rtems_debugger_thread* thread) { int r; @@ -404,7 +404,7 @@ rtems_debugger_target_reg_sp(rtems_debugger_thread* thread) return 0; } -DB_UINT +uintptr_t rtems_debugger_target_tcb_sp(rtems_debugger_thread* thread) { return (DB_UINT) thread->tcb->Registers.esp; @@ -503,7 +503,7 @@ rtems_debugger_target_hwbreak_remove(void) int rtems_debugger_target_hwbreak_control(rtems_debugger_target_watchpoint wp, bool insert, - DB_UINT addr, + uintptr_t addr, DB_UINT kind) { /* diff --git a/cpukit/libdebugger/rtems-debugger-microblaze.c b/cpukit/libdebugger/rtems-debugger-microblaze.c new file mode 100644 index 0000000000..377a731664 --- /dev/null +++ b/cpukit/libdebugger/rtems-debugger-microblaze.c @@ -0,0 +1,1393 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + +/** + * @file + * + * @ingroup RTEMSLibdebugger + * + * @brief MicroBlaze libdebugger implementation + */ + +/* + * Copyright (C) 2022 On-Line Applications Research Corporation (OAR) + * + * 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 COPYRIGHT HOLDERS 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 COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#define TARGET_DEBUG 0 + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <errno.h> +#include <inttypes.h> +#include <stdlib.h> + +/* Defined by linkcmds.base */ +extern char bsp_section_text_begin[]; +extern char bsp_section_text_end[]; +extern char bsp_section_fast_text_begin[]; +extern char bsp_section_fast_text_end[]; + +#include <rtems.h> +#include <rtems/score/cpu.h> +#include <rtems/score/threadimpl.h> +#include <rtems/score/userextimpl.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 + +/* + * Number of registers. + */ +#define RTEMS_DEBUGGER_NUMREGS 57 + +/* + * Number of bytes per type of register. + */ +#define RTEMS_DEBUGGER_REG_BYTES 4 + +/* Debugger registers layout. See microblaze-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_R13 13 +#define REG_R14 14 +#define REG_R15 15 +#define REG_R16 16 +#define REG_R17 17 +#define REG_R18 18 +#define REG_R19 19 +#define REG_R20 20 +#define REG_R21 21 +#define REG_R22 22 +#define REG_R23 23 +#define REG_R24 24 +#define REG_R25 25 +#define REG_R26 26 +#define REG_R27 27 +#define REG_R28 28 +#define REG_R29 29 +#define REG_R30 30 +#define REG_R31 31 +#define REG_PC 32 +#define REG_MS 33 +#define REG_EA 34 +#define REG_ES 35 +#define REG_FS 36 +#define REG_BT 37 +#define REG_PV0 38 +#define REG_PV1 39 +#define REG_PV2 40 +#define REG_PV3 41 +#define REG_PV4 42 +#define REG_PV5 43 +#define REG_PV6 44 +#define REG_PV7 45 +#define REG_PV8 46 +#define REG_PV9 47 +#define REG_PV10 48 +#define REG_PV11 49 +#define REG_ED 50 +#define REG_PID 51 +#define REG_ZP 52 +#define REG_TBLX 53 +#define REG_TBLSX 54 +#define REG_TBLLO 55 +#define REG_TBLHI 56 + +/** + * 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 microblaze_reg_offsets[ RTEMS_DEBUGGER_NUMREGS + 1 ] = { + REG_R0 * 4, + REG_R1 * 4, + REG_R2 * 4, + REG_R3 * 4, + REG_R4 * 4, + REG_R5 * 4, + REG_R6 * 4, + REG_R7 * 4, + REG_R8 * 4, + REG_R9 * 4, + REG_R10 * 4, + REG_R11 * 4, + REG_R12 * 4, + REG_R13 * 4, + REG_R14 * 4, + REG_R15 * 4, + REG_R16 * 4, + REG_R17 * 4, + REG_R18 * 4, + REG_R19 * 4, + REG_R20 * 4, + REG_R21 * 4, + REG_R22 * 4, + REG_R23 * 4, + REG_R24 * 4, + REG_R25 * 4, + REG_R26 * 4, + REG_R27 * 4, + REG_R28 * 4, + REG_R29 * 4, + REG_R30 * 4, + REG_R31 * 4, + REG_PC * 4, + REG_MS * 4, + REG_EA * 4, + REG_ES * 4, + REG_FS * 4, + REG_BT * 4, + REG_PV0 * 4, + REG_PV1 * 4, + REG_PV2 * 4, + REG_PV3 * 4, + REG_PV4 * 4, + REG_PV5 * 4, + REG_PV6 * 4, + REG_PV7 * 4, + REG_PV8 * 4, + REG_PV9 * 4, + REG_PV10 * 4, + REG_PV11 * 4, + REG_ED * 4, + REG_PID * 4, + REG_ZP * 4, + REG_TBLX * 4, + REG_TBLSX * 4, + REG_TBLLO * 4, + REG_TBLHI * 4, +/* Total size */ + REG_TBLHI * 4 + 4, +}; + +/* + * Number of bytes of registers. + */ +#define RTEMS_DEBUGGER_NUMREGBYTES \ + microblaze_reg_offsets[ RTEMS_DEBUGGER_NUMREGS ] + +/** + * Print the exception frame. + */ +#define EXC_FRAME_PRINT( _out, _prefix, _frame ) \ + do { \ + _out( \ + _prefix " R0 = 0x%08" PRIx32 " R1 = 0x%08" PRIx32 \ + " R2 = 0x%08" PRIx32 " R3 = 0x%08" PRIx32 "\n", \ + 0, \ + _frame->r1, \ + _frame->r2, \ + _frame->r3 \ + ); \ + _out( \ + _prefix " R4 = 0x%08" PRIx32 " R5 = 0x%08" PRIx32 \ + " R6 = 0x%08" PRIx32 " R7 = 0x%08" PRIx32 "\n", \ + _frame->r4, \ + _frame->r5, \ + _frame->r6, \ + _frame->r7 \ + ); \ + _out( \ + _prefix " R8 = 0x%08" PRIx32 " R9 = 0x%08" PRIx32 \ + " R10 = 0x%08" PRIx32 " R11 = 0x%08" PRIx32 "\n", \ + _frame->r8, \ + _frame->r9, \ + _frame->r10, \ + _frame->r11 \ + ); \ + _out( \ + _prefix " R12 = 0x%08" PRIx32 " R13 = 0x%08" PRIx32 \ + " R14 = 0x%08" PRIxPTR " R15 = 0x%08" PRIxPTR "\n", \ + _frame->r12, \ + _frame->r13, \ + (uintptr_t) _frame->r14, \ + (uintptr_t) _frame->r15 \ + ); \ + _out( \ + _prefix " R16 = 0x%08" PRIxPTR " R17 = 0x%08" PRIxPTR \ + " R18 = 0x%08" PRIx32 " R19 = 0x%08" PRIx32 "\n", \ + (uintptr_t) _frame->r16, \ + (uintptr_t) _frame->r17, \ + _frame->r18, \ + _frame->r19 \ + ); \ + _out( \ + _prefix " R20 = 0x%08" PRIx32 " R21 = 0x%08" PRIx32 \ + " R22 = 0x%08" PRIx32 " R23 = 0x%08" PRIx32 "\n", \ + _frame->r20, \ + _frame->r21, \ + _frame->r22, \ + _frame->r23 \ + ); \ + _out( \ + _prefix " R24 = 0x%08" PRIx32 " R25 = 0x%08" PRIx32 \ + " R26 = 0x%08" PRIx32 " R27 = 0x%08" PRIx32 "\n", \ + _frame->r24, \ + _frame->r25, \ + _frame->r26, \ + _frame->r27 \ + ); \ + _out( \ + _prefix " R28 = 0x%08" PRIx32 " R29 = 0x%08" PRIx32 \ + " R30 = 0x%08" PRIxPTR " R31 = 0x%08" PRIxPTR "\n", \ + _frame->r28, \ + _frame->r29, \ + _frame->r30, \ + _frame->r31 \ + ); \ + _out( \ + _prefix " EAR = %p ESR = 0x%08" PRIx32 "\n", \ + _frame->ear, \ + _frame->esr \ + ); \ + _out( \ + _prefix " PC = %p\n", \ + _frame->r16 \ + ); \ + _out( \ + _prefix " MSR = 0x%08" PRIx32 " En:%c%c%c%c Prog:%c%c%c Mode:%c%c Arith:%c%c\n", \ + _frame->msr, \ + ( _frame->msr & MICROBLAZE_MSR_IE ) != 0 ? 'I' : '-', \ + ( _frame->msr & MICROBLAZE_MSR_ICE ) != 0 ? 'C' : '-', \ + ( _frame->msr & MICROBLAZE_MSR_DCE ) != 0 ? 'D' : '-', \ + ( _frame->msr & MICROBLAZE_MSR_EE ) != 0 ? 'E' : '-', \ + ( _frame->msr & MICROBLAZE_MSR_BIP ) != 0 ? 'B' : '-', \ + ( _frame->msr & MICROBLAZE_MSR_FSL ) != 0 ? 'F' : '-', \ + ( _frame->msr & MICROBLAZE_MSR_EIP ) != 0 ? 'E' : '-', \ + ( _frame->msr & MICROBLAZE_MSR_UM ) != 0 ? 'U' : '-', \ + ( _frame->msr & MICROBLAZE_MSR_VM ) != 0 ? 'V' : '-', \ + ( _frame->msr & MICROBLAZE_MSR_C ) != 0 ? 'C' : '-', \ + ( _frame->msr & MICROBLAZE_MSR_DZO ) != 0 ? 'Z' : '-' \ + ); \ + } while ( 0 ) + +/** + * The breakpoint instruction can be intercepted on hardware by an active JTAG + * connection. This instead uses an illegal opcode (0xdeadbeef) to trigger an + * exception as a mechanism to call into the debugger. + */ +static const uint8_t breakpoint[ 4 ] = { 0xef, 0xbe, 0xad, 0xde }; + +/** + * Target lock. + */ +RTEMS_INTERRUPT_LOCK_DEFINE( static, target_lock, "target_lock" ) + +/** + * Is a session active? + */ +static bool debug_session_active; + +/* + * MicroBlaze debug hardware. + */ +static uint8_t hw_breakpoints; +static uint8_t hw_read_watchpoints; +static uint8_t hw_write_watchpoints; + +/* Software breakpoints for single stepping */ +typedef struct { + uint32_t *address; +} microblaze_soft_step; + +microblaze_soft_step next_soft_break = { 0 }; +microblaze_soft_step target_soft_break = { 0 }; + +static void set_soft_break( + microblaze_soft_step *soft_break, + uint32_t *next_ins +) +{ + soft_break->address = next_ins; + rtems_debugger_target_swbreak_control( + true, + (uintptr_t) soft_break->address, + 4 + ); +} + +static void restore_soft_step( microblaze_soft_step *bp ) +{ + /* + * Only restore if the breakpoint is active and the instruction at the address + * is the breakpoint instruction. + */ + if ( bp->address != NULL ) { + rtems_debugger_target_swbreak_control( false, (uintptr_t) bp->address, 4 ); + } + + bp->address = NULL; +} + +/* + * 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, ... ) +#endif + +static int microblaze_debug_probe( rtems_debugger_target *target ) +{ + uint32_t msr; + uint32_t pvr0; + uint32_t pvr3; + const char *version = NULL; + + rtems_debugger_printf( + "rtems-db: MicroBlaze\n" + ); + + _CPU_MSR_GET( msr ); + + if ( ( msr & MICROBLAZE_MSR_PVR ) == 0 ) { + rtems_debugger_printf( + "rtems-db: Processor Version Registers not supported\n" + ); + return 0; + } + + _CPU_PVR0_GET( pvr0 ); + + switch ( MICROBLAZE_PVR0_VERSION_GET( pvr0 ) ) { + case 0x1: + version = "v5.00.a"; + break; + case 0x2: + version = "v5.00.b"; + break; + case 0x3: + version = "v5.00.c"; + break; + case 0x4: + version = "v6.00.a"; + break; + case 0x5: + version = "v7.00.a"; + break; + case 0x6: + version = "v6.00.b"; + break; + case 0x7: + version = "v7.00.b"; + break; + case 0x8: + version = "v7.10.a"; + break; + } + + rtems_debugger_printf( + "rtems-db: Version: %s (%d)\n", + version, + MICROBLAZE_PVR0_VERSION_GET( pvr0 ) + ); + + /* further PVR supported? */ + if ( ( pvr0 >> 31 ) == 0 ) { + rtems_debugger_printf( + "rtems-db: Further Processor Version Registers not supported\n" + ); + return 0; + } + + _CPU_PVR3_GET( pvr3 ); + + hw_breakpoints = MICROBLAZE_PVR3_BP_GET( pvr3 ); + hw_read_watchpoints = MICROBLAZE_PVR3_RWP_GET( pvr3 ); + hw_write_watchpoints = MICROBLAZE_PVR3_WWP_GET( pvr3 ); + + rtems_debugger_printf( + "rtems-db: breakpoints:%" PRIu32 + " read watchpoints:%" PRIu32 " write watchpoints:%" PRIu32 "\n", + hw_breakpoints, + hw_read_watchpoints, + hw_write_watchpoints + ); + + return 0; +} + +int rtems_debugger_target_configure( rtems_debugger_target *target ) +{ + target->capabilities = ( RTEMS_DEBUGGER_TARGET_CAP_SWBREAK + | RTEMS_DEBUGGER_TARGET_CAP_PURE_SWBREAK ); + target->reg_num = RTEMS_DEBUGGER_NUMREGS; + target->reg_offset = microblaze_reg_offsets; + target->breakpoint = &breakpoint[ 0 ]; + target->breakpoint_size = sizeof( breakpoint ); + return microblaze_debug_probe( target ); +} + +static void target_print_frame( CPU_Exception_frame *frame ) +{ + EXC_FRAME_PRINT( target_printk, "[} ", frame ); +} + +/* returns true if cascade is required */ +static bool target_exception( CPU_Exception_frame *frame ) +{ + target_print_frame( 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" ); + /* Continue in fatal error handler chain */ + return true; + } + + target_printk( + "[} < resuming frame = %016" PRIxPTR "\n", + (uintptr_t) frame + ); + target_print_frame( frame ); + + return false; +} + +static void target_exception_handler( CPU_Exception_frame *ef ) +{ + if ( debug_session_active == false ) { + /* Falls into fatal error handler */ + return; + } + + /* + * Blindly roll back R17 in the exception frame due to exceptions resuming at + * the next instruction and not the instruction that caused the exception. + * TODO(kmoore): This does not apply in all cases for MicroBlaze cores that + * have a MMU and can generate MMU exceptions. + */ + ef->r17 = &ef->r17[ -1 ]; + + /* + * Remove single step software breakpoints since they will need to be + * recalculated for the current instruction. + */ + restore_soft_step( &next_soft_break ); + restore_soft_step( &target_soft_break ); + + /* disable all software breakpoints */ + rtems_debugger_target_swbreak_remove(); + + if ( target_exception( ef ) == true ) { + /* Roll R17 forward for an accurate frame in the fatal error handler */ + ef->r17 = &ef->r17[ 1 ]; + + /* Falls into fatal error handler */ + return; + } + + /* Enable all software breakpoints including added single-step breakpoints */ + rtems_debugger_target_swbreak_insert(); + + /* does not return */ + _CPU_Exception_resume( ef ); +} + +static void rtems_debugger_set_int_reg( + rtems_debugger_thread *thread, + size_t reg, + const uint32_t value +) +{ + const size_t offset = microblaze_reg_offsets[ reg ]; + + 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 = microblaze_reg_offsets[ reg ]; + uint32_t value; + + memcpy( &value, &thread->registers[ offset ], sizeof( uint32_t ) ); + return value; +} + +static bool tid_is_excluded( const rtems_id tid ) +{ + rtems_debugger_threads *threads = rtems_debugger->threads; + rtems_id *excludes; + size_t i; + + excludes = rtems_debugger_thread_excludes( threads ); + + for ( i = 0; i < threads->excludes.level; ++i ) { + if ( tid == excludes[ i ] ) { + return true; + } + } + + /* DBSe is dynamically created and destroyed, so might not actually be in the excludes list */ + char name[ RTEMS_DEBUGGER_THREAD_NAME_SIZE ]; + + rtems_object_get_name( tid, sizeof( name ), (char *) &name[ 0 ] ); + + if ( strcmp( "DBSe", name ) == 0 ) { + return true; + } + + return false; +} + +static void mb_thread_switch( Thread_Control *executing, Thread_Control *heir ) +{ + if ( tid_is_excluded( heir->Object.id ) == true ) { + rtems_debugger_target_swbreak_remove(); + return; + } + + /* Insert all software breaks */ + rtems_debugger_target_swbreak_insert(); +} + +User_extensions_Control mb_ext = { + .Callouts = { .thread_switch = mb_thread_switch } +}; + +int rtems_debugger_target_enable( void ) +{ + debug_session_active = true; + rtems_interrupt_lock_context lock_context; + + rtems_interrupt_lock_acquire( &target_lock, &lock_context ); + + _MicroBlaze_Debug_install_handler( target_exception_handler, NULL ); + _MicroBlaze_Exception_install_handler( target_exception_handler, NULL ); + _User_extensions_Add_set( &mb_ext ); + + rtems_interrupt_lock_release( &target_lock, &lock_context ); + return RTEMS_SUCCESSFUL; +} + +int rtems_debugger_target_disable( void ) +{ + debug_session_active = false; + rtems_interrupt_lock_context lock_context; + + rtems_interrupt_lock_acquire( &target_lock, &lock_context ); + + _User_extensions_Remove_set( &mb_ext ); + + rtems_interrupt_lock_release( &target_lock, &lock_context ); + return RTEMS_SUCCESSFUL; +} + +int rtems_debugger_target_read_regs( rtems_debugger_thread *thread ) +{ + if ( + rtems_debugger_thread_flag( + thread, + RTEMS_DEBUGGER_THREAD_FLAG_REG_VALID + ) == 0 + ) { + static const uintptr_t good_address = (uintptr_t) &good_address; + int i; + + memset( &thread->registers[ 0 ], 0, RTEMS_DEBUGGER_NUMREGBYTES ); + + /* set all integer register to a known valid address */ + for ( i = 0; i < RTEMS_DEBUGGER_NUMREGS; ++i ) { + rtems_debugger_set_int_reg( thread, i, (uintptr_t) &good_address ); + } + + if ( thread->frame != NULL ) { + CPU_Exception_frame *frame = thread->frame; + + *( (CPU_Exception_frame *) thread->registers ) = *frame; + rtems_debugger_set_int_reg( thread, REG_R0, 0 ); + rtems_debugger_set_int_reg( thread, REG_R1, frame->r1 ); + rtems_debugger_set_int_reg( thread, REG_R2, frame->r2 ); + rtems_debugger_set_int_reg( thread, REG_R3, frame->r3 ); + rtems_debugger_set_int_reg( thread, REG_R4, frame->r4 ); + rtems_debugger_set_int_reg( thread, REG_R5, frame->r5 ); + rtems_debugger_set_int_reg( thread, REG_R6, frame->r6 ); + rtems_debugger_set_int_reg( thread, REG_R7, frame->r7 ); + rtems_debugger_set_int_reg( thread, REG_R8, frame->r8 ); + rtems_debugger_set_int_reg( thread, REG_R9, frame->r9 ); + rtems_debugger_set_int_reg( thread, REG_R10, frame->r10 ); + rtems_debugger_set_int_reg( thread, REG_R11, frame->r11 ); + rtems_debugger_set_int_reg( thread, REG_R12, frame->r12 ); + rtems_debugger_set_int_reg( thread, REG_R13, frame->r13 ); + rtems_debugger_set_int_reg( thread, REG_R14, (uintptr_t) frame->r14 ); + rtems_debugger_set_int_reg( thread, REG_R15, (uintptr_t) frame->r15 ); + rtems_debugger_set_int_reg( thread, REG_R16, (uintptr_t) frame->r16 ); + rtems_debugger_set_int_reg( thread, REG_R17, (uintptr_t) frame->r17 ); + rtems_debugger_set_int_reg( thread, REG_R18, frame->r18 ); + rtems_debugger_set_int_reg( thread, REG_R19, frame->r19 ); + rtems_debugger_set_int_reg( thread, REG_R20, frame->r20 ); + rtems_debugger_set_int_reg( thread, REG_R21, frame->r21 ); + rtems_debugger_set_int_reg( thread, REG_R22, frame->r22 ); + rtems_debugger_set_int_reg( thread, REG_R23, frame->r23 ); + rtems_debugger_set_int_reg( thread, REG_R24, frame->r24 ); + rtems_debugger_set_int_reg( thread, REG_R25, frame->r25 ); + rtems_debugger_set_int_reg( thread, REG_R26, frame->r26 ); + rtems_debugger_set_int_reg( thread, REG_R27, frame->r27 ); + rtems_debugger_set_int_reg( thread, REG_R28, frame->r28 ); + rtems_debugger_set_int_reg( thread, REG_R29, frame->r29 ); + rtems_debugger_set_int_reg( thread, REG_R30, frame->r30 ); + rtems_debugger_set_int_reg( thread, REG_R31, frame->r31 ); + rtems_debugger_set_int_reg( + thread, + REG_PC, + rtems_debugger_target_frame_pc( frame ) + ); + rtems_debugger_set_int_reg( thread, REG_MS, frame->msr ); + rtems_debugger_set_int_reg( thread, REG_EA, (uintptr_t) frame->ear ); + rtems_debugger_set_int_reg( thread, REG_ES, frame->esr ); + rtems_debugger_set_int_reg( thread, REG_BT, (uintptr_t) frame->btr ); + /* + * Get the signal from the frame. + */ + thread->signal = rtems_debugger_target_exception_to_signal( frame ); + } else { + rtems_debugger_set_int_reg( + thread, + REG_R1, + thread->tcb->Registers.r1 + ); + rtems_debugger_set_int_reg( + thread, + REG_R13, + thread->tcb->Registers.r13 + ); + rtems_debugger_set_int_reg( + thread, + REG_R14, + thread->tcb->Registers.r14 + ); + rtems_debugger_set_int_reg( + thread, + REG_R15, + thread->tcb->Registers.r15 + ); + rtems_debugger_set_int_reg( + thread, + REG_R16, + thread->tcb->Registers.r16 + ); + rtems_debugger_set_int_reg( + thread, + REG_R17, + thread->tcb->Registers.r17 + ); + rtems_debugger_set_int_reg( + thread, + REG_R18, + thread->tcb->Registers.r18 + ); + rtems_debugger_set_int_reg( + thread, + REG_R19, + thread->tcb->Registers.r19 + ); + rtems_debugger_set_int_reg( + thread, + REG_R20, + thread->tcb->Registers.r20 + ); + rtems_debugger_set_int_reg( + thread, + REG_R21, + thread->tcb->Registers.r21 + ); + rtems_debugger_set_int_reg( + thread, + REG_R22, + thread->tcb->Registers.r22 + ); + rtems_debugger_set_int_reg( + thread, + REG_R23, + thread->tcb->Registers.r23 + ); + rtems_debugger_set_int_reg( + thread, + REG_R24, + thread->tcb->Registers.r24 + ); + rtems_debugger_set_int_reg( + thread, + REG_R25, + thread->tcb->Registers.r25 + ); + rtems_debugger_set_int_reg( + thread, + REG_R26, + thread->tcb->Registers.r26 + ); + rtems_debugger_set_int_reg( + thread, + REG_R27, + thread->tcb->Registers.r27 + ); + rtems_debugger_set_int_reg( + thread, + REG_R28, + thread->tcb->Registers.r28 + ); + rtems_debugger_set_int_reg( + thread, + REG_R29, + thread->tcb->Registers.r29 + ); + rtems_debugger_set_int_reg( + thread, + REG_R30, + thread->tcb->Registers.r30 + ); + rtems_debugger_set_int_reg( + thread, + REG_R31, + thread->tcb->Registers.r31 + ); + rtems_debugger_set_int_reg( + thread, + REG_MS, + (intptr_t) thread->tcb->Registers.rmsr + ); + /* + * 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 + ) != 0 + ) { + /* + * 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 + ) != 0 + ) { + CPU_Exception_frame *frame = thread->frame; + frame->r1 = rtems_debugger_get_int_reg( thread, REG_R1 ); + frame->r2 = rtems_debugger_get_int_reg( thread, REG_R2 ); + frame->r3 = rtems_debugger_get_int_reg( thread, REG_R3 ); + frame->r4 = rtems_debugger_get_int_reg( thread, REG_R4 ); + frame->r5 = rtems_debugger_get_int_reg( thread, REG_R5 ); + frame->r6 = rtems_debugger_get_int_reg( thread, REG_R6 ); + frame->r7 = rtems_debugger_get_int_reg( thread, REG_R7 ); + frame->r8 = rtems_debugger_get_int_reg( thread, REG_R8 ); + frame->r9 = rtems_debugger_get_int_reg( thread, REG_R9 ); + frame->r10 = rtems_debugger_get_int_reg( thread, REG_R10 ); + frame->r11 = rtems_debugger_get_int_reg( thread, REG_R11 ); + frame->r12 = rtems_debugger_get_int_reg( thread, REG_R12 ); + frame->r13 = rtems_debugger_get_int_reg( thread, REG_R13 ); + frame->r14 = (uint32_t *) rtems_debugger_get_int_reg( thread, REG_R14 ); + frame->r15 = (uint32_t *) rtems_debugger_get_int_reg( thread, REG_R15 ); + frame->r16 = (uint32_t *) rtems_debugger_get_int_reg( thread, REG_R16 ); + frame->r17 = (uint32_t *) rtems_debugger_get_int_reg( thread, REG_R17 ); + frame->r18 = rtems_debugger_get_int_reg( thread, REG_R18 ); + frame->r19 = rtems_debugger_get_int_reg( thread, REG_R19 ); + frame->r20 = rtems_debugger_get_int_reg( thread, REG_R20 ); + frame->r21 = rtems_debugger_get_int_reg( thread, REG_R21 ); + frame->r22 = rtems_debugger_get_int_reg( thread, REG_R22 ); + frame->r23 = rtems_debugger_get_int_reg( thread, REG_R23 ); + frame->r24 = rtems_debugger_get_int_reg( thread, REG_R24 ); + frame->r25 = rtems_debugger_get_int_reg( thread, REG_R25 ); + frame->r26 = rtems_debugger_get_int_reg( thread, REG_R26 ); + frame->r27 = rtems_debugger_get_int_reg( thread, REG_R27 ); + frame->r28 = rtems_debugger_get_int_reg( thread, REG_R28 ); + frame->r29 = rtems_debugger_get_int_reg( thread, REG_R29 ); + frame->r30 = rtems_debugger_get_int_reg( thread, REG_R30 ); + frame->r31 = rtems_debugger_get_int_reg( thread, REG_R31 ); + frame->msr = rtems_debugger_get_int_reg( thread, REG_MS ); + frame->ear = (uint32_t *) rtems_debugger_get_int_reg( thread, REG_EA ); + frame->esr = rtems_debugger_get_int_reg( thread, REG_ES ); + frame->btr = (uint32_t *) rtems_debugger_get_int_reg( thread, REG_BT ); + } + + thread->flags &= ~RTEMS_DEBUGGER_THREAD_FLAG_REG_DIRTY; + } + + return 0; +} + +uintptr_t rtems_debugger_target_reg_pc( rtems_debugger_thread *thread ) +{ + return thread->tcb->Registers.r15; +} + +uintptr_t rtems_debugger_target_frame_pc( CPU_Exception_frame *frame ) +{ + return (uintptr_t) _MicroBlaze_Get_return_address( frame ); +} + +uintptr_t 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_R1 ); + } + + return 0; +} + +uintptr_t rtems_debugger_target_tcb_sp( rtems_debugger_thread *thread ) +{ + return (uintptr_t) thread->tcb->Registers.r1; +} + +#define IGROUP_MASK 0x3f + +static uint32_t get_igroup( uint32_t ins ) +{ + return ( ins >> 26 ) & IGROUP_MASK; +} + +#define REGISTER_MASK 0x1f + +static uint32_t get_Ra( uint32_t ins ) +{ + return ( ins >> 16 ) & REGISTER_MASK; +} + +static uint32_t get_Rb( uint32_t ins ) +{ + return ( ins >> 11 ) & REGISTER_MASK; +} + +static uint32_t get_Rd( uint32_t ins ) +{ + return ( ins >> 21 ) & REGISTER_MASK; +} + +#define IMM16_MASK 0xffff + +static int32_t get_Imm16( uint32_t ins ) +{ + int16_t base = (int16_t) ins & IMM16_MASK; + + return base; +} + +#define IMM24_MASK 0xffffff + +static int32_t get_Imm24( uint32_t ins ) +{ + int32_t base = ins & IMM24_MASK; + + /* Sign-extend manually if necessary */ + if ( ( base & 0x800000 ) != 0 ) { + base &= 0xFF000000; + } + + return base; +} + +static int64_t get_Imm( uint32_t ins ) +{ + if ( ( get_Rd( ins ) & 0x10 ) != 0 ) { + return get_Imm24( ins ); + } + + return get_Imm16( ins ); +} + +#define IMM_GROUP 0x2c + +static bool is_imm( uint32_t ins ) +{ + return get_igroup( ins ) == IMM_GROUP; +} + +#define RETURN_GROUP 0x2d + +static bool is_return( uint32_t ins ) +{ + return get_igroup( ins ) == RETURN_GROUP; +} + +/* Unconditional branch */ +#define UBRANCH_GROUP 0x26 + +static bool is_ubranch( uint32_t ins ) +{ + return get_igroup( ins ) == UBRANCH_GROUP; +} + +/* Comparison branch */ +#define CBRANCH_GROUP 0x27 + +static bool is_cbranch( uint32_t ins ) +{ + return get_igroup( ins ) == CBRANCH_GROUP; +} + +/* Unconditional Immediate branch */ +#define UIBRANCH_GROUP 0x2e + +static bool is_uibranch( uint32_t ins ) +{ + /* Ra == 0x2 is a memory barrier which continues at the next instruction */ + return get_igroup( ins ) == UIBRANCH_GROUP && get_Ra( ins ) != 0x2; +} + +/* Comparison Immediate branch */ +#define CIBRANCH_GROUP 0x2f + +static bool is_cibranch( uint32_t ins ) +{ + return get_igroup( ins ) == CIBRANCH_GROUP; +} + +static bool branch_has_delay_slot( uint32_t ins ) +{ + if ( is_ubranch( ins ) == true && ( get_Ra( ins ) & 0x10 ) != 0 ) { + return true; + } + + if ( is_cbranch( ins ) == true && ( get_Ra( ins ) & 0x10 ) != 0 ) { + return true; + } + + if ( is_uibranch( ins ) == true && ( get_Ra( ins ) & 0x10 ) != 0 ) { + return true; + } + + if ( is_cibranch( ins ) == true && ( get_Rd( ins ) & 0x10 ) != 0 ) { + return true; + } + + return false; +} + +/* All return instructions have a delay slot */ + +static bool branch_is_absolute( uint32_t ins ) +{ + return ( is_ubranch( ins ) == true || is_uibranch( ins ) == true ) && + ( get_Ra( ins ) & 0x8 ) != 0; +} + +/* All returns are absolute */ + +static bool target_is_absolute( uint32_t ins ) +{ + return branch_is_absolute( ins ) == true || is_return( ins ) == true; +} + +static bool is_branch( uint32_t ins ) +{ + if ( is_ubranch( ins ) == true ) { + return true; + } + + if ( is_cbranch( ins ) == true ) { + return true; + } + + if ( is_uibranch( ins ) == true ) { + return true; + } + + if ( is_cibranch( ins ) == true ) { + return true; + } + + return false; +} + +#define BRK_RA 0xC + +static bool is_brk( uint32_t ins ) +{ + return ( is_ubranch( ins ) == true || is_uibranch( ins ) == true ) && + get_Ra( ins ) == BRK_RA; +} + +static uint32_t get_register_value( + CPU_Exception_frame *frame, + uint32_t target_register +) +{ + if ( target_register == 0 ) { + return 0; + } + + /* Assumes all registers are contiguous and accounted for */ + return ( &( frame->r1 ) )[ target_register - 1 ]; +} + +static void set_frame_pc( CPU_Exception_frame *frame, uint32_t *new_pc ) +{ + Per_CPU_Control *cpu_self = _Per_CPU_Get(); + + /* Break in progress */ + if ( ( frame->msr & MICROBLAZE_MSR_BIP ) != 0 ) { + frame->r16 = (uint32_t *) new_pc; + return; + } + + /* Exception in progress */ + if ( ( frame->msr & MICROBLAZE_MSR_EIP ) != 0 ) { + frame->r17 = (uint32_t *) new_pc; + return; + } + + /* Interrupt in progress must be determined by stack pointer location */ + if ( + frame->r1 >= (uint32_t) cpu_self->interrupt_stack_low + && frame->r1 < (uint32_t) cpu_self->interrupt_stack_high + ) { + frame->r14 = (uint32_t *) new_pc; + return; + } + + /* Default to normal link register */ + frame->r15 = (uint32_t *) new_pc; +} + +static uint32_t bypass_swbreaks( uint32_t *addr ) +{ + rtems_debugger_target *target = rtems_debugger->target; + + if ( target != NULL && target->swbreaks.block != NULL ) { + rtems_debugger_target_swbreak *swbreaks = target->swbreaks.block; + size_t i; + + for ( i = 0; i < target->swbreaks.level; ++i ) { + if ( swbreaks[ i ].address == addr ) { + return *( (uint32_t *) &( swbreaks[ i ].contents[ 0 ] ) ); + } + } + } + + return *addr; +} + +static int setup_single_step_breakpoints( CPU_Exception_frame *frame ) +{ + /* + * It may be necessary to evaluate the current instruction and next immediate + * instruction to determine the address of the "next" instruction and possible + * branch target instructions + */ + uint32_t *pc = (uint32_t *) rtems_debugger_target_frame_pc( frame ); + int64_t imm = 0; + uint32_t *resume_pc; + + /* + * Normalize PC address to the real instruction and not any IMM. This deals + * with a possible cascade of IMM. + */ + while ( is_imm( bypass_swbreaks( pc ) ) == true ) { + pc = &pc[ 1 ]; + } + + resume_pc = pc; + + /* + * If execution ends up on a branch instruction that is preceeded by IMM, bad + * things can happen since it's not possible to know if the IMM was actually + * executed or something jumped to the branch directly. Exceptions treat IMM + * as part of the following instruction, so the RTEMS debugger will do so as + * well. + */ + uint32_t bypass_ins = bypass_swbreaks( &pc[ -1 ] ); + + if ( is_imm( bypass_ins ) == true ) { + imm = get_Imm( bypass_ins ); + imm <<= 16; + resume_pc = &pc[ -1 ]; + } + + uint32_t ins = bypass_swbreaks( pc ); + bool needs_target_break = false; + bool needs_next_break = true; + + if ( is_brk( ins ) == true ) { + /* + * If the instruction being stepped is brk or brki, something bad has + * happened. If this instruction is stepped, the target of the branch (the + * debug vector) has a brki placed in it which results in an tight infinite + * recursive call. Under normal circumstances, this shouldn't happen. + */ + rtems_debugger_printf( + "rtems-db: Unable to set single-step breakpoints for brk/brki instructions\n" + ); + + return -1; + } + + if ( is_branch( ins ) == true ) { + needs_target_break = true; + + /* + * Unconditional branches (including returns) do not need to break on the + * next instruction. + */ + if ( + is_ubranch( ins ) == true + || is_uibranch( ins ) == true + || is_return( ins ) == true + ) { + needs_next_break = false; + } + } + + if ( is_return( ins ) == true ) { + needs_target_break = true; + needs_next_break = false; + } + + if ( needs_next_break == true ) { + uint32_t *next_ins = &pc[ 1 ]; + + if ( branch_has_delay_slot( ins ) == true ) { + next_ins = &pc[ 2 ]; + } + + if ( is_brk( *next_ins ) == false ) { + /* setup next instruction software break */ + set_soft_break( &next_soft_break, next_ins ); + } + } + + if ( imm != 0 ) { + imm |= ( get_Imm16( ins ) & 0xFFFF ); + } else { + imm = get_Imm16( ins ); + } + + if ( needs_target_break == true ) { + /* Calculate target address */ + uintptr_t target_ins = 0; + + if ( target_is_absolute( ins ) == false ) { + target_ins += (uintptr_t) pc; + } + + if ( + is_uibranch( ins ) == true || is_cibranch( ins ) == true || + is_return( ins ) == true + ) { + target_ins += imm; + } + + if ( is_return( ins ) == true ) { + uint32_t target_register = get_Ra( ins ); + target_ins += get_register_value( frame, target_register ); + } + + if ( is_ubranch( ins ) == true || is_cbranch( ins ) == true ) { + uint32_t target_register = get_Rb( ins ); + target_ins += get_register_value( frame, target_register ); + } + + if ( is_brk( *( (uint32_t *) target_ins ) ) == false ) { + /* setup target instruction software break */ + set_soft_break( &target_soft_break, (uint32_t *) target_ins ); + } + } + + /* Alter resume address */ + set_frame_pc( frame, resume_pc ); + + return 0; +} + +int rtems_debugger_target_thread_stepping( rtems_debugger_thread *thread ) +{ + CPU_Exception_frame *frame = thread->frame; + int ret = 0; + + if ( + rtems_debugger_thread_flag( + thread, + RTEMS_DEBUGGER_THREAD_FLAG_STEP_INSTR + ) != 0 + ) { + /* Especially on first startup, frame isn't guaranteed to be non-NULL */ + if ( frame == NULL ) { + return -1; + } + + /* set software breakpoint(s) here */ + ret = setup_single_step_breakpoints( frame ); + } + + return ret; +} + +int rtems_debugger_target_exception_to_signal( CPU_Exception_frame *frame ) +{ + uint32_t BiP = frame->msr & MICROBLAZE_MSR_BIP; + uint32_t EiP = frame->msr & MICROBLAZE_MSR_EIP; + + if ( BiP != 0 ) { + return RTEMS_DEBUGGER_SIGNAL_TRAP; + } + + if ( EiP != 0 ) { + uint32_t EC = frame->esr & 0x1f; + + switch ( EC ) { + case 0x0: /* FSL */ + case 0x1: /* Unaligned data access */ + case 0x3: /* instruction fetch */ + case 0x4: /* data bus error */ + case 0x10: /* MMU data storage */ + case 0x11: /* MMU instruction storage */ + case 0x12: /* MMU data TLB miss */ + case 0x13: /* MMU instruction TLB miss */ + return RTEMS_DEBUGGER_SIGNAL_SEGV; + + case 0x7: /* priveleged */ + return RTEMS_DEBUGGER_SIGNAL_TRAP; + + case 0x5: /* div/0 */ + case 0x6: /* FPU */ + return RTEMS_DEBUGGER_SIGNAL_FPE; + + case 0x2: /* illegal opcode (unknown instruction) */ + + /* Check for the illegal opcode being used in place of brki */ + if ( rtems_debugger_target_swbreak_is_configured( (uintptr_t) frame->r17 ) ) { + return RTEMS_DEBUGGER_SIGNAL_TRAP; + } + + default: + return RTEMS_DEBUGGER_SIGNAL_ILL; + } + } + + /* Default to SIGILL */ + return RTEMS_DEBUGGER_SIGNAL_ILL; +} + +void rtems_debugger_target_exception_print( CPU_Exception_frame *frame ) +{ + EXC_FRAME_PRINT( rtems_debugger_printf, "", frame ); +} + +/* + * Debug hardware is inaccessible to the CPU, so hardware breaks and watchpoints + * are not supported. + */ +int rtems_debugger_target_hwbreak_insert( void ) +{ + return 0; +} + +int rtems_debugger_target_hwbreak_remove( void ) +{ + return 0; +} + +int rtems_debugger_target_hwbreak_control( + rtems_debugger_target_watchpoint wp, + bool insert, + uintptr_t addr, + DB_UINT kind +) +{ + 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; +} diff --git a/cpukit/libdebugger/rtems-debugger-server.c b/cpukit/libdebugger/rtems-debugger-server.c index 975ec23a30..b7b9727d84 100644 --- a/cpukit/libdebugger/rtems-debugger-server.c +++ b/cpukit/libdebugger/rtems-debugger-server.c @@ -154,6 +154,26 @@ hex_encode(int val) return "0123456789abcdef"[val & 0xf]; } +static inline uintptr_t +hex_decode_addr(const uint8_t* data) +{ + uintptr_t ui = 0; + size_t i; + if (data[0] == '-') { + if (data[1] == '1') + ui = (uintptr_t) -1; + } + else { + for (i = 0; i < (sizeof(ui) * 2); ++i) { + int v = hex_decode(data[i]); + if (v < 0) + break; + ui = (ui << 4) | v; + } + } + return ui; +} + static inline DB_UINT hex_decode_uint(const uint8_t* data) { @@ -1438,10 +1458,10 @@ remote_read_memory(uint8_t* buffer, int size) if (comma == NULL) remote_packet_out_str(r_E01); else { - DB_UINT addr; + uintptr_t addr; DB_UINT length; int r; - addr = hex_decode_uint(&buffer[1]); + addr = hex_decode_addr(&buffer[1]); length = hex_decode_uint((const uint8_t*) comma + 1); remote_packet_out_reset(); r = rtems_debugger_target_start_memory_access(); @@ -1468,10 +1488,10 @@ remote_write_memory(uint8_t* buffer, int size) comma = strchr((const char*) buffer, ','); colon = strchr((const char*) buffer, ':'); if (comma != NULL && colon != NULL) { - DB_UINT addr; + uintptr_t addr; DB_UINT length; int r; - addr = hex_decode_uint(&buffer[1]); + addr = hex_decode_addr(&buffer[1]); length = hex_decode_uint((const uint8_t*) comma + 1); r = rtems_debugger_target_start_memory_access(); if (r == 0) { @@ -1519,9 +1539,9 @@ remote_breakpoints(bool insert, uint8_t* buffer, int size) comma2 = strchr(comma1 + 1, ','); if (comma2 != NULL) { uint32_t capabilities; - DB_UINT addr; + uintptr_t addr; DB_UINT kind; - addr = hex_decode_uint((const uint8_t*) comma1 + 1); + addr = hex_decode_addr((const uint8_t*) comma1 + 1); kind = hex_decode_uint((const uint8_t*)comma2 + 1); capabilities = rtems_debugger_target_capabilities(); switch (buffer[1]) { @@ -1658,18 +1678,30 @@ rtems_debugger_events(rtems_task_argument arg) rtems_debugger_target_enable(); - while (rtems_debugger_server_events_running()) { - rtems_debugger_server_events_wait(); - if (rtems_debugger_verbose()) - rtems_debugger_printf("rtems-db: event woken\n"); - if (!rtems_debugger_server_events_running()) - break; + if (rtems_debugger_server_flag(RTEMS_DEBUGGER_FLAG_BREAK_WAITER)) { + rtems_debugger->flags &= ~RTEMS_DEBUGGER_FLAG_BREAK_WAITER; r = rtems_debugger_thread_system_suspend(); - if (r < 0) - break; - r = remote_stop_reason(NULL, 0); - if (r < 0) - break; + if (rtems_debugger_verbose()) + rtems_debugger_printf("rtems-db: break waiter\n"); + rtems_debugger_server_events_signal(); + if (rtems_debugger_verbose()) + rtems_debugger_printf("rtems-db: break waiter: signalled\n"); + } + + if (r == 0) { + while (rtems_debugger_server_events_running()) { + rtems_debugger_server_events_wait(); + if (rtems_debugger_verbose()) + rtems_debugger_printf("rtems-db: event woken\n"); + if (!rtems_debugger_server_events_running()) + break; + r = rtems_debugger_thread_system_suspend(); + if (r < 0) + break; + r = remote_stop_reason(NULL, 0); + if (r < 0) + break; + } } if (r < 0) @@ -1953,6 +1985,30 @@ rtems_debugger_server_crash(void) } int +rtems_debugger_break(bool wait) +{ + int r = 0; + if (!rtems_debugger_running()) { + errno = EIO; + r = -1; + } else { + rtems_debugger_lock(); + if (rtems_debugger_server_events_running()) { + rtems_debugger_server_events_signal(); + } else if ( + wait && !rtems_debugger_server_flag(RTEMS_DEBUGGER_FLAG_BREAK_WAITER)) { + rtems_debugger->flags |= RTEMS_DEBUGGER_FLAG_BREAK_WAITER; + rtems_debugger_server_events_wait(); + } else { + errno = EIO; + r = -1; + } + rtems_debugger_unlock(); + } + return r; +} + +int rtems_debugger_stop(void) { return rtems_debugger_destroy(); @@ -1978,10 +2034,14 @@ rtems_debugger_set_verbose(bool on) int rtems_debugger_remote_debug(bool state) { - rtems_debugger_lock(); - rtems_debugger->remote_debug = state; - rtems_debugger_printf("rtems-db: remote-debug is %s\n", - rtems_debugger->remote_debug ? "on" : "off"); - rtems_debugger_unlock(); + if (rtems_debugger_running()) { + rtems_debugger_lock(); + rtems_debugger->remote_debug = state; + rtems_debugger_printf("rtems-db: remote-debug is %s\n", + rtems_debugger->remote_debug ? "on" : "off"); + rtems_debugger_unlock(); + } else { + rtems_debugger_printf("rtems-db: debug server not running\n"); + } return 0; } diff --git a/cpukit/libdebugger/rtems-debugger-target.c b/cpukit/libdebugger/rtems-debugger-target.c index bf7579700d..2b55c93513 100644 --- a/cpukit/libdebugger/rtems-debugger-target.c +++ b/cpukit/libdebugger/rtems-debugger-target.c @@ -167,8 +167,34 @@ rtems_debugger_target_reg_table_size(void) return 0; } +bool +rtems_debugger_target_swbreak_is_configured( uintptr_t addr ) +{ + size_t i; + rtems_debugger_target_swbreak *swbreaks; + rtems_debugger_target *target = rtems_debugger->target; + + if ( target == NULL ) { + return false; + } + + swbreaks = target->swbreaks.block; + + if ( swbreaks == NULL ) { + return false; + } + + for ( i = 0; i < target->swbreaks.level; ++i ) { + if ( (uintptr_t) swbreaks[ i ].address == addr ) { + return true; + } + } + + return false; +} + int -rtems_debugger_target_swbreak_control(bool insert, DB_UINT addr, DB_UINT kind) +rtems_debugger_target_swbreak_control(bool insert, uintptr_t addr, DB_UINT kind) { rtems_debugger_target* target = rtems_debugger->target; rtems_debugger_target_swbreak* swbreaks; @@ -191,6 +217,22 @@ rtems_debugger_target_swbreak_control(bool insert, DB_UINT addr, DB_UINT kind) if (loc == swbreaks[i].address) { size_t remaining; if (!insert) { + if (target->breakpoint_size > 4) + memcpy(loc, swbreaks[i].contents, target->breakpoint_size); + else { + switch (target->breakpoint_size) { + case 4: + loc[3] = swbreaks[i].contents[3]; + case 3: + loc[2] = swbreaks[i].contents[2]; + case 2: + loc[1] = swbreaks[i].contents[1]; + case 1: + loc[0] = swbreaks[i].contents[0]; + break; + } + } + rtems_debugger_target_cache_sync(&swbreaks[i]); --target->swbreaks.level; remaining = (target->swbreaks.level - i) * swbreak_size; memmove(&swbreaks[i], &swbreaks[i + 1], remaining); @@ -307,15 +349,83 @@ rtems_debugger_target_swbreak_remove(void) return r; } +uintptr_t saved_break_address = 0; +rtems_id saved_tid = 0; + +static rtems_debugger_target_exc_action +soft_step_and_continue(CPU_Exception_frame* frame) +{ + uintptr_t break_address; + rtems_debugger_target *target = rtems_debugger->target; + Thread_Control *thread = _Thread_Get_executing(); + const rtems_id tid = thread->Object.id; + rtems_debugger_thread fake_debugger_thread; + + /* + * If this was a hwbreak, cascade. If this is a swbreak replace the contents + * of the instruction, step then return the swbreak's contents. + */ + if ((target->capabilities & RTEMS_DEBUGGER_TARGET_CAP_SWBREAK) == 0) { + target_printk("rtems-db: exception in an interrupt, cascading\n"); + rtems_debugger_unlock(); + return rtems_debugger_target_exc_cascade; + } + + break_address = rtems_debugger_target_frame_pc( frame ); + if ( rtems_debugger_target_swbreak_is_configured( break_address ) == false ) { + target_printk("rtems-db: exception in an interrupt, cascading\n"); + rtems_debugger_unlock(); + return rtems_debugger_target_exc_cascade; + } + + /* Remove the current breakpoint */ + rtems_debugger_target_swbreak_control( + false, + break_address, + target->breakpoint_size + ); + + /* Save off thread ID and break address for later usage */ + saved_tid = tid; + saved_break_address = break_address; + + /* Populate the fake rtems_debugger_thread */ + fake_debugger_thread.flags |= RTEMS_DEBUGGER_THREAD_FLAG_STEP; + fake_debugger_thread.frame = frame; + target_printk("rtems-db: stepping to the next instruction\n"); + rtems_debugger_target_thread_stepping(&fake_debugger_thread); + + /* rtems_debugger_unlock() not called until the step is resolved */ + return rtems_debugger_target_exc_step; +} + rtems_debugger_target_exc_action rtems_debugger_target_exception(CPU_Exception_frame* frame) { + Thread_Control* thread = _Thread_Get_executing(); + const rtems_id tid = thread->Object.id; + + /* Resolve outstanding step+continue */ + if ( saved_break_address != 0 && tid == saved_tid ) { + rtems_debugger_target_swbreak_control( + true, + saved_break_address, + rtems_debugger->target->breakpoint_size + ); + saved_break_address = saved_tid = 0; + + /* Release the debugger lock now that the step+continue is complete */ + target_printk("rtems-db: resuming after step\n"); + rtems_debugger_unlock(); + return rtems_debugger_target_exc_consumed; + } + + rtems_debugger_lock(); + if (!rtems_interrupt_is_in_progress()) { rtems_debugger_threads* threads = rtems_debugger->threads; - Thread_Control* thread = _Thread_Get_executing(); - const rtems_id tid = thread->Object.id; rtems_id* excludes; - DB_UINT pc; + uintptr_t pc; const rtems_debugger_thread_stepper* stepper; rtems_debugger_exception target_exception; size_t i; @@ -324,8 +434,6 @@ rtems_debugger_target_exception(CPU_Exception_frame* frame) " frame:%08" PRIxPTR "\n", tid, (intptr_t) thread, (intptr_t) frame); - rtems_debugger_lock(); - /* * If the thread is in the debugger recover. If the access is from gdb * continue else shutdown and let the user know. @@ -414,9 +522,10 @@ rtems_debugger_target_exception(CPU_Exception_frame* frame) return rtems_debugger_target_exc_consumed; } - rtems_debugger_printf("rtems-db: exception in an interrupt, cascading\n"); + target_printk("[} tid:%08" PRIx32 ": exception in interrupt context\n", tid); - return rtems_debugger_target_exc_cascade; + /* soft_step_and_continue releases the debugger lock */ + return soft_step_and_continue( frame ); } void diff --git a/cpukit/libdebugger/rtems-debugger-target.h b/cpukit/libdebugger/rtems-debugger-target.h index f2abbe5fd3..3f6ceac80b 100644 --- a/cpukit/libdebugger/rtems-debugger-target.h +++ b/cpukit/libdebugger/rtems-debugger-target.h @@ -49,9 +49,17 @@ extern "C" { /** * Target capabilities mask. */ -#define RTEMS_DEBUGGER_TARGET_CAP_SWBREAK (1 << 0) -#define RTEMS_DEBUGGER_TARGET_CAP_HWBREAK (1 << 1) -#define RTEMS_DEBUGGER_TARGET_CAP_HWWATCH (1 << 2) +#define RTEMS_DEBUGGER_TARGET_CAP_SWBREAK (1 << 0) +#define RTEMS_DEBUGGER_TARGET_CAP_HWBREAK (1 << 1) +#define RTEMS_DEBUGGER_TARGET_CAP_HWWATCH (1 << 2) +/* + * This target capability indicates that the target implementation uses a pure + * software break implementation which must not allow breakpoints to be + * inserted before the actual switch to the thread, be it in interrupt context + * or otherwise. Such implementations must necessarily implement a thread + * switch hook and interrupt hooks to handle these situations. + */ +#define RTEMS_DEBUGGER_TARGET_CAP_PURE_SWBREAK (1 << 3) /** * Types of hardware breakpoints. @@ -164,22 +172,22 @@ extern int rtems_debugger_target_write_regs(rtems_debugger_thread* thread); /** * Return the thread's program counter (PC). */ -extern DB_UINT rtems_debugger_target_reg_pc(rtems_debugger_thread* thread); +extern uintptr_t rtems_debugger_target_reg_pc(rtems_debugger_thread* thread); /** * Return the frame's program counter (PC). */ -extern DB_UINT rtems_debugger_target_frame_pc(CPU_Exception_frame* frame); +extern uintptr_t rtems_debugger_target_frame_pc(CPU_Exception_frame* frame); /** * Return the thread's stack pointer (SP). */ -extern DB_UINT rtems_debugger_target_reg_sp(rtems_debugger_thread* thread); +extern uintptr_t rtems_debugger_target_reg_sp(rtems_debugger_thread* thread); /** * Return the thread's TCB stack pointer (SP). */ -extern DB_UINT rtems_debugger_target_tcb_sp(rtems_debugger_thread* thread); +extern uintptr_t rtems_debugger_target_tcb_sp(rtems_debugger_thread* thread); /** * The thread is stepping. Setup the thread to step an instruction. @@ -200,7 +208,7 @@ extern void rtems_debugger_target_exception_print(CPU_Exception_frame* frame); * Software breakpoints. These are also referred to as memory breakpoints. */ extern int rtems_debugger_target_swbreak_control(bool insert, - DB_UINT addr, + uintptr_t addr, DB_UINT kind); /** @@ -214,6 +222,11 @@ extern int rtems_debugger_target_swbreak_insert(void); extern int rtems_debugger_target_swbreak_remove(void); /** + * Determine whether a software breakpoint is configured for the given address. + */ +extern bool rtems_debugger_target_swbreak_is_configured( uintptr_t addr ); + +/** * Insert hardware breakpoints into the hardware. */ extern int rtems_debugger_target_hwbreak_insert(void); @@ -228,7 +241,7 @@ extern int rtems_debugger_target_hwbreak_remove(void); */ extern int rtems_debugger_target_hwbreak_control(rtems_debugger_target_watchpoint type, bool insert, - DB_UINT addr, + uintptr_t addr, DB_UINT kind); /** diff --git a/cpukit/libdebugger/rtems-debugger-threads.c b/cpukit/libdebugger/rtems-debugger-threads.c index e6ffe4a080..841199bfe3 100644 --- a/cpukit/libdebugger/rtems-debugger-threads.c +++ b/cpukit/libdebugger/rtems-debugger-threads.c @@ -355,9 +355,11 @@ rtems_debugger_thread_system_resume(bool detaching) current = rtems_debugger_thread_current(threads); if (current != NULL) { size_t i; + rtems_debugger_target* target = rtems_debugger->target; if (rtems_debugger_verbose()) rtems_debugger_printf("rtems-db: sys: : resuming\n"); - if (!detaching) { + if (!detaching + && (target->capabilities & RTEMS_DEBUGGER_TARGET_CAP_PURE_SWBREAK) == 0) { r = rtems_debugger_target_swbreak_insert(); if (r == 0) r = rtems_debugger_target_hwbreak_insert(); @@ -469,8 +471,8 @@ rtems_debugger_thread_step(rtems_debugger_thread* thread) int rtems_debugger_thread_stepping(rtems_debugger_thread* thread, - DB_UINT start, - DB_UINT end) + uintptr_t start, + uintptr_t end) { /* add lock */ rtems_debugger_threads* threads = rtems_debugger->threads; @@ -496,7 +498,7 @@ rtems_debugger_thread_stepping(rtems_debugger_thread* thread, } const rtems_debugger_thread_stepper* -rtems_debugger_thread_is_stepping(rtems_id id, DB_UINT pc) +rtems_debugger_thread_is_stepping(rtems_id id, uintptr_t pc) { /* add lock */ rtems_debugger_threads* threads = rtems_debugger->threads; diff --git a/cpukit/libdebugger/rtems-debugger-threads.h b/cpukit/libdebugger/rtems-debugger-threads.h index 200dbbe1c7..60bc87984e 100644 --- a/cpukit/libdebugger/rtems-debugger-threads.h +++ b/cpukit/libdebugger/rtems-debugger-threads.h @@ -102,8 +102,8 @@ typedef struct rtems_debugger_thread typedef struct rtems_debugger_thread_stepper { rtems_debugger_thread* thread; - DB_UINT start; - DB_UINT end; + uintptr_t start; + uintptr_t end; } rtems_debugger_thread_stepper; /** @@ -165,15 +165,15 @@ extern int rtems_debugger_thread_step(rtems_debugger_thread* thread); * Thread is stepping so record the details. */ extern int rtems_debugger_thread_stepping(rtems_debugger_thread* thread, - DB_UINT start, - DB_UINT end); + uintptr_t start, + uintptr_t end); /** * Thread's PC in the stepping range? Returns the stepper is in range else * NULL. */ extern const rtems_debugger_thread_stepper* -rtems_debugger_thread_is_stepping(rtems_id id, DB_UINT pc); +rtems_debugger_thread_is_stepping(rtems_id id, uintptr_t pc); /** * Return the thread's current priority/ |