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 {

-- 

Reply via email to