This is an automated email from Gerrit. "Antonio Borneo <[email protected]>" just uploaded a new patch set to Gerrit, which you can find at https://review.openocd.org/c/openocd/+/9558
-- gerrit commit e764cbd880bd19c3129c2d057a6ff6b1b888776a Author: Antonio Borneo <[email protected]> Date: Wed Apr 1 15:17:55 2026 +0200 cortex_a: allow single step over BKPT instruction The firmware on the target could include the BKPT instruction, added to allow halting and debugging the code. If the current instruction is a BKPT, allow to step over it by simply setting the PC to the next instruction. This aligns the Cortex-A code with the Cortex-M implementation. Change-Id: I370a892ec8e7d45673da8f47b2130513a9b9f8a0 Signed-off-by: Antonio Borneo <[email protected]> diff --git a/src/target/cortex_a.c b/src/target/cortex_a.c index a792bc9e65..657bbd4938 100644 --- a/src/target/cortex_a.c +++ b/src/target/cortex_a.c @@ -87,6 +87,78 @@ static unsigned int ilog2(unsigned int x) return y; } +/* + * If we halted last time due to a bkpt instruction, then we have to manually + * step over it, otherwise the core will break again + */ +static int cortex_a_maybe_skip_bkpt_inst(struct target *target, + bool *inst_found) +{ + struct armv7a_common *armv7a = target_to_armv7a(target); + struct arm *arm = &armv7a->arm; + bool result = false; + + if (target->debug_reason != DBG_REASON_BREAKPOINT) + goto out; + + uint32_t pc = buf_get_u32(arm->pc->value, 0, 32); + + switch (arm->core_state) { + case ARM_STATE_ARM: + { + uint32_t bkpt = ARMV5_BKPT(0); + uint32_t mask = ~(ARMV5_BKPT(0) ^ ARMV5_BKPT(0xffff)); + uint32_t opcode; + + int retval = target_read_u32(target, pc, &opcode); + if (retval != ERROR_OK) + return retval; + + pc += 4; + if ((opcode & mask) == bkpt) + result = true; + } + break; + + case ARM_STATE_THUMB: + case ARM_STATE_THUMB_EE: + { + uint16_t bkpt = (uint16_t)ARMV5_T_BKPT(0); + uint16_t mask = (uint16_t)~(ARMV5_T_BKPT(0) ^ ARMV5_T_BKPT(0xff)); + uint16_t opcode; + + // Drop PC bit 0, always set in Thumb state + int retval = target_read_u16(target, pc & ~0x1U, &opcode); + if (retval != ERROR_OK) + return retval; + + pc += 2; + if ((opcode & mask) == bkpt) + result = true; + } + break; + + default: + LOG_TARGET_ERROR(target, "Unsupported core state \"%s\"", + arm_core_state_string(arm)); + return ERROR_FAIL; + } + + if (result) { + // New PC to skip bkpt instruction + buf_set_u32(arm->pc->value, 0, 32, pc); + arm->pc->dirty = true; + arm->pc->valid = true; + LOG_TARGET_DEBUG(target, "Skipping over BKPT instruction"); + } + +out: + if (inst_found) + *inst_found = result; + + return ERROR_OK; +} + /* restore cp15_control_reg at resume */ static int cortex_a_restore_cp15_control_reg(struct target *target) { @@ -1209,7 +1281,6 @@ static int cortex_a_step(struct target *target, bool current, target_addr_t addr struct breakpoint *breakpoint = NULL; struct breakpoint stepbreakpoint; struct reg *r; - int retval; if (target->state != TARGET_HALTED) { LOG_TARGET_ERROR(target, "not halted"); @@ -1234,6 +1305,28 @@ static int cortex_a_step(struct target *target, bool current, target_addr_t addr cortex_a_unset_breakpoint(target, breakpoint); } + bool bkpt_inst_found = false; + int retval = cortex_a_maybe_skip_bkpt_inst(target, &bkpt_inst_found); + if (retval != ERROR_OK) + return retval; + + if (bkpt_inst_found) { + /* + * FW includes a breakpoint instruction at current PC. + * Update the PC for a dummy single step! + */ + target->debug_reason = DBG_REASON_SINGLESTEP; + target_call_event_callbacks(target, TARGET_EVENT_RESUMED); + + if (breakpoint) + cortex_a_set_breakpoint(target, breakpoint, 0); + + target->debug_reason = DBG_REASON_BREAKPOINT; + target_call_event_callbacks(target, TARGET_EVENT_HALTED); + + return ERROR_OK; + } + /* Setup single step breakpoint */ stepbreakpoint.address = address; stepbreakpoint.asid = 0; --
