diff options
-rw-r--r-- | src/target/riscv/encoding.h | 40 | ||||
-rw-r--r-- | src/target/riscv/gdb_regs.h | 75 | ||||
-rw-r--r-- | src/target/riscv/program.c | 8 | ||||
-rw-r--r-- | src/target/riscv/riscv-011.c | 196 | ||||
-rw-r--r-- | src/target/riscv/riscv-013.c | 156 | ||||
-rw-r--r-- | src/target/riscv/riscv.c | 431 | ||||
-rw-r--r-- | src/target/riscv/riscv.h | 10 | ||||
-rw-r--r-- | src/target/target.c | 28 |
8 files changed, 583 insertions, 361 deletions
diff --git a/src/target/riscv/encoding.h b/src/target/riscv/encoding.h index 8ec1345..c109ce1 100644 --- a/src/target/riscv/encoding.h +++ b/src/target/riscv/encoding.h @@ -113,19 +113,19 @@ #define PRV_H 2 #define PRV_M 3 -#define SPTBR32_MODE 0x80000000 -#define SPTBR32_ASID 0x7FC00000 -#define SPTBR32_PPN 0x003FFFFF -#define SPTBR64_MODE 0xF000000000000000 -#define SPTBR64_ASID 0x0FFFF00000000000 -#define SPTBR64_PPN 0x00000FFFFFFFFFFF +#define SATP32_MODE 0x80000000 +#define SATP32_ASID 0x7FC00000 +#define SATP32_PPN 0x003FFFFF +#define SATP64_MODE 0xF000000000000000 +#define SATP64_ASID 0x0FFFF00000000000 +#define SATP64_PPN 0x00000FFFFFFFFFFF -#define SPTBR_MODE_OFF 0 -#define SPTBR_MODE_SV32 1 -#define SPTBR_MODE_SV39 8 -#define SPTBR_MODE_SV48 9 -#define SPTBR_MODE_SV57 10 -#define SPTBR_MODE_SV64 11 +#define SATP_MODE_OFF 0 +#define SATP_MODE_SV32 1 +#define SATP_MODE_SV39 8 +#define SATP_MODE_SV48 9 +#define SATP_MODE_SV57 10 +#define SATP_MODE_SV64 11 #define PMP_R 0x01 #define PMP_W 0x02 @@ -177,12 +177,12 @@ # define MSTATUS_SD MSTATUS64_SD # define SSTATUS_SD SSTATUS64_SD # define RISCV_PGLEVEL_BITS 9 -# define SPTBR_MODE SPTBR64_MODE +# define SATP_MODE SATP64_MODE #else # define MSTATUS_SD MSTATUS32_SD # define SSTATUS_SD SSTATUS32_SD # define RISCV_PGLEVEL_BITS 10 -# define SPTBR_MODE SPTBR32_MODE +# define SATP_MODE SATP32_MODE #endif #define RISCV_PGSHIFT 12 #define RISCV_PGSIZE (1 << RISCV_PGSHIFT) @@ -790,9 +790,9 @@ #define CSR_SSCRATCH 0x140 #define CSR_SEPC 0x141 #define CSR_SCAUSE 0x142 -#define CSR_SBADADDR 0x143 +#define CSR_STVAL 0x143 #define CSR_SIP 0x144 -#define CSR_SPTBR 0x180 +#define CSR_SATP 0x180 #define CSR_MSTATUS 0x300 #define CSR_MISA 0x301 #define CSR_MEDELEG 0x302 @@ -803,7 +803,7 @@ #define CSR_MSCRATCH 0x340 #define CSR_MEPC 0x341 #define CSR_MCAUSE 0x342 -#define CSR_MBADADDR 0x343 +#define CSR_MTVAL 0x343 #define CSR_MIP 0x344 #define CSR_PMPCFG0 0x3a0 #define CSR_PMPCFG1 0x3a1 @@ -1282,9 +1282,9 @@ DECLARE_CSR(scounteren, CSR_SCOUNTEREN) DECLARE_CSR(sscratch, CSR_SSCRATCH) DECLARE_CSR(sepc, CSR_SEPC) DECLARE_CSR(scause, CSR_SCAUSE) -DECLARE_CSR(sbadaddr, CSR_SBADADDR) +DECLARE_CSR(stval, CSR_STVAL) DECLARE_CSR(sip, CSR_SIP) -DECLARE_CSR(sptbr, CSR_SPTBR) +DECLARE_CSR(satp, CSR_SATP) DECLARE_CSR(mstatus, CSR_MSTATUS) DECLARE_CSR(misa, CSR_MISA) DECLARE_CSR(medeleg, CSR_MEDELEG) @@ -1295,7 +1295,7 @@ DECLARE_CSR(mcounteren, CSR_MCOUNTEREN) DECLARE_CSR(mscratch, CSR_MSCRATCH) DECLARE_CSR(mepc, CSR_MEPC) DECLARE_CSR(mcause, CSR_MCAUSE) -DECLARE_CSR(mbadaddr, CSR_MBADADDR) +DECLARE_CSR(mtval, CSR_MTVAL) DECLARE_CSR(mip, CSR_MIP) DECLARE_CSR(pmpcfg0, CSR_PMPCFG0) DECLARE_CSR(pmpcfg1, CSR_PMPCFG1) diff --git a/src/target/riscv/gdb_regs.h b/src/target/riscv/gdb_regs.h index 2952e10..731f3e3 100644 --- a/src/target/riscv/gdb_regs.h +++ b/src/target/riscv/gdb_regs.h @@ -4,15 +4,76 @@ // gdb's register list is defined in riscv_gdb_reg_names gdb/riscv-tdep.c in // its source tree. We must interpret the numbers the same here. enum gdb_regno { - GDB_REGNO_XPR0 = 0, - GDB_REGNO_X0 = GDB_REGNO_XPR0 + 0, - GDB_REGNO_ZERO = GDB_REGNO_XPR0 + 0, - GDB_REGNO_S0 = GDB_REGNO_XPR0 + 8, - GDB_REGNO_S1 = GDB_REGNO_XPR0 + 9, - GDB_REGNO_XPR31 = GDB_REGNO_XPR0 + 31, + GDB_REGNO_ZERO = 0, /* Read-only register, always 0. */ + GDB_REGNO_RA = 1, /* Return Address. */ + GDB_REGNO_SP = 2, /* Stack Pointer. */ + GDB_REGNO_GP = 3, /* Global Pointer. */ + GDB_REGNO_TP = 4, /* Thread Pointer. */ + GDB_REGNO_T0, + GDB_REGNO_T1, + GDB_REGNO_T2, + GDB_REGNO_S0 = 8, + GDB_REGNO_FP = 8, /* Frame Pointer. */ + GDB_REGNO_S1, + GDB_REGNO_A0 = 10, /* First argument. */ + GDB_REGNO_A1 = 11, /* Second argument. */ + GDB_REGNO_A2, + GDB_REGNO_A3, + GDB_REGNO_A4, + GDB_REGNO_A5, + GDB_REGNO_A6, + GDB_REGNO_A7, + GDB_REGNO_S2, + GDB_REGNO_S3, + GDB_REGNO_S4, + GDB_REGNO_S5, + GDB_REGNO_S6, + GDB_REGNO_S7, + GDB_REGNO_S8, + GDB_REGNO_S9, + GDB_REGNO_S10, + GDB_REGNO_S11, + GDB_REGNO_T3, + GDB_REGNO_T4, + GDB_REGNO_T5, + GDB_REGNO_T6, + GDB_REGNO_XPR31 = GDB_REGNO_T6, + GDB_REGNO_PC = 32, GDB_REGNO_FPR0 = 33, - GDB_REGNO_FPR31 = GDB_REGNO_FPR0 + 31, + GDB_REGNO_FT0 = GDB_REGNO_FPR0, + GDB_REGNO_FT1, + GDB_REGNO_FT2, + GDB_REGNO_FT3, + GDB_REGNO_FT4, + GDB_REGNO_FT5, + GDB_REGNO_FT6, + GDB_REGNO_FT7, + GDB_REGNO_FS0, + GDB_REGNO_FS1, + GDB_REGNO_FA0, + GDB_REGNO_FA1, + GDB_REGNO_FA2, + GDB_REGNO_FA3, + GDB_REGNO_FA4, + GDB_REGNO_FA5, + GDB_REGNO_FA6, + GDB_REGNO_FA7, + GDB_REGNO_FS2, + GDB_REGNO_FS3, + GDB_REGNO_FS4, + GDB_REGNO_FS5, + GDB_REGNO_FS6, + GDB_REGNO_FS7, + GDB_REGNO_FS8, + GDB_REGNO_FS9, + GDB_REGNO_FS10, + GDB_REGNO_FS11, + GDB_REGNO_FT8, + GDB_REGNO_FT9, + GDB_REGNO_FT10, + GDB_REGNO_FT11, + GDB_REGNO_FPR31 = GDB_REGNO_FT11, GDB_REGNO_CSR0 = 65, GDB_REGNO_TSELECT = CSR_TSELECT + GDB_REGNO_CSR0, GDB_REGNO_TDATA1 = CSR_TDATA1 + GDB_REGNO_CSR0, diff --git a/src/target/riscv/program.c b/src/target/riscv/program.c index e7238dd..0c0dbd8 100644 --- a/src/target/riscv/program.c +++ b/src/target/riscv/program.c @@ -45,7 +45,7 @@ int riscv_program_exec(struct riscv_program *p, struct target *t) keep_alive(); riscv_reg_t saved_registers[GDB_REGNO_XPR31 + 1]; - for (size_t i = GDB_REGNO_XPR0 + 1; i <= GDB_REGNO_XPR31; ++i) { + for (size_t i = GDB_REGNO_ZERO + 1; i <= GDB_REGNO_XPR31; ++i) { if (p->writes_xreg[i]) { LOG_DEBUG("Saving register %d as used by program", (int)i); saved_registers[i] = riscv_get_register(t, i); @@ -72,7 +72,7 @@ int riscv_program_exec(struct riscv_program *p, struct target *t) if (i >= riscv_debug_buffer_size(p->target)) p->debug_buffer[i] = riscv_read_debug_buffer(t, i); - for (size_t i = GDB_REGNO_XPR0; i <= GDB_REGNO_XPR31; ++i) + for (size_t i = GDB_REGNO_ZERO; i <= GDB_REGNO_XPR31; ++i) if (p->writes_xreg[i]) riscv_set_register(t, i, saved_registers[i]); @@ -112,13 +112,13 @@ int riscv_program_lbr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno int riscv_program_csrr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno csr) { assert(csr >= GDB_REGNO_CSR0 && csr <= GDB_REGNO_CSR4095); - return riscv_program_insert(p, csrrs(d, GDB_REGNO_X0, csr - GDB_REGNO_CSR0)); + return riscv_program_insert(p, csrrs(d, GDB_REGNO_ZERO, csr - GDB_REGNO_CSR0)); } int riscv_program_csrw(struct riscv_program *p, enum gdb_regno s, enum gdb_regno csr) { assert(csr >= GDB_REGNO_CSR0); - return riscv_program_insert(p, csrrw(GDB_REGNO_X0, s, csr - GDB_REGNO_CSR0)); + return riscv_program_insert(p, csrrw(GDB_REGNO_ZERO, s, csr - GDB_REGNO_CSR0)); } int riscv_program_fence_i(struct riscv_program *p) diff --git a/src/target/riscv/riscv-011.c b/src/target/riscv/riscv-011.c index 6e576fe..6692a9d 100644 --- a/src/target/riscv/riscv-011.c +++ b/src/target/riscv/riscv-011.c @@ -189,12 +189,6 @@ typedef struct { struct memory_cache_line dram_cache[DRAM_CACHE_SIZE]; - /* Single buffer that contains all register names, instead of calling - * malloc for each register. Needs to be freed when reg_list is freed. */ - char *reg_names; - /* Single buffer that contains all register values. */ - void *reg_values; - // Number of run-test/idle cycles the target requests we do after each dbus // access. unsigned int dtmcontrol_idle; @@ -223,7 +217,7 @@ typedef struct { static int poll_target(struct target *target, bool announce); static int riscv011_poll(struct target *target); -static int register_get(struct reg *reg); +static riscv_reg_t get_register(struct target *target, int hartid, int regid); /*** Utility functions. ***/ @@ -1181,30 +1175,6 @@ static int resume(struct target *target, int debug_execution, bool step) return execute_resume(target, step); } -/** Update register sizes based on xlen. */ -static void update_reg_list(struct target *target) -{ - riscv011_info_t *info = get_info(target); - if (info->reg_values) { - free(info->reg_values); - } - info->reg_values = malloc(GDB_REGNO_COUNT * riscv_xlen(target) / 4); - - for (unsigned int i = 0; i < GDB_REGNO_COUNT; i++) { - struct reg *r = &target->reg_cache->reg_list[i]; - r->value = info->reg_values + i * riscv_xlen(target) / 4; - if (r->dirty) { - LOG_ERROR("Register %d was dirty. Its value is lost.", i); - } - if (i == GDB_REGNO_PRIV) { - r->size = 8; - } else { - r->size = riscv_xlen(target); - } - r->valid = false; - } -} - static uint64_t reg_cache_get(struct target *target, unsigned int number) { struct reg *r = &target->reg_cache->reg_list[number]; @@ -1236,7 +1206,8 @@ static int update_mstatus_actual(struct target *target) // Force reading the register. In that process mstatus_actual will be // updated. - return register_get(&target->reg_cache->reg_list[GDB_REGNO_MSTATUS]); + get_register(target, 0, GDB_REGNO_MSTATUS); + return ERROR_OK; } /*** OpenOCD target functions. ***/ @@ -1275,58 +1246,6 @@ static int register_read(struct target *target, riscv_reg_t *value, int regnum) return ERROR_OK; } -static int register_get(struct reg *reg) -{ - struct target *target = (struct target *) reg->arch_info; - riscv011_info_t *info = get_info(target); - - maybe_write_tselect(target); - riscv_reg_t value = ~0; - - if (reg->number <= GDB_REGNO_XPR31) { - value = reg_cache_get(target, reg->number); - LOG_DEBUG("%s=0x%" PRIx64, reg->name, reg_cache_get(target, reg->number)); - } else if (reg->number == GDB_REGNO_PC) { - value = info->dpc; - LOG_DEBUG("%s=0x%" PRIx64 " (cached)", reg->name, info->dpc); - } else if (reg->number >= GDB_REGNO_FPR0 && reg->number <= GDB_REGNO_FPR31) { - int result = update_mstatus_actual(target); - if (result != ERROR_OK) { - return result; - } - unsigned i = 0; - if ((info->mstatus_actual & MSTATUS_FS) == 0) { - info->mstatus_actual = set_field(info->mstatus_actual, MSTATUS_FS, 1); - cache_set_load(target, i++, S0, SLOT1); - cache_set32(target, i++, csrw(S0, CSR_MSTATUS)); - cache_set(target, SLOT1, info->mstatus_actual); - } - - if (riscv_xlen(target) == 32) { - cache_set32(target, i++, fsw(reg->number - GDB_REGNO_FPR0, 0, DEBUG_RAM_START + 16)); - } else { - cache_set32(target, i++, fsd(reg->number - GDB_REGNO_FPR0, 0, DEBUG_RAM_START + 16)); - } - cache_set_jump(target, i++); - - if (cache_write(target, 4, true) != ERROR_OK) { - return ERROR_FAIL; - } - } else if (reg->number == GDB_REGNO_PRIV) { - value = get_field(info->dcsr, DCSR_PRV); - } else { - if (register_read(target, &value, reg->number) != ERROR_OK) - return ERROR_FAIL; - } - buf_set_u64(reg->value, 0, riscv_xlen(target), value); - - if (reg->number == GDB_REGNO_MSTATUS) { - reg->valid = true; - } - - return ERROR_OK; -} - // Write the register. No caching or games. static int register_write(struct target *target, unsigned int number, uint64_t value) @@ -1344,7 +1263,7 @@ static int register_write(struct target *target, unsigned int number, cache_set_store(target, 1, S0, SLOT_LAST); cache_set_jump(target, 2); } else if (number <= GDB_REGNO_XPR31) { - cache_set_load(target, 0, number - GDB_REGNO_XPR0, SLOT0); + cache_set_load(target, 0, number - GDB_REGNO_ZERO, SLOT0); cache_set_jump(target, 1); } else if (number == GDB_REGNO_PC) { info->dpc = value; @@ -1399,33 +1318,53 @@ static int register_write(struct target *target, unsigned int number, return ERROR_OK; } -static int register_set(struct reg *reg, uint8_t *buf) +static riscv_reg_t get_register(struct target *target, int hartid, int regid) { - struct target *target = (struct target *) reg->arch_info; + assert(hartid == 0); + riscv011_info_t *info = get_info(target); - uint64_t value = buf_get_u64(buf, 0, riscv_xlen(target)); + maybe_write_tselect(target); + riscv_reg_t value = ~0; - LOG_DEBUG("write 0x%" PRIx64 " to %s", value, reg->name); - struct reg *r = &target->reg_cache->reg_list[reg->number]; - r->valid = true; - memcpy(r->value, buf, (r->size + 7) / 8); + if (regid <= GDB_REGNO_XPR31) { + value = reg_cache_get(target, regid); + } else if (regid == GDB_REGNO_PC) { + value = info->dpc; + } else if (regid >= GDB_REGNO_FPR0 && regid <= GDB_REGNO_FPR31) { + int result = update_mstatus_actual(target); + if (result != ERROR_OK) { + return ~0; + } + unsigned i = 0; + if ((info->mstatus_actual & MSTATUS_FS) == 0) { + info->mstatus_actual = set_field(info->mstatus_actual, MSTATUS_FS, 1); + cache_set_load(target, i++, S0, SLOT1); + cache_set32(target, i++, csrw(S0, CSR_MSTATUS)); + cache_set(target, SLOT1, info->mstatus_actual); + } - return register_write(target, reg->number, value); -} + if (riscv_xlen(target) == 32) { + cache_set32(target, i++, fsw(regid - GDB_REGNO_FPR0, 0, DEBUG_RAM_START + 16)); + } else { + cache_set32(target, i++, fsd(regid - GDB_REGNO_FPR0, 0, DEBUG_RAM_START + 16)); + } + cache_set_jump(target, i++); -static struct reg_arch_type riscv_reg_arch_type = { - .get = register_get, - .set = register_set -}; + if (cache_write(target, 4, true) != ERROR_OK) { + return ~0; + } + } else if (regid == GDB_REGNO_PRIV) { + value = get_field(info->dcsr, DCSR_PRV); + } else { + if (register_read(target, &value, regid) != ERROR_OK) { + value = ~0; + } + } -static riscv_reg_t get_register(struct target *target, int hartid, int regid) -{ - assert(hartid == 0); - riscv_reg_t value; - if (register_read(target, &value, regid) != ERROR_OK) { - // TODO: propagate errors - value = ~0; + if (regid == GDB_REGNO_MSTATUS) { + target->reg_cache->reg_list[regid].valid = true; } + return value; } @@ -1466,45 +1405,10 @@ static int init_target(struct command_context *cmd_ctx, generic_info->version_specific = calloc(1, sizeof(riscv011_info_t)); if (!generic_info->version_specific) return ERROR_FAIL; - riscv011_info_t *info = get_info(target); - target->reg_cache = calloc(1, sizeof(*target->reg_cache)); - target->reg_cache->name = "RISC-V registers"; - target->reg_cache->num_regs = GDB_REGNO_COUNT; - - target->reg_cache->reg_list = calloc(GDB_REGNO_COUNT, sizeof(struct reg)); - - const unsigned int max_reg_name_len = 12; - info->reg_names = calloc(1, GDB_REGNO_COUNT * max_reg_name_len); - char *reg_name = info->reg_names; - info->reg_values = NULL; - - for (unsigned int i = 0; i < GDB_REGNO_COUNT; i++) { - struct reg *r = &target->reg_cache->reg_list[i]; - r->number = i; - r->caller_save = true; - r->dirty = false; - r->valid = false; - r->exist = true; - r->type = &riscv_reg_arch_type; - r->arch_info = target; - if (i <= GDB_REGNO_XPR31) { - sprintf(reg_name, "x%d", i); - } else if (i == GDB_REGNO_PC) { - sprintf(reg_name, "pc"); - } else if (i >= GDB_REGNO_FPR0 && i <= GDB_REGNO_FPR31) { - sprintf(reg_name, "f%d", i - GDB_REGNO_FPR0); - } else if (i >= GDB_REGNO_CSR0 && i <= GDB_REGNO_CSR4095) { - sprintf(reg_name, "csr%d", i - GDB_REGNO_CSR0); - } else if (i == GDB_REGNO_PRIV) { - sprintf(reg_name, "priv"); - } - if (reg_name[0]) { - r->name = reg_name; - } - reg_name += strlen(reg_name) + 1; - assert(reg_name < info->reg_names + GDB_REGNO_COUNT * max_reg_name_len); - } + // Assume 32-bit until we discover the real value in examine(). + generic_info->xlen[0] = 32; + riscv_init_registers(target); return ERROR_OK; } @@ -1690,9 +1594,6 @@ static int examine(struct target *target) } LOG_DEBUG("Discovered XLEN is %d", riscv_xlen(target)); - // Update register list to match discovered XLEN. - update_reg_list(target); - if (read_csr(target, &r->misa, CSR_MISA) != ERROR_OK) { const unsigned old_csr_misa = 0xf10; LOG_WARNING("Failed to read misa at 0x%x; trying 0x%x.", CSR_MISA, @@ -1705,6 +1606,9 @@ static int examine(struct target *target) } } + // Update register list to match discovered XLEN/supported extensions. + riscv_init_registers(target); + info->never_halted = true; int result = riscv011_poll(target); diff --git a/src/target/riscv/riscv-013.c b/src/target/riscv/riscv-013.c index 14a7d26..98a6352 100644 --- a/src/target/riscv/riscv-013.c +++ b/src/target/riscv/riscv-013.c @@ -128,12 +128,6 @@ struct trigger { int unique_id; }; -struct memory_cache_line { - uint32_t data; - bool valid; - bool dirty; -}; - typedef enum { YNM_MAYBE, YNM_YES, @@ -156,12 +150,6 @@ typedef struct { /* We only need the address so that we know the alignment of the buffer. */ riscv_addr_t progbuf_address; - /* Single buffer that contains all register names, instead of calling - * malloc for each register. Needs to be freed when reg_list is freed. */ - char *reg_names; - /* Single buffer that contains all register values. */ - void *reg_values; - // Number of run-test/idle cycles the target requests we do after each dbus // access. unsigned int dtmcontrol_idle; @@ -296,26 +284,8 @@ static riscv013_info_t *get_info(const struct target *target) return (riscv013_info_t *) info->version_specific; } -/*** Necessary prototypes. ***/ - -static int register_get(struct reg *reg); - /*** Utility functions. ***/ -bool supports_extension(struct target *target, char letter) -{ - RISCV_INFO(r); - unsigned num; - if (letter >= 'a' && letter <= 'z') { - num = letter - 'a'; - } else if (letter >= 'A' && letter <= 'Z') { - num = letter - 'A'; - } else { - return false; - } - return r->misa & (1 << num); -} - static void select_dmi(struct target *target) { static uint8_t ir_dmi[1] = {DTM_DMI}; @@ -667,7 +637,7 @@ static uint32_t access_register_command(uint32_t number, unsigned size, if (number <= GDB_REGNO_XPR31) { command = set_field(command, AC_ACCESS_REGISTER_REGNO, - 0x1000 + number - GDB_REGNO_XPR0); + 0x1000 + number - GDB_REGNO_ZERO); } else if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) { command = set_field(command, AC_ACCESS_REGISTER_REGNO, 0x1020 + number - GDB_REGNO_FPR0); @@ -969,7 +939,7 @@ static int register_write_direct(struct target *target, unsigned number, return ERROR_FAIL; if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31 && - supports_extension(target, 'D') && + riscv_supports_extension(target, 'D') && riscv_xlen(target) < 64) { /* There are no instructions to move all the bits from a register, so * we need to use some scratch RAM. */ @@ -991,7 +961,7 @@ static int register_write_direct(struct target *target, unsigned number, return ERROR_FAIL; if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) { - if (supports_extension(target, 'D')) { + if (riscv_supports_extension(target, 'D')) { riscv_program_insert(&program, fmv_d_x(number - GDB_REGNO_FPR0, S0)); } else { riscv_program_insert(&program, fmv_w_x(number - GDB_REGNO_FPR0, S0)); @@ -1038,7 +1008,7 @@ static int register_read_direct(struct target *target, uint64_t *value, uint32_t if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) { // TODO: Possibly set F in mstatus. - if (supports_extension(target, 'D') && riscv_xlen(target) < 64) { + if (riscv_supports_extension(target, 'D') && riscv_xlen(target) < 64) { /* There are no instructions to move all the bits from a * register, so we need to use some scratch RAM. */ riscv_program_insert(&program, fsd(number - GDB_REGNO_FPR0, S0, @@ -1051,7 +1021,7 @@ static int register_read_direct(struct target *target, uint64_t *value, uint32_t if (register_write_direct(target, GDB_REGNO_S0, scratch.hart_address) != ERROR_OK) return ERROR_FAIL; - } else if (supports_extension(target, 'D')) { + } else if (riscv_supports_extension(target, 'D')) { riscv_program_insert(&program, fmv_x_d(S0, number - GDB_REGNO_FPR0)); } else { riscv_program_insert(&program, fmv_x_w(S0, number - GDB_REGNO_FPR0)); @@ -1090,40 +1060,6 @@ static int register_read_direct(struct target *target, uint64_t *value, uint32_t /*** OpenOCD target functions. ***/ -static int register_get(struct reg *reg) -{ - struct target *target = (struct target *) reg->arch_info; - uint64_t value = riscv_get_register(target, reg->number); - buf_set_u64(reg->value, 0, 64, value); - return ERROR_OK; -} - -static int register_write(struct target *target, unsigned int number, - uint64_t value) -{ - riscv_set_register(target, number, value); - return ERROR_OK; -} - -static int register_set(struct reg *reg, uint8_t *buf) -{ - struct target *target = (struct target *) reg->arch_info; - - uint64_t value = buf_get_u64(buf, 0, riscv_xlen(target)); - - LOG_DEBUG("write 0x%" PRIx64 " to %s", value, reg->name); - struct reg *r = &target->reg_cache->reg_list[reg->number]; - r->valid = true; - memcpy(r->value, buf, (r->size + 7) / 8); - - return register_write(target, reg->number, value); -} - -static struct reg_arch_type riscv_reg_arch_type = { - .get = register_get, - .set = register_set -}; - static int init_target(struct command_context *cmd_ctx, struct target *target) { @@ -1168,44 +1104,6 @@ static int init_target(struct command_context *cmd_ctx, info->abstract_read_fpr_supported = true; info->abstract_write_fpr_supported = true; - target->reg_cache = calloc(1, sizeof(*target->reg_cache)); - target->reg_cache->name = "RISC-V Registers"; - target->reg_cache->num_regs = GDB_REGNO_COUNT; - - target->reg_cache->reg_list = calloc(GDB_REGNO_COUNT, sizeof(struct reg)); - - const unsigned int max_reg_name_len = 12; - info->reg_names = calloc(1, GDB_REGNO_COUNT * max_reg_name_len); - char *reg_name = info->reg_names; - info->reg_values = NULL; - - for (unsigned int i = 0; i < GDB_REGNO_COUNT; i++) { - struct reg *r = &target->reg_cache->reg_list[i]; - r->number = i; - r->caller_save = true; - r->dirty = false; - r->valid = false; - r->exist = true; - r->type = &riscv_reg_arch_type; - r->arch_info = target; - if (i <= GDB_REGNO_XPR31) { - sprintf(reg_name, "x%d", i); - } else if (i == GDB_REGNO_PC) { - sprintf(reg_name, "pc"); - } else if (i >= GDB_REGNO_FPR0 && i <= GDB_REGNO_FPR31) { - sprintf(reg_name, "f%d", i - GDB_REGNO_FPR0); - } else if (i >= GDB_REGNO_CSR0 && i <= GDB_REGNO_CSR4095) { - sprintf(reg_name, "csr%d", i - GDB_REGNO_CSR0); - } else if (i == GDB_REGNO_PRIV) { - sprintf(reg_name, "priv"); - } - if (reg_name[0]) { - r->name = reg_name; - } - reg_name += strlen(reg_name) + 1; - assert(reg_name < info->reg_names + GDB_REGNO_COUNT * max_reg_name_len); - } - return ERROR_OK; } @@ -1295,36 +1193,24 @@ static int examine(struct target *target) RISCV_INFO(r); r->impebreak = get_field(dmstatus, DMI_DMSTATUS_IMPEBREAK); - int original_coreid = target->coreid; + // Don't call any riscv_* functions until after we've counted the number of + // cores and initialized registers. for (int i = 0; i < RISCV_MAX_HARTS; ++i) { - /* Fake being a non-RTOS targeted to this core so we can see if - * it exists. This avoids the assertion in - * riscv_set_current_hartid() that ensures non-RTOS targets - * don't touch the harts they're not assigned to. */ - target->coreid = i; - r->hart_count = i + 1; - riscv_set_current_hartid(target, i); + if (!riscv_rtos_enabled(target) && i != target->coreid) + continue; + + r->current_hartid = i; + riscv013_select_current_hart(target); uint32_t s = dmi_read(target, DMI_DMSTATUS); if (get_field(s, DMI_DMSTATUS_ANYNONEXISTENT)) { - r->hart_count--; break; } - } - target->coreid = original_coreid; - - LOG_DEBUG("Enumerated %d harts", r->hart_count); - - /* Halt every hart so we can probe them. */ - riscv_halt_all_harts(target); - - /* Find the address of the program buffer, which must be done without - * knowing anything about the target. */ - for (int i = 0; i < riscv_count_harts(target); ++i) { - if (!riscv_hart_enabled(target, i)) - continue; + r->hart_count = i + 1; - riscv_set_current_hartid(target, i); + if (!riscv_is_halted(target)) { + riscv013_halt_current_hart(target); + } /* Without knowing anything else we can at least mess with the * program buffer. */ @@ -1337,7 +1223,11 @@ static int examine(struct target *target) r->xlen[i] = 32; } - r->misa = riscv_get_register_on_hart(target, i, GDB_REGNO_MISA); + register_read_direct(target, &r->misa, GDB_REGNO_MISA); + + // Now init registers based on what we discovered. + if (riscv_init_registers(target) != ERROR_OK) + return ERROR_FAIL; /* Display this as early as possible to help people who are using * really slow simulators. */ @@ -1345,12 +1235,16 @@ static int examine(struct target *target) r->misa); } + LOG_DEBUG("Enumerated %d harts", r->hart_count); + /* Then we check the number of triggers availiable to each hart. */ riscv_enumerate_triggers(target); /* Resumes all the harts, so the debugger can later pause them. */ + // TODO: Only do this if the harts were halted to start with. riscv_resume_all_harts(target); target->state = TARGET_RUNNING; + target_set_examined(target); if (target->rtos) { diff --git a/src/target/riscv/riscv.c b/src/target/riscv/riscv.c index b9be133..2f1084f 100644 --- a/src/target/riscv/riscv.c +++ b/src/target/riscv/riscv.c @@ -151,21 +151,6 @@ typedef enum slot { #define DBUS_ADDRESS_UNKNOWN 0xffff -// gdb's register list is defined in riscv_gdb_reg_names gdb/riscv-tdep.c in -// its source tree. We must interpret the numbers the same here. -enum { - REG_XPR0 = 0, - REG_XPR31 = 31, - REG_PC = 32, - REG_FPR0 = 33, - REG_FPR31 = 64, - REG_CSR0 = 65, - REG_MSTATUS = CSR_MSTATUS + REG_CSR0, - REG_CSR4095 = 4160, - REG_PRIV = 4161, - REG_COUNT -}; - #define MAX_HWBPS 16 #define DRAM_CACHE_SIZE 16 @@ -203,6 +188,14 @@ int riscv_reset_timeout_sec = DEFAULT_RESET_TIMEOUT_SEC; bool riscv_use_scratch_ram = false; uint64_t riscv_scratch_ram_address = 0; +/* In addition to the ones in the standard spec, we'll also expose additional + * CSRs in this list. + * The list is either NULL, or a series of ranges (inclusive), terminated with + * 1,0. */ +struct { + uint16_t low, high; +} *expose_csr; + static uint32_t dtmcontrol_scan(struct target *target, uint32_t out) { struct scan_field field; @@ -430,7 +423,6 @@ static int add_trigger(struct target *target, struct trigger *trigger) int result = ERROR_OK; for (int hartid = first_hart; hartid < riscv_count_harts(target); ++hartid) { - LOG_DEBUG(">>> hartid=%d", hartid); if (!riscv_hart_enabled(target, hartid)) continue; if (hartid > first_hart) { @@ -760,6 +752,11 @@ static int riscv_get_gdb_reg_list(struct target *target, LOG_DEBUG("reg_class=%d", reg_class); LOG_DEBUG("rtos_hartid=%d current_hartid=%d", r->rtos_hartid, r->current_hartid); + if (!target->reg_cache) { + LOG_ERROR("Target not initialized. Return ERROR_FAIL."); + return ERROR_FAIL; + } + if (r->rtos_hartid != -1 && riscv_rtos_enabled(target)) riscv_set_current_hartid(target, r->rtos_hartid); else @@ -770,7 +767,7 @@ static int riscv_get_gdb_reg_list(struct target *target, *reg_list_size = 32; break; case REG_CLASS_ALL: - *reg_list_size = REG_COUNT; + *reg_list_size = GDB_REGNO_COUNT; break; default: LOG_ERROR("Unsupported reg_class: %d", reg_class); @@ -781,14 +778,10 @@ static int riscv_get_gdb_reg_list(struct target *target, if (!*reg_list) { return ERROR_FAIL; } - - if (!target->reg_cache) { - LOG_ERROR("Target not initialized. Return ERROR_FAIL."); - return ERROR_FAIL; - } - + for (int i = 0; i < *reg_list_size; i++) { - assert(target->reg_cache->reg_list[i].size > 0); + assert(!target->reg_cache->reg_list[i].valid || + target->reg_cache->reg_list[i].size > 0); (*reg_list)[i] = &target->reg_cache->reg_list[i]; } @@ -841,7 +834,7 @@ static int riscv_run_algorithm(struct target *target, int num_mem_params, return ERROR_FAIL; } - if (r->number > REG_XPR31) { + if (r->number > GDB_REGNO_XPR31) { LOG_ERROR("Only GPRs can be use as argument registers."); return ERROR_FAIL; } @@ -1180,6 +1173,88 @@ COMMAND_HANDLER(riscv_set_scratch_ram) return ERROR_OK; } +void parse_error(const char *string, char c, unsigned position) +{ + char buf[position+2]; + for (unsigned i = 0; i < position; i++) + buf[i] = ' '; + buf[position] = '^'; + buf[position + 1] = 0; + + LOG_ERROR("Parse error at character %c in:", c); + LOG_ERROR("%s", string); + LOG_ERROR("%s", buf); +} + +COMMAND_HANDLER(riscv_set_expose_csrs) +{ + if (CMD_ARGC != 1) { + LOG_ERROR("Command takes exactly 1 parameter"); + return ERROR_COMMAND_SYNTAX_ERROR; + } + + for (unsigned pass = 0; pass < 2; pass++) { + unsigned range = 0; + unsigned low = 0; + bool parse_low = true; + unsigned high = 0; + for (unsigned i = 0; i == 0 || CMD_ARGV[0][i-1]; i++) { + char c = CMD_ARGV[0][i]; + if isspace(c) { + // Ignore whitespace. + continue; + } + + if (parse_low) { + if (isdigit(c)) { + low *= 10; + low += c - '0'; + } else if (c == '-') { + parse_low = false; + } else if (c == ',' || c == 0) { + if (pass == 1) { + expose_csr[range].low = low; + expose_csr[range].high = low; + } + low = 0; + range++; + } else { + parse_error(CMD_ARGV[0], c, i); + return ERROR_COMMAND_SYNTAX_ERROR; + } + + } else { + if (isdigit(c)) { + high *= 10; + high += c - '0'; + } else if (c == ',' || c == 0) { + parse_low = true; + if (pass == 1) { + expose_csr[range].low = low; + expose_csr[range].high = high; + } + low = 0; + high = 0; + range++; + } else { + parse_error(CMD_ARGV[0], c, i); + return ERROR_COMMAND_SYNTAX_ERROR; + } + } + } + + if (pass == 0) { + if (expose_csr) + free(expose_csr); + expose_csr = calloc(range + 2, sizeof(*expose_csr)); + } else { + expose_csr[range].low = 1; + expose_csr[range].high = 0; + } + } + return ERROR_OK; +} + static const struct command_registration riscv_exec_command_handlers[] = { { .name = "set_command_timeout_sec", @@ -1202,6 +1277,15 @@ static const struct command_registration riscv_exec_command_handlers[] = { .usage = "riscv set_scratch_ram none|[address]", .help = "Set address of 16 bytes of scratch RAM the debugger can use, or 'none'." }, + { + .name = "expose_csrs", + .handler = riscv_set_expose_csrs, + .mode = COMMAND_ANY, + .usage = "riscv expose_csrs n0[-m0][,n0[-m0]]...", + .help = "Configure a list of inclusive ranges for CSRs to expose in " + "addition to the standard ones. This must be executed before " + "`init`." + }, COMMAND_REGISTRATION_DONE }; @@ -1352,6 +1436,20 @@ int riscv_step_rtos_hart(struct target *target) return ERROR_OK; } +bool riscv_supports_extension(struct target *target, char letter) +{ + RISCV_INFO(r); + unsigned num; + if (letter >= 'a' && letter <= 'z') { + num = letter - 'a'; + } else if (letter >= 'A' && letter <= 'Z') { + num = letter - 'A'; + } else { + return false; + } + return r->misa & (1 << num); +} + int riscv_xlen(const struct target *target) { return riscv_xlen_of_hart(target, riscv_current_hartid(target)); @@ -1389,7 +1487,7 @@ void riscv_set_current_hartid(struct target *target, int hartid) /* Avoid invalidating the register cache all the time. */ if (r->registers_initialized && (!riscv_rtos_enabled(target) || (previous_hartid == hartid)) - && target->reg_cache->reg_list[GDB_REGNO_XPR0].size == (unsigned)riscv_xlen(target) + && target->reg_cache->reg_list[GDB_REGNO_ZERO].size == (unsigned)riscv_xlen(target) && (!riscv_rtos_enabled(target) || (r->rtos_hartid != -1))) { return; } else @@ -1402,22 +1500,10 @@ void riscv_invalidate_register_cache(struct target *target) { RISCV_INFO(r); - /* Update the register list's widths. */ register_cache_invalidate(target->reg_cache); for (size_t i = 0; i < GDB_REGNO_COUNT; ++i) { struct reg *reg = &target->reg_cache->reg_list[i]; - - reg->value = &r->reg_cache_values[i]; reg->valid = false; - - switch (i) { - case GDB_REGNO_PRIV: - reg->size = 8; - break; - default: - reg->size = riscv_xlen(target); - break; - } } r->registers_initialized = true; @@ -1457,6 +1543,7 @@ bool riscv_has_register(struct target *target, int hartid, int regid) void riscv_set_register(struct target *target, enum gdb_regno r, riscv_reg_t v) { + // TODO: propagate errors return riscv_set_register_on_hart(target, riscv_current_hartid(target), r, v); } @@ -1676,7 +1763,7 @@ const char *gdb_regno_name(enum gdb_regno regno) return "priv"; default: if (regno <= GDB_REGNO_XPR31) { - sprintf(buf, "x%d", regno - GDB_REGNO_XPR0); + sprintf(buf, "x%d", regno - GDB_REGNO_ZERO); } else if (regno >= GDB_REGNO_CSR0 && regno <= GDB_REGNO_CSR4095) { sprintf(buf, "csr%d", regno - GDB_REGNO_CSR0); } else if (regno >= GDB_REGNO_FPR0 && regno <= GDB_REGNO_FPR31) { @@ -1687,3 +1774,269 @@ const char *gdb_regno_name(enum gdb_regno regno) return buf; } } + +static int register_get(struct reg *reg) +{ + struct target *target = (struct target *) reg->arch_info; + uint64_t value = riscv_get_register(target, reg->number); + buf_set_u64(reg->value, 0, reg->size, value); + return ERROR_OK; +} + +static int register_set(struct reg *reg, uint8_t *buf) +{ + struct target *target = (struct target *) reg->arch_info; + + uint64_t value = buf_get_u64(buf, 0, reg->size); + + LOG_DEBUG("write 0x%" PRIx64 " to %s", value, reg->name); + struct reg *r = &target->reg_cache->reg_list[reg->number]; + r->valid = true; + memcpy(r->value, buf, (r->size + 7) / 8); + + riscv_set_register(target, reg->number, value); + return ERROR_OK; +} + +static struct reg_arch_type riscv_reg_arch_type = { + .get = register_get, + .set = register_set +}; + +struct csr_info { + unsigned number; + const char *name; +}; + +static int cmp_csr_info(const void *p1, const void *p2) +{ + return (int) (((struct csr_info *)p1)->number) - (int) (((struct csr_info *)p2)->number); +} + +int riscv_init_registers(struct target *target) +{ + RISCV_INFO(info); + + if (target->reg_cache) { + if (target->reg_cache->reg_list) + free(target->reg_cache->reg_list); + free(target->reg_cache); + } + + target->reg_cache = calloc(1, sizeof(*target->reg_cache)); + target->reg_cache->name = "RISC-V Registers"; + target->reg_cache->num_regs = GDB_REGNO_COUNT; + + target->reg_cache->reg_list = calloc(GDB_REGNO_COUNT, sizeof(struct reg)); + + const unsigned int max_reg_name_len = 12; + if (info->reg_names) + free(info->reg_names); + info->reg_names = calloc(1, GDB_REGNO_COUNT * max_reg_name_len); + char *reg_name = info->reg_names; + + static struct reg_feature feature_cpu = { + .name = "org.gnu.gdb.riscv.cpu" + }; + static struct reg_feature feature_fpu = { + .name = "org.gnu.gdb.riscv.fpu" + }; + static struct reg_feature feature_csr = { + .name = "org.gnu.gdb.riscv.csr" + }; + static struct reg_feature feature_virtual = { + .name = "org.gnu.gdb.riscv.virtual" + }; + + static struct reg_data_type type_ieee_single = { + .type = REG_TYPE_IEEE_SINGLE, + .id = "ieee_single" + }; + static struct reg_data_type type_ieee_double = { + .type = REG_TYPE_IEEE_DOUBLE, + .id = "ieee_double" + }; + struct csr_info csr_info[] = { +#define DECLARE_CSR(name, number) { number, #name }, +#include "encoding.h" +#undef DECLARE_CSR + }; + // encoding.h does not contain the registers in sorted order. + qsort(csr_info, DIM(csr_info), sizeof(*csr_info), cmp_csr_info); + unsigned csr_info_index = 0; + + // When gdb request register N, gdb_get_register_packet() assumes that this + // is register at index N in reg_list. So if there are certain registers + // that don't exist, we need to leave holes in the list (or renumber, but + // it would be nice not to have yet another set of numbers to translate + // between). + for (uint32_t number = 0; number < GDB_REGNO_COUNT; number++) { + struct reg *r = &target->reg_cache->reg_list[number]; + r->caller_save = true; + r->dirty = false; + r->valid = false; + r->exist = true; + r->type = &riscv_reg_arch_type; + r->arch_info = target; + r->number = number; + r->size = riscv_xlen(target); + // r->size is set in riscv_invalidate_register_cache, maybe because the + // target is in theory allowed to change XLEN on us. But I expect a lot + // of other things to break in that case as well. + if (number <= GDB_REGNO_XPR31) { + switch (number) { + case GDB_REGNO_ZERO: r->name = "zero"; break; + case GDB_REGNO_RA: r->name = "ra"; break; + case GDB_REGNO_SP: r->name = "sp"; break; + case GDB_REGNO_GP: r->name = "gp"; break; + case GDB_REGNO_TP: r->name = "tp"; break; + case GDB_REGNO_T0: r->name = "t0"; break; + case GDB_REGNO_T1: r->name = "t1"; break; + case GDB_REGNO_T2: r->name = "t2"; break; + case GDB_REGNO_FP: r->name = "fp"; break; + case GDB_REGNO_S1: r->name = "s1"; break; + case GDB_REGNO_A0: r->name = "a0"; break; + case GDB_REGNO_A1: r->name = "a1"; break; + case GDB_REGNO_A2: r->name = "a2"; break; + case GDB_REGNO_A3: r->name = "a3"; break; + case GDB_REGNO_A4: r->name = "a4"; break; + case GDB_REGNO_A5: r->name = "a5"; break; + case GDB_REGNO_A6: r->name = "a6"; break; + case GDB_REGNO_A7: r->name = "a7"; break; + case GDB_REGNO_S2: r->name = "s2"; break; + case GDB_REGNO_S3: r->name = "s3"; break; + case GDB_REGNO_S4: r->name = "s4"; break; + case GDB_REGNO_S5: r->name = "s5"; break; + case GDB_REGNO_S6: r->name = "s6"; break; + case GDB_REGNO_S7: r->name = "s7"; break; + case GDB_REGNO_S8: r->name = "s8"; break; + case GDB_REGNO_S9: r->name = "s9"; break; + case GDB_REGNO_S10: r->name = "s10"; break; + case GDB_REGNO_S11: r->name = "s11"; break; + case GDB_REGNO_T3: r->name = "t3"; break; + case GDB_REGNO_T4: r->name = "t4"; break; + case GDB_REGNO_T5: r->name = "t5"; break; + case GDB_REGNO_T6: r->name = "t6"; break; + } + r->group = "general"; + r->feature = &feature_cpu; + } else if (number == GDB_REGNO_PC) { + sprintf(reg_name, "pc"); + r->group = "general"; + r->feature = &feature_cpu; + } else if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) { + if (riscv_supports_extension(target, 'D')) { + r->reg_data_type = &type_ieee_double; + r->size = 64; + } else if (riscv_supports_extension(target, 'F')) { + r->reg_data_type = &type_ieee_single; + r->size = 32; + } else { + r->exist = false; + } + switch (number) { + case GDB_REGNO_FT0: r->name = "ft0"; break; + case GDB_REGNO_FT1: r->name = "ft1"; break; + case GDB_REGNO_FT2: r->name = "ft2"; break; + case GDB_REGNO_FT3: r->name = "ft3"; break; + case GDB_REGNO_FT4: r->name = "ft4"; break; + case GDB_REGNO_FT5: r->name = "ft5"; break; + case GDB_REGNO_FT6: r->name = "ft6"; break; + case GDB_REGNO_FT7: r->name = "ft7"; break; + case GDB_REGNO_FS0: r->name = "fs0"; break; + case GDB_REGNO_FS1: r->name = "fs1"; break; + case GDB_REGNO_FA0: r->name = "fa0"; break; + case GDB_REGNO_FA1: r->name = "fa1"; break; + case GDB_REGNO_FA2: r->name = "fa2"; break; + case GDB_REGNO_FA3: r->name = "fa3"; break; + case GDB_REGNO_FA4: r->name = "fa4"; break; + case GDB_REGNO_FA5: r->name = "fa5"; break; + case GDB_REGNO_FA6: r->name = "fa6"; break; + case GDB_REGNO_FA7: r->name = "fa7"; break; + case GDB_REGNO_FS2: r->name = "fs2"; break; + case GDB_REGNO_FS3: r->name = "fs3"; break; + case GDB_REGNO_FS4: r->name = "fs4"; break; + case GDB_REGNO_FS5: r->name = "fs5"; break; + case GDB_REGNO_FS6: r->name = "fs6"; break; + case GDB_REGNO_FS7: r->name = "fs7"; break; + case GDB_REGNO_FS8: r->name = "fs8"; break; + case GDB_REGNO_FS9: r->name = "fs9"; break; + case GDB_REGNO_FS10: r->name = "fs10"; break; + case GDB_REGNO_FS11: r->name = "fs11"; break; + case GDB_REGNO_FT8: r->name = "ft8"; break; + case GDB_REGNO_FT9: r->name = "ft9"; break; + case GDB_REGNO_FT10: r->name = "ft10"; break; + case GDB_REGNO_FT11: r->name = "ft11"; break; + } + r->group = "float"; + r->feature = &feature_fpu; + } else if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095) { + r->group = "csr"; + r->feature = &feature_csr; + unsigned csr_number = number - GDB_REGNO_CSR0; + + while (csr_info[csr_info_index].number < csr_number && + csr_info_index < DIM(csr_info) - 1) { + csr_info_index++; + } + if (csr_info[csr_info_index].number == csr_number) { + r->name = csr_info[csr_info_index].name; + } else { + sprintf(reg_name, "csr%d", csr_number); + // Assume unnamed registers don't exist, unless we have some + // configuration that tells us otherwise. That's important + // because eg. Eclipse crashes if a target has too many + // registers, and apparently has no way of only showing a + // subset of registers in any case. + r->exist = false; + } + + switch (csr_number) { + case CSR_FFLAGS: + case CSR_FRM: + case CSR_FCSR: + r->exist = riscv_supports_extension(target, 'F'); + r->group = "float"; + r->feature = &feature_fpu; + break; + case CSR_SSTATUS: + case CSR_STVEC: + case CSR_SIP: + case CSR_SIE: + case CSR_SCOUNTEREN: + case CSR_SSCRATCH: + case CSR_SEPC: + case CSR_SCAUSE: + case CSR_STVAL: + case CSR_SATP: + r->exist = riscv_supports_extension(target, 'S'); + break; + } + + if (!r->exist && expose_csr) { + for (unsigned i = 0; expose_csr[i].low <= expose_csr[i].high; i++) { + if (csr_number >= expose_csr[i].low && csr_number <= expose_csr[i].high) { + LOG_INFO("Exposing additional CSR %d", csr_number); + r->exist = true; + break; + } + } + } + + } else if (number == GDB_REGNO_PRIV) { + sprintf(reg_name, "priv"); + r->group = "general"; + r->feature = &feature_virtual; + r->size = 8; + } + if (reg_name[0]) { + r->name = reg_name; + } + reg_name += strlen(reg_name) + 1; + assert(reg_name < info->reg_names + GDB_REGNO_COUNT * max_reg_name_len); + r->value = &info->reg_cache_values[number]; + } + + return ERROR_OK; +} + diff --git a/src/target/riscv/riscv.h b/src/target/riscv/riscv.h index 4ff6127..80f6ba2 100644 --- a/src/target/riscv/riscv.h +++ b/src/target/riscv/riscv.h @@ -59,7 +59,11 @@ typedef struct { /* The register cache points into here. */ uint64_t reg_cache_values[RISCV_MAX_REGISTERS]; - + + /* Single buffer that contains all register names, instead of calling + * malloc for each register. Needs to be freed when reg_list is freed. */ + char *reg_names; + /* It's possible that each core has a different supported ISA set. */ int xlen[RISCV_MAX_HARTS]; @@ -168,6 +172,8 @@ int riscv_resume_one_hart(struct target *target, int hartid); * then the only hart. */ int riscv_step_rtos_hart(struct target *target); +bool riscv_supports_extension(struct target *target, char letter); + /* Returns XLEN for the given (or current) hart. */ int riscv_xlen(const struct target *target); int riscv_xlen_of_hart(const struct target *target, int hartid); @@ -241,4 +247,6 @@ int riscv_add_watchpoint(struct target *target, struct watchpoint *watchpoint); int riscv_remove_watchpoint(struct target *target, struct watchpoint *watchpoint); +int riscv_init_registers(struct target *target); + #endif diff --git a/src/target/target.c b/src/target/target.c index adedd47..3278444 100644 --- a/src/target/target.c +++ b/src/target/target.c @@ -2742,21 +2742,23 @@ COMMAND_HANDLER(handle_reg_command) i < cache->num_regs; i++, reg++, count++) { /* only print cached values if they are valid */ - if (reg->valid) { - value = buf_to_str(reg->value, - reg->size, 16); - command_print(CMD_CTX, - "(%i) %s (/%" PRIu32 "): 0x%s%s", - count, reg->name, - reg->size, value, - reg->dirty + if (reg->exist) { + if (reg->valid) { + value = buf_to_str(reg->value, + reg->size, 16); + command_print(CMD_CTX, + "(%i) %s (/%" PRIu32 "): 0x%s%s", + count, reg->name, + reg->size, value, + reg->dirty ? " (dirty)" : ""); - free(value); - } else { - command_print(CMD_CTX, "(%i) %s (/%" PRIu32 ")", - count, reg->name, - reg->size) ; + free(value); + } else { + command_print(CMD_CTX, "(%i) %s (/%" PRIu32 ")", + count, reg->name, + reg->size) ; + } } } cache = cache->next; |