This is an automated email from Gerrit. "Evgeniy Naydanov <[email protected]>" just uploaded a new patch set to Gerrit, which you can find at https://review.openocd.org/c/openocd/+/9664
-- gerrit commit 76c5afb0393379b0536b06d17e2cf2d56166aaba Author: Evgeniy Naydanov <[email protected]> Date: Mon Mar 23 17:11:07 2026 +0300 src: jtag: cleanup `pathmove` command * Before the patch the call `jtag pathmove RESET` used to segfault. * Error reporting was lacking. * The documentation was inconsistent with the implementation. Change-Id: I78d10986fea2d73146ed1889dbef69fb0b6d55f8 Signed-off-by: Evgeniy Naydanov <[email protected]> diff --git a/doc/openocd.texi b/doc/openocd.texi index 6538ff936b..0c985a897b 100644 --- a/doc/openocd.texi +++ b/doc/openocd.texi @@ -12769,7 +12769,8 @@ portable scripts currently must issue only BYPASS instructions. @deffn {Command} {pathmove} start_state [next_state ...] Start by moving to @var{start_state}, which -must be one of the @emph{stable} states. +must be one of the @emph{stable} states or a state reacheable from the current +state in one step. Unless it is the only state given, this will often be the current state, so that no TCK transitions are needed. Then, in a series of single state transitions diff --git a/src/jtag/core.c b/src/jtag/core.c index b46381f0ae..650f283901 100644 --- a/src/jtag/core.c +++ b/src/jtag/core.c @@ -517,6 +517,7 @@ int jtag_add_tms_seq(unsigned int nbits, const uint8_t *seq, enum tap_state stat void jtag_add_pathmove(unsigned int num_states, const enum tap_state *path) { + assert(num_states > 0); enum tap_state cur_state = cmd_queue_cur_state; /* the last state has to be a stable state */ diff --git a/src/jtag/tcl.c b/src/jtag/tcl.c index 90d6799abc..f4feb9ad16 100644 --- a/src/jtag/tcl.c +++ b/src/jtag/tcl.c @@ -176,35 +176,73 @@ fail: COMMAND_HANDLER(handle_jtag_command_pathmove) { - enum tap_state states[8]; - - if (CMD_ARGC < 1 || CMD_ARGC > ARRAY_SIZE(states)) + if (CMD_ARGC < 1) return ERROR_COMMAND_SYNTAX_ERROR; + int retval; + enum tap_state *states = malloc(sizeof(*states) * CMD_ARGC); + if (!states) { + LOG_ERROR("Out of memory"); + return ERROR_FAIL; + } for (unsigned int i = 0; i < CMD_ARGC; i++) { states[i] = tap_state_by_name(CMD_ARGV[i]); if (states[i] < 0) { - command_print(CMD, "endstate: %s invalid", CMD_ARGV[i]); - return ERROR_COMMAND_ARGUMENT_INVALID; + command_print(CMD, "%s is not a state name", CMD_ARGV[i]); + retval = ERROR_COMMAND_ARGUMENT_INVALID; + goto cleanup; } } - - int retval = jtag_add_statemove(states[0]); - if (retval == ERROR_OK) - retval = jtag_execute_queue(); - if (retval != ERROR_OK) { - command_print(CMD, "pathmove: jtag execute failed"); - return retval; + for (unsigned int i = 1; i < CMD_ARGC; i++) { + if (states[i] == TAP_RESET) { + command_print(CMD, "%s can only be the start state. ", + tap_state_name(TAP_RESET)); + retval = ERROR_COMMAND_ARGUMENT_INVALID; + goto cleanup; + } + if (!tap_is_state_next(states[i - 1], states[i])) { + command_print(CMD, "Can not transition from %s to %s", + tap_state_name(states[i - 1]), + tap_state_name(states[i])); + retval = ERROR_COMMAND_ARGUMENT_INVALID; + goto cleanup; + } + } + enum tap_state last_state = states[CMD_ARGC - 1]; + if (!scan_is_safe(last_state)) { + command_print(CMD, "The last state %s is not stable.", + tap_state_name(last_state)); + retval = ERROR_COMMAND_ARGUMENT_INVALID; + goto cleanup; + } + if (tap_is_state_stable(states[0])) { + retval = jtag_add_statemove(states[0]); + if (retval != ERROR_OK) { + command_print(CMD, "Can not transition form current " + "state %s to the starting state %s", + tap_state_name(cmd_queue_cur_state), + tap_state_name(states[0])); + goto cleanup; + } + if (CMD_ARGC > 1) + jtag_add_pathmove(CMD_ARGC - 1, states + 1); + } else { + if (!tap_is_state_next(cmd_queue_cur_state, states[0])) { + command_print(CMD, "Can not transition form current " + "state %s to the starting state %s", + tap_state_name(cmd_queue_cur_state), + tap_state_name(states[0])); + retval = ERROR_COMMAND_ARGUMENT_INVALID; + goto cleanup; + } + jtag_add_pathmove(CMD_ARGC, states); } - - jtag_add_pathmove(CMD_ARGC - 1, states + 1); retval = jtag_execute_queue(); - if (retval != ERROR_OK) { + if (retval != ERROR_OK) command_print(CMD, "pathmove: failed"); - return retval; - } - - return ERROR_OK; +cleanup: + free(states); + return retval; } COMMAND_HANDLER(handle_jtag_flush_count) @@ -249,9 +287,9 @@ static const struct command_registration jtag_command_handlers_to_move[] = { .name = "pathmove", .mode = COMMAND_EXEC, .handler = handle_jtag_command_pathmove, - .usage = "start_state state1 [state2 [state3 ...]]", - .help = "Move JTAG state machine from current state " - "(start_state) to state1, then state2, state3, etc.", + .usage = "start_state [next_state ...]", + .help = "Move JTAG state machine to the state start and then " + "follow the provided path through states.", }, COMMAND_REGISTRATION_DONE }; diff --git a/testing/tcl_commands/Makefile.am b/testing/tcl_commands/Makefile.am index fe8d150707..b0eee4dc96 100644 --- a/testing/tcl_commands/Makefile.am +++ b/testing/tcl_commands/Makefile.am @@ -5,7 +5,8 @@ TESTS = if DUMMY TESTS += \ test-target-create-command.cfg \ - test-target-configure-cget-command.cfg + test-target-configure-cget-command.cfg \ + test-jtag-pathmove.cfg endif EXTRA_DIST = utils.tcl $(TESTS) diff --git a/testing/tcl_commands/test-jtag-pathmove.cfg b/testing/tcl_commands/test-jtag-pathmove.cfg new file mode 100644 index 0000000000..3aa0b57445 --- /dev/null +++ b/testing/tcl_commands/test-jtag-pathmove.cfg @@ -0,0 +1,22 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +namespace import testing_helpers::* + +namespace import jtag_dummy_testing::* + +set ntaps 1 +setup $ntaps +init + +check_syntax_err {jtag pathmove} +check_invalid_arg {jtag pathmove NOT-A-STATE} +jtag pathmove RESET +jtag pathmove RUN/IDLE +jtag pathmove RUN/IDLE RUN/IDLE +jtag pathmove RESET RUN/IDLE +jtag pathmove DRSELECT DRCAPTURE DREXIT1 DRPAUSE +check_invalid_arg {jtag pathmove RESET RESET} RESET +check_invalid_arg {jtag pathmove RUN/IDLE RESET} RESET +check_invalid_arg {jtag pathmove RESET DRPAUSE} DRPAUSE +check_invalid_arg {jtag pathmove RESET RUN/IDLE DRSELECT} DRSELECT + +shutdown diff --git a/testing/tcl_commands/utils.tcl b/testing/tcl_commands/utils.tcl index 087bf04378..6caa285cfb 100644 --- a/testing/tcl_commands/utils.tcl +++ b/testing/tcl_commands/utils.tcl @@ -36,6 +36,10 @@ namespace eval testing_helpers { tailcall check_for_error {} $pattern $script } + proc check_invalid_arg {script {msg_ptrn {}}} { + tailcall check_for_error -603 $msg_ptrn $script + } + proc check_syntax_err script { tailcall check_for_error -601 {} $script } @@ -48,7 +52,7 @@ namespace eval testing_helpers { Was expecting '$pattern'." } - namespace export check_error_matches check_syntax_err check_matches + namespace export check_error_matches check_invalid_arg check_syntax_err check_matches } namespace eval configure_testing { --
