Merge pull request #1101 from en-sc/en-sc/ref-reg-examine

target/riscv: reg cache entry is initialized before access
This commit is contained in:
Evgeniy Naydanov 2024-08-16 17:04:24 +03:00 committed by GitHub
commit e4d6885eb5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 238 additions and 121 deletions

View File

@ -38,7 +38,9 @@ static const struct reg_arch_type *riscv011_gdb_regno_reg_type(uint32_t regno)
static int riscv011_init_reg(struct target *target, uint32_t regno)
{
return riscv_reg_impl_init_one(target, regno, riscv011_gdb_regno_reg_type(regno));
return riscv_reg_impl_init_cache_entry(target, regno,
riscv_reg_impl_gdb_regno_exist(target, regno),
riscv011_gdb_regno_reg_type(regno));
}
int riscv011_reg_init_all(struct target *target)

View File

@ -727,7 +727,7 @@ clear_cmderr:
return res;
}
static int execute_abstract_command(struct target *target, uint32_t command,
int riscv013_execute_abstract_command(struct target *target, uint32_t command,
uint32_t *cmderr)
{
assert(cmderr);
@ -863,7 +863,7 @@ static int write_abstract_arg(struct target *target, unsigned index,
/**
* @par size in bits
*/
static uint32_t access_register_command(struct target *target, uint32_t number,
uint32_t riscv013_access_register_command(struct target *target, uint32_t number,
unsigned size, uint32_t flags)
{
uint32_t command = set_field(0, DM_COMMAND_CMDTYPE, 0);
@ -920,11 +920,11 @@ static int register_read_abstract_with_size(struct target *target,
if (number >= GDB_REGNO_V0 && number <= GDB_REGNO_V31)
return ERROR_FAIL;
uint32_t command = access_register_command(target, number, size,
uint32_t command = riscv013_access_register_command(target, number, size,
AC_ACCESS_REGISTER_TRANSFER);
uint32_t cmderr;
int result = execute_abstract_command(target, command, &cmderr);
int result = riscv013_execute_abstract_command(target, command, &cmderr);
if (result != ERROR_OK) {
if (cmderr == CMDERR_NOT_SUPPORTED) {
if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) {
@ -969,7 +969,7 @@ static int register_write_abstract(struct target *target, enum gdb_regno number,
return ERROR_FAIL;
const unsigned int size_bits = register_size(target, number);
const uint32_t command = access_register_command(target, number, size_bits,
const uint32_t command = riscv013_access_register_command(target, number, size_bits,
AC_ACCESS_REGISTER_TRANSFER |
AC_ACCESS_REGISTER_WRITE);
LOG_DEBUG_REG(target, AC_ACCESS_REGISTER, command);
@ -2112,70 +2112,9 @@ static int examine(struct target *target)
* program buffer. */
r->progbuf_size = info->progbufsize;
result = register_read_abstract_with_size(target, NULL, GDB_REGNO_S0, 64);
if (result == ERROR_OK)
r->xlen = 64;
else
r->xlen = 32;
/* Save s0 and s1. The register cache hasn't be initialized yet so we
* need to take care of this manually. */
uint64_t s0, s1;
if (register_read_abstract(target, &s0, GDB_REGNO_S0) != ERROR_OK) {
LOG_TARGET_ERROR(target, "Fatal: Failed to read s0.");
return ERROR_FAIL;
}
if (register_read_abstract(target, &s1, GDB_REGNO_S1) != ERROR_OK) {
LOG_TARGET_ERROR(target, "Fatal: Failed to read s1.");
return ERROR_FAIL;
}
if (register_read_direct(target, &r->misa, GDB_REGNO_MISA)) {
LOG_TARGET_ERROR(target, "Fatal: Failed to read MISA.");
return ERROR_FAIL;
}
uint64_t value;
if (register_read_direct(target, &value, GDB_REGNO_VLENB) != ERROR_OK) {
if (riscv_supports_extension(target, 'V'))
LOG_TARGET_WARNING(target, "Couldn't read vlenb; vector register access won't work.");
r->vlenb = 0;
} else {
r->vlenb = value;
LOG_TARGET_INFO(target, "Vector support with vlenb=%d", r->vlenb);
}
if (register_read_direct(target, &value, GDB_REGNO_MTOPI) == ERROR_OK) {
r->mtopi_readable = true;
if (register_read_direct(target, &value, GDB_REGNO_MTOPEI) == ERROR_OK) {
LOG_TARGET_INFO(target, "S?aia detected with IMSIC");
r->mtopei_readable = true;
} else {
r->mtopei_readable = false;
LOG_TARGET_INFO(target, "S?aia detected without IMSIC");
}
} else {
r->mtopi_readable = false;
}
/* Display this as early as possible to help people who are using
* really slow simulators. */
LOG_TARGET_DEBUG(target, " XLEN=%d, misa=0x%" PRIx64, r->xlen, r->misa);
/* Restore s0 and s1. */
if (register_write_direct(target, GDB_REGNO_S0, s0) != ERROR_OK) {
LOG_TARGET_ERROR(target, "Fatal: Failed to write back s0.");
return ERROR_FAIL;
}
if (register_write_direct(target, GDB_REGNO_S1, s1) != ERROR_OK) {
LOG_TARGET_ERROR(target, "Fatal: Failed to write back s1.");
return ERROR_FAIL;
}
/* Now init registers based on what we discovered. */
if (riscv013_reg_init_all(target) != ERROR_OK)
return ERROR_FAIL;
result = riscv013_reg_examine_all(target);
if (result != ERROR_OK)
return result;
if (set_dcsr_ebreak(target, false) != ERROR_OK)
return ERROR_FAIL;
@ -3651,7 +3590,7 @@ static int read_memory_abstract(struct target *target, target_addr_t address,
/* Execute the command */
uint32_t cmderr;
result = execute_abstract_command(target, command, &cmderr);
result = riscv013_execute_abstract_command(target, command, &cmderr);
/* TODO: we need to modify error handling here. */
/* NOTE: in case of timeout cmderr is set to CMDERR_NONE */
@ -3673,7 +3612,7 @@ static int read_memory_abstract(struct target *target, target_addr_t address,
} else {
/* Try the same access but with postincrement disabled. */
command = access_memory_command(target, false, width, false, false);
result = execute_abstract_command(target, command, &cmderr);
result = riscv013_execute_abstract_command(target, command, &cmderr);
if (result == ERROR_OK) {
LOG_TARGET_DEBUG(target, "aampostincrement is not supported on this target.");
info->has_aampostincrement = YNM_NO;
@ -3744,7 +3683,7 @@ static int write_memory_abstract(struct target *target, target_addr_t address,
/* Execute the command */
uint32_t cmderr;
result = execute_abstract_command(target, command, &cmderr);
result = riscv013_execute_abstract_command(target, command, &cmderr);
/* TODO: we need to modify error handling here. */
/* NOTE: in case of timeout cmderr is set to CMDERR_NONE */
@ -3766,7 +3705,7 @@ static int write_memory_abstract(struct target *target, target_addr_t address,
} else {
/* Try the same access but with postincrement disabled. */
command = access_memory_command(target, false, width, false, true);
result = execute_abstract_command(target, command, &cmderr);
result = riscv013_execute_abstract_command(target, command, &cmderr);
if (result == ERROR_OK) {
LOG_TARGET_DEBUG(target, "aampostincrement is not supported on this target.");
info->has_aampostincrement = YNM_NO;
@ -3812,11 +3751,11 @@ static int read_memory_progbuf_inner_startup(struct target *target,
/* AC_ACCESS_REGISTER_POSTEXEC is used to trigger first stage of the
* pipeline (memory -> s1) whenever this command is executed.
*/
const uint32_t startup_command = access_register_command(target,
const uint32_t startup_command = riscv013_access_register_command(target,
GDB_REGNO_S1, riscv_xlen(target),
AC_ACCESS_REGISTER_TRANSFER | AC_ACCESS_REGISTER_POSTEXEC);
uint32_t cmderr;
if (execute_abstract_command(target, startup_command, &cmderr) != ERROR_OK)
if (riscv013_execute_abstract_command(target, startup_command, &cmderr) != ERROR_OK)
return ERROR_FAIL;
/* TODO: we need to modify error handling here. */
/* NOTE: in case of timeout cmderr is set to CMDERR_NONE */
@ -4299,11 +4238,11 @@ static int read_memory_progbuf_inner_one(struct target *target,
if (write_abstract_arg(target, 0, access.target_address, riscv_xlen(target))
!= ERROR_OK)
return ERROR_FAIL;
uint32_t command = access_register_command(target, GDB_REGNO_S1,
uint32_t command = riscv013_access_register_command(target, GDB_REGNO_S1,
riscv_xlen(target), AC_ACCESS_REGISTER_WRITE |
AC_ACCESS_REGISTER_TRANSFER | AC_ACCESS_REGISTER_POSTEXEC);
uint32_t cmderr;
if (execute_abstract_command(target, command, &cmderr) != ERROR_OK)
if (riscv013_execute_abstract_command(target, command, &cmderr) != ERROR_OK)
return ERROR_FAIL;
return read_word_from_s1(target, access, 0);
@ -4642,14 +4581,14 @@ static int write_memory_progbuf_startup(struct target *target, target_addr_t *ad
/* Write and execute command that moves the value from data0 [, data1]
* into S1 and executes program buffer. */
uint32_t command = access_register_command(target,
uint32_t command = riscv013_access_register_command(target,
GDB_REGNO_S1, riscv_xlen(target),
AC_ACCESS_REGISTER_POSTEXEC |
AC_ACCESS_REGISTER_TRANSFER |
AC_ACCESS_REGISTER_WRITE);
uint32_t cmderr;
if (execute_abstract_command(target, command, &cmderr) != ERROR_OK)
if (riscv013_execute_abstract_command(target, command, &cmderr) != ERROR_OK)
return ERROR_FAIL;
log_memory_access64(*address_p, value, size, /*is_read*/ false);
@ -5320,7 +5259,7 @@ static int riscv013_execute_progbuf(struct target *target, uint32_t *cmderr)
run_program = set_field(run_program, AC_ACCESS_REGISTER_TRANSFER, 0);
run_program = set_field(run_program, AC_ACCESS_REGISTER_REGNO, 0x1000);
return execute_abstract_command(target, run_program, cmderr);
return riscv013_execute_abstract_command(target, run_program, cmderr);
}
static void riscv013_fill_dmi_write(struct target *target, char *buf, uint64_t a, uint32_t d)

View File

@ -19,5 +19,9 @@ int riscv013_set_register(struct target *target, enum gdb_regno rid,
riscv_reg_t value);
int riscv013_set_register_buf(struct target *target, enum gdb_regno regno,
const uint8_t *value);
uint32_t riscv013_access_register_command(struct target *target, uint32_t number,
unsigned int size, uint32_t flags);
int riscv013_execute_abstract_command(struct target *target, uint32_t command,
uint32_t *cmderr);
#endif /* OPENOCD_TARGET_RISCV_RISCV_013_H */

View File

@ -9,6 +9,7 @@
#include "riscv_reg.h"
#include "riscv_reg_impl.h"
#include "riscv-013.h"
#include "debug_defines.h"
#include <helper/time_support.h>
static int riscv013_reg_get(struct reg *reg)
@ -85,33 +86,196 @@ static int riscv013_reg_set(struct reg *reg, uint8_t *buf)
static const struct reg_arch_type *riscv013_gdb_regno_reg_type(uint32_t regno)
{
static const struct reg_arch_type riscv011_reg_type = {
static const struct reg_arch_type riscv013_reg_type = {
.get = riscv013_reg_get,
.set = riscv013_reg_set
};
return &riscv011_reg_type;
return &riscv013_reg_type;
}
static int riscv013_init_reg(struct target *target, uint32_t regno)
static int init_cache_entry(struct target *target, uint32_t regno)
{
return riscv_reg_impl_init_one(target, regno, riscv013_gdb_regno_reg_type(regno));
struct reg * const reg = riscv_reg_impl_cache_entry(target, regno);
if (riscv_reg_impl_is_initialized(reg))
return ERROR_OK;
return riscv_reg_impl_init_cache_entry(target, regno,
riscv_reg_impl_gdb_regno_exist(target, regno),
riscv013_gdb_regno_reg_type(regno));
}
int riscv013_reg_init_all(struct target *target)
/**
* Some registers are optional (e.g. "misa"). For such registers it is first
* assumed they exist (via "assume_reg_exist()"), then the read is attempted
* (via the usual "riscv_reg_get()") and if the read fails, the register is
* marked as non-existing (via "riscv_reg_impl_set_exist()").
*/
static int assume_reg_exist(struct target *target, uint32_t regno)
{
if (riscv_reg_impl_init_cache(target) != ERROR_OK)
return riscv_reg_impl_init_cache_entry(target, regno,
/* exist */ true, riscv013_gdb_regno_reg_type(regno));
}
static int examine_xlen(struct target *target)
{
RISCV_INFO(r);
unsigned int cmderr;
const uint32_t command = riscv013_access_register_command(target,
GDB_REGNO_S0, /* size */ 64, AC_ACCESS_REGISTER_TRANSFER);
int res = riscv013_execute_abstract_command(target, command, &cmderr);
if (res == ERROR_OK) {
r->xlen = 64;
return ERROR_OK;
}
if (res == ERROR_TIMEOUT_REACHED)
return ERROR_FAIL;
r->xlen = 32;
return ERROR_OK;
}
static int examine_vlenb(struct target *target)
{
RISCV_INFO(r);
/* Reading "vlenb" requires "mstatus.vs" to be set, so "mstatus" should
* be accessible.*/
int res = init_cache_entry(target, GDB_REGNO_MSTATUS);
if (res != ERROR_OK)
return res;
res = assume_reg_exist(target, GDB_REGNO_VLENB);
if (res != ERROR_OK)
return res;
riscv_reg_t vlenb_val;
if (riscv_reg_get(target, &vlenb_val, GDB_REGNO_VLENB) != ERROR_OK) {
if (riscv_supports_extension(target, 'V'))
LOG_TARGET_WARNING(target, "Couldn't read vlenb; vector register access won't work.");
r->vlenb = 0;
return riscv_reg_impl_set_exist(target, GDB_REGNO_VLENB, false);
}
/* As defined by RISC-V V extension specification:
* https://github.com/riscv/riscv-v-spec/blob/2f68ef7256d6ec53e4d2bd7cb12862f406d64e34/v-spec.adoc?plain=1#L67-L72 */
const unsigned int vlen_max = 65536;
const unsigned int vlenb_max = vlen_max / 8;
if (vlenb_val > vlenb_max) {
LOG_TARGET_WARNING(target, "'vlenb == %" PRIu64
"' is greater than maximum allowed by specification (%u); vector register access won't work.",
vlenb_val, vlenb_max);
r->vlenb = 0;
return ERROR_OK;
}
assert(vlenb_max <= UINT_MAX);
r->vlenb = (unsigned int)vlenb_val;
LOG_TARGET_INFO(target, "Vector support with vlenb=%u", r->vlenb);
return ERROR_OK;
}
static int examine_misa(struct target *target)
{
RISCV_INFO(r);
int res = init_cache_entry(target, GDB_REGNO_MISA);
if (res != ERROR_OK)
return res;
res = riscv_reg_get(target, &r->misa, GDB_REGNO_MISA);
if (res != ERROR_OK)
return res;
return ERROR_OK;
}
static int examine_mtopi(struct target *target)
{
RISCV_INFO(r);
/* Assume the registers exist */
r->mtopi_readable = true;
r->mtopei_readable = true;
int res = assume_reg_exist(target, GDB_REGNO_MTOPI);
if (res != ERROR_OK)
return res;
res = assume_reg_exist(target, GDB_REGNO_MTOPEI);
if (res != ERROR_OK)
return res;
riscv_reg_t value;
if (riscv_reg_get(target, &value, GDB_REGNO_MTOPI) != ERROR_OK) {
r->mtopi_readable = false;
r->mtopei_readable = false;
} else if (riscv_reg_get(target, &value, GDB_REGNO_MTOPEI) != ERROR_OK) {
LOG_TARGET_INFO(target, "S?aia detected without IMSIC");
r->mtopei_readable = false;
} else {
LOG_TARGET_INFO(target, "S?aia detected with IMSIC");
}
res = riscv_reg_impl_set_exist(target, GDB_REGNO_MTOPI, r->mtopi_readable);
if (res != ERROR_OK)
return res;
return riscv_reg_impl_set_exist(target, GDB_REGNO_MTOPEI, r->mtopei_readable);
}
/**
* This function assumes target's DM to be initialized (target is able to
* access DMs registers, execute program buffer, etc.)
*/
int riscv013_reg_examine_all(struct target *target)
{
RISCV_INFO(r);
int res = riscv_reg_impl_init_cache(target);
if (res != ERROR_OK)
return res;
init_shared_reg_info(target);
assert(target->state == TARGET_HALTED);
res = examine_xlen(target);
if (res != ERROR_OK)
return res;
/* Reading CSRs may clobber "s0", "s1", so it should be possible to
* save them in cache. */
res = init_cache_entry(target, GDB_REGNO_S0);
if (res != ERROR_OK)
return res;
res = init_cache_entry(target, GDB_REGNO_S1);
if (res != ERROR_OK)
return res;
res = examine_misa(target);
if (res != ERROR_OK)
return res;
/* Display this as early as possible to help people who are using
* really slow simulators. */
LOG_TARGET_DEBUG(target, " XLEN=%d, misa=0x%" PRIx64, riscv_xlen(target), r->misa);
res = examine_vlenb(target);
if (res != ERROR_OK)
return res;
riscv_reg_impl_init_vector_reg_type(target);
for (uint32_t regno = 0; regno < target->reg_cache->num_regs; ++regno)
if (riscv013_init_reg(target, regno) != ERROR_OK)
return ERROR_FAIL;
res = examine_mtopi(target);
if (res != ERROR_OK)
return res;
if (riscv_reg_impl_expose_csrs(target) != ERROR_OK)
return ERROR_FAIL;
for (uint32_t regno = 0; regno < target->reg_cache->num_regs; ++regno) {
res = init_cache_entry(target, regno);
if (res != ERROR_OK)
return res;
}
res = riscv_reg_impl_expose_csrs(target);
if (res != ERROR_OK)
return res;
riscv_reg_impl_hide_csrs(target);

View File

@ -13,10 +13,11 @@
*/
/**
* Init initialize register cache. After this function all registers can be
* safely accessed via functions described here and in `riscv_reg.h`.
* This function assumes target is halted.
* After this function all registers can be safely accessed via functions
* described here and in `riscv_reg.h`.
*/
int riscv013_reg_init_all(struct target *target);
int riscv013_reg_examine_all(struct target *target);
/**
* This function is used to save the value of a register in cache. The register

View File

@ -376,8 +376,19 @@ static bool is_known_standard_csr(unsigned int csr_num)
return is_csr_in_buf[csr_num];
}
static bool gdb_regno_exist(const struct target *target, uint32_t regno)
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)
@ -403,7 +414,6 @@ static bool gdb_regno_exist(const struct target *target, uint32_t regno)
case CSR_VL:
case CSR_VCSR:
case CSR_VTYPE:
case CSR_VLENB:
return vlenb_exists(target);
case CSR_SCOUNTEREN:
case CSR_SSTATUS:
@ -599,14 +609,15 @@ static int resize_reg(const struct target *target, uint32_t regno, bool exist,
return ERROR_OK;
}
static int set_reg_exist(const struct target *target, uint32_t regno, bool exist)
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_one(struct target *target, uint32_t regno, const struct reg_arch_type *reg_type)
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))
@ -634,8 +645,7 @@ int riscv_reg_impl_init_one(struct target *target, uint32_t regno, const struct
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));
return resize_reg(target, regno, exist, gdb_regno_size(target, regno));
}
static int init_custom_register_names(struct list_head *expose_custom,
@ -725,7 +735,7 @@ int riscv_reg_impl_expose_csrs(const struct target *target)
csr_number);
continue;
}
if (set_reg_exist(target, regno, /*exist*/ true) != ERROR_OK)
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);
@ -834,15 +844,8 @@ static int riscv_set_or_write_register(struct target *target,
return riscv_set_or_write_register(target, GDB_REGNO_DCSR, dcsr, write_through);
}
if (!target->reg_cache) {
assert(!target_was_examined(target));
LOG_TARGET_DEBUG(target,
"No cache, writing to target: %s <- 0x%" PRIx64,
riscv_reg_gdb_regno_name(target, regid), value);
return riscv013_set_register(target, regid, value);
}
struct reg *reg = riscv_reg_impl_cache_entry(target, regid);
assert(riscv_reg_impl_is_initialized(reg));
if (!reg->exist) {
LOG_TARGET_DEBUG(target, "Register %s does not exist.", reg->name);
@ -935,21 +938,15 @@ int riscv_reg_get(struct target *target, riscv_reg_t *value,
RISCV_INFO(r);
assert(r);
if (r->dtm_version == DTM_DTMCS_VERSION_0_11)
return riscv013_get_register(target, value, regid);
return riscv011_get_register(target, value, regid);
keep_alive();
if (regid == GDB_REGNO_PC)
return riscv_reg_get(target, value, GDB_REGNO_DPC);
if (!target->reg_cache) {
assert(!target_was_examined(target));
LOG_TARGET_DEBUG(target, "No cache, reading %s from target",
riscv_reg_gdb_regno_name(target, regid));
return riscv013_get_register(target, value, regid);
}
struct reg *reg = riscv_reg_impl_cache_entry(target, regid);
assert(riscv_reg_impl_is_initialized(reg));
if (!reg->exist) {
LOG_TARGET_DEBUG(target, "Register %s does not exist.", reg->name);
return ERROR_FAIL;

View File

@ -13,7 +13,7 @@
* This file describes the helpers to use during register cache initialization
* of a RISC-V target. Each cache entry proceedes through the following stages:
* - not allocated before `riscv_reg_impl_init_cache()`
* - not initialized before the call to `riscv_reg_impl_init_one()` with appropriate regno.
* - not initialized before the call to `riscv_reg_impl_init_cache_entry()` with appropriate regno.
* - initialized until `riscv_reg_free_all()` is called.
*/
static inline bool riscv_reg_impl_is_initialized(const struct reg *reg)
@ -37,8 +37,18 @@ static inline bool riscv_reg_impl_is_initialized(const struct reg *reg)
int riscv_reg_impl_init_cache(struct target *target);
/** Initialize register. */
int riscv_reg_impl_init_one(struct target *target, uint32_t regno,
const struct reg_arch_type *reg_type);
int riscv_reg_impl_init_cache_entry(struct target *target, uint32_t regno,
bool exist, const struct reg_arch_type *reg_type);
/**
* For most registers, returns whether they exist or not.
* For some registers the "exist" bit should be set explicitly.
*/
bool riscv_reg_impl_gdb_regno_exist(const struct target *target, uint32_t regno);
/** Mark register as existing or not. */
int riscv_reg_impl_set_exist(const struct target *target,
uint32_t regno, bool exist);
/** Return the entry in the register cache of the target. */
struct reg *riscv_reg_impl_cache_entry(const struct target *target,