Merge pull request #149 from riscv/xml_registers

Send gdb an XML target description that contains only a list of registers we think exist on this target
This commit is contained in:
Tim Newsome 2017-12-19 11:13:19 -08:00 committed by GitHub
commit f13093fff7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 586 additions and 364 deletions

View File

@ -113,19 +113,19 @@
#define PRV_H 2 #define PRV_H 2
#define PRV_M 3 #define PRV_M 3
#define SPTBR32_MODE 0x80000000 #define SATP32_MODE 0x80000000
#define SPTBR32_ASID 0x7FC00000 #define SATP32_ASID 0x7FC00000
#define SPTBR32_PPN 0x003FFFFF #define SATP32_PPN 0x003FFFFF
#define SPTBR64_MODE 0xF000000000000000 #define SATP64_MODE 0xF000000000000000
#define SPTBR64_ASID 0x0FFFF00000000000 #define SATP64_ASID 0x0FFFF00000000000
#define SPTBR64_PPN 0x00000FFFFFFFFFFF #define SATP64_PPN 0x00000FFFFFFFFFFF
#define SPTBR_MODE_OFF 0 #define SATP_MODE_OFF 0
#define SPTBR_MODE_SV32 1 #define SATP_MODE_SV32 1
#define SPTBR_MODE_SV39 8 #define SATP_MODE_SV39 8
#define SPTBR_MODE_SV48 9 #define SATP_MODE_SV48 9
#define SPTBR_MODE_SV57 10 #define SATP_MODE_SV57 10
#define SPTBR_MODE_SV64 11 #define SATP_MODE_SV64 11
#define PMP_R 0x01 #define PMP_R 0x01
#define PMP_W 0x02 #define PMP_W 0x02
@ -177,12 +177,12 @@
# define MSTATUS_SD MSTATUS64_SD # define MSTATUS_SD MSTATUS64_SD
# define SSTATUS_SD SSTATUS64_SD # define SSTATUS_SD SSTATUS64_SD
# define RISCV_PGLEVEL_BITS 9 # define RISCV_PGLEVEL_BITS 9
# define SPTBR_MODE SPTBR64_MODE # define SATP_MODE SATP64_MODE
#else #else
# define MSTATUS_SD MSTATUS32_SD # define MSTATUS_SD MSTATUS32_SD
# define SSTATUS_SD SSTATUS32_SD # define SSTATUS_SD SSTATUS32_SD
# define RISCV_PGLEVEL_BITS 10 # define RISCV_PGLEVEL_BITS 10
# define SPTBR_MODE SPTBR32_MODE # define SATP_MODE SATP32_MODE
#endif #endif
#define RISCV_PGSHIFT 12 #define RISCV_PGSHIFT 12
#define RISCV_PGSIZE (1 << RISCV_PGSHIFT) #define RISCV_PGSIZE (1 << RISCV_PGSHIFT)
@ -790,9 +790,9 @@
#define CSR_SSCRATCH 0x140 #define CSR_SSCRATCH 0x140
#define CSR_SEPC 0x141 #define CSR_SEPC 0x141
#define CSR_SCAUSE 0x142 #define CSR_SCAUSE 0x142
#define CSR_SBADADDR 0x143 #define CSR_STVAL 0x143
#define CSR_SIP 0x144 #define CSR_SIP 0x144
#define CSR_SPTBR 0x180 #define CSR_SATP 0x180
#define CSR_MSTATUS 0x300 #define CSR_MSTATUS 0x300
#define CSR_MISA 0x301 #define CSR_MISA 0x301
#define CSR_MEDELEG 0x302 #define CSR_MEDELEG 0x302
@ -803,7 +803,7 @@
#define CSR_MSCRATCH 0x340 #define CSR_MSCRATCH 0x340
#define CSR_MEPC 0x341 #define CSR_MEPC 0x341
#define CSR_MCAUSE 0x342 #define CSR_MCAUSE 0x342
#define CSR_MBADADDR 0x343 #define CSR_MTVAL 0x343
#define CSR_MIP 0x344 #define CSR_MIP 0x344
#define CSR_PMPCFG0 0x3a0 #define CSR_PMPCFG0 0x3a0
#define CSR_PMPCFG1 0x3a1 #define CSR_PMPCFG1 0x3a1
@ -1282,9 +1282,9 @@ DECLARE_CSR(scounteren, CSR_SCOUNTEREN)
DECLARE_CSR(sscratch, CSR_SSCRATCH) DECLARE_CSR(sscratch, CSR_SSCRATCH)
DECLARE_CSR(sepc, CSR_SEPC) DECLARE_CSR(sepc, CSR_SEPC)
DECLARE_CSR(scause, CSR_SCAUSE) DECLARE_CSR(scause, CSR_SCAUSE)
DECLARE_CSR(sbadaddr, CSR_SBADADDR) DECLARE_CSR(stval, CSR_STVAL)
DECLARE_CSR(sip, CSR_SIP) DECLARE_CSR(sip, CSR_SIP)
DECLARE_CSR(sptbr, CSR_SPTBR) DECLARE_CSR(satp, CSR_SATP)
DECLARE_CSR(mstatus, CSR_MSTATUS) DECLARE_CSR(mstatus, CSR_MSTATUS)
DECLARE_CSR(misa, CSR_MISA) DECLARE_CSR(misa, CSR_MISA)
DECLARE_CSR(medeleg, CSR_MEDELEG) DECLARE_CSR(medeleg, CSR_MEDELEG)
@ -1295,7 +1295,7 @@ DECLARE_CSR(mcounteren, CSR_MCOUNTEREN)
DECLARE_CSR(mscratch, CSR_MSCRATCH) DECLARE_CSR(mscratch, CSR_MSCRATCH)
DECLARE_CSR(mepc, CSR_MEPC) DECLARE_CSR(mepc, CSR_MEPC)
DECLARE_CSR(mcause, CSR_MCAUSE) DECLARE_CSR(mcause, CSR_MCAUSE)
DECLARE_CSR(mbadaddr, CSR_MBADADDR) DECLARE_CSR(mtval, CSR_MTVAL)
DECLARE_CSR(mip, CSR_MIP) DECLARE_CSR(mip, CSR_MIP)
DECLARE_CSR(pmpcfg0, CSR_PMPCFG0) DECLARE_CSR(pmpcfg0, CSR_PMPCFG0)
DECLARE_CSR(pmpcfg1, CSR_PMPCFG1) DECLARE_CSR(pmpcfg1, CSR_PMPCFG1)

