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:
commit
f13093fff7
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
struct target *target = (struct target *) reg->arch_info;
|
||||
|
||||
uint64_t value = buf_get_u64(buf, 0, riscv_xlen(target));
|
||||
|
||||
LOG_DEBUG("write 0x%" PRIx64 " to %s", value, reg->name);
|
||||
struct reg *r = &target->reg_cache->reg_list[reg->number];
|
||||
r->valid = true;
|
||||
memcpy(r->value, buf, (r->size + 7) / 8);
|
||||
|
||||
return register_write(target, reg->number, value);
|
||||
}
|
||||
|
||||
static struct reg_arch_type riscv_reg_arch_type = {
|
||||
.get = register_get,
|
||||
.set = register_set
|
||||
};
|
||||
|
||||
static riscv_reg_t get_register(struct target *target, int hartid, int regid)
|
||||
{
|
||||
assert(hartid == 0);
|
||||
riscv_reg_t value;
|
||||
if (register_read(target, &value, regid) != ERROR_OK) {
|
||||
// TODO: propagate errors
|
||||
value = ~0;
|
||||
riscv011_info_t *info = get_info(target);
|
||||
|
||||
maybe_write_tselect(target);
|
||||
riscv_reg_t value = ~0;
|
||||
|
||||
if (regid <= GDB_REGNO_XPR31) {
|
||||
value = reg_cache_get(target, regid);
|
||||
} else if (regid == GDB_REGNO_PC) {
|
||||
value = info->dpc;
|
||||
} else if (regid >= GDB_REGNO_FPR0 && regid <= GDB_REGNO_FPR31) {
|
||||
int result = update_mstatus_actual(target);
|
||||
if (result != ERROR_OK) {
|
||||
return ~0;
|
||||
}
|
||||
unsigned i = 0;
|
||||
if ((info->mstatus_actual & MSTATUS_FS) == 0) {
|
||||
info->mstatus_actual = set_field(info->mstatus_actual, MSTATUS_FS, 1);
|
||||
cache_set_load(target, i++, S0, SLOT1);
|
||||
cache_set32(target, i++, csrw(S0, CSR_MSTATUS));
|
||||
cache_set(target, SLOT1, info->mstatus_actual);
|
||||
}
|
||||
|
||||
if (riscv_xlen(target) == 32) {
|
||||
cache_set32(target, i++, fsw(regid - GDB_REGNO_FPR0, 0, DEBUG_RAM_START + 16));
|
||||
} else {
|
||||
cache_set32(target, i++, fsd(regid - GDB_REGNO_FPR0, 0, DEBUG_RAM_START + 16));
|
||||
}
|
||||
cache_set_jump(target, i++);
|
||||
|
||||
if (cache_write(target, 4, true) != ERROR_OK) {
|
||||
return ~0;
|
||||
}
|
||||
} else if (regid == GDB_REGNO_PRIV) {
|
||||
value = get_field(info->dcsr, DCSR_PRV);
|
||||
} else {
|
||||
if (register_read(target, &value, regid) != ERROR_OK) {
|
||||
value = ~0;
|
||||
}
|
||||
}
|
||||
|
||||
if (regid == GDB_REGNO_MSTATUS) {
|
||||
target->reg_cache->reg_list[regid].valid = true;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
|
@ -1466,45 +1405,10 @@ static int init_target(struct command_context *cmd_ctx,
|
|||
generic_info->version_specific = calloc(1, sizeof(riscv011_info_t));
|
||||
if (!generic_info->version_specific)
|
||||
return ERROR_FAIL;
|
||||
riscv011_info_t *info = get_info(target);
|
||||
|
||||
target->reg_cache = calloc(1, sizeof(*target->reg_cache));
|
||||
target->reg_cache->name = "RISC-V registers";
|
||||
target->reg_cache->num_regs = GDB_REGNO_COUNT;
|
||||
|
||||
target->reg_cache->reg_list = calloc(GDB_REGNO_COUNT, sizeof(struct reg));
|
||||
|
||||
const unsigned int max_reg_name_len = 12;
|
||||
info->reg_names = calloc(1, GDB_REGNO_COUNT * max_reg_name_len);
|
||||
char *reg_name = info->reg_names;
|
||||
info->reg_values = NULL;
|
||||
|
||||
for (unsigned int i = 0; i < GDB_REGNO_COUNT; i++) {
|
||||
struct reg *r = &target->reg_cache->reg_list[i];
|
||||
r->number = i;
|
||||
r->caller_save = true;
|
||||
r->dirty = false;
|
||||
r->valid = false;
|
||||
r->exist = true;
|
||||
r->type = &riscv_reg_arch_type;
|
||||
r->arch_info = target;
|
||||
if (i <= GDB_REGNO_XPR31) {
|
||||
sprintf(reg_name, "x%d", i);
|
||||
} else if (i == GDB_REGNO_PC) {
|
||||
sprintf(reg_name, "pc");
|
||||
} else if (i >= GDB_REGNO_FPR0 && i <= GDB_REGNO_FPR31) {
|
||||
sprintf(reg_name, "f%d", i - GDB_REGNO_FPR0);
|
||||
} else if (i >= GDB_REGNO_CSR0 && i <= GDB_REGNO_CSR4095) {
|
||||
sprintf(reg_name, "csr%d", i - GDB_REGNO_CSR0);
|
||||
} else if (i == GDB_REGNO_PRIV) {
|
||||
sprintf(reg_name, "priv");
|
||||
}
|
||||
if (reg_name[0]) {
|
||||
r->name = reg_name;
|
||||
}
|
||||
reg_name += strlen(reg_name) + 1;
|
||||
assert(reg_name < info->reg_names + GDB_REGNO_COUNT * max_reg_name_len);
|
||||
}
|
||||
// Assume 32-bit until we discover the real value in examine().
|
||||
generic_info->xlen[0] = 32;
|
||||
riscv_init_registers(target);
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
@ -1690,9 +1594,6 @@ static int examine(struct target *target)
|
|||
}
|
||||
LOG_DEBUG("Discovered XLEN is %d", riscv_xlen(target));
|
||||
|
||||
// Update register list to match discovered XLEN.
|
||||
update_reg_list(target);
|
||||
|
||||
if (read_csr(target, &r->misa, CSR_MISA) != ERROR_OK) {
|
||||
const unsigned old_csr_misa = 0xf10;
|
||||
LOG_WARNING("Failed to read misa at 0x%x; trying 0x%x.", CSR_MISA,
|
||||
|
@ -1705,6 +1606,9 @@ static int examine(struct target *target)
|
|||
}
|
||||
}
|
||||
|
||||
// Update register list to match discovered XLEN/supported extensions.
|
||||
riscv_init_registers(target);
|
||||
|
||||
info->never_halted = true;
|
||||
|
||||
int result = riscv011_poll(target);
|
||||
|
|
|
@ -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;
|
||||
r->hart_count = i + 1;
|
||||
|
||||
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;
|
||||
|
||||
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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue