From f341db9f72f8e35df0b92190558fd3c2f4c85446 Mon Sep 17 00:00:00 2001 From: Tim Newsome Date: Fri, 1 Dec 2017 12:42:16 -0800 Subject: [PATCH] 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