[NFC] target/riscv: refactor `init_registers()`

The logic in `init_registers()` was quite convoluted.
Initialization of each `struct reg` field is separated into function
`gdb_regno_<field_name>()`.
IMHO, this makes it much easier to reason about the code.

Change-Id: Id7faa1464ce026cc5025585d0a6a95a01fb39cee
Signed-off-by: Evgeniy Naydanov <evgeniy.naydanov@syntacore.com>
This commit is contained in:
Evgeniy Naydanov 2024-01-17 21:52:21 +03:00
parent cb2b394514
commit ea7e17491d
3 changed files with 535 additions and 385 deletions

View File

@ -78,6 +78,9 @@ enum gdb_regno {
GDB_REGNO_FT11,
GDB_REGNO_FPR31 = GDB_REGNO_FT11,
GDB_REGNO_CSR0 = 65,
GDB_REGNO_FCSR = CSR_FCSR + GDB_REGNO_CSR0,
GDB_REGNO_FFLAGS = CSR_FFLAGS + GDB_REGNO_CSR0,
GDB_REGNO_FRM = CSR_FRM + GDB_REGNO_CSR0,
GDB_REGNO_VSTART = CSR_VSTART + GDB_REGNO_CSR0,
GDB_REGNO_VXSAT = CSR_VXSAT + GDB_REGNO_CSR0,
GDB_REGNO_VXRM = CSR_VXRM + GDB_REGNO_CSR0,
@ -120,6 +123,6 @@ enum gdb_regno {
GDB_REGNO_COUNT
};
const char *gdb_regno_name(struct target *target, enum gdb_regno regno);
const char *gdb_regno_name(const struct target *target, enum gdb_regno regno);
#endif

View File

@ -478,14 +478,16 @@ static int riscv_init_target(struct command_context *cmd_ctx,
return ERROR_OK;
}
static void free_reg_names(struct target *target);
static void riscv_free_registers(struct target *target)
{
free_reg_names(target);
/* Free the shared structure use for most registers. */
if (target->reg_cache) {
if (!target->reg_cache)
return;
if (target->reg_cache->reg_list) {
free(target->reg_cache->reg_list[0].arch_info);
/* Free the ones we allocated separately. */
for (unsigned i = GDB_REGNO_COUNT; i < target->reg_cache->num_regs; i++)
for (unsigned int i = GDB_REGNO_COUNT; i < target->reg_cache->num_regs; i++)
free(target->reg_cache->reg_list[i].arch_info);
for (unsigned int i = 0; i < target->reg_cache->num_regs; i++)
free(target->reg_cache->reg_list[i].value);
@ -494,9 +496,6 @@ static void riscv_free_registers(struct target *target)
free(target->reg_cache);
target->reg_cache = NULL;
}
}
static void free_reg_names(struct target *target);
static void free_custom_register_names(struct target *target)
{
@ -567,7 +566,6 @@ static void riscv_deinit_target(struct target *target)
free(entry);
}
free_reg_names(target);
free(target->arch_info);
target->arch_info = NULL;
@ -1779,8 +1777,8 @@ static int old_or_new_riscv_poll(struct target *target)
return riscv_openocd_poll(target);
}
static struct reg *get_reg_cache_entry(struct target *target,
unsigned int number)
static struct reg *get_reg_cache_entry(const struct target *target,
uint32_t number)
{
assert(target->reg_cache);
assert(target->reg_cache->reg_list);
@ -5036,7 +5034,7 @@ static int riscv_step_rtos_hart(struct target *target)
return ERROR_OK;
}
bool riscv_supports_extension(struct target *target, char letter)
bool riscv_supports_extension(const struct target *target, char letter)
{
RISCV_INFO(r);
unsigned num;
@ -5055,6 +5053,12 @@ unsigned riscv_xlen(const struct target *target)
return r->xlen;
}
unsigned int riscv_vlenb(const struct target *target)
{
RISCV_INFO(r);
return r->vlenb;
}
static void riscv_invalidate_register_cache(struct target *target)
{
/* Do not invalidate the register cache if it is not yet set up
@ -5627,7 +5631,7 @@ static void free_reg_names(struct target *target)
free_custom_register_names(target);
}
static void init_custom_csr_names(struct target *target)
static void init_custom_csr_names(const struct target *target)
{
RISCV_INFO(info);
range_list_t *entry;
@ -5644,7 +5648,7 @@ static void init_custom_csr_names(struct target *target)
}
}
const char *gdb_regno_name(struct target *target, enum gdb_regno regno)
const char *gdb_regno_name(const struct target *target, enum gdb_regno regno)
{
RISCV_INFO(info);
@ -5827,57 +5831,82 @@ static bool is_known_standard_csr(unsigned int csr_num)
return is_csr_in_buf[csr_num];
}
int riscv_init_registers(struct target *target)
bool reg_is_initialized(const struct reg *reg)
{
RISCV_INFO(info);
riscv_free_registers(target);
target->reg_cache = calloc(1, sizeof(*target->reg_cache));
if (!target->reg_cache) {
LOG_TARGET_ERROR(target, "Failed to allocate memory for target->reg_cache");
return ERROR_FAIL;
assert(reg);
if (!reg->feature) {
const struct reg default_reg = {0};
assert(!memcmp(&default_reg, reg, sizeof(*reg)));
return false;
}
target->reg_cache->name = "RISC-V Registers";
if (init_custom_register_names(&info->expose_custom, &info->custom_register_names) != ERROR_OK) {
LOG_TARGET_ERROR(target, "init_custom_register_names failed");
return ERROR_FAIL;
}
target->reg_cache->num_regs = GDB_REGNO_COUNT + info->custom_register_names.num_entries;
LOG_TARGET_DEBUG(target, "create register cache for %d registers",
target->reg_cache->num_regs);
target->reg_cache->reg_list =
calloc(target->reg_cache->num_regs, sizeof(struct reg));
if (!target->reg_cache->reg_list) {
LOG_TARGET_ERROR(target, "Failed to allocate memory for target->reg_cache->reg_list");
return ERROR_FAIL;
assert(reg->arch_info);
assert(((riscv_reg_info_t *)reg->arch_info)->target);
assert((!reg->exist && !reg->value) || (reg->exist && reg->value));
assert(reg->valid || !reg->dirty);
return true;
}
static struct reg_feature *gdb_regno_feature(uint32_t regno)
{
if (regno <= GDB_REGNO_XPR31 || regno == GDB_REGNO_PC) {
static struct reg_feature feature_cpu = {
.name = "org.gnu.gdb.riscv.cpu"
};
return &feature_cpu;
}
if ((regno >= GDB_REGNO_FPR0 && regno <= GDB_REGNO_FPR31) ||
regno == GDB_REGNO_FFLAGS ||
regno == GDB_REGNO_FRM ||
regno == GDB_REGNO_FCSR) {
static struct reg_feature feature_fpu = {
.name = "org.gnu.gdb.riscv.fpu"
};
static struct reg_feature feature_csr = {
.name = "org.gnu.gdb.riscv.csr"
};
return &feature_fpu;
}
if (regno >= GDB_REGNO_V0 && regno <= GDB_REGNO_V31) {
static struct reg_feature feature_vector = {
.name = "org.gnu.gdb.riscv.vector"
};
return &feature_vector;
}
if (regno >= GDB_REGNO_CSR0 && regno <= GDB_REGNO_CSR4095) {
static struct reg_feature feature_csr = {
.name = "org.gnu.gdb.riscv.csr"
};
return &feature_csr;
}
if (regno == GDB_REGNO_PRIV) {
static struct reg_feature feature_virtual = {
.name = "org.gnu.gdb.riscv.virtual"
};
return &feature_virtual;
}
assert(regno >= GDB_REGNO_COUNT);
static struct reg_feature feature_custom = {
.name = "org.gnu.gdb.riscv.custom"
};
return &feature_custom;
}
/* These types are built into gdb. */
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" };
static bool gdb_regno_caller_save(uint32_t regno)
{
return regno <= GDB_REGNO_XPR31 ||
regno == GDB_REGNO_PC ||
(regno >= GDB_REGNO_FPR0 && regno <= GDB_REGNO_FPR31);
}
static struct reg_data_type *gdb_regno_reg_data_type(const struct target *target,
uint32_t regno)
{
if (regno >= GDB_REGNO_FPR0 && regno <= GDB_REGNO_FPR31) {
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"
};
static struct reg_data_type_union_field single_double_fields[] = {
{"float", &type_ieee_single, single_double_fields + 1},
{"double", &type_ieee_double, NULL},
@ -5891,187 +5920,108 @@ int riscv_init_registers(struct target *target)
.type_class = REG_TYPE_CLASS_UNION,
{.reg_type_union = &single_double_union}
};
static struct reg_data_type type_uint8 = { .type = REG_TYPE_UINT8, .id = "uint8" };
static struct reg_data_type type_uint16 = { .type = REG_TYPE_UINT16, .id = "uint16" };
static struct reg_data_type type_uint32 = { .type = REG_TYPE_UINT32, .id = "uint32" };
static struct reg_data_type type_uint64 = { .type = REG_TYPE_UINT64, .id = "uint64" };
static struct reg_data_type type_uint128 = { .type = REG_TYPE_UINT128, .id = "uint128" };
/* This is roughly the XML we want:
* <vector id="bytes" type="uint8" count="16"/>
* <vector id="shorts" type="uint16" count="8"/>
* <vector id="words" type="uint32" count="4"/>
* <vector id="longs" type="uint64" count="2"/>
* <vector id="quads" type="uint128" count="1"/>
* <union id="riscv_vector_type">
* <field name="b" type="bytes"/>
* <field name="s" type="shorts"/>
* <field name="w" type="words"/>
* <field name="l" type="longs"/>
* <field name="q" type="quads"/>
* </union>
*/
info->vector_uint8.type = &type_uint8;
info->vector_uint8.count = info->vlenb;
info->type_uint8_vector.type = REG_TYPE_ARCH_DEFINED;
info->type_uint8_vector.id = "bytes";
info->type_uint8_vector.type_class = REG_TYPE_CLASS_VECTOR;
info->type_uint8_vector.reg_type_vector = &info->vector_uint8;
info->vector_uint16.type = &type_uint16;
info->vector_uint16.count = info->vlenb / 2;
info->type_uint16_vector.type = REG_TYPE_ARCH_DEFINED;
info->type_uint16_vector.id = "shorts";
info->type_uint16_vector.type_class = REG_TYPE_CLASS_VECTOR;
info->type_uint16_vector.reg_type_vector = &info->vector_uint16;
info->vector_uint32.type = &type_uint32;
info->vector_uint32.count = info->vlenb / 4;
info->type_uint32_vector.type = REG_TYPE_ARCH_DEFINED;
info->type_uint32_vector.id = "words";
info->type_uint32_vector.type_class = REG_TYPE_CLASS_VECTOR;
info->type_uint32_vector.reg_type_vector = &info->vector_uint32;
info->vector_uint64.type = &type_uint64;
info->vector_uint64.count = info->vlenb / 8;
info->type_uint64_vector.type = REG_TYPE_ARCH_DEFINED;
info->type_uint64_vector.id = "longs";
info->type_uint64_vector.type_class = REG_TYPE_CLASS_VECTOR;
info->type_uint64_vector.reg_type_vector = &info->vector_uint64;
info->vector_uint128.type = &type_uint128;
info->vector_uint128.count = info->vlenb / 16;
info->type_uint128_vector.type = REG_TYPE_ARCH_DEFINED;
info->type_uint128_vector.id = "quads";
info->type_uint128_vector.type_class = REG_TYPE_CLASS_VECTOR;
info->type_uint128_vector.reg_type_vector = &info->vector_uint128;
info->vector_fields[0].name = "b";
info->vector_fields[0].type = &info->type_uint8_vector;
if (info->vlenb >= 2) {
info->vector_fields[0].next = info->vector_fields + 1;
info->vector_fields[1].name = "s";
info->vector_fields[1].type = &info->type_uint16_vector;
} else {
info->vector_fields[0].next = NULL;
return riscv_supports_extension(target, 'D') ?
&type_ieee_single_double :
&type_ieee_single;
}
if (info->vlenb >= 4) {
info->vector_fields[1].next = info->vector_fields + 2;
info->vector_fields[2].name = "w";
info->vector_fields[2].type = &info->type_uint32_vector;
} else {
info->vector_fields[1].next = NULL;
if (regno >= GDB_REGNO_V0 && regno <= GDB_REGNO_V31) {
RISCV_INFO(info);
return &info->type_vector;
}
if (info->vlenb >= 8) {
info->vector_fields[2].next = info->vector_fields + 3;
info->vector_fields[3].name = "l";
info->vector_fields[3].type = &info->type_uint64_vector;
} else {
info->vector_fields[2].next = NULL;
}
if (info->vlenb >= 16) {
info->vector_fields[3].next = info->vector_fields + 4;
info->vector_fields[4].name = "q";
info->vector_fields[4].type = &info->type_uint128_vector;
} else {
info->vector_fields[3].next = NULL;
}
info->vector_fields[4].next = NULL;
info->vector_union.fields = info->vector_fields;
info->type_vector.type = REG_TYPE_ARCH_DEFINED;
info->type_vector.id = "riscv_vector";
info->type_vector.type_class = REG_TYPE_CLASS_UNION;
info->type_vector.reg_type_union = &info->vector_union;
riscv_reg_info_t *shared_reg_info = calloc(1, sizeof(riscv_reg_info_t));
if (!shared_reg_info)
return ERROR_FAIL;
shared_reg_info->target = target;
int custom_within_range = 0;
/* When gdb requests 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 reg_num = 0; reg_num < target->reg_cache->num_regs; reg_num++) {
struct reg *r = &target->reg_cache->reg_list[reg_num];
r->dirty = false;
r->valid = false;
r->exist = true;
r->type = &riscv_reg_arch_type;
r->arch_info = shared_reg_info;
r->number = reg_num;
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. */
r->name = gdb_regno_name(target, reg_num);
if (reg_num <= GDB_REGNO_XPR31) {
r->exist = reg_num <= GDB_REGNO_XPR15 ||
!riscv_supports_extension(target, 'E');
/* TODO: For now we fake that all GPRs exist because otherwise gdb
* doesn't work. */
r->exist = true;
r->caller_save = true;
r->group = "general";
r->feature = &feature_cpu;
} else if (reg_num == GDB_REGNO_PC) {
r->caller_save = true;
r->group = "general";
r->feature = &feature_cpu;
} else if (reg_num >= GDB_REGNO_FPR0 && reg_num <= GDB_REGNO_FPR31) {
r->caller_save = true;
if (riscv_supports_extension(target, 'D')) {
r->size = 64;
if (riscv_supports_extension(target, 'F'))
r->reg_data_type = &type_ieee_single_double;
else
r->reg_data_type = &type_ieee_double;
} else if (riscv_supports_extension(target, 'F')) {
r->reg_data_type = &type_ieee_single;
r->size = 32;
} else {
r->exist = false;
}
r->group = "float";
r->feature = &feature_fpu;
} else if (reg_num >= GDB_REGNO_CSR0 && reg_num <= GDB_REGNO_CSR4095) {
r->group = "csr";
r->feature = &feature_csr;
const unsigned int csr_num = reg_num - GDB_REGNO_CSR0;
if (!is_known_standard_csr(csr_num)) {
/* 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;
return NULL;
}
switch (csr_num) {
static const char *gdb_regno_group(uint32_t regno)
{
if (regno <= GDB_REGNO_XPR31 ||
regno == GDB_REGNO_PC ||
regno == GDB_REGNO_PRIV)
return "general";
if ((regno >= GDB_REGNO_FPR0 && regno <= GDB_REGNO_FPR31) ||
regno == GDB_REGNO_FFLAGS ||
regno == GDB_REGNO_FRM ||
regno == GDB_REGNO_FCSR)
return "float";
if (regno >= GDB_REGNO_CSR0 && regno <= GDB_REGNO_CSR4095)
return "csr";
if (regno >= GDB_REGNO_V0 && regno <= GDB_REGNO_V31)
return "vector";
assert(regno >= GDB_REGNO_COUNT);
return "custom";
}
uint32_t gdb_regno_size(const struct target *target, uint32_t regno)
{
if (regno >= GDB_REGNO_FPR0 && regno <= GDB_REGNO_FPR31)
return riscv_supports_extension(target, 'D') ? 64 : 32;
if (regno >= GDB_REGNO_V0 && regno <= GDB_REGNO_V31)
return riscv_vlenb(target) * 8;
if (regno == GDB_REGNO_PRIV)
return 8;
if (regno >= GDB_REGNO_CSR0 && regno <= GDB_REGNO_CSR4095) {
const unsigned int csr_number = regno - GDB_REGNO_CSR0;
switch (csr_number) {
case CSR_DCSR:
case CSR_MVENDORID:
case CSR_MCOUNTINHIBIT:
r->size = 32;
break;
case CSR_FCSR:
r->size = 32;
/* fall through */
case CSR_FFLAGS:
case CSR_FRM:
r->exist = riscv_supports_extension(target, 'F');
r->group = "float";
r->feature = &feature_fpu;
break;
case CSR_FCSR:
case CSR_SCOUNTEREN:
case CSR_MCOUNTEREN:
return 32;
}
}
return riscv_xlen(target);
}
static bool vlenb_exists(const struct target *target)
{
return riscv_vlenb(target) != 0;
}
static bool mtopi_exists(const struct target *target)
{
RISCV_INFO(info)
/* TODO: The naming is quite unfortunate here. `mtopi_readable` refers
* to how the fact that `mtopi` exists was deduced during examine.
*/
return info->mtopi_readable;
}
static bool mtopei_exists(const struct target *target)
{
RISCV_INFO(info)
/* TODO: The naming is quite unfortunate here. `mtopei_readable` refers
* to how the fact that `mtopei` exists was deduced during examine.
*/
return info->mtopei_readable;
}
static bool gdb_regno_exist(const struct target *target, uint32_t regno)
{
if (regno <= GDB_REGNO_XPR15 ||
regno == GDB_REGNO_PC ||
regno == GDB_REGNO_PRIV)
return true;
if (regno > GDB_REGNO_XPR15 && regno <= GDB_REGNO_XPR31)
return !riscv_supports_extension(target, 'E');
if (regno >= GDB_REGNO_FPR0 && regno <= GDB_REGNO_FPR31)
return riscv_supports_extension(target, 'F');
if (regno >= GDB_REGNO_V0 && regno <= GDB_REGNO_V31)
return vlenb_exists(target);
if (regno >= GDB_REGNO_COUNT)
return true;
assert(regno >= GDB_REGNO_CSR0 && regno <= GDB_REGNO_CSR4095);
const unsigned int csr_number = regno - GDB_REGNO_CSR0;
switch (csr_number) {
case CSR_FFLAGS:
case CSR_FRM:
case CSR_FCSR:
return riscv_supports_extension(target, 'F');
case CSR_SCOUNTEREN:
r->size = 32;
/* fall through */
case CSR_SSTATUS:
case CSR_STVEC:
case CSR_SIP:
@ -6081,16 +6031,14 @@ int riscv_init_registers(struct target *target)
case CSR_SCAUSE:
case CSR_STVAL:
case CSR_SATP:
r->exist = riscv_supports_extension(target, 'S');
break;
return riscv_supports_extension(target, 'S');
case CSR_MEDELEG:
case CSR_MIDELEG:
/* "In systems with only M-mode, or with both M-mode and
* U-mode but without U-mode trap support, the medeleg and
* mideleg registers should not exist." */
r->exist = riscv_supports_extension(target, 'S') ||
return riscv_supports_extension(target, 'S') ||
riscv_supports_extension(target, 'N');
break;
case CSR_PMPCFG1:
case CSR_PMPCFG3:
@ -6128,7 +6076,6 @@ int riscv_init_registers(struct target *target)
case CSR_HPMCOUNTER31H:
case CSR_MCYCLEH:
case CSR_MINSTRETH:
case CSR_MHPMCOUNTER3H:
case CSR_MHPMCOUNTER4H:
case CSR_MHPMCOUNTER5H:
case CSR_MHPMCOUNTER6H:
@ -6157,62 +6104,37 @@ int riscv_init_registers(struct target *target)
case CSR_MHPMCOUNTER29H:
case CSR_MHPMCOUNTER30H:
case CSR_MHPMCOUNTER31H:
r->exist = riscv_xlen(target) == 32;
break;
case CSR_VSTART:
case CSR_VXSAT:
case CSR_VXRM:
case CSR_VL:
case CSR_VCSR:
case CSR_VTYPE:
case CSR_VLENB:
r->exist = (info->vlenb > 0);
break;
return riscv_xlen(target) == 32;
case CSR_MCOUNTEREN:
r->size = 32;
r->exist = riscv_supports_extension(target, 'U');
break;
return riscv_supports_extension(target, 'U');
/* Interrupts M-Mode CSRs. */
case CSR_MISELECT:
case CSR_MIREG:
case CSR_MTOPI:
case CSR_MVIEN:
case CSR_MVIP:
r->exist = info->mtopi_readable;
break;
case CSR_MTOPEI:
r->exist = info->mtopei_readable;
break;
case CSR_MIEH:
case CSR_MIPH:
return mtopi_exists(target);
case CSR_MIDELEGH:
case CSR_MVIENH:
case CSR_MVIPH:
r->exist = info->mtopi_readable &&
return mtopi_exists(target) &&
riscv_xlen(target) == 32 &&
riscv_supports_extension(target, 'S');
break;
case CSR_MIEH:
case CSR_MIPH:
r->exist = info->mtopi_readable;
break;
/* Interrupts S-Mode CSRs. */
case CSR_SISELECT:
case CSR_SIREG:
case CSR_STOPI:
r->exist = info->mtopi_readable &&
return mtopi_exists(target) &&
riscv_supports_extension(target, 'S');
break;
case CSR_STOPEI:
r->exist = info->mtopei_readable &&
return mtopei_exists(target) &&
riscv_supports_extension(target, 'S');
break;
case CSR_SIEH:
case CSR_SIPH:
r->exist = info->mtopi_readable &&
return mtopi_exists(target) &&
riscv_xlen(target) == 32 &&
riscv_supports_extension(target, 'S');
break;
/* Interrupts Hypervisor and VS CSRs. */
case CSR_HVIEN:
case CSR_HVICTL:
@ -6221,13 +6143,11 @@ int riscv_init_registers(struct target *target)
case CSR_VSISELECT:
case CSR_VSIREG:
case CSR_VSTOPI:
r->exist = info->mtopi_readable &&
return mtopi_exists(target) &&
riscv_supports_extension(target, 'H');
break;
case CSR_VSTOPEI:
r->exist = info->mtopei_readable &&
return mtopei_exists(target) &&
riscv_supports_extension(target, 'H');
break;
case CSR_HIDELEGH:
case CSR_HVIENH:
case CSR_HVIPH:
@ -6235,83 +6155,304 @@ int riscv_init_registers(struct target *target)
case CSR_HVIPRIO2H:
case CSR_VSIEH:
case CSR_VSIPH:
r->exist = info->mtopi_readable &&
return mtopi_exists(target) &&
riscv_xlen(target) == 32 &&
riscv_supports_extension(target, 'H');
break;
}
return is_known_standard_csr(csr_number);
}
if (!r->exist && !list_empty(&info->expose_csr)) {
range_list_t *entry;
list_for_each_entry(entry, &info->expose_csr, list)
if (entry->low <= csr_num && csr_num <= entry->high) {
LOG_TARGET_DEBUG(target, "Exposing additional CSR %d (name=%s)",
csr_num, r->name);
r->exist = true;
break;
}
} else if (r->exist && !list_empty(&info->hide_csr)) {
range_list_t *entry;
list_for_each_entry(entry, &info->hide_csr, list)
if (entry->low <= csr_num && csr_num <= entry->high) {
LOG_TARGET_DEBUG(target, "Hiding CSR %d (name=%s).", csr_num, r->name);
r->hidden = true;
break;
}
}
} else if (reg_num == GDB_REGNO_PRIV) {
r->group = "general";
r->feature = &feature_virtual;
r->size = 8;
} else if (reg_num >= GDB_REGNO_V0 && reg_num <= GDB_REGNO_V31) {
r->caller_save = false;
r->exist = (info->vlenb > 0);
r->size = info->vlenb * 8;
r->group = "vector";
r->feature = &feature_vector;
r->reg_data_type = &info->type_vector;
} else if (reg_num >= GDB_REGNO_COUNT) {
/* Custom registers. */
const unsigned int custom_reg_index = reg_num - GDB_REGNO_COUNT;
static unsigned int gdb_regno_custom_number(const struct target *target, uint32_t regno)
{
if (regno < GDB_REGNO_COUNT)
return 0;
RISCV_INFO(info);
assert(!list_empty(&info->expose_custom));
assert(custom_reg_index < info->custom_register_names.num_entries);
range_list_t *range;
unsigned int regno_start = GDB_REGNO_COUNT;
unsigned int start = 0;
unsigned int offset = 0;
list_for_each_entry(range, &info->expose_custom, list) {
start = range->low;
assert(regno >= regno_start);
offset = regno - regno_start;
const unsigned int regs_in_range = range->high - range->low + 1;
if (offset < regs_in_range)
break;
regno_start += regs_in_range;
}
return start + offset;
}
range_list_t *range = list_first_entry(&info->expose_custom, range_list_t, list);
const unsigned int custom_number = range->low + custom_within_range;
r->group = "custom";
r->feature = &feature_custom;
r->arch_info = calloc(1, sizeof(riscv_reg_info_t));
if (!r->arch_info) {
LOG_ERROR("Failed to allocate memory for r->arch_info");
static int resize_reg(const struct target *target, uint32_t regno, bool exist,
uint32_t size)
{
struct reg *reg = get_reg_cache_entry(target, regno);
assert(reg_is_initialized(reg));
free(reg->value);
reg->size = size;
reg->exist = exist;
if (reg->exist) {
reg->value = malloc(DIV_ROUND_UP(reg->size, 8));
if (!reg->value) {
LOG_ERROR("Failed to allocate memory.");
return ERROR_FAIL;
}
((riscv_reg_info_t *)r->arch_info)->target = target;
((riscv_reg_info_t *)r->arch_info)->custom_number = custom_number;
char **reg_names = info->custom_register_names.reg_names;
r->name = reg_names[custom_reg_index];
LOG_TARGET_DEBUG(target, "Exposing additional custom register %d (name=%s)", reg_num, r->name);
custom_within_range++;
if (custom_within_range > range->high - range->low) {
custom_within_range = 0;
list_rotate_left(&info->expose_custom);
} else {
reg->value = NULL;
}
}
r->value = calloc(1, DIV_ROUND_UP(r->size, 8));
}
assert(reg_is_initialized(reg));
return ERROR_OK;
}
static int set_reg_exist(const struct target *target, uint32_t regno, bool exist)
{
const struct reg *reg = get_reg_cache_entry(target, regno);
assert(reg_is_initialized(reg));
return resize_reg(target, regno, exist, reg->size);
}
static int init_reg(struct target *target, uint32_t regno)
{
struct reg * const reg = get_reg_cache_entry(target, regno);
if (reg_is_initialized(reg))
return ERROR_OK;
reg->number = regno;
reg->type = &riscv_reg_arch_type;
reg->dirty = false;
reg->valid = false;
reg->hidden = false;
reg->name = gdb_regno_name(target, regno);
reg->feature = gdb_regno_feature(regno);
reg->caller_save = gdb_regno_caller_save(regno);
reg->reg_data_type = gdb_regno_reg_data_type(target, regno);
reg->group = gdb_regno_group(regno);
if (regno < GDB_REGNO_COUNT) {
RISCV_INFO(info);
reg->arch_info = &info->shared_reg_info;
} else {
reg->arch_info = calloc(1, sizeof(riscv_reg_info_t));
if (!reg->arch_info) {
LOG_ERROR("Out of memory.");
return ERROR_FAIL;
}
riscv_reg_info_t * const reg_arch_info = reg->arch_info;
reg_arch_info->target = target;
reg_arch_info->custom_number = gdb_regno_custom_number(target, regno);
}
return resize_reg(target, regno, gdb_regno_exist(target, regno),
gdb_regno_size(target, regno));
}
static int riscv_init_reg_cache(struct target *target)
{
RISCV_INFO(info);
riscv_free_registers(target);
target->reg_cache = calloc(1, sizeof(*target->reg_cache));
if (!target->reg_cache) {
LOG_TARGET_ERROR(target, "Failed to allocate memory for target->reg_cache");
return ERROR_FAIL;
}
target->reg_cache->name = "RISC-V Registers";
if (init_custom_register_names(&info->expose_custom, &info->custom_register_names) != ERROR_OK) {
LOG_TARGET_ERROR(target, "init_custom_register_names failed");
return ERROR_FAIL;
}
target->reg_cache->num_regs = GDB_REGNO_COUNT + info->custom_register_names.num_entries;
LOG_TARGET_DEBUG(target, "create register cache for %d registers",
target->reg_cache->num_regs);
target->reg_cache->reg_list =
calloc(target->reg_cache->num_regs, sizeof(struct reg));
if (!target->reg_cache->reg_list) {
LOG_TARGET_ERROR(target, "Failed to allocate memory for target->reg_cache->reg_list");
return ERROR_FAIL;
}
return ERROR_OK;
}
static void init_shared_reg_info(struct target *target)
{
RISCV_INFO(info);
info->shared_reg_info.target = target;
info->shared_reg_info.custom_number = 0;
}
static void init_vector_reg_type(const struct target *target)
{
RISCV_INFO(info);
static struct reg_data_type type_uint8 = { .type = REG_TYPE_UINT8, .id = "uint8" };
static struct reg_data_type type_uint16 = { .type = REG_TYPE_UINT16, .id = "uint16" };
static struct reg_data_type type_uint32 = { .type = REG_TYPE_UINT32, .id = "uint32" };
static struct reg_data_type type_uint64 = { .type = REG_TYPE_UINT64, .id = "uint64" };
static struct reg_data_type type_uint128 = { .type = REG_TYPE_UINT128, .id = "uint128" };
/* This is roughly the XML we want:
* <vector id="bytes" type="uint8" count="16"/>
* <vector id="shorts" type="uint16" count="8"/>
* <vector id="words" type="uint32" count="4"/>
* <vector id="longs" type="uint64" count="2"/>
* <vector id="quads" type="uint128" count="1"/>
* <union id="riscv_vector_type">
* <field name="b" type="bytes"/>
* <field name="s" type="shorts"/>
* <field name="w" type="words"/>
* <field name="l" type="longs"/>
* <field name="q" type="quads"/>
* </union>
*/
info->vector_uint8.type = &type_uint8;
info->vector_uint8.count = riscv_vlenb(target);
info->type_uint8_vector.type = REG_TYPE_ARCH_DEFINED;
info->type_uint8_vector.id = "bytes";
info->type_uint8_vector.type_class = REG_TYPE_CLASS_VECTOR;
info->type_uint8_vector.reg_type_vector = &info->vector_uint8;
info->vector_uint16.type = &type_uint16;
info->vector_uint16.count = riscv_vlenb(target) / 2;
info->type_uint16_vector.type = REG_TYPE_ARCH_DEFINED;
info->type_uint16_vector.id = "shorts";
info->type_uint16_vector.type_class = REG_TYPE_CLASS_VECTOR;
info->type_uint16_vector.reg_type_vector = &info->vector_uint16;
info->vector_uint32.type = &type_uint32;
info->vector_uint32.count = riscv_vlenb(target) / 4;
info->type_uint32_vector.type = REG_TYPE_ARCH_DEFINED;
info->type_uint32_vector.id = "words";
info->type_uint32_vector.type_class = REG_TYPE_CLASS_VECTOR;
info->type_uint32_vector.reg_type_vector = &info->vector_uint32;
info->vector_uint64.type = &type_uint64;
info->vector_uint64.count = riscv_vlenb(target) / 8;
info->type_uint64_vector.type = REG_TYPE_ARCH_DEFINED;
info->type_uint64_vector.id = "longs";
info->type_uint64_vector.type_class = REG_TYPE_CLASS_VECTOR;
info->type_uint64_vector.reg_type_vector = &info->vector_uint64;
info->vector_uint128.type = &type_uint128;
info->vector_uint128.count = riscv_vlenb(target) / 16;
info->type_uint128_vector.type = REG_TYPE_ARCH_DEFINED;
info->type_uint128_vector.id = "quads";
info->type_uint128_vector.type_class = REG_TYPE_CLASS_VECTOR;
info->type_uint128_vector.reg_type_vector = &info->vector_uint128;
info->vector_fields[0].name = "b";
info->vector_fields[0].type = &info->type_uint8_vector;
if (riscv_vlenb(target) >= 2) {
info->vector_fields[0].next = info->vector_fields + 1;
info->vector_fields[1].name = "s";
info->vector_fields[1].type = &info->type_uint16_vector;
} else {
info->vector_fields[0].next = NULL;
}
if (riscv_vlenb(target) >= 4) {
info->vector_fields[1].next = info->vector_fields + 2;
info->vector_fields[2].name = "w";
info->vector_fields[2].type = &info->type_uint32_vector;
} else {
info->vector_fields[1].next = NULL;
}
if (riscv_vlenb(target) >= 8) {
info->vector_fields[2].next = info->vector_fields + 3;
info->vector_fields[3].name = "l";
info->vector_fields[3].type = &info->type_uint64_vector;
} else {
info->vector_fields[2].next = NULL;
}
if (riscv_vlenb(target) >= 16) {
info->vector_fields[3].next = info->vector_fields + 4;
info->vector_fields[4].name = "q";
info->vector_fields[4].type = &info->type_uint128_vector;
} else {
info->vector_fields[3].next = NULL;
}
info->vector_fields[4].next = NULL;
info->vector_union.fields = info->vector_fields;
info->type_vector.type = REG_TYPE_ARCH_DEFINED;
info->type_vector.id = "riscv_vector";
info->type_vector.type_class = REG_TYPE_CLASS_UNION;
info->type_vector.reg_type_union = &info->vector_union;
}
static int expose_csrs(const struct target *target)
{
RISCV_INFO(info);
range_list_t *entry;
list_for_each_entry(entry, &info->expose_csr, list) {
assert(entry->low <= entry->high);
assert(entry->high <= GDB_REGNO_CSR4095 - GDB_REGNO_CSR0);
const enum gdb_regno last_regno = GDB_REGNO_CSR0 + entry->high;
for (enum gdb_regno regno = GDB_REGNO_CSR0 + entry->low;
regno <= last_regno; ++regno) {
struct reg * const reg = get_reg_cache_entry(target, regno);
const unsigned int csr_number = regno - GDB_REGNO_CSR0;
if (reg->exist) {
LOG_TARGET_WARNING(target,
"Not exposing CSR %d: register already exists.",
csr_number);
continue;
}
if (set_reg_exist(target, regno, /*exist*/ true) != ERROR_OK)
return ERROR_FAIL;
LOG_TARGET_DEBUG(target, "Exposing additional CSR %d (name=%s)",
csr_number, reg->name);
}
}
return ERROR_OK;
}
static void hide_csrs(const struct target *target)
{
RISCV_INFO(info);
range_list_t *entry;
list_for_each_entry(entry, &info->hide_csr, list) {
assert(entry->high <= GDB_REGNO_CSR4095 - GDB_REGNO_CSR0);
const enum gdb_regno last_regno = GDB_REGNO_CSR0 + entry->high;
for (enum gdb_regno regno = GDB_REGNO_CSR0 + entry->low;
regno <= last_regno; ++regno) {
struct reg * const reg = get_reg_cache_entry(target, regno);
const unsigned int csr_number = regno - GDB_REGNO_CSR0;
if (!reg->exist) {
LOG_TARGET_WARNING(target,
"Not hiding CSR %d: register does not exist.",
csr_number);
continue;
}
LOG_TARGET_DEBUG(target, "Hiding CSR %d (name=%s).", csr_number, reg->name);
reg->hidden = true;
}
}
}
int riscv_init_registers(struct target *target)
{
if (riscv_init_reg_cache(target) != ERROR_OK)
return ERROR_FAIL;
init_shared_reg_info(target);
init_vector_reg_type(target);
for (uint32_t reg_num = 0; reg_num < target->reg_cache->num_regs; reg_num++)
if (init_reg(target, reg_num) != ERROR_OK)
return ERROR_FAIL;
if (expose_csrs(target) != ERROR_OK)
return ERROR_FAIL;
hide_csrs(target);
return ERROR_OK;
}
void riscv_add_bscan_tunneled_scan(struct target *target, struct scan_field *field,
riscv_bscan_tunneled_scan_context_t *ctxt)

View File

@ -260,6 +260,9 @@ struct riscv_info {
COMMAND_HELPER((*print_info), struct target *target);
/* Storage for arch_info of non-custom registers. */
riscv_reg_info_t shared_reg_info;
/* Storage for vector register types. */
struct reg_data_type_vector vector_uint8;
struct reg_data_type_vector vector_uint16;
@ -390,11 +393,14 @@ int riscv_openocd_step(
/*** RISC-V Interface ***/
bool riscv_supports_extension(struct target *target, char letter);
bool riscv_supports_extension(const struct target *target, char letter);
/* Returns XLEN for the given (or current) hart. */
unsigned riscv_xlen(const struct target *target);
/* Returns VLENB for the given (or current) hart. */
unsigned int riscv_vlenb(const struct target *target);
/*** Support functions for the RISC-V 'RTOS', which provides multihart support
* without requiring multiple targets. */