target/riscv: Add `riscv etrigger` command.

Change-Id: I7982231c5067b82e4ddb2999bca51dba06ccac7a
Signed-off-by: Tim Newsome <tim@sifive.com>
This commit is contained in:
Tim Newsome 2022-12-28 11:47:50 -08:00
parent 0b022a349e
commit 6c027e0df4
3 changed files with 133 additions and 0 deletions

View File

@ -10723,6 +10723,21 @@ cleanly onto OpenOCD's notion of hardware breakpoints. These commands let you
set those triggers directly. (It's also possible to do so by writing the set those triggers directly. (It's also possible to do so by writing the
appropriate CSRs.) appropriate CSRs.)
@deffn {Command} {riscv etrigger set} [@option{m}] [@option{s}] [@option{u}] [@option{vs}] [@option{vu}] exception_codes
Set an exception trigger (type 5) on the current target, which halts the target when it
fires. @option{m}, @option{s}, @option{u}, @option{vs}, and @option{vu} control
which execution modes the trigger fires in. @var{exception_codes} is a bit
field, where each bit corresponds to an exception code in mcause (defined in the
RISC-V Privileged Spec). The etrigger will fire on the exceptions whose bits are
set in @var{exception_codes}.
For details on this trigger type, see the RISC-V Debug Specification.
@end deffn
@deffn {Command} {riscv etrigger clear}
Clear the type 5 trigger that was set using @command{riscv etrigger set}.
@end deffn
@deffn {Command} {riscv itrigger set} [@option{m}] [@option{s}] [@option{u}] [@option{vs}] [@option{vu}] [@option{nmi}] mie_bits @deffn {Command} {riscv itrigger set} [@option{m}] [@option{s}] [@option{u}] [@option{vs}] [@option{vu}] [@option{nmi}] mie_bits
Set an interrupt trigger (type 4) on the current target, which halts the target when it Set an interrupt trigger (type 4) on the current target, which halts the target when it
fires. @option{m}, @option{s}, @option{u}, @option{vs}, and @option{vu} control fires. @option{m}, @option{s}, @option{u}, @option{vs}, and @option{vu} control

View File

