This is an automated email from Gerrit. "liangzhen <[email protected]>" just uploaded a new patch set to Gerrit, which you can find at https://review.openocd.org/c/openocd/+/9030
-- gerrit commit 7ad5a5d63ef22f3719bfc7a7520c33cab2b8dcc0 Author: liangzhen <[email protected]> Date: Wed Oct 22 10:52:34 2025 +0800 target: Add coresight trace support Implement a basic framework for coresight trace where each type of coresight trace components only need to implement corresponding interfaces. Change-Id: I5f54fcd86be6359b4e376ad00137d79c624b7d55 Signed-off-by: liangzhen <[email protected]> diff --git a/doc/openocd.texi b/doc/openocd.texi index 9540e69f3f..afbf0e5317 100644 --- a/doc/openocd.texi +++ b/doc/openocd.texi @@ -10310,6 +10310,75 @@ how the event caused trouble. @end deffn +@anchor{armcoresightdrivers} +@section ARM Coresight Drivers +@cindex Coresight + +OpenOCD has limited support for coresight trace drivers using the @emph{cstrace} group of commands. + +@deffn {Command} {cstrace create} component_name type configparams... +This command creates a coresight component instance@var{component_name} +@itemize +@item @emph{component_name} ... is the name of the coresight component instance. +This name is also used to create the coresight component object command, referred to here as +$component_name, and in other places the component needs to be identified. +@item @emph{type} ... specifies the coresight component type. +@item @emph{configparams} ... all parameters accepted by $component_name configure are permitted. +You must set the @option{-dap} @emph{dap_name} @option{-ap-num} @emph{apn} here. +@end itemize +@end deffn + +@deffn {Command} {cstrace types} +Lists all supported coresight component types. At this writing, the supported coresight component types are: +@end deffn + +@deffn {Command} {cstrace names} +Lists the names of all current coresight components in the list. +@end deffn + +@deffn {Command} {$component_name configure configparams...} +The options accepted by this command may also be specified as parameters to @command{cstrace create}. +These values can later be queried one at a time by using the @command{$component_name cget} command. +@itemize +@item @option{-dap} @emph{dap_name} - names the DAP used to access this coresight component. +@item @option{-ap-num} @emph{apn} - set DAP access port for the coresight component. +On ADIv5 DAP ap number is the numeric index of the DAP AP the component is connected to. +On ADIv6 DAP ap number is the base address of the DAP AP the component is connected to. +@item @option{-baseaddr} @emph{base_address} - base address of the component on the respective MEM-AP. +@end itemize +@end deffn + +@deffn {Command} {$component_name cget queryparm} +Each configuration parameter accepted by @command{$component_name configure} can +be individu ally queried, to return its current value. The queryparm is a parameter +name accepted by that command, such as -baseaddr. There is a special cases: +@itemize +@item @option{-type} – returns the coresight component type. This is a special case +because this is set using @command{cstrace create} and can’t be changed using +@command{$component_name configure}. +@end itemize +@end deffn + +@deffn {Command} {$component_name enable} +Enable the coresight component. +@end deffn + +@deffn {Command} {$component_name disable} +Disable the coresight component. +@end deffn + +@deffn {Command} {$component_name status} +Show the coresight component status. +@end deffn + +@deffn {Command} {$component_name write} @var{reg_name} @var{value} +Write @var{value} to the coresight component register with the symbolic name @var{reg_name}. +@end deffn + +@deffn {Command} {$component_name read} @var{reg_name} +Print the value read from the coresight component register with the symbolic name @var{reg_name}. +@end deffn + @anchor{armcrosstrigger} @section ARM Cross-Trigger Interface @cindex CTI diff --git a/src/openocd.c b/src/openocd.c index f3e1bee48e..6e7018ce02 100644 --- a/src/openocd.c +++ b/src/openocd.c @@ -27,6 +27,7 @@ #include <target/arm_cti.h> #include <target/arm_adi_v5.h> #include <target/arm_tpiu_swo.h> +#include <target/coresight_trace.h> #include <rtt/rtt.h> #include <server/server.h> @@ -248,6 +249,7 @@ static int (* const command_registrants[])(struct command_context *cmd_ctx_value cti_register_commands, dap_register_commands, arm_tpiu_swo_register_commands, + cstrace_register_commands, }; static struct command_context *setup_command_handler(Jim_Interp *interp) @@ -360,6 +362,7 @@ int openocd_main(int argc, char *argv[]) help_del_all_commands(cmd_ctx); /* free all DAP and CTI objects */ + cstrace_cleanup_all(); arm_cti_cleanup_all(); dap_cleanup_all(); diff --git a/src/target/Makefile.am b/src/target/Makefile.am index 0b5a85704c..35f0cb60b7 100644 --- a/src/target/Makefile.am +++ b/src/target/Makefile.am @@ -111,7 +111,8 @@ ARM_DEBUG_SRC = \ %D%/etm.c \ %D%/etm_dummy.c \ %D%/arm_tpiu_swo.c \ - %D%/arm_cti.c + %D%/arm_cti.c \ + %D%/coresight_trace.c AVR32_SRC = \ %D%/avr32_ap7k.c \ @@ -229,6 +230,7 @@ ARC_SRC = \ %D%/lakemont.h \ %D%/x86_32_common.h \ %D%/arm_cti.h \ + %D%/coresight_trace.h \ %D%/esirisc.h \ %D%/esirisc_jtag.h \ %D%/esirisc_regs.h \ diff --git a/src/target/coresight_trace.c b/src/target/coresight_trace.c new file mode 100644 index 0000000000..dda03654d7 --- /dev/null +++ b/src/target/coresight_trace.c @@ -0,0 +1,561 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/*************************************************************************** + * Copyright (C) 2025 by liangzhen * + * [email protected] * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <helper/log.h> +#include <helper/types.h> +#include <helper/list.h> +#include <helper/command.h> +#include <helper/nvp.h> +#include <helper/jim-nvp.h> +#include "coresight_trace.h" + +static OOCD_LIST_HEAD(all_cstrace_component); + +static struct coresight_type *coresight_type[] = { +}; + +int coresight_write_reg(const struct cstrace_component *comp, unsigned int reg, uint32_t value) +{ + return mem_ap_write_atomic_u32(comp->ap, comp->spot.base + reg, value); +} + +int coresight_read_reg(const struct cstrace_component *comp, unsigned int reg, uint32_t *value) +{ + if (!value) + return ERROR_COMMAND_ARGUMENT_INVALID; + + return mem_ap_read_atomic_u32(comp->ap, comp->spot.base + reg, value); +} + +int coresight_read_buffer(const struct cstrace_component *comp, target_addr_t address, + uint32_t size, uint8_t *buffer) +{ + return mem_ap_read_buf(comp->ap, buffer, 4, size / 4, address); +} + +int coresight_lock(const struct cstrace_component *comp) +{ + return coresight_write_reg(comp, CORESIGHT_LAR, 0x0); +} + +int coresight_unlock(const struct cstrace_component *comp) +{ + return coresight_write_reg(comp, CORESIGHT_LAR, CORESIGHT_UNLOCK); +} + +int coresight_poll_register(const struct cstrace_component *comp, unsigned int reg, + uint32_t mask, uint32_t value, int timeout) +{ + assert(timeout > 0); + + int ret; + uint32_t regval; + + do { + ret = coresight_read_reg(comp, reg, ®val); + if (ret != ERROR_OK) + return ret; + + if (get_field(regval, mask) == value) + break; + + alive_sleep(10); + } while (--timeout); + + if (!timeout) { + LOG_ERROR("%s poll %x timeout", comp->name, reg); + return ERROR_WAIT; + } + + return ERROR_OK; +} + +int cstrace_cleanup_all(void) +{ + struct cstrace_component *obj, *tmp; + list_for_each_entry_safe(obj, tmp, &all_cstrace_component, lh) { + free(obj->component_info); + free(obj->private_config); + free(obj->name); + free(obj->type); + free(obj); + } + + return ERROR_OK; +} + +COMMAND_HANDLER(handle_cstrace_read) +{ + if (CMD_ARGC != 1) + return ERROR_COMMAND_SYNTAX_ERROR; + + struct cstrace_component *comp = CMD_DATA; + + if (coresight_unlock(comp) != ERROR_OK) + return ERROR_FAIL; + + uint32_t value; + int retval = comp->type->read(comp, CMD_ARGV[0], &value); + + if (coresight_lock(comp) != ERROR_OK) + return ERROR_FAIL; + + if (retval != ERROR_OK) { + command_print(CMD, "failed to read register '%s'", CMD_ARGV[0]); + return retval; + } + + command_print(CMD, "0x%08"PRIx32, value); + + return retval; +} + +COMMAND_HANDLER(handle_cstrace_write) +{ + if (CMD_ARGC != 2) + return ERROR_COMMAND_SYNTAX_ERROR; + + struct cstrace_component *comp = CMD_DATA; + + uint32_t value; + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], value); + + if (coresight_unlock(comp) != ERROR_OK) + return ERROR_FAIL; + + int retval = comp->type->write(comp, CMD_ARGV[0], value); + + if (coresight_lock(comp) != ERROR_OK) + return ERROR_FAIL; + + if (retval != ERROR_OK) + command_print(CMD, "failed to write '0x%x' to register '%s'", value, CMD_ARGV[0]); + + return retval; +} + +COMMAND_HANDLER(handle_cstrace_status) +{ + if (CMD_ARGC != 0) + return ERROR_COMMAND_SYNTAX_ERROR; + + struct cstrace_component *comp = CMD_DATA; + + if (coresight_unlock(comp) != ERROR_OK) + return ERROR_FAIL; + + int retval = comp->type->status(comp, CMD); + + if (coresight_lock(comp) != ERROR_OK) + return ERROR_FAIL; + + if (retval != ERROR_OK) + command_print(CMD, "failed to get '%s' status", comp->name); + + return retval; +} + +COMMAND_HANDLER(handle_cstrace_disable) +{ + if (CMD_ARGC != 0) + return ERROR_COMMAND_SYNTAX_ERROR; + + struct cstrace_component *comp = CMD_DATA; + + if (coresight_unlock(comp) != ERROR_OK) + return ERROR_FAIL; + + int retval = comp->type->disable(comp); + + if (coresight_lock(comp) != ERROR_OK) + return ERROR_FAIL; + + if (retval != ERROR_OK) + command_print(CMD, "failed to disable '%s'", comp->name); + + return retval; +} + +COMMAND_HANDLER(handle_cstrace_enable) +{ + if (CMD_ARGC != 0) + return ERROR_COMMAND_SYNTAX_ERROR; + + struct cstrace_component *comp = CMD_DATA; + + if (coresight_unlock(comp) != ERROR_OK) + return ERROR_FAIL; + + int retval = comp->type->enable(comp); + + if (coresight_lock(comp) != ERROR_OK) + return ERROR_FAIL; + + if (retval != ERROR_OK) + command_print(CMD, "failed to enable '%s'", comp->name); + + return retval; +} + +enum coresight_cfg_opts { + CORESIGHT_CFG_TYPE, + CORESIGHT_CFG_INVALID = -1 +}; + +static struct nvp nvp_config_opts[] = { + { .name = "-type", .value = CORESIGHT_CFG_TYPE }, + { .name = NULL, .value = CORESIGHT_CFG_INVALID } +}; + +static COMMAND_HELPER(cstrace_component_configure, struct cstrace_component *comp, unsigned int index, bool is_configure) +{ + const struct nvp *n; + struct jim_getopt_info goi; + const char *result; + int reslen; + int retval; + while (index < CMD_ARGC) { + if (comp->type->coresight_jim_configure) { + jim_getopt_setup(&goi, CMD_CTX->interp, CMD_ARGC - index, CMD_JIMTCL_ARGV + index); + goi.is_configure = is_configure; + retval = (*comp->type->coresight_jim_configure)(comp, &goi); + index = CMD_ARGC - goi.argc; + + result = Jim_GetString(Jim_GetResult(CMD_CTX->interp), &reslen); + if (reslen > 0) + command_print(CMD, "%s", result); + + if (retval == JIM_OK) { + /* more? */ + continue; + } + if (retval == JIM_ERR) { + /* An error */ + return ERROR_FAIL; + } + /* otherwise we 'continue' below */ + } + + jim_getopt_setup(&goi, CMD_CTX->interp, CMD_ARGC - index, CMD_JIMTCL_ARGV + index); + goi.is_configure = is_configure; + retval = adiv5_jim_mem_ap_spot_configure(&comp->spot, &goi); + index = CMD_ARGC - goi.argc; + + result = Jim_GetString(Jim_GetResult(CMD_CTX->interp), &reslen); + if (reslen > 0) + command_print(CMD, "%s", result); + + if (retval == JIM_OK) + continue; + if (retval == JIM_ERR) + return retval; + + n = nvp_name2value(nvp_config_opts, CMD_ARGV[index]); + if (!n->name) { + nvp_unknown_command_print(CMD, nvp_config_opts, NULL, CMD_ARGV[index]); + return ERROR_COMMAND_ARGUMENT_INVALID; + } + index++; + switch (n->value) { + case CORESIGHT_CFG_TYPE: + if (is_configure) { + command_print(CMD, "not settable: %s", n->name); + return ERROR_COMMAND_ARGUMENT_INVALID; + } + if (index != CMD_ARGC) + return ERROR_COMMAND_SYNTAX_ERROR; + command_print(CMD, "%s", comp->type->name); + break; + } + } + + return ERROR_OK; +} + +COMMAND_HANDLER(handle_cstrace_configure) +{ + if (!CMD_ARGC) + return ERROR_COMMAND_SYNTAX_ERROR; + + bool is_configure = !strcmp(CMD_NAME, "configure"); + + struct cstrace_component *comp = CMD_DATA; + + return CALL_COMMAND_HANDLER(cstrace_component_configure, comp, 0, is_configure); +} + +static const struct command_registration cstrace_instance_command_handlers[] = { + { + .name = "configure", + .mode = COMMAND_ANY, + .handler = handle_cstrace_configure, + .help = "configure a new coresight trace component for use", + .usage = "[cstrace_component_attribute ...]", + }, + { + .name = "cget", + .mode = COMMAND_ANY, + .handler = handle_cstrace_configure, + .help = "returns the specified coresight trace component attribute", + .usage = "cstrace_component_attribute", + }, + { + .name = "enable", + .handler = handle_cstrace_enable, + .mode = COMMAND_EXEC, + .help = "enable the coresight trace component", + .usage = "", + }, + { + .name = "disable", + .handler = handle_cstrace_disable, + .mode = COMMAND_EXEC, + .help = "disable the coresight trace component", + .usage = "", + }, + { + .name = "status", + .mode = COMMAND_EXEC, + .handler = handle_cstrace_status, + .help = "show coresight trace component status", + .usage = "", + }, + { + .name = "write", + .mode = COMMAND_EXEC, + .handler = handle_cstrace_write, + .help = "write to a coresight trace component register", + .usage = "<register> <value>", + }, + { + .name = "read", + .mode = COMMAND_EXEC, + .handler = handle_cstrace_read, + .help = "read a trace component register", + .usage = "<register>", + }, + COMMAND_REGISTRATION_DONE +}; + +COMMAND_HANDLER(handle_cstrace_create) +{ + int retval = ERROR_OK; + + if (CMD_ARGC < 2) + return ERROR_COMMAND_SYNTAX_ERROR; + + /* check if the coresight trace component name clashes with an existing command name */ + Jim_Cmd *jimcmd = Jim_GetCommand(CMD_CTX->interp, CMD_JIMTCL_ARGV[0], JIM_NONE); + if (jimcmd) { + command_print(CMD, "Command: %s Exists", CMD_ARGV[0]); + return ERROR_FAIL; + } + + /* does coresight trace component type exist */ + const char *cp = CMD_ARGV[1]; + size_t x; + for (x = 0; x < ARRAY_SIZE(coresight_type); x++) { + if (strcmp(cp, coresight_type[x]->name) == 0) { + /* found */ + break; + } + } + if (x == ARRAY_SIZE(coresight_type)) { + char *all = NULL; + for (x = 0; x < ARRAY_SIZE(coresight_type); x++) { + char *prev = all; + if (all) + all = alloc_printf("%s, %s", all, coresight_type[x]->name); + else + all = alloc_printf("%s", coresight_type[x]->name); + free(prev); + if (!all) { + LOG_ERROR("Out of memory"); + return ERROR_FAIL; + } + } + command_print(CMD, "Unknown coresight trace component type %s, try one of %s", cp, all); + free(all); + return ERROR_FAIL; + } + + /* Create it */ + struct cstrace_component *comp = calloc(1, sizeof(struct cstrace_component)); + if (!comp) { + LOG_ERROR("Out of memory"); + return ERROR_FAIL; + } + + /* allocate memory for each unique coresight type */ + comp->type = malloc(sizeof(struct coresight_type)); + if (!comp->type) { + LOG_ERROR("Out of memory"); + free(comp); + return ERROR_FAIL; + } + + memcpy(comp->type, coresight_type[x], sizeof(struct coresight_type)); + + comp->name = strdup(CMD_ARGV[0]); + if (!comp->name) { + LOG_ERROR("Out of memory"); + free(comp->type); + free(comp); + return ERROR_FAIL; + } + + adiv5_mem_ap_spot_init(&comp->spot); + + /* Do the rest as "configure" options */ + bool is_configure = true; + retval = CALL_COMMAND_HANDLER(cstrace_component_configure, comp, 2, is_configure); + if (retval == ERROR_OK) { + if (!comp->spot.dap) { + command_print(CMD, "-dap required when creating coresight trace component"); + retval = ERROR_COMMAND_ARGUMENT_INVALID; + } + } + + if (retval != ERROR_OK) { + free(comp->name); + free(comp->type); + free(comp); + return retval; + } + + if (comp->type->coresight_create) { + retval = (*comp->type->coresight_create)(comp); + if (retval != ERROR_OK) { + LOG_DEBUG("coresight_create failed"); + free(comp->private_config); + free(comp->component_info); + free(comp->name); + free(comp->type); + free(comp); + return retval; + } + } + + /* now - create the new coresight component name command */ + const struct command_registration cstrace_subcommands[] = { + { + .chain = cstrace_instance_command_handlers, + }, + { + .chain = comp->type->commands, + }, + COMMAND_REGISTRATION_DONE + }; + const struct command_registration cstrace_commands[] = { + { + .name = CMD_ARGV[0], + .mode = COMMAND_ANY, + .help = "coresight trace command group", + .usage = "", + .chain = cstrace_subcommands, + }, + COMMAND_REGISTRATION_DONE + }; + retval = register_commands_with_data(CMD_CTX, NULL, cstrace_commands, comp); + if (retval != ERROR_OK) { + free(comp->private_config); + free(comp->component_info); + free(comp->name); + free(comp->type); + free(comp); + return retval; + } + + comp->ap = dap_get_ap(comp->spot.dap, comp->spot.ap_num); + if (!comp->ap) { + LOG_ERROR("coresight trace cannot get AP"); + free(comp->private_config); + free(comp->component_info); + free(comp->name); + free(comp->type); + free(comp); + return ERROR_FAIL; + } + + list_add_tail(&comp->lh, &all_cstrace_component); + + return ERROR_OK; +} + +COMMAND_HANDLER(handle_cstrace_types) +{ + if (CMD_ARGC != 0) + return ERROR_COMMAND_SYNTAX_ERROR; + + for (size_t x = 0; x < ARRAY_SIZE(coresight_type); x++) + command_print(CMD, "%s", coresight_type[x]->name); + + return ERROR_OK; +} + +COMMAND_HANDLER(handle_cstrace_names) +{ + struct cstrace_component *entry; + + if (CMD_ARGC != 0) + return ERROR_COMMAND_SYNTAX_ERROR; + + list_for_each_entry(entry, &all_cstrace_component, lh) + command_print(CMD, "%s", entry->name); + + return ERROR_OK; +} + +static const struct command_registration cstrace_subcommand_handlers[] = { + { + .name = "create", + .mode = COMMAND_CONFIG, + .handler = handle_cstrace_create, + .usage = "name type [options ...]", + .help = "Creates a new coresight trace component", + }, + { + .name = "types", + .mode = COMMAND_ANY, + .handler = handle_cstrace_types, + .help = "Returns the available coresight trace component types as " + "a list of strings", + .usage = "", + }, + { + .name = "names", + .mode = COMMAND_ANY, + .handler = handle_cstrace_names, + .help = "Returns the names of all coresight trace components as a " + "list of strings", + .usage = "", + }, + COMMAND_REGISTRATION_DONE +}; + +static const struct command_registration cstrace_command_handlers[] = { + { + .name = "cstrace", + .mode = COMMAND_CONFIG, + .help = "configure coresight trace", + .chain = cstrace_subcommand_handlers, + .usage = "", + }, + COMMAND_REGISTRATION_DONE +}; + +int cstrace_register_commands(struct command_context *cmd_ctx) +{ + return register_commands(cmd_ctx, NULL, cstrace_command_handlers); +} diff --git a/src/target/coresight_trace.h b/src/target/coresight_trace.h new file mode 100644 index 0000000000..a0b9303630 --- /dev/null +++ b/src/target/coresight_trace.h @@ -0,0 +1,71 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/*************************************************************************** + * Copyright (C) 2025 by liangzhen * + * [email protected] * + ***************************************************************************/ + +#ifndef OPENOCD_TARGET_CORESIGHT_TRACE_H +#define OPENOCD_TARGET_CORESIGHT_TRACE_H + +#include "arm_adi_v5.h" + +#define CORESIGHT_ITCTRL 0xf00 +#define CORESIGHT_CLAIMSET 0xfa0 +#define CORESIGHT_CLAIMCLR 0xfa4 +#define CORESIGHT_LAR 0xfb0 +#define CORESIGHT_LSR 0xfb4 +#define CORESIGHT_DEVARCH 0xfbc +#define CORESIGHT_AUTHSTATUS 0xfb8 +#define CORESIGHT_DEVID 0xfc8 +#define CORESIGHT_DEVTYPE 0xfcc + +#define CORESIGHT_UNLOCK 0xc5acce55 + +#define CORESIGHT_TIMEOUT 10 + +struct cstrace_component { + struct list_head lh; + target_addr_t base; + char *name; + struct coresight_type *type; + struct adiv5_mem_ap_spot spot; + struct adiv5_ap *ap; + void *component_info; + void *private_config; +}; + +struct coresight_type { + const char *name; + int (*coresight_create)(struct cstrace_component *comp); + int (*coresight_jim_configure)(struct cstrace_component *comp, struct jim_getopt_info *goi); + int (*coresight_jim_commands)(struct cstrace_component *comp, struct jim_getopt_info *goi); + + int (*enable)(struct cstrace_component *comp); + int (*disable)(struct cstrace_component *comp); + int (*status)(struct cstrace_component *comp, struct command_invocation *cmd); + int (*write)(struct cstrace_component *comp, const char *reg_name, uint32_t value); + int (*read)(struct cstrace_component *comp, const char *reg_name, uint32_t *value); + + const struct command_registration *commands; +}; + +#define get_field(reg, mask) (((reg) & (mask)) / ((mask) & ~((mask) << 1))) +#define set_field(reg, mask, val) (((reg) & ~(mask)) | (((val) * ((mask) & ~((mask) << 1))) & (mask))) + +int coresight_write_reg(const struct cstrace_component *comp, unsigned int reg, uint32_t value); +int coresight_read_reg(const struct cstrace_component *comp, unsigned int reg, uint32_t *value); +int coresight_read_buffer(const struct cstrace_component *comp, target_addr_t address, + uint32_t size, uint8_t *buffer); + +int coresight_lock(const struct cstrace_component *comp); +int coresight_unlock(const struct cstrace_component *comp); + +int coresight_poll_register(const struct cstrace_component *comp, unsigned int reg, + uint32_t mask, uint32_t value, int timeout); + +int cstrace_cleanup_all(void); + +int cstrace_register_commands(struct command_context *cmd_ctx); + +#endif /* OPENOCD_TARGET_CORESIGHT_TRACE_H */ --
