This is an automated email from Gerrit. "Antonio Borneo <borneo.anto...@gmail.com>" just uploaded a new patch set to Gerrit, which you can find at https://review.openocd.org/c/openocd/+/8871
-- gerrit commit 9c2ad5810574010e0da259011e6d3d338da1b7f9 Author: Antonio Borneo <borneo.anto...@gmail.com> Date: Fri May 2 16:10:39 2025 +0200 cortex-a: fix single-step on infinite loop On ARMv7a/r the single-step is implemented through a HW breakpoint that hits instructions at any address except the address of the current instruction. The method above fails in case of an infinite loop coded by a single instruction that jumps on itself; in such case, the same instruction (at the same address) is executed over and over and the breakpoint never hits. In current code this case is wrongly considered as an error. Reduce the timeout while waiting for the HW breakpoint being hit, then halt. The jump on itself would be executed several times before the timeout and the halt, but this is not an issue. There are few "pathological" instructions in ARMv7a/r that jumps on itself and that can have side effects if executed more than once. They are listed in the code. We do not consider these as real use cases generated by a compiler. Document the method in the code. Report that the single-step function is not properly managing the HW breakpoints if it exits on error. To be fixed in the future. Change-Id: I9641a4a3e2f68b83897ccf3a12d3c34e98a7805c Signed-off-by: Antonio Borneo <borneo.anto...@gmail.com> diff --git a/src/target/cortex_a.c b/src/target/cortex_a.c index ee27e1b217..46362017af 100644 --- a/src/target/cortex_a.c +++ b/src/target/cortex_a.c @@ -1169,6 +1169,29 @@ static int cortex_a_set_dscr_bits(struct target *target, return retval; } +/* + * Single-step on ARMv7a/r is implemented through a HW breakpoint that hits + * every instruction at any address except the address of the current + * instruction. + * Such HW breakpoint is never hit in case of a single instruction that jumps + * on itself (infinite loop), or a WFI or a WFE. In this case, halt the CPU + * after a timeout. + * The jump on itself would be executed several times before the timeout forces + * the halt, but this is not an issue. In ARMv7a/r there are few "pathological" + * instructions, listed below, that jumps on itself and that can have side + * effects if executed more than once; but they are not considered as real use + * cases generated by a compiler. + * Some example: + * - 'pop {pc}' or multi register 'pop' including PC, when the new PC value is + * the same value of current PC. The single step will not stop at the first + * 'pop' and will continue taking values from the stack, modifying SP at each + * iteration. + * - 'rfeda', 'rfedb', 'rfeia', 'rfeib', when the new PC value is the same + * value of current PC. The register provided to the instruction (usually SP) + * will be incremented or decremented at each iteration. + * + * TODO: fix exit in case of error, cleaning HW breakpoints. + */ static int cortex_a_step(struct target *target, bool current, target_addr_t address, bool handle_breakpoints) { @@ -1227,15 +1250,34 @@ static int cortex_a_step(struct target *target, bool current, target_addr_t addr if (retval != ERROR_OK) return retval; - int64_t then = timeval_ms(); + // poll at least once before starting the timeout + retval = cortex_a_poll(target); + if (retval != ERROR_OK) + return retval; + + int64_t then = timeval_ms() + 100; while (target->state != TARGET_HALTED) { + if (timeval_ms() > then) + break; + retval = cortex_a_poll(target); if (retval != ERROR_OK) return retval; - if (target->state == TARGET_HALTED) - break; - if (timeval_ms() > then + 1000) { - LOG_ERROR("timeout waiting for target halt"); + } + + if (target->state != TARGET_HALTED) { + LOG_TARGET_DEBUG(target, "timeout waiting for target halt, try halt"); + + retval = cortex_a_halt(target); + if (retval != ERROR_OK) + return retval; + + retval = cortex_a_poll(target); + if (retval != ERROR_OK) + return retval; + + if (target->state != TARGET_HALTED) { + LOG_TARGET_ERROR(target, "timeout waiting for target halt"); return ERROR_FAIL; } } @@ -1255,9 +1297,6 @@ static int cortex_a_step(struct target *target, bool current, target_addr_t addr if (breakpoint) cortex_a_set_breakpoint(target, breakpoint, 0); - if (target->state != TARGET_HALTED) - LOG_DEBUG("target stepped"); - return ERROR_OK; } --