WIP xml register for 0.11.

On HiFive1, FPRs show up with no name, and misa is 0x1105 instead of
0x40001105.

Change-Id: I4ee223c905ad7d860147014e7b6394668658c6ea
This commit is contained in:
Tim Newsome 2017-12-01 12:42:16 -08:00
parent 8926e66d3a
commit f341db9f72
4 changed files with 293 additions and 395 deletions

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)
@ -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

@ -150,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;
@ -290,10 +284,6 @@ 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. ***/
static void select_dmi(struct target *target) static void select_dmi(struct target *target)
@ -1070,225 +1060,6 @@ static int register_read_direct(struct target *target, uint64_t *value, uint32_t
/*** OpenOCD target functions. ***/ /*** OpenOCD target functions. ***/
static int register_get(struct reg *reg)
{
struct target *target = (struct target *) reg->arch_info;
uint64_t value = riscv_get_register(target, reg->number);
buf_set_u64(reg->value, 0, reg->size, value);
return ERROR_OK;
}
static int register_write(struct target *target, unsigned int number,
uint64_t value)
{
riscv_set_register(target, number, value);
return ERROR_OK;
}
static int register_set(struct reg *reg, uint8_t *buf)
{
struct target *target = (struct target *) reg->arch_info;
uint64_t value = buf_get_u64(buf, 0, reg->size);
LOG_DEBUG("write 0x%" PRIx64 " to %s", value, reg->name);
struct reg *r = &target->reg_cache->reg_list[reg->number];
r->valid = true;
memcpy(r->value, buf, (r->size + 7) / 8);
return register_write(target, reg->number, value);
}
static struct reg_arch_type riscv_reg_arch_type = {
.get = register_get,
.set = register_set
};
struct csr_info {
unsigned number;
const char *name;
};
static int cmp_csr_info(const void *p1, const void *p2)
{
return (int) (((struct csr_info *)p1)->number) - (int) (((struct csr_info *)p2)->number);
}
static int init_registers(struct target *target)
{
riscv013_info_t *info = get_info(target);
if (target->reg_cache) {
if (target->reg_cache->reg_list)
free(target->reg_cache->reg_list);
free(target->reg_cache);
}
target->reg_cache = calloc(1, sizeof(*target->reg_cache));
target->reg_cache->name = "RISC-V Registers";
target->reg_cache->num_regs = GDB_REGNO_COUNT;
target->reg_cache->reg_list = calloc(GDB_REGNO_COUNT, sizeof(struct reg));
const unsigned int max_reg_name_len = 12;
if (info->reg_names)
free(info->reg_names);
info->reg_names = calloc(1, GDB_REGNO_COUNT * max_reg_name_len);
char *reg_name = info->reg_names;
info->reg_values = NULL;
static struct reg_feature feature_cpu = {
.name = "org.gnu.gdb.riscv.cpu"
};
static struct reg_feature feature_fpu = {
.name = "org.gnu.gdb.riscv.fpu"
};
static struct reg_feature feature_csr = {
.name = "org.gnu.gdb.riscv.csr"
};
static struct reg_feature feature_virtual = {
.name = "org.gnu.gdb.riscv.virtual"
};
static struct reg_data_type type_ieee_single = {
.type = REG_TYPE_IEEE_SINGLE,
.id = "ieee_single"
};
static struct reg_data_type type_ieee_double = {
.type = REG_TYPE_IEEE_DOUBLE,
.id = "ieee_double"
};
struct csr_info csr_info[] = {
#define DECLARE_CSR(name, number) { number, #name },
#include "encoding.h"
#undef DECLARE_CSR
};
// encoding.h does not contain the registers in sorted order.
qsort(csr_info, DIM(csr_info), sizeof(*csr_info), cmp_csr_info);
unsigned csr_info_index = 0;
// When gdb request register N, gdb_get_register_packet() assumes that this
// is register at index N in reg_list. So if there are certain registers
// that don't exist, we need to leave holes in the list (or renumber, but
// it would be nice not to have yet another set of numbers to translate
// between).
for (uint32_t number = 0; number < GDB_REGNO_COUNT; number++) {
struct reg *r = &target->reg_cache->reg_list[number];
r->caller_save = true;
r->dirty = false;
r->valid = false;
r->exist = true;
r->type = &riscv_reg_arch_type;
r->arch_info = target;
r->number = number;
// r->size is set in riscv_invalidate_register_cache, maybe because the
// target is in theory allowed to change XLEN on us. But I expect a lot
// of other things to break in that case as well.
if (number <= GDB_REGNO_XPR31) {
switch (number) {
case GDB_REGNO_ZERO: r->name = "zero"; break;
case GDB_REGNO_RA: r->name = "ra"; break;
case GDB_REGNO_SP: r->name = "sp"; break;
case GDB_REGNO_GP: r->name = "gp"; break;
case GDB_REGNO_TP: r->name = "tp"; break;
case GDB_REGNO_T0: r->name = "t0"; break;
case GDB_REGNO_T1: r->name = "t1"; break;
case GDB_REGNO_T2: r->name = "t2"; break;
case GDB_REGNO_FP: r->name = "fp"; break;
case GDB_REGNO_S1: r->name = "s1"; break;
case GDB_REGNO_A0: r->name = "a0"; break;
case GDB_REGNO_A1: r->name = "a1"; break;
case GDB_REGNO_A2: r->name = "a2"; break;
case GDB_REGNO_A3: r->name = "a3"; break;
case GDB_REGNO_A4: r->name = "a4"; break;
case GDB_REGNO_A5: r->name = "a5"; break;
case GDB_REGNO_A6: r->name = "a6"; break;
case GDB_REGNO_A7: r->name = "a7"; break;
case GDB_REGNO_S2: r->name = "s2"; break;
case GDB_REGNO_S3: r->name = "s3"; break;
case GDB_REGNO_S4: r->name = "s4"; break;
case GDB_REGNO_S5: r->name = "s5"; break;
case GDB_REGNO_S6: r->name = "s6"; break;
case GDB_REGNO_S7: r->name = "s7"; break;
case GDB_REGNO_S8: r->name = "s8"; break;
case GDB_REGNO_S9: r->name = "s9"; break;
case GDB_REGNO_S10: r->name = "s10"; break;
case GDB_REGNO_S11: r->name = "s11"; break;
case GDB_REGNO_T3: r->name = "t3"; break;
case GDB_REGNO_T4: r->name = "t4"; break;
case GDB_REGNO_T5: r->name = "t5"; break;
case GDB_REGNO_T6: r->name = "t6"; break;
}
r->group = "general";
r->feature = &feature_cpu;
} else if (number == GDB_REGNO_PC) {
sprintf(reg_name, "pc");
r->group = "general";
r->feature = &feature_cpu;
} else if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) {
if (riscv_supports_extension(target, 'D')) {
r->reg_data_type = &type_ieee_double;
} else if (riscv_supports_extension(target, 'F')) {
r->reg_data_type = &type_ieee_single;
} else {
r->exist = false;
}
sprintf(reg_name, "f%d", number - GDB_REGNO_FPR0);
r->group = "float";
r->feature = &feature_fpu;
} else if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095) {
r->group = "csr";
r->feature = &feature_csr;
r->exist = true;
unsigned csr_number = number - GDB_REGNO_CSR0;
while (csr_info[csr_info_index].number < csr_number) {
csr_info_index++;
}
if (csr_info[csr_info_index].number == csr_number) {
r->name = csr_info[csr_info_index].name;
} else {
sprintf(reg_name, "csr%d", csr_number);
// Assume unnamed registers don't exist, unless we have some
// configuration that tells us otherwise. That's important
// because eg. Eclipse crashes if a target has too many
// registers, and apparently has no way of only showing a
// subset of registers in any case.
r->exist = false;
}
switch (csr_number) {
case CSR_FFLAGS:
r->exist = riscv_supports_extension(target, 'F');
r->group = "float";
r->feature = &feature_fpu;
break;
case CSR_FRM:
r->exist = riscv_supports_extension(target, 'F');
r->group = "float";
r->feature = &feature_fpu;
break;
case CSR_FCSR:
r->exist = riscv_supports_extension(target, 'F');
r->group = "float";
r->feature = &feature_fpu;
break;
}
} else if (number == GDB_REGNO_PRIV) {
sprintf(reg_name, "priv");
r->group = "general";
r->feature = &feature_virtual;
}
if (reg_name[0]) {
r->name = reg_name;
}
reg_name += strlen(reg_name) + 1;
assert(reg_name < info->reg_names + GDB_REGNO_COUNT * max_reg_name_len);
}
return ERROR_OK;
}
static int init_target(struct command_context *cmd_ctx, static int init_target(struct command_context *cmd_ctx,
struct target *target) struct target *target)
{ {
@ -1438,12 +1209,24 @@ static int examine(struct target *target)
break; break;
} }
} }
// Reset hartid to one that exists. Set coreid to avoid assert in
// riscv_set_current_hartid(). (TODO)
target->coreid = 0;
riscv_set_current_hartid(target, 0);
target->coreid = original_coreid; target->coreid = original_coreid;
LOG_DEBUG("Enumerated %d harts", r->hart_count); LOG_DEBUG("Enumerated %d harts", r->hart_count);
// Assume 32-bit until we discover the real value in examine().
for (int i = 0; i < riscv_count_harts(target); ++i) {
if (riscv_hart_enabled(target, i)) {
r->xlen[i] = 32;
}
LOG_DEBUG(">>> temporary XLEN for hart %d is %d", i, r->xlen[i]);
}
// Get a functional register cache going. // Get a functional register cache going.
if (init_registers(target) != ERROR_OK) if (riscv_init_registers(target) != ERROR_OK)
return ERROR_FAIL; return ERROR_FAIL;
/* Halt every hart so we can probe them. */ /* Halt every hart so we can probe them. */
@ -1484,7 +1267,7 @@ static int examine(struct target *target)
target->state = TARGET_RUNNING; target->state = TARGET_RUNNING;
// Now reinit registers based on what we discovered. // Now reinit registers based on what we discovered.
if (init_registers(target) != ERROR_OK) if (riscv_init_registers(target) != ERROR_OK)
return ERROR_FAIL; return ERROR_FAIL;
target_set_examined(target); target_set_examined(target);

