/*
* 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 */
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 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: 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;
}