Skip to content

Commit

Permalink
target/riscv:Perform single step before resume if necessary
Browse files Browse the repository at this point in the history
Two cases where single step is needed before resume:
1. ebreak used in software breakpoint;
2. a trigger that is taken just before the instruction
   that triggered it is retired.

Signed-off-by: Songhe Zhu <zhusonghe@eswincomputing.com> Co-developed-by: Fei Gao <gaofei@eswincomputing.com> Co-developed-by: xiatianyi <xiatianyi@eswincomputing.com>
  • Loading branch information
zhusonghe committed Oct 21, 2024
1 parent a4020f1 commit 5f65be9
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 7 deletions.
94 changes: 87 additions & 7 deletions src/target/riscv/riscv.c
Original file line number Diff line number Diff line change
Expand Up @@ -623,12 +623,12 @@ static int find_first_trigger_by_id(struct target *target, int unique_id)

static unsigned int count_trailing_ones(riscv_reg_t reg)
{
assert(sizeof(riscv_reg_t) * 8 == 64);
for (unsigned int i = 0; i < 64; i++) {
const unsigned int riscv_reg_bits = sizeof(riscv_reg_t) * CHAR_BIT;
for (unsigned int i = 0; i < riscv_reg_bits; i++) {
if ((1 & (reg >> i)) == 0)
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)
Expand Down Expand Up @@ -1561,6 +1561,59 @@ int riscv_remove_watchpoint(struct target *target,
return ERROR_OK;
}

static bool mcontrol_hit_supported(struct target *target, riscv_reg_t tdata1,
uint64_t hit_mask)
{
/* Check if mcontrol.hit is implemented */
bool local_mcontrol_hit_not_supported = false;
const riscv_reg_t tdata1_test = set_field(tdata1, CSR_MCONTROL_HIT, 1);
riscv_reg_t tdata1_test_rb;
riscv_reg_set(target, GDB_REGNO_TDATA1, tdata1_test);
riscv_reg_get(target, &tdata1_test_rb, GDB_REGNO_TDATA1);
int tdata1_test_hit = get_field(tdata1_test, CSR_MCONTROL_HIT);
int tdata1_test_rb_hit = get_field(tdata1_test_rb, CSR_MCONTROL_HIT);
if (tdata1_test_hit != tdata1_test_rb_hit) {
local_mcontrol_hit_not_supported = true;
} else {
/* mcontrol.hit is implemented, but not fired. */
riscv_reg_set(target, GDB_REGNO_TDATA1, tdata1_test & ~hit_mask);
}
return local_mcontrol_hit_not_supported;
}

static bool mcontrol6_hit_supported(struct target *target, riscv_reg_t tdata1,
uint64_t hit_mask, int trigger_hit_info)
{
bool local_mcontrol6_hit_not_supported = false;
/* Check if mcontrol6.hit0[1] is implemented. */
riscv_reg_t tdata1_hit0_test, tdata1_hit1_test, tdata1_test_rb;
tdata1_hit0_test = set_field(tdata1, CSR_MCONTROL6_HIT0, 1);
tdata1_hit1_test = set_field(tdata1, CSR_MCONTROL6_HIT1, 1);

/* Check timing "before" support. */
riscv_reg_set(target, GDB_REGNO_TDATA1, tdata1_hit0_test);
riscv_reg_get(target, &tdata1_test_rb, GDB_REGNO_TDATA1);
if (tdata1_test_rb != tdata1_hit0_test) {
local_mcontrol6_hit_not_supported = true;
} else {
riscv_reg_set(target, GDB_REGNO_TDATA1, tdata1_test_rb & ~hit_mask);
goto done;
}

/* Check timing "after" support. */
riscv_reg_set(target, GDB_REGNO_TDATA1, tdata1_hit1_test);
riscv_reg_get(target, &tdata1_test_rb, GDB_REGNO_TDATA1);
if (tdata1_test_rb != tdata1_hit1_test) {
local_mcontrol6_hit_not_supported = true;
} else {
local_mcontrol6_hit_not_supported = false;
riscv_reg_set(target, GDB_REGNO_TDATA1, tdata1_test_rb & ~hit_mask);
}
/* Ignore checking timing "imm after" support. */
done:
return local_mcontrol6_hit_not_supported;
}

/**
* 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
Expand All @@ -1576,6 +1629,7 @@ static int riscv_trigger_detect_hit_bits(struct target *target, int64_t *unique_

// FIXME: Add hit bits support detection and caching
RISCV_INFO(r);
r->need_single_step = false;

riscv_reg_t tselect;
if (riscv_reg_get(target, &tselect, GDB_REGNO_TSELECT) != ERROR_OK)
Expand All @@ -1601,9 +1655,26 @@ static int riscv_trigger_detect_hit_bits(struct target *target, int64_t *unique_
break;
case CSR_TDATA1_TYPE_MCONTROL:
hit_mask = CSR_MCONTROL_HIT;
bool mcontrol_hit_not_supported = false;
if ((tdata1 & hit_mask) == 0)
mcontrol_hit_not_supported = mcontrol_hit_supported(target, tdata1, hit_mask);
if (get_field(tdata1, CSR_MCONTROL_TIMING) == CSR_MCONTROL_TIMING_BEFORE
&& (mcontrol_hit_not_supported || (tdata1 & hit_mask)))
r->need_single_step = true;
break;
case CSR_TDATA1_TYPE_MCONTROL6:
hit_mask = CSR_MCONTROL6_HIT0 | CSR_MCONTROL6_HIT1;
bool mcontrol6_hit_not_supported = false;
int hit0 = get_field(tdata1, CSR_MCONTROL6_HIT0);
int hit1 = get_field(tdata1, CSR_MCONTROL6_HIT1);
int trigger_hit_info = (hit1 << 1) | hit0;
if (trigger_hit_info == CSR_MCONTROL6_HIT0_FALSE) {
mcontrol6_hit_not_supported = mcontrol6_hit_supported(target, tdata1,
hit_mask, trigger_hit_info);
}
if (mcontrol6_hit_not_supported
|| trigger_hit_info == CSR_MCONTROL6_HIT0_BEFORE)
r->need_single_step = true;
break;
case CSR_TDATA1_TYPE_ICOUNT:
hit_mask = CSR_ICOUNT_HIT;
Expand Down Expand Up @@ -2553,10 +2624,19 @@ static int resume_prep(struct target *target, int current,
if (handle_breakpoints) {
/* 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
* 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)
return ERROR_FAIL;
* pending breakpoints so we can safely perform the step.
*
* Two cases where single step is needed before resuming:
* 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) {
Expand Down
3 changes: 3 additions & 0 deletions src/target/riscv/riscv.h
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,9 @@ struct riscv_info {
/* record the tinfo of each trigger */
unsigned int trigger_tinfo[RISCV_MAX_TRIGGERS];

/* record the dpc that triggered it is retired. */
bool need_single_step;

/* For each physical trigger contains:
* -1: the hwbp is available
* -4: The trigger is used by the itrigger command
Expand Down

0 comments on commit 5f65be9

Please sign in to comment.