Skip to content

Commit

Permalink
target/riscv: cleanup riscv_enumerate_triggers()
Browse files Browse the repository at this point in the history
1. Propagate error codes.
2. Disable leftover triggers in case `tinfo` is supported.

Change-Id: Ie20edb4d8b9245e13ac8757bf6afe549ac99c4f1
Signed-off-by: Evgeniy Naydanov <[email protected]>
  • Loading branch information
en-sc committed Aug 28, 2023
1 parent 928f2b3 commit d4cdb99
Showing 1 changed file with 120 additions and 77 deletions.
197 changes: 120 additions & 77 deletions src/target/riscv/riscv.c
Original file line number Diff line number Diff line change
Expand Up @@ -4162,15 +4162,25 @@ COMMAND_HANDLER(handle_dump_sample_buf_command)
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);
Expand All @@ -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);

Expand Down Expand Up @@ -5029,6 +5040,73 @@ int riscv_dmi_write_u64_bits(struct target *target)
return r->dmi_write_u64_bits(target);
}

static int check_if_trigger_is_availible(struct target *target, unsigned int 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)
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 old
* implementations. */
tselect_rb &= ~(1ULL << (riscv_xlen(target) - 1));
if (tselect_rb != t)
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
return ERROR_OK;
}

static int get_trigger_types(struct target *target, unsigned int *trigger_tinfo,
riscv_reg_t tdata1)
{
riscv_reg_t tinfo;
const bool may_support_multiple_trigger_types =
riscv_get_register(target, &tinfo, GDB_REGNO_TINFO) == ERROR_OK;
if (may_support_multiple_trigger_types) {
/* tinfo == 0: invalid
* tinfo == 1: trigger doesn’t exist */
if (tinfo == 0 || tinfo == 1)
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_leftover(struct target *target, riscv_reg_t tdata1)
{
bool is_leftover = 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. */
is_leftover = true;
break;
case CSR_TDATA1_TYPE_MCONTROL:
is_leftover = tdata1 & CSR_MCONTROL_DMODE(riscv_xlen(target));
break;
case CSR_TDATA1_TYPE_MCONTROL6:
is_leftover = tdata1 & CSR_MCONTROL6_DMODE(riscv_xlen(target));
break;
case CSR_TDATA1_TYPE_ICOUNT:
is_leftover = tdata1 & CSR_ICOUNT_DMODE(riscv_xlen(target));
break;
case CSR_TDATA1_TYPE_ITRIGGER:
is_leftover = tdata1 & CSR_ITRIGGER_DMODE(riscv_xlen(target));
break;
case CSR_TDATA1_TYPE_ETRIGGER:
is_leftover = tdata1 & CSR_ETRIGGER_DMODE(riscv_xlen(target));
break;
}
if (is_leftover && riscv_set_register(target, GDB_REGNO_TDATA1, 0) != ERROR_OK)
return ERROR_FAIL;
return ERROR_OK;
}

/**
* Count triggers, and initialize trigger_count for each hart.
* trigger_count is initialized even if this function fails to discover
Expand All @@ -5043,89 +5121,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. "
"Assuming that triggers are not implemented.");
r->trigger_count = 0;
return ERROR_OK;
LOG_DEBUG("[%s] Cannot access tselect register. "
"Assuming that triggers are not implemented.", target_name(target));
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
}

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)
unsigned int t = 0;
unsigned int trigger_tinfo[ARRAY_SIZE(r->trigger_tinfo)];
for (; t < ARRAY_SIZE(r->trigger_tinfo); ++t) {
result = check_if_trigger_is_availible(target, t);
if (result == ERROR_FAIL)
return ERROR_FAIL;
if (result == ERROR_TARGET_RESOURCE_NOT_AVAILABLE)
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)

riscv_reg_t tdata1;
if (riscv_get_register(target, &tdata1, GDB_REGNO_TDATA1) != ERROR_OK)
return ERROR_FAIL;

result = get_trigger_types(target, &trigger_tinfo[t], tdata1);
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;
LOG_TARGET_DEBUG(target, "Trigger %u: supported types (mask) = 0x%08x",
t, trigger_tinfo[t]);

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]);
if (disable_trigger_if_leftover(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;
memcpy(r->trigger_tinfo, trigger_tinfo, r->trigger_count * sizeof(*trigger_tinfo));
LOG_TARGET_INFO(target, "Found %d triggers", r->trigger_count);
return ERROR_OK;
}

Expand Down

0 comments on commit d4cdb99

Please sign in to comment.