riscv-openocd/src/target/riscv/riscv-013_reg.c

760 lines
21 KiB
C

// SPDX-License-Identifier: GPL-2.0-or-later
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "riscv-013_reg.h"
#include "field_helpers.h"
#include "riscv_reg.h"
#include "riscv_reg_impl.h"
#include "riscv-013.h"
#include "debug_defines.h"
#include <helper/time_support.h>
/* Start of register fetch/send definitions.
* - "reg_fetcher" (named "fetch_*") -- loads the value from target into reg
* cache entry. Modifies only "reg->value".
* - "reg_sender" (named "send_*") -- stores the value from "buf" to the
* target. Does not modify "reg" at all.
*/
typedef int (*reg_fetcher)(struct reg *reg);
typedef int (*reg_sender)(const struct reg *reg, const uint8_t *buf);
/* TODO: Introduce separate "fetch_*" and "send_*" functions for register
* classes (xregs, fregs, csrs).
*/
static int fetch_riscv013_reg(struct reg *reg)
{
riscv_reg_t value;
int res = riscv013_get_register(riscv_reg_impl_get_target(reg),
&value, reg->number);
if (res != ERROR_OK)
return res;
buf_set_u64(reg->value, 0, reg->size, value);
return res;
}
static int send_riscv013_reg(const struct reg *reg, const uint8_t *buf)
{
assert(reg->size <= 64);
const riscv_reg_t value = buf_get_u64(buf, 0, reg->size);
return riscv013_set_register(riscv_reg_impl_get_target(reg),
reg->number, value);
}
/* TODO: Inline "riscv013_*_register_buf" into "vreg" accessors.
*/
static int fetch_vreg(struct reg *reg)
{
return riscv013_get_register_buf(riscv_reg_impl_get_target(reg),
reg->value, reg->number);
}
static int send_vreg(const struct reg *reg, const uint8_t *buf)
{
return riscv013_set_register_buf(riscv_reg_impl_get_target(reg),
reg->number, reg->value);
}
/* End of register fetch/send definitions. */
/* Start of cache entry utils. */
static void set_cache_value(struct reg *reg, const uint8_t *buf)
{
assert(riscv_reg_impl_is_existing(reg));
buf_cpy(buf, reg->value, reg->size);
}
/**
* Note: There are only 3 reachable states for an item in the register cache:
* |: state :|: "reg->valid" :|: "reg->dirty" :|
* | "invalid" | false | false |
* | "valid" | true | false |
* | "dirty" | true | true |
*
* The state "reg->valid == dirty && reg->valid == false" should be unreacheable.
* To achieve this, "reg->valid" and "reg->dirty" are not assigned directly
* outside of "mark_<state>()" functions.
*/
static void mark_invalid(struct reg *reg)
{
assert(riscv_reg_impl_is_existing(reg));
reg->valid = false;
reg->dirty = false;
}
static void mark_clean(struct reg *reg)
{
assert(riscv_reg_impl_is_existing(reg));
assert(riscv_reg_impl_get_target(reg)->state == TARGET_HALTED);
reg->valid = true;
reg->dirty = false;
}
static void mark_dirty(struct reg *reg)
{
assert(riscv_reg_impl_is_existing(reg));
assert(riscv_reg_impl_get_target(reg)->state == TARGET_HALTED);
reg->valid = true;
reg->dirty = true;
}
static int access_prologue(const struct reg *reg, bool write)
{
assert(riscv_reg_impl_is_initialized(reg));
const struct target * const target = riscv_reg_impl_get_target(reg);
if (!reg->exist) {
LOG_TARGET_DEBUG(target, "Register %s does not exist",
reg->name);
return ERROR_FAIL;
}
LOG_TARGET_DEBUG(target, "%s %s (valid=%s, dirty=%s)",
write ? "Writing" : "Reading",
reg->name,
reg->valid ? "true" : "false",
reg->dirty ? "true" : "false");
return ERROR_OK;
}
static void access_epilogue(const struct reg *reg, bool write)
{
assert(riscv_reg_impl_is_existing(reg));
if (debug_level < LOG_LVL_DEBUG)
return;
char *value_hex = buf_to_hex_str(reg->value, reg->size);
LOG_TARGET_DEBUG(riscv_reg_impl_get_target(reg),
"%s 0x%s %s %s (valid=%s, dirty=%s)",
write ? "Wrote" : "Read",
value_hex,
write ? "to" : "from",
reg->name,
reg->valid ? "true" : "false",
reg->dirty ? "true" : "false");
free(value_hex);
}
static int noncaching_get(struct reg *reg, reg_fetcher fetch_reg)
{
assert(riscv_reg_impl_is_initialized(reg));
int res = access_prologue(reg, /*write*/ false);
if (res != ERROR_OK)
return res;
res = fetch_reg(reg);
mark_invalid(reg);
if (res != ERROR_OK)
return res;
access_epilogue(reg, /*write*/ false);
return ERROR_OK;
}
static int caching_get(struct reg *reg, reg_fetcher fetch_reg)
{
assert(riscv_reg_impl_is_initialized(reg));
int res = access_prologue(reg, /*write*/ false);
if (res != ERROR_OK)
return res;
if (reg->valid) {
LOG_TARGET_DEBUG(riscv_reg_impl_get_target(reg),
"Reading %s from cache", reg->name);
return ERROR_OK;
}
res = fetch_reg(reg);
if (res != ERROR_OK) {
mark_invalid(reg);
return res;
}
if (riscv_reg_impl_get_target(reg)->state == TARGET_HALTED)
mark_clean(reg);
else
mark_invalid(reg);
access_epilogue(reg, /*write*/ false);
return ERROR_OK;
}
static int noncaching_set(struct reg *reg, const uint8_t *buf,
reg_sender send_reg)
{
int res = access_prologue(reg, /*write*/ true);
if (res != ERROR_OK)
return res;
res = send_reg(reg, buf);
mark_invalid(reg);
if (res != ERROR_OK)
return res;
set_cache_value(reg, buf);
access_epilogue(reg, /*write*/ true);
return ERROR_OK;
}
static int caching_set(struct reg *reg, const uint8_t *buf,
reg_sender send_reg)
{
assert(riscv_reg_impl_is_initialized(reg));
const struct target * const target = riscv_reg_impl_get_target(reg);
if (target->state != TARGET_HALTED) {
LOG_TARGET_DEBUG(target, "Not caching the write to %s", reg->name);
return noncaching_set(reg, buf, send_reg);
}
int res = access_prologue(reg, /*write*/ true);
if (res != ERROR_OK)
return res;
if (reg->valid && buf_eq(reg->value, buf, reg->size)) {
LOG_TARGET_DEBUG(riscv_reg_impl_get_target(reg),
"Writing the same value to %s", reg->name);
} else {
LOG_TARGET_DEBUG(target, "Caching the write to %s", reg->name);
set_cache_value(reg, buf);
mark_dirty(reg);
}
access_epilogue(reg, /*write*/ true);
return ERROR_OK;
}
static int flush_reg(struct reg *reg, reg_sender send_reg)
{
if (!reg->dirty)
return ERROR_OK;
int res = access_prologue(reg, /*write*/ true);
if (res != ERROR_OK)
return res;
assert(reg->dirty);
assert(reg->valid);
const struct target * const target = riscv_reg_impl_get_target(reg);
if (target->state != TARGET_HALTED) {
LOG_TARGET_ERROR(target,
"BUG: register %s is dirty while the target is not halted",
reg->name);
return ERROR_TARGET_NOT_HALTED;
}
res = send_reg(reg, reg->value);
if (res != ERROR_OK) {
/* Register is not marked "invalid" here (like it's done on failure in
* "caching_set") since it is "dirty", i.e. the most recent value
* is currently in the cache. */
return res;
}
mark_clean(reg);
access_epilogue(reg, /*write*/ true);
return res;
}
/* End of cache entry utils. */
/* Start of "reg_arch_type" method definitions. */
static int riscv013_noncaching_get(struct reg *reg)
{
return noncaching_get(reg, fetch_riscv013_reg);
}
static int riscv013_caching_get(struct reg *reg)
{
return caching_get(reg, fetch_riscv013_reg);
}
static int riscv013_noncaching_set(struct reg *reg, uint8_t *buf)
{
return noncaching_set(reg, buf, send_riscv013_reg);
}
static int riscv013_caching_set(struct reg *reg, uint8_t *buf)
{
return caching_set(reg, buf, send_riscv013_reg);
}
static int riscv013_reg_flush(struct reg *reg)
{
return flush_reg(reg, send_riscv013_reg);
}
static int flush_noncacheable(struct reg *reg)
{
assert(!reg->dirty && "Non-cacheable register must never get dirty");
return ERROR_OK;
}
static int get_vreg(struct reg *reg)
{
assert(reg->number >= GDB_REGNO_V0 && reg->number <= GDB_REGNO_V31);
return caching_get(reg, fetch_vreg);
}
static int set_vreg(struct reg *reg, uint8_t *buf)
{
assert(reg->number >= GDB_REGNO_V0 && reg->number <= GDB_REGNO_V31);
return caching_set(reg, buf, send_vreg);
}
static int flush_vreg(struct reg *reg)
{
assert(reg->number >= GDB_REGNO_V0 && reg->number <= GDB_REGNO_V31);
return flush_reg(reg, send_vreg);
}
static int get_pc(struct reg *pc)
{
assert(pc->number == GDB_REGNO_PC);
assert(!pc->valid);
assert(!pc->dirty);
struct reg * const dpc =
riscv_reg_impl_cache_entry(riscv_reg_impl_get_target(pc),
GDB_REGNO_DPC);
int res = dpc->type->get(dpc);
if (res == ERROR_OK) {
/* "pc" is just an alias for "dpc". "dpc" just got updated, sync up "pc" */
set_cache_value(pc, dpc->value);
}
return res;
}
static int set_pc(struct reg *pc, uint8_t *buf)
{
assert(pc->number == GDB_REGNO_PC);
assert(!pc->valid);
assert(!pc->dirty);
struct reg * const dpc =
riscv_reg_impl_cache_entry(riscv_reg_impl_get_target(pc),
GDB_REGNO_DPC);
int res = dpc->type->set(dpc, buf);
if (res == ERROR_OK) {
/* "pc" is just an alias for "dpc". "dpc" just got updated, sync up "pc" */
set_cache_value(pc, dpc->value);
}
return res;
}
static int get_priv(struct reg *priv)
{
assert(priv->number == GDB_REGNO_PRIV);
assert(!priv->valid);
assert(!priv->dirty);
struct reg * const dcsr =
riscv_reg_impl_cache_entry(riscv_reg_impl_get_target(priv),
GDB_REGNO_DCSR);
assert(dcsr->size == 32);
int res = dcsr->type->get(dcsr);
if (res != ERROR_OK)
return res;
buf_set_u32(priv->value, VIRT_PRIV_PRV_OFFSET, VIRT_PRIV_PRV_LENGTH,
buf_get_u32(dcsr->value, CSR_DCSR_PRV_OFFSET, CSR_DCSR_PRV_LENGTH));
buf_set_u32(priv->value, VIRT_PRIV_V_OFFSET, VIRT_PRIV_V_LENGTH,
buf_get_u32(dcsr->value, CSR_DCSR_V_OFFSET, CSR_DCSR_V_LENGTH));
return ERROR_OK;
}
static int set_priv(struct reg *priv, uint8_t *priv_buf)
{
assert(priv->number == GDB_REGNO_PRIV);
assert(!priv->valid);
assert(!priv->dirty);
struct reg * const dcsr = riscv_reg_impl_cache_entry(riscv_reg_impl_get_target(priv),
GDB_REGNO_DCSR);
int res = dcsr->type->get(dcsr);
if (res != ERROR_OK)
return res;
assert(dcsr->size == 32);
uint8_t dcsr_buf[32 / 8];
buf_cpy(dcsr->value, dcsr_buf, dcsr->size);
buf_set_u32(dcsr_buf, CSR_DCSR_PRV_OFFSET, CSR_DCSR_PRV_LENGTH,
buf_get_u32(priv_buf, VIRT_PRIV_PRV_OFFSET, VIRT_PRIV_PRV_LENGTH));
buf_set_u32(dcsr_buf, CSR_DCSR_V_OFFSET, CSR_DCSR_V_LENGTH,
buf_get_u32(priv_buf, VIRT_PRIV_V_OFFSET, VIRT_PRIV_V_LENGTH));
res = dcsr->type->set(dcsr, dcsr_buf);
if (res == ERROR_OK)
set_cache_value(priv, priv_buf);
return res;
}
/* End of "reg_arch_type" method definitions. */
static const struct reg_arch_type zero_type = {
.get = riscv013_caching_get,
/* "zero" is read-only, but there is nothig wrong with attempting to
* write it (e.g. for the purpose of HW testing). */
.set = riscv013_noncaching_set,
.flush = riscv013_reg_flush,
};
static const struct reg_arch_type xreg_type = {
.get = riscv013_caching_get,
.set = riscv013_caching_set,
.flush = riscv013_reg_flush,
};
static const struct reg_arch_type freg_type = {
.get = riscv013_caching_get,
.set = riscv013_caching_set,
.flush = riscv013_reg_flush,
};
static const struct reg_arch_type vreg_type = {
.get = get_vreg,
.set = set_vreg,
.flush = flush_vreg
};
static const struct reg_arch_type pc_type = {
.get = get_pc,
.set = set_pc,
.flush = flush_noncacheable
};
static const struct reg_arch_type priv_type = {
.get = get_priv,
.set = set_priv,
.flush = flush_noncacheable
};
static const struct reg_arch_type warl_csr_type = {
.get = riscv013_caching_get,
.set = riscv013_noncaching_set,
.flush = riscv013_reg_flush
};
static const struct reg_arch_type noncacheable_type = {
.get = riscv013_noncaching_get,
.set = riscv013_noncaching_set,
.flush = flush_noncacheable
};
static const struct reg_arch_type *riscv013_gdb_regno_reg_type(uint32_t regno)
{
if (regno != GDB_REGNO_ZERO && regno <= GDB_REGNO_XPR31)
return &xreg_type;
if (regno >= GDB_REGNO_FPR0 && regno <= GDB_REGNO_FPR31) {
/* For now "freg_type" is the same as "xreg_type", but it will be
* changed soon */
return &freg_type;
}
if (regno >= GDB_REGNO_V0 && regno <= GDB_REGNO_V31)
return &vreg_type;
switch (regno) {
case GDB_REGNO_ZERO:
return &zero_type;
case GDB_REGNO_PC:
return &pc_type;
case GDB_REGNO_PRIV:
return &priv_type;
case GDB_REGNO_VLENB:
/* "vlenb" is read-only, but there is nothig wrong with attempting to
* write it (e.g. for the purpose of HW testing). */
case GDB_REGNO_DPC:
case GDB_REGNO_VSTART:
case GDB_REGNO_VXSAT:
case GDB_REGNO_VXRM:
case GDB_REGNO_VL:
case GDB_REGNO_VTYPE:
case GDB_REGNO_MISA:
case GDB_REGNO_DCSR:
case GDB_REGNO_DSCRATCH0:
case GDB_REGNO_MEPC:
case GDB_REGNO_SATP:
return &warl_csr_type;
/* TODO: https://github.com/riscv-collab/riscv-openocd/issues/1219 */
case GDB_REGNO_MSTATUS:
case GDB_REGNO_MCAUSE:
/* TODO: Sdtrig register should be WARL. Note that "tdata*" and "tinfo"
* values must get invalidated when "tselect" changes. */
case GDB_REGNO_TSELECT:
case GDB_REGNO_TDATA1:
case GDB_REGNO_TDATA2:
default:
return &noncacheable_type;
}
}
static int init_cache_entry(struct target *target, uint32_t 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));
}
/**
* 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)
{
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;
}
enum misa_mxl {
MISA_MXL_INVALID = 0,
MISA_MXL_32 = 1,
MISA_MXL_64 = 2,
MISA_MXL_128 = 3
};
unsigned int mxl_to_xlen(enum misa_mxl mxl)
{
switch (mxl) {
case MISA_MXL_32:
return 32;
case MISA_MXL_64:
return 64;
case MISA_MXL_128:
return 128;
case MISA_MXL_INVALID:
assert(0);
}
return 0;
}
static int check_misa_mxl(const struct target *target)
{
RISCV_INFO(r);
if (r->misa == 0) {
LOG_TARGET_WARNING(target, "'misa' register is read as zero."
"OpenOCD will not be able to determine some hart's capabilities.");
return ERROR_OK;
}
const unsigned int dxlen = riscv_xlen(target);
assert(dxlen <= sizeof(riscv_reg_t) * CHAR_BIT);
assert(dxlen >= 2);
const riscv_reg_t misa_mxl_mask = (riscv_reg_t)0x3 << (dxlen - 2);
const unsigned int mxl = get_field(r->misa, misa_mxl_mask);
if (mxl == MISA_MXL_INVALID) {
/* This is not an error!
* Imagine the platform that:
* - Has no abstract access to CSRs, so that CSRs are read
* through Program Buffer via "csrr" instruction.
* - Complies to v1.10 of the Priveleged Spec, so that misa.mxl
* is WARL and MXLEN may be chainged.
* https://github.com/riscv/riscv-isa-manual/commit/9a7dd2fe29011587954560b5dcf1875477b27ad8
* - DXLEN == MXLEN on reset == 64.
* In a following scenario:
* - misa.mxl was written, so that MXLEN is 32.
* - Debugger connects to the target.
* - Debugger observes DXLEN == 64.
* - Debugger reads misa:
* - Abstract access fails with "cmderr == not supported".
* - Access via Program Buffer involves reading "misa" to an
* "xreg" via "csrr", so that the "xreg" is filled with
* zero-extended value of "misa" (since "misa" is
* MXLEN-wide).
* - Debugger derives "misa.mxl" assumig "misa" is DXLEN-bit
* wide (64) while MXLEN is 32 and therefore erroneously
* assumes "misa.mxl" to be zero (invalid).
*/
LOG_TARGET_WARNING(target, "Detected DXLEN (%u) does not match "
"MXLEN: misa.mxl == 0, misa == 0x%" PRIx64 ".",
dxlen, r->misa);
return ERROR_OK;
}
const unsigned int mxlen = mxl_to_xlen(mxl);
if (dxlen < mxlen) {
LOG_TARGET_ERROR(target,
"MXLEN (%u) reported in misa.mxl field exceeds "
"the detected DXLEN (%u)",
mxlen, dxlen);
return ERROR_FAIL;
}
/* NOTE:
* The value of "misa.mxl" may stil not coincide with "xlen".
* "misa[26:XLEN-3]" bits are marked as WIRI in at least version 1.10
* of RISC-V Priveleged Spec. Therefore, if "xlen" is erroneously
* assumed to be 32 when it actually is 64, "mxl" will be read from
* this WIRI field and may be equal to "MISA_MXL_32" by coincidence.
* This is not an issue though from the version 1.11 onward, since
* "misa[26:XLEN-3]" became WARL and equal to 0.
*/
/* 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);
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 check_misa_mxl(target);
}
static int examine_mtopi(struct target *target)
{
/* Assume the registers exist */
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) {
res = riscv_reg_impl_set_exist(target, GDB_REGNO_MTOPI, false);
if (res != ERROR_OK)
return res;
return riscv_reg_impl_set_exist(target, GDB_REGNO_MTOPEI, false);
}
if (riscv_reg_get(target, &value, GDB_REGNO_MTOPEI) != ERROR_OK) {
LOG_TARGET_INFO(target, "S?aia detected without IMSIC");
return riscv_reg_impl_set_exist(target, GDB_REGNO_MTOPEI, false);
}
LOG_TARGET_INFO(target, "S?aia detected with IMSIC");
return ERROR_OK;
}
/**
* 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)
{
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;
res = examine_vlenb(target);
if (res != ERROR_OK)
return res;
riscv_reg_impl_init_vector_reg_type(target);
res = examine_mtopi(target);
if (res != ERROR_OK)
return res;
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);
return ERROR_OK;
}
int riscv013_reg_save_gpr(struct target *target, enum gdb_regno regid)
{
assert(regid >= GDB_REGNO_ZERO && regid <= GDB_REGNO_XPR31);
struct reg *reg = riscv_reg_impl_cache_entry(target, regid);
assert(riscv_reg_impl_is_initialized(reg));
RISCV_INFO(r);
if (target->state != TARGET_HALTED) {
LOG_TARGET_ERROR(target, "Can't save register %s on a hart that is not halted",
reg->name);
return ERROR_TARGET_NOT_HALTED;
}
LOG_TARGET_DEBUG(target, "Saving %s", reg->name);
int res = reg->type->get(reg);
if (res != ERROR_OK)
return res;
assert(reg->valid &&
"The register is cacheable, so the cache entry must be valid now.");
/* Mark the register as dirty so that its value is written back to the target
* during the next register cache flush. We assume that this function is called
* because the caller is about to mess with the underlying value of the
* register, and wants to make sure the value gets restored later. */
reg->dirty = true;
r->last_activity = timeval_ms();
return ERROR_OK;
}