diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/target/riscv/riscv-011.c | 2 | ||||
-rw-r--r-- | src/target/riscv/riscv.c | 84 | ||||
-rw-r--r-- | src/target/riscv/riscv.h | 1 |
3 files changed, 86 insertions, 1 deletions
diff --git a/src/target/riscv/riscv-011.c b/src/target/riscv/riscv-011.c index db9aad3..a77b007 100644 --- a/src/target/riscv/riscv-011.c +++ b/src/target/riscv/riscv-011.c @@ -1838,7 +1838,7 @@ static int handle_halt(struct target *target, bool announce) target->debug_reason = DBG_REASON_BREAKPOINT; break; case DCSR_CAUSE_HWBP: - target->debug_reason = DBG_REASON_WPTANDBKPT; + target->debug_reason = DBG_REASON_WATCHPOINT; /* If we halted because of a data trigger, gdb doesn't know to do * the disable-breakpoints-step-enable-breakpoints dance. */ info->need_strict_step = true; diff --git a/src/target/riscv/riscv.c b/src/target/riscv/riscv.c index 24d633d..293a4da 100644 --- a/src/target/riscv/riscv.c +++ b/src/target/riscv/riscv.c @@ -643,6 +643,89 @@ int riscv_remove_watchpoint(struct target *target, return ERROR_OK; } +/* Sets *hit_watchpoint to the first watchpoint identified as causing the + * current halt. + * + * The GDB server uses this information to tell GDB what data address has + * been hit, which enables GDB to print the hit variable along with its old + * and new value. */ +int riscv_hit_watchpoint(struct target *target, struct watchpoint **hit_watchpoint) +{ + struct watchpoint *wp = target->watchpoints; + + LOG_DEBUG("Current hartid = %d", riscv_current_hartid(target)); + + /*TODO instead of disassembling the instruction that we think caused the + * trigger, check the hit bit of each watchpoint first. The hit bit is + * simpler and more reliable to check but as it is optional and relatively + * new, not all hardware will implement it */ + riscv_reg_t dpc; + riscv_get_register(target, &dpc, GDB_REGNO_DPC); + const uint8_t length = 4; + LOG_DEBUG("dpc is 0x%" PRIx64, dpc); + + /* fetch the instruction at dpc */ + uint8_t buffer[length]; + if (target_read_buffer(target, dpc, length, buffer) != ERROR_OK) { + LOG_ERROR("Failed to read instruction at dpc 0x%" PRIx64, dpc); + return ERROR_FAIL; + } + + uint32_t instruction = 0; + + for (int i = 0; i < length; i++) { + LOG_DEBUG("Next byte is %x", buffer[i]); + instruction += (buffer[i] << 8 * i); + } + LOG_DEBUG("Full instruction is %x", instruction); + + /* find out which memory address is accessed by the instruction at dpc */ + /* opcode is first 7 bits of the instruction */ + uint8_t opcode = instruction & 0x7F; + uint32_t rs1; + int16_t imm; + riscv_reg_t mem_addr; + + if (opcode == MATCH_LB || opcode == MATCH_SB) { + rs1 = (instruction & 0xf8000) >> 15; + riscv_get_register(target, &mem_addr, rs1); + + if (opcode == MATCH_SB) { + LOG_DEBUG("%x is store instruction", instruction); + imm = ((instruction & 0xf80) >> 7) | ((instruction & 0xfe000000) >> 20); + } else { + LOG_DEBUG("%x is load instruction", instruction); + imm = (instruction & 0xfff00000) >> 20; + } + /* sign extend 12-bit imm to 16-bits */ + if (imm & (1 << 11)) + imm |= 0xf000; + mem_addr += imm; + LOG_DEBUG("memory address=0x%" PRIx64, mem_addr); + } else { + LOG_DEBUG("%x is not a RV32I load or store", instruction); + return ERROR_FAIL; + } + + while (wp) { + /*TODO support length/mask */ + if (wp->address == mem_addr) { + *hit_watchpoint = wp; + LOG_DEBUG("Hit address=%" TARGET_PRIxADDR, wp->address); + return ERROR_OK; + } + wp = wp->next; + } + + /* No match found - either we hit a watchpoint caused by an instruction that + * this function does not yet disassemble, or we hit a breakpoint. + * + * OpenOCD will behave as if this function had never been implemented i.e. + * report the halt to GDB with no address information. */ + return ERROR_FAIL; +} + + static int oldriscv_step(struct target *target, int current, uint32_t address, int handle_breakpoints) { @@ -1587,6 +1670,7 @@ struct target_type riscv_target = { .add_watchpoint = riscv_add_watchpoint, .remove_watchpoint = riscv_remove_watchpoint, + .hit_watchpoint = riscv_hit_watchpoint, .arch_state = riscv_arch_state, diff --git a/src/target/riscv/riscv.h b/src/target/riscv/riscv.h index 31f3cf6..72f6f2a 100644 --- a/src/target/riscv/riscv.h +++ b/src/target/riscv/riscv.h @@ -253,6 +253,7 @@ int riscv_remove_breakpoint(struct target *target, int riscv_add_watchpoint(struct target *target, struct watchpoint *watchpoint); int riscv_remove_watchpoint(struct target *target, struct watchpoint *watchpoint); +int riscv_hit_watchpoint(struct target *target, struct watchpoint **hit_wp_address); int riscv_init_registers(struct target *target); |