From b4f8d99c8d9e453b2beb3f6c9db7ff01c8d00756 Mon Sep 17 00:00:00 2001 From: Antonio Borneo Date: Mon, 28 Feb 2022 11:46:46 +0100 Subject: [PATCH 01/60] smp: deprecate legacy SMP core switching support The deprecation was already in the documentation since v0.11.0 through commit 85ba2dc4c6ab ("rtos/hwthread: add hardware-thread pseudo rtos") but OpenOCD was not informing the user printing a runtime message. Remove the deprecated method from the documentation and print a deprecated message at runtime. There is no reliable way to print the same message in GDB console, so we have to rely on user noticing it in the OpenOCD log. Target is to remove the functionality after v0.12.0. Change-Id: Idd2d9e3b6eccc92dcf0432c3c7de2f8a0fcabe9f Signed-off-by: Antonio Borneo Reviewed-on: https://review.openocd.org/c/openocd/+/6862 Tested-by: jenkins --- doc/openocd.texi | 51 ----------------------------------------- src/server/gdb_server.c | 2 ++ src/target/smp.c | 7 ++++++ src/target/smp.h | 2 ++ 4 files changed, 11 insertions(+), 51 deletions(-) diff --git a/doc/openocd.texi b/doc/openocd.texi index d55002733..07d4ad70a 100644 --- a/doc/openocd.texi +++ b/doc/openocd.texi @@ -11568,57 +11568,6 @@ The @command{step} and @command{stepi} commands can be used to step a specific c while other cores are free-running or remain halted, depending on the scheduler-locking mode configured in GDB. -@section Legacy SMP core switching support -@quotation Note -This method is deprecated in favor of the @emph{hwthread} pseudo RTOS. -@end quotation - -For SMP support following GDB serial protocol packet have been defined : -@itemize @bullet -@item j - smp status request -@item J - smp set request -@end itemize - -OpenOCD implements : -@itemize @bullet -@item @option{jc} packet for reading core id displayed by -GDB connection. Reply is @option{XXXXXXXX} (8 hex digits giving core id) or - @option{E01} for target not smp. -@item @option{JcXXXXXXXX} (8 hex digits) packet for setting core id displayed at next GDB continue -(core id -1 is reserved for returning to normal resume mode). Reply @option{E01} -for target not smp or @option{OK} on success. -@end itemize - -Handling of this packet within GDB can be done : -@itemize @bullet -@item by the creation of an internal variable (i.e @option{_core}) by mean -of function allocate_computed_value allowing following GDB command. -@example -set $_core 1 -#Jc01 packet is sent -print $_core -#jc packet is sent and result is affected in $ -@end example - -@item by the usage of GDB maintenance command as described in following example (2 cpus in SMP with -core id 0 and 1 @pxref{definecputargetsworkinginsmp,,Define CPU targets working in SMP}). - -@example -# toggle0 : force display of coreid 0 -define toggle0 -maint packet Jc0 -continue -main packet Jc-1 -end -# toggle1 : force display of coreid 1 -define toggle1 -maint packet Jc1 -continue -main packet Jc-1 -end -@end example -@end itemize - @node Tcl Scripting API @chapter Tcl Scripting API @cindex Tcl Scripting API diff --git a/src/server/gdb_server.c b/src/server/gdb_server.c index fcc87fba1..728cb53bb 100644 --- a/src/server/gdb_server.c +++ b/src/server/gdb_server.c @@ -3657,12 +3657,14 @@ static int gdb_input_inner(struct connection *connection) break; case 'j': + /* DEPRECATED */ /* packet supported only by smp target i.e cortex_a.c*/ /* handle smp packet replying coreid played to gbd */ gdb_read_smp_packet(connection, packet, packet_size); break; case 'J': + /* DEPRECATED */ /* packet supported only by smp target i.e cortex_a.c */ /* handle smp packet setting coreid to be played at next * resume to gdb */ diff --git a/src/target/smp.c b/src/target/smp.c index 3e1ded8bc..569abd786 100644 --- a/src/target/smp.c +++ b/src/target/smp.c @@ -28,6 +28,7 @@ #include "smp.h" #include "helper/binarybuffer.h" +/* DEPRECATED: gdb_read_smp_packet/gdb_write_smp_packet to be removed */ /* implementation of new packet in gdb interface for smp feature */ /* */ /* j : smp status request */ @@ -53,11 +54,15 @@ /* maint packet jc */ /* packet j :smp status request */ +#define DEPRECATED_MSG "DEPRECATED: This method is deprecated in favor of the hwthread pseudo RTOS" int gdb_read_smp_packet(struct connection *connection, char const *packet, int packet_size) { struct target *target = get_target_from_connection(connection); int retval = ERROR_OK; + + LOG_WARNING(DEPRECATED_MSG); + if (target->smp) { if (strncmp(packet, "jc", 2) == 0) { const uint32_t len = sizeof(target->gdb_service->core[0]); @@ -83,6 +88,8 @@ int gdb_write_smp_packet(struct connection *connection, int coreid = 0; int retval = ERROR_OK; + LOG_WARNING(DEPRECATED_MSG); + /* skip command character */ if (target->smp) { if (strncmp(packet, "Jc", 2) == 0) { diff --git a/src/target/smp.h b/src/target/smp.h index 490a49310..d373c90bc 100644 --- a/src/target/smp.h +++ b/src/target/smp.h @@ -30,8 +30,10 @@ extern const struct command_registration smp_command_handlers[]; +/* DEPRECATED */ int gdb_read_smp_packet(struct connection *connection, char const *packet, int packet_size); +/* DEPRECATED */ int gdb_write_smp_packet(struct connection *connection, char const *packet, int packet_size); From f88a7dde6a0c5e0a74806f6e81528a8525591e9e Mon Sep 17 00:00:00 2001 From: Antonio Borneo Date: Wed, 4 May 2022 16:17:08 +0200 Subject: [PATCH 02/60] server/gdb: fix gdb remote monitor cmd on multi-target Commit 5ebb1bdea1df ("server/gdb: fix return of gdb remote monitor command") replaces the call to command_run_line() with call to Jim_EvalObj() but does not properly set the "context". In multi-target environment, his can cause the erroneously execution of the command on the wrong target. Copy from the code in command_run_line() the proper setup before executing Jim_EvalObj(). Change-Id: I56738c80779082ca146a06c01bc30e28bc835fd3 Signed-off-by: Antonio Borneo Reported-by: Bohdan Tymkiv Fixes: 5ebb1bdea1df ("server/gdb: fix return of gdb remote monitor command") Reviewed-on: https://review.openocd.org/c/openocd/+/6966 Tested-by: jenkins Reviewed-by: Bohdan Tymkiv Reviewed-by: Tomas Vanek Reviewed-by: Tim Newsome --- src/server/gdb_server.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/server/gdb_server.c b/src/server/gdb_server.c index 728cb53bb..4efdc1ee7 100644 --- a/src/server/gdb_server.c +++ b/src/server/gdb_server.c @@ -2750,6 +2750,7 @@ static int gdb_query_packet(struct connection *connection, if (strncmp(packet, "qRcmd,", 6) == 0) { if (packet_size > 6) { + Jim_Interp *interp = cmd_ctx->interp; char *cmd; cmd = malloc((packet_size - 6) / 2 + 1); size_t len = unhexify((uint8_t *)cmd, packet + 6, (packet_size - 6) / 2); @@ -2761,20 +2762,31 @@ static int gdb_query_packet(struct connection *connection, /* some commands need to know the GDB connection, make note of current * GDB connection. */ current_gdb_connection = gdb_connection; - struct target *saved_target_override = cmd_ctx->current_target_override; - cmd_ctx->current_target_override = target; - int retval = Jim_EvalObj(cmd_ctx->interp, Jim_NewStringObj(cmd_ctx->interp, cmd, -1)); + struct target *saved_target_override = cmd_ctx->current_target_override; + cmd_ctx->current_target_override = NULL; + + struct command_context *old_context = Jim_GetAssocData(interp, "context"); + Jim_DeleteAssocData(interp, "context"); + int retval = Jim_SetAssocData(interp, "context", NULL, cmd_ctx); + if (retval == JIM_OK) { + retval = Jim_EvalObj(interp, Jim_NewStringObj(interp, cmd, -1)); + Jim_DeleteAssocData(interp, "context"); + } + int inner_retval = Jim_SetAssocData(interp, "context", NULL, old_context); + if (retval == JIM_OK) + retval = inner_retval; cmd_ctx->current_target_override = saved_target_override; + current_gdb_connection = NULL; target_call_timer_callbacks_now(); gdb_connection->output_flag = GDB_OUTPUT_NO; free(cmd); if (retval == JIM_RETURN) - retval = cmd_ctx->interp->returnCode; + retval = interp->returnCode; int lenmsg; - const char *cretmsg = Jim_GetString(Jim_GetResult(cmd_ctx->interp), &lenmsg); + const char *cretmsg = Jim_GetString(Jim_GetResult(interp), &lenmsg); char *retmsg; if (lenmsg && cretmsg[lenmsg - 1] != '\n') { retmsg = alloc_printf("%s\n", cretmsg); From ad5ca263e931792483cdb2a820b7aba3980829eb Mon Sep 17 00:00:00 2001 From: Salvatore Giorgio PECORINO Date: Tue, 12 Apr 2022 18:15:42 +0200 Subject: [PATCH 03/60] bluenrg: add support for bluenrg-lps device and board Added bluenrg-lps support Added file for the board steval-idb012v1 Fixed size_info information using a mask Changed the if condition in bluenrg-x.cfg to be valid only for bluenrg-1 and bluenrg-2 Signed-off-by: Salvatore Giorgio PECORINO Change-Id: Ic0777ec0811ee6fac7d5e1d065c4629e47d84a1f Reviewed-on: https://review.openocd.org/c/openocd/+/6928 Tested-by: jenkins Reviewed-by: Antonio Borneo --- doc/openocd.texi | 2 +- src/flash/nor/bluenrg-x.c | 19 ++++++++++++++++--- tcl/board/steval-idb012v1.cfg | 5 +++++ tcl/target/bluenrg-x.cfg | 7 +++++-- 4 files changed, 27 insertions(+), 6 deletions(-) create mode 100644 tcl/board/steval-idb012v1.cfg diff --git a/doc/openocd.texi b/doc/openocd.texi index 07d4ad70a..8bf0a31f7 100644 --- a/doc/openocd.texi +++ b/doc/openocd.texi @@ -6418,7 +6418,7 @@ The AVR 8-bit microcontrollers from Atmel integrate flash memory. @end deffn @deffn {Flash Driver} {bluenrg-x} -STMicroelectronics BlueNRG-1, BlueNRG-2 and BlueNRG-LP Bluetooth low energy wireless system-on-chip. They include ARM Cortex-M0/M0+ core and internal flash memory. +STMicroelectronics BlueNRG-1, BlueNRG-2 and BlueNRG-LP/LPS Bluetooth low energy wireless system-on-chip. They include ARM Cortex-M0/M0+ core and internal flash memory. The driver automatically recognizes these chips using the chip identification registers, and autoconfigures itself. diff --git a/src/flash/nor/bluenrg-x.c b/src/flash/nor/bluenrg-x.c index 60eccefaf..16075ec4c 100644 --- a/src/flash/nor/bluenrg-x.c +++ b/src/flash/nor/bluenrg-x.c @@ -35,6 +35,8 @@ #define JTAG_IDCODE_REG(bluenrgx_info) (bluenrgx_info->flash_ptr->jtag_idcode_reg) #define FLASH_PAGE_SIZE(bluenrgx_info) (bluenrgx_info->flash_ptr->flash_page_size) +#define FLASH_SIZE_REG_MASK (0xFFFF) + struct flash_ctrl_priv_data { uint32_t die_id_reg; uint32_t jtag_idcode_reg; @@ -75,6 +77,16 @@ static const struct flash_ctrl_priv_data flash_priv_data_lp = { .part_name = "BLUENRG-LP", }; +static const struct flash_ctrl_priv_data flash_priv_data_lps = { + .die_id_reg = 0x40000000, + .jtag_idcode_reg = 0x40000004, + .flash_base = 0x10040000, + .flash_regs_base = 0x40001000, + .flash_page_size = 2048, + .jtag_idcode = 0x02028041, + .part_name = "BLUENRG-LPS", +}; + struct bluenrgx_flash_bank { bool probed; uint32_t die_id; @@ -84,8 +96,8 @@ struct bluenrgx_flash_bank { static const struct flash_ctrl_priv_data *flash_ctrl[] = { &flash_priv_data_1, &flash_priv_data_2, - &flash_priv_data_lp -}; + &flash_priv_data_lp, + &flash_priv_data_lps}; /* flash_bank bluenrg-x 0 0 0 0 */ FLASH_BANK_COMMAND_HANDLER(bluenrgx_flash_bank_command) @@ -377,7 +389,7 @@ static int bluenrgx_probe(struct flash_bank *bank) if (retval != ERROR_OK) return retval; - if (idcode != flash_priv_data_lp.jtag_idcode) { + if ((idcode != flash_priv_data_lp.jtag_idcode) && (idcode != flash_priv_data_lps.jtag_idcode)) { retval = target_read_u32(bank->target, BLUENRG2_JTAG_REG, &idcode); if (retval != ERROR_OK) return retval; @@ -395,6 +407,7 @@ static int bluenrgx_probe(struct flash_bank *bank) } } retval = bluenrgx_read_flash_reg(bank, FLASH_SIZE_REG, &size_info); + size_info = size_info & FLASH_SIZE_REG_MASK; if (retval != ERROR_OK) return retval; diff --git a/tcl/board/steval-idb012v1.cfg b/tcl/board/steval-idb012v1.cfg new file mode 100644 index 000000000..25efc581f --- /dev/null +++ b/tcl/board/steval-idb012v1.cfg @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-or-later. +# This is an evaluation board with a single BlueNRG-LPS chip. +set CHIPNAME bluenrg-lps +source [find interface/cmsis-dap.cfg] +source [find target/bluenrg-x.cfg] \ No newline at end of file diff --git a/tcl/target/bluenrg-x.cfg b/tcl/target/bluenrg-x.cfg index ea94be962..1eba37616 100644 --- a/tcl/target/bluenrg-x.cfg +++ b/tcl/target/bluenrg-x.cfg @@ -47,11 +47,14 @@ if {![using_hla]} { cortex_m reset_config sysresetreq } +set JTAG_IDCODE_B2 0x0200A041 +set JTAG_IDCODE_B1 0x0 + $_TARGETNAME configure -event halted { global WDOG_VALUE global WDOG_VALUE_SET set _JTAG_IDCODE [mrw 0x40000004] - if {$_JTAG_IDCODE != 0x0201E041} { + if {$_JTAG_IDCODE == $JTAG_IDCODE_B2 || $_JTAG_IDCODE == $JTAG_IDCODE_B1} { # Stop watchdog during halt, if enabled. Only Bluenrg-1/2 set WDOG_VALUE [mrw 0x40700008] if [expr {$WDOG_VALUE & (1 << 1)}] { @@ -64,7 +67,7 @@ $_TARGETNAME configure -event resumed { global WDOG_VALUE global WDOG_VALUE_SET set _JTAG_IDCODE [mrw 0x40000004] - if {$_JTAG_IDCODE != 0x0201E041} { + if {$_JTAG_IDCODE == $JTAG_IDCODE_B2 || $_JTAG_IDCODE == $JTAG_IDCODE_B1} { if {$WDOG_VALUE_SET} { # Restore watchdog enable value after resume. Only Bluenrg-1/2 mww 0x40700008 $WDOG_VALUE From 7819834ace37ca7f5d1b834c761dfcb9964ef845 Mon Sep 17 00:00:00 2001 From: Antonio Borneo Date: Mon, 25 Apr 2022 23:04:49 +0200 Subject: [PATCH 04/60] target: fix build with jimtcl 0.79 In jimtcl 0.80 the prototype of Jim_DictPairs() has changed. The only code in OpenOCD that uses Jim_DictPairs() has been merged recently and it only uses the current jimtcl syntax. To allow compiling OpenOCD master branch with older versions of jimtcl, detect the version of jimtcl and use the appropriate syntax. Change-Id: I6fc78303b6a4db064a97f326c46119f4568e88f3 Signed-off-by: Antonio Borneo Reported-by: dullfire@yahoo.com Reviewed-on: https://review.openocd.org/c/openocd/+/6948 Tested-by: jenkins --- src/target/target.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/target/target.c b/src/target/target.c index 690526eb7..d2dff111a 100644 --- a/src/target/target.c +++ b/src/target/target.c @@ -5222,10 +5222,18 @@ static int target_jim_set_reg(Jim_Interp *interp, int argc, } int tmp; +#if JIM_VERSION >= 80 Jim_Obj **dict = Jim_DictPairs(interp, argv[1], &tmp); if (!dict) return JIM_ERR; +#else + Jim_Obj **dict; + int ret = Jim_DictPairs(interp, argv[1], &dict, &tmp); + + if (ret != JIM_OK) + return ret; +#endif const unsigned int length = tmp; struct command_context *cmd_ctx = current_command_context(interp); From 4c1919c566e79e6abcf006f3d03e4cc8cba54ff9 Mon Sep 17 00:00:00 2001 From: Tomas Vanek Date: Fri, 29 Apr 2022 10:10:58 +0200 Subject: [PATCH 05/60] target: document possibly unreachable target in deinit_target() Change-Id: I95ff3d200bb2c8f5bc43a34c92726d9c47f8c172 Signed-off-by: Tomas Vanek Reviewed-on: https://review.openocd.org/c/openocd/+/6953 Tested-by: jenkins Reviewed-by: Antonio Borneo --- src/target/target_type.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/target/target_type.h b/src/target/target_type.h index d6b6086b3..a26c2e7d8 100644 --- a/src/target/target_type.h +++ b/src/target/target_type.h @@ -242,6 +242,17 @@ struct target_type { /** * Free all the resources allocated by the target. * + * WARNING: deinit_target is called unconditionally regardless the target has + * ever been examined/initialised or not. + * If a problem has prevented establishing JTAG/SWD/... communication + * or + * if the target was created with -defer-examine flag and has never been + * examined + * then it is not possible to communicate with the target. + * + * If you need to talk to the target during deinit, first check if + * target_was_examined()! + * * @param target The target to deinit */ void (*deinit_target)(struct target *target); From 2fa3e2489fbaf3f99e2a869f18827cf4d0d5e3c9 Mon Sep 17 00:00:00 2001 From: Piotr Kasprzyk Date: Tue, 3 May 2022 15:42:41 +0200 Subject: [PATCH 06/60] doc: fix typo s/Not/Note/ Append lacking e to word Note Signed-off-by: Piotr Kasprzyk Change-Id: Ibd40a2f93d11cf1945361f0c46329b88963d6826 Reviewed-on: https://review.openocd.org/c/openocd/+/6963 Tested-by: jenkins Reviewed-by: Antonio Borneo --- doc/openocd.texi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/openocd.texi b/doc/openocd.texi index 8bf0a31f7..b87e87f87 100644 --- a/doc/openocd.texi +++ b/doc/openocd.texi @@ -12135,7 +12135,7 @@ It sort of works like this: When the command ``proc'' is parsed (which creates a procedure function) it gets 3 parameters on the command line. @b{1} the name of the proc (function), @b{2} the list of parameters, and @b{3} the body -of the function. Not the choice of words: LIST and BODY. The PROC +of the function. Note the choice of words: LIST and BODY. The PROC command stores these items in a table somewhere so it can be found by ``LookupCommand()'' From d1e14abdba478c59f4b948c86191e67216941151 Mon Sep 17 00:00:00 2001 From: Tarek BOCHKATI Date: Fri, 6 May 2022 12:45:33 +0100 Subject: [PATCH 07/60] cross-build.sh: fix build with capstone since commit 12d1ad0c7529 : update capstone include path ... the generated capstone.pc is not working so fix the includedir in capstone.pc to get github action working Change-Id: I7767e181a74c73a7514eeb6293cd556a794dbfe9 Signed-off-by: Tarek BOCHKATI Reviewed-on: https://review.openocd.org/c/openocd/+/6969 Tested-by: jenkins Reviewed-by: Tim Newsome Reviewed-by: Antonio Borneo --- contrib/cross-build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/cross-build.sh b/contrib/cross-build.sh index 8b31a3f00..7c572f55d 100755 --- a/contrib/cross-build.sh +++ b/contrib/cross-build.sh @@ -148,7 +148,7 @@ if [ -d $CAPSTONE_SRC ] ; then sed -i '1s;^;prefix=/usr \ exec_prefix=${prefix} \ libdir=${exec_prefix}/lib \ -includedir=${prefix}/include\n\n;' $CAPSTONE_PC_FILE +includedir=${prefix}/include/capstone\n\n;' $CAPSTONE_PC_FILE fi From d796f5929c3b2baafae4b8bf27d2b1b71c49ee53 Mon Sep 17 00:00:00 2001 From: Antonio Borneo Date: Sun, 23 Jan 2022 14:44:38 +0100 Subject: [PATCH 08/60] arm_coresight: define ARM_CS_CIDR_CLASS() Right now it has a single use but it will soon be used more. Change-Id: I9a819c65df467fc859e4b5251035a17ed33daa35 Signed-off-by: Antonio Borneo Reviewed-on: https://review.openocd.org/c/openocd/+/6813 Tested-by: jenkins Reviewed-by: Daniel Goehring --- src/target/arm_adi_v5.c | 2 +- src/target/arm_coresight.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/target/arm_adi_v5.c b/src/target/arm_adi_v5.c index 8d6d6618b..2848671db 100644 --- a/src/target/arm_adi_v5.c +++ b/src/target/arm_adi_v5.c @@ -1434,7 +1434,7 @@ static int dap_rom_display(struct command_invocation *cmd, command_print(cmd, "\t\tPeripheral ID 0x%010" PRIx64, pid); - const unsigned int class = (cid & ARM_CS_CIDR_CLASS_MASK) >> ARM_CS_CIDR_CLASS_SHIFT; + const unsigned int class = ARM_CS_CIDR_CLASS(cid); const unsigned int part_num = ARM_CS_PIDR_PART(pid); unsigned int designer_id = ARM_CS_PIDR_DESIGNER(pid); diff --git a/src/target/arm_coresight.h b/src/target/arm_coresight.h index a08f4fb53..71ee1719c 100644 --- a/src/target/arm_coresight.h +++ b/src/target/arm_coresight.h @@ -44,7 +44,7 @@ #define ARM_CS_CIDR3 (0xFFC) #define ARM_CS_CIDR_CLASS_MASK (0x0000F000) -#define ARM_CS_CIDR_CLASS_SHIFT (12) +#define ARM_CS_CIDR_CLASS(cidr) (((cidr) >> 12) & 0x000F) #define ARM_CS_CLASS_0X1_ROM_TABLE (0x1) #define ARM_CS_CLASS_0X9_CS_COMPONENT (0x9) From a785ca315ba5cb71317d43525321be8fb418bfd5 Mon Sep 17 00:00:00 2001 From: Antonio Borneo Date: Sun, 2 Jan 2022 11:04:08 +0100 Subject: [PATCH 09/60] adi_v5_jtag: reduce verbosity on persistent WAIT In case of AP not responding, e.g. not clocked, the first WAIT reply is logged as: DAP transaction stalled (WAIT) - slowing down then OpenOCD retries the transaction few times, until it timeouts. At each retry it prints the message: DAP transaction stalled during replay (WAIT) - resending Depending on JTAG speed and transport latency, the amount of log messages can be quite annoying and not relevant. The last printed line is at timeout: Timeout during WAIT recovery Reduce the verbosity. Change-Id: I5a7a337527c98b2450de59066b13713511c2894f Signed-off-by: Antonio Borneo Reviewed-on: https://review.openocd.org/c/openocd/+/6814 Tested-by: jenkins Reviewed-by: Tomas Vanek Reviewed-by: Daniel Goehring --- src/target/adi_v5_jtag.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/target/adi_v5_jtag.c b/src/target/adi_v5_jtag.c index be625807c..94ee8cf41 100644 --- a/src/target/adi_v5_jtag.c +++ b/src/target/adi_v5_jtag.c @@ -532,7 +532,7 @@ static int jtagdp_overrun_check(struct adiv5_dap *dap) /* check for overrun condition in the last batch of transactions */ if (found_wait) { - LOG_INFO("DAP transaction stalled (WAIT) - slowing down"); + LOG_INFO("DAP transaction stalled (WAIT) - slowing down and resending"); /* clear the sticky overrun condition */ retval = adi_jtag_scan_inout_check_u32(dap, JTAG_DP_DPACC, DP_CTRL_STAT, DPAP_WRITE, @@ -574,7 +574,7 @@ static int jtagdp_overrun_check(struct adiv5_dap *dap) retval = ERROR_JTAG_DEVICE_ERROR; break; } - LOG_INFO("DAP transaction stalled during replay (WAIT) - resending"); + LOG_DEBUG("DAP transaction stalled during replay (WAIT) - resending"); /* clear the sticky overrun condition */ retval = adi_jtag_scan_inout_check_u32(dap, JTAG_DP_DPACC, DP_CTRL_STAT, DPAP_WRITE, From fed329feec985b74961f5209c824ba3ca618eea7 Mon Sep 17 00:00:00 2001 From: Antonio Borneo Date: Mon, 16 Aug 2021 15:24:52 +0200 Subject: [PATCH 10/60] arm_adi_v5: describe Class 0x9 Device Architecture register Use the list of values from ARM IHI0029E to decode and print the Device Architecture register. Add attribute 'unused' to the function, not used yet. Change-Id: I7b1dd204bd1db671578c588372b667e23611876c Signed-off-by: Antonio Borneo Reviewed-on: https://review.openocd.org/c/openocd/+/6463 Tested-by: jenkins Reviewed-by: Daniel Goehring --- src/target/arm_adi_v5.c | 49 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/src/target/arm_adi_v5.c b/src/target/arm_adi_v5.c index 2848671db..d886a765f 100644 --- a/src/target/arm_adi_v5.c +++ b/src/target/arm_adi_v5.c @@ -893,6 +893,55 @@ static const char *class_description[16] = { [0xF] = "CoreLink, PrimeCell or System component", }; +#define ARCH_ID(architect, archid) ( \ + (((architect) << ARM_CS_C9_DEVARCH_ARCHITECT_SHIFT) & ARM_CS_C9_DEVARCH_ARCHITECT_MASK) | \ + (((archid) << ARM_CS_C9_DEVARCH_ARCHID_SHIFT) & ARM_CS_C9_DEVARCH_ARCHID_MASK) \ +) + +static const struct { + uint32_t arch_id; + const char *description; +} class0x9_devarch[] = { + /* keep same unsorted order as in ARM IHI0029E */ + { ARCH_ID(ARM_ID, 0x0A00), "RAS architecture" }, + { ARCH_ID(ARM_ID, 0x1A01), "Instrumentation Trace Macrocell (ITM) architecture" }, + { ARCH_ID(ARM_ID, 0x1A02), "DWT architecture" }, + { ARCH_ID(ARM_ID, 0x1A03), "Flash Patch and Breakpoint unit (FPB) architecture" }, + { ARCH_ID(ARM_ID, 0x2A04), "Processor debug architecture (ARMv8-M)" }, + { ARCH_ID(ARM_ID, 0x6A05), "Processor debug architecture (ARMv8-R)" }, + { ARCH_ID(ARM_ID, 0x0A10), "PC sample-based profiling" }, + { ARCH_ID(ARM_ID, 0x4A13), "Embedded Trace Macrocell (ETM) architecture" }, + { ARCH_ID(ARM_ID, 0x1A14), "Cross Trigger Interface (CTI) architecture" }, + { ARCH_ID(ARM_ID, 0x6A15), "Processor debug architecture (v8.0-A)" }, + { ARCH_ID(ARM_ID, 0x7A15), "Processor debug architecture (v8.1-A)" }, + { ARCH_ID(ARM_ID, 0x8A15), "Processor debug architecture (v8.2-A)" }, + { ARCH_ID(ARM_ID, 0x2A16), "Processor Performance Monitor (PMU) architecture" }, + { ARCH_ID(ARM_ID, 0x0A17), "Memory Access Port v2 architecture" }, + { ARCH_ID(ARM_ID, 0x0A27), "JTAG Access Port v2 architecture" }, + { ARCH_ID(ARM_ID, 0x0A31), "Basic trace router" }, + { ARCH_ID(ARM_ID, 0x0A37), "Power requestor" }, + { ARCH_ID(ARM_ID, 0x0A47), "Unknown Access Port v2 architecture" }, + { ARCH_ID(ARM_ID, 0x0A50), "HSSTP architecture" }, + { ARCH_ID(ARM_ID, 0x0A63), "System Trace Macrocell (STM) architecture" }, + { ARCH_ID(ARM_ID, 0x0A75), "CoreSight ELA architecture" }, + { ARCH_ID(ARM_ID, 0x0AF7), "CoreSight ROM architecture" }, +}; + +#define DEVARCH_ID_MASK (ARM_CS_C9_DEVARCH_ARCHITECT_MASK | ARM_CS_C9_DEVARCH_ARCHID_MASK) + +__attribute__((unused)) +static const char *class0x9_devarch_description(uint32_t devarch) +{ + if (!(devarch & ARM_CS_C9_DEVARCH_PRESENT)) + return "not present"; + + for (unsigned int i = 0; i < ARRAY_SIZE(class0x9_devarch); i++) + if ((devarch & DEVARCH_ID_MASK) == class0x9_devarch[i].arch_id) + return class0x9_devarch[i].description; + + return "unknown"; +} + static const struct { enum ap_type type; const char *description; From 1fd2a6c7f5cba7c5ec655e355e63dd31804bb900 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Tue, 6 Jul 2021 20:19:28 -0700 Subject: [PATCH 11/60] arm_adi_v5: add support for display Class 0x9 ROM tables ADI v5.1 and v6.0 permit the definition of CoreSight components (class 9 ROM entries). dap_rom_display() is refactored a bit such that we always end up with attempting to parse the ROM contents using the appropriate upper limit for class 1 and 9 ROM types. Change-Id: I4ba497b3807f1f11f06186eb6e61959ea3540c59 Signed-off-by: Florian Fainelli Signed-off-by: Antonio Borneo Reviewed-on: https://review.openocd.org/c/openocd/+/6359 Tested-by: jenkins --- src/target/arm_adi_v5.c | 69 ++++++++++++++++++++++++++++------------- 1 file changed, 47 insertions(+), 22 deletions(-) diff --git a/src/target/arm_adi_v5.c b/src/target/arm_adi_v5.c index d886a765f..99673f233 100644 --- a/src/target/arm_adi_v5.c +++ b/src/target/arm_adi_v5.c @@ -928,8 +928,8 @@ static const struct { }; #define DEVARCH_ID_MASK (ARM_CS_C9_DEVARCH_ARCHITECT_MASK | ARM_CS_C9_DEVARCH_ARCHID_MASK) +#define DEVARCH_ROM_C_0X9 ARCH_ID(ARM_ID, 0x0AF7) -__attribute__((unused)) static const char *class0x9_devarch_description(uint32_t devarch) { if (!(devarch & ARM_CS_C9_DEVARCH_PRESENT)) @@ -1449,6 +1449,7 @@ static int dap_devtype_display(struct command_invocation *cmd, uint32_t devtype) static int dap_rom_display(struct command_invocation *cmd, struct adiv5_ap *ap, target_addr_t dbgbase, int depth) { + unsigned int rom_num_entries; int retval; uint64_t pid; uint32_t cid; @@ -1513,27 +1514,7 @@ static int dap_rom_display(struct command_invocation *cmd, else command_print(cmd, "\t\tMEMTYPE system memory not present: dedicated debug bus"); - /* Read ROM table entries from base address until we get 0x00000000 or reach the reserved area */ - for (uint16_t entry_offset = 0; entry_offset < 0xF00; entry_offset += 4) { - uint32_t romentry; - retval = mem_ap_read_atomic_u32(ap, base_addr | entry_offset, &romentry); - if (retval != ERROR_OK) - return retval; - command_print(cmd, "\t%sROMTABLE[0x%x] = 0x%" PRIx32 "", - tabs, entry_offset, romentry); - if (romentry & ARM_CS_ROMENTRY_PRESENT) { - /* Recurse. "romentry" is signed */ - retval = dap_rom_display(cmd, ap, base_addr + (int32_t)(romentry & ARM_CS_ROMENTRY_OFFSET_MASK), - depth + 1); - if (retval != ERROR_OK) - return retval; - } else if (romentry != 0) { - command_print(cmd, "\t\tComponent not present"); - } else { - command_print(cmd, "\t%s\tEnd of ROM table", tabs); - break; - } - } + rom_num_entries = 960; } else if (class == ARM_CS_CLASS_0X9_CS_COMPONENT) { uint32_t devtype; retval = mem_ap_read_atomic_u32(ap, base_addr + ARM_CS_C9_DEVTYPE, &devtype); @@ -1545,6 +1526,50 @@ static int dap_rom_display(struct command_invocation *cmd, return retval; /* REVISIT also show ARM_CS_C9_DEVID */ + + uint32_t devarch; + retval = mem_ap_read_atomic_u32(ap, base_addr + ARM_CS_C9_DEVARCH, &devarch); + if (retval != ERROR_OK) + return retval; + + if ((devarch & ARM_CS_C9_DEVARCH_PRESENT) == 0) + return ERROR_OK; + + unsigned int architect_id = (devarch & ARM_CS_C9_DEVARCH_ARCHITECT_MASK) >> ARM_CS_C9_DEVARCH_ARCHITECT_SHIFT; + unsigned int revision = (devarch & ARM_CS_C9_DEVARCH_REVISION_MASK) >> ARM_CS_C9_DEVARCH_REVISION_SHIFT; + command_print(cmd, "\t\tDev Arch is 0x%08" PRIx32 ", %s \"%s\" rev.%u", devarch, + jep106_manufacturer(architect_id), class0x9_devarch_description(devarch), + revision); + /* quit if not ROM table */ + if ((devarch & DEVARCH_ID_MASK) != DEVARCH_ROM_C_0X9) + return ERROR_OK; + + rom_num_entries = 512; + } else { + /* Class other than 0x1 and 0x9 */ + return ERROR_OK; + } + + /* Read ROM table entries from base address until we get 0x00000000 or reach the reserved area */ + for (unsigned int entry_offset = 0; entry_offset < 4 * rom_num_entries; entry_offset += 4) { + uint32_t romentry; + retval = mem_ap_read_atomic_u32(ap, base_addr + entry_offset, &romentry); + if (retval != ERROR_OK) + return retval; + command_print(cmd, "\t%sROMTABLE[0x%x] = 0x%08" PRIx32 "", + tabs, entry_offset, romentry); + if (romentry & ARM_CS_ROMENTRY_PRESENT) { + /* Recurse. "romentry" is signed */ + retval = dap_rom_display(cmd, ap, base_addr + (int32_t)(romentry & ARM_CS_ROMENTRY_OFFSET_MASK), + depth + 1); + if (retval != ERROR_OK) + return retval; + } else if (romentry != 0) { + command_print(cmd, "\t\tComponent not present"); + } else { + command_print(cmd, "\t%s\tEnd of ROM table", tabs); + break; + } } return ERROR_OK; From c3f6e21d5ccc165493b23677b6d63c24f5c183c3 Mon Sep 17 00:00:00 2001 From: Antonio Borneo Date: Fri, 14 Jan 2022 00:43:37 +0100 Subject: [PATCH 12/60] arm_adi_v5: rework dap_read_part_id() Rework dap_read_part_id() while preparing for reorganizing the 'ROM Table Parsing' (RTP): - rename it with 'rtp' prefix; - extends it to read other CoreSight registers, thus improving the overall speed by queuing more reads; - reduce the list of arguments by using a struct; - reorder the reads by increasing offset, potentially gaining speed using MEM_AP_REG_BDx and/or auto-increment; - log a debug message in case of read error. Change-Id: I6544ac7740b808a6c0fbacf97ac00b97f5bd3832 Signed-off-by: Antonio Borneo Reviewed-on: https://review.openocd.org/c/openocd/+/6815 Tested-by: jenkins --- src/target/arm_adi_v5.c | 151 +++++++++++++++++++++------------------- 1 file changed, 81 insertions(+), 70 deletions(-) diff --git a/src/target/arm_adi_v5.c b/src/target/arm_adi_v5.c index 99673f233..19af45a94 100644 --- a/src/target/arm_adi_v5.c +++ b/src/target/arm_adi_v5.c @@ -1094,53 +1094,80 @@ int dap_lookup_cs_component(struct adiv5_ap *ap, return ERROR_OK; } -static int dap_read_part_id(struct adiv5_ap *ap, target_addr_t component_base, uint32_t *cid, uint64_t *pid) +/** Holds registers of a CoreSight component */ +struct cs_component_vals { + uint64_t pid; + uint32_t cid; + uint32_t devarch; + uint32_t devtype_memtype; +}; + +/** + * Read the CoreSight registers needed during ROM Table Parsing (RTP). + * + * @param ap Pointer to AP containing the component. + * @param component_base On MEM-AP access method, base address of the component. + * @param v Pointer to the struct holding the value of registers. + * + * @return ERROR_OK on success, else a fault code. + */ +static int rtp_read_cs_regs(struct adiv5_ap *ap, target_addr_t component_base, + struct cs_component_vals *v) { assert(IS_ALIGNED(component_base, ARM_CS_ALIGN)); - assert(ap && cid && pid); + assert(ap && v); uint32_t cid0, cid1, cid2, cid3; uint32_t pid0, pid1, pid2, pid3, pid4; - int retval; + int retval = ERROR_OK; - /* IDs are in last 4K section */ - retval = mem_ap_read_u32(ap, component_base + ARM_CS_PIDR0, &pid0); - if (retval != ERROR_OK) - return retval; - retval = mem_ap_read_u32(ap, component_base + ARM_CS_PIDR1, &pid1); - if (retval != ERROR_OK) - return retval; - retval = mem_ap_read_u32(ap, component_base + ARM_CS_PIDR2, &pid2); - if (retval != ERROR_OK) - return retval; - retval = mem_ap_read_u32(ap, component_base + ARM_CS_PIDR3, &pid3); - if (retval != ERROR_OK) - return retval; - retval = mem_ap_read_u32(ap, component_base + ARM_CS_PIDR4, &pid4); - if (retval != ERROR_OK) - return retval; - retval = mem_ap_read_u32(ap, component_base + ARM_CS_CIDR0, &cid0); - if (retval != ERROR_OK) - return retval; - retval = mem_ap_read_u32(ap, component_base + ARM_CS_CIDR1, &cid1); - if (retval != ERROR_OK) - return retval; - retval = mem_ap_read_u32(ap, component_base + ARM_CS_CIDR2, &cid2); - if (retval != ERROR_OK) - return retval; - retval = mem_ap_read_u32(ap, component_base + ARM_CS_CIDR3, &cid3); - if (retval != ERROR_OK) - return retval; + /* sort by offset to gain speed */ - retval = dap_run(ap->dap); - if (retval != ERROR_OK) - return retval; + /* + * Registers DEVARCH and DEVTYPE are valid on Class 0x9 devices + * only, but are at offset above 0xf00, so can be read on any device + * without triggering error. Read them for eventual use on Class 0x9. + */ + if (retval == ERROR_OK) + retval = mem_ap_read_u32(ap, component_base + ARM_CS_C9_DEVARCH, &v->devarch); - *cid = (cid3 & 0xff) << 24 + /* Same address as ARM_CS_C1_MEMTYPE */ + if (retval == ERROR_OK) + retval = mem_ap_read_u32(ap, component_base + ARM_CS_C9_DEVTYPE, &v->devtype_memtype); + + if (retval == ERROR_OK) + retval = mem_ap_read_u32(ap, component_base + ARM_CS_PIDR4, &pid4); + + if (retval == ERROR_OK) + retval = mem_ap_read_u32(ap, component_base + ARM_CS_PIDR0, &pid0); + if (retval == ERROR_OK) + retval = mem_ap_read_u32(ap, component_base + ARM_CS_PIDR1, &pid1); + if (retval == ERROR_OK) + retval = mem_ap_read_u32(ap, component_base + ARM_CS_PIDR2, &pid2); + if (retval == ERROR_OK) + retval = mem_ap_read_u32(ap, component_base + ARM_CS_PIDR3, &pid3); + + if (retval == ERROR_OK) + retval = mem_ap_read_u32(ap, component_base + ARM_CS_CIDR0, &cid0); + if (retval == ERROR_OK) + retval = mem_ap_read_u32(ap, component_base + ARM_CS_CIDR1, &cid1); + if (retval == ERROR_OK) + retval = mem_ap_read_u32(ap, component_base + ARM_CS_CIDR2, &cid2); + if (retval == ERROR_OK) + retval = mem_ap_read_u32(ap, component_base + ARM_CS_CIDR3, &cid3); + + if (retval == ERROR_OK) + retval = dap_run(ap->dap); + if (retval != ERROR_OK) { + LOG_DEBUG("Failed read CoreSight registers"); + return retval; + } + + v->cid = (cid3 & 0xff) << 24 | (cid2 & 0xff) << 16 | (cid1 & 0xff) << 8 | (cid0 & 0xff); - *pid = (uint64_t)(pid4 & 0xff) << 32 + v->pid = (uint64_t)(pid4 & 0xff) << 32 | (pid3 & 0xff) << 24 | (pid2 & 0xff) << 16 | (pid1 & 0xff) << 8 @@ -1449,10 +1476,9 @@ static int dap_devtype_display(struct command_invocation *cmd, uint32_t devtype) static int dap_rom_display(struct command_invocation *cmd, struct adiv5_ap *ap, target_addr_t dbgbase, int depth) { + struct cs_component_vals v; unsigned int rom_num_entries; int retval; - uint64_t pid; - uint32_t cid; char tabs[16] = ""; if (depth > 16) { @@ -1466,29 +1492,29 @@ static int dap_rom_display(struct command_invocation *cmd, target_addr_t base_addr = dbgbase & 0xFFFFFFFFFFFFF000ull; command_print(cmd, "\t\tComponent base address " TARGET_ADDR_FMT, base_addr); - retval = dap_read_part_id(ap, base_addr, &cid, &pid); + retval = rtp_read_cs_regs(ap, base_addr, &v); if (retval != ERROR_OK) { command_print(cmd, "\t\tCan't read component, the corresponding core might be turned off"); return ERROR_OK; /* Don't abort recursion */ } - if (!is_valid_arm_cs_cidr(cid)) { - command_print(cmd, "\t\tInvalid CID 0x%08" PRIx32, cid); + if (!is_valid_arm_cs_cidr(v.cid)) { + command_print(cmd, "\t\tInvalid CID 0x%08" PRIx32, v.cid); return ERROR_OK; /* Don't abort recursion */ } /* component may take multiple 4K pages */ - uint32_t size = ARM_CS_PIDR_SIZE(pid); + uint32_t size = ARM_CS_PIDR_SIZE(v.pid); if (size > 0) command_print(cmd, "\t\tStart address " TARGET_ADDR_FMT, base_addr - 0x1000 * size); - command_print(cmd, "\t\tPeripheral ID 0x%010" PRIx64, pid); + command_print(cmd, "\t\tPeripheral ID 0x%010" PRIx64, v.pid); - const unsigned int class = ARM_CS_CIDR_CLASS(cid); - const unsigned int part_num = ARM_CS_PIDR_PART(pid); - unsigned int designer_id = ARM_CS_PIDR_DESIGNER(pid); + const unsigned int class = ARM_CS_CIDR_CLASS(v.cid); + const unsigned int part_num = ARM_CS_PIDR_PART(v.pid); + unsigned int designer_id = ARM_CS_PIDR_DESIGNER(v.pid); - if (pid & ARM_CS_PIDR_JEDEC) { + if (v.pid & ARM_CS_PIDR_JEDEC) { /* JEP106 code */ command_print(cmd, "\t\tDesigner is 0x%03x, %s", designer_id, jep106_manufacturer(designer_id)); @@ -1504,44 +1530,29 @@ static int dap_rom_display(struct command_invocation *cmd, command_print(cmd, "\t\tComponent class is 0x%x, %s", class, class_description[class]); if (class == ARM_CS_CLASS_0X1_ROM_TABLE) { - uint32_t memtype; - retval = mem_ap_read_atomic_u32(ap, base_addr + ARM_CS_C1_MEMTYPE, &memtype); - if (retval != ERROR_OK) - return retval; - - if (memtype & ARM_CS_C1_MEMTYPE_SYSMEM_MASK) + if (v.devtype_memtype & ARM_CS_C1_MEMTYPE_SYSMEM_MASK) command_print(cmd, "\t\tMEMTYPE system memory present on bus"); else command_print(cmd, "\t\tMEMTYPE system memory not present: dedicated debug bus"); rom_num_entries = 960; } else if (class == ARM_CS_CLASS_0X9_CS_COMPONENT) { - uint32_t devtype; - retval = mem_ap_read_atomic_u32(ap, base_addr + ARM_CS_C9_DEVTYPE, &devtype); - if (retval != ERROR_OK) - return retval; - - retval = dap_devtype_display(cmd, devtype); + retval = dap_devtype_display(cmd, v.devtype_memtype); if (retval != ERROR_OK) return retval; /* REVISIT also show ARM_CS_C9_DEVID */ - uint32_t devarch; - retval = mem_ap_read_atomic_u32(ap, base_addr + ARM_CS_C9_DEVARCH, &devarch); - if (retval != ERROR_OK) - return retval; - - if ((devarch & ARM_CS_C9_DEVARCH_PRESENT) == 0) + if ((v.devarch & ARM_CS_C9_DEVARCH_PRESENT) == 0) return ERROR_OK; - unsigned int architect_id = (devarch & ARM_CS_C9_DEVARCH_ARCHITECT_MASK) >> ARM_CS_C9_DEVARCH_ARCHITECT_SHIFT; - unsigned int revision = (devarch & ARM_CS_C9_DEVARCH_REVISION_MASK) >> ARM_CS_C9_DEVARCH_REVISION_SHIFT; - command_print(cmd, "\t\tDev Arch is 0x%08" PRIx32 ", %s \"%s\" rev.%u", devarch, - jep106_manufacturer(architect_id), class0x9_devarch_description(devarch), + unsigned int architect_id = (v.devarch & ARM_CS_C9_DEVARCH_ARCHITECT_MASK) >> ARM_CS_C9_DEVARCH_ARCHITECT_SHIFT; + unsigned int revision = (v.devarch & ARM_CS_C9_DEVARCH_REVISION_MASK) >> ARM_CS_C9_DEVARCH_REVISION_SHIFT; + command_print(cmd, "\t\tDev Arch is 0x%08" PRIx32 ", %s \"%s\" rev.%u", v.devarch, + jep106_manufacturer(architect_id), class0x9_devarch_description(v.devarch), revision); /* quit if not ROM table */ - if ((devarch & DEVARCH_ID_MASK) != DEVARCH_ROM_C_0X9) + if ((v.devarch & DEVARCH_ID_MASK) != DEVARCH_ROM_C_0X9) return ERROR_OK; rom_num_entries = 512; From 209cb38aa155825175e2806d6093cecb5d77edc1 Mon Sep 17 00:00:00 2001 From: Antonio Borneo Date: Fri, 14 Jan 2022 13:52:01 +0100 Subject: [PATCH 13/60] arm_adi_v5: split ROM table loop from generic coresight During ROM table parsing, each ROM table entry points to a CoreSight component that can, in turn, be another ROM table. Split the specific code for ROM table handling from the generic CoreSight code. Log an error if a ROM table entry cannot be read. Change-Id: I5ad106a99b9c21ddb48b5b162ae87101e4f49878 Signed-off-by: Antonio Borneo Reviewed-on: https://review.openocd.org/c/openocd/+/6816 Tested-by: jenkins --- src/target/arm_adi_v5.c | 102 ++++++++++++++++++++++++++-------------- 1 file changed, 66 insertions(+), 36 deletions(-) diff --git a/src/target/arm_adi_v5.c b/src/target/arm_adi_v5.c index 19af45a94..4176dcadd 100644 --- a/src/target/arm_adi_v5.c +++ b/src/target/arm_adi_v5.c @@ -1473,14 +1473,67 @@ static int dap_devtype_display(struct command_invocation *cmd, uint32_t devtype) return ERROR_OK; } -static int dap_rom_display(struct command_invocation *cmd, - struct adiv5_ap *ap, target_addr_t dbgbase, int depth) +static int rtp_cs_component(struct command_invocation *cmd, + struct adiv5_ap *ap, target_addr_t dbgbase, int depth); + +static int rtp_rom_loop(struct command_invocation *cmd, + struct adiv5_ap *ap, target_addr_t base_address, int depth, + unsigned int max_entries) +{ + assert(IS_ALIGNED(base_address, ARM_CS_ALIGN)); + + char tabs[16] = ""; + + if (depth) + snprintf(tabs, sizeof(tabs), "[L%02d] ", depth); + + unsigned int offset = 0; + while (max_entries--) { + uint32_t romentry; + unsigned int saved_offset = offset; + + int retval = mem_ap_read_atomic_u32(ap, base_address + offset, &romentry); + offset += 4; + if (retval != ERROR_OK) { + LOG_DEBUG("Failed read ROM table entry"); + command_print(cmd, "\t%sROMTABLE[0x%x] Read error", tabs, saved_offset); + command_print(cmd, "\t\tUnable to continue"); + command_print(cmd, "\t%s\tStop parsing of ROM table", tabs); + return retval; + } + + command_print(cmd, "\t%sROMTABLE[0x%x] = 0x%08" PRIx32, + tabs, saved_offset, romentry); + + if (romentry == 0) { + command_print(cmd, "\t%s\tEnd of ROM table", tabs); + break; + } + + if (!(romentry & ARM_CS_ROMENTRY_PRESENT)) { + command_print(cmd, "\t\tComponent not present"); + continue; + } + + /* Recurse. "romentry" is signed */ + target_addr_t component_base = base_address + (int32_t)(romentry & ARM_CS_ROMENTRY_OFFSET_MASK); + retval = rtp_cs_component(cmd, ap, component_base, depth + 1); + if (retval != ERROR_OK) + return retval; + } + + return ERROR_OK; +} + +static int rtp_cs_component(struct command_invocation *cmd, + struct adiv5_ap *ap, target_addr_t base_address, int depth) { struct cs_component_vals v; - unsigned int rom_num_entries; int retval; char tabs[16] = ""; + assert(IS_ALIGNED(base_address, ARM_CS_ALIGN)); + if (depth > 16) { command_print(cmd, "\tTables too deep"); return ERROR_FAIL; @@ -1489,10 +1542,9 @@ static int dap_rom_display(struct command_invocation *cmd, if (depth) snprintf(tabs, sizeof(tabs), "[L%02d] ", depth); - target_addr_t base_addr = dbgbase & 0xFFFFFFFFFFFFF000ull; - command_print(cmd, "\t\tComponent base address " TARGET_ADDR_FMT, base_addr); + command_print(cmd, "\t\tComponent base address " TARGET_ADDR_FMT, base_address); - retval = rtp_read_cs_regs(ap, base_addr, &v); + retval = rtp_read_cs_regs(ap, base_address, &v); if (retval != ERROR_OK) { command_print(cmd, "\t\tCan't read component, the corresponding core might be turned off"); return ERROR_OK; /* Don't abort recursion */ @@ -1506,7 +1558,7 @@ static int dap_rom_display(struct command_invocation *cmd, /* component may take multiple 4K pages */ uint32_t size = ARM_CS_PIDR_SIZE(v.pid); if (size > 0) - command_print(cmd, "\t\tStart address " TARGET_ADDR_FMT, base_addr - 0x1000 * size); + command_print(cmd, "\t\tStart address " TARGET_ADDR_FMT, base_address - 0x1000 * size); command_print(cmd, "\t\tPeripheral ID 0x%010" PRIx64, v.pid); @@ -1535,8 +1587,10 @@ static int dap_rom_display(struct command_invocation *cmd, else command_print(cmd, "\t\tMEMTYPE system memory not present: dedicated debug bus"); - rom_num_entries = 960; - } else if (class == ARM_CS_CLASS_0X9_CS_COMPONENT) { + return rtp_rom_loop(cmd, ap, base_address, depth, 960); + } + + if (class == ARM_CS_CLASS_0X9_CS_COMPONENT) { retval = dap_devtype_display(cmd, v.devtype_memtype); if (retval != ERROR_OK) return retval; @@ -1555,34 +1609,10 @@ static int dap_rom_display(struct command_invocation *cmd, if ((v.devarch & DEVARCH_ID_MASK) != DEVARCH_ROM_C_0X9) return ERROR_OK; - rom_num_entries = 512; - } else { - /* Class other than 0x1 and 0x9 */ - return ERROR_OK; - } - - /* Read ROM table entries from base address until we get 0x00000000 or reach the reserved area */ - for (unsigned int entry_offset = 0; entry_offset < 4 * rom_num_entries; entry_offset += 4) { - uint32_t romentry; - retval = mem_ap_read_atomic_u32(ap, base_addr + entry_offset, &romentry); - if (retval != ERROR_OK) - return retval; - command_print(cmd, "\t%sROMTABLE[0x%x] = 0x%08" PRIx32 "", - tabs, entry_offset, romentry); - if (romentry & ARM_CS_ROMENTRY_PRESENT) { - /* Recurse. "romentry" is signed */ - retval = dap_rom_display(cmd, ap, base_addr + (int32_t)(romentry & ARM_CS_ROMENTRY_OFFSET_MASK), - depth + 1); - if (retval != ERROR_OK) - return retval; - } else if (romentry != 0) { - command_print(cmd, "\t\tComponent not present"); - } else { - command_print(cmd, "\t%s\tEnd of ROM table", tabs); - break; - } + return rtp_rom_loop(cmd, ap, base_address, depth, 512); } + /* Class other than 0x1 and 0x9 */ return ERROR_OK; } @@ -1628,7 +1658,7 @@ int dap_info_command(struct command_invocation *cmd, else command_print(cmd, "\tROM table in legacy format"); - dap_rom_display(cmd, ap, dbgbase & 0xFFFFFFFFFFFFF000ull, 0); + rtp_cs_component(cmd, ap, dbgbase & 0xFFFFFFFFFFFFF000ull, 0); } } From d4335071b8460b227889a3a2072df424eebb4e5e Mon Sep 17 00:00:00 2001 From: Antonio Borneo Date: Sat, 15 Jan 2022 15:44:20 +0100 Subject: [PATCH 14/60] arm_adi_v5: report sysmem on class 0x9 ROM tables As Class 0x1 ROM table, also Class 0x9 ROM tables encodes a flag for system memory access. Detect the flag in rtp_cs_component() and dump the same message for both type of ROM tables. Extend rtp_read_cs_regs() to read ARM_CS_C9_DEVID. Change-Id: Ic85d1ea068ed706ceedfd65076ff4c96d04e9792 Signed-off-by: Antonio Borneo Reviewed-on: https://review.openocd.org/c/openocd/+/6817 Tested-by: jenkins Reviewed-by: Daniel Goehring --- src/target/arm_adi_v5.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/target/arm_adi_v5.c b/src/target/arm_adi_v5.c index 4176dcadd..5c3941b3f 100644 --- a/src/target/arm_adi_v5.c +++ b/src/target/arm_adi_v5.c @@ -1099,6 +1099,7 @@ struct cs_component_vals { uint64_t pid; uint32_t cid; uint32_t devarch; + uint32_t devid; uint32_t devtype_memtype; }; @@ -1124,13 +1125,16 @@ static int rtp_read_cs_regs(struct adiv5_ap *ap, target_addr_t component_base, /* sort by offset to gain speed */ /* - * Registers DEVARCH and DEVTYPE are valid on Class 0x9 devices + * Registers DEVARCH, DEVID and DEVTYPE are valid on Class 0x9 devices * only, but are at offset above 0xf00, so can be read on any device * without triggering error. Read them for eventual use on Class 0x9. */ if (retval == ERROR_OK) retval = mem_ap_read_u32(ap, component_base + ARM_CS_C9_DEVARCH, &v->devarch); + if (retval == ERROR_OK) + retval = mem_ap_read_u32(ap, component_base + ARM_CS_C9_DEVID, &v->devid); + /* Same address as ARM_CS_C1_MEMTYPE */ if (retval == ERROR_OK) retval = mem_ap_read_u32(ap, component_base + ARM_CS_C9_DEVTYPE, &v->devtype_memtype); @@ -1609,6 +1613,11 @@ static int rtp_cs_component(struct command_invocation *cmd, if ((v.devarch & DEVARCH_ID_MASK) != DEVARCH_ROM_C_0X9) return ERROR_OK; + if (v.devid & ARM_CS_C9_DEVID_SYSMEM_MASK) + command_print(cmd, "\t\tMEMTYPE system memory present on bus"); + else + command_print(cmd, "\t\tMEMTYPE system memory not present: dedicated debug bus"); + return rtp_rom_loop(cmd, ap, base_address, depth, 512); } From a73adb52410acb4b4b92f281920d14bd5c490fe4 Mon Sep 17 00:00:00 2001 From: Antonio Borneo Date: Fri, 14 Jan 2022 15:15:30 +0100 Subject: [PATCH 15/60] arm_adi_v5: handle faulting entry in ROM table ARM IHI0031F "Arm Debug Interface Architecture Specification" chapter C2.6.1 "BASE, Debug Base Address register" reports: A debugger must handle the following situations as non-fatal errors: - ... - An entry in the ROM Table points to a faulting location. - ... Typically, a debugger issues a warning if it encounters one of these situations. However, Arm recommends that it continues operating. An example of an implementation that might cause errors of this type is a system with static base address or ROM Table entries that enable entire subsystems to be disabled, for example by a tie-off input, packaging choice, fuse, or similar. Don't halt ROM table parsing if one entry causes an error; log the error condition and continue to next entry. Not sure if we have to send an ABORT before continuing. Change-Id: I94fdb5b175bfb07dde378149421582b7e7cd5b09 Signed-off-by: Antonio Borneo Reviewed-on: https://review.openocd.org/c/openocd/+/6818 Tested-by: jenkins Reviewed-by: Daniel Goehring --- src/target/arm_adi_v5.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/target/arm_adi_v5.c b/src/target/arm_adi_v5.c index 5c3941b3f..4d24e8f2f 100644 --- a/src/target/arm_adi_v5.c +++ b/src/target/arm_adi_v5.c @@ -1522,8 +1522,11 @@ static int rtp_rom_loop(struct command_invocation *cmd, /* Recurse. "romentry" is signed */ target_addr_t component_base = base_address + (int32_t)(romentry & ARM_CS_ROMENTRY_OFFSET_MASK); retval = rtp_cs_component(cmd, ap, component_base, depth + 1); - if (retval != ERROR_OK) - return retval; + if (retval != ERROR_OK) { + /* TODO: do we need to send an ABORT before continuing? */ + LOG_DEBUG("Ignore error parsing CoreSight component"); + continue; + } } return ERROR_OK; From 21f7885d1c2aa8a8f3d6967d8ee2910570e5cc11 Mon Sep 17 00:00:00 2001 From: Antonio Borneo Date: Fri, 14 Jan 2022 18:20:09 +0100 Subject: [PATCH 16/60] arm_adi_v5: separate ROM table parsing from command output [1/3] In OpenOCD arm_adi_v5 we have already two implementations of code for parsing the ADIv5 ROM table: - in the commands "dap info" and "$dap_name info"; - in the function dap_lookup_cs_component(). Adding support for ADIv6 requires extending both implementations. Moreover, current code does not handle few aspects of the ROM parsing, e.g. the "Power Domain IDs". To add such extensions both implementations should be touched. I plan to add a command to parses (again) the ROM table and dump a simple prototype of a configuration script for the target, useful while analysing a new target. Keeping aligned all these implementation would be too complex. With focus to "dap info" command, decouple the part of code to walk-through the ROM table from the code that creates the command output. The idea is to keep a single implementation for the walk-through code, while parametrizing the output code to handle the generation of a configuration script or the result of the function dap_lookup_cs_component(). This change only targets the output of MEM-AP header Further changes will target other parts of the code. While there, add a message if MEM-AP is not accessible. Change-Id: I112f637edfdb8688afb4e631297f6536da9604f1 Signed-off-by: Antonio Borneo Reviewed-on: https://review.openocd.org/c/openocd/+/6819 Tested-by: jenkins --- src/target/arm_adi_v5.c | 50 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 44 insertions(+), 6 deletions(-) diff --git a/src/target/arm_adi_v5.c b/src/target/arm_adi_v5.c index 4d24e8f2f..333b1889a 100644 --- a/src/target/arm_adi_v5.c +++ b/src/target/arm_adi_v5.c @@ -1477,6 +1477,11 @@ static int dap_devtype_display(struct command_invocation *cmd, uint32_t devtype) return ERROR_OK; } +/* TODO: these prototypes will be removed in a following patch */ +static int dap_info_mem_ap_header(struct command_invocation *cmd, + int retval, struct adiv5_ap *ap, + target_addr_t dbgbase, uint32_t apid); + static int rtp_cs_component(struct command_invocation *cmd, struct adiv5_ap *ap, target_addr_t dbgbase, int depth); @@ -1634,13 +1639,48 @@ int dap_info_command(struct command_invocation *cmd, int retval; uint32_t apid; target_addr_t dbgbase; - target_addr_t dbgaddr; + target_addr_t invalid_entry; /* Now we read ROM table ID registers, ref. ARM IHI 0029B sec */ retval = dap_get_debugbase(ap, &dbgbase, &apid); + retval = dap_info_mem_ap_header(cmd, retval, ap, dbgbase, apid); if (retval != ERROR_OK) return retval; + if (apid == 0) + return ERROR_FAIL; + + /* NOTE: a MEM-AP may have a single CoreSight component that's + * not a ROM table ... or have no such components at all. + */ + const unsigned int class = (apid & AP_REG_IDR_CLASS_MASK) >> AP_REG_IDR_CLASS_SHIFT; + + if (class == AP_REG_IDR_CLASS_MEM_AP) { + if (is_64bit_ap(ap)) + invalid_entry = 0xFFFFFFFFFFFFFFFFull; + else + invalid_entry = 0xFFFFFFFFul; + + if (dbgbase != invalid_entry && (dbgbase & 0x3) != 0x2) + rtp_cs_component(cmd, ap, dbgbase & 0xFFFFFFFFFFFFF000ull, 0); + } + + return ERROR_OK; +} + +/* Actions for command "dap info" */ + +static int dap_info_mem_ap_header(struct command_invocation *cmd, + int retval, struct adiv5_ap *ap, + target_addr_t dbgbase, uint32_t apid) +{ + target_addr_t invalid_entry; + + if (retval != ERROR_OK) { + command_print(cmd, "\t\tCan't read MEM-AP, the corresponding core might be turned off"); + return retval; + } + command_print(cmd, "AP ID register 0x%8.8" PRIx32, apid); if (apid == 0) { command_print(cmd, "No AP found at this ap 0x%x", ap->ap_num); @@ -1656,21 +1696,19 @@ int dap_info_command(struct command_invocation *cmd, if (class == AP_REG_IDR_CLASS_MEM_AP) { if (is_64bit_ap(ap)) - dbgaddr = 0xFFFFFFFFFFFFFFFFull; + invalid_entry = 0xFFFFFFFFFFFFFFFFull; else - dbgaddr = 0xFFFFFFFFul; + invalid_entry = 0xFFFFFFFFul; command_print(cmd, "MEM-AP BASE " TARGET_ADDR_FMT, dbgbase); - if (dbgbase == dbgaddr || (dbgbase & 0x3) == 0x2) { + if (dbgbase == invalid_entry || (dbgbase & 0x3) == 0x2) { command_print(cmd, "\tNo ROM table present"); } else { if (dbgbase & 0x01) command_print(cmd, "\tValid ROM table present"); else command_print(cmd, "\tROM table in legacy format"); - - rtp_cs_component(cmd, ap, dbgbase & 0xFFFFFFFFFFFFF000ull, 0); } } From c83b94b2c8a77adfe7febc2e340ffcd9ce9a6c97 Mon Sep 17 00:00:00 2001 From: Antonio Borneo Date: Sun, 16 Jan 2022 13:59:38 +0100 Subject: [PATCH 17/60] arm_adi_v5: separate ROM table parsing from command output [2/3] This change only targets the output of rtp_cs_component(). To easily propagate the coordinates of the CoreSight component, add them in the struct that holds the register values. While there, define a macro for the max depth of ROM tables. Change-Id: I75e5ef4f9419da3192123aebcd61471c2af9374f Signed-off-by: Antonio Borneo Reviewed-on: https://review.openocd.org/c/openocd/+/6820 Tested-by: jenkins --- src/target/arm_adi_v5.c | 168 +++++++++++++++++++++++-------------- src/target/arm_coresight.h | 4 + 2 files changed, 107 insertions(+), 65 deletions(-) diff --git a/src/target/arm_adi_v5.c b/src/target/arm_adi_v5.c index 333b1889a..b6053b098 100644 --- a/src/target/arm_adi_v5.c +++ b/src/target/arm_adi_v5.c @@ -1094,8 +1094,10 @@ int dap_lookup_cs_component(struct adiv5_ap *ap, return ERROR_OK; } -/** Holds registers of a CoreSight component */ +/** Holds registers and coordinates of a CoreSight component */ struct cs_component_vals { + struct adiv5_ap *ap; + target_addr_t component_base; uint64_t pid; uint32_t cid; uint32_t devarch; @@ -1122,6 +1124,9 @@ static int rtp_read_cs_regs(struct adiv5_ap *ap, target_addr_t component_base, uint32_t pid0, pid1, pid2, pid3, pid4; int retval = ERROR_OK; + v->ap = ap; + v->component_base = component_base; + /* sort by offset to gain speed */ /* @@ -1477,10 +1482,15 @@ static int dap_devtype_display(struct command_invocation *cmd, uint32_t devtype) return ERROR_OK; } +/* Broken ROM tables can have circular references. Stop after a while */ +#define ROM_TABLE_MAX_DEPTH (16) + /* TODO: these prototypes will be removed in a following patch */ static int dap_info_mem_ap_header(struct command_invocation *cmd, int retval, struct adiv5_ap *ap, target_addr_t dbgbase, uint32_t apid); +static int dap_info_cs_component(struct command_invocation *cmd, + int retval, struct cs_component_vals *v, int depth); static int rtp_cs_component(struct command_invocation *cmd, struct adiv5_ap *ap, target_addr_t dbgbase, int depth); @@ -1542,90 +1552,34 @@ static int rtp_cs_component(struct command_invocation *cmd, { struct cs_component_vals v; int retval; - char tabs[16] = ""; assert(IS_ALIGNED(base_address, ARM_CS_ALIGN)); - if (depth > 16) { - command_print(cmd, "\tTables too deep"); - return ERROR_FAIL; - } + if (depth > ROM_TABLE_MAX_DEPTH) + retval = ERROR_FAIL; + else + retval = rtp_read_cs_regs(ap, base_address, &v); - if (depth) - snprintf(tabs, sizeof(tabs), "[L%02d] ", depth); - - command_print(cmd, "\t\tComponent base address " TARGET_ADDR_FMT, base_address); - - retval = rtp_read_cs_regs(ap, base_address, &v); - if (retval != ERROR_OK) { - command_print(cmd, "\t\tCan't read component, the corresponding core might be turned off"); + retval = dap_info_cs_component(cmd, retval, &v, depth); + if (retval != ERROR_OK) return ERROR_OK; /* Don't abort recursion */ - } - if (!is_valid_arm_cs_cidr(v.cid)) { - command_print(cmd, "\t\tInvalid CID 0x%08" PRIx32, v.cid); + if (!is_valid_arm_cs_cidr(v.cid)) return ERROR_OK; /* Don't abort recursion */ - } - - /* component may take multiple 4K pages */ - uint32_t size = ARM_CS_PIDR_SIZE(v.pid); - if (size > 0) - command_print(cmd, "\t\tStart address " TARGET_ADDR_FMT, base_address - 0x1000 * size); - - command_print(cmd, "\t\tPeripheral ID 0x%010" PRIx64, v.pid); const unsigned int class = ARM_CS_CIDR_CLASS(v.cid); - const unsigned int part_num = ARM_CS_PIDR_PART(v.pid); - unsigned int designer_id = ARM_CS_PIDR_DESIGNER(v.pid); - - if (v.pid & ARM_CS_PIDR_JEDEC) { - /* JEP106 code */ - command_print(cmd, "\t\tDesigner is 0x%03x, %s", - designer_id, jep106_manufacturer(designer_id)); - } else { - /* Legacy ASCII ID, clear invalid bits */ - designer_id &= 0x7f; - command_print(cmd, "\t\tDesigner ASCII code 0x%02x, %s", - designer_id, designer_id == 0x41 ? "ARM" : ""); - } - - const struct dap_part_nums *partnum = pidr_to_part_num(designer_id, part_num); - command_print(cmd, "\t\tPart is 0x%03x, %s %s", part_num, partnum->type, partnum->full); - command_print(cmd, "\t\tComponent class is 0x%x, %s", class, class_description[class]); - - if (class == ARM_CS_CLASS_0X1_ROM_TABLE) { - if (v.devtype_memtype & ARM_CS_C1_MEMTYPE_SYSMEM_MASK) - command_print(cmd, "\t\tMEMTYPE system memory present on bus"); - else - command_print(cmd, "\t\tMEMTYPE system memory not present: dedicated debug bus"); + if (class == ARM_CS_CLASS_0X1_ROM_TABLE) return rtp_rom_loop(cmd, ap, base_address, depth, 960); - } if (class == ARM_CS_CLASS_0X9_CS_COMPONENT) { - retval = dap_devtype_display(cmd, v.devtype_memtype); - if (retval != ERROR_OK) - return retval; - - /* REVISIT also show ARM_CS_C9_DEVID */ - if ((v.devarch & ARM_CS_C9_DEVARCH_PRESENT) == 0) return ERROR_OK; - unsigned int architect_id = (v.devarch & ARM_CS_C9_DEVARCH_ARCHITECT_MASK) >> ARM_CS_C9_DEVARCH_ARCHITECT_SHIFT; - unsigned int revision = (v.devarch & ARM_CS_C9_DEVARCH_REVISION_MASK) >> ARM_CS_C9_DEVARCH_REVISION_SHIFT; - command_print(cmd, "\t\tDev Arch is 0x%08" PRIx32 ", %s \"%s\" rev.%u", v.devarch, - jep106_manufacturer(architect_id), class0x9_devarch_description(v.devarch), - revision); /* quit if not ROM table */ if ((v.devarch & DEVARCH_ID_MASK) != DEVARCH_ROM_C_0X9) return ERROR_OK; - if (v.devid & ARM_CS_C9_DEVID_SYSMEM_MASK) - command_print(cmd, "\t\tMEMTYPE system memory present on bus"); - else - command_print(cmd, "\t\tMEMTYPE system memory not present: dedicated debug bus"); - return rtp_rom_loop(cmd, ap, base_address, depth, 512); } @@ -1715,6 +1669,90 @@ static int dap_info_mem_ap_header(struct command_invocation *cmd, return ERROR_OK; } +static int dap_info_cs_component(struct command_invocation *cmd, + int retval, struct cs_component_vals *v, int depth) +{ + if (depth > ROM_TABLE_MAX_DEPTH) { + command_print(cmd, "\tTables too deep"); + return ERROR_FAIL; + } + + command_print(cmd, "\t\tComponent base address " TARGET_ADDR_FMT, v->component_base); + + if (retval != ERROR_OK) { + command_print(cmd, "\t\tCan't read component, the corresponding core might be turned off"); + return retval; + } + + if (!is_valid_arm_cs_cidr(v->cid)) { + command_print(cmd, "\t\tInvalid CID 0x%08" PRIx32, v->cid); + return ERROR_OK; /* Don't abort recursion */ + } + + /* component may take multiple 4K pages */ + uint32_t size = ARM_CS_PIDR_SIZE(v->pid); + if (size > 0) + command_print(cmd, "\t\tStart address " TARGET_ADDR_FMT, v->component_base - 0x1000 * size); + + command_print(cmd, "\t\tPeripheral ID 0x%010" PRIx64, v->pid); + + const unsigned int part_num = ARM_CS_PIDR_PART(v->pid); + unsigned int designer_id = ARM_CS_PIDR_DESIGNER(v->pid); + + if (v->pid & ARM_CS_PIDR_JEDEC) { + /* JEP106 code */ + command_print(cmd, "\t\tDesigner is 0x%03x, %s", + designer_id, jep106_manufacturer(designer_id)); + } else { + /* Legacy ASCII ID, clear invalid bits */ + designer_id &= 0x7f; + command_print(cmd, "\t\tDesigner ASCII code 0x%02x, %s", + designer_id, designer_id == 0x41 ? "ARM" : ""); + } + + const struct dap_part_nums *partnum = pidr_to_part_num(designer_id, part_num); + command_print(cmd, "\t\tPart is 0x%03x, %s %s", part_num, partnum->type, partnum->full); + + const unsigned int class = ARM_CS_CIDR_CLASS(v->cid); + command_print(cmd, "\t\tComponent class is 0x%x, %s", class, class_description[class]); + + if (class == ARM_CS_CLASS_0X1_ROM_TABLE) { + if (v->devtype_memtype & ARM_CS_C1_MEMTYPE_SYSMEM_MASK) + command_print(cmd, "\t\tMEMTYPE system memory present on bus"); + else + command_print(cmd, "\t\tMEMTYPE system memory not present: dedicated debug bus"); + return ERROR_OK; + } + + if (class == ARM_CS_CLASS_0X9_CS_COMPONENT) { + dap_devtype_display(cmd, v->devtype_memtype); + + /* REVISIT also show ARM_CS_C9_DEVID */ + + if ((v->devarch & ARM_CS_C9_DEVARCH_PRESENT) == 0) + return ERROR_OK; + + unsigned int architect_id = ARM_CS_C9_DEVARCH_ARCHITECT(v->devarch); + unsigned int revision = ARM_CS_C9_DEVARCH_REVISION(v->devarch); + command_print(cmd, "\t\tDev Arch is 0x%08" PRIx32 ", %s \"%s\" rev.%u", v->devarch, + jep106_manufacturer(architect_id), class0x9_devarch_description(v->devarch), + revision); + + if ((v->devarch & DEVARCH_ID_MASK) == DEVARCH_ROM_C_0X9) { + command_print(cmd, "\t\tType is ROM table"); + + if (v->devid & ARM_CS_C9_DEVID_SYSMEM_MASK) + command_print(cmd, "\t\tMEMTYPE system memory present on bus"); + else + command_print(cmd, "\t\tMEMTYPE system memory not present: dedicated debug bus"); + } + return ERROR_OK; + } + + /* Class other than 0x1 and 0x9 */ + return ERROR_OK; +} + enum adiv5_cfg_param { CFG_DAP, CFG_AP_NUM, diff --git a/src/target/arm_coresight.h b/src/target/arm_coresight.h index 71ee1719c..ff4c25ad7 100644 --- a/src/target/arm_coresight.h +++ b/src/target/arm_coresight.h @@ -66,6 +66,10 @@ static inline bool is_valid_arm_cs_cidr(uint32_t cidr) #define ARM_CS_C9_DEVARCH_PRESENT BIT(20) #define ARM_CS_C9_DEVARCH_ARCHITECT_MASK (0xFFE00000) #define ARM_CS_C9_DEVARCH_ARCHITECT_SHIFT (21) +#define ARM_CS_C9_DEVARCH_REVISION(devarch) \ + (((devarch) & ARM_CS_C9_DEVARCH_REVISION_MASK) >> ARM_CS_C9_DEVARCH_REVISION_SHIFT) +#define ARM_CS_C9_DEVARCH_ARCHITECT(devarch) \ + (((devarch) & ARM_CS_C9_DEVARCH_ARCHITECT_MASK) >> ARM_CS_C9_DEVARCH_ARCHITECT_SHIFT) #define ARM_CS_C9_DEVID (0xFC8) From d01b3d69ec17da19576c85bb36245399211eb620 Mon Sep 17 00:00:00 2001 From: Antonio Borneo Date: Sun, 16 Jan 2022 14:02:43 +0100 Subject: [PATCH 18/60] arm_adi_v5: separate ROM table parsing from command output [3/3] This change only targets the output of rtp_rom_loop(). Change-Id: If9ac013798923428c3b897a969887e98b6935a2b Signed-off-by: Antonio Borneo Reviewed-on: https://review.openocd.org/c/openocd/+/6821 Tested-by: jenkins --- src/target/arm_adi_v5.c | 58 +++++++++++++++++++++++++++++------------ 1 file changed, 41 insertions(+), 17 deletions(-) diff --git a/src/target/arm_adi_v5.c b/src/target/arm_adi_v5.c index b6053b098..60bd54666 100644 --- a/src/target/arm_adi_v5.c +++ b/src/target/arm_adi_v5.c @@ -1491,6 +1491,9 @@ static int dap_info_mem_ap_header(struct command_invocation *cmd, target_addr_t dbgbase, uint32_t apid); static int dap_info_cs_component(struct command_invocation *cmd, int retval, struct cs_component_vals *v, int depth); +static int dap_info_rom_table_entry(struct command_invocation *cmd, + int retval, int depth, + unsigned int offset, uint32_t romentry); static int rtp_cs_component(struct command_invocation *cmd, struct adiv5_ap *ap, target_addr_t dbgbase, int depth); @@ -1501,11 +1504,6 @@ static int rtp_rom_loop(struct command_invocation *cmd, { assert(IS_ALIGNED(base_address, ARM_CS_ALIGN)); - char tabs[16] = ""; - - if (depth) - snprintf(tabs, sizeof(tabs), "[L%02d] ", depth); - unsigned int offset = 0; while (max_entries--) { uint32_t romentry; @@ -1513,26 +1511,20 @@ static int rtp_rom_loop(struct command_invocation *cmd, int retval = mem_ap_read_atomic_u32(ap, base_address + offset, &romentry); offset += 4; - if (retval != ERROR_OK) { + if (retval != ERROR_OK) LOG_DEBUG("Failed read ROM table entry"); - command_print(cmd, "\t%sROMTABLE[0x%x] Read error", tabs, saved_offset); - command_print(cmd, "\t\tUnable to continue"); - command_print(cmd, "\t%s\tStop parsing of ROM table", tabs); - return retval; - } - command_print(cmd, "\t%sROMTABLE[0x%x] = 0x%08" PRIx32, - tabs, saved_offset, romentry); + retval = dap_info_rom_table_entry(cmd, retval, depth, saved_offset, romentry); + if (retval != ERROR_OK) + return retval; if (romentry == 0) { - command_print(cmd, "\t%s\tEnd of ROM table", tabs); + /* End of ROM table */ break; } - if (!(romentry & ARM_CS_ROMENTRY_PRESENT)) { - command_print(cmd, "\t\tComponent not present"); + if (!(romentry & ARM_CS_ROMENTRY_PRESENT)) continue; - } /* Recurse. "romentry" is signed */ target_addr_t component_base = base_address + (int32_t)(romentry & ARM_CS_ROMENTRY_OFFSET_MASK); @@ -1753,6 +1745,38 @@ static int dap_info_cs_component(struct command_invocation *cmd, return ERROR_OK; } +static int dap_info_rom_table_entry(struct command_invocation *cmd, + int retval, int depth, + unsigned int offset, uint32_t romentry) +{ + char tabs[16] = ""; + + if (depth) + snprintf(tabs, sizeof(tabs), "[L%02d] ", depth); + + if (retval != ERROR_OK) { + command_print(cmd, "\t%sROMTABLE[0x%x] Read error", tabs, offset); + command_print(cmd, "\t\tUnable to continue"); + command_print(cmd, "\t%s\tStop parsing of ROM table", tabs); + return retval; + } + + command_print(cmd, "\t%sROMTABLE[0x%x] = 0x%08" PRIx32, + tabs, offset, romentry); + + if (romentry == 0) { + command_print(cmd, "\t%s\tEnd of ROM table", tabs); + return ERROR_OK; + } + + if (!(romentry & ARM_CS_ROMENTRY_PRESENT)) { + command_print(cmd, "\t\tComponent not present"); + return ERROR_OK; + } + + return ERROR_OK; +} + enum adiv5_cfg_param { CFG_DAP, CFG_AP_NUM, From 7351330a0f1f6ac626964e515ed50bb43499e0e5 Mon Sep 17 00:00:00 2001 From: Antonio Borneo Date: Sun, 16 Jan 2022 14:05:44 +0100 Subject: [PATCH 19/60] arm_adi_v5: abstract actions during ROM table parsing Now all the actions (build command output) of command "dap info" are decoupled from the ROM walk-through. Pass the actions as a generic parameter to ROM walk-through code. Put as private data every information that is only required by the actions and not by the ROM walk-through. Change-Id: I3b6ad112ea21296458c94aebbf91bf65bf6657a7 Signed-off-by: Antonio Borneo Reviewed-on: https://review.openocd.org/c/openocd/+/6822 Tested-by: jenkins --- src/target/arm_adi_v5.c | 153 ++++++++++++++++++++++++++++++++-------- 1 file changed, 123 insertions(+), 30 deletions(-) diff --git a/src/target/arm_adi_v5.c b/src/target/arm_adi_v5.c index 60bd54666..54d04abd6 100644 --- a/src/target/arm_adi_v5.c +++ b/src/target/arm_adi_v5.c @@ -1482,23 +1482,104 @@ static int dap_devtype_display(struct command_invocation *cmd, uint32_t devtype) return ERROR_OK; } +/** + * Actions/operations to be executed while parsing ROM tables. + */ +struct rtp_ops { + /** + * Executed at the start of a new MEM-AP, typically to print the MEM-AP header. + * @param retval Error encountered while reading AP. + * @param ap Pointer to AP. + * @param dbgbase Value of MEM-AP Debug Base Address register. + * @param apid Value of MEM-AP IDR Identification Register. + * @param priv Pointer to private data. + * @return ERROR_OK on success, else a fault code. + */ + int (*mem_ap_header)(int retval, struct adiv5_ap *ap, uint64_t dbgbase, + uint32_t apid, void *priv); + /** + * Executed when a CoreSight component is parsed, typically to print + * information on the component. + * @param retval Error encountered while reading component's registers. + * @param v Pointer to a container of the component's registers. + * @param depth The current depth level of ROM table. + * @param priv Pointer to private data. + * @return ERROR_OK on success, else a fault code. + */ + int (*cs_component)(int retval, struct cs_component_vals *v, int depth, void *priv); + /** + * Executed for each entry of a ROM table, typically to print the entry + * and information about validity or end-of-table mark. + * @param retval Error encountered while reading the ROM table entry. + * @param depth The current depth level of ROM table. + * @param offset The offset of the entry in the ROM table. + * @param romentry The value of the ROM table entry. + * @param priv Pointer to private data. + * @return ERROR_OK on success, else a fault code. + */ + int (*rom_table_entry)(int retval, int depth, unsigned int offset, uint32_t romentry, + void *priv); + /** + * Private data + */ + void *priv; +}; + +/** + * Wrapper around struct rtp_ops::mem_ap_header. + * Input parameter @a retval is propagated. + */ +static int rtp_ops_mem_ap_header(const struct rtp_ops *ops, + int retval, struct adiv5_ap *ap, uint64_t dbgbase, uint32_t apid) +{ + if (!ops->mem_ap_header) + return retval; + + int retval1 = ops->mem_ap_header(retval, ap, dbgbase, apid, ops->priv); + if (retval != ERROR_OK) + return retval; + return retval1; +} + +/** + * Wrapper around struct rtp_ops::cs_component. + * Input parameter @a retval is propagated. + */ +static int rtp_ops_cs_component(const struct rtp_ops *ops, + int retval, struct cs_component_vals *v, int depth) +{ + if (!ops->cs_component) + return retval; + + int retval1 = ops->cs_component(retval, v, depth, ops->priv); + if (retval != ERROR_OK) + return retval; + return retval1; +} + +/** + * Wrapper around struct rtp_ops::rom_table_entry. + * Input parameter @a retval is propagated. + */ +static int rtp_ops_rom_table_entry(const struct rtp_ops *ops, + int retval, int depth, unsigned int offset, uint32_t romentry) +{ + if (!ops->rom_table_entry) + return retval; + + int retval1 = ops->rom_table_entry(retval, depth, offset, romentry, ops->priv); + if (retval != ERROR_OK) + return retval; + return retval1; +} + /* Broken ROM tables can have circular references. Stop after a while */ #define ROM_TABLE_MAX_DEPTH (16) -/* TODO: these prototypes will be removed in a following patch */ -static int dap_info_mem_ap_header(struct command_invocation *cmd, - int retval, struct adiv5_ap *ap, - target_addr_t dbgbase, uint32_t apid); -static int dap_info_cs_component(struct command_invocation *cmd, - int retval, struct cs_component_vals *v, int depth); -static int dap_info_rom_table_entry(struct command_invocation *cmd, - int retval, int depth, - unsigned int offset, uint32_t romentry); - -static int rtp_cs_component(struct command_invocation *cmd, +static int rtp_cs_component(const struct rtp_ops *ops, struct adiv5_ap *ap, target_addr_t dbgbase, int depth); -static int rtp_rom_loop(struct command_invocation *cmd, +static int rtp_rom_loop(const struct rtp_ops *ops, struct adiv5_ap *ap, target_addr_t base_address, int depth, unsigned int max_entries) { @@ -1514,7 +1595,7 @@ static int rtp_rom_loop(struct command_invocation *cmd, if (retval != ERROR_OK) LOG_DEBUG("Failed read ROM table entry"); - retval = dap_info_rom_table_entry(cmd, retval, depth, saved_offset, romentry); + retval = rtp_ops_rom_table_entry(ops, retval, depth, saved_offset, romentry); if (retval != ERROR_OK) return retval; @@ -1528,7 +1609,7 @@ static int rtp_rom_loop(struct command_invocation *cmd, /* Recurse. "romentry" is signed */ target_addr_t component_base = base_address + (int32_t)(romentry & ARM_CS_ROMENTRY_OFFSET_MASK); - retval = rtp_cs_component(cmd, ap, component_base, depth + 1); + retval = rtp_cs_component(ops, ap, component_base, depth + 1); if (retval != ERROR_OK) { /* TODO: do we need to send an ABORT before continuing? */ LOG_DEBUG("Ignore error parsing CoreSight component"); @@ -1539,7 +1620,7 @@ static int rtp_rom_loop(struct command_invocation *cmd, return ERROR_OK; } -static int rtp_cs_component(struct command_invocation *cmd, +static int rtp_cs_component(const struct rtp_ops *ops, struct adiv5_ap *ap, target_addr_t base_address, int depth) { struct cs_component_vals v; @@ -1552,7 +1633,7 @@ static int rtp_cs_component(struct command_invocation *cmd, else retval = rtp_read_cs_regs(ap, base_address, &v); - retval = dap_info_cs_component(cmd, retval, &v, depth); + retval = rtp_ops_cs_component(ops, retval, &v, depth); if (retval != ERROR_OK) return ERROR_OK; /* Don't abort recursion */ @@ -1562,7 +1643,7 @@ static int rtp_cs_component(struct command_invocation *cmd, const unsigned int class = ARM_CS_CIDR_CLASS(v.cid); if (class == ARM_CS_CLASS_0X1_ROM_TABLE) - return rtp_rom_loop(cmd, ap, base_address, depth, 960); + return rtp_rom_loop(ops, ap, base_address, depth, 960); if (class == ARM_CS_CLASS_0X9_CS_COMPONENT) { if ((v.devarch & ARM_CS_C9_DEVARCH_PRESENT) == 0) @@ -1572,15 +1653,14 @@ static int rtp_cs_component(struct command_invocation *cmd, if ((v.devarch & DEVARCH_ID_MASK) != DEVARCH_ROM_C_0X9) return ERROR_OK; - return rtp_rom_loop(cmd, ap, base_address, depth, 512); + return rtp_rom_loop(ops, ap, base_address, depth, 512); } /* Class other than 0x1 and 0x9 */ return ERROR_OK; } -int dap_info_command(struct command_invocation *cmd, - struct adiv5_ap *ap) +static int rtp_ap(const struct rtp_ops *ops, struct adiv5_ap *ap) { int retval; uint32_t apid; @@ -1589,7 +1669,7 @@ int dap_info_command(struct command_invocation *cmd, /* Now we read ROM table ID registers, ref. ARM IHI 0029B sec */ retval = dap_get_debugbase(ap, &dbgbase, &apid); - retval = dap_info_mem_ap_header(cmd, retval, ap, dbgbase, apid); + retval = rtp_ops_mem_ap_header(ops, retval, ap, dbgbase, apid); if (retval != ERROR_OK) return retval; @@ -1608,7 +1688,7 @@ int dap_info_command(struct command_invocation *cmd, invalid_entry = 0xFFFFFFFFul; if (dbgbase != invalid_entry && (dbgbase & 0x3) != 0x2) - rtp_cs_component(cmd, ap, dbgbase & 0xFFFFFFFFFFFFF000ull, 0); + rtp_cs_component(ops, ap, dbgbase & 0xFFFFFFFFFFFFF000ull, 0); } return ERROR_OK; @@ -1616,10 +1696,10 @@ int dap_info_command(struct command_invocation *cmd, /* Actions for command "dap info" */ -static int dap_info_mem_ap_header(struct command_invocation *cmd, - int retval, struct adiv5_ap *ap, - target_addr_t dbgbase, uint32_t apid) +static int dap_info_mem_ap_header(int retval, struct adiv5_ap *ap, + target_addr_t dbgbase, uint32_t apid, void *priv) { + struct command_invocation *cmd = priv; target_addr_t invalid_entry; if (retval != ERROR_OK) { @@ -1661,9 +1741,10 @@ static int dap_info_mem_ap_header(struct command_invocation *cmd, return ERROR_OK; } -static int dap_info_cs_component(struct command_invocation *cmd, - int retval, struct cs_component_vals *v, int depth) +static int dap_info_cs_component(int retval, struct cs_component_vals *v, int depth, void *priv) { + struct command_invocation *cmd = priv; + if (depth > ROM_TABLE_MAX_DEPTH) { command_print(cmd, "\tTables too deep"); return ERROR_FAIL; @@ -1745,10 +1826,10 @@ static int dap_info_cs_component(struct command_invocation *cmd, return ERROR_OK; } -static int dap_info_rom_table_entry(struct command_invocation *cmd, - int retval, int depth, - unsigned int offset, uint32_t romentry) +static int dap_info_rom_table_entry(int retval, int depth, + unsigned int offset, uint32_t romentry, void *priv) { + struct command_invocation *cmd = priv; char tabs[16] = ""; if (depth) @@ -1777,6 +1858,18 @@ static int dap_info_rom_table_entry(struct command_invocation *cmd, return ERROR_OK; } +int dap_info_command(struct command_invocation *cmd, struct adiv5_ap *ap) +{ + struct rtp_ops dap_info_ops = { + .mem_ap_header = dap_info_mem_ap_header, + .cs_component = dap_info_cs_component, + .rom_table_entry = dap_info_rom_table_entry, + .priv = cmd, + }; + + return rtp_ap(&dap_info_ops, ap); +} + enum adiv5_cfg_param { CFG_DAP, CFG_AP_NUM, From 613f1c6abbc92aff22ef1731469626759f91ebaa Mon Sep 17 00:00:00 2001 From: Antonio Borneo Date: Sat, 15 Jan 2022 18:34:21 +0100 Subject: [PATCH 20/60] arm_adi_v5: let dap_lookup_cs_component() to get AP dbgbase Simplify the code in cortex_a and aarch64 by moving the call to dap_get_debugbase() inside dap_lookup_cs_component(). This has the further effects: - dap_get_debugbase() is not referenced outside arm_adi_v5.c and becomes static; - dap_lookup_cs_component() looses one parameter; - the coreid parameter 'idx' is passed as value; - the caller in aarch64 don't have and don't print the irrelevant value of AP register APID; - fixes the debug message in the caller in aarch64 to print the coreid value instead of always zero. Change-Id: Ic7f0f643fdf067c059c8f2455a02ff18a3fed054 Signed-off-by: Antonio Borneo Reviewed-on: https://review.openocd.org/c/openocd/+/6823 Tested-by: jenkins --- src/target/aarch64.c | 15 ++++----------- src/target/arm_adi_v5.c | 20 +++++++++++++++++--- src/target/arm_adi_v5.h | 6 +----- src/target/cortex_a.c | 11 ++--------- 4 files changed, 24 insertions(+), 28 deletions(-) diff --git a/src/target/aarch64.c b/src/target/aarch64.c index 8838da927..d8a9664d7 100644 --- a/src/target/aarch64.c +++ b/src/target/aarch64.c @@ -2574,20 +2574,13 @@ static int aarch64_examine_first(struct target *target) armv8->debug_ap->memaccess_tck = 10; if (!target->dbgbase_set) { - target_addr_t dbgbase; - /* Get ROM Table base */ - uint32_t apid; - int32_t coreidx = target->coreid; - retval = dap_get_debugbase(armv8->debug_ap, &dbgbase, &apid); - if (retval != ERROR_OK) - return retval; /* Lookup Processor DAP */ - retval = dap_lookup_cs_component(armv8->debug_ap, dbgbase, ARM_CS_C9_DEVTYPE_CORE_DEBUG, - &armv8->debug_base, &coreidx); + retval = dap_lookup_cs_component(armv8->debug_ap, ARM_CS_C9_DEVTYPE_CORE_DEBUG, + &armv8->debug_base, target->coreid); if (retval != ERROR_OK) return retval; - LOG_DEBUG("Detected core %" PRId32 " dbgbase: " TARGET_ADDR_FMT - " apid: %08" PRIx32, coreidx, armv8->debug_base, apid); + LOG_DEBUG("Detected core %" PRId32 " dbgbase: " TARGET_ADDR_FMT, + target->coreid, armv8->debug_base); } else armv8->debug_base = target->dbgbase; diff --git a/src/target/arm_adi_v5.c b/src/target/arm_adi_v5.c index 54d04abd6..d9544e9eb 100644 --- a/src/target/arm_adi_v5.c +++ b/src/target/arm_adi_v5.c @@ -1002,7 +1002,7 @@ int dap_find_ap(struct adiv5_dap *dap, enum ap_type type_to_find, struct adiv5_a return ERROR_FAIL; } -int dap_get_debugbase(struct adiv5_ap *ap, +static int dap_get_debugbase(struct adiv5_ap *ap, target_addr_t *dbgbase, uint32_t *apid) { struct adiv5_dap *dap = ap->dap; @@ -1038,7 +1038,7 @@ int dap_get_debugbase(struct adiv5_ap *ap, return ERROR_OK; } -int dap_lookup_cs_component(struct adiv5_ap *ap, +static int _dap_lookup_cs_component(struct adiv5_ap *ap, target_addr_t dbgbase, uint8_t type, target_addr_t *addr, int32_t *idx) { uint32_t romentry, entry_offset = 0, devtype; @@ -1066,7 +1066,7 @@ int dap_lookup_cs_component(struct adiv5_ap *ap, } unsigned int class = (c_cid1 & ARM_CS_CIDR1_CLASS_MASK) >> ARM_CS_CIDR1_CLASS_SHIFT; if (class == ARM_CS_CLASS_0X1_ROM_TABLE) { - retval = dap_lookup_cs_component(ap, component_base, + retval = _dap_lookup_cs_component(ap, component_base, type, addr, idx); if (retval == ERROR_OK) break; @@ -1094,6 +1094,20 @@ int dap_lookup_cs_component(struct adiv5_ap *ap, return ERROR_OK; } +int dap_lookup_cs_component(struct adiv5_ap *ap, uint8_t type, + target_addr_t *addr, int32_t core_id) +{ + int32_t idx = core_id; + target_addr_t dbgbase; + uint32_t apid; + + int retval = dap_get_debugbase(ap, &dbgbase, &apid); + if (retval != ERROR_OK) + return retval; + + return _dap_lookup_cs_component(ap, dbgbase, type, addr, &idx); +} + /** Holds registers and coordinates of a CoreSight component */ struct cs_component_vals { struct adiv5_ap *ap; diff --git a/src/target/arm_adi_v5.h b/src/target/arm_adi_v5.h index 5c598f16e..8c9a60f25 100644 --- a/src/target/arm_adi_v5.h +++ b/src/target/arm_adi_v5.h @@ -619,10 +619,6 @@ int mem_ap_init(struct adiv5_ap *ap); /* Invalidate cached DP select and cached TAR and CSW of all APs */ void dap_invalidate_cache(struct adiv5_dap *dap); -/* Probe the AP for ROM Table location */ -int dap_get_debugbase(struct adiv5_ap *ap, - target_addr_t *dbgbase, uint32_t *apid); - /* Probe Access Ports to find a particular type */ int dap_find_ap(struct adiv5_dap *dap, enum ap_type type_to_find, @@ -641,7 +637,7 @@ static inline bool dap_is_multidrop(struct adiv5_dap *dap) /* Lookup CoreSight component */ int dap_lookup_cs_component(struct adiv5_ap *ap, - target_addr_t dbgbase, uint8_t type, target_addr_t *addr, int32_t *idx); + uint8_t type, target_addr_t *addr, int32_t idx); struct target; diff --git a/src/target/cortex_a.c b/src/target/cortex_a.c index 2dc109108..20b2e5173 100644 --- a/src/target/cortex_a.c +++ b/src/target/cortex_a.c @@ -2905,18 +2905,11 @@ static int cortex_a_examine_first(struct target *target) armv7a->debug_ap->memaccess_tck = 80; if (!target->dbgbase_set) { - target_addr_t dbgbase; - /* Get ROM Table base */ - uint32_t apid; - int32_t coreidx = target->coreid; LOG_DEBUG("%s's dbgbase is not set, trying to detect using the ROM table", target->cmd_name); - retval = dap_get_debugbase(armv7a->debug_ap, &dbgbase, &apid); - if (retval != ERROR_OK) - return retval; /* Lookup Processor DAP */ - retval = dap_lookup_cs_component(armv7a->debug_ap, dbgbase, ARM_CS_C9_DEVTYPE_CORE_DEBUG, - &armv7a->debug_base, &coreidx); + retval = dap_lookup_cs_component(armv7a->debug_ap, ARM_CS_C9_DEVTYPE_CORE_DEBUG, + &armv7a->debug_base, target->coreid); if (retval != ERROR_OK) { LOG_ERROR("Can't detect %s's dbgbase from the ROM table; you need to specify it explicitly.", target->cmd_name); From d74f11dcd43914702ea7b011c4bbb7da8d4a2f5c Mon Sep 17 00:00:00 2001 From: Antonio Borneo Date: Sat, 15 Jan 2022 21:01:37 +0100 Subject: [PATCH 21/60] arm_adi_v5: replace dap_lookup_cs_component() With the generic function for ROM table walk-through, reimplement dap_lookup_cs_component(). Catch the code CORESIGHT_COMPONENT_FOUND and halt the search. While there, drop two macros in arm_coresight.h, now unused. Change-Id: I589ef7ae8a651d0c422ce7d0b4ed913713a8217e Signed-off-by: Antonio Borneo Reviewed-on: https://review.openocd.org/c/openocd/+/6824 Tested-by: jenkins --- src/target/arm_adi_v5.c | 158 ++++++++++++++++++++----------------- src/target/arm_coresight.h | 3 - 2 files changed, 85 insertions(+), 76 deletions(-) diff --git a/src/target/arm_adi_v5.c b/src/target/arm_adi_v5.c index d9544e9eb..789fe5a44 100644 --- a/src/target/arm_adi_v5.c +++ b/src/target/arm_adi_v5.c @@ -1038,76 +1038,6 @@ static int dap_get_debugbase(struct adiv5_ap *ap, return ERROR_OK; } -static int _dap_lookup_cs_component(struct adiv5_ap *ap, - target_addr_t dbgbase, uint8_t type, target_addr_t *addr, int32_t *idx) -{ - uint32_t romentry, entry_offset = 0, devtype; - target_addr_t component_base; - int retval; - - dbgbase &= 0xFFFFFFFFFFFFF000ull; - *addr = 0; - - do { - retval = mem_ap_read_atomic_u32(ap, dbgbase | - entry_offset, &romentry); - if (retval != ERROR_OK) - return retval; - - component_base = dbgbase + (target_addr_t)(romentry & ARM_CS_ROMENTRY_OFFSET_MASK); - - if (romentry & ARM_CS_ROMENTRY_PRESENT) { - uint32_t c_cid1; - retval = mem_ap_read_atomic_u32(ap, component_base + ARM_CS_CIDR1, &c_cid1); - if (retval != ERROR_OK) { - LOG_ERROR("Can't read component with base address " TARGET_ADDR_FMT - ", the corresponding core might be turned off", component_base); - return retval; - } - unsigned int class = (c_cid1 & ARM_CS_CIDR1_CLASS_MASK) >> ARM_CS_CIDR1_CLASS_SHIFT; - if (class == ARM_CS_CLASS_0X1_ROM_TABLE) { - retval = _dap_lookup_cs_component(ap, component_base, - type, addr, idx); - if (retval == ERROR_OK) - break; - if (retval != ERROR_TARGET_RESOURCE_NOT_AVAILABLE) - return retval; - } - - retval = mem_ap_read_atomic_u32(ap, component_base + ARM_CS_C9_DEVTYPE, &devtype); - if (retval != ERROR_OK) - return retval; - if ((devtype & ARM_CS_C9_DEVTYPE_MASK) == type) { - if (!*idx) { - *addr = component_base; - break; - } else - (*idx)--; - } - } - entry_offset += 4; - } while ((romentry > 0) && (entry_offset < 0xf00)); - - if (!*addr) - return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; - - return ERROR_OK; -} - -int dap_lookup_cs_component(struct adiv5_ap *ap, uint8_t type, - target_addr_t *addr, int32_t core_id) -{ - int32_t idx = core_id; - target_addr_t dbgbase; - uint32_t apid; - - int retval = dap_get_debugbase(ap, &dbgbase, &apid); - if (retval != ERROR_OK) - return retval; - - return _dap_lookup_cs_component(ap, dbgbase, type, addr, &idx); -} - /** Holds registers and coordinates of a CoreSight component */ struct cs_component_vals { struct adiv5_ap *ap; @@ -1590,6 +1520,14 @@ static int rtp_ops_rom_table_entry(const struct rtp_ops *ops, /* Broken ROM tables can have circular references. Stop after a while */ #define ROM_TABLE_MAX_DEPTH (16) +/** + * Value used only during lookup of a CoreSight component in ROM table. + * Return CORESIGHT_COMPONENT_FOUND when component is found. + * Return ERROR_OK when component is not found yet. + * Return any other ERROR_* in case of error. + */ +#define CORESIGHT_COMPONENT_FOUND (1) + static int rtp_cs_component(const struct rtp_ops *ops, struct adiv5_ap *ap, target_addr_t dbgbase, int depth); @@ -1624,6 +1562,8 @@ static int rtp_rom_loop(const struct rtp_ops *ops, /* Recurse. "romentry" is signed */ target_addr_t component_base = base_address + (int32_t)(romentry & ARM_CS_ROMENTRY_OFFSET_MASK); retval = rtp_cs_component(ops, ap, component_base, depth + 1); + if (retval == CORESIGHT_COMPONENT_FOUND) + return CORESIGHT_COMPONENT_FOUND; if (retval != ERROR_OK) { /* TODO: do we need to send an ABORT before continuing? */ LOG_DEBUG("Ignore error parsing CoreSight component"); @@ -1648,6 +1588,8 @@ static int rtp_cs_component(const struct rtp_ops *ops, retval = rtp_read_cs_regs(ap, base_address, &v); retval = rtp_ops_cs_component(ops, retval, &v, depth); + if (retval == CORESIGHT_COMPONENT_FOUND) + return CORESIGHT_COMPONENT_FOUND; if (retval != ERROR_OK) return ERROR_OK; /* Don't abort recursion */ @@ -1678,7 +1620,7 @@ static int rtp_ap(const struct rtp_ops *ops, struct adiv5_ap *ap) { int retval; uint32_t apid; - target_addr_t dbgbase; + target_addr_t dbgbase = 0; /* GCC complains can be used uninitialized */ target_addr_t invalid_entry; /* Now we read ROM table ID registers, ref. ARM IHI 0029B sec */ @@ -1701,8 +1643,11 @@ static int rtp_ap(const struct rtp_ops *ops, struct adiv5_ap *ap) else invalid_entry = 0xFFFFFFFFul; - if (dbgbase != invalid_entry && (dbgbase & 0x3) != 0x2) - rtp_cs_component(ops, ap, dbgbase & 0xFFFFFFFFFFFFF000ull, 0); + if (dbgbase != invalid_entry && (dbgbase & 0x3) != 0x2) { + retval = rtp_cs_component(ops, ap, dbgbase & 0xFFFFFFFFFFFFF000ull, 0); + if (retval == CORESIGHT_COMPONENT_FOUND) + return CORESIGHT_COMPONENT_FOUND; + } } return ERROR_OK; @@ -1884,6 +1829,73 @@ int dap_info_command(struct command_invocation *cmd, struct adiv5_ap *ap) return rtp_ap(&dap_info_ops, ap); } +/* Actions for dap_lookup_cs_component() */ + +struct dap_lookup_data { + /* input */ + unsigned int idx; + unsigned int type; + /* output */ + uint64_t component_base; +}; + +static int dap_lookup_cs_component_cs_component(int retval, + struct cs_component_vals *v, int depth, void *priv) +{ + struct dap_lookup_data *lookup = priv; + + if (retval != ERROR_OK) + return retval; + + if (!is_valid_arm_cs_cidr(v->cid)) + return ERROR_OK; + + const unsigned int class = ARM_CS_CIDR_CLASS(v->cid); + if (class != ARM_CS_CLASS_0X9_CS_COMPONENT) + return ERROR_OK; + + if ((v->devtype_memtype & ARM_CS_C9_DEVTYPE_MASK) != lookup->type) + return ERROR_OK; + + if (lookup->idx) { + /* search for next one */ + --lookup->idx; + return ERROR_OK; + } + + /* Found! */ + lookup->component_base = v->component_base; + return CORESIGHT_COMPONENT_FOUND; +} + +int dap_lookup_cs_component(struct adiv5_ap *ap, uint8_t type, + target_addr_t *addr, int32_t core_id) +{ + struct dap_lookup_data lookup = { + .type = type, + .idx = core_id, + }; + struct rtp_ops dap_lookup_cs_component_ops = { + .mem_ap_header = NULL, + .cs_component = dap_lookup_cs_component_cs_component, + .rom_table_entry = NULL, + .priv = &lookup, + }; + + int retval = rtp_ap(&dap_lookup_cs_component_ops, ap); + if (retval == CORESIGHT_COMPONENT_FOUND) { + LOG_DEBUG("CS lookup found at 0x%" PRIx64, lookup.component_base); + *addr = lookup.component_base; + return ERROR_OK; + } + if (retval != ERROR_OK) { + LOG_DEBUG("CS lookup error %d", retval); + return retval; + } + LOG_DEBUG("CS lookup not found"); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; +} + enum adiv5_cfg_param { CFG_DAP, CFG_AP_NUM, diff --git a/src/target/arm_coresight.h b/src/target/arm_coresight.h index ff4c25ad7..58139dcdb 100644 --- a/src/target/arm_coresight.h +++ b/src/target/arm_coresight.h @@ -48,9 +48,6 @@ #define ARM_CS_CLASS_0X1_ROM_TABLE (0x1) #define ARM_CS_CLASS_0X9_CS_COMPONENT (0x9) -#define ARM_CS_CIDR1_CLASS_MASK (0x000000F0) -#define ARM_CS_CIDR1_CLASS_SHIFT (4) - static inline bool is_valid_arm_cs_cidr(uint32_t cidr) { return (cidr & ~ARM_CS_CIDR_CLASS_MASK) == 0xB105000D; From a47d1f1b795e037fafd16667d0056364159c7623 Mon Sep 17 00:00:00 2001 From: Antonio Borneo Date: Sat, 15 Jan 2022 21:31:41 +0100 Subject: [PATCH 22/60] arm_adi_v5: add support for 64bit Class 0x9 ROM tables Arm documentation does not explicitly report the order of the two 32bit words that compose the 64bit value. But both ADIv5 and ADIv6 specify that only little-endian is supported (ADIv5.2 obsoletes the big-endian support). This change reads the 64bit value in little-endian. Detect the 64bit content and use it. Change-Id: I723ec099c7e8c70c1f9a568e32ea867fcbf1f1db Signed-off-by: Antonio Borneo Reviewed-on: https://review.openocd.org/c/openocd/+/6465 Tested-by: jenkins --- src/target/arm_adi_v5.c | 43 ++++++++++++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/src/target/arm_adi_v5.c b/src/target/arm_adi_v5.c index 789fe5a44..24de30530 100644 --- a/src/target/arm_adi_v5.c +++ b/src/target/arm_adi_v5.c @@ -1461,7 +1461,7 @@ struct rtp_ops { * @param priv Pointer to private data. * @return ERROR_OK on success, else a fault code. */ - int (*rom_table_entry)(int retval, int depth, unsigned int offset, uint32_t romentry, + int (*rom_table_entry)(int retval, int depth, unsigned int offset, uint64_t romentry, void *priv); /** * Private data @@ -1506,7 +1506,7 @@ static int rtp_ops_cs_component(const struct rtp_ops *ops, * Input parameter @a retval is propagated. */ static int rtp_ops_rom_table_entry(const struct rtp_ops *ops, - int retval, int depth, unsigned int offset, uint32_t romentry) + int retval, int depth, unsigned int offset, uint64_t romentry) { if (!ops->rom_table_entry) return retval; @@ -1533,20 +1533,39 @@ static int rtp_cs_component(const struct rtp_ops *ops, static int rtp_rom_loop(const struct rtp_ops *ops, struct adiv5_ap *ap, target_addr_t base_address, int depth, - unsigned int max_entries) + unsigned int width, unsigned int max_entries) { assert(IS_ALIGNED(base_address, ARM_CS_ALIGN)); unsigned int offset = 0; while (max_entries--) { - uint32_t romentry; + uint64_t romentry; + uint32_t romentry_low, romentry_high; + target_addr_t component_base; unsigned int saved_offset = offset; - int retval = mem_ap_read_atomic_u32(ap, base_address + offset, &romentry); + int retval = mem_ap_read_u32(ap, base_address + offset, &romentry_low); offset += 4; + if (retval == ERROR_OK && width == 64) { + retval = mem_ap_read_u32(ap, base_address + offset, &romentry_high); + offset += 4; + } + if (retval == ERROR_OK) + retval = dap_run(ap->dap); if (retval != ERROR_OK) LOG_DEBUG("Failed read ROM table entry"); + if (width == 64) { + romentry = (((uint64_t)romentry_high) << 32) | romentry_low; + component_base = base_address + + ((((uint64_t)romentry_high) << 32) | (romentry_low & ARM_CS_ROMENTRY_OFFSET_MASK)); + } else { + romentry = romentry_low; + /* "romentry" is signed */ + component_base = base_address + (int32_t)(romentry_low & ARM_CS_ROMENTRY_OFFSET_MASK); + if (!is_64bit_ap(ap)) + component_base = (uint32_t)component_base; + } retval = rtp_ops_rom_table_entry(ops, retval, depth, saved_offset, romentry); if (retval != ERROR_OK) return retval; @@ -1559,8 +1578,7 @@ static int rtp_rom_loop(const struct rtp_ops *ops, if (!(romentry & ARM_CS_ROMENTRY_PRESENT)) continue; - /* Recurse. "romentry" is signed */ - target_addr_t component_base = base_address + (int32_t)(romentry & ARM_CS_ROMENTRY_OFFSET_MASK); + /* Recurse */ retval = rtp_cs_component(ops, ap, component_base, depth + 1); if (retval == CORESIGHT_COMPONENT_FOUND) return CORESIGHT_COMPONENT_FOUND; @@ -1599,7 +1617,7 @@ static int rtp_cs_component(const struct rtp_ops *ops, const unsigned int class = ARM_CS_CIDR_CLASS(v.cid); if (class == ARM_CS_CLASS_0X1_ROM_TABLE) - return rtp_rom_loop(ops, ap, base_address, depth, 960); + return rtp_rom_loop(ops, ap, base_address, depth, 32, 960); if (class == ARM_CS_CLASS_0X9_CS_COMPONENT) { if ((v.devarch & ARM_CS_C9_DEVARCH_PRESENT) == 0) @@ -1609,7 +1627,10 @@ static int rtp_cs_component(const struct rtp_ops *ops, if ((v.devarch & DEVARCH_ID_MASK) != DEVARCH_ROM_C_0X9) return ERROR_OK; - return rtp_rom_loop(ops, ap, base_address, depth, 512); + if ((v.devid & ARM_CS_C9_DEVID_FORMAT_MASK) == ARM_CS_C9_DEVID_FORMAT_64BIT) + return rtp_rom_loop(ops, ap, base_address, depth, 64, 256); + else + return rtp_rom_loop(ops, ap, base_address, depth, 32, 512); } /* Class other than 0x1 and 0x9 */ @@ -1786,7 +1807,7 @@ static int dap_info_cs_component(int retval, struct cs_component_vals *v, int de } static int dap_info_rom_table_entry(int retval, int depth, - unsigned int offset, uint32_t romentry, void *priv) + unsigned int offset, uint64_t romentry, void *priv) { struct command_invocation *cmd = priv; char tabs[16] = ""; @@ -1801,7 +1822,7 @@ static int dap_info_rom_table_entry(int retval, int depth, return retval; } - command_print(cmd, "\t%sROMTABLE[0x%x] = 0x%08" PRIx32, + command_print(cmd, "\t%sROMTABLE[0x%x] = 0x%08" PRIx64, tabs, offset, romentry); if (romentry == 0) { From 99293ebd15bd2980fa4cb9e161f0069092741a55 Mon Sep 17 00:00:00 2001 From: Antonio Borneo Date: Mon, 25 Apr 2022 22:25:08 +0200 Subject: [PATCH 23/60] aarch64: don't wait for smp targets halted in deassert reset The function target_type::deassert_reset() is called for every target after reset is deasserted. If the target fails to get halted, we log a warning and issue a halt request for the target itself. Current code calls the generic target_halt() that: - extends the halt to all the targets in the SMP node; - waits for targets to halt. This breaks the logic of running target_type::deassert_reset() per target. Plus, waiting for targets to halt delays the call of target_type::deassert_reset() for the next targets. Replace the call to target_halt() with the aarch64 specific function to halt the single target. Pass the parameter HALT_LAZY to prevent the wait for target halted. Similar solution is already implemented for cortex_a. Change-Id: I446dc03cb91524c6d388db485bc2388177af77b6 Signed-off-by: Antonio Borneo Reviewed-on: https://review.openocd.org/c/openocd/+/6947 Tested-by: jenkins --- src/target/aarch64.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/target/aarch64.c b/src/target/aarch64.c index d8a9664d7..ecd93248c 100644 --- a/src/target/aarch64.c +++ b/src/target/aarch64.c @@ -2026,9 +2026,13 @@ static int aarch64_deassert_reset(struct target *target) if (target->state != TARGET_HALTED) { LOG_WARNING("%s: ran after reset and before halt ...", target_name(target)); - retval = target_halt(target); - if (retval != ERROR_OK) - return retval; + if (target_was_examined(target)) { + retval = aarch64_halt_one(target, HALT_LAZY); + if (retval != ERROR_OK) + return retval; + } else { + target->state = TARGET_UNKNOWN; + } } } From cc75aa37c5ba6267956a3613069b7d35420b76fc Mon Sep 17 00:00:00 2001 From: Antonio Borneo Date: Fri, 18 Feb 2022 17:25:58 +0100 Subject: [PATCH 24/60] openocd: add post-init and pre-shutdown helpers It is a common requirement to automatically execute some command after "init". This can be achieved, either in scripts or through OpenOCD command line, by explicitly calling "init" followed by the commands. But this approach fails if the request for post-init commands is spread across configuration files; only one of the files can split pre-init and post-init status by calling "init". The common workaround is to "rename" the command "init" and replace it with a TCL proc that calls the original "init" and the post-init commands. E.g. in Zephyr script [1]. To simplify and formalize the post-init execution, use a TCL list that contains the list of commands to be executed. Every script can contribute adding new commands, e.g. using "lappend". In the same way, formalize the pre-shutdown execution with a TCL list of user commands to be executed before OpenOCD exit. Document them and add trivial examples. Drop from documentation the suggestion to rename "shutdown". Change-Id: I9464fb40ccede3e7760d425873adca363b49a64f Signed-off-by: Antonio Borneo Link: [1] https://github.com/zephyrproject-rtos/zephyr/blob/zephyr-v2.7.1/boards/arm/nucleo_h743zi/support/openocd.cfg#L15 Reviewed-on: https://review.openocd.org/c/openocd/+/6851 Reviewed-by: Tomas Vanek Tested-by: jenkins --- doc/openocd.texi | 30 ++++++++++++++++++++---------- src/helper/startup.tcl | 22 ++++++++++++++++++++++ src/openocd.c | 3 +++ src/server/server.c | 2 ++ 4 files changed, 47 insertions(+), 10 deletions(-) diff --git a/doc/openocd.texi b/doc/openocd.texi index b87e87f87..9b78fee80 100644 --- a/doc/openocd.texi +++ b/doc/openocd.texi @@ -2120,6 +2120,15 @@ corresponding subsystems: @deffnx {Config Command} {pld init} @deffnx {Command} {tpiu init} @end deffn + +At last, @command{init} executes all the commands that are specified in +the TCL list @var{post_init_commands}. The commands are executed in the +same order they occupy in the list. If one of the commands fails, then +the error is propagated and OpenOCD fails too. +@example +lappend post_init_commands @{echo "OpenOCD successfully initialized."@} +lappend post_init_commands @{echo "Have fun with OpenOCD !"@} +@end example @end deffn @deffn {Config Command} {noinit} @@ -8433,18 +8442,19 @@ Close the OpenOCD server, disconnecting all clients (GDB, telnet, other). If option @option{error} is used, OpenOCD will return a non-zero exit code to the parent process. -Like any TCL commands, also @command{shutdown} can be redefined, e.g.: +If user types CTRL-C or kills OpenOCD, the command @command{shutdown} +will be automatically executed to cause OpenOCD to exit. + +It is possible to specify, in the TCL list @var{pre_shutdown_commands} , a +set of commands to be automatically executed before @command{shutdown} , e.g.: @example -# redefine shutdown -rename shutdown original_shutdown -proc shutdown @{@} @{ - puts "This is my implementation of shutdown" - # my own stuff before exit OpenOCD - original_shutdown -@} +lappend pre_shutdown_commands @{echo "Goodbye, my friend ..."@} +lappend pre_shutdown_commands @{echo "see you soon !"@} @end example -If user types CTRL-C or kills OpenOCD, either the command @command{shutdown} -or its replacement will be automatically executed before OpenOCD exits. +The commands in the list will be executed (in the same order they occupy +in the list) before OpenOCD exits. If one of the commands in the list +fails, then the remaining commands are not executed anymore while OpenOCD +will proceed to quit. @end deffn @anchor{debuglevel} diff --git a/src/helper/startup.tcl b/src/helper/startup.tcl index 71f489dd5..6389262a3 100644 --- a/src/helper/startup.tcl +++ b/src/helper/startup.tcl @@ -28,4 +28,26 @@ proc script {filename} { add_help_text script "filename of OpenOCD script (tcl) to run" add_usage_text script "" +# Run a list of post-init commands +# Each command should be added with 'lappend post_init_commands command' +lappend _telnet_autocomplete_skip _run_post_init_commands +proc _run_post_init_commands {} { + if {[info exists ::post_init_commands]} { + foreach cmd $::post_init_commands { + eval $cmd + } + } +} + +# Run a list of pre-shutdown commands +# Each command should be added with 'lappend pre_shutdown_commands command' +lappend _telnet_autocomplete_skip _run_pre_shutdown_commands +proc _run_pre_shutdown_commands {} { + if {[info exists ::pre_shutdown_commands]} { + foreach cmd $::pre_shutdown_commands { + eval $cmd + } + } +} + ######### diff --git a/src/openocd.c b/src/openocd.c index 3c96d3214..fdc4a874b 100644 --- a/src/openocd.c +++ b/src/openocd.c @@ -182,6 +182,9 @@ COMMAND_HANDLER(handle_init_command) target_register_event_callback(log_target_callback_event_handler, CMD_CTX); + if (command_run_line(CMD_CTX, "_run_post_init_commands") != ERROR_OK) + return ERROR_FAIL; + return ERROR_OK; } diff --git a/src/server/server.c b/src/server/server.c index dd408048c..c7cafb789 100644 --- a/src/server/server.c +++ b/src/server/server.c @@ -762,6 +762,8 @@ COMMAND_HANDLER(handle_shutdown_command) shutdown_openocd = SHUTDOWN_REQUESTED; + command_run_line(CMD_CTX, "_run_pre_shutdown_commands"); + if (CMD_ARGC == 1) { if (!strcmp(CMD_ARGV[0], "error")) { shutdown_openocd = SHUTDOWN_WITH_ERROR_CODE; From 0b241ca042b1376efdcaacc52a798a20a0d9fc2a Mon Sep 17 00:00:00 2001 From: Steve Marple Date: Tue, 19 Apr 2022 23:22:31 +0100 Subject: [PATCH 25/60] bcm2835gpio: Fix incorrect GPIO validation Incorrect validation prevented GPIO0 from controlling the direction of the SWDIO buffer or operating TRST/SRST. Have all GPIO number validation checks performed by is_gpio_valid(). Change-Id: Ib8fb704ab588a618ac41c111f6168d658891d92c Signed-off-by: Steve Marple Reviewed-on: https://review.openocd.org/c/openocd/+/6938 Tested-by: jenkins Reviewed-by: Antonio Borneo Reviewed-by: Andreas Fritiofson --- src/jtag/drivers/bcm2835gpio.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/jtag/drivers/bcm2835gpio.c b/src/jtag/drivers/bcm2835gpio.c index b7a4d998c..22d237f22 100644 --- a/src/jtag/drivers/bcm2835gpio.c +++ b/src/jtag/drivers/bcm2835gpio.c @@ -94,6 +94,11 @@ static int speed_coeff = 113714; static int speed_offset = 28; static unsigned int jtag_delay; +static int is_gpio_valid(int gpio) +{ + return gpio >= 0 && gpio <= 31; +} + static bb_value_t bcm2835gpio_read(void) { return (GPIO_LEV & 1< 0) { + if (is_gpio_valid(trst_gpio)) { set |= !trst< 0) { + if (is_gpio_valid(srst_gpio)) { set |= !srst< 0) { + if (is_gpio_valid(swdio_dir_gpio)) { if (is_output) { GPIO_SET = 1 << swdio_dir_gpio; OUT_GPIO(swdio_gpio); @@ -196,11 +201,6 @@ static int bcm2835gpio_speed(int speed) return ERROR_OK; } -static int is_gpio_valid(int gpio) -{ - return gpio >= 0 && gpio <= 31; -} - COMMAND_HANDLER(bcm2835gpio_handle_jtag_gpionums) { if (CMD_ARGC == 4) { @@ -557,7 +557,7 @@ static int bcm2835gpio_init(void) OUT_GPIO(tck_gpio); OUT_GPIO(tms_gpio); - if (trst_gpio != -1) { + if (is_gpio_valid(trst_gpio)) { trst_gpio_mode = MODE_GPIO(trst_gpio); GPIO_SET = 1 << trst_gpio; OUT_GPIO(trst_gpio); @@ -566,7 +566,7 @@ static int bcm2835gpio_init(void) if (transport_is_swd()) { /* Make buffer an output before the GPIO connected to it */ - if (swdio_dir_gpio != -1) { + if (is_gpio_valid(swdio_dir_gpio)) { swdio_dir_gpio_mode = MODE_GPIO(swdio_dir_gpio); GPIO_SET = 1 << swdio_dir_gpio; OUT_GPIO(swdio_dir_gpio); @@ -581,7 +581,7 @@ static int bcm2835gpio_init(void) OUT_GPIO(swdio_gpio); } - if (srst_gpio != -1) { + if (is_gpio_valid(srst_gpio)) { srst_gpio_mode = MODE_GPIO(srst_gpio); GPIO_SET = 1 << srst_gpio; OUT_GPIO(srst_gpio); @@ -601,7 +601,7 @@ static int bcm2835gpio_quit(void) SET_MODE_GPIO(tdi_gpio, tdi_gpio_mode); SET_MODE_GPIO(tck_gpio, tck_gpio_mode); SET_MODE_GPIO(tms_gpio, tms_gpio_mode); - if (trst_gpio != -1) + if (is_gpio_valid(trst_gpio)) SET_MODE_GPIO(trst_gpio, trst_gpio_mode); } @@ -610,10 +610,10 @@ static int bcm2835gpio_quit(void) SET_MODE_GPIO(swdio_gpio, swdio_gpio_mode); } - if (srst_gpio != -1) + if (is_gpio_valid(srst_gpio)) SET_MODE_GPIO(srst_gpio, srst_gpio_mode); - if (swdio_dir_gpio != -1) + if (is_gpio_valid(swdio_dir_gpio)) SET_MODE_GPIO(swdio_dir_gpio, swdio_dir_gpio_mode); return ERROR_OK; From bd4bd54b60b3297b746d3d5379f25d54846ce517 Mon Sep 17 00:00:00 2001 From: Steve Marple Date: Mon, 18 Apr 2022 23:27:22 +0100 Subject: [PATCH 26/60] drivers/am335xgpio: Add AM335x driver for bitbang support on BeagleBones Change-Id: Iac1c9f3d380e2474c8b77407c89c2aad96fbf2ea Signed-off-by: Steve Marple Reviewed-on: https://review.openocd.org/c/openocd/+/6941 Tested-by: jenkins Reviewed-by: Antonio Borneo --- configure.ac | 12 + doc/openocd.texi | 95 +++ src/jtag/drivers/Makefile.am | 3 + src/jtag/drivers/am335xgpio.c | 733 +++++++++++++++++++++++ src/jtag/interfaces.c | 6 + tcl/interface/beaglebone-jtag-native.cfg | 28 + tcl/interface/beaglebone-swd-native.cfg | 29 + 7 files changed, 906 insertions(+) create mode 100644 src/jtag/drivers/am335xgpio.c create mode 100644 tcl/interface/beaglebone-jtag-native.cfg create mode 100644 tcl/interface/beaglebone-swd-native.cfg diff --git a/configure.ac b/configure.ac index 7037656ae..95b2d50ae 100644 --- a/configure.ac +++ b/configure.ac @@ -298,10 +298,14 @@ AS_CASE(["${host_cpu}"], AC_ARG_ENABLE([imx_gpio], AS_HELP_STRING([--enable-imx_gpio], [Enable building support for bitbanging on NXP IMX processors]), [build_imx_gpio=$enableval], [build_imx_gpio=no]) + AC_ARG_ENABLE([am335xgpio], + AS_HELP_STRING([--enable-am335xgpio], [Enable building support for bitbanging on AM335x (as found in Beaglebones)]), + [build_am335xgpio=$enableval], [build_am335xgpio=no]) ], [ build_bcm2835gpio=no build_imx_gpio=no + build_am335xgpio=no ]) AS_CASE(["${host_cpu}"], @@ -507,6 +511,13 @@ AS_IF([test "x$build_imx_gpio" = "xyes"], [ AC_DEFINE([BUILD_IMX_GPIO], [0], [0 if you don't want imx_gpio.]) ]) +AS_IF([test "x$build_am335xgpio" = "xyes"], [ + build_bitbang=yes + AC_DEFINE([BUILD_AM335XGPIO], [1], [1 if you want am335xgpio.]) +], [ + AC_DEFINE([BUILD_AM335XGPIO], [0], [0 if you don't want am335xgpio.]) +]) + AS_IF([test "x$parport_use_ppdev" = "xyes"], [ AC_DEFINE([PARPORT_USE_PPDEV], [1], [1 if you want parport to use ppdev.]) ], [ @@ -711,6 +722,7 @@ AM_CONDITIONAL([EP93XX], [test "x$build_ep93xx" = "xyes"]) AM_CONDITIONAL([AT91RM9200], [test "x$build_at91rm9200" = "xyes"]) AM_CONDITIONAL([BCM2835GPIO], [test "x$build_bcm2835gpio" = "xyes"]) AM_CONDITIONAL([IMX_GPIO], [test "x$build_imx_gpio" = "xyes"]) +AM_CONDITIONAL([AM335XGPIO], [test "x$build_am335xgpio" = "xyes"]) AM_CONDITIONAL([BITBANG], [test "x$build_bitbang" = "xyes"]) AM_CONDITIONAL([JTAG_VPI], [test "x$build_jtag_vpi" = "xyes"]) AM_CONDITIONAL([VDEBUG], [test "x$build_vdebug" = "xyes"]) diff --git a/doc/openocd.texi b/doc/openocd.texi index 9b78fee80..9681cf536 100644 --- a/doc/openocd.texi +++ b/doc/openocd.texi @@ -584,6 +584,9 @@ produced, PDF schematics are easily found and it is easy to make. @item @b{imx_gpio} @* A NXP i.MX-based board (e.g. Wandboard) using the GPIO pins (should work on any i.MX processor). +@item @b{am335xgpio} +@* A Texas Instruments AM335x-based board (e.g. BeagleBone Black) using the GPIO pins of the expansion headers. + @item @b{jtag_vpi} @* A JTAG driver acting as a client for the JTAG VPI server interface. @* Link: @url{http://github.com/fjullien/jtag_vpi} @@ -3322,6 +3325,98 @@ pinout. @end deffn +@deffn {Interface Driver} {am335xgpio} The AM335x SoC is present in BeagleBone +Black and BeagleBone Green single-board computers which expose some of the GPIOs +on the two expansion headers. + +For maximum performance the driver accesses memory-mapped GPIO peripheral +registers directly. The memory mapping requires read and write permission to +kernel memory; if /dev/gpiomem exists it will be used, otherwise /dev/mem will +be used. The driver restores the GPIO state on exit. + +All four GPIO ports are available. GPIOs numbered 0 to 31 are mapped to GPIO port +0, GPIO numbers 32 to 63 are mapped to GPIO port 1 and so on. + +See @file{interface/beaglebone-swd-native.cfg} for a sample configuration file. + +@deffn {Config Command} {am335xgpio jtag_nums} @var{tck} @var{tms} @var{tdi} @var{tdo} +Set JTAG transport GPIO numbers for TCK, TMS, TDI, and TDO (in that order). +Must be specified to enable JTAG transport. These pins can also be specified +individually. +@end deffn + +@deffn {Config Command} {am335xgpio tck_num} @var{tck} +Set TCK GPIO number. Must be specified to enable JTAG transport. Can also be +specified using the configuration command @command{am335xgpio jtag_nums}. +@end deffn + +@deffn {Config Command} {am335xgpio tms_num} @var{tms} +Set TMS GPIO number. Must be specified to enable JTAG transport. Can also be +specified using the configuration command @command{am335xgpio jtag_nums}. +@end deffn + +@deffn {Config Command} {am335xgpio tdo_num} @var{tdo} +Set TDO GPIO number. Must be specified to enable JTAG transport. Can also be +specified using the configuration command @command{am335xgpio jtag_nums}. +@end deffn + +@deffn {Config Command} {am335xgpio tdi_num} @var{tdi} +Set TDI GPIO number. Must be specified to enable JTAG transport. Can also be +specified using the configuration command @command{am335xgpio jtag_nums}. +@end deffn + +@deffn {Config Command} {am335xgpio swd_nums} @var{swclk} @var{swdio} +Set SWD transport GPIO numbers for SWCLK and SWDIO (in that order). Must be +specified to enable SWD transport. These pins can also be specified individually. +@end deffn + +@deffn {Config Command} {am335xgpio swclk_num} @var{swclk} +Set SWCLK GPIO number. Must be specified to enable SWD transport. Can also be +specified using the configuration command @command{am335xgpio swd_nums}. +@end deffn + +@deffn {Config Command} {am335xgpio swdio_num} @var{swdio} +Set SWDIO GPIO number. Must be specified to enable SWD transport. Can also be +specified using the configuration command @command{am335xgpio swd_nums}. +@end deffn + +@deffn {Config Command} {am335xgpio swdio_dir_num} @var{swdio_dir} +Set SWDIO direction control pin GPIO number. If specified, this pin can be used +to control the direction of an external buffer on the SWDIO pin. The direction +control state can be set with the command @command{am335xgpio +swdio_dir_output_state}. If not specified this feature is disabled. +@end deffn + +@deffn {Config Command} {am335xgpio swdio_dir_output_state} @var{output_state} +Set the state required for an external SWDIO buffer to be an output. Valid +values are @option{on} (default) and @option{off}. +@end deffn + +@deffn {Config Command} {am335xgpio srst_num} @var{srst} +Set SRST GPIO number. Must be specified to enable SRST. +@end deffn + +@deffn {Config Command} {am335xgpio trst_num} @var{trst} +Set TRST GPIO number. Must be specified to enable TRST. +@end deffn + +@deffn {Config Command} {am335xgpio led_num} @var{led} +Set activity LED GPIO number. If not specified an activity LED is not enabled. +@end deffn + +@deffn {Config Command} {am335xgpio led_on_state} @var{on_state} +Set required logic level for the LED to be on. Valid values are @option{on} +(default) and @option{off}. +@end deffn + +@deffn {Config Command} {am335xgpio speed_coeffs} @var{speed_coeff} @var{speed_offset} +Set SPEED_COEFF and SPEED_OFFSET for delay calculations. If unspecified +speed_coeff defaults to 600000 and speed_offset defaults to 575. +@end deffn + +@end deffn + + @deffn {Interface Driver} {linuxgpiod} Linux provides userspace access to GPIO through libgpiod since Linux kernel version v4.6. The driver emulates either JTAG and SWD transport through bitbanging. diff --git a/src/jtag/drivers/Makefile.am b/src/jtag/drivers/Makefile.am index 887f99bcd..d05b7b976 100644 --- a/src/jtag/drivers/Makefile.am +++ b/src/jtag/drivers/Makefile.am @@ -185,6 +185,9 @@ endif if XDS110 DRIVERFILES += %D%/xds110.c endif +if AM335XGPIO +DRIVERFILES += %D%/am335xgpio.c +endif DRIVERHEADERS = \ %D%/bitbang.h \ diff --git a/src/jtag/drivers/am335xgpio.c b/src/jtag/drivers/am335xgpio.c new file mode 100644 index 000000000..e04c44c8e --- /dev/null +++ b/src/jtag/drivers/am335xgpio.c @@ -0,0 +1,733 @@ +/*************************************************************************** + * Copyright (C) 2022 by Steve Marple, stevemarple@googlemail.com * + * * + * Based on bcm2835gpio.c and linuxgpiod.c * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include "bitbang.h" + +#include + +/* + * GPIO register base addresses. Values taken from "AM335x and AMIC110 Sitara + * Processors Technical Reference Manual", Chapter 2 Memory Map. + */ +#define AM335XGPIO_NUM_GPIO_PORTS 4 +#define AM335XGPIO_GPIO0_HW_ADDR 0x44E07000 +#define AM335XGPIO_GPIO1_HW_ADDR 0x4804C000 +#define AM335XGPIO_GPIO2_HW_ADDR 0x481AC000 +#define AM335XGPIO_GPIO3_HW_ADDR 0x481AE000 + +/* 32-bit offsets from GPIO port base address. Values taken from "AM335x and + * AMIC110 Sitara Processors Technical Reference Manual", Chapter 25 + * General-Purpose Input/Output. + */ +#define AM335XGPIO_GPIO_OE_OFFSET (0x134 / 4) +#define AM335XGPIO_GPIO_DATAIN_OFFSET (0x138 / 4) +#define AM335XGPIO_GPIO_DATAOUT_OFFSET (0x13C / 4) /* DATAOUT register uses 0 for output, 1 for input */ +#define AM335XGPIO_GPIO_CLEARDATAOUT_OFFSET (0x190 / 4) +#define AM335XGPIO_GPIO_SETDATAOUT_OFFSET (0x194 / 4) + +/* GPIOs are integer values; need to map to a port module, and the pin within + * that module. GPIOs 0 to 31 map to GPIO0, 32 to 63 to GPIO1 etc. This scheme + * matches that used by Linux on the BeagleBone. + */ +#define AM335XGPIO_PORT_NUM(gpio_num) ((gpio_num) / 32) +#define AM335XGPIO_BIT_NUM(gpio_num) ((gpio_num) % 32) +#define AM335XGPIO_BIT_MASK(gpio_num) BIT(AM335XGPIO_BIT_NUM(gpio_num)) + +#define AM335XGPIO_READ_REG(gpio_num, offset) \ + (*(am335xgpio_gpio_port_mmap_addr[AM335XGPIO_PORT_NUM(gpio_num)] + (offset))) + +#define AM335XGPIO_WRITE_REG(gpio_num, offset, value) \ + (*(am335xgpio_gpio_port_mmap_addr[AM335XGPIO_PORT_NUM(gpio_num)] + (offset)) = (value)) + +#define AM335XGPIO_SET_REG_BITS(gpio_num, offset, bit_mask) \ + (*(am335xgpio_gpio_port_mmap_addr[AM335XGPIO_PORT_NUM(gpio_num)] + (offset)) |= (bit_mask)) + +#define AM335XGPIO_CLEAR_REG_BITS(gpio_num, offset, bit_mask) \ + (*(am335xgpio_gpio_port_mmap_addr[AM335XGPIO_PORT_NUM(gpio_num)] + (offset)) &= ~(bit_mask)) + +enum amx335gpio_gpio_mode { + AM335XGPIO_GPIO_MODE_INPUT, + AM335XGPIO_GPIO_MODE_OUTPUT, /* To set output mode but not state */ + AM335XGPIO_GPIO_MODE_OUTPUT_LOW, + AM335XGPIO_GPIO_MODE_OUTPUT_HIGH, +}; + +static const uint32_t am335xgpio_gpio_port_hw_addr[AM335XGPIO_NUM_GPIO_PORTS] = { + AM335XGPIO_GPIO0_HW_ADDR, + AM335XGPIO_GPIO1_HW_ADDR, + AM335XGPIO_GPIO2_HW_ADDR, + AM335XGPIO_GPIO3_HW_ADDR, +}; + +/* Memory-mapped address pointers */ +static volatile uint32_t *am335xgpio_gpio_port_mmap_addr[AM335XGPIO_NUM_GPIO_PORTS]; + +static int dev_mem_fd; + +/* GPIO numbers for each signal. Negative values are invalid */ +static int tck_gpio = -1; +static enum amx335gpio_gpio_mode tck_gpio_mode; +static int tms_gpio = -1; +static enum amx335gpio_gpio_mode tms_gpio_mode; +static int tdi_gpio = -1; +static enum amx335gpio_gpio_mode tdi_gpio_mode; +static int tdo_gpio = -1; +static enum amx335gpio_gpio_mode tdo_gpio_mode; +static int trst_gpio = -1; +static enum amx335gpio_gpio_mode trst_gpio_mode; +static int srst_gpio = -1; +static enum amx335gpio_gpio_mode srst_gpio_mode; +static int swclk_gpio = -1; +static enum amx335gpio_gpio_mode swclk_gpio_mode; +static int swdio_gpio = -1; +static enum amx335gpio_gpio_mode swdio_gpio_mode; +static int swdio_dir_gpio = -1; +static enum amx335gpio_gpio_mode swdio_dir_gpio_mode; +static int led_gpio = -1; +static enum amx335gpio_gpio_mode led_gpio_mode = -1; + +static bool swdio_dir_is_active_high = true; /* Active state means output */ +static bool led_is_active_high = true; + +/* Transition delay coefficients */ +static int speed_coeff = 600000; +static int speed_offset = 575; +static unsigned int jtag_delay; + +static int is_gpio_valid(int gpio_num) +{ + return gpio_num >= 0 && gpio_num < (32 * AM335XGPIO_NUM_GPIO_PORTS); +} + +static int get_gpio_value(int gpio_num) +{ + unsigned int shift = AM335XGPIO_BIT_NUM(gpio_num); + return (AM335XGPIO_READ_REG(gpio_num, AM335XGPIO_GPIO_DATAIN_OFFSET) >> shift) & 1; +} + +static void set_gpio_value(int gpio_num, int value) +{ + if (value) + AM335XGPIO_WRITE_REG(gpio_num, AM335XGPIO_GPIO_SETDATAOUT_OFFSET, AM335XGPIO_BIT_MASK(gpio_num)); + else + AM335XGPIO_WRITE_REG(gpio_num, AM335XGPIO_GPIO_CLEARDATAOUT_OFFSET, AM335XGPIO_BIT_MASK(gpio_num)); +} + +static enum amx335gpio_gpio_mode get_gpio_mode(int gpio_num) +{ + if (AM335XGPIO_READ_REG(gpio_num, AM335XGPIO_GPIO_OE_OFFSET) & AM335XGPIO_BIT_MASK(gpio_num)) { + return AM335XGPIO_GPIO_MODE_INPUT; + } else { + /* Return output level too so that pin mode can be fully restored */ + if (AM335XGPIO_READ_REG(gpio_num, AM335XGPIO_GPIO_DATAOUT_OFFSET) & AM335XGPIO_BIT_MASK(gpio_num)) + return AM335XGPIO_GPIO_MODE_OUTPUT_HIGH; + else + return AM335XGPIO_GPIO_MODE_OUTPUT_LOW; + } +} + +static void set_gpio_mode(int gpio_num, enum amx335gpio_gpio_mode gpio_mode) +{ + if (gpio_mode == AM335XGPIO_GPIO_MODE_INPUT) { + AM335XGPIO_SET_REG_BITS(gpio_num, AM335XGPIO_GPIO_OE_OFFSET, AM335XGPIO_BIT_MASK(gpio_num)); + return; + } + + if (gpio_mode == AM335XGPIO_GPIO_MODE_OUTPUT_LOW) + set_gpio_value(gpio_num, 0); + if (gpio_mode == AM335XGPIO_GPIO_MODE_OUTPUT_HIGH) + set_gpio_value(gpio_num, 1); + + if (gpio_mode == AM335XGPIO_GPIO_MODE_OUTPUT || + gpio_mode == AM335XGPIO_GPIO_MODE_OUTPUT_LOW || + gpio_mode == AM335XGPIO_GPIO_MODE_OUTPUT_HIGH) { + AM335XGPIO_CLEAR_REG_BITS(gpio_num, AM335XGPIO_GPIO_OE_OFFSET, AM335XGPIO_BIT_MASK(gpio_num)); + } +} + +static const char *get_gpio_mode_name(enum amx335gpio_gpio_mode gpio_mode) +{ + switch (gpio_mode) { + case AM335XGPIO_GPIO_MODE_INPUT: + return "input"; + case AM335XGPIO_GPIO_MODE_OUTPUT: + return "output"; + case AM335XGPIO_GPIO_MODE_OUTPUT_LOW: + return "output (low)"; + case AM335XGPIO_GPIO_MODE_OUTPUT_HIGH: + return "output (high)"; + default: + return "unknown"; + } +} + +static bb_value_t am335xgpio_read(void) +{ + return get_gpio_value(tdo_gpio) ? BB_HIGH : BB_LOW; +} + +static int am335xgpio_write(int tck, int tms, int tdi) +{ + set_gpio_value(tdi_gpio, tdi); + set_gpio_value(tms_gpio, tms); + set_gpio_value(tck_gpio, tck); /* Write clock last */ + + for (unsigned int i = 0; i < jtag_delay; ++i) + asm volatile (""); + + return ERROR_OK; +} + +static int am335xgpio_swd_write(int swclk, int swdio) +{ + set_gpio_value(swdio_gpio, swdio); + set_gpio_value(swclk_gpio, swclk); /* Write clock last */ + + for (unsigned int i = 0; i < jtag_delay; ++i) + asm volatile (""); + + return ERROR_OK; +} + +/* (1) assert or (0) deassert reset lines */ +static int am335xgpio_reset(int trst, int srst) +{ + /* assume active low */ + if (is_gpio_valid(srst_gpio)) { + if (jtag_get_reset_config() & RESET_SRST_PUSH_PULL) + set_gpio_mode(srst_gpio, srst ? AM335XGPIO_GPIO_MODE_OUTPUT_LOW : AM335XGPIO_GPIO_MODE_OUTPUT_HIGH); + else + set_gpio_mode(srst_gpio, srst ? AM335XGPIO_GPIO_MODE_OUTPUT_LOW : AM335XGPIO_GPIO_MODE_INPUT); + } + + /* assume active low */ + if (is_gpio_valid(trst_gpio)) { + if (jtag_get_reset_config() & RESET_TRST_OPEN_DRAIN) + set_gpio_mode(trst_gpio, trst ? AM335XGPIO_GPIO_MODE_OUTPUT_LOW : AM335XGPIO_GPIO_MODE_INPUT); + else + set_gpio_mode(trst_gpio, trst ? AM335XGPIO_GPIO_MODE_OUTPUT_LOW : AM335XGPIO_GPIO_MODE_OUTPUT_HIGH); + } + + LOG_DEBUG("am335xgpio_reset(%d, %d), trst_gpio: %d (%s), srst_gpio: %d (%s)", + trst, srst, + trst_gpio, get_gpio_mode_name(get_gpio_mode(trst_gpio)), + srst_gpio, get_gpio_mode_name(get_gpio_mode(srst_gpio))); + return ERROR_OK; +} + +static void am335xgpio_swdio_drive(bool is_output) +{ + if (is_output) { + set_gpio_value(swdio_dir_gpio, swdio_dir_is_active_high ? 1 : 0); + set_gpio_mode(swdio_gpio, AM335XGPIO_GPIO_MODE_OUTPUT); + } else { + set_gpio_mode(swdio_gpio, AM335XGPIO_GPIO_MODE_INPUT); + set_gpio_value(swdio_dir_gpio, swdio_dir_is_active_high ? 0 : 1); + } +} + +static int am335xgpio_swdio_read(void) +{ + return get_gpio_value(swdio_gpio); +} + +static int am335xgpio_blink(int on) +{ + if (is_gpio_valid(led_gpio)) + set_gpio_value(led_gpio, (!on ^ led_is_active_high) ? 1 : 0); + + return ERROR_OK; +} + +static struct bitbang_interface am335xgpio_bitbang = { + .read = am335xgpio_read, + .write = am335xgpio_write, + .swdio_read = am335xgpio_swdio_read, + .swdio_drive = am335xgpio_swdio_drive, + .swd_write = am335xgpio_swd_write, + .blink = am335xgpio_blink +}; + +static int am335xgpio_khz(int khz, int *jtag_speed) +{ + if (!khz) { + LOG_DEBUG("RCLK not supported"); + return ERROR_FAIL; + } + *jtag_speed = speed_coeff / khz - speed_offset; + if (*jtag_speed < 0) + *jtag_speed = 0; + return ERROR_OK; +} + +static int am335xgpio_speed_div(int speed, int *khz) +{ + *khz = speed_coeff / (speed + speed_offset); + return ERROR_OK; +} + +static int am335xgpio_speed(int speed) +{ + jtag_delay = speed; + return ERROR_OK; +} + +COMMAND_HANDLER(am335xgpio_handle_jtag_gpionums) +{ + if (CMD_ARGC == 4) { + COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], tck_gpio); + COMMAND_PARSE_NUMBER(int, CMD_ARGV[1], tms_gpio); + COMMAND_PARSE_NUMBER(int, CMD_ARGV[2], tdi_gpio); + COMMAND_PARSE_NUMBER(int, CMD_ARGV[3], tdo_gpio); + } else if (CMD_ARGC != 0) { + return ERROR_COMMAND_SYNTAX_ERROR; + } + + command_print(CMD, "AM335x GPIO config: tck = %d, tms = %d, tdi = %d, tdo = %d", + tck_gpio, tms_gpio, tdi_gpio, tdo_gpio); + return ERROR_OK; +} + +COMMAND_HANDLER(am335xgpio_handle_jtag_gpionum_tck) +{ + if (CMD_ARGC == 1) + COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], tck_gpio); + + command_print(CMD, "AM335x GPIO config: tck = %d", tck_gpio); + return ERROR_OK; +} + +COMMAND_HANDLER(am335xgpio_handle_jtag_gpionum_tms) +{ + if (CMD_ARGC == 1) + COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], tms_gpio); + + command_print(CMD, "AM335x GPIO config: tms = %d", tms_gpio); + return ERROR_OK; +} + +COMMAND_HANDLER(am335xgpio_handle_jtag_gpionum_tdo) +{ + if (CMD_ARGC == 1) + COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], tdo_gpio); + + command_print(CMD, "AM335x GPIO config: tdo = %d", tdo_gpio); + return ERROR_OK; +} + +COMMAND_HANDLER(am335xgpio_handle_jtag_gpionum_tdi) +{ + if (CMD_ARGC == 1) + COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], tdi_gpio); + + command_print(CMD, "AM335x GPIO config: tdi = %d", tdi_gpio); + return ERROR_OK; +} + +COMMAND_HANDLER(am335xgpio_handle_jtag_gpionum_srst) +{ + if (CMD_ARGC == 1) + COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], srst_gpio); + + command_print(CMD, "AM335x GPIO config: srst = %d", srst_gpio); + return ERROR_OK; +} + +COMMAND_HANDLER(am335xgpio_handle_jtag_gpionum_trst) +{ + if (CMD_ARGC == 1) + COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], trst_gpio); + + command_print(CMD, "AM335x GPIO config: trst = %d", trst_gpio); + return ERROR_OK; +} + +COMMAND_HANDLER(am335xgpio_handle_swd_gpionums) +{ + if (CMD_ARGC == 2) { + COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], swclk_gpio); + COMMAND_PARSE_NUMBER(int, CMD_ARGV[1], swdio_gpio); + } else if (CMD_ARGC != 0) { + return ERROR_COMMAND_SYNTAX_ERROR; + } + + command_print(CMD, "AM335x GPIO config: swclk = %d, swdio = %d", swclk_gpio, swdio_gpio); + + return ERROR_OK; +} + +COMMAND_HANDLER(am335xgpio_handle_swd_gpionum_swclk) +{ + if (CMD_ARGC == 1) + COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], swclk_gpio); + + command_print(CMD, "AM335x GPIO config: swclk = %d", swclk_gpio); + return ERROR_OK; +} + +COMMAND_HANDLER(am335xgpio_handle_swd_gpionum_swdio) +{ + if (CMD_ARGC == 1) + COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], swdio_gpio); + + command_print(CMD, "AM335x GPIO config: swdio = %d", swdio_gpio); + return ERROR_OK; +} + +COMMAND_HANDLER(am335xgpio_handle_swd_gpionum_swdio_dir) +{ + if (CMD_ARGC == 1) + COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], swdio_dir_gpio); + + command_print(CMD, "AM335x GPIO config: swdio_dir = %d", swdio_dir_gpio); + return ERROR_OK; +} + +COMMAND_HANDLER(am335xgpio_handle_swd_dir_output_state) +{ + if (CMD_ARGC == 1) + COMMAND_PARSE_BOOL(CMD_ARGV[0], swdio_dir_is_active_high, "high", "low"); + + command_print(CMD, "AM335x GPIO config: swdio_dir_output_state = %s", swdio_dir_is_active_high ? "high" : "low"); + return ERROR_OK; +} + +COMMAND_HANDLER(am335xgpio_handle_gpionum_led) +{ + if (CMD_ARGC == 1) + COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], led_gpio); + + command_print(CMD, "AM335x GPIO config: led = %d", led_gpio); + return ERROR_OK; +} + +COMMAND_HANDLER(am335xgpio_handle_led_on_state) +{ + if (CMD_ARGC == 1) + COMMAND_PARSE_BOOL(CMD_ARGV[0], led_is_active_high, "high", "low"); + + command_print(CMD, "AM335x GPIO config: led_on_state = %s", led_is_active_high ? "high" : "low"); + return ERROR_OK; +} + +COMMAND_HANDLER(am335xgpio_handle_speed_coeffs) +{ + if (CMD_ARGC == 2) { + COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], speed_coeff); + COMMAND_PARSE_NUMBER(int, CMD_ARGV[1], speed_offset); + } + + command_print(CMD, "AM335x GPIO config: speed_coeffs = %d, speed_offset = %d", + speed_coeff, speed_offset); + return ERROR_OK; +} + +static const struct command_registration am335xgpio_subcommand_handlers[] = { + { + .name = "jtag_nums", + .handler = am335xgpio_handle_jtag_gpionums, + .mode = COMMAND_CONFIG, + .help = "gpio numbers for tck, tms, tdi, tdo (in that order).", + .usage = "[tck tms tdi tdo]", + }, + { + .name = "tck_num", + .handler = am335xgpio_handle_jtag_gpionum_tck, + .mode = COMMAND_CONFIG, + .help = "gpio number for tck.", + .usage = "[tck]", + }, + { + .name = "tms_num", + .handler = am335xgpio_handle_jtag_gpionum_tms, + .mode = COMMAND_CONFIG, + .help = "gpio number for tms.", + .usage = "[tms]", + }, + { + .name = "tdo_num", + .handler = am335xgpio_handle_jtag_gpionum_tdo, + .mode = COMMAND_CONFIG, + .help = "gpio number for tdo.", + .usage = "[tdo]", + }, + { + .name = "tdi_num", + .handler = am335xgpio_handle_jtag_gpionum_tdi, + .mode = COMMAND_CONFIG, + .help = "gpio number for tdi.", + .usage = "[tdi]", + }, + { + .name = "swd_nums", + .handler = am335xgpio_handle_swd_gpionums, + .mode = COMMAND_CONFIG, + .help = "gpio numbers for swclk, swdio (in that order).", + .usage = "[swclk swdio]", + }, + { + .name = "swclk_num", + .handler = am335xgpio_handle_swd_gpionum_swclk, + .mode = COMMAND_CONFIG, + .help = "gpio number for swclk.", + .usage = "[swclk]", + }, + { + .name = "swdio_num", + .handler = am335xgpio_handle_swd_gpionum_swdio, + .mode = COMMAND_CONFIG, + .help = "gpio number for swdio.", + .usage = "[swdio]", + }, + { + .name = "swdio_dir_num", + .handler = am335xgpio_handle_swd_gpionum_swdio_dir, + .mode = COMMAND_CONFIG, + .help = "gpio number for swdio direction control pin.", + .usage = "[swdio_dir]", + }, + { + .name = "swdio_dir_output_state", + .handler = am335xgpio_handle_swd_dir_output_state, + .mode = COMMAND_CONFIG, + .help = "required state for swdio_dir pin to select SWDIO buffer to be output.", + .usage = "['off'|'on']", + }, + { + .name = "srst_num", + .handler = am335xgpio_handle_jtag_gpionum_srst, + .mode = COMMAND_CONFIG, + .help = "gpio number for srst.", + .usage = "[srst]", + }, + { + .name = "trst_num", + .handler = am335xgpio_handle_jtag_gpionum_trst, + .mode = COMMAND_CONFIG, + .help = "gpio number for trst.", + .usage = "[trst]", + }, + { + .name = "led_num", + .handler = am335xgpio_handle_gpionum_led, + .mode = COMMAND_CONFIG, + .help = "gpio number for led.", + .usage = "[led]", + }, + { + .name = "led_on_state", + .handler = am335xgpio_handle_led_on_state, + .mode = COMMAND_CONFIG, + .help = "required state for led pin to turn on LED.", + .usage = "['off'|'on']", + }, + { + .name = "speed_coeffs", + .handler = am335xgpio_handle_speed_coeffs, + .mode = COMMAND_CONFIG, + .help = "SPEED_COEFF and SPEED_OFFSET for delay calculations.", + .usage = "[SPEED_COEFF SPEED_OFFSET]", + }, + COMMAND_REGISTRATION_DONE +}; + +static const struct command_registration am335xgpio_command_handlers[] = { + { + .name = "am335xgpio", + .mode = COMMAND_ANY, + .help = "perform am335xgpio management", + .chain = am335xgpio_subcommand_handlers, + .usage = "", + }, + COMMAND_REGISTRATION_DONE +}; + +static const char * const am335xgpio_transports[] = { "jtag", "swd", NULL }; + +static struct jtag_interface am335xgpio_interface = { + .supported = DEBUG_CAP_TMS_SEQ, + .execute_queue = bitbang_execute_queue, +}; + +static bool am335xgpio_jtag_mode_possible(void) +{ + if (!is_gpio_valid(tck_gpio)) + return false; + if (!is_gpio_valid(tms_gpio)) + return false; + if (!is_gpio_valid(tdi_gpio)) + return false; + if (!is_gpio_valid(tdo_gpio)) + return false; + return true; +} + +static bool am335xgpio_swd_mode_possible(void) +{ + if (!is_gpio_valid(swclk_gpio)) + return false; + if (!is_gpio_valid(swdio_gpio)) + return false; + return true; +} + +static int am335xgpio_init(void) +{ + bitbang_interface = &am335xgpio_bitbang; + + LOG_INFO("AM335x GPIO JTAG/SWD bitbang driver"); + + if (transport_is_jtag() && !am335xgpio_jtag_mode_possible()) { + LOG_ERROR("Require tck, tms, tdi and tdo gpios for JTAG mode"); + return ERROR_JTAG_INIT_FAILED; + } + + if (transport_is_swd() && !am335xgpio_swd_mode_possible()) { + LOG_ERROR("Require swclk and swdio gpio for SWD mode"); + return ERROR_JTAG_INIT_FAILED; + } + + dev_mem_fd = open("/dev/gpiomem", O_RDWR | O_SYNC); + if (dev_mem_fd < 0) { + LOG_DEBUG("Cannot open /dev/gpiomem, fallback to /dev/mem"); + dev_mem_fd = open("/dev/mem", O_RDWR | O_SYNC); + } + if (dev_mem_fd < 0) { + LOG_ERROR("open: %s", strerror(errno)); + return ERROR_JTAG_INIT_FAILED; + } + + for (unsigned int i = 0; i < AM335XGPIO_NUM_GPIO_PORTS; ++i) { + am335xgpio_gpio_port_mmap_addr[i] = mmap(NULL, sysconf(_SC_PAGE_SIZE), PROT_READ | PROT_WRITE, + MAP_SHARED, dev_mem_fd, am335xgpio_gpio_port_hw_addr[i]); + + if (am335xgpio_gpio_port_mmap_addr[i] == MAP_FAILED) { + LOG_ERROR("mmap: %s", strerror(errno)); + close(dev_mem_fd); + return ERROR_JTAG_INIT_FAILED; + } + } + + /* + * Configure TDO as an input, and TDI, TCK, TMS, TRST, SRST as outputs. + * Drive TDI and TCK low, and TMS high. + */ + if (transport_is_jtag()) { + tdo_gpio_mode = get_gpio_mode(tdo_gpio); + tdi_gpio_mode = get_gpio_mode(tdi_gpio); + tck_gpio_mode = get_gpio_mode(tck_gpio); + tms_gpio_mode = get_gpio_mode(tms_gpio); + LOG_DEBUG("saved GPIO mode for tdo (GPIO #%d): %s", tdo_gpio, get_gpio_mode_name(tdo_gpio_mode)); + LOG_DEBUG("saved GPIO mode for tdi (GPIO #%d): %s", tdi_gpio, get_gpio_mode_name(tdi_gpio_mode)); + LOG_DEBUG("saved GPIO mode for tck (GPIO #%d): %s", tck_gpio, get_gpio_mode_name(tck_gpio_mode)); + LOG_DEBUG("saved GPIO mode for tms (GPIO #%d): %s", tms_gpio, get_gpio_mode_name(tms_gpio_mode)); + + set_gpio_mode(tdo_gpio, AM335XGPIO_GPIO_MODE_INPUT); + set_gpio_mode(tdi_gpio, AM335XGPIO_GPIO_MODE_OUTPUT_LOW); + set_gpio_mode(tms_gpio, AM335XGPIO_GPIO_MODE_OUTPUT_HIGH); + set_gpio_mode(tck_gpio, AM335XGPIO_GPIO_MODE_OUTPUT_LOW); + + if (is_gpio_valid(trst_gpio)) { + trst_gpio_mode = get_gpio_mode(trst_gpio); + LOG_DEBUG("saved GPIO mode for trst (GPIO #%d): %s", trst_gpio, get_gpio_mode_name(trst_gpio_mode)); + } + } + + if (transport_is_swd()) { + swclk_gpio_mode = get_gpio_mode(swclk_gpio); + swdio_gpio_mode = get_gpio_mode(swdio_gpio); + LOG_DEBUG("saved GPIO mode for swclk (GPIO #%d): %s", swclk_gpio, get_gpio_mode_name(swclk_gpio_mode)); + LOG_DEBUG("saved GPIO mode for swdio (GPIO #%d): %s", swdio_gpio, get_gpio_mode_name(swdio_gpio_mode)); + if (is_gpio_valid(swdio_dir_gpio)) { + swdio_dir_gpio_mode = get_gpio_mode(swdio_dir_gpio); + LOG_DEBUG("saved GPIO mode for swdio_dir (GPIO #%d): %s", + swdio_dir_gpio, get_gpio_mode_name(swdio_dir_gpio_mode)); + set_gpio_mode(swdio_dir_gpio, + swdio_dir_is_active_high ? AM335XGPIO_GPIO_MODE_OUTPUT_HIGH : AM335XGPIO_GPIO_MODE_OUTPUT_LOW); + + } + set_gpio_mode(swdio_gpio, AM335XGPIO_GPIO_MODE_OUTPUT_LOW); + set_gpio_mode(swclk_gpio, AM335XGPIO_GPIO_MODE_OUTPUT_LOW); + } + + if (is_gpio_valid(srst_gpio)) { + srst_gpio_mode = get_gpio_mode(srst_gpio); + LOG_DEBUG("saved GPIO mode for srst (GPIO #%d): %s", srst_gpio, get_gpio_mode_name(srst_gpio_mode)); + } + + if (is_gpio_valid(led_gpio)) { + led_gpio_mode = get_gpio_mode(led_gpio); + LOG_DEBUG("saved GPIO mode for led (GPIO #%d): %s", led_gpio, get_gpio_mode_name(led_gpio_mode)); + set_gpio_mode(led_gpio, + led_is_active_high ? AM335XGPIO_GPIO_MODE_OUTPUT_LOW : AM335XGPIO_GPIO_MODE_OUTPUT_HIGH); + } + + /* Set GPIO modes for TRST and SRST and make both inactive */ + am335xgpio_reset(0, 0); + return ERROR_OK; +} + +static int am335xgpio_quit(void) +{ + if (transport_is_jtag()) { + set_gpio_mode(tdo_gpio, tdo_gpio_mode); + set_gpio_mode(tdi_gpio, tdi_gpio_mode); + set_gpio_mode(tck_gpio, tck_gpio_mode); + set_gpio_mode(tms_gpio, tms_gpio_mode); + if (is_gpio_valid(trst_gpio)) + set_gpio_mode(trst_gpio, trst_gpio_mode); + } + + if (transport_is_swd()) { + set_gpio_mode(swclk_gpio, swclk_gpio_mode); + set_gpio_mode(swdio_gpio, swdio_gpio_mode); + if (is_gpio_valid(swdio_dir_gpio)) + set_gpio_mode(swdio_dir_gpio, swdio_dir_gpio_mode); + } + + if (is_gpio_valid(srst_gpio)) + set_gpio_mode(srst_gpio, srst_gpio_mode); + + if (is_gpio_valid(led_gpio)) + set_gpio_mode(led_gpio, led_gpio_mode); + + return ERROR_OK; +} + +struct adapter_driver am335xgpio_adapter_driver = { + .name = "am335xgpio", + .transports = am335xgpio_transports, + .commands = am335xgpio_command_handlers, + + .init = am335xgpio_init, + .quit = am335xgpio_quit, + .reset = am335xgpio_reset, + .speed = am335xgpio_speed, + .khz = am335xgpio_khz, + .speed_div = am335xgpio_speed_div, + + .jtag_ops = &am335xgpio_interface, + .swd_ops = &bitbang_swd, +}; diff --git a/src/jtag/interfaces.c b/src/jtag/interfaces.c index ddf70cc24..9afd69dff 100644 --- a/src/jtag/interfaces.c +++ b/src/jtag/interfaces.c @@ -150,6 +150,9 @@ extern struct adapter_driver stlink_dap_adapter_driver; #if BUILD_RSHIM == 1 extern struct adapter_driver rshim_dap_adapter_driver; #endif +#if BUILD_AM335XGPIO == 1 +extern struct adapter_driver am335xgpio_adapter_driver; +#endif /** * The list of built-in JTAG interfaces, containing entries for those @@ -263,6 +266,9 @@ struct adapter_driver *adapter_drivers[] = { #endif #if BUILD_RSHIM == 1 &rshim_dap_adapter_driver, +#endif +#if BUILD_AM335XGPIO == 1 + &am335xgpio_adapter_driver, #endif NULL, }; diff --git a/tcl/interface/beaglebone-jtag-native.cfg b/tcl/interface/beaglebone-jtag-native.cfg new file mode 100644 index 000000000..cd32ca49b --- /dev/null +++ b/tcl/interface/beaglebone-jtag-native.cfg @@ -0,0 +1,28 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# BeagleBone native GPIO interface for JTAG +# +# This is best used with a fast buffer but it is also suitable for a direct +# connection if the target voltage matches the host's IO voltage (typically +# 3.3V) and the cable is short. +# +# DO NOT APPLY VOLTAGE TO THE GPIO PINS UNTIL SYS_RESETN IS HIGH. +# +# Do not forget the GND connection. + +adapter driver am335xgpio + +# Transition delay calculation: SPEED_COEFF/khz - SPEED_OFFSET +# These depend on the system clock, calibrated for stock 1 GHz BeagleBoneBlack +# am335xgpio speed SPEED_COEFF SPEED_OFFSET +am335xgpio speed_coeffs 600000 575 + +am335xgpio tdo_num 20 +am335xgpio tdi_num 60 +am335xgpio tms_num 4 +am335xgpio tck_num 2 + +am335xgpio led_num 51 +am335xgpio led_on_state on + +am335xgpio srst_num 65 +reset_config srst_only srst_push_pull diff --git a/tcl/interface/beaglebone-swd-native.cfg b/tcl/interface/beaglebone-swd-native.cfg new file mode 100644 index 000000000..f7bff6e57 --- /dev/null +++ b/tcl/interface/beaglebone-swd-native.cfg @@ -0,0 +1,29 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# BeagleBone native GPIO interface for SWD +# +# This is best used with a fast buffer but it is also suitable for a direct +# connection if the target voltage matches the host's IO voltage (typically +# 3.3V) and the cable is short. +# +# DO NOT APPLY VOLTAGE TO THE GPIO PINS UNTIL SYS_RESETN IS HIGH. +# +# Do not forget the GND connection. + +adapter driver am335xgpio + +# Transition delay calculation: SPEED_COEFF/khz - SPEED_OFFSET +# These depend on the system clock, calibrated for stock 1 GHz BeagleBoneBlack +# am335xgpio speed SPEED_COEFF SPEED_OFFSET +am335xgpio speed_coeffs 600000 575 + +am335xgpio swclk_num 2 +am335xgpio swdio_num 4 +am335xgpio swdio_dir_num 60 +am335xgpio swdio_dir_output_state on + +# USR0 LED +am335xgpio led_num 53 +am335xgpio led_on_state on + +am335xgpio srst_num 65 +reset_config srst_only srst_push_pull From dac90163a2875fda5c516a8ddc68fdddfc0077a6 Mon Sep 17 00:00:00 2001 From: Antonio Borneo Date: Mon, 13 Dec 2021 16:50:39 +0100 Subject: [PATCH 27/60] .gitignore: add cross-compile *-libtool While cross-compiling OpenOCD the generated script is not named "libtool" anymore, but e.g. "arm-linux-gnueabi-libtool". Ignore all the possible variants "*-libtool". Change-Id: I7a0dade992dbc13f977610bd4a78f8a4783b0146 Signed-off-by: Antonio Borneo Reviewed-on: https://review.openocd.org/c/openocd/+/6960 Tested-by: jenkins --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 955ca3c2e..f5aa68a40 100644 --- a/.gitignore +++ b/.gitignore @@ -68,6 +68,7 @@ doxygen doxygen.log Doxyfile libtool +*-libtool Makefile !contrib/loaders/**/Makefile stamp-h1 From 62cea61237033b1821d68b1d4d09fd381ddb5d52 Mon Sep 17 00:00:00 2001 From: Steve Marple Date: Sun, 1 May 2022 22:29:43 +0100 Subject: [PATCH 28/60] doc: Document linuxgpiod driver commands Change-Id: I84ad5dba9ab2099137595b46822bc10a0b089524 Signed-off-by: Steve Marple Reviewed-on: https://review.openocd.org/c/openocd/+/6962 Tested-by: jenkins Reviewed-by: Antonio Borneo --- doc/openocd.texi | 79 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 78 insertions(+), 1 deletion(-) diff --git a/doc/openocd.texi b/doc/openocd.texi index 9681cf536..be9e5ac08 100644 --- a/doc/openocd.texi +++ b/doc/openocd.texi @@ -3419,9 +3419,86 @@ speed_coeff defaults to 600000 and speed_offset defaults to 575. @deffn {Interface Driver} {linuxgpiod} Linux provides userspace access to GPIO through libgpiod since Linux kernel version v4.6. -The driver emulates either JTAG and SWD transport through bitbanging. +The driver emulates either JTAG or SWD transport through bitbanging. See @file{interface/dln-2-gpiod.cfg} for a sample config. + +@deffn {Config Command} {linuxgpiod gpiochip} @var{chip} +Set the GPIO chip number for all GPIOs used by linuxgpiod. If GPIOs use +different GPIO chips then the individual GPIO configuration commands (i.e., not +@command{linuxgpiod jtag_nums} or @command{linuxgpiod swd_nums}) can be used to +set chip numbers independently for each GPIO. +@end deffn + +@deffn {Config Command} {linuxgpiod jtag_nums} @var{tck} @var{tms} @var{tdi} @var{tdo} +Set JTAG transport GPIO numbers for TCK, TMS, TDI, and TDO (in that order). Must +be specified to enable JTAG transport. These pins can also be specified +individually. +@end deffn + +@deffn {Config Command} {linuxgpiod tck_num} [@var{chip}] @var{tck} +Set TCK GPIO number, and optionally TCK chip number. Must be specified to enable +JTAG transport. Can also be specified using the configuration command +@command{linuxgpiod jtag_nums}. +@end deffn + +@deffn {Config Command} {linuxgpiod tms_num} [@var{chip}] @var{tms} +Set TMS GPIO number, and optionally TMS chip number. Must be specified to enable +JTAG transport. Can also be specified using the configuration command +@command{linuxgpiod jtag_nums}. +@end deffn + +@deffn {Config Command} {linuxgpiod tdo_num} [@var{chip}] @var{tdo} +Set TDO GPIO number, and optionally TDO chip number. Must be specified to enable +JTAG transport. Can also be specified using the configuration command +@command{linuxgpiod jtag_nums}. +@end deffn + +@deffn {Config Command} {linuxgpiod tdi_num} [@var{chip}] @var{tdi} +Set TDI GPIO number, and optionally TDI chip number. Must be specified to enable +JTAG transport. Can also be specified using the configuration command +@command{linuxgpiod jtag_nums}. +@end deffn + +@deffn {Config Command} {linuxgpiod trst_num} [@var{chip}] @var{trst} +Set TRST GPIO number, and optionally TRST chip number. Must be specified to +enable TRST. +@end deffn + +@deffn {Config Command} {linuxgpiod swd_nums} @var{swclk} @var{swdio} +Set SWD transport GPIO numbers for SWCLK and SWDIO (in that order). Must be +specified to enable SWD transport. These pins can also be specified +individually. +@end deffn + +@deffn {Config Command} {linuxgpiod swclk_num} [@var{chip}] @var{swclk} +Set SWCLK GPIO number, and optionally SWCLK chip number. Must be specified to +enable SWD transport. Can also be specified using the configuration command +@command{linuxgpiod swd_nums}. +@end deffn + +@deffn {Config Command} {linuxgpiod swdio_num} [@var{chip}] @var{swdio} +Set SWDIO GPIO number, and optionally SWDIO chip number. Must be specified to +enable SWD transport. Can also be specified using the configuration command +@command{linuxgpiod swd_nums}. +@end deffn + +@deffn {Config Command} {linuxgpiod swdio_dir_num} [@var{chip}] @var{swdio_dir} +Set SWDIO direction control GPIO number, and optionally SWDIO direction control +chip number. If specified, this GPIO can be used to control the direction of an +external buffer connected to the SWDIO GPIO (set=output mode, clear=input mode). +@end deffn + +@deffn {Config Command} {linuxgpiod srst_num} [@var{chip}] @var{srst} +Set SRST GPIO number, and optionally SRST chip number. Must be specified to +enable SRST. +@end deffn + +@deffn {Config Command} {linuxgpiod led_num} [@var{chip}] @var{led} +Set activity LED GPIO number, and optionally activity LED chip number. If not +specified an activity LED is not enabled. +@end deffn + @end deffn From 830d70bfc66ada2a68c73283b9e4fa4770d408ee Mon Sep 17 00:00:00 2001 From: Thomas Hebb Date: Thu, 28 Apr 2022 16:44:05 -0700 Subject: [PATCH 29/60] tcl/interface/ftdi: Add config for Tigard board Tigard[1] is an FT2232H-based development tool designed for ease of use with many different protocols and targets. It includes a JTAG header wired to channel B, with labeled pins for the four required signals as well as nTRST and nSRST, which are connected through an output buffer to BDBUS4 and BDBUS5 respectively. Add an interface config for Tigard. I wrote it by referencing the Tigard schematic and tested it by debugging a couple of RISC-V development boards. [1] https://github.com/tigard-tools/tigard Signed-off-by: Thomas Hebb Change-Id: I34df9f72538ba1e40ad53b568c9cdca96ae4b082 Reviewed-on: https://review.openocd.org/c/openocd/+/6952 Tested-by: jenkins Reviewed-by: Antonio Borneo --- tcl/interface/ftdi/tigard.cfg | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 tcl/interface/ftdi/tigard.cfg diff --git a/tcl/interface/ftdi/tigard.cfg b/tcl/interface/ftdi/tigard.cfg new file mode 100644 index 000000000..43ce0ad1d --- /dev/null +++ b/tcl/interface/ftdi/tigard.cfg @@ -0,0 +1,19 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Tigard: An FTDI FT2232H-based multi-protocol tool for hardware hacking. +# https://github.com/tigard-tools/tigard + +adapter driver ftdi + +ftdi device_desc "Tigard V1.1" +ftdi vid_pid 0x0403 0x6010 + +ftdi channel 1 + +ftdi layout_init 0x0038 0x003b +ftdi layout_signal nTRST -data 0x0010 +ftdi layout_signal nSRST -data 0x0020 + +# This board doesn't support open-drain reset modes since its output buffer is +# always enabled. +reset_config srst_push_pull trst_push_pull From 4d1b29313ace5b26687f75fb132c04f91cc1edcb Mon Sep 17 00:00:00 2001 From: Tomas Vanek Date: Tue, 9 Nov 2021 19:22:29 +0100 Subject: [PATCH 30/60] doc: prevent writing "topic:" to commit message First time contributors surprisingly often write "topic:" instead of "the main part or subsystem the patch touches" as requested in patchguide.html To prevent them doing so: Use longer self-explaing "specify touched area" instead of "topic" Give some examples in addition to recommended looking at "git log" Change-Id: I1c307b460d7a79ba3c9918af8dbc9e9f827e1fb9 Signed-off-by: Tomas Vanek Reviewed-on: https://review.openocd.org/c/openocd/+/6683 Reviewed-by: Antonio Borneo Tested-by: Paul Fertser --- HACKING | 36 +++++++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/HACKING b/HACKING index cf3f58906..96b05f2d6 100644 --- a/HACKING +++ b/HACKING @@ -171,11 +171,11 @@ while(!done) { @endcode \note use "git add ." before commit to add new files. - Comment template, notice the short first line w/topic. The topic field - should identify the main part or subsystem the patch touches. Check - git log for examples. -@code -topic: Short comment + Commit message template, notice the short first line. + The field 'specify touched area' + should identify the main part or subsystem the patch touches. +@code{.unparsed} +specify touched area: short comment Longer comments over several lines, explaining (where applicable) the reason for the patch and the general idea the solution is based on, @@ -183,6 +183,32 @@ any major design decisions, etc... Signed-off-by: ... @endcode + Examples: +@code{.unparsed} +flash/nor/atsame5: add SAME59 support + +Add new device ID +@endcode +@code{.unparsed} +flash/nor: flash driver for XYZ123 + +Add new flash driver for internal flash of ... +@endcode +@code{.unparsed} +target/cortex_m: fix segmentation fault in cmd 'soft_reset_halt' + +soft_reset_halt command failed reproducibly under following conditions: ... +Test for NULL pointer and return error ... + +Reported-by: John Reporter +Fixes: 123456789abc ("target: the commit where the problem started") +BugLink: https://sourceforge.net/p/openocd/tickets/999/ +@endcode +@code{.unparsed} +doc: fix typos +@endcode + See "git log" for more examples. + -# Next you need to make sure that your patches are on top of the latest stuff on the server and that there are no conflicts: From 7d2ea186cf212c64a8e5b10725e0a2512a137fbe Mon Sep 17 00:00:00 2001 From: Tomas Vanek Date: Sun, 1 May 2022 15:20:24 +0200 Subject: [PATCH 31/60] target/riscv: fix 'reset run' after 'reset halt' 'reset halt' does not clear DM_DMCONTROL_HALTREQ at deassert_reset(). If hw reset line is configured e.g. 'reset_config srst_only' the folowing 'reset run' halts: > gd32v.cpu curstate running > reset halt JTAG tap: gd32v.cpu tap/device found: 0x1000563d (mfg: 0x31e ... > gd32v.cpu curstate halted > reset JTAG tap: gd32v.cpu tap/device found: 0x1000563d (mfg: 0x31e ... > gd32v.cpu curstate halted <<<<---- wrong!!! > reset JTAG tap: gd32v.cpu tap/device found: 0x1000563d (mfg: 0x31e ... > gd32v.cpu curstate running Clear DM_DMCONTROL_HALTREQ when acking reset. Change-Id: Iae0454b425e81e64774b9785bb5ba1d4564d940b Signed-off-by: Tomas Vanek Reviewed-on: https://review.openocd.org/c/openocd/+/6961 Tested-by: jenkins Reviewed-by: Tim Newsome --- src/target/riscv/riscv-013.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/target/riscv/riscv-013.c b/src/target/riscv/riscv-013.c index 2b9179d53..1b1450a7d 100644 --- a/src/target/riscv/riscv-013.c +++ b/src/target/riscv/riscv-013.c @@ -2397,11 +2397,11 @@ static int deassert_reset(struct target *target) select_dmi(target); /* Clear the reset, but make sure haltreq is still set */ - uint32_t control = 0; - control = set_field(control, DM_DMCONTROL_HALTREQ, target->reset_halt ? 1 : 0); + uint32_t control = 0, control_haltreq; control = set_field(control, DM_DMCONTROL_DMACTIVE, 1); + control_haltreq = set_field(control, DM_DMCONTROL_HALTREQ, target->reset_halt ? 1 : 0); dmi_write(target, DM_DMCONTROL, - set_hartsel(control, r->current_hartid)); + set_hartsel(control_haltreq, r->current_hartid)); uint32_t dmstatus; int dmi_busy_delay = info->dmi_busy_delay; @@ -2413,7 +2413,7 @@ static int deassert_reset(struct target *target) if (index != target->coreid) continue; dmi_write(target, DM_DMCONTROL, - set_hartsel(control, index)); + set_hartsel(control_haltreq, index)); } else { index = r->current_hartid; } @@ -2449,7 +2449,7 @@ static int deassert_reset(struct target *target) target->state = TARGET_HALTED; if (get_field(dmstatus, DM_DMSTATUS_ALLHAVERESET)) { - /* Ack reset. */ + /* Ack reset and clear DM_DMCONTROL_HALTREQ if previously set */ dmi_write(target, DM_DMCONTROL, set_hartsel(control, index) | DM_DMCONTROL_ACKHAVERESET); From 0f11f951e7774c54953f3f06916dcb62ac9b086d Mon Sep 17 00:00:00 2001 From: Tomas Vanek Date: Mon, 9 May 2022 22:54:59 +0200 Subject: [PATCH 32/60] target: fix clang static analyzer warning Removes Warning: line 6482, column 12 1st function call argument is an uninitialized value Use target ptr directly as checked in previous lines instead of dereferencing head->target Change-Id: I6804b776fd493af71f3098d702f9cdc7acb50151 Signed-off-by: Tomas Vanek Reviewed-on: https://review.openocd.org/c/openocd/+/6970 Tested-by: jenkins Reviewed-by: Antonio Borneo --- src/target/target.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/target/target.c b/src/target/target.c index d2dff111a..8a451883b 100644 --- a/src/target/target.c +++ b/src/target/target.c @@ -6477,7 +6477,7 @@ static int jim_target_smp(Jim_Interp *interp, int argc, Jim_Obj *const *argv) } if (target && target->rtos) - retval = rtos_smp_init(head->target); + retval = rtos_smp_init(target); return retval; } From 631d0bddfaadac63a7d8ff3ef916614d62c86b1e Mon Sep 17 00:00:00 2001 From: Erhan Kurubas Date: Mon, 9 May 2022 23:01:35 +0200 Subject: [PATCH 33/60] flash: fix clang static analyzer build errors Fixes "variable set but not used" errors. Tested with Homebrew clang version 13.0.1 Signed-off-by: Erhan Kurubas Change-Id: Ia90baf5b4857db2b5569ebe6adbbb832de772aad Reviewed-on: https://review.openocd.org/c/openocd/+/6971 Tested-by: jenkins Reviewed-by: Antonio Borneo --- src/flash/nand/davinci.c | 2 -- src/flash/nor/dsp5680xx_flash.c | 3 --- 2 files changed, 5 deletions(-) diff --git a/src/flash/nand/davinci.c b/src/flash/nand/davinci.c index 84f8e3480..1aa7ffc05 100644 --- a/src/flash/nand/davinci.c +++ b/src/flash/nand/davinci.c @@ -605,8 +605,6 @@ static int davinci_write_page_ecc4infix(struct nand_device *nand, uint32_t page, /* write this "out-of-band" data -- infix */ davinci_write_block_data(nand, oob, 16); oob += 16; - oob_size -= 16; - } while (data_size); /* the last data and OOB writes included the spare area */ diff --git a/src/flash/nor/dsp5680xx_flash.c b/src/flash/nor/dsp5680xx_flash.c index 5e8eec30f..858b66981 100644 --- a/src/flash/nor/dsp5680xx_flash.c +++ b/src/flash/nor/dsp5680xx_flash.c @@ -45,14 +45,11 @@ static int dsp5680xx_build_sector_list(struct flash_bank *bank) { - uint32_t offset = HFM_FLASH_BASE_ADDR; - bank->sectors = malloc(sizeof(struct flash_sector) * bank->num_sectors); for (unsigned int i = 0; i < bank->num_sectors; ++i) { bank->sectors[i].offset = i * HFM_SECTOR_SIZE; bank->sectors[i].size = HFM_SECTOR_SIZE; - offset += bank->sectors[i].size; bank->sectors[i].is_erased = -1; bank->sectors[i].is_protected = -1; } From 19e992e8827a13a9507b64b3a96895e6ed3d714a Mon Sep 17 00:00:00 2001 From: micbis Date: Tue, 10 May 2022 10:49:31 +0200 Subject: [PATCH 34/60] tcl/target/renesas_rz_g2: Added RZ/G2LC and RZ/G2UL Added support for two new devices: RZ/G2LC and RZ/G2UL Change-Id: Iec6ba88c1d279f50808b060343b45c796bbfdbfc Signed-off-by: micbis Reviewed-on: https://review.openocd.org/c/openocd/+/6972 Tested-by: jenkins Reviewed-by: Antonio Borneo --- tcl/target/renesas_rz_g2.cfg | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/tcl/target/renesas_rz_g2.cfg b/tcl/target/renesas_rz_g2.cfg index 3615aa0e2..a3d5f48fb 100644 --- a/tcl/target/renesas_rz_g2.cfg +++ b/tcl/target/renesas_rz_g2.cfg @@ -6,11 +6,13 @@ # - Each SOC can boot through the Cortex-A5x cores # Supported RZ/G2 SOCs and their cores: -# RZ/G2H: Cortex-A57 x4, Cortex-A53 x4, Cortex-R7 -# RZ/G2M: Cortex-A57 x2, Cortex-A53 x4, Cortex-R7 -# RZ/G2N: Cortex-A57 x2, Cortex-R7 -# RZ/G2E: Cortex-A53 x2, Cortex-R7 -# RZ/G2L: Cortex-A55 x2, Cortex-M33 +# RZ/G2H: Cortex-A57 x4, Cortex-A53 x4, Cortex-R7 +# RZ/G2M: Cortex-A57 x2, Cortex-A53 x4, Cortex-R7 +# RZ/G2N: Cortex-A57 x2, Cortex-R7 +# RZ/G2E: Cortex-A53 x2, Cortex-R7 +# RZ/G2L: Cortex-A55 x2, Cortex-M33 +# RZ/G2LC: Cortex-A55 x2, Cortex-M33 +# RZ/G2UL: Cortex-A55 x1, Cortex-M33 # Usage: # There are 2 configuration options: @@ -75,6 +77,20 @@ switch $_soc { set _boot_core CA55 set _ap_num 0 } + G2LC { + set _CHIPNAME r9a07g044c + set _num_ca55 2 + set _num_cm33 1 + set _boot_core CA55 + set _ap_num 0 + } + G2UL { + set _CHIPNAME r9a07g043u + set _num_ca55 1 + set _num_cm33 1 + set _boot_core CA55 + set _ap_num 0 + } default { error "'$_soc' is invalid!" } @@ -169,7 +185,7 @@ if { $_boot_core == "CA57" } { echo "SMP targets:$smp_targets" eval "target smp $smp_targets" -if { $_soc == "G2L"} { +if { $_soc == "G2L" || $_soc == "G2LC" || $_soc == "G2UL" } { target create $_CHIPNAME.axi_ap mem_ap -dap $_DAPNAME -ap-num 1 } From e5f515f990cc345fd3089a5520f39d5a128329bd Mon Sep 17 00:00:00 2001 From: micbis Date: Thu, 12 May 2022 15:17:49 +0200 Subject: [PATCH 35/60] tcl/target/renesas_rz_five: Added RZ/Five Added support for the new Renesas RISC-V device: RZ/Five Signed-off-by: micbis Change-Id: Id8ba29b83528c0bfe4f9b4ed21b0151a6e853bd7 Reviewed-on: https://review.openocd.org/c/openocd/+/6974 Reviewed-by: Antonio Borneo Tested-by: jenkins --- tcl/target/renesas_rz_five.cfg | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 tcl/target/renesas_rz_five.cfg diff --git a/tcl/target/renesas_rz_five.cfg b/tcl/target/renesas_rz_five.cfg new file mode 100644 index 000000000..5ab94ab1f --- /dev/null +++ b/tcl/target/renesas_rz_five.cfg @@ -0,0 +1,22 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +# Renesas RZ/Five SoC +# +# General-purpose Microprocessors with RISC-V CPU Core (Andes AX45MP Single) (1.0 GHz) + +transport select jtag + +reset_config trst_and_srst srst_gates_jtag +adapter speed 4000 +adapter srst delay 500 + +if { [info exists CHIPNAME] } { + set _CHIPNAME $CHIPNAME +} else { + set _CHIPNAME r9A07g043u +} + +jtag newtap $_CHIPNAME cpu -irlen 5 -expected-id 0x1000563d + +set _TARGETNAME $_CHIPNAME.cpu +target create $_TARGETNAME riscv -chain-position $_TARGETNAME From ce5027ab019ac7f96d05ec5f8f533144b3bcafdf Mon Sep 17 00:00:00 2001 From: Erhan Kurubas Date: Tue, 5 Apr 2022 13:49:28 +0300 Subject: [PATCH 36/60] semihosting: add semihosting_basedir command This command allows users to set base working directory for the semihosting I/O operations.Default is the current OpenOCD directory. Signed-off-by: Erhan Kurubas Change-Id: I80c5979e4c96d66cccdd12cc6fcd5f353e5c6b4d Reviewed-on: https://review.openocd.org/c/openocd/+/6888 Tested-by: jenkins Reviewed-by: Antonio Borneo --- doc/openocd.texi | 6 ++++ src/target/semihosting_common.c | 56 +++++++++++++++++++++++++++++++-- src/target/semihosting_common.h | 3 ++ src/target/target.c | 3 ++ 4 files changed, 65 insertions(+), 3 deletions(-) diff --git a/doc/openocd.texi b/doc/openocd.texi index be9e5ac08..cc1d10441 100644 --- a/doc/openocd.texi +++ b/doc/openocd.texi @@ -9624,6 +9624,12 @@ is valid during the run of the event handlers and is accessible with this command. @end deffn +@deffn {Command} {arm semihosting_basedir} [dir] +@cindex ARM semihosting +Set the base directory for semihosting I/O, either an absolute path or a path relative to OpenOCD working directory. +Use "." for the current directory. +@end deffn + @section ARMv4 and ARMv5 Architecture @cindex ARMv4 @cindex ARMv5 diff --git a/src/target/semihosting_common.c b/src/target/semihosting_common.c index bc1f417ef..2df6e38ae 100644 --- a/src/target/semihosting_common.c +++ b/src/target/semihosting_common.c @@ -159,6 +159,7 @@ int semihosting_common_init(struct target *target, void *setup, semihosting->result = -1; semihosting->sys_errno = -1; semihosting->cmdline = NULL; + semihosting->basedir = NULL; /* If possible, update it in setup(). */ semihosting->setup_time = clock(); @@ -870,17 +871,21 @@ int semihosting_common(struct target *target) semihosting->sys_errno = EINVAL; break; } - uint8_t *fn = malloc(len+1); + size_t basedir_len = semihosting->basedir ? strlen(semihosting->basedir) : 0; + uint8_t *fn = malloc(basedir_len + len + 2); if (!fn) { semihosting->result = -1; semihosting->sys_errno = ENOMEM; } else { - retval = target_read_memory(target, addr, 1, len, fn); + strncpy((char *)fn, semihosting->basedir, basedir_len); + if (fn[basedir_len - 1] != '/') + fn[basedir_len++] = '/'; + retval = target_read_memory(target, addr, 1, len, fn + basedir_len); if (retval != ERROR_OK) { free(fn); return retval; } - fn[len] = 0; + fn[basedir_len + len] = 0; /* TODO: implement the :semihosting-features special file. * */ if (semihosting->is_fileio) { @@ -2025,6 +2030,44 @@ COMMAND_HANDLER(handle_common_semihosting_read_user_param_command) return ERROR_OK; } +COMMAND_HANDLER(handle_common_semihosting_basedir_command) +{ + struct target *target = get_current_target(CMD_CTX); + + if (CMD_ARGC > 1) + return ERROR_COMMAND_SYNTAX_ERROR; + + if (!target) { + LOG_ERROR("No target selected"); + return ERROR_FAIL; + } + + struct semihosting *semihosting = target->semihosting; + if (!semihosting) { + command_print(CMD, "semihosting not supported for current target"); + return ERROR_FAIL; + } + + if (!semihosting->is_active) { + command_print(CMD, "semihosting not yet enabled for current target"); + return ERROR_FAIL; + } + + if (CMD_ARGC > 0) { + free(semihosting->basedir); + semihosting->basedir = strdup(CMD_ARGV[0]); + if (!semihosting->basedir) { + command_print(CMD, "semihosting failed to allocate memory for basedir!"); + return ERROR_FAIL; + } + } + + command_print(CMD, "semihosting base dir: %s", + semihosting->basedir ? semihosting->basedir : ""); + + return ERROR_OK; +} + const struct command_registration semihosting_common_handlers[] = { { .name = "semihosting", @@ -2068,5 +2111,12 @@ const struct command_registration semihosting_common_handlers[] = { .usage = "", .help = "read parameters in semihosting-user-cmd-0x10X callbacks", }, + { + .name = "semihosting_basedir", + .handler = handle_common_semihosting_basedir_command, + .mode = COMMAND_EXEC, + .usage = "[dir]", + .help = "set the base directory for semihosting I/O operations", + }, COMMAND_REGISTRATION_DONE }; diff --git a/src/target/semihosting_common.h b/src/target/semihosting_common.h index 459faf656..404080f02 100644 --- a/src/target/semihosting_common.h +++ b/src/target/semihosting_common.h @@ -176,6 +176,9 @@ struct semihosting { /** The current time when 'execution starts' */ clock_t setup_time; + /** Base directory for semihosting I/O operations. */ + char *basedir; + int (*setup)(struct target *target, int enable); int (*post_result)(struct target *target); }; diff --git a/src/target/target.c b/src/target/target.c index 8a451883b..8edd63291 100644 --- a/src/target/target.c +++ b/src/target/target.c @@ -57,6 +57,7 @@ #include "transport/transport.h" #include "arm_cti.h" #include "smp.h" +#include "semihosting_common.h" /* default halt wait timeout (ms) */ #define DEFAULT_HALT_TIMEOUT 5000 @@ -2258,6 +2259,8 @@ static void target_destroy(struct target *target) if (target->type->deinit_target) target->type->deinit_target(target); + if (target->semihosting) + free(target->semihosting->basedir); free(target->semihosting); jtag_unregister_event_callback(jtag_enable_callback, target); From 0a36acbf6ac67cf6e95077aa25a001e2638f2798 Mon Sep 17 00:00:00 2001 From: Erhan Kurubas Date: Tue, 5 Apr 2022 17:19:24 +0300 Subject: [PATCH 37/60] configure: build jimtcl with json extension Signed-off-by: Erhan Kurubas Change-Id: I5845c240e50ee832c082df3f26fabbd4b14d6edd Reviewed-on: https://review.openocd.org/c/openocd/+/6890 Tested-by: jenkins Reviewed-by: Antonio Borneo --- configure.ac | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 95b2d50ae..7eada3a32 100644 --- a/configure.ac +++ b/configure.ac @@ -570,9 +570,9 @@ AS_IF([test "x$enable_buspirate" != "xno"], [ AS_IF([test "x$use_internal_jimtcl" = "xyes"], [ AS_IF([test -f "$srcdir/jimtcl/configure.ac"], [ AS_IF([test "x$use_internal_jimtcl_maintainer" = "xyes"], [ - jimtcl_config_options="--disable-install-jim --maintainer" + jimtcl_config_options="--disable-install-jim --with-ext=json --maintainer" ], [ - jimtcl_config_options="--disable-install-jim" + jimtcl_config_options="--disable-install-jim --with-ext=json" ]) AX_CONFIG_SUBDIR_OPTION([jimtcl], [$jimtcl_config_options]) ], [ From 32b3859258abb97248a52e4c65b1fbce7581fab5 Mon Sep 17 00:00:00 2001 From: Sean Anderson Date: Fri, 13 May 2022 11:03:41 -0400 Subject: [PATCH 38/60] tcl/target/ls1088: Break out common configuration Several Layerscape processors (LS1088A, LS2088A, LS2160A, and LS1028A) share a common architecture. Break out the common setup from the LS1088 config in preparation for adding the LS1028A. There's no official name for this series of processors, but NXP refers to them as "chassis generation 3" in U-Boot, so we'll go with that too. Signed-off-by: Sean Anderson Change-Id: Ic6f89f95c678101f54579bcaa5d79c5b67ddf50a Reviewed-on: https://review.openocd.org/c/openocd/+/6975 Tested-by: jenkins Reviewed-by: Antonio Borneo --- tcl/target/ls1088a.cfg | 57 ++------------------------------- tcl/target/lsch3_common.cfg | 63 +++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 55 deletions(-) create mode 100644 tcl/target/lsch3_common.cfg diff --git a/tcl/target/ls1088a.cfg b/tcl/target/ls1088a.cfg index f9ae9a134..193d6ddc7 100644 --- a/tcl/target/ls1088a.cfg +++ b/tcl/target/ls1088a.cfg @@ -13,62 +13,9 @@ if { [info exists DAP_TAPID] } { set _DAP_TAPID 0x5ba00477 } -jtag newtap $_CHIPNAME dap -irlen 4 -expected-id $_DAP_TAPID -dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.dap +set _CPUS 8 -target create $_CHIPNAME.axi mem_ap -dap $_CHIPNAME.dap -ap-num 1 - -set _CPU_BASE 0x81000000 -set _CPU_STRIDE 0x100000 -set _CPU_DBGOFF 0x10000 -set _CPU_CTIOFF 0x20000 - -set _TARGETS {} -for {set i 0} {$i < 8} {incr i} { - set _BASE [expr {$_CPU_BASE + $_CPU_STRIDE * $i}] - cti create $_CHIPNAME.cti$i -dap $_CHIPNAME.dap -ap-num 0 \ - -baseaddr [expr {$_BASE + $_CPU_CTIOFF}] - target create $_CHIPNAME.cpu$i aarch64 -dap $_CHIPNAME.dap \ - -cti $_CHIPNAME.cti$i -dbgbase [expr {$_BASE + $_CPU_DBGOFF}] \ - {*}[expr {$i ? "-coreid $i" : "-rtos hwthread" }] - lappend _TARGETS $_CHIPNAME.cpu$i -} - -target smp {*}$_TARGETS - -# Service processor -target create $_CHIPNAME.sp cortex_a -dap $_CHIPNAME.dap -ap-num 0 -dbgbase 0x80138000 - -# Normally you will not need to call this, but if you are using the hard-coded -# Reset Configuration Word (RCW) you will need to call this manually. The CPU's -# reset vector is 0, and the boot ROM at that location contains ARMv7-A 32-bit -# instructions. This will cause the CPU to almost immediately execute an -# illegal instruction. -# -# This code is idempotent; releasing a released CPU has no effect, although it -# will halt/resume the service processor. -add_help_text release_cpu "Release a cpu which is held off" -proc release_cpu {cpu} { - set RST_BRRL 0x1e60060 - - set old [target current] - targets $::_CHIPNAME.sp - set not_halted [string compare halted [$::_CHIPNAME.sp curstate]] - if {$not_halted} { - halt - } - - # Release the cpu; it will start executing something bogus - mem2array regs 32 $RST_BRRL 1 - mww $RST_BRRL [expr {$regs(0) | 1 << $cpu}] - - if {$not_halted} { - resume - } - targets $old -} - -targets $_CHIPNAME.cpu0 +source [find target/lsch3_common.cfg] # Seems to work OK in testing adapter speed 10000 diff --git a/tcl/target/lsch3_common.cfg b/tcl/target/lsch3_common.cfg new file mode 100644 index 000000000..f48d59b9d --- /dev/null +++ b/tcl/target/lsch3_common.cfg @@ -0,0 +1,63 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# This contains common configuration for NXP Layerscape chassis generation 3 + +if { ![info exists _CPUS] } { + error "_CPUS must be set to the number of cores" +} + +jtag newtap $_CHIPNAME dap -irlen 4 -expected-id $_DAP_TAPID +dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.dap + +target create $_CHIPNAME.axi mem_ap -dap $_CHIPNAME.dap -ap-num 1 + +set _CPU_BASE 0x81000000 +set _CPU_STRIDE 0x100000 +set _CPU_DBGOFF 0x10000 +set _CPU_CTIOFF 0x20000 + +set _TARGETS {} +for {set i 0} {$i < $_CPUS} {incr i} { + set _BASE [expr {$_CPU_BASE + $_CPU_STRIDE * $i}] + cti create $_CHIPNAME.cti$i -dap $_CHIPNAME.dap -ap-num 0 \ + -baseaddr [expr {$_BASE + $_CPU_CTIOFF}] + target create $_CHIPNAME.cpu$i aarch64 -dap $_CHIPNAME.dap \ + -cti $_CHIPNAME.cti$i -dbgbase [expr {$_BASE + $_CPU_DBGOFF}] \ + {*}[expr {$i ? "-coreid $i" : "-rtos hwthread" }] + lappend _TARGETS $_CHIPNAME.cpu$i +} + +target smp {*}$_TARGETS + +# Service processor +target create $_CHIPNAME.sp cortex_a -dap $_CHIPNAME.dap -ap-num 0 -dbgbase 0x80138000 + +# Normally you will not need to call this, but if you are using the hard-coded +# Reset Configuration Word (RCW) you will need to call this manually. The CPU's +# reset vector is 0, and the boot ROM at that location contains ARMv7-A 32-bit +# instructions. This will cause the CPU to almost immediately execute an +# illegal instruction. +# +# This code is idempotent; releasing a released CPU has no effect, although it +# will halt/resume the service processor. +add_help_text release_cpu "Release a cpu which is held off" +proc release_cpu {cpu} { + set RST_BRRL 0x1e60060 + + set old [target current] + targets $::_CHIPNAME.sp + set not_halted [string compare halted [$::_CHIPNAME.sp curstate]] + if {$not_halted} { + halt + } + + # Release the cpu; it will start executing something bogus + mem2array regs 32 $RST_BRRL 1 + mww $RST_BRRL [expr {$regs(0) | 1 << $cpu}] + + if {$not_halted} { + resume + } + targets $old +} + +targets $_CHIPNAME.cpu0 From 9d8b98da69f1cbbce3f8963be643cc2b4d07ff56 Mon Sep 17 00:00:00 2001 From: Sean Anderson Date: Fri, 13 May 2022 11:07:29 -0400 Subject: [PATCH 39/60] target: Add LS1028A The LS1028A is similar to the LS1088A, except that it has 2 CPUs (and different ethernet capabilities). From a JTAG perspective, all that's different is the number of CPUs and the TAPID. Signed-off-by: Sean Anderson Change-Id: Iba3a0ecfbf82cfcfeb7eea42d52121c3b9dc93a2 Reviewed-on: https://review.openocd.org/c/openocd/+/6976 Tested-by: jenkins Reviewed-by: Antonio Borneo --- tcl/target/ls1028a.cfg | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 tcl/target/ls1028a.cfg diff --git a/tcl/target/ls1028a.cfg b/tcl/target/ls1028a.cfg new file mode 100644 index 000000000..463ec7dda --- /dev/null +++ b/tcl/target/ls1028a.cfg @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# NXP LS1028A + +if { [info exists CHIPNAME] } { + set _CHIPNAME $CHIPNAME +} else { + set _CHIPNAME ls1028a +} + +if { [info exists DAP_TAPID] } { + set _DAP_TAPID $DAP_TAPID +} else { + set _DAP_TAPID 0x6ba00477 +} + +set _CPUS 2 + +source [find target/lsch3_common.cfg] From 390720c283992a0a6e8d7aea94924a15d3c8e900 Mon Sep 17 00:00:00 2001 From: Sean Anderson Date: Fri, 13 May 2022 11:12:58 -0400 Subject: [PATCH 40/60] tcl: Add support for Kontron SMARC-sAL28 This commit is adapted from [1]. [1] https://review.openocd.org/c/openocd/+/4999 Signed-off-by: Michael Walle Signed-off-by: Heiko Thiery [ adapted to use common configuration ] Signed-off-by: Sean Anderson Change-Id: I9a428371694e7864e03055b8de18a55a7843b8c2 Reviewed-on: https://review.openocd.org/c/openocd/+/6977 Tested-by: jenkins Reviewed-by: Antonio Borneo --- tcl/board/kontron_sl28.cfg | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 tcl/board/kontron_sl28.cfg diff --git a/tcl/board/kontron_sl28.cfg b/tcl/board/kontron_sl28.cfg new file mode 100644 index 000000000..9816f3802 --- /dev/null +++ b/tcl/board/kontron_sl28.cfg @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# Kontron SMARC-sAL28 + +transport select jtag +reset_config srst_only srst_nogate + +jtag newtap unknown0 tap -irlen 12 + +set _CPUS 2 +source [find target/ls1028a.cfg] + +source [find tcl/cpld/altera-epm240.cfg] + +adapter speed 2000 From bce93f6d513b373c1fbfb42a12cfeb2305d5e822 Mon Sep 17 00:00:00 2001 From: Tim Newsome Date: Mon, 16 May 2022 10:22:32 -0700 Subject: [PATCH 41/60] Give each SMP group a unique number. This helps e.g. if there are 8 cores, and cores 0--3 are in one SMP group while 4--7 are in another group. (And there are 2 gdb instances connected, one debugging the first group, and one the second.) Signed-off-by: Tim Newsome Change-Id: I7b6c9382eadf964529105eaf0411a42d48768668 Reviewed-on: https://review.openocd.org/c/openocd/+/6979 Tested-by: jenkins Reviewed-by: Antonio Borneo --- src/target/target.c | 4 +++- src/target/target.h | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/target/target.c b/src/target/target.c index 8edd63291..596364cdc 100644 --- a/src/target/target.c +++ b/src/target/target.c @@ -6444,6 +6444,7 @@ static int jim_target_smp(Jim_Interp *interp, int argc, Jim_Obj *const *argv) int i; const char *targetname; int retval, len; + static int smp_group = 1; struct target *target = NULL; struct target_list *head, *new; @@ -6475,9 +6476,10 @@ static int jim_target_smp(Jim_Interp *interp, int argc, Jim_Obj *const *argv) /* now parse the list of cpu and put the target in smp mode*/ foreach_smp_target(head, lh) { target = head->target; - target->smp = 1; + target->smp = smp_group; target->smp_targets = lh; } + smp_group++; if (target && target->rtos) retval = rtos_smp_init(target); diff --git a/src/target/target.h b/src/target/target.h index 1f1a35420..45ef96269 100644 --- a/src/target/target.h +++ b/src/target/target.h @@ -200,7 +200,7 @@ struct target { bool rtos_auto_detect; /* A flag that indicates that the RTOS has been specified as "auto" * and must be detected when symbols are offered */ struct backoff_timer backoff; - int smp; /* add some target attributes for smp support */ + int smp; /* Unique non-zero number for each SMP group */ struct list_head *smp_targets; /* list all targets in this smp group/cluster * The head of the list is shared between the * cluster, thus here there is a pointer */ From cc8b491856c9fee675732dc21a2307f5c9617f54 Mon Sep 17 00:00:00 2001 From: Tomas Vanek Date: Fri, 19 Nov 2021 22:03:44 +0100 Subject: [PATCH 42/60] flash/nor/core, target: don't ask for working mem if no target algo The command 'flash erase_check' showed the message 'Running slow fallback erase check - add working memory' even in the case the target didn't implement blank_check_memory. Change return code of target_blank_check_memory() in this case and sense it in default_flash_blank_check() and show a message without a request for working memory. Change-Id: I7cf9bf77742964b4f377c9ce48ca689e57d0882f Signed-off-by: Tomas Vanek Reviewed-on: https://review.openocd.org/c/openocd/+/6765 Tested-by: jenkins Reviewed-by: Tim Newsome --- src/flash/nor/core.c | 6 +++++- src/target/target.c | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/flash/nor/core.c b/src/flash/nor/core.c index 30d387ae0..f4ff5dfb3 100644 --- a/src/flash/nor/core.c +++ b/src/flash/nor/core.c @@ -429,7 +429,11 @@ int default_flash_blank_check(struct flash_bank *bank) bank->sectors[i].is_erased = block_array[i].result; retval = ERROR_OK; } else { - LOG_USER("Running slow fallback erase check - add working memory"); + if (retval == ERROR_NOT_IMPLEMENTED) + LOG_USER("Running slow fallback erase check"); + else + LOG_USER("Running slow fallback erase check - add working memory"); + retval = default_flash_mem_blank_check(bank); } free(block_array); diff --git a/src/target/target.c b/src/target/target.c index 596364cdc..76327b1c7 100644 --- a/src/target/target.c +++ b/src/target/target.c @@ -2590,7 +2590,7 @@ int target_blank_check_memory(struct target *target, } if (!target->type->blank_check_memory) - return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + return ERROR_NOT_IMPLEMENTED; return target->type->blank_check_memory(target, blocks, num_blocks, erased_value); } From 93e2c082d60586a765d925fb59604b315fbf9dc9 Mon Sep 17 00:00:00 2001 From: Tarek BOCHKATI Date: Mon, 23 May 2022 13:57:16 +0100 Subject: [PATCH 43/60] github workflow: use libusb 1.0.26 and hidapi 0.11.2 Change-Id: Id5348f86026330581d4bae081c9ab2bef435e6a6 Signed-off-by: Tarek BOCHKATI Reviewed-on: https://review.openocd.org/c/openocd/+/6992 Tested-by: jenkins Reviewed-by: Xiaofan Chen Reviewed-by: Antonio Borneo --- .github/workflows/snapshot.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/snapshot.yml b/.github/workflows/snapshot.yml index 96a2d34f2..0de0cf3f6 100644 --- a/.github/workflows/snapshot.yml +++ b/.github/workflows/snapshot.yml @@ -22,7 +22,7 @@ jobs: - run: ./bootstrap - name: Prepare libusb1 env: - LIBUSB1_VER: 1.0.24 + LIBUSB1_VER: 1.0.26 run: | mkdir -p $DL_DIR && cd $DL_DIR wget "https://github.com/libusb/libusb/releases/download/v${LIBUSB1_VER}/libusb-${LIBUSB1_VER}.tar.bz2" @@ -30,7 +30,7 @@ jobs: echo "LIBUSB1_SRC=$PWD/libusb-${LIBUSB1_VER}" >> $GITHUB_ENV - name: Prepare hidapi env: - HIDAPI_VER: 0.10.1 + HIDAPI_VER: 0.11.2 run: | mkdir -p $DL_DIR && cd $DL_DIR wget "https://github.com/libusb/hidapi/archive/hidapi-${HIDAPI_VER}.tar.gz" From 975fb2a4faaaabbacae16a468840dab4b3eb3618 Mon Sep 17 00:00:00 2001 From: Tarek BOCHKATI Date: Mon, 30 May 2022 14:48:06 +0100 Subject: [PATCH 44/60] github/workflow: enable libftdi based adapters Change-Id: I74b07b21573294dd7d9d3caf41c5755622c77149 Signed-off-by: Tarek BOCHKATI Reviewed-on: https://review.openocd.org/c/openocd/+/7008 Tested-by: jenkins Reviewed-by: Xiaofan Chen Reviewed-by: Antonio Borneo --- .github/workflows/snapshot.yml | 9 +++++++++ contrib/cross-build.sh | 14 ++++++++++---- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/.github/workflows/snapshot.yml b/.github/workflows/snapshot.yml index 0de0cf3f6..e5997a055 100644 --- a/.github/workflows/snapshot.yml +++ b/.github/workflows/snapshot.yml @@ -38,6 +38,14 @@ jobs: cd hidapi-hidapi-${HIDAPI_VER} ./bootstrap echo "HIDAPI_SRC=$PWD" >> $GITHUB_ENV + - name: Prepare libftdi + env: + LIBFTDI_VER: 1.5 + run: | + mkdir -p $DL_DIR && cd $DL_DIR + wget "http://www.intra2net.com/en/developer/libftdi/download/libftdi1-${LIBFTDI_VER}.tar.bz2" + tar -xjf libftdi1-${LIBFTDI_VER}.tar.bz2 + echo "LIBFTDI_SRC=$PWD/libftdi1-${LIBFTDI_VER}" >> $GITHUB_ENV - name: Prepare capstone env: CAPSTONE_VER: 4.0.2 @@ -54,6 +62,7 @@ jobs: HOST: i686-w64-mingw32 LIBUSB1_CONFIG: --enable-shared --disable-static HIDAPI_CONFIG: --enable-shared --disable-static --disable-testgui + LIBFTDI_CONFIG: -DSTATICLIBS=OFF -DEXAMPLES=OFF -DFTDI_EEPROM=OFF CAPSTONE_CONFIG: "CAPSTONE_BUILD_CORE_ONLY=yes CAPSTONE_STATIC=yes CAPSTONE_SHARED=no" run: | # check if there is tag pointing at HEAD, otherwise take the HEAD SHA-1 as OPENOCD_TAG diff --git a/contrib/cross-build.sh b/contrib/cross-build.sh index 7c572f55d..9328c3ab6 100755 --- a/contrib/cross-build.sh +++ b/contrib/cross-build.sh @@ -121,11 +121,17 @@ fi if [ -d $LIBFTDI_SRC ] ; then mkdir -p $LIBFTDI_BUILD_DIR cd $LIBFTDI_BUILD_DIR - # libftdi requires libusb1 static libraries, granted by: - # export LIBUSB1_CONFIG="--enable-static ..." + # note : libftdi versions < 1.5 requires libusb1 static + # hint use : # export LIBUSB1_CONFIG="--enable-static ..." + # not needed since libftdi-1.5 when LIBFTDI_CONFIG="-DSTATICLIBS=OFF ..." + + # fix .cmake file + ESCAPED_SYSROOT=$(printf '%s\n' "$SYSROOT" | sed -e 's/[\/&]/\\&/g') + sed -i -E "s/(SET\(CMAKE_FIND_ROOT_PATH\s+).+\)/\1${ESCAPED_SYSROOT})/" \ + ${LIBFTDI_SRC}/cmake/Toolchain-${HOST_TRIPLET}.cmake + cmake $LIBFTDI_CONFIG \ - -DLIBUSB_INCLUDE_DIR=${SYSROOT}${PREFIX}/include/libusb-1.0 \ - -DLIBUSB_LIBRARIES=${SYSROOT}${PREFIX}/lib/libusb-1.0.a \ + -DCMAKE_TOOLCHAIN_FILE=${LIBFTDI_SRC}/cmake/Toolchain-${HOST_TRIPLET}.cmake \ -DCMAKE_INSTALL_PREFIX=${PREFIX} \ -DPKG_CONFIG_EXECUTABLE=`which pkg-config` \ $LIBFTDI_SRC From b470b664ca7ba3f21684848c3819d696fb3c890a Mon Sep 17 00:00:00 2001 From: Tarek BOCHKATI Date: Thu, 14 Apr 2022 14:53:10 +0100 Subject: [PATCH 45/60] cortex_a: get rid of not needed log messages when using semi-hosting with cortex_a this LOG_INFO pollutes openocd console, so just reduce the log level of this message. Change-Id: I91aa70492f4e361b25a0e5517d0cf73f2f8ed599 Signed-off-by: Tarek BOCHKATI Reviewed-on: https://gerrit.st.com/c/stm32ide/official/openocd/+/248225 Tested-by: Tarek BOCHKATI Reviewed-by: Tarek BOCHKATI Reviewed-on: https://review.openocd.org/c/openocd/+/6993 Tested-by: jenkins Reviewed-by: Antonio Borneo --- src/target/armv7a.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/target/armv7a.c b/src/target/armv7a.c index d564f19ae..1e425aa51 100644 --- a/src/target/armv7a.c +++ b/src/target/armv7a.c @@ -112,7 +112,7 @@ static int armv7a_read_midr(struct target *target) armv7a->arch = (midr >> 16) & 0xf; armv7a->variant = (midr >> 20) & 0xf; armv7a->implementor = (midr >> 24) & 0xff; - LOG_INFO("%s rev %" PRIx32 ", partnum %" PRIx32 ", arch %" PRIx32 + LOG_DEBUG("%s rev %" PRIx32 ", partnum %" PRIx32 ", arch %" PRIx32 ", variant %" PRIx32 ", implementor %" PRIx32, target->cmd_name, armv7a->rev, From 78c87f5e81f8b3ee2a72aa546f87985596cb2b9f Mon Sep 17 00:00:00 2001 From: Erhan Kurubas Date: Thu, 21 Apr 2022 07:53:54 +0200 Subject: [PATCH 46/60] target: add Espressif ESP32-S2 basic support ESP32-S2 is a single core Xtensa chip. Not full featured yet. Some of the missing functionality: -Semihosting -Flash breakpoints -Flash loader -Apptrace -FreeRTOS Signed-off-by: Erhan Kurubas Change-Id: I2fb32978e801af5aa21616c581691406ad7cd6bb Reviewed-on: https://review.openocd.org/c/openocd/+/6940 Reviewed-by: Tomas Vanek Reviewed-by: Ian Thompson Reviewed-by: Antonio Borneo Tested-by: jenkins --- doc/openocd.texi | 89 + src/target/Makefile.am | 6 +- src/target/espressif/Makefile.am | 6 + src/target/espressif/esp32s2.c | 715 ++++++ src/target/espressif/esp32s2.h | 40 + src/target/espressif/esp_xtensa.c | 71 + src/target/espressif/esp_xtensa.h | 48 + src/target/target.c | 2 + src/target/xtensa/Makefile.am | 7 + src/target/xtensa/xtensa.c | 2731 ++++++++++++++++++++++ src/target/xtensa/xtensa.h | 309 +++ src/target/xtensa/xtensa_debug_module.c | 359 +++ src/target/xtensa/xtensa_debug_module.h | 385 +++ src/target/xtensa/xtensa_regs.h | 278 +++ tcl/board/esp32s2-kaluga-1.cfg | 18 + tcl/interface/ftdi/esp32s2_kaluga_v1.cfg | 29 + tcl/target/esp32s2.cfg | 30 + 17 files changed, 5122 insertions(+), 1 deletion(-) create mode 100644 src/target/espressif/Makefile.am create mode 100644 src/target/espressif/esp32s2.c create mode 100644 src/target/espressif/esp32s2.h create mode 100644 src/target/espressif/esp_xtensa.c create mode 100644 src/target/espressif/esp_xtensa.h create mode 100644 src/target/xtensa/Makefile.am create mode 100644 src/target/xtensa/xtensa.c create mode 100644 src/target/xtensa/xtensa.h create mode 100644 src/target/xtensa/xtensa_debug_module.c create mode 100644 src/target/xtensa/xtensa_debug_module.h create mode 100644 src/target/xtensa/xtensa_regs.h create mode 100644 tcl/board/esp32s2-kaluga-1.cfg create mode 100644 tcl/interface/ftdi/esp32s2_kaluga_v1.cfg create mode 100644 tcl/target/esp32s2.cfg diff --git a/doc/openocd.texi b/doc/openocd.texi index cc1d10441..85be06ea6 100644 --- a/doc/openocd.texi +++ b/doc/openocd.texi @@ -4895,6 +4895,7 @@ compact Thumb2 instruction set. Supports also ARMv6-M and ARMv8-M cores @item @code{dsp5680xx} -- implements Freescale's 5680x DSP. @item @code{esirisc} -- this is an EnSilica eSi-RISC core. The current implementation supports eSi-32xx cores. +@item @code{esp32s2} -- this is an Espressif SoC with single Xtensa core. @item @code{fa526} -- resembles arm920 (w/o Thumb). @item @code{feroceon} -- resembles arm926. @item @code{hla_target} -- a Cortex-M alternative to work with HL adapters like ST-Link. @@ -10956,6 +10957,94 @@ STMicroelectronics, based on a proprietary 8-bit core architecture. OpenOCD supports debugging STM8 through the STMicroelectronics debug protocol SWIM, @pxref{swimtransport,,SWIM}. +@section Xtensa Architecture +Xtensa processors are based on a modular, highly flexible 32-bit RISC architecture +that can easily scale from a tiny, cache-less controller or task engine to a high-performance +SIMD/VLIW DSP provided by Cadence. +@url{https://www.cadence.com/en_US/home/tools/ip/tensilica-ip/tensilica-xtensa-controllers-and-extensible-processors.html}. + +OpenOCD supports generic Xtensa processors implementation which can be customized by +simply providing vendor-specific core configuration which controls every configurable +Xtensa architecture option, e.g. number of address registers, exceptions, reduced +size instructions support, memory banks configuration etc. Also OpenOCD supports SMP +configurations for Xtensa processors with any number of cores and allows to configure +their debug signals interconnection (so-called "break/stall networks") which control how +debug signals are distributed among cores. Xtensa "break networks" are compatible with +ARM's Cross Trigger Interface (CTI). For debugging code on Xtensa chips OpenOCD +uses JTAG protocol. Currently OpenOCD implements several Epsressif Xtensa-based chips of +@uref{https://www.espressif.com/en/products/socs, ESP32 family}. + +@subsection General Xtensa Commands + +@deffn {Command} {xtensa set_permissive} (0|1) +By default accessing memory beyond defined regions is forbidden. This commnd controls memory access address check. +When set to (1), skips access controls and address range check before read/write memory. +@end deffn + +@deffn {Command} {xtensa maskisr} (on|off) +Selects whether interrupts will be disabled during stepping over single instruction. The default configuration is (off). +@end deffn + +@deffn {Command} {xtensa smpbreak} [none|breakinout|runstall] | [BreakIn] [BreakOut] [RunStallIn] [DebugModeOut] +Configures debug signals connection ("break network") for currently selected core. +@itemize @bullet +@item @code{none} - Core's "break/stall network" is disconnected. Core is not affected by any debug +signal from other cores. +@item @code{breakinout} - Core's "break network" is fully connected (break inputs and outputs are enabled). +Core will receive debug break signals from other cores and send such signals to them. For example when another core +is stopped due to breakpoint hit this core will be stopped too and vice versa. +@item @code{runstall} - Core's "stall network" is fully connected (stall inputs and outputs are enabled). +This feature is not well implemented and tested yet. +@item @code{BreakIn} - Core's "break-in" signal is enabled. +Core will receive debug break signals from other cores. For example when another core is +stopped due to breakpoint hit this core will be stopped too. +@item @code{BreakOut} - Core's "break-out" signal is enabled. +Core will send debug break signal to other cores. For example when this core is +stopped due to breakpoint hit other cores with enabled break-in signals will be stopped too. +@item @code{RunStallIn} - Core's "runstall-in" signal is enabled. +This feature is not well implemented and tested yet. +@item @code{DebugModeOut} - Core's "debugmode-out" signal is enabled. +This feature is not well implemented and tested yet. +@end itemize +@end deffn + +@deffn {Command} {xtensa perfmon_enable} [mask] [kernelcnt] [tracelevel] */ +COMMAND_HELPER(xtensa_cmd_perfmon_enable_do, struct xtensa *xtensa) +{ + struct xtensa_perfmon_config config = { + .mask = 0xffff, + .kernelcnt = 0, + .tracelevel = -1 /* use DEBUGLEVEL by default */ + }; + + if (CMD_ARGC < 2 || CMD_ARGC > 6) + return ERROR_COMMAND_SYNTAX_ERROR; + + unsigned int counter_id = strtoul(CMD_ARGV[0], NULL, 0); + if (counter_id >= XTENSA_MAX_PERF_COUNTERS) { + command_print(CMD, "counter_id should be < %d", XTENSA_MAX_PERF_COUNTERS); + return ERROR_COMMAND_ARGUMENT_INVALID; + } + + config.select = strtoul(CMD_ARGV[1], NULL, 0); + if (config.select > XTENSA_MAX_PERF_SELECT) { + command_print(CMD, "select should be < %d", XTENSA_MAX_PERF_SELECT); + return ERROR_COMMAND_ARGUMENT_INVALID; + } + + if (CMD_ARGC >= 3) { + config.mask = strtoul(CMD_ARGV[2], NULL, 0); + if (config.mask > XTENSA_MAX_PERF_MASK) { + command_print(CMD, "mask should be < %d", XTENSA_MAX_PERF_MASK); + return ERROR_COMMAND_ARGUMENT_INVALID; + } + } + + if (CMD_ARGC >= 4) { + config.kernelcnt = strtoul(CMD_ARGV[3], NULL, 0); + if (config.kernelcnt > 1) { + command_print(CMD, "kernelcnt should be 0 or 1"); + return ERROR_COMMAND_ARGUMENT_INVALID; + } + } + + if (CMD_ARGC >= 5) { + config.tracelevel = strtoul(CMD_ARGV[4], NULL, 0); + if (config.tracelevel > 7) { + command_print(CMD, "tracelevel should be <=7"); + return ERROR_COMMAND_ARGUMENT_INVALID; + } + } + + if (config.tracelevel == -1) + config.tracelevel = xtensa->core_config->debug.irq_level; + + return xtensa_dm_perfmon_enable(&xtensa->dbg_mod, counter_id, &config); +} + +COMMAND_HANDLER(xtensa_cmd_perfmon_enable) +{ + return CALL_COMMAND_HANDLER(xtensa_cmd_perfmon_enable_do, + target_to_xtensa(get_current_target(CMD_CTX))); +} + +/* perfmon_dump [counter_id] */ +COMMAND_HELPER(xtensa_cmd_perfmon_dump_do, struct xtensa *xtensa) +{ + if (CMD_ARGC > 1) + return ERROR_COMMAND_SYNTAX_ERROR; + + int counter_id = -1; + if (CMD_ARGC == 1) { + counter_id = strtol(CMD_ARGV[0], NULL, 0); + if (counter_id > XTENSA_MAX_PERF_COUNTERS) { + command_print(CMD, "counter_id should be < %d", XTENSA_MAX_PERF_COUNTERS); + return ERROR_COMMAND_ARGUMENT_INVALID; + } + } + + unsigned int counter_start = (counter_id < 0) ? 0 : counter_id; + unsigned int counter_end = (counter_id < 0) ? XTENSA_MAX_PERF_COUNTERS : counter_id + 1; + for (unsigned int counter = counter_start; counter < counter_end; ++counter) { + char result_buf[128] = { 0 }; + size_t result_pos = snprintf(result_buf, sizeof(result_buf), "Counter %d: ", counter); + struct xtensa_perfmon_result result; + int res = xtensa_dm_perfmon_dump(&xtensa->dbg_mod, counter, &result); + if (res != ERROR_OK) + return res; + snprintf(result_buf + result_pos, sizeof(result_buf) - result_pos, + "%-12" PRIu64 "%s", + result.value, + result.overflow ? " (overflow)" : ""); + LOG_INFO("%s", result_buf); + } + + return ERROR_OK; +} + +COMMAND_HANDLER(xtensa_cmd_perfmon_dump) +{ + return CALL_COMMAND_HANDLER(xtensa_cmd_perfmon_dump_do, + target_to_xtensa(get_current_target(CMD_CTX))); +} + +COMMAND_HELPER(xtensa_cmd_mask_interrupts_do, struct xtensa *xtensa) +{ + int state = -1; + + if (CMD_ARGC < 1) { + const char *st; + state = xtensa->stepping_isr_mode; + if (state == XT_STEPPING_ISR_ON) + st = "OFF"; + else if (state == XT_STEPPING_ISR_OFF) + st = "ON"; + else + st = "UNKNOWN"; + command_print(CMD, "Current ISR step mode: %s", st); + return ERROR_OK; + } + /* Masking is ON -> interrupts during stepping are OFF, and vice versa */ + if (!strcasecmp(CMD_ARGV[0], "off")) + state = XT_STEPPING_ISR_ON; + else if (!strcasecmp(CMD_ARGV[0], "on")) + state = XT_STEPPING_ISR_OFF; + + if (state == -1) { + command_print(CMD, "Argument unknown. Please pick one of ON, OFF"); + return ERROR_FAIL; + } + xtensa->stepping_isr_mode = state; + return ERROR_OK; +} + +COMMAND_HANDLER(xtensa_cmd_mask_interrupts) +{ + return CALL_COMMAND_HANDLER(xtensa_cmd_mask_interrupts_do, + target_to_xtensa(get_current_target(CMD_CTX))); +} + +COMMAND_HELPER(xtensa_cmd_smpbreak_do, struct target *target) +{ + int res = ERROR_OK; + uint32_t val = 0; + + if (CMD_ARGC >= 1) { + for (unsigned int i = 0; i < CMD_ARGC; i++) { + if (!strcasecmp(CMD_ARGV[0], "none")) { + val = 0; + } else if (!strcasecmp(CMD_ARGV[i], "BreakIn")) { + val |= OCDDCR_BREAKINEN; + } else if (!strcasecmp(CMD_ARGV[i], "BreakOut")) { + val |= OCDDCR_BREAKOUTEN; + } else if (!strcasecmp(CMD_ARGV[i], "RunStallIn")) { + val |= OCDDCR_RUNSTALLINEN; + } else if (!strcasecmp(CMD_ARGV[i], "DebugModeOut")) { + val |= OCDDCR_DEBUGMODEOUTEN; + } else if (!strcasecmp(CMD_ARGV[i], "BreakInOut")) { + val |= OCDDCR_BREAKINEN | OCDDCR_BREAKOUTEN; + } else if (!strcasecmp(CMD_ARGV[i], "RunStall")) { + val |= OCDDCR_RUNSTALLINEN | OCDDCR_DEBUGMODEOUTEN; + } else { + command_print(CMD, "Unknown arg %s", CMD_ARGV[i]); + command_print( + CMD, + "use either BreakInOut, None or RunStall as arguments, or any combination of BreakIn, BreakOut, RunStallIn and DebugModeOut."); + return ERROR_OK; + } + } + res = xtensa_smpbreak_set(target, val); + if (res != ERROR_OK) + command_print(CMD, "Failed to set smpbreak config %d", res); + } else { + struct xtensa *xtensa = target_to_xtensa(target); + res = xtensa_smpbreak_read(xtensa, &val); + if (res == ERROR_OK) { + command_print(CMD, "Current bits set:%s%s%s%s", + (val & OCDDCR_BREAKINEN) ? " BreakIn" : "", + (val & OCDDCR_BREAKOUTEN) ? " BreakOut" : "", + (val & OCDDCR_RUNSTALLINEN) ? " RunStallIn" : "", + (val & OCDDCR_DEBUGMODEOUTEN) ? " DebugModeOut" : "" + ); + } else { + command_print(CMD, "Failed to get smpbreak config %d", res); + } + } + return res; +} + +COMMAND_HANDLER(xtensa_cmd_smpbreak) +{ + return CALL_COMMAND_HANDLER(xtensa_cmd_smpbreak_do, + get_current_target(CMD_CTX)); +} + +COMMAND_HELPER(xtensa_cmd_tracestart_do, struct xtensa *xtensa) +{ + struct xtensa_trace_status trace_status; + struct xtensa_trace_start_config cfg = { + .stoppc = 0, + .stopmask = XTENSA_STOPMASK_DISABLED, + .after = 0, + .after_is_words = false + }; + + /* Parse arguments */ + for (unsigned int i = 0; i < CMD_ARGC; i++) { + if ((!strcasecmp(CMD_ARGV[i], "pc")) && CMD_ARGC > i) { + char *e; + i++; + cfg.stoppc = strtol(CMD_ARGV[i], &e, 0); + cfg.stopmask = 0; + if (*e == '/') + cfg.stopmask = strtol(e, NULL, 0); + } else if ((!strcasecmp(CMD_ARGV[i], "after")) && CMD_ARGC > i) { + i++; + cfg.after = strtol(CMD_ARGV[i], NULL, 0); + } else if (!strcasecmp(CMD_ARGV[i], "ins")) { + cfg.after_is_words = 0; + } else if (!strcasecmp(CMD_ARGV[i], "words")) { + cfg.after_is_words = 1; + } else { + command_print(CMD, "Did not understand %s", CMD_ARGV[i]); + return ERROR_FAIL; + } + } + + int res = xtensa_dm_trace_status_read(&xtensa->dbg_mod, &trace_status); + if (res != ERROR_OK) + return res; + if (trace_status.stat & TRAXSTAT_TRACT) { + LOG_WARNING("Silently stop active tracing!"); + res = xtensa_dm_trace_stop(&xtensa->dbg_mod, false); + if (res != ERROR_OK) + return res; + } + + res = xtensa_dm_trace_start(&xtensa->dbg_mod, &cfg); + if (res != ERROR_OK) + return res; + + xtensa->trace_active = true; + command_print(CMD, "Trace started."); + return ERROR_OK; +} + +COMMAND_HANDLER(xtensa_cmd_tracestart) +{ + return CALL_COMMAND_HANDLER(xtensa_cmd_tracestart_do, + target_to_xtensa(get_current_target(CMD_CTX))); +} + +COMMAND_HELPER(xtensa_cmd_tracestop_do, struct xtensa *xtensa) +{ + struct xtensa_trace_status trace_status; + + int res = xtensa_dm_trace_status_read(&xtensa->dbg_mod, &trace_status); + if (res != ERROR_OK) + return res; + + if (!(trace_status.stat & TRAXSTAT_TRACT)) { + command_print(CMD, "No trace is currently active."); + return ERROR_FAIL; + } + + res = xtensa_dm_trace_stop(&xtensa->dbg_mod, true); + if (res != ERROR_OK) + return res; + + xtensa->trace_active = false; + command_print(CMD, "Trace stop triggered."); + return ERROR_OK; +} + +COMMAND_HANDLER(xtensa_cmd_tracestop) +{ + return CALL_COMMAND_HANDLER(xtensa_cmd_tracestop_do, + target_to_xtensa(get_current_target(CMD_CTX))); +} + +COMMAND_HELPER(xtensa_cmd_tracedump_do, struct xtensa *xtensa, const char *fname) +{ + struct xtensa_trace_config trace_config; + struct xtensa_trace_status trace_status; + uint32_t memsz, wmem; + + int res = xtensa_dm_trace_status_read(&xtensa->dbg_mod, &trace_status); + if (res != ERROR_OK) + return res; + + if (trace_status.stat & TRAXSTAT_TRACT) { + command_print(CMD, "Tracing is still active. Please stop it first."); + return ERROR_FAIL; + } + + res = xtensa_dm_trace_config_read(&xtensa->dbg_mod, &trace_config); + if (res != ERROR_OK) + return res; + + if (!(trace_config.ctrl & TRAXCTRL_TREN)) { + command_print(CMD, "No active trace found; nothing to dump."); + return ERROR_FAIL; + } + + memsz = trace_config.memaddr_end - trace_config.memaddr_start + 1; + LOG_INFO("Total trace memory: %d words", memsz); + if ((trace_config.addr & + ((TRAXADDR_TWRAP_MASK << TRAXADDR_TWRAP_SHIFT) | TRAXADDR_TWSAT)) == 0) { + /*Memory hasn't overwritten itself yet. */ + wmem = trace_config.addr & TRAXADDR_TADDR_MASK; + LOG_INFO("...but trace is only %d words", wmem); + if (wmem < memsz) + memsz = wmem; + } else { + if (trace_config.addr & TRAXADDR_TWSAT) { + LOG_INFO("Real trace is many times longer than that (overflow)"); + } else { + uint32_t trc_sz = (trace_config.addr >> TRAXADDR_TWRAP_SHIFT) & TRAXADDR_TWRAP_MASK; + trc_sz = (trc_sz * memsz) + (trace_config.addr & TRAXADDR_TADDR_MASK); + LOG_INFO("Real trace is %d words, but the start has been truncated.", trc_sz); + } + } + + uint8_t *tracemem = malloc(memsz * 4); + if (!tracemem) { + command_print(CMD, "Failed to alloc memory for trace data!"); + return ERROR_FAIL; + } + res = xtensa_dm_trace_data_read(&xtensa->dbg_mod, tracemem, memsz * 4); + if (res != ERROR_OK) { + free(tracemem); + return res; + } + + int f = open(fname, O_WRONLY | O_CREAT | O_TRUNC, 0666); + if (f <= 0) { + free(tracemem); + command_print(CMD, "Unable to open file %s", fname); + return ERROR_FAIL; + } + if (write(f, tracemem, memsz * 4) != (int)memsz * 4) + command_print(CMD, "Unable to write to file %s", fname); + else + command_print(CMD, "Written %d bytes of trace data to %s", memsz * 4, fname); + close(f); + + bool is_all_zeroes = true; + for (unsigned int i = 0; i < memsz * 4; i++) { + if (tracemem[i] != 0) { + is_all_zeroes = false; + break; + } + } + free(tracemem); + if (is_all_zeroes) + command_print( + CMD, + "WARNING: File written is all zeroes. Are you sure you enabled trace memory?"); + + return ERROR_OK; +} + +COMMAND_HANDLER(xtensa_cmd_tracedump) +{ + if (CMD_ARGC != 1) { + command_print(CMD, "Command takes exactly 1 parameter.Need filename to dump to as output!"); + return ERROR_FAIL; + } + + return CALL_COMMAND_HANDLER(xtensa_cmd_tracedump_do, + target_to_xtensa(get_current_target(CMD_CTX)), CMD_ARGV[0]); +} + +const struct command_registration xtensa_command_handlers[] = { + { + .name = "set_permissive", + .handler = xtensa_cmd_permissive_mode, + .mode = COMMAND_ANY, + .help = "When set to 1, enable Xtensa permissive mode (less client-side checks)", + .usage = "[0|1]", + }, + { + .name = "maskisr", + .handler = xtensa_cmd_mask_interrupts, + .mode = COMMAND_ANY, + .help = "mask Xtensa interrupts at step", + .usage = "['on'|'off']", + }, + { + .name = "smpbreak", + .handler = xtensa_cmd_smpbreak, + .mode = COMMAND_ANY, + .help = "Set the way the CPU chains OCD breaks", + .usage = + "[none|breakinout|runstall] | [BreakIn] [BreakOut] [RunStallIn] [DebugModeOut]", + }, + { + .name = "perfmon_enable", + .handler = xtensa_cmd_perfmon_enable, + .mode = COMMAND_EXEC, + .help = "Enable and start performance counter", + .usage = "