aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTim Newsome <tim@sifive.com>2017-12-01 12:42:16 -0800
committerTim Newsome <tim@sifive.com>2017-12-19 10:41:48 -0800
commitf341db9f72f8e35df0b92190558fd3c2f4c85446 (patch)
tree1c536df8623ba184c4ed7ab06692f9a2d613017c
parent8926e66d3a36304af8244dfa177bd8e45a09010d (diff)
downloadriscv-openocd-f341db9f72f8e35df0b92190558fd3c2f4c85446.zip
riscv-openocd-f341db9f72f8e35df0b92190558fd3c2f4c85446.tar.gz
riscv-openocd-f341db9f72f8e35df0b92190558fd3c2f4c85446.tar.bz2
WIP xml register for 0.11.
On HiFive1, FPRs show up with no name, and misa is 0x1105 instead of 0x40001105. Change-Id: I4ee223c905ad7d860147014e7b6394668658c6ea
-rw-r--r--src/target/riscv/riscv-011.c194
-rw-r--r--src/target/riscv/riscv-013.c245
-rw-r--r--src/target/riscv/riscv.c235
-rw-r--r--src/target/riscv/riscv.h8
4 files changed, 290 insertions, 392 deletions
diff --git a/src/target/riscv/riscv-011.c b/src/target/riscv/riscv-011.c
index 1382387..6692a9d 100644
--- a/src/target/riscv/riscv-011.c
+++ b/src/target/riscv/riscv-011.c
@@ -189,12 +189,6 @@ typedef struct {
struct memory_cache_line dram_cache[DRAM_CACHE_SIZE];
- /* Single buffer that contains all register names, instead of calling
- * malloc for each register. Needs to be freed when reg_list is freed. */
- char *reg_names;
- /* Single buffer that contains all register values. */
- void *reg_values;
-
// Number of run-test/idle cycles the target requests we do after each dbus
// access.
unsigned int dtmcontrol_idle;
@@ -223,7 +217,7 @@ typedef struct {
static int poll_target(struct target *target, bool announce);
static int riscv011_poll(struct target *target);
-static int register_get(struct reg *reg);
+static riscv_reg_t get_register(struct target *target, int hartid, int regid);
/*** Utility functions. ***/
@@ -1181,30 +1175,6 @@ static int resume(struct target *target, int debug_execution, bool step)
return execute_resume(target, step);
}
-/** Update register sizes based on xlen. */
-static void update_reg_list(struct target *target)
-{
- riscv011_info_t *info = get_info(target);
- if (info->reg_values) {
- free(info->reg_values);
- }
- info->reg_values = malloc(GDB_REGNO_COUNT * riscv_xlen(target) / 4);
-
- for (unsigned int i = 0; i < GDB_REGNO_COUNT; i++) {
- struct reg *r = &target->reg_cache->reg_list[i];
- r->value = info->reg_values + i * riscv_xlen(target) / 4;
- if (r->dirty) {
- LOG_ERROR("Register %d was dirty. Its value is lost.", i);
- }
- if (i == GDB_REGNO_PRIV) {
- r->size = 8;
- } else {
- r->size = riscv_xlen(target);
- }
- r->valid = false;
- }
-}
-
static uint64_t reg_cache_get(struct target *target, unsigned int number)
{
struct reg *r = &target->reg_cache->reg_list[number];
@@ -1236,7 +1206,8 @@ static int update_mstatus_actual(struct target *target)
// Force reading the register. In that process mstatus_actual will be
// updated.
- return register_get(&target->reg_cache->reg_list[GDB_REGNO_MSTATUS]);
+ get_register(target, 0, GDB_REGNO_MSTATUS);
+ return ERROR_OK;
}
/*** OpenOCD target functions. ***/
@@ -1275,58 +1246,6 @@ static int register_read(struct target *target, riscv_reg_t *value, int regnum)
return ERROR_OK;
}
-static int register_get(struct reg *reg)
-{
- struct target *target = (struct target *) reg->arch_info;
- riscv011_info_t *info = get_info(target);
-
- maybe_write_tselect(target);
- riscv_reg_t value = ~0;
-
- if (reg->number <= GDB_REGNO_XPR31) {
- value = reg_cache_get(target, reg->number);
- LOG_DEBUG("%s=0x%" PRIx64, reg->name, reg_cache_get(target, reg->number));
- } else if (reg->number == GDB_REGNO_PC) {
- value = info->dpc;
- LOG_DEBUG("%s=0x%" PRIx64 " (cached)", reg->name, info->dpc);
- } else if (reg->number >= GDB_REGNO_FPR0 && reg->number <= GDB_REGNO_FPR31) {
- int result = update_mstatus_actual(target);
- if (result != ERROR_OK) {
- return result;
- }
- unsigned i = 0;
- if ((info->mstatus_actual & MSTATUS_FS) == 0) {
- info->mstatus_actual = set_field(info->mstatus_actual, MSTATUS_FS, 1);
- cache_set_load(target, i++, S0, SLOT1);
- cache_set32(target, i++, csrw(S0, CSR_MSTATUS));
- cache_set(target, SLOT1, info->mstatus_actual);
- }
-
- if (riscv_xlen(target) == 32) {
- cache_set32(target, i++, fsw(reg->number - GDB_REGNO_FPR0, 0, DEBUG_RAM_START + 16));
- } else {
- cache_set32(target, i++, fsd(reg->number - GDB_REGNO_FPR0, 0, DEBUG_RAM_START + 16));
- }
- cache_set_jump(target, i++);
-
- if (cache_write(target, 4, true) != ERROR_OK) {
- return ERROR_FAIL;
- }
- } else if (reg->number == GDB_REGNO_PRIV) {
- value = get_field(info->dcsr, DCSR_PRV);
- } else {
- if (register_read(target, &value, reg->number) != ERROR_OK)
- return ERROR_FAIL;
- }
- buf_set_u64(reg->value, 0, riscv_xlen(target), value);
-
- if (reg->number == GDB_REGNO_MSTATUS) {
- reg->valid = true;
- }
-
- return ERROR_OK;
-}
-
// Write the register. No caching or games.
static int register_write(struct target *target, unsigned int number,
uint64_t value)
@@ -1399,33 +1318,53 @@ static int register_write(struct target *target, unsigned int number,
return ERROR_OK;
}
-static int register_set(struct reg *reg, uint8_t *buf)
+static riscv_reg_t get_register(struct target *target, int hartid, int regid)
{
- struct target *target = (struct target *) reg->arch_info;
+ assert(hartid == 0);
+ riscv011_info_t *info = get_info(target);
- uint64_t value = buf_get_u64(buf, 0, riscv_xlen(target));
+ maybe_write_tselect(target);
+ riscv_reg_t value = ~0;
- LOG_DEBUG("write 0x%" PRIx64 " to %s", value, reg->name);
- struct reg *r = &target->reg_cache->reg_list[reg->number];
- r->valid = true;
- memcpy(r->value, buf, (r->size + 7) / 8);
+ if (regid <= GDB_REGNO_XPR31) {
+ value = reg_cache_get(target, regid);
+ } else if (regid == GDB_REGNO_PC) {
+ value = info->dpc;
+ } else if (regid >= GDB_REGNO_FPR0 && regid <= GDB_REGNO_FPR31) {
+ int result = update_mstatus_actual(target);
+ if (result != ERROR_OK) {
+ return ~0;
+ }
+ unsigned i = 0;
+ if ((info->mstatus_actual & MSTATUS_FS) == 0) {
+ info->mstatus_actual = set_field(info->mstatus_actual, MSTATUS_FS, 1);
+ cache_set_load(target, i++, S0, SLOT1);
+ cache_set32(target, i++, csrw(S0, CSR_MSTATUS));
+ cache_set(target, SLOT1, info->mstatus_actual);
+ }
- return register_write(target, reg->number, value);
-}
+ if (riscv_xlen(target) == 32) {
+ cache_set32(target, i++, fsw(regid - GDB_REGNO_FPR0, 0, DEBUG_RAM_START + 16));
+ } else {
+ cache_set32(target, i++, fsd(regid - GDB_REGNO_FPR0, 0, DEBUG_RAM_START + 16));
+ }
+ cache_set_jump(target, i++);
-static struct reg_arch_type riscv_reg_arch_type = {
- .get = register_get,
- .set = register_set
-};
+ if (cache_write(target, 4, true) != ERROR_OK) {
+ return ~0;
+ }
+ } else if (regid == GDB_REGNO_PRIV) {
+ value = get_field(info->dcsr, DCSR_PRV);
+ } else {
+ if (register_read(target, &value, regid) != ERROR_OK) {
+ value = ~0;
+ }
+ }
-static riscv_reg_t get_register(struct target *target, int hartid, int regid)
-{
- assert(hartid == 0);
- riscv_reg_t value;
- if (register_read(target, &value, regid) != ERROR_OK) {
- // TODO: propagate errors
- value = ~0;
+ if (regid == GDB_REGNO_MSTATUS) {
+ target->reg_cache->reg_list[regid].valid = true;
}
+
return value;
}
@@ -1466,45 +1405,10 @@ static int init_target(struct command_context *cmd_ctx,
generic_info->version_specific = calloc(1, sizeof(riscv011_info_t));
if (!generic_info->version_specific)
return ERROR_FAIL;
- riscv011_info_t *info = get_info(target);
- target->reg_cache = calloc(1, sizeof(*target->reg_cache));
- target->reg_cache->name = "RISC-V registers";
- target->reg_cache->num_regs = GDB_REGNO_COUNT;
-
- target->reg_cache->reg_list = calloc(GDB_REGNO_COUNT, sizeof(struct reg));
-
- const unsigned int max_reg_name_len = 12;
- info->reg_names = calloc(1, GDB_REGNO_COUNT * max_reg_name_len);
- char *reg_name = info->reg_names;
- info->reg_values = NULL;
-
- for (unsigned int i = 0; i < GDB_REGNO_COUNT; i++) {
- struct reg *r = &target->reg_cache->reg_list[i];
- r->number = i;
- r->caller_save = true;
- r->dirty = false;
- r->valid = false;
- r->exist = true;
- r->type = &riscv_reg_arch_type;
- r->arch_info = target;
- if (i <= GDB_REGNO_XPR31) {
- sprintf(reg_name, "x%d", i);
- } else if (i == GDB_REGNO_PC) {
- sprintf(reg_name, "pc");
- } else if (i >= GDB_REGNO_FPR0 && i <= GDB_REGNO_FPR31) {
- sprintf(reg_name, "f%d", i - GDB_REGNO_FPR0);
- } else if (i >= GDB_REGNO_CSR0 && i <= GDB_REGNO_CSR4095) {
- sprintf(reg_name, "csr%d", i - GDB_REGNO_CSR0);
- } else if (i == GDB_REGNO_PRIV) {
- sprintf(reg_name, "priv");
- }
- if (reg_name[0]) {
- r->name = reg_name;
- }
- reg_name += strlen(reg_name) + 1;
- assert(reg_name < info->reg_names + GDB_REGNO_COUNT * max_reg_name_len);
- }
+ // Assume 32-bit until we discover the real value in examine().
+ generic_info->xlen[0] = 32;
+ riscv_init_registers(target);
return ERROR_OK;
}
@@ -1690,9 +1594,6 @@ static int examine(struct target *target)
}
LOG_DEBUG("Discovered XLEN is %d", riscv_xlen(target));
- // Update register list to match discovered XLEN.
- update_reg_list(target);
-
if (read_csr(target, &r->misa, CSR_MISA) != ERROR_OK) {
const unsigned old_csr_misa = 0xf10;
LOG_WARNING("Failed to read misa at 0x%x; trying 0x%x.", CSR_MISA,
@@ -1705,6 +1606,9 @@ static int examine(struct target *target)
}
}
+ // Update register list to match discovered XLEN/supported extensions.
+ riscv_init_registers(target);
+
info->never_halted = true;
int result = riscv011_poll(target);
diff --git a/src/target/riscv/riscv-013.c b/src/target/riscv/riscv-013.c
index 1c48e55..f22ef71 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 a196fdc..4657189 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 8cab47f..80f6ba2 100644
--- a/src/target/riscv/riscv.h
+++ b/src/target/riscv/riscv.h
@@ -59,7 +59,11 @@ typedef struct {
/* The register cache points into here. */
uint64_t reg_cache_values[RISCV_MAX_REGISTERS];
-
+
+ /* Single buffer that contains all register names, instead of calling
+ * malloc for each register. Needs to be freed when reg_list is freed. */
+ char *reg_names;
+
/* It's possible that each core has a different supported ISA set. */
int xlen[RISCV_MAX_HARTS];
@@ -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