Skip to content

Commit d58c656

Browse files
authored
Merge pull request #1111 from en-sc/en-sc/ref-reg-manual-hwbp
target/riscv: manage triggers available to OpenOCD for internal use
2 parents d7a7c98 + 5a8697b commit d58c656

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
@@ -11562,6 +11562,21 @@ as in the mie CSR (defined in the RISC-V Privileged Spec).
1156211562
For details on this trigger type, see the RISC-V Debug Specification.
1156311563
@end deffn
1156411564

11565+
@deffn {Command} {riscv reserve_trigger} [index @option{on|off}]
11566+
Manages the set of reserved triggers. Reserving a trigger results in OpenOCD
11567+
not using it internally (e.g. skipping it when setting a watchpoint or a
11568+
hardware breakpoint), so that the user or the application has unfettered
11569+
control over the trigger. By default there are no reserved triggers.
11570+
11571+
@enumerate
11572+
@item @var{index} specifies the index of a trigger to reserve or free up.
11573+
@item The second argument specifies whether the trigger should be reserved
11574+
(@var{on}) or a prior reservation cancelled (@var{off}).
11575+
@item If called without parameters, returns indices of reserved triggers.
11576+
@end enumerate
11577+
11578+
@end deffn
11579+
1156511580
@deffn {Command} {riscv itrigger clear}
1156611581
Clear the type 4 trigger that was set using @command{riscv itrigger set}.
1156711582
@end deffn

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

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

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

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

src/target/riscv/riscv.c

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

532+
free(info->reserved_triggers);
533+
532534
range_list_t *entry, *tmp;
533535
list_for_each_entry_safe(entry, tmp, &info->hide_csr, list) {
534536
free(entry->name);
@@ -622,6 +624,15 @@ static int find_first_trigger_by_id(struct target *target, int unique_id)
622624
static int set_trigger(struct target *target, unsigned int idx, riscv_reg_t tdata1, riscv_reg_t tdata2,
623625
riscv_reg_t tdata1_ignore_mask)
624626
{
627+
RISCV_INFO(r);
628+
assert(r->reserved_triggers);
629+
assert(idx < r->trigger_count);
630+
if (r->reserved_triggers[idx]) {
631+
LOG_TARGET_DEBUG(target,
632+
"Trigger %u is reserved by 'reserve_trigger' command.", idx);
633+
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
634+
}
635+
625636
riscv_reg_t tdata1_rb, tdata2_rb;
626637
// Select which trigger to use
627638
if (riscv_reg_set(target, GDB_REGNO_TSELECT, idx) != ERROR_OK)
@@ -2455,87 +2466,48 @@ static int riscv_deassert_reset(struct target *target)
24552466
return tt->deassert_reset(target);
24562467
}
24572468

2458-
/* state must be riscv_reg_t state[RISCV_MAX_HWBPS] = {0}; */
2459-
static int disable_triggers(struct target *target, riscv_reg_t *state)
2469+
/* "wp_is_set" array must have at least "r->trigger_count" items. */
2470+
static int disable_watchpoints(struct target *target, bool *wp_is_set)
24602471
{
24612472
RISCV_INFO(r);
2462-
24632473
LOG_TARGET_DEBUG(target, "Disabling triggers.");
24642474

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

25042494
return ERROR_OK;
25052495
}
25062496

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

25412513
return ERROR_OK;
@@ -3783,10 +3755,17 @@ static int riscv_openocd_step_impl(struct target *target, int current,
37833755
return ERROR_FAIL;
37843756
}
37853757

3786-
riscv_reg_t trigger_state[RISCV_MAX_HWBPS] = {0};
3787-
if (disable_triggers(target, trigger_state) != ERROR_OK)
3758+
if (riscv_enumerate_triggers(target) != ERROR_OK)
37883759
return ERROR_FAIL;
37893760

3761+
RISCV_INFO(r);
3762+
bool *wps_to_enable = calloc(sizeof(*wps_to_enable), r->trigger_count);
3763+
if (disable_watchpoints(target, wps_to_enable) != ERROR_OK) {
3764+
LOG_TARGET_ERROR(target, "Failed to temporarily disable "
3765+
"watchpoints before single-step.");
3766+
return ERROR_FAIL;
3767+
}
3768+
37903769
bool success = true;
37913770
uint64_t current_mstatus;
37923771
RISCV_INFO(info);
@@ -3816,9 +3795,10 @@ static int riscv_openocd_step_impl(struct target *target, int current,
38163795
}
38173796

38183797
_exit:
3819-
if (enable_triggers(target, trigger_state) != ERROR_OK) {
3798+
if (enable_watchpoints(target, wps_to_enable) != ERROR_OK) {
38203799
success = false;
3821-
LOG_TARGET_ERROR(target, "Unable to enable triggers.");
3800+
LOG_TARGET_ERROR(target, "Failed to re-enable watchpoints "
3801+
"after single-step.");
38223802
}
38233803

38243804
if (breakpoint && (riscv_add_breakpoint(target, breakpoint) != ERROR_OK)) {
@@ -5038,6 +5018,57 @@ COMMAND_HANDLER(riscv_set_enable_trigger_feature)
50385018
return ERROR_OK;
50395019
}
50405020

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

@@ -5714,6 +5753,8 @@ int riscv_enumerate_triggers(struct target *target)
57145753
"Assuming that triggers are not implemented.");
57155754
r->triggers_enumerated = true;
57165755
r->trigger_count = 0;
5756+
free(r->reserved_triggers);
5757+
r->reserved_triggers = NULL;
57175758
return ERROR_OK;
57185759
}
57195760

@@ -5748,6 +5789,8 @@ int riscv_enumerate_triggers(struct target *target)
57485789
r->triggers_enumerated = true;
57495790
r->trigger_count = t;
57505791
LOG_TARGET_INFO(target, "Found %d triggers", r->trigger_count);
5792+
free(r->reserved_triggers);
5793+
r->reserved_triggers = calloc(sizeof(*r->reserved_triggers), t);
57515794
create_wp_trigger_cache(target);
57525795
return ERROR_OK;
57535796
}

src/target/riscv/riscv.h

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -269,9 +269,7 @@ struct riscv_info {
269269
struct reg_data_type_union vector_union;
270270
struct reg_data_type type_vector;
271271

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

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

0 commit comments

Comments
 (0)