diff --git a/src/target/riscv/riscv.c b/src/target/riscv/riscv.c index d610b33de8..26fd36568d 100644 --- a/src/target/riscv/riscv.c +++ b/src/target/riscv/riscv.c @@ -1617,6 +1617,440 @@ static int riscv_hit_trigger_hit_bit(struct target *target, uint32_t *unique_id) return ERROR_OK; } +/** + * These functions are needed to extract individual bits (for offset) + * from the instruction + */ +// c.lwsp rd_n0 c_uimm8sphi c_uimm8splo - offset[5] offset[4:2|7:6] +static uint16_t get_offset_clwsp(riscv_insn_t instruction) +{ + uint16_t offset_4to2and7to6_bits = + get_field32(instruction, INSN_FIELD_C_UIMM8SPLO); + uint16_t offset_4to2_bits = offset_4to2and7to6_bits >> 2; + uint16_t offset_7to6_bits = offset_4to2and7to6_bits & 0x3; + uint16_t offset_5_bit = get_field32(instruction, INSN_FIELD_C_UIMM8SPHI); + return (offset_4to2_bits << 2) + (offset_5_bit << 5) + + (offset_7to6_bits << 6); +} + +// c.ldsp rd_n0 c_uimm9sphi c_uimm9splo - offset[5] offset[4:3|8:6] +static uint16_t get_offset_cldsp(riscv_insn_t instruction) +{ + uint16_t offset_4to3and8to6_bits = + get_field32(instruction, INSN_FIELD_C_UIMM9SPLO); + uint16_t offset_4to3_bits = offset_4to3and8to6_bits >> 3; + uint16_t offset_8to6_bits = offset_4to3and8to6_bits & 0x7; + uint16_t offset_5_bit = get_field32(instruction, INSN_FIELD_C_UIMM9SPHI); + return (offset_4to3_bits << 3) + (offset_5_bit << 5) + + (offset_8to6_bits << 6); +} + +// c.swsp c_rs2 c_uimm8sp_s - offset[5:2|7:6] +static uint16_t get_offset_cswsp(riscv_insn_t instruction) +{ + uint16_t offset_5to2and7to6_bits = + get_field32(instruction, INSN_FIELD_C_UIMM8SP_S); + uint16_t offset_5to2_bits = offset_5to2and7to6_bits >> 2; + uint16_t offset_7to6_bits = offset_5to2and7to6_bits & 0x3; + return (offset_5to2_bits << 2) + (offset_7to6_bits << 6); +} + +// c.sdsp c_rs2 c_uimm9sp_s - offset[5:3|8:6] +static uint16_t get_offset_csdsp(riscv_insn_t instruction) +{ + uint16_t offset_5to3and8to6_bits = + get_field32(instruction, INSN_FIELD_C_UIMM9SP_S); + uint16_t offset_5to3_bits = offset_5to3and8to6_bits >> 3; + uint16_t offset_8to6_bits = offset_5to3and8to6_bits & 0x7; + return (offset_5to3_bits << 3) + (offset_8to6_bits << 6); +} + +// c.lw rd_p rs1_p c_uimm7lo c_uimm7hi - offset[2|6] offset[5:3] +static uint16_t get_offset_clw(riscv_insn_t instruction) +{ + uint16_t offset_2and6_bits = get_field32(instruction, INSN_FIELD_C_UIMM7LO); + uint16_t offset_2_bit = offset_2and6_bits >> 1; + uint16_t offset_6_bit = offset_2and6_bits & 0x1; + uint16_t offset_5to3_bits = get_field32(instruction, INSN_FIELD_C_UIMM7HI); + return (offset_2_bit << 2) + (offset_5to3_bits << 3) + (offset_6_bit << 6); +} + +// c.ld rd_p rs1_p c_uimm8lo c_uimm8hi - offset[7:6] offset[5:3] +static uint16_t get_offset_cld(riscv_insn_t instruction) +{ + uint16_t offset_7to6_bits = get_field32(instruction, INSN_FIELD_C_UIMM8LO); + uint16_t offset_5to3_bits = get_field32(instruction, INSN_FIELD_C_UIMM8HI); + return (offset_5to3_bits << 3) + (offset_7to6_bits << 6); +} + +// c.lq rd_p rs1_p c_uimm9lo c_uimm9hi - offset[7:6] offset[5|4|8] +static uint16_t get_offset_clq(riscv_insn_t instruction) +{ + uint16_t offset_7to6_bits = get_field32(instruction, INSN_FIELD_C_UIMM9LO); + uint16_t offset_5to4and8_bits = + get_field32(instruction, INSN_FIELD_C_UIMM9HI); + uint16_t offset_5to4_bits = offset_5to4and8_bits >> 1; + uint16_t offset_8_bit = offset_5to4and8_bits & 0x1; + return (offset_5to4_bits << 4) + (offset_7to6_bits << 6) + + (offset_8_bit << 8); +} + +// c.lqsp rd_n0 c_uimm10sphi c_uimm10splo - offset[5] offset[4|9:6] +static uint16_t get_offset_clqsp(riscv_insn_t instruction) +{ + uint16_t offset_4and9to6_bits = + get_field32(instruction, INSN_FIELD_C_UIMM10SPLO); + uint16_t offset_4_bit = offset_4and9to6_bits >> 4; + uint16_t offset_9to6_bits = offset_4and9to6_bits & 0xf; + uint16_t offset_5_bit = get_field32(instruction, INSN_FIELD_C_UIMM10SPHI); + return (offset_4_bit << 4) + (offset_5_bit << 5) + (offset_9to6_bits << 6); +} + +// c.sqsp c_rs2 c_uimm10sp_s - offset[5:4|9:6] +static uint16_t get_offset_csqsp(riscv_insn_t instruction) +{ + uint16_t offset_5to4and9to6_bits = + get_field32(instruction, INSN_FIELD_C_UIMM10SP_S); + uint16_t offset_5to4_biits = offset_5to4and9to6_bits >> 4; + uint16_t offset_9to6_bits = offset_5to4and9to6_bits & 0xf; + return (offset_5to4_biits << 4) + (offset_9to6_bits << 6); +} + +/** + * Decode rs1' register num for RVC. + * See "Table: Registers specified by the three-bit rs1′, rs2′, and rd′ fields + * of the CIW, CL, CS, CA, and CB formats" in "The RISC-V Instruction Set Manual + * Volume I: Unprivileged ISA". + * */ +static uint32_t get_rs1_c(riscv_insn_t instruction) +{ + return GDB_REGNO_S0 + get_field32(instruction, INSN_FIELD_C_SREG1); +} + +static uint32_t get_opcode(const riscv_insn_t instruction) +{ + // opcode is first 7 bits of the instruction + uint32_t opcode = instruction & INSN_FIELD_OPCODE; + if ((instruction & 0x03) < 0x03) { // opcode size RVC + // RVC MASK_C = 0xe003 for load/store instructions + opcode = instruction & MASK_C_LD; + } + return opcode; +} + +static int get_loadstore_membase_regno(struct target *target, + const riscv_insn_t instruction, int *regid) +{ + uint32_t opcode = get_opcode(instruction); + int rs; + + switch (opcode) { + case MATCH_LB: + case MATCH_FLH & ~INSN_FIELD_FUNCT3: + case MATCH_SB: + case MATCH_FSH & ~INSN_FIELD_FUNCT3: + rs = get_field32(instruction, INSN_FIELD_RS1); + break; + + case MATCH_C_LWSP: + case MATCH_C_LDSP: // if xlen >= 64 or MATCH_C_FLWSP: + case MATCH_C_FLDSP: // or MATCH_C_LQSP if xlen == 128 + case MATCH_C_SWSP: + case MATCH_C_SDSP: // if xlen >= 64 or MATCH_C_FSWSP: + case MATCH_C_FSDSP: // or MATCH_C_SQSP if xlen == 128 + rs = GDB_REGNO_SP; + break; + + case MATCH_C_LW: + case MATCH_C_FLW: // or MATCH_C_LD if xlen >= 64 + case MATCH_C_FLD: // or MATCH_C_LQ if xlen == 128 + case MATCH_C_SW: + case MATCH_C_FSW: // or MATCH_C_SD if xlen >= 64 + case MATCH_C_FSD: // or MATCH_C_SQ if xlen == 128 + rs = get_rs1_c(instruction); + break; + + default: + LOG_TARGET_DEBUG(target, "0x%" PRIx32 " is not a RV32I or \"C\" load or" + " store", instruction); + return ERROR_FAIL; + } + *regid = rs; + return ERROR_OK; +} + +static int get_loadstore_memoffset(struct target *target, + const riscv_insn_t instruction, int16_t *memoffset) +{ + uint32_t opcode = get_opcode(instruction); + int16_t offset; + + switch (opcode) { + case MATCH_LB: + case MATCH_FLH & ~INSN_FIELD_FUNCT3: + case MATCH_SB: + case MATCH_FSH & ~INSN_FIELD_FUNCT3: + if (opcode == MATCH_SB || opcode == (MATCH_FSH & ~INSN_FIELD_FUNCT3)) { + offset = get_field32(instruction, INSN_FIELD_IMM12LO) | + (get_field32(instruction, INSN_FIELD_IMM12HI) << 5); + } else if (opcode == MATCH_LB || + opcode == (MATCH_FLH & ~INSN_FIELD_FUNCT3)) { + offset = get_field32(instruction, INSN_FIELD_IMM12); + } else { + assert(false); + } + /* sign extend 12-bit imm to 16-bits */ + if (offset & (1 << 11)) + offset |= 0xf000; + break; + + case MATCH_C_LWSP: + offset = get_offset_clwsp(instruction); + break; + + case MATCH_C_LDSP: // if xlen >= 64 or MATCH_C_FLWSP: + if (riscv_xlen(target) > 32) { // MATCH_C_LDSP + offset = get_offset_cldsp(instruction); + } else { // MATCH_C_FLWSP + offset = get_offset_clwsp(instruction); + } + break; + + case MATCH_C_FLDSP: // or MATCH_C_LQSP if xlen == 128 + if (riscv_xlen(target) == 128) { // MATCH_C_LQSP + offset = get_offset_clqsp(instruction); + } else { // MATCH_C_FLDSP + offset = get_offset_cldsp(instruction); + } + break; + + case MATCH_C_SWSP: + offset = get_offset_cswsp(instruction); + break; + + case MATCH_C_SDSP: // if xlen >= 64 or MATCH_C_FSWSP: + if (riscv_xlen(target) > 32) { // MATCH_C_SDSP + offset = get_offset_csdsp(instruction); + } else { // MATCH_C_FSWSP + offset = get_offset_cswsp(instruction); + } + break; + + case MATCH_C_FSDSP: // or MATCH_C_SQSP if xlen == 128 + if (riscv_xlen(target) == 128) { // MATCH_C_SQSP + offset = get_offset_csqsp(instruction); + } else { // MATCH_C_FSDSP + offset = get_offset_csdsp(instruction); // same as C.SDSP + } + break; + + case MATCH_C_LW: + offset = get_offset_clw(instruction); + break; + + case MATCH_C_FLW: // or MATCH_C_LD if xlen >= 64 + if (riscv_xlen(target) > 32) { // MATCH_C_LD + offset = get_offset_cld(instruction); + } else { // MATCH_C_FLW + offset = get_offset_clw(instruction); // same as C.FLW + } + break; + + case MATCH_C_FLD: // or MATCH_C_LQ if xlen == 128 + if (riscv_xlen(target) == 128) { // MATCH_C_LQ + offset = get_offset_clq(instruction); + } else { // MATCH_C_FLD + offset = get_offset_cld(instruction); // same as C.LD + } + break; + + case MATCH_C_SW: + offset = get_offset_clw(instruction); // same as C.LW + break; + + case MATCH_C_FSW: // or MATCH_C_SD if xlen >= 64 + if (riscv_xlen(target) > 32) { // MATCH_C_SD + offset = get_offset_cld(instruction); // same as C.LD + } else { // MATCH_C_FSW + offset = get_offset_clw(instruction); // same as C.LW + } + break; + + case MATCH_C_FSD: // or MATCH_C_SQ if xlen == 128 + if (riscv_xlen(target) == 128) { // MATCH_C_SQ + offset = get_offset_clq(instruction); // same as C.LQ + } else { // MATCH_C_FSD + offset = get_offset_cld(instruction); // same as C.LD + } + break; + + default: + LOG_TARGET_DEBUG(target, "0x%" PRIx32 " is not a RV32I or \"C\" load or" + " store", instruction); + return ERROR_FAIL; + } + *memoffset = offset; + return ERROR_OK; +} + +static int verify_loadstore(struct target *target, + const riscv_insn_t instruction, bool *is_read) +{ + uint32_t opcode = get_opcode(instruction); + bool misa_f = riscv_supports_extension(target, 'F'); + bool misa_d = riscv_supports_extension(target, 'D'); + enum watchpoint_rw rw; + + switch (opcode) { + case MATCH_LB: + case MATCH_FLH & ~INSN_FIELD_FUNCT3: + rw = WPT_READ; + break; + + case MATCH_SB: + case MATCH_FSH & ~INSN_FIELD_FUNCT3: + rw = WPT_WRITE; + break; + + case MATCH_C_LWSP: + if (get_field32(instruction, INSN_FIELD_RD) == 0) { + LOG_TARGET_DEBUG(target, + "The code points with rd = x0 are reserved for C.LWSP"); + return ERROR_FAIL; + } + rw = WPT_READ; + break; + + case MATCH_C_LDSP: // if xlen >= 64 or MATCH_C_FLWSP: + if (riscv_xlen(target) > 32) { // MATCH_C_LDSP + if (get_field32(instruction, INSN_FIELD_RD) == 0) { + LOG_TARGET_DEBUG(target, + "The code points with rd = x0 are reserved for C.LDSP"); + return ERROR_FAIL; + } + } else { // MATCH_C_FLWSP + if (!misa_f) { + LOG_TARGET_DEBUG(target, "Matched C.FLWSP but target doesn\'t " + "have the \"F\" extension"); + return ERROR_FAIL; + } + } + rw = WPT_READ; + break; + + case MATCH_C_FLDSP: // or MATCH_C_LQSP if xlen == 128 + if (riscv_xlen(target) == 128) { // MATCH_C_LQSP + if (get_field32(instruction, INSN_FIELD_RD) == 0) { + LOG_TARGET_DEBUG(target, + "The code points with rd = x0 are reserved for C.LQSP"); + return ERROR_FAIL; + } + } else { // MATCH_C_FLDSP + if (!misa_d) { + LOG_TARGET_DEBUG(target, "Matched C.FLDSP but target doesn\'t " + "have the \"D\" extension"); + return ERROR_FAIL; + } + } + rw = WPT_READ; + break; + + case MATCH_C_SWSP: + rw = WPT_WRITE; + break; + + case MATCH_C_SDSP: // if xlen >= 64 or MATCH_C_FSWSP: + if (riscv_xlen(target) == 32) { // MATCH_C_FSWSP + if (!misa_f) { + LOG_TARGET_DEBUG(target, "Matched C.FSWSP but target doesn\'t " + "have the \"F\" extension"); + return ERROR_FAIL; + } + } + rw = WPT_WRITE; + break; + + case MATCH_C_FSDSP: // or MATCH_C_SQSP if xlen == 128 + if (riscv_xlen(target) != 128) { // MATCH_C_SQSP + if (!misa_d) { + LOG_TARGET_DEBUG(target, "Matched C.FSDSP but target doesn\'t " + "have the \"D\" extension"); + return ERROR_FAIL; + } + } + rw = WPT_WRITE; + break; + + case MATCH_C_LW: + rw = WPT_READ; + break; + + case MATCH_C_FLW: // or MATCH_C_LD if xlen >= 64 + if (riscv_xlen(target) == 32) { // MATCH_C_FLW + if (!misa_f) { + LOG_TARGET_DEBUG(target, "Matched C.FLW but target doesn\'t " + "have the \"F\" extension"); + return ERROR_FAIL; + } + } + rw = WPT_READ; + break; + + case MATCH_C_FLD: // or MATCH_C_LQ if xlen == 128 + if (riscv_xlen(target) != 128) { // MATCH_C_FLD + if (!misa_d) { + LOG_TARGET_DEBUG(target, "Matched C.FLD but target doesn\'t " + "have the \"D\" extension"); + return ERROR_FAIL; + } + } + rw = WPT_READ; + break; + + case MATCH_C_SW: + rw = WPT_WRITE; + break; + + case MATCH_C_FSW: // or MATCH_C_SD if xlen >= 64 + if (riscv_xlen(target) == 32) { // MATCH_C_FSW + if (!misa_f) { + LOG_TARGET_DEBUG(target, "Matched C.FSW but target doesn\'t " + "have the \"F\" extension"); + return ERROR_FAIL; + } + } + rw = WPT_WRITE; + break; + + case MATCH_C_FSD: // or MATCH_C_SQ if xlen == 128 + if (riscv_xlen(target) != 128) { // MATCH_C_FSD + if (!misa_d) { + LOG_TARGET_DEBUG(target, "Matched C.FSD but target doesn\'t " + "have the \"D\" extension"); + return ERROR_FAIL; + } + } + rw = WPT_WRITE; + break; + + default: + LOG_TARGET_DEBUG(target, "0x%" PRIx32 " is not a RV32I or \"C\" load or" + " store", instruction); + return ERROR_FAIL; + } + + if (rw == WPT_WRITE) { + *is_read = false; + LOG_TARGET_DEBUG(target, "0x%" PRIx32 " is store instruction", + instruction); + } else { + *is_read = true; + LOG_TARGET_DEBUG(target, "0x%" PRIx32 " is load instruction", + instruction); + } + return ERROR_OK; +} + /* Sets *hit_watchpoint to the first watchpoint identified as causing the * current halt. * @@ -1639,18 +2073,20 @@ static int riscv_hit_watchpoint(struct target *target, struct watchpoint **hit_w } riscv_reg_t dpc; - riscv_get_register(target, &dpc, GDB_REGNO_DPC); + if (riscv_get_register(target, &dpc, GDB_REGNO_DPC) != ERROR_OK) + return ERROR_FAIL; const uint8_t length = 4; LOG_TARGET_DEBUG(target, "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_TARGET_ERROR(target, "Failed to read instruction at dpc 0x%" PRIx64, dpc); + LOG_TARGET_ERROR(target, "Failed to read instruction at dpc 0x%" PRIx64, + dpc); return ERROR_FAIL; } - uint32_t instruction = 0; + riscv_insn_t instruction = 0; for (int i = 0; i < length; i++) { LOG_TARGET_DEBUG(target, "Next byte is %x", buffer[i]); @@ -1658,40 +2094,39 @@ static int riscv_hit_watchpoint(struct target *target, struct watchpoint **hit_w } LOG_TARGET_DEBUG(target, "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; + int rs; + target_addr_t mem_addr; + int16_t memoffset; + + if (get_loadstore_membase_regno(target, instruction, &rs) != ERROR_OK) + return ERROR_FAIL; + if (riscv_get_register(target, &mem_addr, rs) != ERROR_OK) + return ERROR_FAIL; + if (get_loadstore_memoffset(target, instruction, &memoffset) != ERROR_OK) + return ERROR_FAIL; - if (opcode == MATCH_LB || opcode == MATCH_SB) { - rs1 = (instruction & 0xf8000) >> 15; - riscv_get_register(target, &mem_addr, rs1); + mem_addr += memoffset; + bool is_load; - if (opcode == MATCH_SB) { - LOG_TARGET_DEBUG(target, "%x is store instruction", instruction); - imm = ((instruction & 0xf80) >> 7) | ((instruction & 0xfe000000) >> 20); - } else { - LOG_TARGET_DEBUG(target, "%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_TARGET_DEBUG(target, "Memory address=0x%" PRIx64, mem_addr); - } else { - LOG_TARGET_DEBUG(target, "%x is not a RV32I load or store", instruction); + if (verify_loadstore(target, instruction, &is_load) != ERROR_OK) return ERROR_FAIL; - } struct watchpoint *wp = target->watchpoints; while (wp) { - /*TODO support length/mask */ - if (wp->address == mem_addr) { + /* TODO support mask and check read/write/access */ + /* TODO check for intersection of the access range and watchpoint range + Recommended matching: + if (intersects(mem_addr, mem_addr + ref_size, wp->address, wp->address + wp->length)) + */ + if (mem_addr >= wp->address && + mem_addr < (wp->address + wp->length)) { *hit_watchpoint = wp; - LOG_TARGET_DEBUG(target, "Hit address=%" TARGET_PRIxADDR, wp->address); + LOG_TARGET_DEBUG(target, "WP hit found: %s 0x%" TARGET_PRIxADDR + " covered by %s wp at address 0x%" TARGET_PRIxADDR, + is_load ? "Load from" : "Store to", mem_addr, + (wp->rw == WPT_READ ? + "read" : (wp->rw == WPT_WRITE ? "write" : "access")), + wp->address); return ERROR_OK; } wp = wp->next; @@ -1702,6 +2137,8 @@ static int riscv_hit_watchpoint(struct target *target, struct watchpoint **hit_w * * OpenOCD will behave as if this function had never been implemented i.e. * report the halt to GDB with no address information. */ + LOG_TARGET_DEBUG(target, "No watchpoint found that would cover %s 0x%" + TARGET_PRIxADDR, is_load ? "load from" : "store to", mem_addr); return ERROR_FAIL; }