From 83a97ca963293fb56fa01d67b64fdce20275aa40 Mon Sep 17 00:00:00 2001 From: zhusonghe Date: Wed, 9 Oct 2024 18:36:42 +0800 Subject: [PATCH] target/riscv:Perform single step before resume if necessary 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. Signed-off-by: Songhe Zhu Co-developed-by: Fei Gao Co-developed-by: xiatianyi --- src/target/riscv/riscv.c | 37 ++++++++++++++++++++++++++++++------- src/target/riscv/riscv.h | 3 +++ 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/src/target/riscv/riscv.c b/src/target/riscv/riscv.c index 59717fd14..2308c1a92 100644 --- a/src/target/riscv/riscv.c +++ b/src/target/riscv/riscv.c @@ -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) @@ -668,6 +668,16 @@ static int set_trigger(struct target *target, unsigned int idx, riscv_reg_t tdat const uint32_t type = get_field(tdata1, CSR_TDATA1_TYPE(riscv_xlen(target))); const bool is_mcontrol = type == CSR_TDATA1_TYPE_MCONTROL; + if (type == CSR_TDATA1_TYPE_MCONTROL) { + if (get_field(tdata1_rb, CSR_MCONTROL_TIMING) == CSR_MCONTROL_TIMING_BEFORE) + r->halt_before_dpc = true; + } else if (type == CSR_TDATA1_TYPE_MCONTROL6) { + int hit0 = get_field(tdata1_rb, CSR_MCONTROL6_HIT0); + int hit1 = get_field(tdata1_rb, CSR_MCONTROL6_HIT1); + if (((hit1 << 1) | hit0) == CSR_MCONTROL6_HIT0_BEFORE) + r->halt_before_dpc = true; + } + /* Determine if tdata1 supports what we need. * For mcontrol triggers, we don't care about * the value in the read-only "maskmax" field. @@ -2547,16 +2557,29 @@ static int resume_prep(struct target *target, int current, assert(target->state == TARGET_HALTED); RISCV_INFO(r); + riscv_reg_t dcsr; + if (riscv_reg_get(target, &dcsr, GDB_REGNO_DCSR) != ERROR_OK) + return ERROR_FAIL; + if (!current && riscv_reg_set(target, GDB_REGNO_PC, address) != ERROR_OK) return ERROR_FAIL; 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 (get_field(dcsr, CSR_DCSR_CAUSE) == CSR_DCSR_CAUSE_EBREAK + || (get_field(dcsr, CSR_DCSR_CAUSE) == CSR_DCSR_CAUSE_TRIGGER + && r->halt_before_dpc)) { + 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) { diff --git a/src/target/riscv/riscv.h b/src/target/riscv/riscv.h index 4ac10fa76..00e1327dd 100644 --- a/src/target/riscv/riscv.h +++ b/src/target/riscv/riscv.h @@ -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 halt_before_dpc; + /* For each physical trigger contains: * -1: the hwbp is available * -4: The trigger is used by the itrigger command