riscv-openocd/src/target/riscv/riscv_reg.c

875 lines
24 KiB
C

// SPDX-License-Identifier: GPL-2.0-or-later
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gdb_regs.h"
#include "riscv.h"
#include "riscv_reg.h"
#include "riscv_reg_impl.h"
static const char * const default_reg_names[GDB_REGNO_COUNT] = {
[GDB_REGNO_ZERO] = "zero",
[GDB_REGNO_RA] = "ra",
[GDB_REGNO_SP] = "sp",
[GDB_REGNO_GP] = "gp",
[GDB_REGNO_TP] = "tp",
[GDB_REGNO_T0] = "t0",
[GDB_REGNO_T1] = "t1",
[GDB_REGNO_T2] = "t2",
[GDB_REGNO_FP] = "fp",
[GDB_REGNO_S1] = "s1",
[GDB_REGNO_A0] = "a0",
[GDB_REGNO_A1] = "a1",
[GDB_REGNO_A2] = "a2",
[GDB_REGNO_A3] = "a3",
[GDB_REGNO_A4] = "a4",
[GDB_REGNO_A5] = "a5",
[GDB_REGNO_A6] = "a6",
[GDB_REGNO_A7] = "a7",
[GDB_REGNO_S2] = "s2",
[GDB_REGNO_S3] = "s3",
[GDB_REGNO_S4] = "s4",
[GDB_REGNO_S5] = "s5",
[GDB_REGNO_S6] = "s6",
[GDB_REGNO_S7] = "s7",
[GDB_REGNO_S8] = "s8",
[GDB_REGNO_S9] = "s9",
[GDB_REGNO_S10] = "s10",
[GDB_REGNO_S11] = "s11",
[GDB_REGNO_T3] = "t3",
[GDB_REGNO_T4] = "t4",
[GDB_REGNO_T5] = "t5",
[GDB_REGNO_T6] = "t6",
[GDB_REGNO_PC] = "pc",
[GDB_REGNO_CSR0] = "csr0",
[GDB_REGNO_PRIV] = "priv",
[GDB_REGNO_FT0] = "ft0",
[GDB_REGNO_FT1] = "ft1",
[GDB_REGNO_FT2] = "ft2",
[GDB_REGNO_FT3] = "ft3",
[GDB_REGNO_FT4] = "ft4",
[GDB_REGNO_FT5] = "ft5",
[GDB_REGNO_FT6] = "ft6",
[GDB_REGNO_FT7] = "ft7",
[GDB_REGNO_FS0] = "fs0",
[GDB_REGNO_FS1] = "fs1",
[GDB_REGNO_FA0] = "fa0",
[GDB_REGNO_FA1] = "fa1",
[GDB_REGNO_FA2] = "fa2",
[GDB_REGNO_FA3] = "fa3",
[GDB_REGNO_FA4] = "fa4",
[GDB_REGNO_FA5] = "fa5",
[GDB_REGNO_FA6] = "fa6",
[GDB_REGNO_FA7] = "fa7",
[GDB_REGNO_FS2] = "fs2",
[GDB_REGNO_FS3] = "fs3",
[GDB_REGNO_FS4] = "fs4",
[GDB_REGNO_FS5] = "fs5",
[GDB_REGNO_FS6] = "fs6",
[GDB_REGNO_FS7] = "fs7",
[GDB_REGNO_FS8] = "fs8",
[GDB_REGNO_FS9] = "fs9",
[GDB_REGNO_FS10] = "fs10",
[GDB_REGNO_FS11] = "fs11",
[GDB_REGNO_FT8] = "ft8",
[GDB_REGNO_FT9] = "ft9",
[GDB_REGNO_FT10] = "ft10",
[GDB_REGNO_FT11] = "ft11",
#define DECLARE_CSR(csr_name, number)[(number) + GDB_REGNO_CSR0] = #csr_name,
#include "encoding.h"
#undef DECLARE_CSR
};
static void free_custom_register_names(struct target *target)
{
RISCV_INFO(info);
if (!info->custom_register_names.reg_names)
return;
for (unsigned int i = 0; i < info->custom_register_names.num_entries; i++)
free(info->custom_register_names.reg_names[i]);
free(info->custom_register_names.reg_names);
info->custom_register_names.reg_names = NULL;
}
static void free_reg_names(struct target *target)
{
RISCV_INFO(info);
if (!info->reg_names)
return;
for (unsigned int i = 0; i < GDB_REGNO_COUNT; ++i)
free(info->reg_names[i]);
free(info->reg_names);
info->reg_names = NULL;
free_custom_register_names(target);
}
static char *init_reg_name(const char *name)
{
const int size_buf = strlen(name) + 1;
char * const buf = calloc(size_buf, sizeof(char));
if (!buf) {
LOG_ERROR("Failed to allocate memory for a register name.");
return NULL;
}
strcpy(buf, name);
return buf;
}
static void init_custom_csr_names(const struct target *target)
{
RISCV_INFO(info);
range_list_t *entry;
list_for_each_entry(entry, &info->expose_csr, list) {
if (!entry->name)
continue;
assert(entry->low == entry->high);
const unsigned int regno = entry->low + GDB_REGNO_CSR0;
assert(regno <= GDB_REGNO_CSR4095);
if (info->reg_names[regno])
return;
info->reg_names[regno] = init_reg_name(entry->name);
}
}
static char *init_reg_name_with_prefix(const char *name_prefix,
unsigned int num)
{
const int size_buf = snprintf(NULL, 0, "%s%d", name_prefix, num) + 1;
char * const buf = calloc(size_buf, sizeof(char));
if (!buf) {
LOG_ERROR("Failed to allocate memory for a register name.");
return NULL;
}
int result = snprintf(buf, size_buf, "%s%d", name_prefix, num);
assert(result > 0 && result <= (size_buf - 1));
return buf;
}
const char *riscv_reg_gdb_regno_name(const struct target *target, enum gdb_regno regno)
{
RISCV_INFO(info);
if (regno >= GDB_REGNO_COUNT) {
assert(info->custom_register_names.reg_names);
assert(regno - GDB_REGNO_COUNT <= info->custom_register_names.num_entries);
return info->custom_register_names.reg_names[regno - GDB_REGNO_COUNT];
}
if (!info->reg_names)
info->reg_names = calloc(GDB_REGNO_COUNT, sizeof(char *));
if (info->reg_names[regno])
return info->reg_names[regno];
if (default_reg_names[regno])
return default_reg_names[regno];
if (regno <= GDB_REGNO_XPR31) {
info->reg_names[regno] = init_reg_name_with_prefix("x", regno - GDB_REGNO_ZERO);
return info->reg_names[regno];
}
if (regno <= GDB_REGNO_V31 && regno >= GDB_REGNO_V0) {
info->reg_names[regno] = init_reg_name_with_prefix("v", regno - GDB_REGNO_V0);
return info->reg_names[regno];
}
if (regno >= GDB_REGNO_CSR0 && regno <= GDB_REGNO_CSR4095) {
init_custom_csr_names(target);
info->reg_names[regno] = init_reg_name_with_prefix("csr", regno - GDB_REGNO_CSR0);
return info->reg_names[regno];
}
assert(!"Encountered uninitialized entry in reg_names table");
return NULL;
}
struct target *riscv_reg_impl_get_target(const struct reg *reg)
{
assert(riscv_reg_impl_is_initialized(reg));
return ((const riscv_reg_info_t *)reg->arch_info)->target;
}
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"
};
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;
}
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},
};
static struct reg_data_type_union single_double_union = {
.fields = single_double_fields
};
static struct reg_data_type type_ieee_single_double = {
.type = REG_TYPE_ARCH_DEFINED,
.id = "FPU_FD",
.type_class = REG_TYPE_CLASS_UNION,
{.reg_type_union = &single_double_union}
};
return riscv_supports_extension(target, 'D') ?
&type_ieee_single_double :
&type_ieee_single;
}
if (regno >= GDB_REGNO_V0 && regno <= GDB_REGNO_V31) {
RISCV_INFO(info);
return &info->type_vector;
}
return NULL;
}
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:
case CSR_FFLAGS:
case CSR_FRM:
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 is_known_standard_csr(unsigned int csr_num)
{
static const bool is_csr_in_buf[GDB_REGNO_CSR4095 - GDB_REGNO_CSR0 + 1] = {
#define DECLARE_CSR(csr_name, number)[number] = true,
#include "encoding.h"
#undef DECLARE_CSR
};
assert(csr_num < ARRAY_SIZE(is_csr_in_buf));
return is_csr_in_buf[csr_num];
}
bool riscv_reg_impl_gdb_regno_exist(const struct target *target, uint32_t regno)
{
switch (regno) {
case GDB_REGNO_VLENB:
case GDB_REGNO_MTOPI:
case GDB_REGNO_MTOPEI:
assert(false
&& "Existence of other registers is determined "
"depending on existence of these ones, so "
"whether these register exist or not should be "
"set explicitly.");
};
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_VSTART:
case CSR_VXSAT:
case CSR_VXRM:
case CSR_VL:
case CSR_VCSR:
case CSR_VTYPE:
return vlenb_exists(target);
case CSR_SCOUNTEREN:
case CSR_SSTATUS:
case CSR_STVEC:
case CSR_SIP:
case CSR_SIE:
case CSR_SSCRATCH:
case CSR_SEPC:
case CSR_SCAUSE:
case CSR_STVAL:
case CSR_SATP:
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." */
return riscv_supports_extension(target, 'S') ||
riscv_supports_extension(target, 'N');
case CSR_PMPCFG1:
case CSR_PMPCFG3:
case CSR_CYCLEH:
case CSR_TIMEH:
case CSR_INSTRETH:
case CSR_HPMCOUNTER3H:
case CSR_HPMCOUNTER4H:
case CSR_HPMCOUNTER5H:
case CSR_HPMCOUNTER6H:
case CSR_HPMCOUNTER7H:
case CSR_HPMCOUNTER8H:
case CSR_HPMCOUNTER9H:
case CSR_HPMCOUNTER10H:
case CSR_HPMCOUNTER11H:
case CSR_HPMCOUNTER12H:
case CSR_HPMCOUNTER13H:
case CSR_HPMCOUNTER14H:
case CSR_HPMCOUNTER15H:
case CSR_HPMCOUNTER16H:
case CSR_HPMCOUNTER17H:
case CSR_HPMCOUNTER18H:
case CSR_HPMCOUNTER19H:
case CSR_HPMCOUNTER20H:
case CSR_HPMCOUNTER21H:
case CSR_HPMCOUNTER22H:
case CSR_HPMCOUNTER23H:
case CSR_HPMCOUNTER24H:
case CSR_HPMCOUNTER25H:
case CSR_HPMCOUNTER26H:
case CSR_HPMCOUNTER27H:
case CSR_HPMCOUNTER28H:
case CSR_HPMCOUNTER29H:
case CSR_HPMCOUNTER30H:
case CSR_HPMCOUNTER31H:
case CSR_MCYCLEH:
case CSR_MINSTRETH:
case CSR_MHPMCOUNTER4H:
case CSR_MHPMCOUNTER5H:
case CSR_MHPMCOUNTER6H:
case CSR_MHPMCOUNTER7H:
case CSR_MHPMCOUNTER8H:
case CSR_MHPMCOUNTER9H:
case CSR_MHPMCOUNTER10H:
case CSR_MHPMCOUNTER11H:
case CSR_MHPMCOUNTER12H:
case CSR_MHPMCOUNTER13H:
case CSR_MHPMCOUNTER14H:
case CSR_MHPMCOUNTER15H:
case CSR_MHPMCOUNTER16H:
case CSR_MHPMCOUNTER17H:
case CSR_MHPMCOUNTER18H:
case CSR_MHPMCOUNTER19H:
case CSR_MHPMCOUNTER20H:
case CSR_MHPMCOUNTER21H:
case CSR_MHPMCOUNTER22H:
case CSR_MHPMCOUNTER23H:
case CSR_MHPMCOUNTER24H:
case CSR_MHPMCOUNTER25H:
case CSR_MHPMCOUNTER26H:
case CSR_MHPMCOUNTER27H:
case CSR_MHPMCOUNTER28H:
case CSR_MHPMCOUNTER29H:
case CSR_MHPMCOUNTER30H:
case CSR_MHPMCOUNTER31H:
return riscv_xlen(target) == 32;
case CSR_MCOUNTEREN:
return riscv_supports_extension(target, 'U');
/* Interrupts M-Mode CSRs. */
case CSR_MISELECT:
case CSR_MIREG:
case CSR_MVIEN:
case CSR_MVIP:
case CSR_MIEH:
case CSR_MIPH:
return mtopi_exists(target);
case CSR_MIDELEGH:
case CSR_MVIENH:
case CSR_MVIPH:
return mtopi_exists(target) &&
riscv_xlen(target) == 32 &&
riscv_supports_extension(target, 'S');
/* Interrupts S-Mode CSRs. */
case CSR_SISELECT:
case CSR_SIREG:
case CSR_STOPI:
return mtopi_exists(target) &&
riscv_supports_extension(target, 'S');
case CSR_STOPEI:
return mtopei_exists(target) &&
riscv_supports_extension(target, 'S');
case CSR_SIEH:
case CSR_SIPH:
return mtopi_exists(target) &&
riscv_xlen(target) == 32 &&
riscv_supports_extension(target, 'S');
/* Interrupts Hypervisor and VS CSRs. */
case CSR_HVIEN:
case CSR_HVICTL:
case CSR_HVIPRIO1:
case CSR_HVIPRIO2:
case CSR_VSISELECT:
case CSR_VSIREG:
case CSR_VSTOPI:
return mtopi_exists(target) &&
riscv_supports_extension(target, 'H');
case CSR_VSTOPEI:
return mtopei_exists(target) &&
riscv_supports_extension(target, 'H');
case CSR_HIDELEGH:
case CSR_HVIENH:
case CSR_HVIPH:
case CSR_HVIPRIO1H:
case CSR_HVIPRIO2H:
case CSR_VSIEH:
case CSR_VSIPH:
return mtopi_exists(target) &&
riscv_xlen(target) == 32 &&
riscv_supports_extension(target, 'H');
}
return is_known_standard_csr(csr_number);
}
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));
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;
}
struct reg *riscv_reg_impl_cache_entry(const struct target *target,
uint32_t number)
{
assert(target->reg_cache);
assert(target->reg_cache->reg_list);
assert(number < target->reg_cache->num_regs);
return &target->reg_cache->reg_list[number];
}
static int resize_reg(const struct target *target, uint32_t regno, bool exist,
uint32_t size)
{
struct reg *reg = riscv_reg_impl_cache_entry(target, regno);
assert(riscv_reg_impl_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;
}
} else {
reg->value = NULL;
}
assert(riscv_reg_impl_is_initialized(reg));
return ERROR_OK;
}
int riscv_reg_impl_set_exist(const struct target *target, uint32_t regno, bool exist)
{
const struct reg *reg = riscv_reg_impl_cache_entry(target, regno);
assert(riscv_reg_impl_is_initialized(reg));
return resize_reg(target, regno, exist, reg->size);
}
int riscv_reg_impl_init_cache_entry(struct target *target, uint32_t regno,
bool exist, const struct reg_arch_type *reg_type)
{
struct reg * const reg = riscv_reg_impl_cache_entry(target, regno);
if (riscv_reg_impl_is_initialized(reg))
return ERROR_OK;
reg->number = regno;
reg->type = reg_type;
reg->dirty = false;
reg->valid = false;
reg->hidden = false;
reg->name = riscv_reg_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, exist, gdb_regno_size(target, regno));
}
static int init_custom_register_names(struct list_head *expose_custom,
struct reg_name_table *custom_register_names)
{
unsigned int custom_regs_num = 0;
if (!list_empty(expose_custom)) {
range_list_t *entry;
list_for_each_entry(entry, expose_custom, list)
custom_regs_num += entry->high - entry->low + 1;
}
if (!custom_regs_num)
return ERROR_OK;
custom_register_names->reg_names = calloc(custom_regs_num, sizeof(char *));
if (!custom_register_names->reg_names) {
LOG_ERROR("Failed to allocate memory for custom_register_names->reg_names");
return ERROR_FAIL;
}
custom_register_names->num_entries = custom_regs_num;
char **reg_names = custom_register_names->reg_names;
range_list_t *range;
unsigned int next_custom_reg_index = 0;
list_for_each_entry(range, expose_custom, list) {
for (unsigned int custom_number = range->low; custom_number <= range->high; ++custom_number) {
if (range->name)
reg_names[next_custom_reg_index] = init_reg_name(range->name);
else
reg_names[next_custom_reg_index] =
init_reg_name_with_prefix("custom", custom_number);
if (!reg_names[next_custom_reg_index])
return ERROR_FAIL;
++next_custom_reg_index;
}
}
return ERROR_OK;
}
int riscv_reg_impl_init_cache(struct target *target)
{
RISCV_INFO(info);
riscv_reg_free_all(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;
}
int riscv_reg_impl_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 = riscv_reg_impl_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 (riscv_reg_impl_set_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;
}
void riscv_reg_impl_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 = riscv_reg_impl_cache_entry(target, regno);
const unsigned int csr_number = regno - GDB_REGNO_CSR0;
if (!reg->exist) {
LOG_TARGET_DEBUG(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;
}
}
}
void riscv_reg_free_all(struct target *target)
{
free_reg_names(target);
/* Free the shared structure use for most registers. */
if (!target->reg_cache)
return;
if (target->reg_cache->reg_list) {
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);
free(target->reg_cache->reg_list);
}
free(target->reg_cache);
target->reg_cache = NULL;
}
int riscv_reg_flush_all(struct target *target)
{
if (!target->reg_cache)
return ERROR_OK;
LOG_TARGET_DEBUG(target, "Flushing register cache");
/* Writing non-GPR registers may require progbuf execution, and some GPRs
* may become dirty in the process (e.g. S0, S1). For that reason, flush
* registers in reverse order, so that GPRs are flushed last.
*/
int res = ERROR_OK;
for (unsigned int number = target->reg_cache->num_regs; number-- > 0; ) {
struct reg *reg = riscv_reg_impl_cache_entry(target, number);
if (reg->type->flush(reg) != ERROR_OK)
res = ERROR_FAIL;
}
if (res == ERROR_OK)
LOG_TARGET_DEBUG(target, "Flush of register cache completed successfully.");
else
LOG_TARGET_DEBUG(target, "Flush of register cache failed.");
return res;
}
bool riscv_reg_cache_any_dirty(const struct target *target, int log_level)
{
bool any_dirty = false;
if (!target->reg_cache)
return any_dirty;
for (unsigned int number = 0; number < target->reg_cache->num_regs; ++number) {
const struct reg * const reg = riscv_reg_impl_cache_entry(target, number);
assert(riscv_reg_impl_is_initialized(reg));
if (reg->dirty) {
log_printf_lf(log_level, __FILE__, __LINE__, __func__,
"[%s] Register %s is dirty!", target_name(target), reg->name);
any_dirty = true;
}
}
return any_dirty;
}
void riscv_reg_cache_invalidate_all(struct target *target)
{
if (!target->reg_cache)
return;
LOG_TARGET_DEBUG(target, "Invalidating register cache.");
register_cache_invalidate(target->reg_cache);
}
/**
* This function is used to change the value of a register. The new value may
* be cached, and may not be written until the hart is resumed.
*/
int riscv_reg_set(struct target *target, enum gdb_regno regid,
riscv_reg_t value)
{
struct reg * const reg = riscv_reg_impl_cache_entry(target, regid);
assert(riscv_reg_impl_is_initialized(reg));
assert(reg->size <= sizeof(riscv_reg_t) * CHAR_BIT);
assert(reg->size <= 64);
uint8_t buf[64 / 8];
buf_set_u64(buf, 0, reg->size, value);
return reg->type->set(reg, buf);
}
/**
* This function is used to change the value of a register. The new value may
* be cached, but it will be written to hart immediately.
*/
int riscv_reg_write(struct target *target, enum gdb_regno regid,
riscv_reg_t value)
{
int res = riscv_reg_set(target, regid, value);
if (res != ERROR_OK)
return res;
struct reg * const reg = riscv_reg_impl_cache_entry(target, regid);
return reg->type->flush(reg);
}
/**
* This function is used to get the value of a register. If possible, the value
* in cache will be updated.
*/
int riscv_reg_get(struct target *target, riscv_reg_t *value,
enum gdb_regno regid)
{
assert(value);
struct reg * const reg = riscv_reg_impl_cache_entry(target, regid);
assert(riscv_reg_impl_is_initialized(reg));
assert(reg->size <= sizeof(riscv_reg_t) * CHAR_BIT);
assert(reg->size <= 64);
int res = reg->type->get(reg);
if (res != ERROR_OK)
return res;
*value = buf_get_u64(reg->value, 0, reg->size);
return ERROR_OK;
}