Merge pull request #1144 from sunnyzhu-learning/resume-before-step-develop
target/riscv:Perform single step before resume if necessary
This commit is contained in:
commit
1bf7efb2d5
|
@ -648,12 +648,12 @@ static int find_first_trigger_by_id(struct target *target, int unique_id)
|
||||||
|
|
||||||
static unsigned int count_trailing_ones(riscv_reg_t reg)
|
static unsigned int count_trailing_ones(riscv_reg_t reg)
|
||||||
{
|
{
|
||||||
assert(sizeof(riscv_reg_t) * 8 == 64);
|
const unsigned int riscv_reg_bits = sizeof(riscv_reg_t) * CHAR_BIT;
|
||||||
for (unsigned int i = 0; i < 64; i++) {
|
for (unsigned int i = 0; i < riscv_reg_bits; i++) {
|
||||||
if ((1 & (reg >> i)) == 0)
|
if ((1 & (reg >> i)) == 0)
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
return 64;
|
return riscv_reg_bits;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int set_trigger(struct target *target, unsigned int idx, riscv_reg_t tdata1, riscv_reg_t tdata2)
|
static int set_trigger(struct target *target, unsigned int idx, riscv_reg_t tdata1, riscv_reg_t tdata2)
|
||||||
|
@ -1586,21 +1586,75 @@ int riscv_remove_watchpoint(struct target *target,
|
||||||
return ERROR_OK;
|
return ERROR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
M6_HIT_ERROR,
|
||||||
|
M6_HIT_NOT_SUPPORTED,
|
||||||
|
M6_NOT_HIT,
|
||||||
|
M6_HIT_BEFORE,
|
||||||
|
M6_HIT_AFTER,
|
||||||
|
M6_HIT_IMM_AFTER
|
||||||
|
} mctrl6hitstatus;
|
||||||
|
|
||||||
|
static mctrl6hitstatus check_mcontrol6_hit_status(struct target *target,
|
||||||
|
riscv_reg_t tdata1, uint64_t hit_mask)
|
||||||
|
{
|
||||||
|
const uint32_t hit0 = get_field(tdata1, CSR_MCONTROL6_HIT0);
|
||||||
|
const uint32_t hit1 = get_field(tdata1, CSR_MCONTROL6_HIT1);
|
||||||
|
const uint32_t hit_info = (hit1 << 1) | hit0;
|
||||||
|
if (hit_info == CSR_MCONTROL6_HIT0_BEFORE)
|
||||||
|
return M6_HIT_BEFORE;
|
||||||
|
|
||||||
|
if (hit_info == CSR_MCONTROL6_HIT0_AFTER)
|
||||||
|
return M6_HIT_AFTER;
|
||||||
|
|
||||||
|
if (hit_info == CSR_MCONTROL6_HIT0_IMMEDIATELY_AFTER)
|
||||||
|
return M6_HIT_IMM_AFTER;
|
||||||
|
|
||||||
|
if (hit_info == CSR_MCONTROL6_HIT0_FALSE) {
|
||||||
|
/* hit[1..0] equals 0, which can mean one of the following:
|
||||||
|
* - "hit" bits are supported and this trigger has not fired
|
||||||
|
* - "hit" bits are not supported on this trigger
|
||||||
|
* To distinguish these two cases, try writing all non-zero bit
|
||||||
|
* patterns to hit[1..0] to determine if the "hit" bits are supported:
|
||||||
|
*/
|
||||||
|
riscv_reg_t tdata1_tests[] = {
|
||||||
|
set_field(tdata1, CSR_MCONTROL6_HIT0, 1),
|
||||||
|
set_field(tdata1, CSR_MCONTROL6_HIT1, 1),
|
||||||
|
set_field(tdata1, CSR_MCONTROL6_HIT0, 1) | field_value(CSR_MCONTROL6_HIT1, 1)
|
||||||
|
};
|
||||||
|
riscv_reg_t tdata1_test_rb;
|
||||||
|
for (uint64_t i = 0; i < ARRAY_SIZE(tdata1_tests); ++i) {
|
||||||
|
if (riscv_reg_set(target, GDB_REGNO_TDATA1, tdata1_tests[i]) != ERROR_OK)
|
||||||
|
return M6_HIT_ERROR;
|
||||||
|
if (riscv_reg_get(target, &tdata1_test_rb, GDB_REGNO_TDATA1) != ERROR_OK)
|
||||||
|
return M6_HIT_ERROR;
|
||||||
|
if (tdata1_test_rb == tdata1_tests[i]) {
|
||||||
|
if (riscv_reg_set(target, GDB_REGNO_TDATA1, tdata1_test_rb & ~hit_mask) != ERROR_OK)
|
||||||
|
return M6_HIT_ERROR;
|
||||||
|
return M6_NOT_HIT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return M6_HIT_NOT_SUPPORTED;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Look at the trigger hit bits to find out which trigger is the reason we're
|
* Look at the trigger hit bits to find out which trigger is the reason we're
|
||||||
* halted. Sets *unique_id to the unique ID of that trigger. If *unique_id is
|
* halted. Sets *unique_id to the unique ID of that trigger. If *unique_id is
|
||||||
* RISCV_TRIGGER_HIT_NOT_FOUND, no match was found.
|
* RISCV_TRIGGER_HIT_NOT_FOUND, no match was found.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static int riscv_trigger_detect_hit_bits(struct target *target, int64_t *unique_id)
|
static int riscv_trigger_detect_hit_bits(struct target *target, int64_t *unique_id,
|
||||||
|
bool *need_single_step)
|
||||||
{
|
{
|
||||||
/* FIXME: this function assumes that we have only one trigger that can
|
/* FIXME: this function assumes that we have only one trigger that can
|
||||||
* have hit bit set. Debug spec allows hit bit to bit set if a trigger has
|
* have hit bit set. Debug spec allows hit bit to bit set if a trigger has
|
||||||
* matched but did not fire. Such targets will receive erroneous results.
|
* matched but did not fire. Such targets will receive erroneous results.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// FIXME: Add hit bits support detection and caching
|
|
||||||
RISCV_INFO(r);
|
RISCV_INFO(r);
|
||||||
|
assert(need_single_step);
|
||||||
|
*need_single_step = false;
|
||||||
|
|
||||||
riscv_reg_t tselect;
|
riscv_reg_t tselect;
|
||||||
if (riscv_reg_get(target, &tselect, GDB_REGNO_TSELECT) != ERROR_OK)
|
if (riscv_reg_get(target, &tselect, GDB_REGNO_TSELECT) != ERROR_OK)
|
||||||
|
@ -1626,9 +1680,21 @@ static int riscv_trigger_detect_hit_bits(struct target *target, int64_t *unique_
|
||||||
break;
|
break;
|
||||||
case CSR_TDATA1_TYPE_MCONTROL:
|
case CSR_TDATA1_TYPE_MCONTROL:
|
||||||
hit_mask = CSR_MCONTROL_HIT;
|
hit_mask = CSR_MCONTROL_HIT;
|
||||||
|
*need_single_step = true;
|
||||||
break;
|
break;
|
||||||
case CSR_TDATA1_TYPE_MCONTROL6:
|
case CSR_TDATA1_TYPE_MCONTROL6:
|
||||||
hit_mask = CSR_MCONTROL6_HIT0 | CSR_MCONTROL6_HIT1;
|
hit_mask = CSR_MCONTROL6_HIT0 | CSR_MCONTROL6_HIT1;
|
||||||
|
if (r->tinfo_version == CSR_TINFO_VERSION_0) {
|
||||||
|
*need_single_step = true;
|
||||||
|
} else if (r->tinfo_version == RISCV_TINFO_VERSION_UNKNOWN
|
||||||
|
|| r->tinfo_version == CSR_TINFO_VERSION_1) {
|
||||||
|
mctrl6hitstatus hits_status = check_mcontrol6_hit_status(target,
|
||||||
|
tdata1, hit_mask);
|
||||||
|
if (hits_status == M6_HIT_ERROR)
|
||||||
|
return ERROR_FAIL;
|
||||||
|
if (hits_status == M6_HIT_BEFORE || hits_status == M6_HIT_NOT_SUPPORTED)
|
||||||
|
*need_single_step = true;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case CSR_TDATA1_TYPE_ICOUNT:
|
case CSR_TDATA1_TYPE_ICOUNT:
|
||||||
hit_mask = CSR_ICOUNT_HIT;
|
hit_mask = CSR_ICOUNT_HIT;
|
||||||
|
@ -1647,8 +1713,9 @@ static int riscv_trigger_detect_hit_bits(struct target *target, int64_t *unique_
|
||||||
/* FIXME: this logic needs to be changed to ignore triggers that are not
|
/* FIXME: this logic needs to be changed to ignore triggers that are not
|
||||||
* the last one in the chain. */
|
* the last one in the chain. */
|
||||||
if (tdata1 & hit_mask) {
|
if (tdata1 & hit_mask) {
|
||||||
LOG_TARGET_DEBUG(target, "Trigger %u (unique_id=%" PRIi64 ") has hit bit set.",
|
LOG_TARGET_DEBUG(target, "Trigger %u (unique_id=%" PRIi64
|
||||||
i, r->trigger_unique_id[i]);
|
") has hit bit set. (need_single_step=%s)",
|
||||||
|
i, r->trigger_unique_id[i], (*need_single_step) ? "yes" : "no");
|
||||||
if (riscv_reg_set(target, GDB_REGNO_TDATA1, tdata1 & ~hit_mask) != ERROR_OK)
|
if (riscv_reg_set(target, GDB_REGNO_TDATA1, tdata1 & ~hit_mask) != ERROR_OK)
|
||||||
return ERROR_FAIL;
|
return ERROR_FAIL;
|
||||||
|
|
||||||
|
@ -2310,13 +2377,15 @@ static int set_debug_reason(struct target *target, enum riscv_halt_reason halt_r
|
||||||
{
|
{
|
||||||
RISCV_INFO(r);
|
RISCV_INFO(r);
|
||||||
r->trigger_hit = -1;
|
r->trigger_hit = -1;
|
||||||
|
r->need_single_step = false;
|
||||||
switch (halt_reason) {
|
switch (halt_reason) {
|
||||||
case RISCV_HALT_EBREAK:
|
case RISCV_HALT_EBREAK:
|
||||||
target->debug_reason = DBG_REASON_BREAKPOINT;
|
target->debug_reason = DBG_REASON_BREAKPOINT;
|
||||||
break;
|
break;
|
||||||
case RISCV_HALT_TRIGGER:
|
case RISCV_HALT_TRIGGER:
|
||||||
target->debug_reason = DBG_REASON_UNDEFINED;
|
target->debug_reason = DBG_REASON_UNDEFINED;
|
||||||
if (riscv_trigger_detect_hit_bits(target, &r->trigger_hit) != ERROR_OK)
|
if (riscv_trigger_detect_hit_bits(target, &r->trigger_hit,
|
||||||
|
&r->need_single_step) != ERROR_OK)
|
||||||
return ERROR_FAIL;
|
return ERROR_FAIL;
|
||||||
// FIXME: handle multiple hit bits
|
// FIXME: handle multiple hit bits
|
||||||
if (r->trigger_hit != RISCV_TRIGGER_HIT_NOT_FOUND) {
|
if (r->trigger_hit != RISCV_TRIGGER_HIT_NOT_FOUND) {
|
||||||
|
@ -2578,10 +2647,19 @@ static int resume_prep(struct target *target, int current,
|
||||||
if (handle_breakpoints) {
|
if (handle_breakpoints) {
|
||||||
/* To be able to run off a trigger, we perform a step operation and then
|
/* To be able to run off a trigger, we perform a step operation and then
|
||||||
* resume. If handle_breakpoints is true then step temporarily disables
|
* resume. If handle_breakpoints is true then step temporarily disables
|
||||||
* pending breakpoints so we can safely perform the step. */
|
* pending breakpoints so we can safely perform the step.
|
||||||
if (old_or_new_riscv_step_impl(target, current, address, handle_breakpoints,
|
*
|
||||||
false /* callbacks are not called */) != ERROR_OK)
|
* Two cases where single step is needed before resuming:
|
||||||
return ERROR_FAIL;
|
* 1. ebreak used in software breakpoint;
|
||||||
|
* 2. a trigger that is taken just before the instruction that triggered it is retired.
|
||||||
|
*/
|
||||||
|
if (target->debug_reason == DBG_REASON_BREAKPOINT
|
||||||
|
|| (target->debug_reason == DBG_REASON_WATCHPOINT
|
||||||
|
&& r->need_single_step)) {
|
||||||
|
if (old_or_new_riscv_step_impl(target, current, address, handle_breakpoints,
|
||||||
|
false /* callbacks are not called */) != ERROR_OK)
|
||||||
|
return ERROR_FAIL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (r->get_hart_state) {
|
if (r->get_hart_state) {
|
||||||
|
@ -5848,6 +5926,19 @@ int riscv_enumerate_triggers(struct target *target)
|
||||||
return ERROR_OK;
|
return ERROR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Obtaining tinfo.version value once.
|
||||||
|
* No need to enumerate per-trigger.
|
||||||
|
* See https://github.com/riscv/riscv-debug-spec/pull/1081.
|
||||||
|
*/
|
||||||
|
riscv_reg_t tinfo;
|
||||||
|
if (riscv_reg_get(target, &tinfo, GDB_REGNO_TINFO) == ERROR_OK) {
|
||||||
|
r->tinfo_version = get_field(tinfo, CSR_TINFO_VERSION);
|
||||||
|
LOG_TARGET_DEBUG(target, "Trigger tinfo.version = %d.", r->tinfo_version);
|
||||||
|
} else {
|
||||||
|
r->tinfo_version = RISCV_TINFO_VERSION_UNKNOWN;
|
||||||
|
LOG_TARGET_DEBUG(target, "Trigger tinfo.version is unknown.");
|
||||||
|
}
|
||||||
|
|
||||||
unsigned int t = 0;
|
unsigned int t = 0;
|
||||||
for (; t < ARRAY_SIZE(r->trigger_tinfo); ++t) {
|
for (; t < ARRAY_SIZE(r->trigger_tinfo); ++t) {
|
||||||
result = check_if_trigger_exists(target, t);
|
result = check_if_trigger_exists(target, t);
|
||||||
|
|
|
@ -123,6 +123,7 @@ typedef struct {
|
||||||
} range_list_t;
|
} range_list_t;
|
||||||
|
|
||||||
#define DTM_DTMCS_VERSION_UNKNOWN ((unsigned int)-1)
|
#define DTM_DTMCS_VERSION_UNKNOWN ((unsigned int)-1)
|
||||||
|
#define RISCV_TINFO_VERSION_UNKNOWN (-1)
|
||||||
|
|
||||||
struct reg_name_table {
|
struct reg_name_table {
|
||||||
unsigned int num_entries;
|
unsigned int num_entries;
|
||||||
|
@ -163,6 +164,17 @@ struct riscv_info {
|
||||||
/* record the tinfo of each trigger */
|
/* record the tinfo of each trigger */
|
||||||
unsigned int trigger_tinfo[RISCV_MAX_TRIGGERS];
|
unsigned int trigger_tinfo[RISCV_MAX_TRIGGERS];
|
||||||
|
|
||||||
|
/* Version of the implemented Sdtrig extension */
|
||||||
|
int tinfo_version;
|
||||||
|
|
||||||
|
/* Record if single-step is needed prior to resuming
|
||||||
|
* from a software breakpoint or trigger.
|
||||||
|
* Single-step is needed if the instruction that
|
||||||
|
* caused the halt was not retired. That is,
|
||||||
|
* when we halted "before" that instruction.
|
||||||
|
*/
|
||||||
|
bool need_single_step;
|
||||||
|
|
||||||
/* 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
|
||||||
|
|
Loading…
Reference in New Issue