jtag/drivers: Add dmem driver 88/7088/8
authorNishanth Menon <nm@ti.com>
Thu, 14 Jul 2022 21:37:54 +0000 (16:37 -0500)
committerAntonio Borneo <borneo.antonio@gmail.com>
Sat, 26 Aug 2023 11:42:28 +0000 (11:42 +0000)
Direct memory driver support for CoreSight Access Port(AP).

Even though we emulate SWD (serial wire debug), we aren't actually
using swd. Instead, we are using a direct memory access to get to the
register set. This is similar in approach to other fast access native
drivers such as am335xgpio drivers.

Example operation on Texas Instrument's AM62x K3 SoC:

+-----------+
|  OpenOCD  |   SoC mem map
|    on     |--------------+
| Cortex-A53|              |
+-----------+              |
                           |
+-----------+        +-----v-----+
|Cortex-M4F |<───────|           |
+-----------+        |           |
                     |  DebugSS  |
+-----------+        |           |
|Cortex-M4F |<───────|           |
+-----------+        +-----------+

Signed-off-by: Nishanth Menon <nm@ti.com>
Signed-off-by: Jason Peck <jpeck@ti.com>
Change-Id: I8470cb15348863dd844b2c0e3f63a9063cb032c6
Reviewed-on: https://review.openocd.org/c/openocd/+/7088
Tested-by: jenkins
Reviewed-by: Antonio Borneo <borneo.antonio@gmail.com>
configure.ac
doc/openocd.texi
src/jtag/drivers/Makefile.am
src/jtag/drivers/dmem.c [new file with mode: 0644]
src/jtag/interface.h
src/jtag/interfaces.c

index ecf8384bf8fd871c052b549de4d1c72963f1b4c6..a2442d40b5bd1ae74bb99d70717508a41041234e 100644 (file)
@@ -242,6 +242,10 @@ AC_ARG_ENABLE([rshim],
   AS_HELP_STRING([--enable-rshim], [Enable building the rshim driver]),
   [build_rshim=$enableval], [build_rshim=no])
 
