Skip to content

Commit 0097f6b

Browse files
author
zhusonghe
committed
target/riscv:Perform single step before resume if necessary
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 <[email protected]> Co-developed-by: Fei Gao <[email protected]> Co-developed-by: xiatianyi <[email protected]>
1 parent a4020f1 commit 0097f6b

File tree

2 files changed

+100
-8
lines changed

2 files changed

+100
-8
lines changed

src/target/riscv/riscv.c

+97-8
Original file line numberDiff line numberDiff line change
@@ -623,12 +623,12 @@ static int find_first_trigger_by_id(struct target *target, int unique_id)
623623

624624
static unsigned int count_trailing_ones(riscv_reg_t reg)
625625
{
626-
assert(sizeof(riscv_reg_t) * 8 == 64);
627-
for (unsigned int i = 0; i < 64; i++) {
626+
const unsigned int riscv_reg_bits = sizeof(riscv_reg_t) * CHAR_BIT;
627+
for (unsigned int i = 0; i < riscv_reg_bits; i++) {
628628
if ((1 & (reg >> i)) == 0)
629629
return i;
630630
}
631-
return 64;
631+
return riscv_reg_bits;
632632
}
633633

634634
static int set_trigger(struct target *target, unsigned int idx, riscv_reg_t tdata1, riscv_reg_t tdata2)
@@ -1576,6 +1576,7 @@ static int riscv_trigger_detect_hit_bits(struct target *target, int64_t *unique_
15761576

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

15801581
riscv_reg_t tselect;
15811582
if (riscv_reg_get(target, &tselect, GDB_REGNO_TSELECT) != ERROR_OK)
@@ -1589,21 +1590,100 @@ static int riscv_trigger_detect_hit_bits(struct target *target, int64_t *unique_
15891590
if (riscv_reg_set(target, GDB_REGNO_TSELECT, i) != ERROR_OK)
15901591
return ERROR_FAIL;
15911592

1592-
uint64_t tdata1;
1593+
uint64_t tdata1, tdata1_test_rb;
15931594
if (riscv_reg_get(target, &tdata1, GDB_REGNO_TDATA1) != ERROR_OK)
15941595
return ERROR_FAIL;
15951596
int type = get_field(tdata1, CSR_TDATA1_TYPE(riscv_xlen(target)));
15961597

15971598
uint64_t hit_mask = 0;
1599+
bool mcontrol_hit_not_supported = false;
1600+
bool mcontrol6_hit_not_supported = false;
15981601
switch (type) {
15991602
case CSR_TDATA1_TYPE_LEGACY:
16001603
/* Doesn't support hit bit. */
16011604
break;
16021605
case CSR_TDATA1_TYPE_MCONTROL:
16031606
hit_mask = CSR_MCONTROL_HIT;
1607+
/* Check if mcontrol.hit is implemented */
1608+
if ((tdata1 & hit_mask) == 0) {
1609+
uint64_t tdata1_test =
1610+
set_field(tdata1, CSR_MCONTROL_HIT, 1);
1611+
if (riscv_reg_set(target,
1612+
GDB_REGNO_TDATA1, tdata1_test) != ERROR_OK)
1613+
return ERROR_FAIL;
1614+
if (riscv_reg_get(target,
1615+
&tdata1_test_rb, GDB_REGNO_TDATA1) != ERROR_OK)
1616+
return ERROR_FAIL;
1617+
int tdata1_test_hit =
1618+
get_field(tdata1_test, CSR_MCONTROL_HIT);
1619+
int tdata1_test_rb_hit =
1620+
get_field(tdata1_test_rb, CSR_MCONTROL_HIT);
1621+
if (tdata1_test_hit != tdata1_test_rb_hit)
1622+
mcontrol_hit_not_supported = true;
1623+
if (riscv_reg_set(target, GDB_REGNO_TDATA1,
1624+
tdata1_test & ~hit_mask) != ERROR_OK)
1625+
return ERROR_FAIL;
1626+
}
1627+
1628+
if (get_field(tdata1, CSR_MCONTROL_TIMING)
1629+
== CSR_MCONTROL_TIMING_BEFORE
1630+
&& (mcontrol_hit_not_supported || (tdata1 & hit_mask)))
1631+
r->need_single_step = true;
16041632
break;
16051633
case CSR_TDATA1_TYPE_MCONTROL6:
16061634
hit_mask = CSR_MCONTROL6_HIT0 | CSR_MCONTROL6_HIT1;
1635+
int hit0 = get_field(tdata1, CSR_MCONTROL6_HIT0);
1636+
int hit1 = get_field(tdata1, CSR_MCONTROL6_HIT1);
1637+
int trigger_retired_info = (hit1 << 1) | hit0;
1638+
/* Check if mcontrol6.hit0[1] is not implemented. */
1639+
if (trigger_retired_info == 0) {
1640+
for (unsigned int j = 0; j < 2; j++) {
1641+
uint64_t tdata1_test =
1642+
set_field(tdata1, CSR_MCONTROL6_HIT0, j);
1643+
for (unsigned int k = 0; k < 2; k++) {
1644+
tdata1_test =
1645+
set_field(tdata1_test, CSR_MCONTROL6_HIT1, k);
1646+
if (riscv_reg_set(target,
1647+
GDB_REGNO_TDATA1, tdata1_test) != ERROR_OK)
1648+
return ERROR_FAIL;
1649+
if (riscv_reg_get(target,
1650+
&tdata1_test_rb, GDB_REGNO_TDATA1) != ERROR_OK)
1651+
return ERROR_FAIL;
1652+
int tdata1_test_hit0 =
1653+
get_field(tdata1_test, CSR_MCONTROL6_HIT0);
1654+
int tdata1_test_rb_hit0 =
1655+
get_field(tdata1_test_rb, CSR_MCONTROL6_HIT0);
1656+
int tdata1_test_hit1 =
1657+
get_field(tdata1_test, CSR_MCONTROL6_HIT1);
1658+
int tdata1_test_rb_hit1 =
1659+
get_field(tdata1_test_rb, CSR_MCONTROL6_HIT1);
1660+
int trigger_retired_test_info =
1661+
(tdata1_test_hit1 << 1) | tdata1_test_hit0;
1662+
int trigger_retired_test_rb_info =
1663+
(tdata1_test_rb_hit1 << 1) | tdata1_test_rb_hit0;
1664+
if (trigger_retired_test_info == 0)
1665+
continue;
1666+
1667+
if (trigger_retired_test_info
1668+
!= trigger_retired_test_rb_info) {
1669+
mcontrol6_hit_not_supported = true;
1670+
} else {
1671+
mcontrol6_hit_not_supported = false;
1672+
if (riscv_reg_set(target, GDB_REGNO_TDATA1,
1673+
tdata1_test & ~hit_mask) != ERROR_OK)
1674+
return ERROR_FAIL;
1675+
goto done;
1676+
}
1677+
}
1678+
if (riscv_reg_set(target, GDB_REGNO_TDATA1,
1679+
tdata1_test & ~hit_mask) != ERROR_OK)
1680+
return ERROR_FAIL;
1681+
}
1682+
}
1683+
done:
1684+
if (mcontrol6_hit_not_supported
1685+
|| trigger_retired_info == CSR_MCONTROL6_HIT0_BEFORE)
1686+
r->need_single_step = true;
16071687
break;
16081688
case CSR_TDATA1_TYPE_ICOUNT:
16091689
hit_mask = CSR_ICOUNT_HIT;
@@ -2553,10 +2633,19 @@ static int resume_prep(struct target *target, int current,
25532633
if (handle_breakpoints) {
25542634
/* To be able to run off a trigger, we perform a step operation and then
25552635
* resume. If handle_breakpoints is true then step temporarily disables
2556-
* pending breakpoints so we can safely perform the step. */
2557-
if (old_or_new_riscv_step_impl(target, current, address, handle_breakpoints,
2558-
false /* callbacks are not called */) != ERROR_OK)
2559-
return ERROR_FAIL;
2636+
* pending breakpoints so we can safely perform the step.
2637+
*
2638+
* Two cases where single step is needed before resuming:
2639+
* 1. ebreak used in software breakpoint;
2640+
* 2. a trigger that is taken just before the instruction that triggered it is retired.
2641+
*/
2642+
if (target->debug_reason == DBG_REASON_BREAKPOINT
2643+
|| (target->debug_reason == DBG_REASON_WATCHPOINT
2644+
&& r->need_single_step)) {
2645+
if (old_or_new_riscv_step_impl(target, current, address, handle_breakpoints,
2646+
false /* callbacks are not called */) != ERROR_OK)
2647+
return ERROR_FAIL;
2648+
}
25602649
}
25612650

25622651
if (r->get_hart_state) {

src/target/riscv/riscv.h

+3
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,9 @@ struct riscv_info {
152152
/* record the tinfo of each trigger */
153153
unsigned int trigger_tinfo[RISCV_MAX_TRIGGERS];
154154

155+
/* record the dpc that triggered it is retired. */
156+
bool need_single_step;
157+
155158
/* For each physical trigger contains:
156159
* -1: the hwbp is available
157160
* -4: The trigger is used by the itrigger command

0 commit comments

Comments
 (0)