Skip to content

Commit cb0fa3f

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 cb0fa3f

File tree

2 files changed

+108
-14
lines changed

2 files changed

+108
-14
lines changed

src/target/riscv/riscv.c

+97-14
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)
@@ -1561,13 +1561,69 @@ int riscv_remove_watchpoint(struct target *target,
15611561
return ERROR_OK;
15621562
}
15631563

1564+
typedef enum {
1565+
M6_HIT_ERROR,
1566+
M6_HIT_NOT_SUPPORTED,
1567+
M6_NOT_HIT,
1568+
M6_HIT_BEFORE,
1569+
M6_HIT_AFTER,
1570+
M6_HIT_IMM_AFTER
1571+
} mctrl6hitstatus;
1572+
1573+
/* TODO Record info:mcontrol6_supports_hit, This way
1574+
* the number of reads and writes of TDATA1 will be
1575+
* greatly reduced.
1576+
*/
1577+
static mctrl6hitstatus check_mcontrol6_hit_status(struct target *target,
1578+
riscv_reg_t tdata1, uint64_t hit_mask)
1579+
{
1580+
const uint32_t hit0 = get_field(tdata1, CSR_MCONTROL6_HIT0);
1581+
const uint32_t hit1 = get_field(tdata1, CSR_MCONTROL6_HIT1);
1582+
const uint32_t hit_info = (hit1 << 1) | hit0;
1583+
if (hit_info == CSR_MCONTROL6_HIT0_BEFORE)
1584+
return M6_HIT_BEFORE;
1585+
1586+
if (hit_info == CSR_MCONTROL6_HIT0_AFTER)
1587+
return M6_HIT_AFTER;
1588+
1589+
if (hit_info == CSR_MCONTROL6_HIT0_IMMEDIATELY_AFTER)
1590+
return M6_HIT_IMM_AFTER;
1591+
1592+
if (hit_info == CSR_MCONTROL6_HIT0_FALSE) {
1593+
/* hit[1..0] equals 0, which can mean one of the following:
1594+
* - "hit" bits are supported and this trigger has not fired
1595+
* - "hit" bits are not supported on this trigger
1596+
* To distinguish these two cases, try writing all non-zero bit
1597+
* patterns to hit[1..0] to determine if the "hit" bits are supported:
1598+
*/
1599+
riscv_reg_t tdata1_tests[] = {
1600+
set_field(tdata1, CSR_MCONTROL6_HIT0, 1),
1601+
set_field(tdata1, CSR_MCONTROL6_HIT1, 1),
1602+
set_field(tdata1, CSR_MCONTROL6_HIT0, 1) | field_value(CSR_MCONTROL6_HIT1, 1)
1603+
};
1604+
riscv_reg_t tdata1_test_rb;
1605+
for (uint64_t i = 0; i < ARRAY_SIZE(tdata1_tests); ++i) {
1606+
if (riscv_reg_set(target, GDB_REGNO_TDATA1, tdata1_tests[i]) != ERROR_OK)
1607+
return M6_HIT_ERROR;
1608+
if (riscv_reg_get(target, &tdata1_test_rb, GDB_REGNO_TDATA1) != ERROR_OK)
1609+
return M6_HIT_ERROR;
1610+
if (riscv_reg_set(target, GDB_REGNO_TDATA1, tdata1_test_rb & ~hit_mask) != ERROR_OK)
1611+
return M6_HIT_ERROR;
1612+
if (tdata1_test_rb == tdata1_tests[i])
1613+
return M6_NOT_HIT;
1614+
}
1615+
}
1616+
return M6_HIT_NOT_SUPPORTED;
1617+
}
1618+
15641619
/**
15651620
* Look at the trigger hit bits to find out which trigger is the reason we're
15661621
* halted. Sets *unique_id to the unique ID of that trigger. If *unique_id is
15671622
* RISCV_TRIGGER_HIT_NOT_FOUND, no match was found.
15681623
*/
15691624

