Skip to content

Commit 5a8697b

Browse files
committed
target/riscv: manage triggers available to OpenOCD for internal use
Before the change, if the user wrote to any `tdata*` register, OpenOCD would sometimes start to disable all the triggers (by writing zeroes to `tdata1`) and re-enable them again (by witing all trigger registers to the values read before for each `tselect` value), e.g. on `step` (see `disable/enable_triggers()`). There are a couple of issues with such approach: 1. RISC-V Debug Specification does not require custom register types to support re-enabling by such sequence of writes (e.g. some custom trigger type may require writing a custom CSR to enable it). 2. OpenOCD may still overwrite these triggers when a user asks to set a new WP. This commit introduces `riscv reserve_trigger ...` command to explicitly mark the triggers OpenOCD should not touch. Such approach allows to separate management of custom triggers and offload it onto the user (e.g. disable/enable such triggers by setting up an event handler on `step`-related events). Change-Id: I3339000445185ab221368442a070f412bf44bfab Signed-off-by: Evgeniy Naydanov <[email protected]>
1 parent 5afed58 commit 5a8697b

File tree

4 files changed

+131
-87
lines changed

4 files changed

+131
-87
lines changed

doc/openocd.texi

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11524,6 +11524,21 @@ as in the mie CSR (defined in the RISC-V Privileged Spec).
1152411524
For details on this trigger type, see the RISC-V Debug Specification.
1152511525
@end deffn
1152611526

11527+
@deffn {Command} {riscv reserve_trigger} [index @option{on|off}]
11528+
Manages the set of reserved triggers. Reserving a trigger results in OpenOCD
11529+
not using it internally (e.g. skipping it when setting a watchpoint or a
11530+
hardware breakpoint), so that the user or the application has unfettered
11531+
control over the trigger. By default there are no reserved triggers.
11532+
11533+
@enumerate
11534+
@item @var{index} specifies the index of a trigger to reserve or free up.
11535+
@item The second argument specifies whether the trigger should be reserved
11536+
(@var{on}) or a prior reservation cancelled (@var{off}).
11537+
@item If called without parameters, returns indices of reserved triggers.
11538+
@end enumerate
11539+
11540+
@end deffn
11541+
1152711542
@deffn {Command} {riscv itrigger clear}
1152811543
Clear the type 4 trigger that was set using @command{riscv itrigger set}.
1152911544
@end deffn