@ -704,6 +704,37 @@ static int maybe_add_trigger_t4(struct target *target, bool vs, bool vu,
return ERROR_OK; return ERROR_OK;
} }
static int maybe_add_trigger_t5(struct target *target, bool vs, bool vu,
bool m, bool s, bool u, riscv_reg_t exception_codes,
int unique_id)
{
int idx, ret;
riscv_reg_t tdata1, tdata2;
RISCV_INFO(r);
tdata1 = 0;
tdata1 = set_field(tdata1, CSR_ETRIGGER_TYPE(riscv_xlen(target)), CSR_TDATA1_TYPE_ETRIGGER);
tdata1 = set_field(tdata1, CSR_ETRIGGER_DMODE(riscv_xlen(target)), 1);
tdata1 = set_field(tdata1, CSR_ETRIGGER_ACTION, CSR_ETRIGGER_ACTION_DEBUG_MODE);
tdata1 = set_field(tdata1, CSR_ETRIGGER_VS, vs);
tdata1 = set_field(tdata1, CSR_ETRIGGER_VU, vu);
tdata1 = set_field(tdata1, CSR_ETRIGGER_M, m);
tdata1 = set_field(tdata1, CSR_ETRIGGER_S, s);
tdata1 = set_field(tdata1, CSR_ETRIGGER_U, u);
tdata2 = exception_codes;
ret = find_trigger(target, CSR_TDATA1_TYPE_ETRIGGER, false, &idx);
if (ret != ERROR_OK)
return ret;
ret = set_trigger(target, idx, tdata1, tdata2, 0);
if (ret != ERROR_OK)
return ret;
r->trigger_unique_id[idx] = unique_id;
return ERROR_OK;
}
static int maybe_add_trigger_t6(struct target *target, struct trigger *trigger) static int maybe_add_trigger_t6(struct target *target, struct trigger *trigger)
{ {
int idx, ret; int idx, ret;
@ -1126,6 +1157,9 @@ static int riscv_hit_trigger_hit_bit(struct target *target, uint32_t *unique_id)
case CSR_TDATA1_TYPE_ITRIGGER: case CSR_TDATA1_TYPE_ITRIGGER:
hit_mask = CSR_ITRIGGER_HIT(riscv_xlen(target)); hit_mask = CSR_ITRIGGER_HIT(riscv_xlen(target));
break; break;
case CSR_TDATA1_TYPE_ETRIGGER:
hit_mask = CSR_ETRIGGER_HIT(riscv_xlen(target));
break;
default: default:
LOG_DEBUG("trigger %d has unknown type %d", i, type); LOG_DEBUG("trigger %d has unknown type %d", i, type);
continue; continue;
@ -3294,6 +3328,78 @@ COMMAND_HANDLER(riscv_itrigger)
return ERROR_OK; return ERROR_OK;
} }
COMMAND_HANDLER(riscv_etrigger)
{
if (CMD_ARGC < 1) {
LOG_ERROR("Command takes at least 1 parameter");
return ERROR_COMMAND_SYNTAX_ERROR;
}
struct target *target = get_current_target(CMD_CTX);
const int ETRIGGER_UNIQUE_ID = -CSR_TDATA1_TYPE_ETRIGGER;
if (riscv_enumerate_triggers(target) != ERROR_OK)
return ERROR_FAIL;
if (!strcmp(CMD_ARGV[0], "set")) {
if (find_first_trigger_by_id(target, ETRIGGER_UNIQUE_ID) >= 0) {
LOG_TARGET_ERROR(target, "An etrigger is already set, and OpenOCD "
"doesn't support setting more than one at a time.");
return ERROR_FAIL;
}
bool vs = false;
bool vu = false;
bool m = false;
bool s = false;
bool u = false;
riscv_reg_t exception_codes = 0;
for (unsigned int i = 1; i < CMD_ARGC; i++) {
if (!strcmp(CMD_ARGV[i], "vs"))
vs = true;
else if (!strcmp(CMD_ARGV[i], "vu"))
vu = true;
else if (!strcmp(CMD_ARGV[i], "m"))
m = true;
else if (!strcmp(CMD_ARGV[i], "s"))
s = true;
else if (!strcmp(CMD_ARGV[i], "u"))
u = true;
else
COMMAND_PARSE_NUMBER(u64, CMD_ARGV[i], exception_codes);
}
if (exception_codes == 0) {
LOG_ERROR("Doesn't make sense to set etrigger with "
"exception_codes=0.");
return ERROR_FAIL;
} else if (!vs && !vu && !m && !s && !u) {
LOG_ERROR("Doesn't make sense to set etrigger without at "
"least one of vs, vu, m, s, or u.");
return ERROR_FAIL;
}
int result = maybe_add_trigger_t5(target, vs, vu, m, s, u, exception_codes, ETRIGGER_UNIQUE_ID);
if (result != ERROR_OK)
LOG_TARGET_ERROR(target, "Failed to set requested etrigger.");
return result;
} else if (!strcmp(CMD_ARGV[0], "clear")) {
if (CMD_ARGC != 1) {
LOG_ERROR("clear command takes no extra arguments.");
return ERROR_COMMAND_SYNTAX_ERROR;
}
if (find_first_trigger_by_id(target, ETRIGGER_UNIQUE_ID) < 0) {
LOG_TARGET_ERROR(target, "No etrigger is set. Nothing to clear.");
return ERROR_FAIL;
}
return remove_trigger(target, ETRIGGER_UNIQUE_ID);
} else {
LOG_ERROR("First argument must be either 'set' or 'clear'.");
return ERROR_COMMAND_SYNTAX_ERROR;
}
return ERROR_OK;
}
COMMAND_HANDLER(handle_repeat_read) COMMAND_HANDLER(handle_repeat_read)
{ {
struct target *target = get_current_target(CMD_CTX); struct target *target = get_current_target(CMD_CTX);
@ -3711,6 +3817,13 @@ static const struct command_registration riscv_exec_command_handlers[] = {
.help = "Control dcsr.ebreaku. When off, U-mode ebreak instructions " .help = "Control dcsr.ebreaku. When off, U-mode ebreak instructions "
"don't trap to OpenOCD. Defaults to on." "don't trap to OpenOCD. Defaults to on."
}, },
{
.name = "etrigger",
.handler = riscv_etrigger,
.mode = COMMAND_EXEC,
.usage = "set [vs] [vu] [m] [s] [u] <exception_codes>|clear",
.help = "Set or clear a single exception trigger."
},
{ {
.name = "itrigger", .name = "itrigger",
.handler = riscv_itrigger, .handler = riscv_itrigger,
@ -4265,6 +4378,10 @@ int riscv_enumerate_triggers(struct target *target)
if (tdata1 & CSR_ITRIGGER_DMODE(riscv_xlen(target))) if (tdata1 & CSR_ITRIGGER_DMODE(riscv_xlen(target)))
riscv_set_register(target, GDB_REGNO_TDATA1, 0); riscv_set_register(target, GDB_REGNO_TDATA1, 0);
break; break;
case CSR_TDATA1_TYPE_ETRIGGER:
if (tdata1 & CSR_ETRIGGER_DMODE(riscv_xlen(target)))
riscv_set_register(target, GDB_REGNO_TDATA1, 0);
break;
} }
r->trigger_tinfo[t] = 1 << type; r->trigger_tinfo[t] = 1 << type;
} }

View File

@ -127,6 +127,7 @@ typedef struct {
/* For each physical trigger contains: /* For each physical trigger contains:
* -1: the hwbp is available * -1: the hwbp is available
* -4: The trigger is used by the itrigger command * -4: The trigger is used by the itrigger command
* -5: The trigger is used by the etrigger command
* >= 0: unique_id of the breakpoint/watchpoint that is using it. * >= 0: unique_id of the breakpoint/watchpoint that is using it.
* Note that in RTOS mode the triggers are the same across all harts the * Note that in RTOS mode the triggers are the same across all harts the
* target controls, while otherwise only a single hart is controlled. */ * target controls, while otherwise only a single hart is controlled. */