This is an automated email from Gerrit. "Nishanth Menon <n...@ti.com>" just uploaded a new patch set to Gerrit, which you can find at https://review.openocd.org/c/openocd/+/7088
-- gerrit commit 97dfd84e645bdfa4ee18a5572622315f4446c75b Author: Nishanth Menon <n...@ti.com> Date: Thu Jul 14 16:37:54 2022 -0500 jtag/drivers: Add dmem driver Direct memory driver support for CoreSight Access Port(AP). Even though we emulate SWD (serial wire debug), we are'nt 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│ │ └───────────┘ │ │ ┌───────────┐ ┌─────▼─────┐ │Cortex-M4F ◄────────┤ │ └───────────┘ │ │ │ DebugSS │ ┌───────────┐ │ │ │Cortex-M4F ◄────────┤ │ └───────────┘ └───────────┘ Signed-off-by: Nishanth Menon <n...@ti.com> Signed-off-by: Jason Peck <jp...@ti.com> Change-Id: I8470cb15348863dd844b2c0e3f63a9063cb032c6 diff --git a/configure.ac b/configure.ac index 66b07b179c..b612daa751 100644 --- a/configure.ac +++ b/configure.ac @@ -245,6 +245,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]), @@ -483,6 +487,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.]) @@ -757,6 +767,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"]) diff --git a/doc/openocd.texi b/doc/openocd.texi index b213798c31..bd5713f108 100644 --- a/doc/openocd.texi +++ b/doc/openocd.texi @@ -3685,6 +3685,42 @@ 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. + +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 {Config Command} {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} {ap_address_offset offset_address} +Set the address offset between Access Ports (APs). +@end deffn + +@deffn {Config Command} {max_aps} +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, diff --git a/src/jtag/drivers/Makefile.am b/src/jtag/drivers/Makefile.am index 1440eb5a68..e56ddc60e7 100644 --- a/src/jtag/drivers/Makefile.am +++ b/src/jtag/drivers/Makefile.am @@ -151,6 +151,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 index 0000000000..1d74b93518 --- /dev/null +++ b/src/jtag/drivers/dmem.c @@ -0,0 +1,385 @@ +/* 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/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> + +/* Use local variable stub for DP/AP registers. */ +static uint32_t dp_ctrl_stat; +static uint32_t dp_id_code; +static uint32_t ap_sel, ap_bank; + +/* Dmem file handler. */ +static int dmem_fd = -1; +static void *dmem_map_base, *dmem_virt_base_addr; +static long dmem_mapped_start, dmem_mapped_size; + +/* DAP error code. */ +static int dmem_dap_retval = ERROR_OK; + +/* Default dmem device. */ +#define DMEM_DEV_PATH_DEFAULT "/dev/mem" +static char *dmem_dev_path; +static uint64_t dmem_dap_base_address; +static uint8_t 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, unsigned int val) +{ + *(volatile uint32_t *)((char *)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 *)((char *)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_DPIDR: + *data = dp_id_code; + break; + + case DP_CTRL_STAT: + *data = CDBGPWRUPACK | CSYSPWRUPACK; + break; + + default: + break; + } + + return ERROR_OK; +} + +static int dmem_dp_q_write(struct adiv5_dap *dap, unsigned int reg, uint32_t data) +{ + switch (reg) { + case DP_CTRL_STAT: + dp_ctrl_stat = data; + break; + case DP_SELECT: + ap_sel = (data & DP_SELECT_APSEL) >> 24; + ap_bank = (data & DP_SELECT_APBANK) >> 4; + break; + default: + break; + } + + 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; + } + + if (ap_bank != 0) { + LOG_ERROR("%s: BANK=%d AP=%ld wrote[0x%02x]: 0x%08x\n", __func__, ap_bank, + ap->ap_num, reg, data); + dmem_dap_retval = ERROR_FAIL; + 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) +{ + int retval = dmem_dap_retval; + + /* Clear the error code. */ + dmem_dap_retval = ERROR_OK; + + return retval; +} + +static int dmem_connect(struct adiv5_dap *dap) +{ + 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); + long start_delta, end_delta; + + if (!dmem_dap_base_address) { + LOG_ERROR("dmem DAP Base address NOT set? value is 0\n"); + return ERROR_FAIL; + } + + dmem_fd = open(path, O_RDWR | O_SYNC); + if (dmem_fd == -1) { + LOG_ERROR("Unable to open %s\n", path); + return ERROR_FAIL; + } + + dmem_total_memory_window_size = (dmem_dap_max_aps + 1) * dmem_dap_ap_offset; + + /* if start is NOT aligned, then we need to map a page previously */ + dmem_mapped_start = dmem_dap_base_address; + dmem_mapped_size = dmem_total_memory_window_size; + + start_delta = dmem_dap_base_address % page_size; + if (start_delta) { + dmem_mapped_start -= start_delta; + dmem_mapped_size += start_delta; + } + + /* End should also be aligned to page_size */ + end_delta = dmem_mapped_size % page_size; + if (end_delta) + dmem_mapped_size += page_size - end_delta; + + dmem_map_base = mmap(NULL, + dmem_mapped_size, + (PROT_READ | PROT_WRITE), + MAP_SHARED, dmem_fd, + dmem_mapped_start & ~(off_t) (page_size - 1)); + if (dmem_map_base == MAP_FAILED) { + LOG_ERROR("Mapping address 0x%lx for 0x%lx bytes failed!\n", + dmem_mapped_start, dmem_mapped_size); + return ERROR_FAIL; + } + + dmem_virt_base_addr = (char *)dmem_map_base + start_delta; + + return ERROR_OK; +} + +static void dmem_disconnect(struct adiv5_dap *dap) +{ + if (munmap(dmem_map_base, dmem_mapped_size) == -1) + LOG_ERROR("%s: Failed to unmap mapped memory!\n", __func__); + if (dmem_fd != -1) { + close(dmem_fd); + dmem_fd = -1; + } +} + +COMMAND_HANDLER(dmem_dap_device_command) +{ + if (CMD_ARGC != 1) { + command_print(CMD, "Too many arguments"); + 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) { + command_print(CMD, "Too many arguments"); + 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) { + command_print(CMD, "Too many arguments"); + return ERROR_COMMAND_SYNTAX_ERROR; + } + COMMAND_PARSE_NUMBER(u8, CMD_ARGV[0], dmem_dap_max_aps); + return ERROR_OK; +} + +COMMAND_HANDLER(dmem_dap_ap_offset_command) +{ + if (CMD_ARGC != 1) { + command_print(CMD, "Too many arguments"); + 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) { + command_print(CMD, "Too many arguments"); + 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%lx", dmem_dap_base_address); + command_print(CMD, " Max APs : %d", dmem_dap_max_aps); + command_print(CMD, " AP offset : 0x%08x", dmem_dap_ap_offset); + + return ERROR_OK; +} + +static const struct command_registration dmem_dap_subcommand_handlers[] = { + { + .name = "device", + .handler = dmem_dap_device_command, + .mode = COMMAND_CONFIG, + .help = "set the dmem memory access device", + .usage = "</dev/mem>", + }, + { + .name = "base_address", + .handler = dmem_dap_base_address_command, + .mode = COMMAND_CONFIG, + .help = "set the dmem dap AP memory map base address", + .usage = "<0x700000000>", + }, + { + .name = "max_aps", + .handler = dmem_dap_max_aps_command, + .mode = COMMAND_CONFIG, + .help = "set the maximum number of APs this will support", + .usage = "<1>", + }, + { + .name = "ap_address_offset", + .handler = dmem_dap_ap_offset_command, + .mode = COMMAND_CONFIG, + .help = "set the offsets of each ap index", + .usage = "<0x100>", + }, + { + .name = "info", + .handler = dmem_dap_config_info_command, + .mode = COMMAND_ANY, + .help = "print the config info", + .usage = "", + }, + 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) +{ + return ERROR_OK; +} + +static int dmem_dap_quit(void) +{ + 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, + .quit = dmem_disconnect, +}; + +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, +}; diff --git a/src/jtag/interfaces.c b/src/jtag/interfaces.c index 4cc197b502..c933d42d8d 100644 --- a/src/jtag/interfaces.c +++ b/src/jtag/interfaces.c @@ -153,6 +153,9 @@ extern struct adapter_driver stlink_dap_adapter_driver; #if BUILD_RSHIM == 1 extern struct adapter_driver rshim_dap_adapter_driver; #endif +#if BUILD_DMEM == 1 +extern struct adapter_driver dmem_dap_adapter_driver; +#endif #if BUILD_AM335XGPIO == 1 extern struct adapter_driver am335xgpio_adapter_driver; #endif @@ -273,6 +276,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 --