From c421fefdcb4c16648e22574afdbc0677675844f8 Mon Sep 17 00:00:00 2001 From: Tim Newsome Date: Fri, 10 Nov 2017 15:30:52 -0800 Subject: [PATCH 01/17] Checkpoint that seems to work. Change-Id: I9599aacc256f6340795097732b6f8e8869c2099f --- src/target/riscv/riscv-013.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/target/riscv/riscv-013.c b/src/target/riscv/riscv-013.c index 14a7d2671..338c2432c 100644 --- a/src/target/riscv/riscv-013.c +++ b/src/target/riscv/riscv-013.c @@ -1179,6 +1179,24 @@ static int init_target(struct command_context *cmd_ctx, char *reg_name = info->reg_names; info->reg_values = NULL; + 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" + }; + for (unsigned int i = 0; i < GDB_REGNO_COUNT; i++) { struct reg *r = &target->reg_cache->reg_list[i]; r->number = i; @@ -1190,14 +1208,26 @@ static int init_target(struct command_context *cmd_ctx, r->arch_info = target; if (i <= GDB_REGNO_XPR31) { sprintf(reg_name, "x%d", i); + r->group = "general"; + r->feature = &feature_cpu; } else if (i == GDB_REGNO_PC) { sprintf(reg_name, "pc"); + r->group = "general"; + r->feature = &feature_cpu; } else if (i >= GDB_REGNO_FPR0 && i <= GDB_REGNO_FPR31) { sprintf(reg_name, "f%d", i - GDB_REGNO_FPR0); + r->group = "float"; + r->feature = &feature_fpu; + // TODO: check D or F extension + r->reg_data_type = &type_ieee_single; } else if (i >= GDB_REGNO_CSR0 && i <= GDB_REGNO_CSR4095) { sprintf(reg_name, "csr%d", i - GDB_REGNO_CSR0); + r->group = "csr"; + r->feature = &feature_csr; } else if (i == GDB_REGNO_PRIV) { sprintf(reg_name, "priv"); + r->group = "general"; + r->feature = &feature_virtual; } if (reg_name[0]) { r->name = reg_name; From e648856a412b8d0aca6efe100e83c44cd8a60bef Mon Sep 17 00:00:00 2001 From: Tim Newsome Date: Mon, 13 Nov 2017 13:14:07 -0800 Subject: [PATCH 02/17] `make all` debug tests now pass. Also properly support (I think) D extension on RV32. Change-Id: I2f0162d36e4c18c251f99b6943403cef30d17d29 --- src/target/riscv/riscv-013.c | 202 +++++++++++++++++++---------------- src/target/riscv/riscv.c | 27 ++++- src/target/riscv/riscv.h | 2 + 3 files changed, 132 insertions(+), 99 deletions(-) diff --git a/src/target/riscv/riscv-013.c b/src/target/riscv/riscv-013.c index 338c2432c..ba55379a3 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, @@ -302,20 +296,6 @@ 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}; @@ -969,7 +949,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 +971,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 +1018,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 +1031,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)); @@ -1094,7 +1074,7 @@ 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); + buf_set_u64(reg->value, 0, reg->size, value); return ERROR_OK; } @@ -1109,7 +1089,7 @@ 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)); + 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]; @@ -1124,6 +1104,99 @@ static struct reg_arch_type riscv_reg_arch_type = { .set = register_set }; +static int init_registers(struct target *target) +{ + riscv013_info_t *info = get_info(target); + + 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; + info->reg_values = NULL; + + 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" + }; + + 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; + // 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 (i <= GDB_REGNO_XPR31) { + sprintf(reg_name, "x%d", i); + r->group = "general"; + r->feature = &feature_cpu; + } else if (i == GDB_REGNO_PC) { + sprintf(reg_name, "pc"); + r->group = "general"; + r->feature = &feature_cpu; + } else if (i >= GDB_REGNO_FPR0 && i <= GDB_REGNO_FPR31) { + sprintf(reg_name, "f%d", i - GDB_REGNO_FPR0); + r->group = "float"; + r->feature = &feature_fpu; + if (riscv_supports_extension(target, 'D')) { + r->reg_data_type = &type_ieee_double; + } else if (riscv_supports_extension(target, 'F')) { + r->reg_data_type = &type_ieee_single; + } + } else if (i >= GDB_REGNO_CSR0 && i <= GDB_REGNO_CSR4095) { + sprintf(reg_name, "csr%d", i - GDB_REGNO_CSR0); + r->group = "csr"; + r->feature = &feature_csr; + } else if (i == GDB_REGNO_PRIV) { + sprintf(reg_name, "priv"); + r->group = "general"; + r->feature = &feature_virtual; + } + 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; +} + static int init_target(struct command_context *cmd_ctx, struct target *target) { @@ -1168,74 +1241,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; - - 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" - }; - - 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); - r->group = "general"; - r->feature = &feature_cpu; - } else if (i == GDB_REGNO_PC) { - sprintf(reg_name, "pc"); - r->group = "general"; - r->feature = &feature_cpu; - } else if (i >= GDB_REGNO_FPR0 && i <= GDB_REGNO_FPR31) { - sprintf(reg_name, "f%d", i - GDB_REGNO_FPR0); - r->group = "float"; - r->feature = &feature_fpu; - // TODO: check D or F extension - r->reg_data_type = &type_ieee_single; - } else if (i >= GDB_REGNO_CSR0 && i <= GDB_REGNO_CSR4095) { - sprintf(reg_name, "csr%d", i - GDB_REGNO_CSR0); - r->group = "csr"; - r->feature = &feature_csr; - } else if (i == GDB_REGNO_PRIV) { - sprintf(reg_name, "priv"); - r->group = "general"; - r->feature = &feature_virtual; - } - 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; } @@ -1345,6 +1350,10 @@ static int examine(struct target *target) LOG_DEBUG("Enumerated %d harts", r->hart_count); + // Get a functional register cache going. + if (init_registers(target) != ERROR_OK) + return ERROR_FAIL; + /* Halt every hart so we can probe them. */ riscv_halt_all_harts(target); @@ -1381,6 +1390,11 @@ static int examine(struct target *target) /* Resumes all the harts, so the debugger can later pause them. */ riscv_resume_all_harts(target); target->state = TARGET_RUNNING; + + // Now reinit registers based on what we discovered. + if (init_registers(target) != ERROR_OK) + return ERROR_FAIL; + target_set_examined(target); if (target->rtos) { diff --git a/src/target/riscv/riscv.c b/src/target/riscv/riscv.c index b9be133f3..9f00a52c9 100644 --- a/src/target/riscv/riscv.c +++ b/src/target/riscv/riscv.c @@ -1352,6 +1352,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)); @@ -1410,13 +1424,16 @@ void riscv_invalidate_register_cache(struct target *target) reg->value = &r->reg_cache_values[i]; reg->valid = false; - switch (i) { - case GDB_REGNO_PRIV: + if (i == GDB_REGNO_PRIV) { reg->size = 8; - break; - default: + } else if (i >= GDB_REGNO_FPR0 && i <= GDB_REGNO_FPR31) { + if (riscv_supports_extension(target, 'D')) { + reg->size = 64; + } else if (riscv_supports_extension(target, 'F')) { + reg->size = 32; + } + } else { reg->size = riscv_xlen(target); - break; } } diff --git a/src/target/riscv/riscv.h b/src/target/riscv/riscv.h index 4ff61270f..8cab47fad 100644 --- a/src/target/riscv/riscv.h +++ b/src/target/riscv/riscv.h @@ -168,6 +168,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); From a5cb0b22704c25fa2cbadd2b3b2024a662362c87 Mon Sep 17 00:00:00 2001 From: Tim Newsome Date: Fri, 17 Nov 2017 15:34:18 -0800 Subject: [PATCH 03/17] WIP. Hide FPRs if the hart doesn't support F/D. Change-Id: I988c0c36f2de8157d76874a697b3c054773b787d --- src/target/riscv/riscv-013.c | 33 ++++++++++++++++++++------------- src/target/riscv/riscv.c | 21 +++++++++++---------- 2 files changed, 31 insertions(+), 23 deletions(-) diff --git a/src/target/riscv/riscv-013.c b/src/target/riscv/riscv-013.c index ba55379a3..9e57a2245 100644 --- a/src/target/riscv/riscv-013.c +++ b/src/target/riscv/riscv-013.c @@ -1149,40 +1149,47 @@ static int init_registers(struct target *target) .id = "ieee_double" }; - for (unsigned int i = 0; i < GDB_REGNO_COUNT; i++) { - struct reg *r = &target->reg_cache->reg_list[i]; - r->number = i; + // 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 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 (i <= GDB_REGNO_XPR31) { - sprintf(reg_name, "x%d", i); + if (number <= GDB_REGNO_XPR31) { + sprintf(reg_name, "x%d", number); r->group = "general"; r->feature = &feature_cpu; - } else if (i == GDB_REGNO_PC) { + } else if (number == GDB_REGNO_PC) { sprintf(reg_name, "pc"); r->group = "general"; r->feature = &feature_cpu; - } else if (i >= GDB_REGNO_FPR0 && i <= GDB_REGNO_FPR31) { - sprintf(reg_name, "f%d", i - GDB_REGNO_FPR0); - r->group = "float"; - r->feature = &feature_fpu; + } else if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) { if (riscv_supports_extension(target, 'D')) { r->reg_data_type = &type_ieee_double; } else if (riscv_supports_extension(target, 'F')) { r->reg_data_type = &type_ieee_single; + } else { + r->exist = false; } - } else if (i >= GDB_REGNO_CSR0 && i <= GDB_REGNO_CSR4095) { - sprintf(reg_name, "csr%d", i - GDB_REGNO_CSR0); + sprintf(reg_name, "f%d", number - GDB_REGNO_FPR0); + r->group = "float"; + r->feature = &feature_fpu; + } else if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095) { + sprintf(reg_name, "csr%d", number - GDB_REGNO_CSR0); r->group = "csr"; r->feature = &feature_csr; - } else if (i == GDB_REGNO_PRIV) { + } else if (number == GDB_REGNO_PRIV) { sprintf(reg_name, "priv"); r->group = "general"; r->feature = &feature_virtual; diff --git a/src/target/riscv/riscv.c b/src/target/riscv/riscv.c index 9f00a52c9..431e06f79 100644 --- a/src/target/riscv/riscv.c +++ b/src/target/riscv/riscv.c @@ -760,6 +760,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 +775,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 +786,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]; } @@ -1424,9 +1425,9 @@ void riscv_invalidate_register_cache(struct target *target) reg->value = &r->reg_cache_values[i]; reg->valid = false; - if (i == GDB_REGNO_PRIV) { + if (reg->number == GDB_REGNO_PRIV) { reg->size = 8; - } else if (i >= GDB_REGNO_FPR0 && i <= GDB_REGNO_FPR31) { + } else if (reg->number >= GDB_REGNO_FPR0 && reg->number <= GDB_REGNO_FPR31) { if (riscv_supports_extension(target, 'D')) { reg->size = 64; } else if (riscv_supports_extension(target, 'F')) { From 7c989698a14b2626702ffb8812e78c7fd274af75 Mon Sep 17 00:00:00 2001 From: Tim Newsome Date: Mon, 27 Nov 2017 13:09:42 -0800 Subject: [PATCH 04/17] WIP better CSR names, and include only existing Change-Id: I1a234ee07c417ba56da10a61fc2bdbdcc60490a8 --- src/target/riscv/riscv-013.c | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/src/target/riscv/riscv-013.c b/src/target/riscv/riscv-013.c index 9e57a2245..a63d168c9 100644 --- a/src/target/riscv/riscv-013.c +++ b/src/target/riscv/riscv-013.c @@ -1186,9 +1186,40 @@ static int init_registers(struct target *target) r->group = "float"; r->feature = &feature_fpu; } else if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095) { - sprintf(reg_name, "csr%d", number - GDB_REGNO_CSR0); r->group = "csr"; r->feature = &feature_csr; + r->exist = true; + switch (number) { + case CSR_FFLAGS: + strcpy(reg_name, "fflags"); + r->exist = riscv_supports_extension(target, 'F'); + r->group = "float"; + r->feature = &feature_fpu; + break; + case CSR_FRM: + strcpy(reg_name, "frm"); + r->exist = riscv_supports_extension(target, 'F'); + r->group = "float"; + r->feature = &feature_fpu; + break; + case CSR_FCSR: + strcpy(reg_name, "fcsr"); + r->exist = riscv_supports_extension(target, 'F'); + r->group = "float"; + r->feature = &feature_fpu; + break; + case CSR_CYCLE: + strcpy(reg_name, "cycle"); + break; + case CSR_TIME: + strcpy(reg_name, "time"); + break; + case CSR_INSTRET: + strcpy(reg_name, "instret"); + break; + default: + sprintf(reg_name, "csr%d", number - GDB_REGNO_CSR0); + } } else if (number == GDB_REGNO_PRIV) { sprintf(reg_name, "priv"); r->group = "general"; From 26a54452d2acb33e5343ee44b5982ad16e3af169 Mon Sep 17 00:00:00 2001 From: Tim Newsome Date: Mon, 27 Nov 2017 14:47:33 -0800 Subject: [PATCH 05/17] Fix register names. Use the ABI ones for every register that we have one for. Change-Id: I2a993abff416d2652dbe026b3fb498e144a5006f --- src/target/riscv/gdb_regs.h | 41 +++++++++++++++--- src/target/riscv/program.c | 8 ++-- src/target/riscv/riscv-011.c | 2 +- src/target/riscv/riscv-013.c | 82 ++++++++++++++++++++++++++++-------- src/target/riscv/riscv.c | 21 ++------- 5 files changed, 108 insertions(+), 46 deletions(-) diff --git a/src/target/riscv/gdb_regs.h b/src/target/riscv/gdb_regs.h index 2952e10ab..3f60d018d 100644 --- a/src/target/riscv/gdb_regs.h +++ b/src/target/riscv/gdb_regs.h @@ -4,12 +4,41 @@ // 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, diff --git a/src/target/riscv/program.c b/src/target/riscv/program.c index e7238ddce..0c0dbd8bf 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 6e576fef9..1382387d3 100644 --- a/src/target/riscv/riscv-011.c +++ b/src/target/riscv/riscv-011.c @@ -1344,7 +1344,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; diff --git a/src/target/riscv/riscv-013.c b/src/target/riscv/riscv-013.c index a63d168c9..4238273a6 100644 --- a/src/target/riscv/riscv-013.c +++ b/src/target/riscv/riscv-013.c @@ -647,7 +647,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); @@ -1104,6 +1104,16 @@ static struct reg_arch_type riscv_reg_arch_type = { .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); +} + static int init_registers(struct target *target) { riscv013_info_t *info = get_info(target); @@ -1148,6 +1158,14 @@ static int init_registers(struct target *target) .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 @@ -1167,7 +1185,40 @@ static int init_registers(struct target *target) // 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) { - sprintf(reg_name, "x%d", number); + 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) { @@ -1189,36 +1240,33 @@ static int init_registers(struct target *target) r->group = "csr"; r->feature = &feature_csr; r->exist = true; - switch (number) { + unsigned csr_number = number - GDB_REGNO_CSR0; + + while (csr_info[csr_info_index].number < csr_number) { + 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); + } + + switch (csr_number) { case CSR_FFLAGS: - strcpy(reg_name, "fflags"); r->exist = riscv_supports_extension(target, 'F'); r->group = "float"; r->feature = &feature_fpu; break; case CSR_FRM: - strcpy(reg_name, "frm"); r->exist = riscv_supports_extension(target, 'F'); r->group = "float"; r->feature = &feature_fpu; break; case CSR_FCSR: - strcpy(reg_name, "fcsr"); r->exist = riscv_supports_extension(target, 'F'); r->group = "float"; r->feature = &feature_fpu; break; - case CSR_CYCLE: - strcpy(reg_name, "cycle"); - break; - case CSR_TIME: - strcpy(reg_name, "time"); - break; - case CSR_INSTRET: - strcpy(reg_name, "instret"); - break; - default: - sprintf(reg_name, "csr%d", number - GDB_REGNO_CSR0); } } else if (number == GDB_REGNO_PRIV) { sprintf(reg_name, "priv"); diff --git a/src/target/riscv/riscv.c b/src/target/riscv/riscv.c index 431e06f79..a196fdce8 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 @@ -842,7 +827,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; } @@ -1404,7 +1389,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 @@ -1694,7 +1679,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) { From 8926e66d3a36304af8244dfa177bd8e45a09010d Mon Sep 17 00:00:00 2001 From: Tim Newsome Date: Wed, 29 Nov 2017 13:38:04 -0800 Subject: [PATCH 06/17] Hide unknown registers, which probably don't exist Change-Id: Iffa8fa5ff4b0a01abd30fa302b7087e2011337bf --- src/target/riscv/riscv-013.c | 6 ++++++ src/target/target.c | 28 +++++++++++++++------------- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/src/target/riscv/riscv-013.c b/src/target/riscv/riscv-013.c index 4238273a6..1c48e5566 100644 --- a/src/target/riscv/riscv-013.c +++ b/src/target/riscv/riscv-013.c @@ -1249,6 +1249,12 @@ static int init_registers(struct target *target) 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) { diff --git a/src/target/target.c b/src/target/target.c index adedd4709..327844479 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; From f341db9f72f8e35df0b92190558fd3c2f4c85446 Mon Sep 17 00:00:00 2001 From: Tim Newsome Date: Fri, 1 Dec 2017 12:42:16 -0800 Subject: [PATCH 07/17] WIP xml register for 0.11. On HiFive1, FPRs show up with no name, and misa is 0x1105 instead of 0x40001105. Change-Id: I4ee223c905ad7d860147014e7b6394668658c6ea --- src/target/riscv/riscv-011.c | 200 ++++++++-------------------- src/target/riscv/riscv-013.c | 245 ++--------------------------------- src/target/riscv/riscv.c | 235 ++++++++++++++++++++++++++++++--- src/target/riscv/riscv.h | 8 +- 4 files changed, 293 insertions(+), 395 deletions(-) diff --git a/src/target/riscv/riscv-011.c b/src/target/riscv/riscv-011.c index 1382387d3..6692a9de1 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) @@ -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) -{ - 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 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; + riscv011_info_t *info = get_info(target); + + maybe_write_tselect(target); + riscv_reg_t value = ~0; + + 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); + } + + 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++); + + 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; + } } + + 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 1c48e5566..f22ef714c 100644 --- a/src/target/riscv/riscv-013.c +++ b/src/target/riscv/riscv-013.c @@ -150,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; @@ -290,10 +284,6 @@ 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. ***/ static void select_dmi(struct target *target) @@ -1070,225 +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, reg->size, 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, 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); - - return register_write(target, reg->number, value); -} - -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); -} - -static int init_registers(struct target *target) -{ - riscv013_info_t *info = get_info(target); - - 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; - info->reg_values = NULL; - - 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 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; - } else if (riscv_supports_extension(target, 'F')) { - r->reg_data_type = &type_ieee_single; - } else { - r->exist = false; - } - sprintf(reg_name, "f%d", number - GDB_REGNO_FPR0); - r->group = "float"; - r->feature = &feature_fpu; - } else if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095) { - r->group = "csr"; - r->feature = &feature_csr; - r->exist = true; - unsigned csr_number = number - GDB_REGNO_CSR0; - - while (csr_info[csr_info_index].number < csr_number) { - 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: - r->exist = riscv_supports_extension(target, 'F'); - r->group = "float"; - r->feature = &feature_fpu; - break; - case CSR_FRM: - r->exist = riscv_supports_extension(target, 'F'); - r->group = "float"; - r->feature = &feature_fpu; - break; - case CSR_FCSR: - r->exist = riscv_supports_extension(target, 'F'); - r->group = "float"; - r->feature = &feature_fpu; - break; - } - } else if (number == GDB_REGNO_PRIV) { - sprintf(reg_name, "priv"); - r->group = "general"; - r->feature = &feature_virtual; - } - 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; -} - static int init_target(struct command_context *cmd_ctx, struct target *target) { @@ -1438,12 +1209,24 @@ static int examine(struct target *target) break; } } + // Reset hartid to one that exists. Set coreid to avoid assert in + // riscv_set_current_hartid(). (TODO) + target->coreid = 0; + riscv_set_current_hartid(target, 0); + target->coreid = original_coreid; LOG_DEBUG("Enumerated %d harts", r->hart_count); + // Assume 32-bit until we discover the real value in examine(). + for (int i = 0; i < riscv_count_harts(target); ++i) { + if (riscv_hart_enabled(target, i)) { + r->xlen[i] = 32; + } + LOG_DEBUG(">>> temporary XLEN for hart %d is %d", i, r->xlen[i]); + } // Get a functional register cache going. - if (init_registers(target) != ERROR_OK) + if (riscv_init_registers(target) != ERROR_OK) return ERROR_FAIL; /* Halt every hart so we can probe them. */ @@ -1484,7 +1267,7 @@ static int examine(struct target *target) target->state = TARGET_RUNNING; // Now reinit registers based on what we discovered. - if (init_registers(target) != ERROR_OK) + if (riscv_init_registers(target) != ERROR_OK) return ERROR_FAIL; target_set_examined(target); diff --git a/src/target/riscv/riscv.c b/src/target/riscv/riscv.c index a196fdce8..46571890b 100644 --- a/src/target/riscv/riscv.c +++ b/src/target/riscv/riscv.c @@ -415,7 +415,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) { @@ -1248,6 +1247,7 @@ void riscv_info_init(struct target *target, riscv_info_t *r) memset(r, 0, sizeof(*r)); r->dtm_version = 1; r->registers_initialized = false; + LOG_DEBUG(">>> current_hartid=%d", target->coreid); r->current_hartid = target->coreid; memset(r->trigger_unique_id, 0xff, sizeof(r->trigger_unique_id)); @@ -1360,6 +1360,7 @@ int riscv_xlen(const struct target *target) int riscv_xlen_of_hart(const struct target *target, int hartid) { RISCV_INFO(r); + LOG_DEBUG(">>> xlen[%d] = %d", hartid, r->xlen[hartid]); assert(r->xlen[hartid] != -1); return r->xlen[hartid]; } @@ -1406,21 +1407,7 @@ void riscv_invalidate_register_cache(struct target *target) 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; - - if (reg->number == GDB_REGNO_PRIV) { - reg->size = 8; - } else if (reg->number >= GDB_REGNO_FPR0 && reg->number <= GDB_REGNO_FPR31) { - if (riscv_supports_extension(target, 'D')) { - reg->size = 64; - } else if (riscv_supports_extension(target, 'F')) { - reg->size = 32; - } - } else { - reg->size = riscv_xlen(target); - } } r->registers_initialized = true; @@ -1460,6 +1447,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); } @@ -1690,3 +1678,220 @@ 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; + } + sprintf(reg_name, "f%d", number - GDB_REGNO_FPR0); + 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: + r->exist = riscv_supports_extension(target, 'F'); + r->group = "float"; + r->feature = &feature_fpu; + break; + case CSR_FRM: + r->exist = riscv_supports_extension(target, 'F'); + r->group = "float"; + r->feature = &feature_fpu; + break; + case CSR_FCSR: + r->exist = riscv_supports_extension(target, 'F'); + r->group = "float"; + r->feature = &feature_fpu; + break; + } + } else if (number == GDB_REGNO_PRIV) { + sprintf(reg_name, "priv"); + r->group = "general"; + r->feature = &feature_virtual; + } + 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 8cab47fad..80f6ba204 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]; @@ -243,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 From 37278cf2eca9b1496a5d7c0df88fd3519d7e3c25 Mon Sep 17 00:00:00 2001 From: Tim Newsome Date: Fri, 8 Dec 2017 13:51:40 -0800 Subject: [PATCH 08/17] Make priv register 8 bits. (It's really only 2 bits, but something wonky happens between gdb and OpenOCD if I make it that size.) Change-Id: I562a65cb0ebe5aa0edcc54c251d0fea0e26f9cb1 --- src/target/riscv/riscv.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/target/riscv/riscv.c b/src/target/riscv/riscv.c index 46571890b..f1152a34f 100644 --- a/src/target/riscv/riscv.c +++ b/src/target/riscv/riscv.c @@ -1883,6 +1883,7 @@ int riscv_init_registers(struct target *target) sprintf(reg_name, "priv"); r->group = "general"; r->feature = &feature_virtual; + r->size = 8; } if (reg_name[0]) { r->name = reg_name; From 120477b2a21d20302df86804f25fb8425be558e6 Mon Sep 17 00:00:00 2001 From: Tim Newsome Date: Fri, 8 Dec 2017 15:11:23 -0800 Subject: [PATCH 09/17] Simplify examine() Now we don't have to play tricks fooling other parts of our code that might assert. Change-Id: Ia574378e1f95ed62d297e6b2e852245e58c9ffc9 --- src/target/riscv/riscv-013.c | 62 ++++++++++-------------------------- 1 file changed, 16 insertions(+), 46 deletions(-) diff --git a/src/target/riscv/riscv-013.c b/src/target/riscv/riscv-013.c index f22ef714c..a832494b8 100644 --- a/src/target/riscv/riscv-013.c +++ b/src/target/riscv/riscv-013.c @@ -1193,52 +1193,20 @@ static int examine(struct target *target) RISCV_INFO(r); r->impebreak = get_field(dmstatus, DMI_DMSTATUS_IMPEBREAK); - int original_coreid = target->coreid; 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); - - uint32_t s = dmi_read(target, DMI_DMSTATUS); - if (get_field(s, DMI_DMSTATUS_ANYNONEXISTENT)) { - r->hart_count--; - break; - } - } - // Reset hartid to one that exists. Set coreid to avoid assert in - // riscv_set_current_hartid(). (TODO) - target->coreid = 0; - riscv_set_current_hartid(target, 0); - - target->coreid = original_coreid; - - LOG_DEBUG("Enumerated %d harts", r->hart_count); - - // Assume 32-bit until we discover the real value in examine(). - for (int i = 0; i < riscv_count_harts(target); ++i) { - if (riscv_hart_enabled(target, i)) { - r->xlen[i] = 32; - } - LOG_DEBUG(">>> temporary XLEN for hart %d is %d", i, r->xlen[i]); - } - // Get a functional register cache going. - if (riscv_init_registers(target) != ERROR_OK) - return ERROR_FAIL; - - /* 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; riscv_set_current_hartid(target, i); + uint32_t s = dmi_read(target, DMI_DMSTATUS); + if (get_field(s, DMI_DMSTATUS_ANYNONEXISTENT)) { + break; + } + r->hart_count = i + 1; + + if (!riscv_is_halted(target)) { + riscv013_halt_current_hart(target); + } /* Without knowing anything else we can at least mess with the * program buffer. */ @@ -1251,25 +1219,27 @@ static int examine(struct target *target) r->xlen[i] = 32; } - r->misa = riscv_get_register_on_hart(target, i, GDB_REGNO_MISA); + // Now init registers based on what we discovered. + if (riscv_init_registers(target) != ERROR_OK) + return ERROR_FAIL; + r->misa = riscv_get_register_on_hart(target, i, GDB_REGNO_MISA); /* Display this as early as possible to help people who are using * really slow simulators. */ LOG_DEBUG(" hart %d: XLEN=%d, misa=0x%" PRIx64, i, r->xlen[i], 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; - // Now reinit registers based on what we discovered. - if (riscv_init_registers(target) != ERROR_OK) - return ERROR_FAIL; - target_set_examined(target); if (target->rtos) { From 46715c7d8adfda02d50fcd9d19ff8899ab1f69bf Mon Sep 17 00:00:00 2001 From: Tim Newsome Date: Mon, 11 Dec 2017 12:52:48 -0800 Subject: [PATCH 10/17] Remove no-longer-true comment. Change-Id: I888680e73682582438a0de0496238867f1604754 --- src/target/riscv/riscv.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/target/riscv/riscv.c b/src/target/riscv/riscv.c index f1152a34f..ef241afde 100644 --- a/src/target/riscv/riscv.c +++ b/src/target/riscv/riscv.c @@ -1403,7 +1403,6 @@ 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]; From ec1c814017af4a4dab865be99dcc31c63d45da36 Mon Sep 17 00:00:00 2001 From: Tim Newsome Date: Mon, 11 Dec 2017 14:21:41 -0800 Subject: [PATCH 11/17] Don't rely on hart count until it's correct. Change-Id: I4e05eb091823b2e0fb481ca0b599072ba1ca70f2 --- src/target/riscv/riscv-013.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/target/riscv/riscv-013.c b/src/target/riscv/riscv-013.c index a832494b8..1fff99016 100644 --- a/src/target/riscv/riscv-013.c +++ b/src/target/riscv/riscv-013.c @@ -1194,7 +1194,7 @@ static int examine(struct target *target) r->impebreak = get_field(dmstatus, DMI_DMSTATUS_IMPEBREAK); for (int i = 0; i < RISCV_MAX_HARTS; ++i) { - if (!riscv_hart_enabled(target, i)) + if (!riscv_rtos_enabled(target) && i != target->coreid) continue; riscv_set_current_hartid(target, i); From 10c17fdf17548d5ee32ae23023bb62e532bfef03 Mon Sep 17 00:00:00 2001 From: Tim Newsome Date: Mon, 11 Dec 2017 14:59:11 -0800 Subject: [PATCH 12/17] Read misa before using it to check for extensions. Change-Id: I7a172d83055d8bd833e3349a5b22b47dd5f31f5c --- src/target/riscv/riscv-013.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/target/riscv/riscv-013.c b/src/target/riscv/riscv-013.c index 1fff99016..6083ba8f5 100644 --- a/src/target/riscv/riscv-013.c +++ b/src/target/riscv/riscv-013.c @@ -1219,11 +1219,12 @@ static int examine(struct target *target) r->xlen[i] = 32; } + 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; - r->misa = riscv_get_register_on_hart(target, i, GDB_REGNO_MISA); /* Display this as early as possible to help people who are using * really slow simulators. */ LOG_DEBUG(" hart %d: XLEN=%d, misa=0x%" PRIx64, i, r->xlen[i], From 56ad0e5b300c9f2c68637922972e12e6ff2d06dd Mon Sep 17 00:00:00 2001 From: Tim Newsome Date: Mon, 11 Dec 2017 15:32:01 -0800 Subject: [PATCH 13/17] Avoid another assertion failure. Change-Id: Ia54f778152974164697b712c360918e17a127d95 --- src/target/riscv/riscv-013.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/target/riscv/riscv-013.c b/src/target/riscv/riscv-013.c index 6083ba8f5..98a63522f 100644 --- a/src/target/riscv/riscv-013.c +++ b/src/target/riscv/riscv-013.c @@ -1193,11 +1193,15 @@ static int examine(struct target *target) RISCV_INFO(r); r->impebreak = get_field(dmstatus, DMI_DMSTATUS_IMPEBREAK); + // 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) { if (!riscv_rtos_enabled(target) && i != target->coreid) continue; - riscv_set_current_hartid(target, i); + r->current_hartid = i; + riscv013_select_current_hart(target); + uint32_t s = dmi_read(target, DMI_DMSTATUS); if (get_field(s, DMI_DMSTATUS_ANYNONEXISTENT)) { break; From c7cddd2b5c4d78e72c617919f40dd2cc26ed1e91 Mon Sep 17 00:00:00 2001 From: Tim Newsome Date: Mon, 11 Dec 2017 15:32:15 -0800 Subject: [PATCH 14/17] Remove some debug printfs. Change-Id: I09989d4c0e102889ecb0eedbd3f4138f8b7bdb8c --- src/target/riscv/riscv.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/target/riscv/riscv.c b/src/target/riscv/riscv.c index ef241afde..85876214a 100644 --- a/src/target/riscv/riscv.c +++ b/src/target/riscv/riscv.c @@ -1247,7 +1247,6 @@ void riscv_info_init(struct target *target, riscv_info_t *r) memset(r, 0, sizeof(*r)); r->dtm_version = 1; r->registers_initialized = false; - LOG_DEBUG(">>> current_hartid=%d", target->coreid); r->current_hartid = target->coreid; memset(r->trigger_unique_id, 0xff, sizeof(r->trigger_unique_id)); @@ -1360,7 +1359,6 @@ int riscv_xlen(const struct target *target) int riscv_xlen_of_hart(const struct target *target, int hartid) { RISCV_INFO(r); - LOG_DEBUG(">>> xlen[%d] = %d", hartid, r->xlen[hartid]); assert(r->xlen[hartid] != -1); return r->xlen[hartid]; } From f55d1a20305d6acf801db3aff024a8b4eed2acad Mon Sep 17 00:00:00 2001 From: Tim Newsome Date: Thu, 14 Dec 2017 13:06:31 -0800 Subject: [PATCH 15/17] Give FPRs ABI names. Change-Id: If198d10e16671b9868836e23386aaf8d4b05f317 --- src/target/riscv/gdb_regs.h | 34 +++++++++++++++++++++++++++++++++- src/target/riscv/riscv.c | 35 ++++++++++++++++++++++++++++++++++- 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/src/target/riscv/gdb_regs.h b/src/target/riscv/gdb_regs.h index 3f60d018d..731f3e36c 100644 --- a/src/target/riscv/gdb_regs.h +++ b/src/target/riscv/gdb_regs.h @@ -41,7 +41,39 @@ enum gdb_regno { 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/riscv.c b/src/target/riscv/riscv.c index 85876214a..6d41620f9 100644 --- a/src/target/riscv/riscv.c +++ b/src/target/riscv/riscv.c @@ -1835,7 +1835,40 @@ int riscv_init_registers(struct target *target) } else { r->exist = false; } - sprintf(reg_name, "f%d", number - GDB_REGNO_FPR0); + 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) { From 5f86f7208d60ed92e70a04602462015751dff621 Mon Sep 17 00:00:00 2001 From: Tim Newsome Date: Thu, 14 Dec 2017 13:43:14 -0800 Subject: [PATCH 16/17] Hide supervisor registers if there is no S mode. Also update encoding.h. Change-Id: I275be7de0aa1af64d13ea191b9f4ff391cfb16dc --- src/target/riscv/encoding.h | 40 ++++++++++++++++++------------------- src/target/riscv/riscv.c | 20 +++++++++++-------- 2 files changed, 32 insertions(+), 28 deletions(-) diff --git a/src/target/riscv/encoding.h b/src/target/riscv/encoding.h index 8ec134596..c109ce189 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/riscv.c b/src/target/riscv/riscv.c index 6d41620f9..e7ead4ceb 100644 --- a/src/target/riscv/riscv.c +++ b/src/target/riscv/riscv.c @@ -1894,20 +1894,24 @@ int riscv_init_registers(struct target *target) switch (csr_number) { case CSR_FFLAGS: - r->exist = riscv_supports_extension(target, 'F'); - r->group = "float"; - r->feature = &feature_fpu; - break; case CSR_FRM: - r->exist = riscv_supports_extension(target, 'F'); - r->group = "float"; - r->feature = &feature_fpu; - break; 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; } } else if (number == GDB_REGNO_PRIV) { sprintf(reg_name, "priv"); From 11c261cd50d35e70ccf6fbdc76f23230d5b9b557 Mon Sep 17 00:00:00 2001 From: Tim Newsome Date: Fri, 15 Dec 2017 13:27:26 -0800 Subject: [PATCH 17/17] Add `riscv expose_csrs` command. This lets users tell OpenOCD which non-standard CSRs exist on their target, that will also be accessible and whose existence will be communicated to gdb. Change-Id: I56163a9fcb84ad7ebe815ae74fbd9fcc208f5a9d --- src/target/riscv/riscv.c | 110 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) diff --git a/src/target/riscv/riscv.c b/src/target/riscv/riscv.c index e7ead4ceb..2f1084f83 100644 --- a/src/target/riscv/riscv.c +++ b/src/target/riscv/riscv.c @@ -188,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; @@ -1165,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", @@ -1187,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 }; @@ -1913,6 +2012,17 @@ int riscv_init_registers(struct target *target) 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";