summaryrefslogtreecommitdiffstats
path: root/cpukit/libdebugger
diff options
context:
space:
mode:
Diffstat (limited to 'cpukit/libdebugger')
-rw-r--r--cpukit/libdebugger/rtems-debugger-aarch64.c1905
-rw-r--r--cpukit/libdebugger/rtems-debugger-arm.c808
-rw-r--r--cpukit/libdebugger/rtems-debugger-i386.c12
-rw-r--r--cpukit/libdebugger/rtems-debugger-microblaze.c1393
-rw-r--r--cpukit/libdebugger/rtems-debugger-server.c104
-rw-r--r--cpukit/libdebugger/rtems-debugger-target.c125
-rw-r--r--cpukit/libdebugger/rtems-debugger-target.h31
-rw-r--r--cpukit/libdebugger/rtems-debugger-threads.c10
-rw-r--r--cpukit/libdebugger/rtems-debugger-threads.h10
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/