diff options
Diffstat (limited to 'cpukit/libdebugger')
-rw-r--r-- | cpukit/libdebugger/rtems-debugger-aarch64.c | 23 | ||||
-rw-r--r-- | cpukit/libdebugger/rtems-debugger-arm.c | 808 | ||||
-rw-r--r-- | cpukit/libdebugger/rtems-debugger-microblaze.c | 1393 | ||||
-rw-r--r-- | cpukit/libdebugger/rtems-debugger-server.c | 72 | ||||
-rw-r--r-- | cpukit/libdebugger/rtems-debugger-target.c | 121 | ||||
-rw-r--r-- | cpukit/libdebugger/rtems-debugger-target.h | 19 | ||||
-rw-r--r-- | cpukit/libdebugger/rtems-debugger-threads.c | 4 |
7 files changed, 2100 insertions, 340 deletions
diff --git a/cpukit/libdebugger/rtems-debugger-aarch64.c b/cpukit/libdebugger/rtems-debugger-aarch64.c index 279c2d61ef..9a53da0de9 100644 --- a/cpukit/libdebugger/rtems-debugger-aarch64.c +++ b/cpukit/libdebugger/rtems-debugger-aarch64.c @@ -34,7 +34,9 @@ #include <inttypes.h> #include <stdlib.h> -/* Defined by linkcmds.base */ +/* 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[]; @@ -1231,6 +1233,8 @@ 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; @@ -1243,6 +1247,23 @@ static rtems_status_code rtems_debugger_target_set_text_writable( } 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, 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-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 9de9421b6b..b7b9727d84 100644 --- a/cpukit/libdebugger/rtems-debugger-server.c +++ b/cpukit/libdebugger/rtems-debugger-server.c @@ -1678,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) @@ -1973,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(); @@ -1998,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 04b274909b..2b55c93513 100644 --- a/cpukit/libdebugger/rtems-debugger-target.c +++ b/cpukit/libdebugger/rtems-debugger-target.c @@ -167,6 +167,32 @@ 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, uintptr_t addr, DB_UINT kind) { @@ -191,6 +217,22 @@ rtems_debugger_target_swbreak_control(bool insert, uintptr_t 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,13 +349,81 @@ 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; uintptr_t pc; const rtems_debugger_thread_stepper* stepper; @@ -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 1e132fb28c..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. @@ -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); diff --git a/cpukit/libdebugger/rtems-debugger-threads.c b/cpukit/libdebugger/rtems-debugger-threads.c index c628c0250e..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(); |