View File

@ -4,15 +4,76 @@
// gdb's register list is defined in riscv_gdb_reg_names gdb/riscv-tdep.c in // 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. // its source tree. We must interpret the numbers the same here.
enum gdb_regno { enum gdb_regno {
GDB_REGNO_XPR0 = 0, GDB_REGNO_ZERO = 0, /* Read-only register, always 0. */
GDB_REGNO_X0 = GDB_REGNO_XPR0 + 0, GDB_REGNO_RA = 1, /* Return Address. */
GDB_REGNO_ZERO = GDB_REGNO_XPR0 + 0, GDB_REGNO_SP = 2, /* Stack Pointer. */
GDB_REGNO_S0 = GDB_REGNO_XPR0 + 8, GDB_REGNO_GP = 3, /* Global Pointer. */
GDB_REGNO_S1 = GDB_REGNO_XPR0 + 9, GDB_REGNO_TP = 4, /* Thread Pointer. */
GDB_REGNO_XPR31 = GDB_REGNO_XPR0 + 31, 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_PC = 32,
GDB_REGNO_FPR0 = 33, 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_CSR0 = 65,
GDB_REGNO_TSELECT = CSR_TSELECT + GDB_REGNO_CSR0, GDB_REGNO_TSELECT = CSR_TSELECT + GDB_REGNO_CSR0,
GDB_REGNO_TDATA1 = CSR_TDATA1 + GDB_REGNO_CSR0, GDB_REGNO_TDATA1 = CSR_TDATA1 + GDB_REGNO_CSR0,

View File

@ -45,7 +45,7 @@ int riscv_program_exec(struct riscv_program *p, struct target *t)
keep_alive(); keep_alive();
riscv_reg_t saved_registers[GDB_REGNO_XPR31 + 1]; 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]) { if (p->writes_xreg[i]) {
LOG_DEBUG("Saving register %d as used by program", (int)i); LOG_DEBUG("Saving register %d as used by program", (int)i);
saved_registers[i] = riscv_get_register(t, 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)) if (i >= riscv_debug_buffer_size(p->target))
p->debug_buffer[i] = riscv_read_debug_buffer(t, i); 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]) if (p->writes_xreg[i])
riscv_set_register(t, i, saved_registers[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) 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); 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) int riscv_program_csrw(struct riscv_program *p, enum gdb_regno s, enum gdb_regno csr)
{ {
assert(csr >= GDB_REGNO_CSR0); 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) int riscv_program_fence_i(struct riscv_program *p)

View File

@ -189,12 +189,6 @@ typedef struct {
struct memory_cache_line dram_cache[DRAM_CACHE_SIZE]; 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 // Number of run-test/idle cycles the target requests we do after each dbus
// access. // access.
unsigned int dtmcontrol_idle; unsigned int dtmcontrol_idle;
@ -223,7 +217,7 @@ typedef struct {
static int poll_target(struct target *target, bool announce); static int poll_target(struct target *target, bool announce);
static int riscv011_poll(struct target *target); 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. ***/ /*** Utility functions. ***/
@ -1181,30 +1175,6 @@ static int resume(struct target *target, int debug_execution, bool step)
return execute_resume(target, 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) static uint64_t reg_cache_get(struct target *target, unsigned int number)
{ {
struct reg *r = &target->reg_cache->reg_list[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 // Force reading the register. In that process mstatus_actual will be
// updated. // 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. ***/ /*** OpenOCD target functions. ***/
@ -1275,58 +1246,6 @@ static int register_read(struct target *target, riscv_reg_t *value, int regnum)
return ERROR_OK; 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. // Write the register. No caching or games.
static int register_write(struct target *target, unsigned int number, static int register_write(struct target *target, unsigned int number,
uint64_t value) 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_store(target, 1, S0, SLOT_LAST);
cache_set_jump(target, 2); cache_set_jump(target, 2);
} else if (number <= GDB_REGNO_XPR31) { } 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); cache_set_jump(target, 1);
} else if (number == GDB_REGNO_PC) { } else if (number == GDB_REGNO_PC) {
info->dpc = value; info->dpc = value;
@ -1399,33 +1318,53 @@ static int register_write(struct target *target, unsigned int number,
return ERROR_OK; 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) static riscv_reg_t get_register(struct target *target, int hartid, int regid)
{ {
assert(hartid == 0); assert(hartid == 0);
riscv_reg_t value; riscv011_info_t *info = get_info(target);
if (register_read(target, &value, regid) != ERROR_OK) {
// TODO: propagate errors maybe_write_tselect(target);
value = ~0; 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; 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)); generic_info->version_specific = calloc(1, sizeof(riscv011_info_t));
if (!generic_info->version_specific) if (!generic_info->version_specific)
return ERROR_FAIL; return ERROR_FAIL;
riscv011_info_t *info = get_info(target);
target->reg_cache = calloc(1, sizeof(*target->reg_cache)); // Assume 32-bit until we discover the real value in examine().
target->reg_cache->name = "RISC-V registers"; generic_info->xlen[0] = 32;
target->reg_cache->num_regs = GDB_REGNO_COUNT; riscv_init_registers(target);
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; return ERROR_OK;
} }
@ -1690,9 +1594,6 @@ static int examine(struct target *target)
} }
LOG_DEBUG("Discovered XLEN is %d", riscv_xlen(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) { if (read_csr(target, &r->misa, CSR_MISA) != ERROR_OK) {
const unsigned old_csr_misa = 0xf10; const unsigned old_csr_misa = 0xf10;
LOG_WARNING("Failed to read misa at 0x%x; trying 0x%x.", CSR_MISA, 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; info->never_halted = true;
int result = riscv011_poll(target); int result = riscv011_poll(target);

View File

@ -128,12 +128,6 @@ struct trigger {
int unique_id; int unique_id;
}; };
struct memory_cache_line {
uint32_t data;
bool valid;
bool dirty;
};
typedef enum { typedef enum {
YNM_MAYBE, YNM_MAYBE,
YNM_YES, YNM_YES,
@ -156,12 +150,6 @@ typedef struct {
/* We only need the address so that we know the alignment of the buffer. */ /* We only need the address so that we know the alignment of the buffer. */
riscv_addr_t progbuf_address; 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 // Number of run-test/idle cycles the target requests we do after each dbus
// access. // access.
unsigned int dtmcontrol_idle; 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; return (riscv013_info_t *) info->version_specific;
} }
/*** Necessary prototypes. ***/
static int register_get(struct reg *reg);
/*** Utility functions. ***/ /*** 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 void select_dmi(struct target *target)
{ {
static uint8_t ir_dmi[1] = {DTM_DMI}; 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) { if (number <= GDB_REGNO_XPR31) {
command = set_field(command, AC_ACCESS_REGISTER_REGNO, 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) { } else if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) {
command = set_field(command, AC_ACCESS_REGISTER_REGNO, command = set_field(command, AC_ACCESS_REGISTER_REGNO,
0x1020 + number - GDB_REGNO_FPR0); 0x1020 + number - GDB_REGNO_FPR0);
@ -969,7 +939,7 @@ static int register_write_direct(struct target *target, unsigned number,
return ERROR_FAIL; return ERROR_FAIL;
if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31 && if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31 &&
supports_extension(target, 'D') && riscv_supports_extension(target, 'D') &&
riscv_xlen(target) < 64) { riscv_xlen(target) < 64) {
/* There are no instructions to move all the bits from a register, so /* There are no instructions to move all the bits from a register, so
* we need to use some scratch RAM. */ * we need to use some scratch RAM. */
@ -991,7 +961,7 @@ static int register_write_direct(struct target *target, unsigned number,
return ERROR_FAIL; return ERROR_FAIL;
if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) { 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)); riscv_program_insert(&program, fmv_d_x(number - GDB_REGNO_FPR0, S0));
} else { } else {
riscv_program_insert(&program, fmv_w_x(number - GDB_REGNO_FPR0, S0)); 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) { if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) {
// TODO: Possibly set F in mstatus. // 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 /* There are no instructions to move all the bits from a
* register, so we need to use some scratch RAM. */ * register, so we need to use some scratch RAM. */
riscv_program_insert(&program, fsd(number - GDB_REGNO_FPR0, S0, 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, if (register_write_direct(target, GDB_REGNO_S0,
scratch.hart_address) != ERROR_OK) scratch.hart_address) != ERROR_OK)
return ERROR_FAIL; 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)); riscv_program_insert(&program, fmv_x_d(S0, number - GDB_REGNO_FPR0));
} else { } else {
riscv_program_insert(&program, fmv_x_w(S0, number - GDB_REGNO_FPR0)); 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. ***/ /*** 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, static int init_target(struct command_context *cmd_ctx,
struct target *target) struct target *target)
{ {
@ -1168,44 +1104,6 @@ static int init_target(struct command_context *cmd_ctx,
info->abstract_read_fpr_supported = true; info->abstract_read_fpr_supported = true;
info->abstract_write_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; return ERROR_OK;
} }
@ -1295,36 +1193,24 @@ static int examine(struct target *target)
RISCV_INFO(r); RISCV_INFO(r);
r->impebreak = get_field(dmstatus, DMI_DMSTATUS_IMPEBREAK); 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) { for (int i = 0; i < RISCV_MAX_HARTS; ++i) {
/* Fake being a non-RTOS targeted to this core so we can see if if (!riscv_rtos_enabled(target) && i != target->coreid)
* it exists. This avoids the assertion in continue;
* riscv_set_current_hartid() that ensures non-RTOS targets
* don't touch the harts they're not assigned to. */ r->current_hartid = i;
target->coreid = i; riscv013_select_current_hart(target);
r->hart_count = i + 1;
riscv_set_current_hartid(target, i);
uint32_t s = dmi_read(target, DMI_DMSTATUS); uint32_t s = dmi_read(target, DMI_DMSTATUS);
if (get_field(s, DMI_DMSTATUS_ANYNONEXISTENT)) { if (get_field(s, DMI_DMSTATUS_ANYNONEXISTENT)) {
r->hart_count--;
break; break;
} }
} r->hart_count = i + 1;
target->coreid = original_coreid;
LOG_DEBUG("Enumerated %d harts", r->hart_count); if (!riscv_is_halted(target)) {
riscv013_halt_current_hart(target);
/* Halt every hart so we can probe them. */ }
riscv_halt_all_harts(target);
/* Find the address of the program buffer, which must be done without
* knowing anything about the target. */
for (int i = 0; i < riscv_count_harts(target); ++i) {
if (!riscv_hart_enabled(target, i))
continue;
riscv_set_current_hartid(target, i);
/* Without knowing anything else we can at least mess with the /* Without knowing anything else we can at least mess with the
* program buffer. */ * program buffer. */
@ -1337,7 +1223,11 @@ static int examine(struct target *target)
r->xlen[i] = 32; 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 /* Display this as early as possible to help people who are using
* really slow simulators. */ * really slow simulators. */
@ -1345,12 +1235,16 @@ static int examine(struct target *target)
r->misa); r->misa);
} }
LOG_DEBUG("Enumerated %d harts", r->hart_count);
/* Then we check the number of triggers availiable to each hart. */ /* Then we check the number of triggers availiable to each hart. */
riscv_enumerate_triggers(target); riscv_enumerate_triggers(target);
/* Resumes all the harts, so the debugger can later pause them. */ /* 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); riscv_resume_all_harts(target);
target->state = TARGET_RUNNING; target->state = TARGET_RUNNING;
target_set_examined(target); target_set_examined(target);
if (target->rtos) { if (target->rtos) {

View File

@ -151,21 +151,6 @@ typedef enum slot {
#define DBUS_ADDRESS_UNKNOWN 0xffff #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 MAX_HWBPS 16
#define DRAM_CACHE_SIZE 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; bool riscv_use_scratch_ram = false;
uint64_t riscv_scratch_ram_address = 0; 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) static uint32_t dtmcontrol_scan(struct target *target, uint32_t out)
{ {
struct scan_field field; struct scan_field field;
@ -430,7 +423,6 @@ static int add_trigger(struct target *target, struct trigger *trigger)
int result = ERROR_OK; int result = ERROR_OK;
for (int hartid = first_hart; hartid < riscv_count_harts(target); ++hartid) { for (int hartid = first_hart; hartid < riscv_count_harts(target); ++hartid) {
LOG_DEBUG(">>> hartid=%d", hartid);
if (!riscv_hart_enabled(target, hartid)) if (!riscv_hart_enabled(target, hartid))
continue; continue;
if (hartid > first_hart) { 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("reg_class=%d", reg_class);
LOG_DEBUG("rtos_hartid=%d current_hartid=%d", r->rtos_hartid, r->current_hartid); 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)) if (r->rtos_hartid != -1 && riscv_rtos_enabled(target))
riscv_set_current_hartid(target, r->rtos_hartid); riscv_set_current_hartid(target, r->rtos_hartid);
else else
@ -770,7 +767,7 @@ static int riscv_get_gdb_reg_list(struct target *target,
*reg_list_size = 32; *reg_list_size = 32;
break; break;
case REG_CLASS_ALL: case REG_CLASS_ALL:
*reg_list_size = REG_COUNT; *reg_list_size = GDB_REGNO_COUNT;
break; break;
default: default:
LOG_ERROR("Unsupported reg_class: %d", reg_class); 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) { if (!*reg_list) {
return ERROR_FAIL; 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++) { 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]; (*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; return ERROR_FAIL;
} }
if (r->number > REG_XPR31) { if (r->number > GDB_REGNO_XPR31) {
LOG_ERROR("Only GPRs can be use as argument registers."); LOG_ERROR("Only GPRs can be use as argument registers.");
return ERROR_FAIL; return ERROR_FAIL;
} }
@ -1180,6 +1173,88 @@ COMMAND_HANDLER(riscv_set_scratch_ram)
return ERROR_OK; 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[] = { static const struct command_registration riscv_exec_command_handlers[] = {
{ {
.name = "set_command_timeout_sec", .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]", .usage = "riscv set_scratch_ram none|[address]",
.help = "Set address of 16 bytes of scratch RAM the debugger can use, or 'none'." .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 COMMAND_REGISTRATION_DONE
}; };
@ -1352,6 +1436,20 @@ int riscv_step_rtos_hart(struct target *target)
return ERROR_OK; 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) int riscv_xlen(const struct target *target)
{ {
return riscv_xlen_of_hart(target, riscv_current_hartid(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. */ /* Avoid invalidating the register cache all the time. */
if (r->registers_initialized if (r->registers_initialized
&& (!riscv_rtos_enabled(target) || (previous_hartid == hartid)) && (!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))) { && (!riscv_rtos_enabled(target) || (r->rtos_hartid != -1))) {
return; return;
} else } else
@ -1402,22 +1500,10 @@ void riscv_invalidate_register_cache(struct target *target)
{ {
RISCV_INFO(r); RISCV_INFO(r);
/* Update the register list's widths. */
register_cache_invalidate(target->reg_cache); register_cache_invalidate(target->reg_cache);
for (size_t i = 0; i < GDB_REGNO_COUNT; ++i) { for (size_t i = 0; i < GDB_REGNO_COUNT; ++i) {
struct reg *reg = &target->reg_cache->reg_list[i]; struct reg *reg = &target->reg_cache->reg_list[i];
reg->value = &r->reg_cache_values[i];
reg->valid = false; reg->valid = false;
switch (i) {
case GDB_REGNO_PRIV:
reg->size = 8;
break;
default:
reg->size = riscv_xlen(target);
break;
}
} }
r->registers_initialized = true; 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) 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); 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"; return "priv";
default: default:
if (regno <= GDB_REGNO_XPR31) { 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) { } else if (regno >= GDB_REGNO_CSR0 && regno <= GDB_REGNO_CSR4095) {
sprintf(buf, "csr%d", regno - GDB_REGNO_CSR0); sprintf(buf, "csr%d", regno - GDB_REGNO_CSR0);
} else if (regno >= GDB_REGNO_FPR0 && regno <= GDB_REGNO_FPR31) { } 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; 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;
}