src/target/riscv/riscv-013_reg.c

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@ static int riscv013_reg_get(struct reg *reg)
4444
static int riscv013_reg_set(struct reg *reg, uint8_t *buf)
4545
{
4646
struct target *target = riscv_reg_impl_get_target(reg);
47-
RISCV_INFO(r);
4847

4948
char *str = buf_to_hex_str(buf, reg->size);
5049
LOG_TARGET_DEBUG(target, "Write 0x%s to %s (valid=%d).", str, reg->name,
@@ -57,17 +56,6 @@ static int riscv013_reg_set(struct reg *reg, uint8_t *buf)
5756
buf_get_u64(buf, 0, reg->size) == 0)
5857
return ERROR_OK;
5958

60-
if (reg->number == GDB_REGNO_TDATA1 ||
61-
reg->number == GDB_REGNO_TDATA2) {
62-
r->manual_hwbp_set = true;
63-
/* When enumerating triggers, we clear any triggers with DMODE set,
64-
* assuming they were left over from a previous debug session. So make
65-
* sure that is done before a user might be setting their own triggers.
66-
*/
67-
if (riscv_enumerate_triggers(target) != ERROR_OK)
68-
return ERROR_FAIL;
69-
}
70-
7159
if (reg->number >= GDB_REGNO_V0 && reg->number <= GDB_REGNO_V31) {
7260
if (riscv013_set_register_buf(target, reg->number, buf) != ERROR_OK)
7361
return ERROR_FAIL;

src/target/riscv/riscv.c

Lines changed: 115 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -527,6 +527,8 @@ static void riscv_deinit_target(struct target *target)
527527
if (!info)
528528
return;
529529

530+
free(info->reserved_triggers);
531+
530532
range_list_t *entry, *tmp;
531533
list_for_each_entry_safe(entry, tmp, &info->hide_csr, list) {
532534
free(entry->name);
@@ -620,6 +622,15 @@ static int find_first_trigger_by_id(struct target *target, int unique_id)
620622
static int set_trigger(struct target *target, unsigned int idx, riscv_reg_t tdata1, riscv_reg_t tdata2,
621623
riscv_reg_t tdata1_ignore_mask)
622624
{
625+
RISCV_INFO(r);
626+
assert(r->reserved_triggers);
627+
assert(idx < r->trigger_count);
628+
if (r->reserved_triggers[idx]) {
629+
LOG_TARGET_DEBUG(target,
630+
"Trigger %u is reserved by 'reserve_trigger' command.", idx);
631+
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
632+
}
633+
623634
riscv_reg_t tdata1_rb, tdata2_rb;
624635
// Select which trigger to use
625636
if (riscv_reg_set(target, GDB_REGNO_TSELECT, idx) != ERROR_OK)
@@ -2453,87 +2464,48 @@ static int riscv_deassert_reset(struct target *target)
24532464
return tt->deassert_reset(target);
24542465
}
24552466

2456-
/* state must be riscv_reg_t state[RISCV_MAX_HWBPS] = {0}; */
2457-
static int disable_triggers(struct target *target, riscv_reg_t *state)
2467+
/* "wp_is_set" array must have at least "r->trigger_count" items. */
2468+
static int disable_watchpoints(struct target *target, bool *wp_is_set)
24582469
{
24592470
RISCV_INFO(r);
2460-
24612471
LOG_TARGET_DEBUG(target, "Disabling triggers.");
24622472

2463-
if (riscv_enumerate_triggers(target) != ERROR_OK)
2464-
return ERROR_FAIL;
2465-
2466-
if (r->manual_hwbp_set) {
2467-
/* Look at every trigger that may have been set. */
2468-
riscv_reg_t tselect;
2469-
if (riscv_reg_get(target, &tselect, GDB_REGNO_TSELECT) != ERROR_OK)
2470-
return ERROR_FAIL;
2471-
for (unsigned int t = 0; t < r->trigger_count; t++) {
2472-
if (riscv_reg_set(target, GDB_REGNO_TSELECT, t) != ERROR_OK)
2473-
return ERROR_FAIL;
2474-
riscv_reg_t tdata1;
2475-
if (riscv_reg_get(target, &tdata1, GDB_REGNO_TDATA1) != ERROR_OK)
2473+
/* TODO: The algorithm is flawed and may result in a situation described in
2474+
* https://github.com/riscv-collab/riscv-openocd/issues/1108
2475+
*/
2476+
memset(wp_is_set, false, r->trigger_count);
2477+
struct watchpoint *watchpoint = target->watchpoints;
2478+
int i = 0;
2479+
while (watchpoint) {
2480+
LOG_TARGET_DEBUG(target, "Watchpoint %" PRIu32 ": set=%s",
2481+
watchpoint->unique_id,
2482+
wp_is_set[i] ? "true" : "false");
2483+
wp_is_set[i] = watchpoint->is_set;
2484+
if (watchpoint->is_set) {
2485+
if (riscv_remove_watchpoint(target, watchpoint) != ERROR_OK)
24762486
return ERROR_FAIL;
2477-
if (tdata1 & CSR_TDATA1_DMODE(riscv_xlen(target))) {
2478-
state[t] = tdata1;
2479-
if (riscv_reg_set(target, GDB_REGNO_TDATA1, 0) != ERROR_OK)
2480-
return ERROR_FAIL;
2481-
}
2482-
}
2483-
if (riscv_reg_set(target, GDB_REGNO_TSELECT, tselect) != ERROR_OK)
2484-
return ERROR_FAIL;
2485-
2486-
} else {
2487-
/* Just go through the triggers we manage. */
2488-
struct watchpoint *watchpoint = target->watchpoints;
2489-
int i = 0;
2490-
while (watchpoint) {
2491-
LOG_TARGET_DEBUG(target, "Watchpoint %d: set=%d", i, watchpoint->is_set);
2492-
state[i] = watchpoint->is_set;
2493-
if (watchpoint->is_set) {
2494-
if (riscv_remove_watchpoint(target, watchpoint) != ERROR_OK)
2495-
return ERROR_FAIL;
2496-
}
2497-
watchpoint = watchpoint->next;
2498-
i++;
24992487
}
2488+
watchpoint = watchpoint->next;
2489+
i++;
25002490
}
25012491

25022492
return ERROR_OK;
25032493
}
25042494

2505-
static int enable_triggers(struct target *target, riscv_reg_t *state)
2495+
static int enable_watchpoints(struct target *target, bool *wp_is_set)
25062496
{
2507-
RISCV_INFO(r);
2508-
2509-
if (r->manual_hwbp_set) {
2510-
/* Look at every trigger that may have been set. */
2511-
riscv_reg_t tselect;
2512-
if (riscv_reg_get(target, &tselect, GDB_REGNO_TSELECT) != ERROR_OK)
2513-
return ERROR_FAIL;
2514-
for (unsigned int t = 0; t < r->trigger_count; t++) {
2515-
if (state[t] != 0) {
2516-
if (riscv_reg_set(target, GDB_REGNO_TSELECT, t) != ERROR_OK)
2517-
return ERROR_FAIL;
2518-
if (riscv_reg_set(target, GDB_REGNO_TDATA1, state[t]) != ERROR_OK)
2519-
return ERROR_FAIL;
2520-
}
2521-
}
2522-
if (riscv_reg_set(target, GDB_REGNO_TSELECT, tselect) != ERROR_OK)
2523-
return ERROR_FAIL;
2524-
2525-
} else {
2526-
struct watchpoint *watchpoint = target->watchpoints;
2527-
int i = 0;
2528-
while (watchpoint) {
2529-
LOG_TARGET_DEBUG(target, "Watchpoint %d: cleared=%" PRId64, i, state[i]);
2530-
if (state[i]) {
2531-
if (riscv_add_watchpoint(target, watchpoint) != ERROR_OK)
2532-
return ERROR_FAIL;
2533-
}
2534-
watchpoint = watchpoint->next;
2535-
i++;
2497+
struct watchpoint *watchpoint = target->watchpoints;
2498+
int i = 0;
2499+
while (watchpoint) {
2500+
LOG_TARGET_DEBUG(target, "Watchpoint %" PRIu32
2501+
": %s to be re-enabled.", watchpoint->unique_id,
2502+
wp_is_set[i] ? "needs " : "does not need");
2503+
if (wp_is_set[i]) {
2504+
if (riscv_add_watchpoint(target, watchpoint) != ERROR_OK)
2505+
return ERROR_FAIL;
25362506
}
2507+
watchpoint = watchpoint->next;
2508+
i++;
25372509
}
25382510

25392511
return ERROR_OK;
@@ -3781,10 +3753,17 @@ static int riscv_openocd_step_impl(struct target *target, int current,
37813753
return ERROR_FAIL;
37823754
}
37833755

3784-
riscv_reg_t trigger_state[RISCV_MAX_HWBPS] = {0};
3785-
if (disable_triggers(target, trigger_state) != ERROR_OK)
3756+
if (riscv_enumerate_triggers(target) != ERROR_OK)
37863757
return ERROR_FAIL;
37873758

3759+
RISCV_INFO(r);
3760+
bool *wps_to_enable = calloc(sizeof(*wps_to_enable), r->trigger_count);
3761+
if (disable_watchpoints(target, wps_to_enable) != ERROR_OK) {
3762+
LOG_TARGET_ERROR(target, "Failed to temporarily disable "
3763+
"watchpoints before single-step.");
3764+
return ERROR_FAIL;
3765+
}
3766+
37883767
bool success = true;
37893768
uint64_t current_mstatus;
37903769
RISCV_INFO(info);
@@ -3814,9 +3793,10 @@ static int riscv_openocd_step_impl(struct target *target, int current,
38143793
}
38153794

38163795
_exit:
3817-
if (enable_triggers(target, trigger_state) != ERROR_OK) {
3796+
if (enable_watchpoints(target, wps_to_enable) != ERROR_OK) {
38183797
success = false;
3819-
LOG_TARGET_ERROR(target, "Unable to enable triggers.");
3798+
LOG_TARGET_ERROR(target, "Failed to re-enable watchpoints "
3799+
"after single-step.");
38203800
}
38213801

38223802
if (breakpoint && (riscv_add_breakpoint(target, breakpoint) != ERROR_OK)) {
@@ -5031,6 +5011,57 @@ COMMAND_HANDLER(riscv_set_enable_trigger_feature)
50315011
return ERROR_OK;
50325012
}
50335013

5014+
static COMMAND_HELPER(report_reserved_triggers, struct target *target)
5015+
{
5016+
RISCV_INFO(r);
5017+
if (riscv_enumerate_triggers(target) != ERROR_OK)
5018+
return ERROR_FAIL;
5019+
const char *separator = "";
5020+
for (riscv_reg_t t = 0; t < r->trigger_count; ++t) {
5021+
if (r->reserved_triggers[t]) {
5022+
command_print_sameline(CMD, "%s%" PRIu64, separator, t);
5023+
separator = " ";
5024+
}
5025+
}
5026+
command_print_sameline(CMD, "\n");
5027+
return ERROR_OK;
5028+
}
5029+
5030+
COMMAND_HANDLER(handle_reserve_trigger)
5031+
{
5032+
struct target *target = get_current_target(CMD_CTX);
5033+
if (CMD_ARGC == 0)
5034+
return CALL_COMMAND_HANDLER(report_reserved_triggers, target);
5035+
5036+
if (CMD_ARGC != 2)
5037+
return ERROR_COMMAND_SYNTAX_ERROR;
5038+
5039+
riscv_reg_t t;
5040+
COMMAND_PARSE_NUMBER(u64, CMD_ARGV[0], t);
5041+
5042+
if (riscv_enumerate_triggers(target) != ERROR_OK)
5043+
return ERROR_FAIL;
5044+
RISCV_INFO(r);
5045+
if (r->trigger_count == 0) {
5046+
command_print(CMD, "Error: There are no triggers on the target.");
5047+
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
5048+
}
5049+
if (t >= r->trigger_count) {
5050+
command_print(CMD, "Error: trigger with index %" PRIu64
5051+
" does not exist. There are only %u triggers"
5052+
" on the target (with indexes 0 .. %u).",
5053+
t, r->trigger_count, r->trigger_count - 1);
5054+
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
5055+
}
5056+
if (r->trigger_unique_id[t] != -1) {
5057+
command_print(CMD, "Error: trigger with index %" PRIu64
5058+
" is already in use and can not be reserved.", t);
5059+
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
5060+
}
5061+
COMMAND_PARSE_ON_OFF(CMD_ARGV[1], r->reserved_triggers[t]);
5062+
return ERROR_OK;
5063+
}
5064+
50345065
static const struct command_registration riscv_exec_command_handlers[] = {
50355066
{
50365067
.name = "dump_sample_buf",
@@ -5287,6 +5318,14 @@ static const struct command_registration riscv_exec_command_handlers[] = {
52875318
.usage = "[('eq'|'napot'|'ge_lt'|'all') ('wp'|'none')]",
52885319
.help = "Control whether OpenOCD is allowed to use certain RISC-V trigger features for watchpoints."
52895320
},
5321+
{
5322+
.name = "reserve_trigger",
5323+
.handler = handle_reserve_trigger,
5324+
/* TODO: Move this to COMMAND_ANY */
5325+
.mode = COMMAND_EXEC,
5326+
.usage = "[index ('on'|'off')]",
5327+
.help = "Controls which RISC-V triggers shall not be touched by OpenOCD.",
5328+
},
52905329
COMMAND_REGISTRATION_DONE
52915330
};
52925331

@@ -5711,6 +5750,8 @@ int riscv_enumerate_triggers(struct target *target)
57115750
"Assuming that triggers are not implemented.");
57125751
r->triggers_enumerated = true;
57135752
r->trigger_count = 0;
5753+
free(r->reserved_triggers);
5754+
r->reserved_triggers = NULL;
57145755
return ERROR_OK;
57155756
}
57165757

@@ -5745,6 +5786,8 @@ int riscv_enumerate_triggers(struct target *target)
57455786
r->triggers_enumerated = true;
57465787
r->trigger_count = t;
57475788
LOG_TARGET_INFO(target, "Found %d triggers", r->trigger_count);
5789+
free(r->reserved_triggers);
5790+
r->reserved_triggers = calloc(sizeof(*r->reserved_triggers), t);
57485791
create_wp_trigger_cache(target);
57495792
return ERROR_OK;
57505793
}

src/target/riscv/riscv.h

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -272,9 +272,7 @@ struct riscv_info {
272272
struct reg_data_type_union vector_union;
273273
struct reg_data_type type_vector;
274274

275-
/* Set when trigger registers are changed by the user. This indicates we need
276-
* to beware that we may hit a trigger that we didn't realize had been set. */
277-
bool manual_hwbp_set;
275+
bool *reserved_triggers;
278276

279277
/* Memory access methods to use, ordered by priority, highest to lowest. */
280278
int mem_access_methods[RISCV_NUM_MEM_ACCESS_METHODS];

0 commit comments

Comments
 (0)