+AC_ARG_ENABLE([dmem],
+  AS_HELP_STRING([--enable-dmem], [Enable building the dmem driver]),
+  [build_dmem=$enableval], [build_dmem=no])
+
 m4_define([AC_ARG_ADAPTERS], [
   m4_foreach([adapter], [$1],
        [AC_ARG_ENABLE(ADAPTER_OPT([adapter]),
@@ -358,6 +362,10 @@ AS_CASE([$host_os],
         AC_MSG_ERROR([build_rshim is only available on linux or freebsd])
       ])
     ])
+
+    AS_IF([test "x$build_dmem" = "xyes"], [
+      AC_MSG_ERROR([dmem is only available on linux])
+    ])
 ])
 
 AC_ARG_ENABLE([internal-jimtcl],
@@ -478,6 +486,12 @@ AS_IF([test "x$build_rshim" = "xyes"], [
   AC_DEFINE([BUILD_RSHIM], [0], [0 if you don't want to debug BlueField SoC via rshim.])
 ])
 
+AS_IF([test "x$build_dmem" = "xyes"], [
+  AC_DEFINE([BUILD_DMEM], [1], [1 if you want to debug via Direct Mem.])
+], [
+  AC_DEFINE([BUILD_DMEM], [0], [0 if you don't want to debug via Direct Mem.])
+])
+
 AS_IF([test "x$build_dummy" = "xyes"], [
   build_bitbang=yes
   AC_DEFINE([BUILD_DUMMY], [1], [1 if you want dummy driver.])
@@ -754,6 +768,7 @@ AM_CONDITIONAL([USE_LIBGPIOD], [test "x$use_libgpiod" = "xyes"])
 AM_CONDITIONAL([USE_HIDAPI], [test "x$use_hidapi" = "xyes"])
 AM_CONDITIONAL([USE_LIBJAYLINK], [test "x$use_libjaylink" = "xyes"])
 AM_CONDITIONAL([RSHIM], [test "x$build_rshim" = "xyes"])
+AM_CONDITIONAL([DMEM], [test "x$build_dmem" = "xyes"])
 AM_CONDITIONAL([HAVE_CAPSTONE], [test "x$enable_capstone" != "xno"])
 
 AM_CONDITIONAL([INTERNAL_JIMTCL], [test "x$use_internal_jimtcl" = "xyes"])
index 3348e472b0c9aa20e33dc3e7e7a96b153959296f..6f16c9f70906e915d510dba375ecd31b9605cd06 100644 (file)
@@ -3568,6 +3568,66 @@ espusbjtag chip_id 1
 
 @end deffn
 
+@deffn {Interface Driver} {dmem} Direct Memory access debug interface
+
+The Texas Instruments K3 SoC family provides memory access to DAP
+and coresight control registers. This allows control over the
+microcontrollers directly from one of the processors on the SOC
+itself.
+
+For maximum performance, the driver accesses the debug registers
+directly over the SoC memory map. The memory mapping requires read
+and write permission to kernel memory via "/dev/mem" and assumes that
+the system firewall configurations permit direct access to the debug
+memory space.
+
+@verbatim
++-----------+
+|  OpenOCD  |   SoC mem map (/dev/mem)
+|    on     +--------------+
+| Cortex-A53|              |
++-----------+              |
+                           |
++-----------+        +-----v-----+
+|Cortex-M4F <--------+           |
++-----------+        |           |
+                     |  DebugSS  |
++-----------+        |           |
+|Cortex-M4F <--------+           |
++-----------+        +-----------+
+@end verbatim
+
+NOTE: Firewalls are configurable in K3 SoC and depending on various types of
+device configuration, this function may be blocked out. Typical behavior
+observed in such cases is a firewall exception report on the security
+controller and armv8 processor reporting a system error.
+
+See @file{tcl/interface/ti_k3_am625-swd-native.cfg} for a sample configuration
+file.
+
+@deffn {Command} {dmem info}
+Print the DAPBUS dmem configuration.
+@end deffn
+
+@deffn {Config Command} {dmem device} device_path
+Set the DAPBUS memory access device (default: /dev/mem).
+@end deffn
+
+@deffn {Config Command} {dmem base_address} base_address
+Set the DAPBUS base address which is used to access CoreSight
+compliant Access Ports (APs) directly.
+@end deffn
+
+@deffn {Config Command} {dmem ap_address_offset} offset_address
+Set the address offset between Access Ports (APs).
+@end deffn
+
+@deffn {Config Command} {dmem max_aps} n
+Set the maximum number of valid access ports on the SoC.
+@end deffn
+
+@end deffn
+
 @section Transport Configuration
 @cindex Transport
 As noted earlier, depending on the version of OpenOCD you use,
index 4b2dbc44db24203defbf9904a0ee04ed82bb3066..e404afe9f04c383d1088f7371f95a53011385671 100644 (file)
@@ -161,6 +161,9 @@ endif
 if RSHIM
 DRIVERFILES += %D%/rshim.c
 endif
+if DMEM
+DRIVERFILES += %D%/dmem.c
+endif
 if OSBDM
 DRIVERFILES += %D%/osbdm.c
 endif
diff --git a/src/jtag/drivers/dmem.c b/src/jtag/drivers/dmem.c
new file mode 100644 (file)
index 0000000..8d603ad
--- /dev/null
@@ -0,0 +1,342 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+/* Copyright (C) 2022 Texas Instruments Incorporated - https://www.ti.com/ */
+
+/**
+ * @file
+ * This file implements support for the Direct memory access to CoreSight
+ * Access Ports (APs) or emulate the same to access CoreSight debug registers
+ * directly.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/mman.h>
+
+#include <helper/align.h>
+#include <helper/types.h>
+#include <helper/system.h>
+#include <helper/time_support.h>
+#include <helper/list.h>
+#include <jtag/interface.h>
+
+#include <target/arm_adi_v5.h>
+#include <transport/transport.h>
+
+static void *dmem_map_base, *dmem_virt_base_addr;
+static size_t dmem_mapped_size;
+
+/* Default dmem device. */
+#define DMEM_DEV_PATH_DEFAULT   "/dev/mem"
+static char *dmem_dev_path;
+static uint64_t dmem_dap_base_address;
+static unsigned int dmem_dap_max_aps = 1;
+static uint32_t dmem_dap_ap_offset = 0x100;
+
+/* AP MODE */
+static uint32_t dmem_get_ap_reg_offset(struct adiv5_ap *ap, unsigned int reg)
+{
+       return (dmem_dap_ap_offset * ap->ap_num) + reg;
+}
+
+static void dmem_set_ap_reg(struct adiv5_ap *ap, unsigned int reg, uint32_t val)
+{
+       *(volatile uint32_t *)((uintptr_t)dmem_virt_base_addr +
+                                                  dmem_get_ap_reg_offset(ap, reg)) = val;
+}
+
+static uint32_t dmem_get_ap_reg(struct adiv5_ap *ap, unsigned int reg)
+{
+       return *(volatile uint32_t *)((uintptr_t)dmem_virt_base_addr +
+                                                                 dmem_get_ap_reg_offset(ap, reg));
+}
+
+static int dmem_dp_q_read(struct adiv5_dap *dap, unsigned int reg, uint32_t *data)
+{
+       if (!data)
+               return ERROR_OK;
+
+       switch (reg) {
+               case DP_CTRL_STAT:
+                       *data = CDBGPWRUPACK | CSYSPWRUPACK;
+                       break;
+
+               default:
+                       *data = 0;
+                       break;
+       }
+
+       return ERROR_OK;
+}
+
+static int dmem_dp_q_write(struct adiv5_dap *dap, unsigned int reg, uint32_t data)
+{
+       return ERROR_OK;
+}
+
+static int dmem_ap_q_read(struct adiv5_ap *ap, unsigned int reg, uint32_t *data)
+{
+       if (is_adiv6(ap->dap)) {
+               static bool error_flagged;
+
+               if (!error_flagged)
+                       LOG_ERROR("ADIv6 dap not supported by dmem dap-direct mode");
+
+               error_flagged = true;
+
+               return ERROR_FAIL;
+       }
+
+       *data = dmem_get_ap_reg(ap, reg);
+
+       return ERROR_OK;
+}
+
+static int dmem_ap_q_write(struct adiv5_ap *ap, unsigned int reg, uint32_t data)
+{
+       if (is_adiv6(ap->dap)) {
+               static bool error_flagged;
+
+               if (!error_flagged)
+                       LOG_ERROR("ADIv6 dap not supported by dmem dap-direct mode");
+
+               error_flagged = true;
+
+               return ERROR_FAIL;
+       }
+
+       dmem_set_ap_reg(ap, reg, data);
+
+       return ERROR_OK;
+}
+
+static int dmem_ap_q_abort(struct adiv5_dap *dap, uint8_t *ack)
+{
+       return ERROR_OK;
+}
+
+static int dmem_dp_run(struct adiv5_dap *dap)
+{
+       return ERROR_OK;
+}
+
+static int dmem_connect(struct adiv5_dap *dap)
+{
+       return ERROR_OK;
+}
+
+COMMAND_HANDLER(dmem_dap_device_command)
+{
+       if (CMD_ARGC != 1)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       free(dmem_dev_path);
+       dmem_dev_path = strdup(CMD_ARGV[0]);
+
+       return ERROR_OK;
+}
+
+COMMAND_HANDLER(dmem_dap_base_address_command)
+{
+       if (CMD_ARGC != 1)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       COMMAND_PARSE_NUMBER(u64, CMD_ARGV[0], dmem_dap_base_address);
+
+       return ERROR_OK;
+}
+
+COMMAND_HANDLER(dmem_dap_max_aps_command)
+{
+       if (CMD_ARGC != 1)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], dmem_dap_max_aps);
+
+       return ERROR_OK;
+}
+
+COMMAND_HANDLER(dmem_dap_ap_offset_command)
+{
+       if (CMD_ARGC != 1)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], dmem_dap_ap_offset);
+
+       return ERROR_OK;
+}
+
+COMMAND_HANDLER(dmem_dap_config_info_command)
+{
+       if (CMD_ARGC != 0)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       command_print(CMD, "dmem (Direct Memory) AP Adapter Configuration:");
+       command_print(CMD, " Device       : %s",
+                                 dmem_dev_path ? dmem_dev_path : DMEM_DEV_PATH_DEFAULT);
+       command_print(CMD, " Base Address : 0x%" PRIx64, dmem_dap_base_address);
+       command_print(CMD, " Max APs      : %u", dmem_dap_max_aps);
+       command_print(CMD, " AP offset    : 0x%08" PRIx32, dmem_dap_ap_offset);
+
+       return ERROR_OK;
+}
+
+static const struct command_registration dmem_dap_subcommand_handlers[] = {
+       {
+               .name = "info",
+               .handler = dmem_dap_config_info_command,
+               .mode = COMMAND_ANY,
+               .help = "print the config info",
+               .usage = "",
+       },
+       {
+               .name = "device",
+               .handler = dmem_dap_device_command,
+               .mode = COMMAND_CONFIG,
+               .help = "set the dmem memory access device (default: /dev/mem)",
+               .usage = "device_path",
+       },
+       {
+               .name = "base_address",
+               .handler = dmem_dap_base_address_command,
+               .mode = COMMAND_CONFIG,
+               .help = "set the dmem dap AP memory map base address",
+               .usage = "base_address",
+       },
+       {
+               .name = "ap_address_offset",
+               .handler = dmem_dap_ap_offset_command,
+               .mode = COMMAND_CONFIG,
+               .help = "set the offsets of each ap index",
+               .usage = "offset_address",
+       },
+       {
+               .name = "max_aps",
+               .handler = dmem_dap_max_aps_command,
+               .mode = COMMAND_CONFIG,
+               .help = "set the maximum number of APs this will support",
+               .usage = "n",
+       },
+       COMMAND_REGISTRATION_DONE
+};
+
+static const struct command_registration dmem_dap_command_handlers[] = {
+       {
+               .name = "dmem",
+               .mode = COMMAND_ANY,
+               .help = "Perform dmem (Direct Memory) DAP management and configuration",
+               .chain = dmem_dap_subcommand_handlers,
+               .usage = "",
+       },
+       COMMAND_REGISTRATION_DONE
+};
+
+static int dmem_dap_init(void)
+{
+       char *path = dmem_dev_path ? dmem_dev_path : DMEM_DEV_PATH_DEFAULT;
+       uint32_t dmem_total_memory_window_size;
+       long page_size = sysconf(_SC_PAGESIZE);
+       size_t dmem_mapped_start, dmem_mapped_end;
+       long start_delta;
+       int dmem_fd;
+
+       if (!dmem_dap_base_address) {
+               LOG_ERROR("dmem DAP Base address NOT set? value is 0");
+               return ERROR_FAIL;
+       }
+
+       dmem_fd = open(path, O_RDWR | O_SYNC);
+       if (dmem_fd == -1) {
+               LOG_ERROR("Unable to open %s", path);
+               return ERROR_FAIL;
+       }
+
+       dmem_total_memory_window_size = (dmem_dap_max_aps + 1) * dmem_dap_ap_offset;
+
+       dmem_mapped_start = dmem_dap_base_address;
+       dmem_mapped_end = dmem_dap_base_address + dmem_total_memory_window_size;
+        /* mmap() requires page aligned offsets */
+       dmem_mapped_start = ALIGN_DOWN(dmem_mapped_start, page_size);
+       dmem_mapped_end = ALIGN_UP(dmem_mapped_end, page_size);
+
+       dmem_mapped_size = dmem_mapped_end - dmem_mapped_start;
+       start_delta = dmem_mapped_start - dmem_dap_base_address;
+
+       dmem_map_base = mmap(NULL,
+                                                dmem_mapped_size,
+                                                (PROT_READ | PROT_WRITE),
+                                                MAP_SHARED, dmem_fd,
+                                                dmem_mapped_start);
+
+       close(dmem_fd);
+
+       if (dmem_map_base == MAP_FAILED) {
+               LOG_ERROR("Mapping address 0x%lx for 0x%lx bytes failed!",
+                       dmem_mapped_start, dmem_mapped_size);
+               return ERROR_FAIL;
+       }
+
+       dmem_virt_base_addr = (void *)((uintptr_t)dmem_map_base + start_delta);
+
+       return ERROR_OK;
+}
+
+static int dmem_dap_quit(void)
+{
+       if (munmap(dmem_map_base, dmem_mapped_size) == -1)
+               LOG_ERROR("%s: Failed to unmap mapped memory!", __func__);
+
+       return ERROR_OK;
+}
+
+static int dmem_dap_reset(int req_trst, int req_srst)
+{
+       return ERROR_OK;
+}
+
+static int dmem_dap_speed(int speed)
+{
+       return ERROR_OK;
+}
+
+static int dmem_dap_khz(int khz, int *jtag_speed)
+{
+       *jtag_speed = khz;
+       return ERROR_OK;
+}
+
+static int dmem_dap_speed_div(int speed, int *khz)
+{
+       *khz = speed;
+       return ERROR_OK;
+}
+
+/* DAP operations. */
+static const struct dap_ops dmem_dap_ops = {
+       .connect = dmem_connect,
+       .queue_dp_read = dmem_dp_q_read,
+       .queue_dp_write = dmem_dp_q_write,
+       .queue_ap_read = dmem_ap_q_read,
+       .queue_ap_write = dmem_ap_q_write,
+       .queue_ap_abort = dmem_ap_q_abort,
+       .run = dmem_dp_run,
+};
+
+static const char *const dmem_dap_transport[] = { "dapdirect_swd", NULL };
+
+struct adapter_driver dmem_dap_adapter_driver = {
+       .name = "dmem",
+       .transports = dmem_dap_transport,
+       .commands = dmem_dap_command_handlers,
+
+       .init = dmem_dap_init,
+       .quit = dmem_dap_quit,
+       .reset = dmem_dap_reset,
+       .speed = dmem_dap_speed,
+       .khz = dmem_dap_khz,
+       .speed_div = dmem_dap_speed_div,
+
+       .dap_swd_ops = &dmem_dap_ops,
+};
index 25ae7e8a169bc02a6f425b6cb939a62128e72564..3df424086dd8bcac93f27d94d8462775e8245848 100644 (file)
@@ -370,6 +370,7 @@ extern struct adapter_driver at91rm9200_adapter_driver;
 extern struct adapter_driver bcm2835gpio_adapter_driver;
 extern struct adapter_driver buspirate_adapter_driver;
 extern struct adapter_driver cmsis_dap_adapter_driver;
+extern struct adapter_driver dmem_dap_adapter_driver;
 extern struct adapter_driver dummy_adapter_driver;
 extern struct adapter_driver ep93xx_adapter_driver;
 extern struct adapter_driver esp_usb_adapter_driver;
index aa0ad3ade146d21743e433f83b5c112ae3ae7916..c24ead8cd91a4965e2e4e92443431cb1aee54d64 100644 (file)
@@ -147,6 +147,9 @@ struct adapter_driver *adapter_drivers[] = {
 #if BUILD_RSHIM == 1
                &rshim_dap_adapter_driver,
 #endif
+#if BUILD_DMEM == 1
+               &dmem_dap_adapter_driver,
+#endif
 #if BUILD_AM335XGPIO == 1
                &am335xgpio_adapter_driver,
 #endif

Linking to existing account procedure

If you already have an account and want to add another login method you MUST first sign in with your existing account and then change URL to read https://review.openocd.org/login/?link to get to this page again but this time it'll work for linking. Thank you.

SSH host keys fingerprints

1024 SHA256:YKx8b7u5ZWdcbp7/4AeXNaqElP49m6QrwfXaqQGJAOk gerrit-code-review@openocd.zylin.com (DSA)
384 SHA256:jHIbSQa4REvwCFG4cq5LBlBLxmxSqelQPem/EXIrxjk gerrit-code-review@openocd.org (ECDSA)
521 SHA256:UAOPYkU9Fjtcao0Ul/Rrlnj/OsQvt+pgdYSZ4jOYdgs gerrit-code-review@openocd.org (ECDSA)
256 SHA256:A13M5QlnozFOvTllybRZH6vm7iSt0XLxbA48yfc2yfY gerrit-code-review@openocd.org (ECDSA)
256 SHA256:spYMBqEYoAOtK7yZBrcwE8ZpYt6b68Cfh9yEVetvbXg gerrit-code-review@openocd.org (ED25519)
+--[ED25519 256]--+
|=..              |
|+o..   .         |
|*.o   . .        |
|+B . . .         |
|Bo. = o S        |
|Oo.+ + =         |
|oB=.* = . o      |
| =+=.+   + E     |
|. .=o   . o      |
+----[SHA256]-----+
2048 SHA256:0Onrb7/PHjpo6iVZ7xQX2riKN83FJ3KGU0TvI0TaFG4 gerrit-code-review@openocd.zylin.com (RSA)