View File

@ -415,7 +415,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) {
@ -1248,6 +1247,7 @@ void riscv_info_init(struct target *target, riscv_info_t *r)
memset(r, 0, sizeof(*r)); memset(r, 0, sizeof(*r));
r->dtm_version = 1; r->dtm_version = 1;
r->registers_initialized = false; r->registers_initialized = false;
LOG_DEBUG(">>> current_hartid=%d", target->coreid);
r->current_hartid = target->coreid; r->current_hartid = target->coreid;
memset(r->trigger_unique_id, 0xff, sizeof(r->trigger_unique_id)); memset(r->trigger_unique_id, 0xff, sizeof(r->trigger_unique_id));
@ -1360,6 +1360,7 @@ int riscv_xlen(const struct target *target)
int riscv_xlen_of_hart(const struct target *target, int hartid) int riscv_xlen_of_hart(const struct target *target, int hartid)
{ {
RISCV_INFO(r); RISCV_INFO(r);
LOG_DEBUG(">>> xlen[%d] = %d", hartid, r->xlen[hartid]);
assert(r->xlen[hartid] != -1); assert(r->xlen[hartid] != -1);
return r->xlen[hartid]; return r->xlen[hartid];
} }
@ -1406,21 +1407,7 @@ void riscv_invalidate_register_cache(struct target *target)
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;
if (reg->number == GDB_REGNO_PRIV) {
reg->size = 8;
} else if (reg->number >= GDB_REGNO_FPR0 && reg->number <= GDB_REGNO_FPR31) {
if (riscv_supports_extension(target, 'D')) {
reg->size = 64;
} else if (riscv_supports_extension(target, 'F')) {
reg->size = 32;
}
} else {
reg->size = riscv_xlen(target);
}
} }
r->registers_initialized = true; r->registers_initialized = true;
@ -1460,6 +1447,7 @@ bool riscv_has_register(struct target *target, int hartid, int regid)
void riscv_set_register(struct target *target, enum gdb_regno r, riscv_reg_t v) 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);
} }
@ -1690,3 +1678,220 @@ 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;
}
sprintf(reg_name, "f%d", number - GDB_REGNO_FPR0);
r->group = "float";
r->feature = &feature_fpu;
} else if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095) {
r->group = "csr";
r->feature = &feature_csr;
unsigned csr_number = number - GDB_REGNO_CSR0;
while (csr_info[csr_info_index].number < csr_number &&
csr_info_index < DIM(csr_info) - 1) {
csr_info_index++;
}
if (csr_info[csr_info_index].number == csr_number) {
r->name = csr_info[csr_info_index].name;
} else {
sprintf(reg_name, "csr%d", csr_number);
// Assume unnamed registers don't exist, unless we have some
// configuration that tells us otherwise. That's important
// because eg. Eclipse crashes if a target has too many
// registers, and apparently has no way of only showing a
// subset of registers in any case.
r->exist = false;
}
switch (csr_number) {
case CSR_FFLAGS:
r->exist = riscv_supports_extension(target, 'F');
r->group = "float";
r->feature = &feature_fpu;
break;
case CSR_FRM:
r->exist = riscv_supports_extension(target, 'F');
r->group = "float";
r->feature = &feature_fpu;
break;
case CSR_FCSR:
r->exist = riscv_supports_extension(target, 'F');
r->group = "float";
r->feature = &feature_fpu;
break;
}
} else if (number == GDB_REGNO_PRIV) {
sprintf(reg_name, "priv");
r->group = "general";
r->feature = &feature_virtual;
}
if (reg_name[0]) {
r->name = reg_name;
}
reg_name += strlen(reg_name) + 1;
assert(reg_name < info->reg_names + GDB_REGNO_COUNT * max_reg_name_len);
r->value = &info->reg_cache_values[number];
}
return ERROR_OK;
}

View File

@ -60,6 +60,10 @@ 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];
@ -243,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