1570-
static int riscv_trigger_detect_hit_bits(struct target *target, int64_t *unique_id)
1625+
static int riscv_trigger_detect_hit_bits(struct target *target, int64_t *unique_id,
1626+
bool *need_single_step)
15711627
{
15721628
/* FIXME: this function assumes that we have only one trigger that can
15731629
* have hit bit set. Debug spec allows hit bit to bit set if a trigger has
@@ -1601,9 +1657,21 @@ static int riscv_trigger_detect_hit_bits(struct target *target, int64_t *unique_
16011657
break;
16021658
case CSR_TDATA1_TYPE_MCONTROL:
16031659
hit_mask = CSR_MCONTROL_HIT;
1660+
r->need_single_step = true;
16041661
break;
16051662
case CSR_TDATA1_TYPE_MCONTROL6:
16061663
hit_mask = CSR_MCONTROL6_HIT0 | CSR_MCONTROL6_HIT1;
1664+
if (r->trigger_tinfo_version[i] == CSR_TINFO_VERSION_0) {
1665+
r->need_single_step = true;
1666+
} else if (r->trigger_tinfo_version[i] == ERROR_TARGET_RESOURCE_NOT_AVAILABLE
1667+
|| r->trigger_tinfo_version[i] == CSR_TINFO_VERSION_1) {
1668+
mctrl6hitstatus hits_status = check_mcontrol6_hit_status(target,
1669+
tdata1, hit_mask);
1670+
if (hits_status == M6_HIT_ERROR)
1671+
return ERROR_FAIL;
1672+
if (hits_status == M6_HIT_BEFORE || hits_status == M6_HIT_NOT_SUPPORTED)
1673+
r->need_single_step = true;
1674+
}
16071675
break;
16081676
case CSR_TDATA1_TYPE_ICOUNT:
16091677
hit_mask = CSR_ICOUNT_HIT;
@@ -1622,8 +1690,9 @@ static int riscv_trigger_detect_hit_bits(struct target *target, int64_t *unique_
16221690
/* FIXME: this logic needs to be changed to ignore triggers that are not
16231691
* the last one in the chain. */
16241692
if (tdata1 & hit_mask) {
1625-
LOG_TARGET_DEBUG(target, "Trigger %u (unique_id=%" PRIi64 ") has hit bit set.",
1626-
i, r->trigger_unique_id[i]);
1693+
LOG_TARGET_DEBUG(target, "Trigger %u (unique_id=%" PRIi64 ""
1694+
", need_single_step=%" PRId32 ") has hit bit set.",
1695+
i, r->trigger_unique_id[i], r->need_single_step);
16271696
if (riscv_reg_set(target, GDB_REGNO_TDATA1, tdata1 & ~hit_mask) != ERROR_OK)
16281697
return ERROR_FAIL;
16291698

@@ -2285,13 +2354,15 @@ static int set_debug_reason(struct target *target, enum riscv_halt_reason halt_r
22852354
{
22862355
RISCV_INFO(r);
22872356
r->trigger_hit = -1;
2357+
r->need_single_step = false;
22882358
switch (halt_reason) {
22892359
case RISCV_HALT_EBREAK:
22902360
target->debug_reason = DBG_REASON_BREAKPOINT;
22912361
break;
22922362
case RISCV_HALT_TRIGGER:
22932363
target->debug_reason = DBG_REASON_UNDEFINED;
2294-
if (riscv_trigger_detect_hit_bits(target, &r->trigger_hit) != ERROR_OK)
2364+
if (riscv_trigger_detect_hit_bits(target, &r->trigger_hit,
2365+
&r->need_single_step) != ERROR_OK)
22952366
return ERROR_FAIL;
22962367
// FIXME: handle multiple hit bits
22972368
if (r->trigger_hit != RISCV_TRIGGER_HIT_NOT_FOUND) {
@@ -2553,10 +2624,19 @@ static int resume_prep(struct target *target, int current,
25532624
if (handle_breakpoints) {
25542625
/* To be able to run off a trigger, we perform a step operation and then
25552626
* 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;
2627+
* pending breakpoints so we can safely perform the step.
2628+
*
2629+
* Two cases where single step is needed before resuming:
2630+
* 1. ebreak used in software breakpoint;
2631+
* 2. a trigger that is taken just before the instruction that triggered it is retired.
2632+
*/
2633+
if (target->debug_reason == DBG_REASON_BREAKPOINT
2634+
|| (target->debug_reason == DBG_REASON_WATCHPOINT
2635+
&& r->need_single_step)) {
2636+
if (old_or_new_riscv_step_impl(target, current, address, handle_breakpoints,
2637+
false /* callbacks are not called */) != ERROR_OK)
2638+
return ERROR_FAIL;
2639+
}
25602640
}
25612641

25622642
if (r->get_hart_state) {
@@ -5698,19 +5778,21 @@ static int check_if_trigger_exists(struct target *target, unsigned int index)
56985778
* to determine trigger types supported by a trigger.
56995779
* It is assumed that the trigger is already selected via writing `tselect`.
57005780
*/
5701-
static int get_trigger_types(struct target *target, unsigned int *trigger_tinfo,
5702-
riscv_reg_t tdata1)
5781+
static int get_trigger_types_and_version(struct target *target, unsigned int *trigger_tinfo,
5782+
int64_t *trigger_tinfo_version, riscv_reg_t tdata1)
57035783
{
57045784
assert(trigger_tinfo);
57055785
riscv_reg_t tinfo;
57065786
if (riscv_reg_get(target, &tinfo, GDB_REGNO_TINFO) == ERROR_OK) {
5787+
*trigger_tinfo_version = get_field(tinfo, CSR_TINFO_VERSION);
57075788
/* tinfo.INFO == 1: trigger doesn’t exist
57085789
* tinfo == 0 or tinfo.INFO != 1 and tinfo LSB is set: invalid tinfo */
57095790
if (tinfo == 0 || tinfo & 0x1)
57105791
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
57115792
*trigger_tinfo = tinfo;
57125793
return ERROR_OK;
57135794
}
5795+
*trigger_tinfo_version = ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
57145796
const unsigned int type = get_field(tdata1, CSR_TDATA1_TYPE(riscv_xlen(target)));
57155797
if (type == 0)
57165798
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
@@ -5794,7 +5876,8 @@ int riscv_enumerate_triggers(struct target *target)
57945876
if (riscv_reg_get(target, &tdata1, GDB_REGNO_TDATA1) != ERROR_OK)
57955877
return ERROR_FAIL;
57965878

5797-
result = get_trigger_types(target, &r->trigger_tinfo[t], tdata1);
5879+
result = get_trigger_types_and_version(target, &r->trigger_tinfo[t],
5880+
&r->trigger_tinfo_version[t], tdata1);
57985881
if (result == ERROR_FAIL)
57995882
return ERROR_FAIL;
58005883
if (result == ERROR_TARGET_RESOURCE_NOT_AVAILABLE)

src/target/riscv/riscv.h

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

155+
/* record the tinfo_version of each trigger */
156+
int64_t trigger_tinfo_version[RISCV_MAX_TRIGGERS];
157+
158+
/* Record if single-step is needed prior to resuming
159+
* from a software breakpoint or trigger.
160+
* Single-step is needed if the instruction that
161+
* caused the halt was not retired. That is,
162+
* when we halted "before" that instruction.
163+
*/
164+
bool need_single_step;
165+
155166
/* For each physical trigger contains:
156167
* -1: the hwbp is available
157168
* -4: The trigger is used by the itrigger command

0 commit comments

Comments
 (0)