View File

@ -59,7 +59,11 @@ typedef struct {
/* The register cache points into here. */ /* The register cache points into here. */
uint64_t reg_cache_values[RISCV_MAX_REGISTERS]; 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. */ /* It's possible that each core has a different supported ISA set. */
int xlen[RISCV_MAX_HARTS]; int xlen[RISCV_MAX_HARTS];
@ -168,6 +172,8 @@ int riscv_resume_one_hart(struct target *target, int hartid);
* then the only hart. */ * then the only hart. */
int riscv_step_rtos_hart(struct target *target); 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. */ /* Returns XLEN for the given (or current) hart. */
int riscv_xlen(const struct target *target); int riscv_xlen(const struct target *target);
int riscv_xlen_of_hart(const struct target *target, int hartid); 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, int riscv_remove_watchpoint(struct target *target,
struct watchpoint *watchpoint); struct watchpoint *watchpoint);
int riscv_init_registers(struct target *target);
#endif #endif

View File

@ -2742,21 +2742,23 @@ COMMAND_HANDLER(handle_reg_command)
i < cache->num_regs; i < cache->num_regs;
i++, reg++, count++) { i++, reg++, count++) {
/* only print cached values if they are valid */ /* only print cached values if they are valid */
if (reg->valid) { if (reg->exist) {
value = buf_to_str(reg->value, if (reg->valid) {
reg->size, 16); value = buf_to_str(reg->value,
command_print(CMD_CTX, reg->size, 16);
"(%i) %s (/%" PRIu32 "): 0x%s%s", command_print(CMD_CTX,
count, reg->name, "(%i) %s (/%" PRIu32 "): 0x%s%s",
reg->size, value, count, reg->name,
reg->dirty reg->size, value,
reg->dirty
? " (dirty)" ? " (dirty)"
: ""); : "");
free(value); free(value);
} else { } else {
command_print(CMD_CTX, "(%i) %s (/%" PRIu32 ")", command_print(CMD_CTX, "(%i) %s (/%" PRIu32 ")",
count, reg->name, count, reg->name,
reg->size) ; reg->size) ;
}
} }
} }
cache = cache->next; cache = cache->next;