From e76a9b799d2040092465a838d29523fa08c7cf12 Mon Sep 17 00:00:00 2001 From: Kirill Radkin Date: Thu, 14 Sep 2023 18:11:53 +0300 Subject: [PATCH] provide riscv-specific controls to disable triggers from beeing used for watchpoints Add a new riscv specific commands to disable triggers Change-Id: Ic1842085aa66851c740e0abcbfbe0adbe930920e Signed-off-by: Kirill Radkin --- doc/openocd.texi | 15 +++ src/target/riscv/riscv.c | 206 ++++++++++++++++++++++++++++++--------- src/target/riscv/riscv.h | 4 + 3 files changed, 177 insertions(+), 48 deletions(-) diff --git a/doc/openocd.texi b/doc/openocd.texi index 2d0a16ddd..4883e66da 100644 --- a/doc/openocd.texi +++ b/doc/openocd.texi @@ -10871,6 +10871,21 @@ Control dcsr.ebreaku. When on (default), U-mode ebreak instructions trap to OpenOCD. When off, they generate a breakpoint exception handled internally. @end deffn +The commands below can be used to prevent OpenOCD from using certain RISC-V trigger features. +For example in cases when there are known issues in the target hardware. + +@deffn {Command} {riscv set_enable_eq_match_trigger} [on|off] +When on (default), allow OpenOCD to use exact-match triggers in watchpoints. +@end deffn + +@deffn {Command} {riscv set_enable_napot_trigger} [on|off] +When on (default), allow OpenOCD to use NAPOT trigger in watchpoints. +@end deffn + +@deffn {Command} {riscv set_enable_ge_lt_trigger} [on|off] +When on (default), allow OpenOCD to use a pair of chained less-than & greater-than triggers in watchpoints. +@end deffn + @subsection RISC-V Authentication Commands The following commands can be used to authenticate to a RISC-V system. Eg. a diff --git a/src/target/riscv/riscv.c b/src/target/riscv/riscv.c index a544cbdfd..1c230512c 100644 --- a/src/target/riscv/riscv.c +++ b/src/target/riscv/riscv.c @@ -857,15 +857,16 @@ static struct match_triggers_tdata1_fields fill_match_triggers_tdata1_fields_t6( return result; } -static int maybe_add_trigger_t2_t6(struct target *target, +static int maybe_add_trigger_t2_t6_for_wp(struct target *target, struct trigger *trigger, struct match_triggers_tdata1_fields fields) { - int ret = ERROR_OK; + RISCV_INFO(r); + int ret = ERROR_FAIL; - if (!trigger->is_execute && trigger->length > 1) { + if (trigger->length > 0) { /* Setting a load/store trigger ("watchpoint") on a range of addresses */ - if (can_use_napot_match(trigger)) { + if (r->enable_napot_trigger && can_use_napot_match(trigger)) { LOG_TARGET_DEBUG(target, "trying to setup NAPOT match trigger"); struct trigger_request_info napot = { .tdata1 = fields.common | fields.size.any | @@ -877,52 +878,58 @@ static int maybe_add_trigger_t2_t6(struct target *target, if (ret != ERROR_TARGET_RESOURCE_NOT_AVAILABLE) return ret; } - LOG_TARGET_DEBUG(target, "trying to setup GE+LT chained match trigger pair"); - struct trigger_request_info ge_1 = { - .tdata1 = fields.common | fields.size.any | fields.chain.enable | - fields.match.ge, - .tdata2 = trigger->address, - .tdata1_ignore_mask = fields.tdata1_ignore_mask - }; - struct trigger_request_info lt_2 = { - .tdata1 = fields.common | fields.size.any | fields.chain.disable | - fields.match.lt, - .tdata2 = trigger->address + trigger->length, - .tdata1_ignore_mask = fields.tdata1_ignore_mask - }; - ret = try_setup_chained_match_triggers(target, trigger, ge_1, lt_2); - if (ret != ERROR_TARGET_RESOURCE_NOT_AVAILABLE) - return ret; - LOG_TARGET_DEBUG(target, "trying to setup LT+GE chained match trigger pair"); - struct trigger_request_info lt_1 = { - .tdata1 = fields.common | fields.size.any | fields.chain.enable | - fields.match.lt, - .tdata2 = trigger->address + trigger->length, - .tdata1_ignore_mask = fields.tdata1_ignore_mask - }; - struct trigger_request_info ge_2 = { - .tdata1 = fields.common | fields.size.any | fields.chain.disable | - fields.match.ge, - .tdata2 = trigger->address, - .tdata1_ignore_mask = fields.tdata1_ignore_mask - }; - ret = try_setup_chained_match_triggers(target, trigger, lt_1, ge_2); - if (ret != ERROR_TARGET_RESOURCE_NOT_AVAILABLE) - return ret; + if (r->enable_ge_lt_trigger) { + LOG_TARGET_DEBUG(target, "trying to setup GE+LT chained match trigger pair"); + struct trigger_request_info ge_1 = { + .tdata1 = fields.common | fields.size.any | fields.chain.enable | + fields.match.ge, + .tdata2 = trigger->address, + .tdata1_ignore_mask = fields.tdata1_ignore_mask + }; + struct trigger_request_info lt_2 = { + .tdata1 = fields.common | fields.size.any | fields.chain.disable | + fields.match.lt, + .tdata2 = trigger->address + trigger->length, + .tdata1_ignore_mask = fields.tdata1_ignore_mask + }; + ret = try_setup_chained_match_triggers(target, trigger, ge_1, lt_2); + if (ret != ERROR_TARGET_RESOURCE_NOT_AVAILABLE) + return ret; + + LOG_TARGET_DEBUG(target, "trying to setup LT+GE chained match trigger pair"); + struct trigger_request_info lt_1 = { + .tdata1 = fields.common | fields.size.any | fields.chain.enable | + fields.match.lt, + .tdata2 = trigger->address + trigger->length, + .tdata1_ignore_mask = fields.tdata1_ignore_mask + }; + struct trigger_request_info ge_2 = { + .tdata1 = fields.common | fields.size.any | fields.chain.disable | + fields.match.ge, + .tdata2 = trigger->address, + .tdata1_ignore_mask = fields.tdata1_ignore_mask + }; + ret = try_setup_chained_match_triggers(target, trigger, lt_1, ge_2); + if (ret != ERROR_TARGET_RESOURCE_NOT_AVAILABLE) + return ret; + } } - LOG_TARGET_DEBUG(target, "trying to setup equality match trigger"); - struct trigger_request_info eq = { - .tdata1 = fields.common | fields.size.any | fields.chain.disable | - fields.match.eq, - .tdata2 = trigger->address, - .tdata1_ignore_mask = fields.tdata1_ignore_mask - }; - ret = try_setup_single_match_trigger(target, trigger, eq); - if (ret != ERROR_OK) - return ret; - if (trigger->length > 1) { + if (r->enable_equality_match_trigger) { + LOG_TARGET_DEBUG(target, "trying to setup equality match trigger"); + struct trigger_request_info eq = { + .tdata1 = fields.common | fields.size.any | fields.chain.disable | + fields.match.eq, + .tdata2 = trigger->address, + .tdata1_ignore_mask = fields.tdata1_ignore_mask + }; + ret = try_setup_single_match_trigger(target, trigger, eq); + if (ret != ERROR_OK) + return ERROR_FAIL; + } + + if (ret == ERROR_OK && trigger->length > 1) { LOG_TARGET_DEBUG(target, "Trigger will match accesses at address 0x%" TARGET_PRIxADDR ", but may not match accesses at addresses in the inclusive range from 0x%" TARGET_PRIxADDR " to 0x%" TARGET_PRIxADDR ".", trigger->address, @@ -938,7 +945,34 @@ static int maybe_add_trigger_t2_t6(struct target *target, "against the first address of the range."); info->range_trigger_fallback_encountered = true; } - return ERROR_OK; + + return ret; +} + +static int maybe_add_trigger_t2_t6_for_bp(struct target *target, + struct trigger *trigger, struct match_triggers_tdata1_fields fields) +{ + LOG_TARGET_DEBUG(target, "trying to setup equality match trigger"); + struct trigger_request_info eq = { + .tdata1 = fields.common | fields.size.any | fields.chain.disable | + fields.match.eq, + .tdata2 = trigger->address, + .tdata1_ignore_mask = fields.tdata1_ignore_mask + }; + + return try_setup_single_match_trigger(target, trigger, eq); +} + +static int maybe_add_trigger_t2_t6(struct target *target, + struct trigger *trigger, struct match_triggers_tdata1_fields fields) +{ + if (trigger->is_execute) { + assert(!trigger->is_read && !trigger->is_write); + return maybe_add_trigger_t2_t6_for_bp(target, trigger, fields); + } + + assert(trigger->is_read || trigger->is_write); + return maybe_add_trigger_t2_t6_for_wp(target, trigger, fields); } static int maybe_add_trigger_t3(struct target *target, bool vs, bool vu, @@ -4273,6 +4307,57 @@ COMMAND_HANDLER(riscv_exec_progbuf) return ERROR_OK; } +COMMAND_HANDLER(riscv_set_enable_eq_match_trigger) +{ + struct target *target = get_current_target(CMD_CTX); + RISCV_INFO(r); + + if (CMD_ARGC == 0) { + command_print(CMD, "equality match trigger enabled: %s", r->enable_equality_match_trigger ? "on" : "off"); + return ERROR_OK; + } else if (CMD_ARGC == 1) { + COMMAND_PARSE_ON_OFF(CMD_ARGV[0], r->enable_equality_match_trigger); + return ERROR_OK; + } + + LOG_ERROR("Command takes 0 or 1 parameters"); + return ERROR_COMMAND_SYNTAX_ERROR; +} + +COMMAND_HANDLER(riscv_set_enable_napot_trigger) +{ + struct target *target = get_current_target(CMD_CTX); + RISCV_INFO(r); + + if (CMD_ARGC == 0) { + command_print(CMD, "NAPOT trigger enabled: %s", r->enable_napot_trigger ? "on" : "off"); + return ERROR_OK; + } else if (CMD_ARGC == 1) { + COMMAND_PARSE_ON_OFF(CMD_ARGV[0], r->enable_napot_trigger); + return ERROR_OK; + } + + LOG_ERROR("Command takes 0 or 1 parameters"); + return ERROR_COMMAND_SYNTAX_ERROR; +} + +COMMAND_HANDLER(riscv_set_enable_ge_lt_trigger) +{ + struct target *target = get_current_target(CMD_CTX); + RISCV_INFO(r); + + if (CMD_ARGC == 0) { + command_print(CMD, "ge-lt triggers enabled: %s", r->enable_ge_lt_trigger ? "on" : "off"); + return ERROR_OK; + } else if (CMD_ARGC == 1) { + COMMAND_PARSE_ON_OFF(CMD_ARGV[0], r->enable_ge_lt_trigger); + return ERROR_OK; + } + + LOG_ERROR("Command takes 0 or 1 parameters"); + return ERROR_COMMAND_SYNTAX_ERROR; +} + static const struct command_registration riscv_exec_command_handlers[] = { { .name = "dump_sample_buf", @@ -4519,6 +4604,27 @@ static const struct command_registration riscv_exec_command_handlers[] = { .help = "Execute a sequence of 32-bit instructions using the program buffer. " "The final ebreak instruction is added automatically, if needed." }, + { + .name = "set_enable_eq_match_trigger", + .handler = riscv_set_enable_eq_match_trigger, + .mode = COMMAND_CONFIG, + .usage = "[on|off]", + .help = "When on, allow OpenOCD to use equality match trigger in wp." + }, + { + .name = "set_enable_napot_trigger", + .handler = riscv_set_enable_napot_trigger, + .mode = COMMAND_CONFIG, + .usage = "[on|off]", + .help = "When on, allow OpenOCD to use NAPOT trigger in wp." + }, + { + .name = "set_enable_ge_lt_trigger", + .handler = riscv_set_enable_ge_lt_trigger, + .mode = COMMAND_CONFIG, + .usage = "[on|off]", + .help = "When on, allow OpenOCD to use GE/LT triggers in wp." + }, COMMAND_REGISTRATION_DONE }; @@ -4654,6 +4760,10 @@ static void riscv_info_init(struct target *target, struct riscv_info *r) r->riscv_ebreakm = true; r->riscv_ebreaks = true; r->riscv_ebreaku = true; + + r->enable_equality_match_trigger = true; + r->enable_ge_lt_trigger = true; + r->enable_napot_trigger = true; } static int riscv_resume_go_all_harts(struct target *target) diff --git a/src/target/riscv/riscv.h b/src/target/riscv/riscv.h index 3cf6aac26..ca3599c5a 100644 --- a/src/target/riscv/riscv.h +++ b/src/target/riscv/riscv.h @@ -301,6 +301,10 @@ struct riscv_info { bool riscv_ebreakm; bool riscv_ebreaks; bool riscv_ebreaku; + + bool enable_equality_match_trigger; + bool enable_napot_trigger; + bool enable_ge_lt_trigger; }; COMMAND_HELPER(riscv_print_info_line, const char *section, const char *key,