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