target/riscv: cleanup riscv_enumerate_triggers()
1. Propagate error codes. 2. Disable leftover triggers in case `tinfo` is supported. Change-Id: Ie20edb4d8b9245e13ac8757bf6afe549ac99c4f1 Signed-off-by: Evgeniy Naydanov <evgeniy.naydanov@syntacore.com>
This commit is contained in:
parent
928f2b374a
commit
c286f938f4
|
@ -10734,7 +10734,9 @@ address, or to sample a changing value in a memory-mapped device.
|
|||
@end deffn
|
||||
|
||||
@deffn {Command} {riscv info}
|
||||
Displays some information OpenOCD detected about the target.
|
||||
Displays some information OpenOCD detected about the target. Output's format
|
||||
allows to use it directly with TCL's `array set` function. In case obtaining an
|
||||
info point failed, the corresponding value is displayed as "unavailable".
|
||||
@end deffn
|
||||
|
||||
@deffn {Command} {riscv reset_delays} [wait]
|
||||
|
|
|
@ -4162,15 +4162,25 @@ error:
|
|||
return result;
|
||||
}
|
||||
|
||||
COMMAND_HELPER(riscv_print_info_line, const char *section, const char *key,
|
||||
unsigned int value)
|
||||
static COMMAND_HELPER(riscv_print_info_line_if_available, const char *section,
|
||||
const char *key, unsigned int value, bool is_available)
|
||||
{
|
||||
char full_key[80];
|
||||
snprintf(full_key, sizeof(full_key), "%s.%s", section, key);
|
||||
command_print(CMD, "%-21s %3d", full_key, value);
|
||||
if (is_available)
|
||||
command_print(CMD, "%-21s %3d", full_key, value);
|
||||
else
|
||||
command_print(CMD, "%-21s unavailable", full_key);
|
||||
return 0;
|
||||
}
|
||||
|
||||
COMMAND_HELPER(riscv_print_info_line, const char *section, const char *key,
|
||||
unsigned int value)
|
||||
{
|
||||
return CALL_COMMAND_HANDLER(riscv_print_info_line_if_available, section,
|
||||
key, value, /*is_available*/ true);
|
||||
}
|
||||
|
||||
COMMAND_HANDLER(handle_info)
|
||||
{
|
||||
struct target *target = get_current_target(CMD_CTX);
|
||||
|
@ -4179,10 +4189,11 @@ COMMAND_HANDLER(handle_info)
|
|||
/* This output format can be fed directly into TCL's "array set". */
|
||||
|
||||
riscv_print_info_line(CMD, "hart", "xlen", riscv_xlen(target));
|
||||
riscv_enumerate_triggers(target);
|
||||
riscv_print_info_line(CMD, "hart", "trigger_count",
|
||||
r->trigger_count);
|
||||
|
||||
const bool trigger_count_available =
|
||||
riscv_enumerate_triggers(target) == ERROR_OK;
|
||||
riscv_print_info_line_if_available(CMD, "hart", "trigger_count",
|
||||
r->trigger_count, trigger_count_available);
|
||||
if (r->print_info)
|
||||
return CALL_COMMAND_HANDLER(r->print_info, target);
|
||||
|
||||
|
@ -5029,6 +5040,78 @@ int riscv_dmi_write_u64_bits(struct target *target)
|
|||
return r->dmi_write_u64_bits(target);
|
||||
}
|
||||
|
||||
static int check_if_trigger_exists(struct target *target, unsigned int index)
|
||||
{
|
||||
/* If we can't write tselect, then this hart does not support triggers. */
|
||||
if (riscv_set_register(target, GDB_REGNO_TSELECT, index) != ERROR_OK)
|
||||
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
|
||||
riscv_reg_t tselect_rb;
|
||||
if (riscv_get_register(target, &tselect_rb, GDB_REGNO_TSELECT) != ERROR_OK)
|
||||
return ERROR_FAIL;
|
||||
/* Mask off the top bit, which is used as tdrmode in legacy RISC-V Debug Spec
|
||||
* (old revisions of v0.11 spec). */
|
||||
tselect_rb &= ~(1ULL << (riscv_xlen(target) - 1));
|
||||
if (tselect_rb != index)
|
||||
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function reads `tinfo` or `tdata1`, when reading `tinfo` fails,
|
||||
* to determine trigger types supported by a trigger.
|
||||
* It is assumed that the trigger is already selected via writing `tselect`.
|
||||
*/
|
||||
static int get_trigger_types(struct target *target, unsigned int *trigger_tinfo,
|
||||
riscv_reg_t tdata1)
|
||||
{
|
||||
assert(trigger_tinfo);
|
||||
riscv_reg_t tinfo;
|
||||
if (riscv_get_register(target, &tinfo, GDB_REGNO_TINFO) == ERROR_OK) {
|
||||
/* tinfo.INFO == 1: trigger doesn’t exist
|
||||
* tinfo == 0 or tinfo.INFO != 1 and tinfo LSB is set: invalid tinfo */
|
||||
if (tinfo == 0 || tinfo & 0x1)
|
||||
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
|
||||
*trigger_tinfo = tinfo;
|
||||
return ERROR_OK;
|
||||
}
|
||||
const unsigned int type = get_field(tdata1, CSR_TDATA1_TYPE(riscv_xlen(target)));
|
||||
if (type == 0)
|
||||
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
|
||||
*trigger_tinfo = 1 << type;
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static int disable_trigger_if_dmode(struct target *target, riscv_reg_t tdata1)
|
||||
{
|
||||
bool dmode_is_set = false;
|
||||
switch (get_field(tdata1, CSR_TDATA1_TYPE(riscv_xlen(target)))) {
|
||||
case CSR_TDATA1_TYPE_LEGACY:
|
||||
/* On these older cores we don't support software using
|
||||
* triggers. */
|
||||
dmode_is_set = true;
|
||||
break;
|
||||
case CSR_TDATA1_TYPE_MCONTROL:
|
||||
dmode_is_set = tdata1 & CSR_MCONTROL_DMODE(riscv_xlen(target));
|
||||
break;
|
||||
case CSR_TDATA1_TYPE_MCONTROL6:
|
||||
dmode_is_set = tdata1 & CSR_MCONTROL6_DMODE(riscv_xlen(target));
|
||||
break;
|
||||
case CSR_TDATA1_TYPE_ICOUNT:
|
||||
dmode_is_set = tdata1 & CSR_ICOUNT_DMODE(riscv_xlen(target));
|
||||
break;
|
||||
case CSR_TDATA1_TYPE_ITRIGGER:
|
||||
dmode_is_set = tdata1 & CSR_ITRIGGER_DMODE(riscv_xlen(target));
|
||||
break;
|
||||
case CSR_TDATA1_TYPE_ETRIGGER:
|
||||
dmode_is_set = tdata1 & CSR_ETRIGGER_DMODE(riscv_xlen(target));
|
||||
break;
|
||||
}
|
||||
if (!dmode_is_set)
|
||||
/* Nothing to do */
|
||||
return ERROR_OK;
|
||||
return riscv_set_register(target, GDB_REGNO_TDATA1, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Count triggers, and initialize trigger_count for each hart.
|
||||
* trigger_count is initialized even if this function fails to discover
|
||||
|
@ -5043,89 +5126,54 @@ int riscv_enumerate_triggers(struct target *target)
|
|||
if (r->triggers_enumerated)
|
||||
return ERROR_OK;
|
||||
|
||||
r->triggers_enumerated = true; /* At the very least we tried. */
|
||||
if (target->state != TARGET_HALTED) {
|
||||
LOG_TARGET_ERROR(target, "Unable to enumerate triggers: target not halted.");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
riscv_reg_t tselect;
|
||||
int result = riscv_get_register(target, &tselect, GDB_REGNO_TSELECT);
|
||||
riscv_reg_t orig_tselect;
|
||||
int result = riscv_get_register(target, &orig_tselect, GDB_REGNO_TSELECT);
|
||||
/* If tselect is not readable, the trigger module is likely not
|
||||
* implemented. There are no triggers to enumerate then and no error
|
||||
* should be thrown. */
|
||||
* implemented. */
|
||||
if (result != ERROR_OK) {
|
||||
LOG_TARGET_DEBUG(target, "Cannot access tselect register. "
|
||||
LOG_TARGET_INFO(target, "Cannot access tselect register. "
|
||||
"Assuming that triggers are not implemented.");
|
||||
r->triggers_enumerated = true;
|
||||
r->trigger_count = 0;
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
for (unsigned int t = 0; t < RISCV_MAX_TRIGGERS; ++t) {
|
||||
r->trigger_count = t;
|
||||
|
||||
/* If we can't write tselect, then this hart does not support triggers. */
|
||||
if (riscv_set_register(target, GDB_REGNO_TSELECT, t) != ERROR_OK)
|
||||
break;
|
||||
uint64_t tselect_rb;
|
||||
result = riscv_get_register(target, &tselect_rb, GDB_REGNO_TSELECT);
|
||||
if (result != ERROR_OK)
|
||||
return result;
|
||||
/* Mask off the top bit, which is used as tdrmode in old
|
||||
* implementations. */
|
||||
tselect_rb &= ~(1ULL << (riscv_xlen(target) - 1));
|
||||
if (tselect_rb != t)
|
||||
unsigned int t = 0;
|
||||
for (; t < ARRAY_SIZE(r->trigger_tinfo); ++t) {
|
||||
result = check_if_trigger_exists(target, t);
|
||||
if (result == ERROR_FAIL)
|
||||
return ERROR_FAIL;
|
||||
if (result == ERROR_TARGET_RESOURCE_NOT_AVAILABLE)
|
||||
break;
|
||||
|
||||
uint64_t tinfo;
|
||||
result = riscv_get_register(target, &tinfo, GDB_REGNO_TINFO);
|
||||
if (result == ERROR_OK) {
|
||||
/* tinfo == 0 invalid tinfo
|
||||
* tinfo == 1 trigger doesn’t exist */
|
||||
if (tinfo == 0 || tinfo == 1)
|
||||
break;
|
||||
r->trigger_tinfo[t] = tinfo;
|
||||
} else {
|
||||
uint64_t tdata1;
|
||||
result = riscv_get_register(target, &tdata1, GDB_REGNO_TDATA1);
|
||||
if (result != ERROR_OK)
|
||||
return result;
|
||||
riscv_reg_t tdata1;
|
||||
if (riscv_get_register(target, &tdata1, GDB_REGNO_TDATA1) != ERROR_OK)
|
||||
return ERROR_FAIL;
|
||||
|
||||
int type = get_field(tdata1, CSR_TDATA1_TYPE(riscv_xlen(target)));
|
||||
if (type == 0)
|
||||
break;
|
||||
switch (type) {
|
||||
case CSR_TDATA1_TYPE_LEGACY:
|
||||
/* On these older cores we don't support software using
|
||||
* triggers. */
|
||||
riscv_set_register(target, GDB_REGNO_TDATA1, 0);
|
||||
break;
|
||||
case CSR_TDATA1_TYPE_MCONTROL:
|
||||
if (tdata1 & CSR_MCONTROL_DMODE(riscv_xlen(target)))
|
||||
riscv_set_register(target, GDB_REGNO_TDATA1, 0);
|
||||
break;
|
||||
case CSR_TDATA1_TYPE_MCONTROL6:
|
||||
if (tdata1 & CSR_MCONTROL6_DMODE(riscv_xlen(target)))
|
||||
riscv_set_register(target, GDB_REGNO_TDATA1, 0);
|
||||
break;
|
||||
case CSR_TDATA1_TYPE_ICOUNT:
|
||||
if (tdata1 & CSR_ICOUNT_DMODE(riscv_xlen(target)))
|
||||
riscv_set_register(target, GDB_REGNO_TDATA1, 0);
|
||||
break;
|
||||
case CSR_TDATA1_TYPE_ITRIGGER:
|
||||
if (tdata1 & CSR_ITRIGGER_DMODE(riscv_xlen(target)))
|
||||
riscv_set_register(target, GDB_REGNO_TDATA1, 0);
|
||||
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;
|
||||
}
|
||||
LOG_TARGET_DEBUG(target, "Trigger %u: supported types (mask) = 0x%08x", t, r->trigger_tinfo[t]);
|
||||
result = get_trigger_types(target, &r->trigger_tinfo[t], tdata1);
|
||||
if (result == ERROR_FAIL)
|
||||
return ERROR_FAIL;
|
||||
if (result == ERROR_TARGET_RESOURCE_NOT_AVAILABLE)
|
||||
break;
|
||||
|
||||
LOG_TARGET_DEBUG(target, "Trigger %u: supported types (mask) = 0x%08x",
|
||||
t, r->trigger_tinfo[t]);
|
||||
|
||||
if (disable_trigger_if_dmode(target, tdata1) != ERROR_OK)
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
riscv_set_register(target, GDB_REGNO_TSELECT, tselect);
|
||||
|
||||
LOG_TARGET_INFO(target, "Found %d triggers.", r->trigger_count);
|
||||
if (riscv_set_register(target, GDB_REGNO_TSELECT, orig_tselect) != ERROR_OK)
|
||||
return ERROR_FAIL;
|
||||
|
||||
r->triggers_enumerated = true;
|
||||
r->trigger_count = t;
|
||||
LOG_TARGET_INFO(target, "Found %d triggers", r->trigger_count);
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue