aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorEvgeniy Naydanov <evgeniy.naydanov@syntacore.com>2024-04-19 20:43:47 +0300
committerEvgeniy Naydanov <evgeniy.naydanov@syntacore.com>2024-07-02 10:15:20 +0300
commit3883b03aaafef5e1eff423664f7ea2e1d8f14ba4 (patch)
tree997ec7890005a72a065504c7c4f4e50f17b8524f /src
parentcb87885c0067840962b65d7ebb86de6949cb5967 (diff)
downloadriscv-openocd-3883b03aaafef5e1eff423664f7ea2e1d8f14ba4.zip
riscv-openocd-3883b03aaafef5e1eff423664f7ea2e1d8f14ba4.tar.gz
riscv-openocd-3883b03aaafef5e1eff423664f7ea2e1d8f14ba4.tar.bz2
target/riscv: separate register cache stuff into files
This commit creates file structure for register cache related functions. Specifically: * `riscv_reg.h` -- general interface to registers. Safe to use after register cache initialization is successful. * `riscv_reg_impl.h` -- helper functions to use while implementing register cache initialization. * `riscv_reg.c` -- definitions of functions from `riscv_reg.h` and `riscv_reg_impl.h`. * `riscv-011_reg.h` -- register cache interface specific to 0.11 targets. * `riscv-013_reg.h` -- register cache interface specific to 0.13+ targets. * `riscv-011/0.13.h` -- version-specific methods used to access registers. Will be extended as needed once other functionality (not related to register access) is separated (e.g. DM/DTM specific stuff). Change-Id: I7918f78d0d79b97188c5703efd0296660e529f2a Signed-off-by: Evgeniy Naydanov <evgeniy.naydanov@syntacore.com>
Diffstat (limited to 'src')
-rw-r--r--src/target/riscv/Makefile.am7
-rw-r--r--src/target/riscv/gdb_regs.h4
-rw-r--r--src/target/riscv/program.h2
-rw-r--r--src/target/riscv/riscv-011.c26
-rw-r--r--src/target/riscv/riscv-011.h15
-rw-r--r--src/target/riscv/riscv-011_reg.c61
-rw-r--r--src/target/riscv/riscv-011_reg.h19
-rw-r--r--src/target/riscv/riscv-013.c113
-rw-r--r--src/target/riscv/riscv-013.h23
-rw-r--r--src/target/riscv/riscv-013_reg.c162
-rw-r--r--src/target/riscv/riscv-013_reg.h31
-rw-r--r--src/target/riscv/riscv.c1434
-rw-r--r--src/target/riscv/riscv.h36
-rw-r--r--src/target/riscv/riscv_reg.c968
-rw-r--r--src/target/riscv/riscv_reg.h38
-rw-r--r--src/target/riscv/riscv_reg_impl.h214
-rw-r--r--src/target/riscv/riscv_semihosting.c11
17 files changed, 1678 insertions, 1486 deletions
diff --git a/src/target/riscv/Makefile.am b/src/target/riscv/Makefile.am
index 7f25eca..aa82f58 100644
--- a/src/target/riscv/Makefile.am
+++ b/src/target/riscv/Makefile.am
@@ -11,11 +11,18 @@ noinst_LTLIBRARIES += %D%/libriscv.la
%D%/opcodes.h \
%D%/program.h \
%D%/riscv.h \
+ %D%/riscv-011.h \
+ %D%/riscv-011_reg.h \
+ %D%/riscv-013.h \
+ %D%/riscv-013_reg.h \
%D%/batch.c \
%D%/program.c \
%D%/riscv-011.c \
+ %D%/riscv-011_reg.c \
%D%/riscv-013.c \
+ %D%/riscv-013_reg.c \
%D%/riscv.c \
+ %D%/riscv_reg.c \
%D%/riscv_semihosting.c \
%D%/debug_defines.c \
%D%/debug_reg_printer.c
diff --git a/src/target/riscv/gdb_regs.h b/src/target/riscv/gdb_regs.h
index 92c8cc5..d606f73 100644
--- a/src/target/riscv/gdb_regs.h
+++ b/src/target/riscv/gdb_regs.h
@@ -3,6 +3,8 @@
#ifndef TARGET__RISCV__GDB_REGS_H
#define TARGET__RISCV__GDB_REGS_H
+#include "encoding.h"
+
/* gdb's register list is defined in riscv_gdb_reg_names gdb/riscv-tdep.c in
* its source tree. We must interpret the numbers the same here. */
enum gdb_regno {
@@ -123,6 +125,4 @@ enum gdb_regno {
GDB_REGNO_COUNT
};
-const char *gdb_regno_name(const struct target *target, enum gdb_regno regno);
-
#endif
diff --git a/src/target/riscv/program.h b/src/target/riscv/program.h
index 361191a..93dbdbf 100644
--- a/src/target/riscv/program.h
+++ b/src/target/riscv/program.h
@@ -41,7 +41,7 @@ int riscv_program_write(struct riscv_program *program);
/* Executes a program, returning 0 if the program successfully executed. Note
* that this may cause registers to be saved or restored, which could result to
- * calls to things like riscv_save_register which itself could require a
+ * calls to things like riscv013_reg_save which itself could require a
* program to execute. That's OK, just make sure this eventually terminates.
* */
int riscv_program_exec(struct riscv_program *p, struct target *t);
diff --git a/src/target/riscv/riscv-011.c b/src/target/riscv/riscv-011.c
index df59a49..cd30a52 100644
--- a/src/target/riscv/riscv-011.c
+++ b/src/target/riscv/riscv-011.c
@@ -13,6 +13,8 @@
#include "config.h"
#endif
+#include "riscv-011.h"
+
#include "target/target.h"
#include "target/algorithm.h"
#include "target/target_type.h"
@@ -22,6 +24,8 @@
#include "target/breakpoints.h"
#include "helper/time_support.h"
#include "riscv.h"
+#include "riscv_reg.h"
+#include "riscv-011_reg.h"
#include "asm.h"
#include "gdb_regs.h"
#include "field_helpers.h"
@@ -210,8 +214,6 @@ typedef struct {
static int poll_target(struct target *target, bool announce);
static int riscv011_poll(struct target *target);
-static int get_register(struct target *target, riscv_reg_t *value,
- enum gdb_regno regid);
/*** Utility functions. ***/
@@ -1045,7 +1047,7 @@ static int read_remote_csr(struct target *target, uint64_t *value, uint32_t csr)
uint32_t exception = cache_get32(target, info->dramsize-1);
if (exception) {
LOG_WARNING("Got exception 0x%x when reading %s", exception,
- gdb_regno_name(target, GDB_REGNO_CSR0 + csr));
+ riscv_reg_gdb_regno_name(target, GDB_REGNO_CSR0 + csr));
*value = ~0;
return ERROR_FAIL;
}
@@ -1111,7 +1113,7 @@ static int execute_resume(struct target *target, bool step)
LOG_DEBUG("step=%d", step);
- if (riscv_flush_registers(target) != ERROR_OK)
+ if (riscv_reg_flush_all(target) != ERROR_OK)
return ERROR_FAIL;
maybe_write_tselect(target);
@@ -1225,7 +1227,7 @@ static int update_mstatus_actual(struct target *target)
/* Force reading the register. In that process mstatus_actual will be
* updated. */
riscv_reg_t mstatus;
- return get_register(target, &mstatus, GDB_REGNO_MSTATUS);
+ return riscv011_get_register(target, &mstatus, GDB_REGNO_MSTATUS);
}
/*** OpenOCD target functions. ***/
@@ -1247,7 +1249,7 @@ static int register_read(struct target *target, riscv_reg_t *value, int regnum)
uint32_t exception = cache_get32(target, info->dramsize-1);
if (exception) {
- LOG_WARNING("Got exception 0x%x when reading %s", exception, gdb_regno_name(target, regnum));
+ LOG_WARNING("Got exception 0x%x when reading %s", exception, riscv_reg_gdb_regno_name(target, regnum));
*value = ~0;
return ERROR_FAIL;
}
@@ -1322,14 +1324,14 @@ static int register_write(struct target *target, unsigned int number,
uint32_t exception = cache_get32(target, info->dramsize-1);
if (exception) {
LOG_WARNING("Got exception 0x%x when writing %s", exception,
- gdb_regno_name(target, number));
+ riscv_reg_gdb_regno_name(target, number));
return ERROR_FAIL;
}
return ERROR_OK;
}
-static int get_register(struct target *target, riscv_reg_t *value,
+int riscv011_get_register(struct target *target, riscv_reg_t *value,
enum gdb_regno regid)
{
riscv011_info_t *info = get_info(target);
@@ -1377,7 +1379,7 @@ static int get_register(struct target *target, riscv_reg_t *value,
/* This function is intended to handle accesses to registers through register
* cache. */
-static int set_register(struct target *target, enum gdb_regno regid,
+int riscv011_set_register(struct target *target, enum gdb_regno regid,
riscv_reg_t value)
{
assert(target->reg_cache);
@@ -1595,7 +1597,7 @@ static int examine(struct target *target)
}
/* Update register list to match discovered XLEN/supported extensions. */
- riscv_init_registers(target);
+ riscv011_reg_init_all(target);
info->never_halted = true;
@@ -2391,8 +2393,6 @@ static int init_target(struct command_context *cmd_ctx,
{
LOG_DEBUG("init");
RISCV_INFO(generic_info);
- generic_info->get_register = get_register;
- generic_info->set_register = set_register;
generic_info->read_memory = read_memory;
generic_info->authdata_read = &riscv011_authdata_read;
generic_info->authdata_write = &riscv011_authdata_write;
@@ -2404,7 +2404,7 @@ static int init_target(struct command_context *cmd_ctx,
/* Assume 32-bit until we discover the real value in examine(). */
generic_info->xlen = 32;
- riscv_init_registers(target);
+ riscv011_reg_init_all(target);
return ERROR_OK;
}
diff --git a/src/target/riscv/riscv-011.h b/src/target/riscv/riscv-011.h
new file mode 100644
index 0000000..8d1d06a
--- /dev/null
+++ b/src/target/riscv/riscv-011.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#ifndef OPENOCD_TARGET_RISCV_RISCV_011_H
+#define OPENOCD_TARGET_RISCV_RISCV_011_H
+
+#include "riscv.h"
+#include "gdb_regs.h"
+#include "target/target.h"
+
+int riscv011_get_register(struct target *target, riscv_reg_t *value,
+ enum gdb_regno regid);
+int riscv011_set_register(struct target *target, enum gdb_regno regid,
+ riscv_reg_t value);
+
+#endif /*OPENOCD_TARGET_RISCV_RISCV_011_H*/
diff --git a/src/target/riscv/riscv-011_reg.c b/src/target/riscv/riscv-011_reg.c
new file mode 100644
index 0000000..7f29064
--- /dev/null
+++ b/src/target/riscv/riscv-011_reg.c
@@ -0,0 +1,61 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "riscv-011_reg.h"
+
+#include "riscv_reg_impl.h"
+#include "riscv-011.h"
+
+static int riscv011_reg_get(struct reg *reg)
+{
+ struct target * const target = riscv_reg_impl_get_target(reg);
+ riscv_reg_t value;
+ const int result = riscv011_get_register(target, &value, reg->number);
+ if (result != ERROR_OK)
+ return result;
+ buf_set_u64(reg->value, 0, reg->size, value);
+ return ERROR_OK;
+}
+
+static int riscv011_reg_set(struct reg *reg, uint8_t *buf)
+{
+ const riscv_reg_t value = buf_get_u64(buf, 0, reg->size);
+ struct target * const target = riscv_reg_impl_get_target(reg);
+ return riscv011_set_register(target, reg->number, value);
+}
+
+static const struct reg_arch_type *riscv011_gdb_regno_reg_type(uint32_t regno)
+{
+ static const struct reg_arch_type riscv011_reg_type = {
+ .get = riscv011_reg_get,
+ .set = riscv011_reg_set
+ };
+ return &riscv011_reg_type;
+}
+
+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));
+}
+
+int riscv011_reg_init_all(struct target *target)
+{
+ if (riscv_reg_impl_init_cache(target) != ERROR_OK)
+ return ERROR_FAIL;
+
+ init_shared_reg_info(target);
+
+ for (uint32_t regno = 0; regno < target->reg_cache->num_regs; ++regno)
+ if (riscv011_init_reg(target, regno) != ERROR_OK)
+ return ERROR_FAIL;
+
+ if (riscv_reg_impl_expose_csrs(target) != ERROR_OK)
+ return ERROR_FAIL;
+
+ riscv_reg_impl_hide_csrs(target);
+
+ return ERROR_OK;
+}
diff --git a/src/target/riscv/riscv-011_reg.h b/src/target/riscv/riscv-011_reg.h
new file mode 100644
index 0000000..ee00c9b
--- /dev/null
+++ b/src/target/riscv/riscv-011_reg.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#ifndef OPENOCD_TARGET_RISCV_RISCV_REG_011_H
+#define OPENOCD_TARGET_RISCV_RISCV_REG_011_H
+
+#include "target/target.h"
+
+/**
+ * This file describes additional register cache interface available to the
+ * RISC-V Debug Specification v0.11 targets.
+ */
+
+/**
+ * Initialize register cache. After this function all registers can be
+ * safely accessed via functions described here and in `riscv_reg.h`.
+ */
+int riscv011_reg_init_all(struct target *target);
+
+#endif /*OPENOCD_TARGET_RISCV_RISCV_REG_011_H*/
diff --git a/src/target/riscv/riscv-013.c b/src/target/riscv/riscv-013.c
index 191a17e..4e30de9 100644
--- a/src/target/riscv/riscv-013.c
+++ b/src/target/riscv/riscv-013.c
@@ -23,6 +23,9 @@
#include "helper/time_support.h"
#include "helper/list.h"
#include "riscv.h"
+#include "riscv-013.h"
+#include "riscv_reg.h"
+#include "riscv-013_reg.h"
#include "debug_defines.h"
#include "rtos/rtos.h"
#include "program.h"
@@ -37,10 +40,6 @@ static int riscv013_step_or_resume_current_hart(struct target *target,
static int riscv013_clear_abstract_error(struct target *target);
/* Implementations of the functions in struct riscv_info. */
-static int riscv013_get_register(struct target *target,
- riscv_reg_t *value, enum gdb_regno rid);
-static int riscv013_set_register(struct target *target, enum gdb_regno regid,
- riscv_reg_t value);
static int dm013_select_hart(struct target *target, int hart_index);
static int riscv013_halt_prep(struct target *target);
static int riscv013_halt_go(struct target *target);
@@ -1050,7 +1049,7 @@ static uint32_t access_register_command(struct target *target, uint32_t number,
break;
default:
LOG_TARGET_ERROR(target, "%d-bit register %s not supported.",
- size, gdb_regno_name(target, number));
+ size, riscv_reg_gdb_regno_name(target, number));
assert(0);
}
@@ -1218,7 +1217,7 @@ static int examine_progbuf(struct target *target)
return ERROR_OK;
}
- if (riscv_save_register(target, GDB_REGNO_S0) != ERROR_OK)
+ if (riscv013_reg_save(target, GDB_REGNO_S0) != ERROR_OK)
return ERROR_FAIL;
struct riscv_program program;
@@ -1293,12 +1292,12 @@ static int prep_for_register_access(struct target *target,
}
LOG_TARGET_DEBUG(target, "Preparing mstatus to access %s",
- gdb_regno_name(target, regno));
+ riscv_reg_gdb_regno_name(target, regno));
assert(target->state == TARGET_HALTED &&
"The target must be halted to modify and then restore mstatus");
- if (riscv_get_register(target, orig_mstatus, GDB_REGNO_MSTATUS) != ERROR_OK)
+ if (riscv_reg_get(target, orig_mstatus, GDB_REGNO_MSTATUS) != ERROR_OK)
return ERROR_FAIL;
riscv_reg_t new_mstatus = *orig_mstatus;
@@ -1309,11 +1308,11 @@ static int prep_for_register_access(struct target *target,
new_mstatus = set_field(new_mstatus, field_mask, 1);
- if (riscv_write_register(target, GDB_REGNO_MSTATUS, new_mstatus) != ERROR_OK)
+ if (riscv_reg_write(target, GDB_REGNO_MSTATUS, new_mstatus) != ERROR_OK)
return ERROR_FAIL;
LOG_TARGET_DEBUG(target, "Prepared to access %s (mstatus=0x%" PRIx64 ")",
- gdb_regno_name(target, regno), new_mstatus);
+ riscv_reg_gdb_regno_name(target, regno), new_mstatus);
return ERROR_OK;
}
@@ -1325,7 +1324,7 @@ static int cleanup_after_register_access(struct target *target,
return ERROR_OK;
LOG_TARGET_DEBUG(target, "Restoring mstatus to 0x%" PRIx64, mstatus);
- return riscv_write_register(target, GDB_REGNO_MSTATUS, mstatus);
+ return riscv_reg_write(target, GDB_REGNO_MSTATUS, mstatus);
}
typedef enum {
@@ -1544,7 +1543,7 @@ static int fpr_read_progbuf(struct target *target, uint64_t *value,
const unsigned int freg = number - GDB_REGNO_FPR0;
- if (riscv_save_register(target, GDB_REGNO_S0) != ERROR_OK)
+ if (riscv013_reg_save(target, GDB_REGNO_S0) != ERROR_OK)
return ERROR_FAIL;
struct riscv_program program;
@@ -1574,7 +1573,7 @@ static int csr_read_progbuf(struct target *target, uint64_t *value,
assert(target->state == TARGET_HALTED);
assert(number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095);
- if (riscv_save_register(target, GDB_REGNO_S0) != ERROR_OK)
+ if (riscv013_reg_save(target, GDB_REGNO_S0) != ERROR_OK)
return ERROR_FAIL;
struct riscv_program program;
@@ -1602,7 +1601,7 @@ static int register_read_progbuf(struct target *target, uint64_t *value,
return csr_read_progbuf(target, value, number);
LOG_TARGET_ERROR(target, "Unexpected read of %s via program buffer.",
- gdb_regno_name(target, number));
+ riscv_reg_gdb_regno_name(target, number));
return ERROR_FAIL;
}
@@ -1642,7 +1641,7 @@ static int fpr_write_progbuf(struct target *target, enum gdb_regno number,
assert(number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31);
const unsigned int freg = number - GDB_REGNO_FPR0;
- if (riscv_save_register(target, GDB_REGNO_S0) != ERROR_OK)
+ if (riscv013_reg_save(target, GDB_REGNO_S0) != ERROR_OK)
return ERROR_FAIL;
struct riscv_program program;
@@ -1672,11 +1671,11 @@ static int vtype_write_progbuf(struct target *target, riscv_reg_t value)
{
assert(target->state == TARGET_HALTED);
- if (riscv_save_register(target, GDB_REGNO_S0) != ERROR_OK)
+ if (riscv013_reg_save(target, GDB_REGNO_S0) != ERROR_OK)
return ERROR_FAIL;
if (register_write_abstract(target, GDB_REGNO_S0, value) != ERROR_OK)
return ERROR_FAIL;
- if (riscv_save_register(target, GDB_REGNO_S1) != ERROR_OK)
+ if (riscv013_reg_save(target, GDB_REGNO_S1) != ERROR_OK)
return ERROR_FAIL;
struct riscv_program program;
@@ -1693,11 +1692,11 @@ static int vl_write_progbuf(struct target *target, riscv_reg_t value)
{
assert(target->state == TARGET_HALTED);
- if (riscv_save_register(target, GDB_REGNO_S0) != ERROR_OK)
+ if (riscv013_reg_save(target, GDB_REGNO_S0) != ERROR_OK)
return ERROR_FAIL;
if (register_write_abstract(target, GDB_REGNO_S0, value) != ERROR_OK)
return ERROR_FAIL;
- if (riscv_save_register(target, GDB_REGNO_S1) != ERROR_OK)
+ if (riscv013_reg_save(target, GDB_REGNO_S1) != ERROR_OK)
return ERROR_FAIL;
struct riscv_program program;
@@ -1716,7 +1715,7 @@ static int csr_write_progbuf(struct target *target, enum gdb_regno number,
assert(target->state == TARGET_HALTED);
assert(number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095);
- if (riscv_save_register(target, GDB_REGNO_S0) != ERROR_OK)
+ if (riscv013_reg_save(target, GDB_REGNO_S0) != ERROR_OK)
return ERROR_FAIL;
if (register_write_abstract(target, GDB_REGNO_S0, value) != ERROR_OK)
return ERROR_FAIL;
@@ -1748,7 +1747,7 @@ static int register_write_progbuf(struct target *target, enum gdb_regno number,
return csr_write_progbuf(target, number, value);
LOG_TARGET_ERROR(target, "Unexpected write to %s via program buffer.",
- gdb_regno_name(target, number));
+ riscv_reg_gdb_regno_name(target, number));
return ERROR_FAIL;
}
@@ -1760,7 +1759,7 @@ static int register_write_direct(struct target *target, enum gdb_regno number,
riscv_reg_t value)
{
LOG_TARGET_DEBUG(target, "Writing 0x%" PRIx64 " to %s", value,
- gdb_regno_name(target, number));
+ riscv_reg_gdb_regno_name(target, number));
if (target->state != TARGET_HALTED)
return register_write_abstract(target, number, value);
@@ -1778,7 +1777,7 @@ static int register_write_direct(struct target *target, enum gdb_regno number,
return ERROR_FAIL;
if (result == ERROR_OK)
- LOG_TARGET_DEBUG(target, "%s <- 0x%" PRIx64, gdb_regno_name(target, number),
+ LOG_TARGET_DEBUG(target, "%s <- 0x%" PRIx64, riscv_reg_gdb_regno_name(target, number),
value);
return result;
@@ -1788,7 +1787,7 @@ static int register_write_direct(struct target *target, enum gdb_regno number,
static int register_read_direct(struct target *target, riscv_reg_t *value,
enum gdb_regno number)
{
- LOG_TARGET_DEBUG(target, "Reading %s", gdb_regno_name(target, number));
+ LOG_TARGET_DEBUG(target, "Reading %s", riscv_reg_gdb_regno_name(target, number));
if (target->state != TARGET_HALTED)
return register_read_abstract(target, value, number);
@@ -1807,7 +1806,7 @@ static int register_read_direct(struct target *target, riscv_reg_t *value,
return ERROR_FAIL;
if (result == ERROR_OK)
- LOG_TARGET_DEBUG(target, "%s = 0x%" PRIx64, gdb_regno_name(target, number),
+ LOG_TARGET_DEBUG(target, "%s = 0x%" PRIx64, riscv_reg_gdb_regno_name(target, number),
*value);
return result;
@@ -1847,7 +1846,7 @@ static int set_dcsr_ebreak(struct target *target, bool step)
RISCV013_INFO(info);
riscv_reg_t original_dcsr, dcsr;
/* We want to twiddle some bits in the debug CSR so debugging works. */
- if (riscv_get_register(target, &dcsr, GDB_REGNO_DCSR) != ERROR_OK)
+ if (riscv_reg_get(target, &dcsr, GDB_REGNO_DCSR) != ERROR_OK)
return ERROR_FAIL;
original_dcsr = dcsr;
dcsr = set_field(dcsr, CSR_DCSR_STEP, step);
@@ -1857,7 +1856,7 @@ static int set_dcsr_ebreak(struct target *target, bool step)
dcsr = set_field(dcsr, CSR_DCSR_EBREAKVS, r->riscv_ebreaku && riscv_supports_extension(target, 'H'));
dcsr = set_field(dcsr, CSR_DCSR_EBREAKVU, r->riscv_ebreaku && riscv_supports_extension(target, 'H'));
if (dcsr != original_dcsr &&
- riscv_set_register(target, GDB_REGNO_DCSR, dcsr) != ERROR_OK)
+ riscv_reg_set(target, GDB_REGNO_DCSR, dcsr) != ERROR_OK)
return ERROR_FAIL;
info->dcsr_ebreak_is_set = true;
return ERROR_OK;
@@ -2332,7 +2331,7 @@ static int examine(struct target *target)
}
/* Now init registers based on what we discovered. */
- if (riscv_init_registers(target) != ERROR_OK)
+ if (riscv013_reg_init_all(target) != ERROR_OK)
return ERROR_FAIL;
if (set_dcsr_ebreak(target, false) != ERROR_OK)
@@ -2492,14 +2491,14 @@ static int try_set_vsew(struct target *target, unsigned int *debug_vsew)
/* Set standard element width to match XLEN, for vmv instruction to move
* the least significant bits into a GPR.
*/
- if (riscv_write_register(target, GDB_REGNO_VTYPE, encoded_vsew << 3) != ERROR_OK)
+ if (riscv_reg_write(target, GDB_REGNO_VTYPE, encoded_vsew << 3) != ERROR_OK)
return ERROR_FAIL;
if (encoded_vsew == 3 && r->vsew64_supported == YNM_MAYBE) {
/* Check that it's supported. */
riscv_reg_t vtype;
- if (riscv_get_register(target, &vtype, GDB_REGNO_VTYPE) != ERROR_OK)
+ if (riscv_reg_get(target, &vtype, GDB_REGNO_VTYPE) != ERROR_OK)
return ERROR_FAIL;
if (vtype >> (riscv_xlen(target) - 1)) {
r->vsew64_supported = YNM_NO;
@@ -2532,9 +2531,9 @@ static int prep_for_vector_access(struct target *target,
return ERROR_FAIL;
/* Save vtype and vl. */
- if (riscv_get_register(target, orig_vtype, GDB_REGNO_VTYPE) != ERROR_OK)
+ if (riscv_reg_get(target, orig_vtype, GDB_REGNO_VTYPE) != ERROR_OK)
return ERROR_FAIL;
- if (riscv_get_register(target, orig_vl, GDB_REGNO_VL) != ERROR_OK)
+ if (riscv_reg_get(target, orig_vl, GDB_REGNO_VL) != ERROR_OK)
return ERROR_FAIL;
if (try_set_vsew(target, debug_vsew) != ERROR_OK)
@@ -2543,22 +2542,22 @@ static int prep_for_vector_access(struct target *target,
* instruction, for the vslide1down instruction.
* Set it so the entire V register is updated. */
*debug_vl = DIV_ROUND_UP(r->vlenb * 8, *debug_vsew);
- return riscv_write_register(target, GDB_REGNO_VL, *debug_vl);
+ return riscv_reg_write(target, GDB_REGNO_VL, *debug_vl);
}
static int cleanup_after_vector_access(struct target *target,
riscv_reg_t mstatus, riscv_reg_t vtype, riscv_reg_t vl)
{
/* Restore vtype and vl. */
- if (riscv_write_register(target, GDB_REGNO_VTYPE, vtype) != ERROR_OK)
+ if (riscv_reg_write(target, GDB_REGNO_VTYPE, vtype) != ERROR_OK)
return ERROR_FAIL;
- if (riscv_write_register(target, GDB_REGNO_VL, vl) != ERROR_OK)
+ if (riscv_reg_write(target, GDB_REGNO_VL, vl) != ERROR_OK)
return ERROR_FAIL;
return cleanup_after_register_access(target, mstatus, GDB_REGNO_VL);
}
-static int riscv013_get_register_buf(struct target *target,
- uint8_t *value, enum gdb_regno regno)
+int riscv013_get_register_buf(struct target *target, uint8_t *value,
+ enum gdb_regno regno)
{
assert(regno >= GDB_REGNO_V0 && regno <= GDB_REGNO_V31);
@@ -2572,7 +2571,7 @@ static int riscv013_get_register_buf(struct target *target,
&debug_vl, &debug_vsew) != ERROR_OK)
return ERROR_FAIL;
- if (riscv_save_register(target, GDB_REGNO_S0) != ERROR_OK)
+ if (riscv013_reg_save(target, GDB_REGNO_S0) != ERROR_OK)
return ERROR_FAIL;
unsigned int vnum = regno - GDB_REGNO_V0;
@@ -2601,7 +2600,7 @@ static int riscv013_get_register_buf(struct target *target,
} else {
LOG_TARGET_ERROR(target,
"Failed to execute vmv/vslide1down while reading %s",
- gdb_regno_name(target, regno));
+ riscv_reg_gdb_regno_name(target, regno));
break;
}
}
@@ -2612,8 +2611,8 @@ static int riscv013_get_register_buf(struct target *target,
return result;
}
-static int riscv013_set_register_buf(struct target *target,
- enum gdb_regno regno, const uint8_t *value)
+int riscv013_set_register_buf(struct target *target, enum gdb_regno regno,
+ const uint8_t *value)
{
assert(regno >= GDB_REGNO_V0 && regno <= GDB_REGNO_V31);
@@ -2627,7 +2626,7 @@ static int riscv013_set_register_buf(struct target *target,
&debug_vl, &debug_vsew) != ERROR_OK)
return ERROR_FAIL;
- if (riscv_save_register(target, GDB_REGNO_S0) != ERROR_OK)
+ if (riscv013_reg_save(target, GDB_REGNO_S0) != ERROR_OK)
return ERROR_FAIL;
unsigned int vnum = regno - GDB_REGNO_V0;
@@ -3002,10 +3001,6 @@ static int init_target(struct command_context *cmd_ctx,
LOG_TARGET_DEBUG(target, "Init.");
RISCV_INFO(generic_info);
- generic_info->get_register = &riscv013_get_register;
- generic_info->set_register = &riscv013_set_register;
- generic_info->get_register_buf = &riscv013_get_register_buf;
- generic_info->set_register_buf = &riscv013_set_register_buf;
generic_info->select_target = &dm013_select_target;
generic_info->get_hart_state = &riscv013_get_hart_state;
generic_info->resume_go = &riscv013_resume_go;
@@ -4338,11 +4333,11 @@ static int read_memory_progbuf_inner_fill_progbuf(struct target *target,
{
const bool is_repeated_read = increment == 0;
- if (riscv_save_register(target, GDB_REGNO_S0) != ERROR_OK)
+ if (riscv013_reg_save(target, GDB_REGNO_S0) != ERROR_OK)
return ERROR_FAIL;
- if (riscv_save_register(target, GDB_REGNO_S1) != ERROR_OK)
+ if (riscv013_reg_save(target, GDB_REGNO_S1) != ERROR_OK)
return ERROR_FAIL;
- if (is_repeated_read && riscv_save_register(target, GDB_REGNO_A0) != ERROR_OK)
+ if (is_repeated_read && riscv013_reg_save(target, GDB_REGNO_A0) != ERROR_OK)
return ERROR_FAIL;
struct riscv_program program;
@@ -4435,7 +4430,7 @@ static int read_memory_progbuf_inner(struct target *target,
static int read_memory_progbuf_inner_one(struct target *target,
struct memory_access_info access, bool mprven)
{
- if (riscv_save_register(target, GDB_REGNO_S1) != ERROR_OK)
+ if (riscv013_reg_save(target, GDB_REGNO_S1) != ERROR_OK)
return ERROR_FAIL;
struct riscv_program program;
@@ -4975,9 +4970,9 @@ static int riscv_program_store_mprv(struct riscv_program *p, enum gdb_regno d,
static int write_memory_progbuf_fill_progbuf(struct target *target,
uint32_t size, bool mprven)
{
- if (riscv_save_register(target, GDB_REGNO_S0) != ERROR_OK)
+ if (riscv013_reg_save(target, GDB_REGNO_S0) != ERROR_OK)
return ERROR_FAIL;
- if (riscv_save_register(target, GDB_REGNO_S1) != ERROR_OK)
+ if (riscv013_reg_save(target, GDB_REGNO_S1) != ERROR_OK)
return ERROR_FAIL;
struct riscv_program program;
@@ -5148,7 +5143,7 @@ struct target_type riscv013_target = {
};
/*** 0.13-specific implementations of various RISC-V helper functions. ***/
-static int riscv013_get_register(struct target *target,
+int riscv013_get_register(struct target *target,
riscv_reg_t *value, enum gdb_regno rid)
{
/* It would be beneficial to move this redirection to the
@@ -5157,14 +5152,14 @@ static int riscv013_get_register(struct target *target,
*/
if (rid == GDB_REGNO_PRIV) {
uint64_t dcsr;
- if (riscv_get_register(target, &dcsr, GDB_REGNO_DCSR) != ERROR_OK)
+ if (riscv_reg_get(target, &dcsr, GDB_REGNO_DCSR) != ERROR_OK)
return ERROR_FAIL;
*value = set_field(0, VIRT_PRIV_V, get_field(dcsr, CSR_DCSR_V));
*value = set_field(*value, VIRT_PRIV_PRV, get_field(dcsr, CSR_DCSR_PRV));
return ERROR_OK;
}
- LOG_TARGET_DEBUG(target, "reading register %s", gdb_regno_name(target, rid));
+ LOG_TARGET_DEBUG(target, "reading register %s", riscv_reg_gdb_regno_name(target, rid));
if (dm013_select_target(target) != ERROR_OK)
return ERROR_FAIL;
@@ -5177,11 +5172,11 @@ static int riscv013_get_register(struct target *target,
return ERROR_OK;
}
-static int riscv013_set_register(struct target *target, enum gdb_regno rid,
+int riscv013_set_register(struct target *target, enum gdb_regno rid,
riscv_reg_t value)
{
LOG_TARGET_DEBUG(target, "writing 0x%" PRIx64 " to register %s",
- value, gdb_regno_name(target, rid));
+ value, riscv_reg_gdb_regno_name(target, rid));
if (dm013_select_target(target) != ERROR_OK)
return ERROR_FAIL;
@@ -5545,7 +5540,7 @@ static int riscv013_on_step_or_resume(struct target *target, bool step)
if (set_dcsr_ebreak(target, step) != ERROR_OK)
return ERROR_FAIL;
- if (riscv_flush_registers(target) != ERROR_OK)
+ if (riscv_reg_flush_all(target) != ERROR_OK)
return ERROR_FAIL;
return ERROR_OK;
}
@@ -5559,7 +5554,7 @@ static int riscv013_step_or_resume_current_hart(struct target *target,
}
LOG_TARGET_DEBUG(target, "resuming (for step?=%d)", step);
- if (riscv_flush_registers(target) != ERROR_OK)
+ if (riscv_reg_flush_all(target) != ERROR_OK)
return ERROR_FAIL;
dm013_info_t *dm = get_dm(target);
diff --git a/src/target/riscv/riscv-013.h b/src/target/riscv/riscv-013.h
new file mode 100644
index 0000000..be508f7
--- /dev/null
+++ b/src/target/riscv/riscv-013.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#ifndef OPENOCD_TARGET_RISCV_RISCV_013_H
+#define OPENOCD_TARGET_RISCV_RISCV_013_H
+
+#include "riscv.h"
+
+/* TODO: These functions should be replaced here by access methods that can be
+ * reused by other modules (e.g. a function writing an abstract commands, a
+ * function filling/executing program buffer, etc.), while the specifics on how
+ * to use these general-purpose version-specific methods to get a register's
+ * value will be in `riscv-013_reg.c`.
+ */
+int riscv013_get_register(struct target *target,
+ riscv_reg_t *value, enum gdb_regno rid);
+int riscv013_get_register_buf(struct target *target, uint8_t *value,
+ enum gdb_regno regno);
+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);
+
+#endif /*OPENOCD_TARGET_RISCV_RISCV_013_H*/
diff --git a/src/target/riscv/riscv-013_reg.c b/src/target/riscv/riscv-013_reg.c
new file mode 100644
index 0000000..a71a01c
--- /dev/null
+++ b/src/target/riscv/riscv-013_reg.c
@@ -0,0 +1,162 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "riscv-013_reg.h"
+
+#include "riscv_reg.h"
+#include "riscv_reg_impl.h"
+#include "riscv-013.h"
+#include <helper/time_support.h>
+
+static int riscv013_reg_get(struct reg *reg)
+{
+ struct target *target = riscv_reg_impl_get_target(reg);
+
+ /* TODO: Hack to deal with gdb that thinks these registers still exist. */
+ if (reg->number > GDB_REGNO_XPR15 && reg->number <= GDB_REGNO_XPR31 &&
+ riscv_supports_extension(target, 'E')) {
+ buf_set_u64(reg->value, 0, reg->size, 0);
+ return ERROR_OK;
+ }
+
+ if (reg->number >= GDB_REGNO_V0 && reg->number <= GDB_REGNO_V31) {
+ if (riscv013_get_register_buf(target, reg->value, reg->number) != ERROR_OK)
+ return ERROR_FAIL;
+
+ reg->valid = riscv_reg_impl_gdb_regno_cacheable(reg->number, /* is write? */ false);
+ } else {
+ uint64_t value;
+ int result = riscv_reg_get(target, &value, reg->number);
+ if (result != ERROR_OK)
+ return result;
+ buf_set_u64(reg->value, 0, reg->size, value);
+ }
+ char *str = buf_to_hex_str(reg->value, reg->size);
+ LOG_TARGET_DEBUG(target, "Read 0x%s from %s (valid=%d).", str, reg->name,
+ reg->valid);
+ free(str);
+ return ERROR_OK;
+}
+
+static int riscv013_reg_set(struct reg *reg, uint8_t *buf)
+{
+ struct target *target = riscv_reg_impl_get_target(reg);
+ RISCV_INFO(r);
+
+ char *str = buf_to_hex_str(buf, reg->size);
+ LOG_TARGET_DEBUG(target, "Write 0x%s to %s (valid=%d).", str, reg->name,
+ reg->valid);
+ free(str);
+
+ /* TODO: Hack to deal with gdb that thinks these registers still exist. */
+ if (reg->number > GDB_REGNO_XPR15 && reg->number <= GDB_REGNO_XPR31 &&
+ riscv_supports_extension(target, 'E') &&
+ buf_get_u64(buf, 0, reg->size) == 0)
+ return ERROR_OK;
+
+ if (reg->number == GDB_REGNO_TDATA1 ||
+ reg->number == GDB_REGNO_TDATA2) {
+ r->manual_hwbp_set = true;
+ /* When enumerating triggers, we clear any triggers with DMODE set,
+ * assuming they were left over from a previous debug session. So make
+ * sure that is done before a user might be setting their own triggers.
+ */
+ if (riscv_enumerate_triggers(target) != ERROR_OK)
+ return ERROR_FAIL;
+ }
+
+ if (reg->number >= GDB_REGNO_V0 && reg->number <= GDB_REGNO_V31) {
+ if (riscv013_set_register_buf(target, reg->number, buf) != ERROR_OK)
+ return ERROR_FAIL;
+
+ memcpy(reg->value, buf, DIV_ROUND_UP(reg->size, 8));
+ reg->valid = riscv_reg_impl_gdb_regno_cacheable(reg->number, /* is write? */ true);
+ } else {
+ const riscv_reg_t value = buf_get_u64(buf, 0, reg->size);
+ if (riscv_reg_set(target, reg->number, value) != ERROR_OK)
+ return ERROR_FAIL;
+ }
+
+ return ERROR_OK;
+}
+
+static const struct reg_arch_type *riscv013_gdb_regno_reg_type(uint32_t regno)
+{
+ static const struct reg_arch_type riscv011_reg_type = {
+ .get = riscv013_reg_get,
+ .set = riscv013_reg_set
+ };
+ return &riscv011_reg_type;
+}
+
+static int riscv013_init_reg(struct target *target, uint32_t regno)
+{
+ return riscv_reg_impl_init_one(target, regno, riscv013_gdb_regno_reg_type(regno));
+}
+
+int riscv013_reg_init_all(struct target *target)
+{
+ if (riscv_reg_impl_init_cache(target) != ERROR_OK)
+ return ERROR_FAIL;
+
+ init_shared_reg_info(target);
+
+ 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;
+
+ if (riscv_reg_impl_expose_csrs(target) != ERROR_OK)
+ return ERROR_FAIL;
+
+ riscv_reg_impl_hide_csrs(target);
+
+ return ERROR_OK;
+}
+
+/**
+ * This function is used to save the value of a register in cache. The register
+ * is marked as dirty, and writeback is delayed for as long as possible.
+ */
+int riscv013_reg_save(struct target *target, enum gdb_regno regid)
+{
+ if (target->state != TARGET_HALTED) {
+ LOG_TARGET_ERROR(target, "Can't save register %s on a hart that is not halted.",
+ riscv_reg_gdb_regno_name(target, regid));
+ return ERROR_FAIL;
+ }
+ assert(riscv_reg_impl_gdb_regno_cacheable(regid, /* is write? */ false) &&
+ "Only cacheable registers can be saved.");
+
+ RISCV_INFO(r);
+ riscv_reg_t value;
+ if (!target->reg_cache) {
+ assert(!target_was_examined(target));
+ /* To create register cache it is needed to examine the target first,
+ * therefore during examine, any changed register needs to be saved
+ * and restored manually.
+ */
+ return ERROR_OK;
+ }
+
+ struct reg *reg = riscv_reg_impl_cache_entry(target, regid);
+
+ LOG_TARGET_DEBUG(target, "Saving %s", reg->name);
+ if (riscv_reg_get(target, &value, regid) != ERROR_OK)
+ return ERROR_FAIL;
+
+ assert(reg->valid &&
+ "The register is cacheable, so the cache entry must be valid now.");
+ /* Mark the register dirty. We assume that this function is called
+ * because the caller is about to mess with the underlying value of the
+ * register. */
+ reg->dirty = true;
+
+ r->last_activity = timeval_ms();
+
+ return ERROR_OK;
+}
diff --git a/src/target/riscv/riscv-013_reg.h b/src/target/riscv/riscv-013_reg.h
new file mode 100644
index 0000000..2bdaaa0
--- /dev/null
+++ b/src/target/riscv/riscv-013_reg.h
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#ifndef OPENOCD_TARGET_RISCV_RISCV_REG_013_H
+#define OPENOCD_TARGET_RISCV_RISCV_REG_013_H
+
+#include "target/target.h"
+#include "gdb_regs.h"
+#include "riscv.h"
+
+/**
+ * This file describes additional register cache interface available to the
+ * RISC-V Debug Specification v0.13+ targets.
+ */
+
+/**
+ * Init initialize register cache. 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);
+
+/**
+ * This function is used to save the value of a register in cache. The register
+ * is marked as dirty, and writeback is delayed for as long as possible.
+ * Generally used to save registers before program buffer execution.
+ *
+ * TODO: The interface should be restricted in such a way that only GPRs can be
+ * saved.
+ */
+int riscv013_reg_save(struct target *target, enum gdb_regno regid);
+
+#endif /*OPENOCD_TARGET_RISCV_RISCV_REG_013_H*/
diff --git a/src/target/riscv/riscv.c b/src/target/riscv/riscv.c
index b2612c2..9c4796d 100644
--- a/src/target/riscv/riscv.c
+++ b/src/target/riscv/riscv.c
@@ -20,6 +20,7 @@
#include "helper/base64.h"
#include "helper/time_support.h"
#include "riscv.h"
+#include "riscv_reg.h"
#include "program.h"
#include "gdb_regs.h"
#include "rtos/rtos.h"
@@ -481,38 +482,6 @@ 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)
- 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;
-}
-
-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_wp_triggers_cache(struct target *target)
{
RISCV_INFO(r);
@@ -541,13 +510,13 @@ static void riscv_deinit_target(struct target *target)
if (!tt)
LOG_TARGET_ERROR(target, "Could not identify target type.");
- if (riscv_flush_registers(target) != ERROR_OK)
+ if (riscv_reg_flush_all(target) != ERROR_OK)
LOG_TARGET_ERROR(target, "Failed to flush registers. Ignoring this error.");
if (tt && info && info->version_specific)
tt->deinit_target(target);
- riscv_free_registers(target);
+ riscv_reg_free_all(target);
free_wp_triggers_cache(target);
if (!info)
@@ -648,25 +617,25 @@ static int set_trigger(struct target *target, unsigned int idx, riscv_reg_t tdat
{
riscv_reg_t tdata1_rb, tdata2_rb;
// Select which trigger to use
- if (riscv_set_register(target, GDB_REGNO_TSELECT, idx) != ERROR_OK)
+ if (riscv_reg_set(target, GDB_REGNO_TSELECT, idx) != ERROR_OK)
return ERROR_FAIL;
// Disable the trigger by writing 0 to it
- if (riscv_set_register(target, GDB_REGNO_TDATA1, 0) != ERROR_OK)
+ if (riscv_reg_set(target, GDB_REGNO_TDATA1, 0) != ERROR_OK)
return ERROR_FAIL;
// Set trigger data for tdata2 (and tdata3 if it was supported)
- if (riscv_set_register(target, GDB_REGNO_TDATA2, tdata2) != ERROR_OK)
+ if (riscv_reg_set(target, GDB_REGNO_TDATA2, tdata2) != ERROR_OK)
return ERROR_FAIL;
// Set trigger data for tdata1
- if (riscv_set_register(target, GDB_REGNO_TDATA1, tdata1) != ERROR_OK)
+ if (riscv_reg_set(target, GDB_REGNO_TDATA1, tdata1) != ERROR_OK)
return ERROR_FAIL;
// Read back tdata1, tdata2, (tdata3), and check if the configuration is supported
- if (riscv_get_register(target, &tdata1_rb, GDB_REGNO_TDATA1) != ERROR_OK)
+ if (riscv_reg_get(target, &tdata1_rb, GDB_REGNO_TDATA1) != ERROR_OK)
return ERROR_FAIL;
- if (riscv_get_register(target, &tdata2_rb, GDB_REGNO_TDATA2) != ERROR_OK)
+ if (riscv_reg_get(target, &tdata2_rb, GDB_REGNO_TDATA2) != ERROR_OK)
return ERROR_FAIL;
bool tdata1_config_denied = (tdata1 & ~tdata1_ignore_mask) != (tdata1_rb & ~tdata1_ignore_mask);
bool tdata2_config_denied = tdata2 != tdata2_rb;
@@ -682,7 +651,7 @@ static int set_trigger(struct target *target, unsigned int idx, riscv_reg_t tdat
LOG_TARGET_DEBUG(target,
"wrote 0x%" PRIx64 " to tdata2 but read back 0x%" PRIx64,
tdata2, tdata2_rb);
- if (riscv_set_register(target, GDB_REGNO_TDATA1, 0) != ERROR_OK)
+ if (riscv_reg_set(target, GDB_REGNO_TDATA1, 0) != ERROR_OK)
return ERROR_FAIL;
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
}
@@ -712,7 +681,7 @@ static int maybe_add_trigger_t1(struct target *target, struct trigger *trigger)
if (ret != ERROR_OK)
return ret;
- if (riscv_get_register(target, &tdata1, GDB_REGNO_TDATA1) != ERROR_OK)
+ if (riscv_reg_get(target, &tdata1, GDB_REGNO_TDATA1) != ERROR_OK)
return ERROR_FAIL;
if (tdata1 & (bpcontrol_r | bpcontrol_w | bpcontrol_x)) {
/* Trigger is already in use, presumably by user code. */
@@ -1242,7 +1211,7 @@ static int add_trigger(struct target *target, struct trigger *trigger)
if (ret != ERROR_OK)
return ret;
- ret = riscv_get_register(target, &tselect, GDB_REGNO_TSELECT);
+ ret = riscv_reg_get(target, &tselect, GDB_REGNO_TSELECT);
if (ret != ERROR_OK)
return ret;
@@ -1260,7 +1229,7 @@ static int add_trigger(struct target *target, struct trigger *trigger)
break;
} while (0);
- if (riscv_set_register(target, GDB_REGNO_TSELECT, tselect) != ERROR_OK &&
+ if (riscv_reg_set(target, GDB_REGNO_TSELECT, tselect) != ERROR_OK &&
ret == ERROR_OK)
return ERROR_FAIL;
@@ -1444,15 +1413,15 @@ static int remove_trigger(struct target *target, int unique_id)
return ERROR_FAIL;
riscv_reg_t tselect;
- int result = riscv_get_register(target, &tselect, GDB_REGNO_TSELECT);
+ int result = riscv_reg_get(target, &tselect, GDB_REGNO_TSELECT);
if (result != ERROR_OK)
return result;
bool done = false;
for (unsigned int i = 0; i < r->trigger_count; i++) {
if (r->trigger_unique_id[i] == unique_id) {
- riscv_set_register(target, GDB_REGNO_TSELECT, i);
- riscv_set_register(target, GDB_REGNO_TDATA1, 0);
+ riscv_reg_set(target, GDB_REGNO_TSELECT, i);
+ riscv_reg_set(target, GDB_REGNO_TDATA1, 0);
r->trigger_unique_id[i] = -1;
LOG_TARGET_DEBUG(target, "Stop using resource %d for bp %d",
i, unique_id);
@@ -1465,7 +1434,7 @@ static int remove_trigger(struct target *target, int unique_id)
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
}
- riscv_set_register(target, GDB_REGNO_TSELECT, tselect);
+ riscv_reg_set(target, GDB_REGNO_TSELECT, tselect);
return ERROR_OK;
}
@@ -1566,7 +1535,7 @@ static int riscv_trigger_detect_hit_bits(struct target *target, int64_t *unique_
RISCV_INFO(r);
riscv_reg_t tselect;
- if (riscv_get_register(target, &tselect, GDB_REGNO_TSELECT) != ERROR_OK)
+ if (riscv_reg_get(target, &tselect, GDB_REGNO_TSELECT) != ERROR_OK)
return ERROR_FAIL;
*unique_id = RISCV_TRIGGER_HIT_NOT_FOUND;
@@ -1574,11 +1543,11 @@ static int riscv_trigger_detect_hit_bits(struct target *target, int64_t *unique_
if (r->trigger_unique_id[i] == -1)
continue;
- if (riscv_set_register(target, GDB_REGNO_TSELECT, i) != ERROR_OK)
+ if (riscv_reg_set(target, GDB_REGNO_TSELECT, i) != ERROR_OK)
return ERROR_FAIL;
uint64_t tdata1;
- if (riscv_get_register(target, &tdata1, GDB_REGNO_TDATA1) != ERROR_OK)
+ if (riscv_reg_get(target, &tdata1, GDB_REGNO_TDATA1) != ERROR_OK)
return ERROR_FAIL;
int type = get_field(tdata1, CSR_TDATA1_TYPE(riscv_xlen(target)));
@@ -1612,7 +1581,7 @@ static int riscv_trigger_detect_hit_bits(struct target *target, int64_t *unique_
if (tdata1 & hit_mask) {
LOG_TARGET_DEBUG(target, "Trigger %u (unique_id=%" PRIi64 ") has hit bit set.",
i, r->trigger_unique_id[i]);
- if (riscv_set_register(target, GDB_REGNO_TDATA1, tdata1 & ~hit_mask) != ERROR_OK)
+ if (riscv_reg_set(target, GDB_REGNO_TDATA1, tdata1 & ~hit_mask) != ERROR_OK)
return ERROR_FAIL;
*unique_id = r->trigger_unique_id[i];
@@ -1620,7 +1589,7 @@ static int riscv_trigger_detect_hit_bits(struct target *target, int64_t *unique_
}
}
- if (riscv_set_register(target, GDB_REGNO_TSELECT, tselect) != ERROR_OK)
+ if (riscv_reg_set(target, GDB_REGNO_TSELECT, tselect) != ERROR_OK)
return ERROR_FAIL;
return ERROR_OK;
@@ -2082,7 +2051,7 @@ static int riscv_hit_watchpoint(struct target *target, struct watchpoint **hit_w
}
riscv_reg_t dpc;
- if (riscv_get_register(target, &dpc, GDB_REGNO_DPC) != ERROR_OK)
+ if (riscv_reg_get(target, &dpc, GDB_REGNO_DPC) != ERROR_OK)
return ERROR_FAIL;
const uint8_t length = 4;
LOG_TARGET_DEBUG(target, "dpc is 0x%" PRIx64, dpc);
@@ -2109,7 +2078,7 @@ static int riscv_hit_watchpoint(struct target *target, struct watchpoint **hit_w
if (get_loadstore_membase_regno(target, instruction, &rs) != ERROR_OK)
return ERROR_FAIL;
- if (riscv_get_register(target, &mem_addr, rs) != ERROR_OK)
+ if (riscv_reg_get(target, &mem_addr, rs) != ERROR_OK)
return ERROR_FAIL;
if (get_loadstore_memoffset(target, instruction, &memoffset) != ERROR_OK)
return ERROR_FAIL;
@@ -2239,44 +2208,6 @@ static int old_or_new_riscv_poll(struct target *target)
return riscv_openocd_poll(target);
}
-static struct reg *get_reg_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];
-}
-
-int riscv_flush_registers(struct target *target)
-{
- RISCV_INFO(r);
-
- 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.
- */
- for (unsigned int number = target->reg_cache->num_regs; number-- > 0; ) {
- struct reg *reg = get_reg_cache_entry(target, number);
- if (reg->valid && reg->dirty) {
- riscv_reg_t value = buf_get_u64(reg->value, 0, reg->size);
-
- LOG_TARGET_DEBUG(target, "%s is dirty; write back 0x%" PRIx64,
- reg->name, value);
- if (r->set_register(target, number, value) != ERROR_OK)
- return ERROR_FAIL;
- reg->dirty = false;
- }
- }
- LOG_TARGET_DEBUG(target, "Flush of register cache completed");
- return ERROR_OK;
-}
-
static enum target_debug_reason
derive_debug_reason_without_hitbit(const struct target *target, riscv_reg_t dpc)
{
@@ -2352,7 +2283,7 @@ static int set_debug_reason(struct target *target, enum riscv_halt_reason halt_r
LOG_TARGET_DEBUG(target,
"No trigger hit found, deriving debug reason without it.");
riscv_reg_t dpc;
- if (riscv_get_register(target, &dpc, GDB_REGNO_DPC) != ERROR_OK)
+ if (riscv_reg_get(target, &dpc, GDB_REGNO_DPC) != ERROR_OK)
return ERROR_FAIL;
/* Here we don't have the hit bit set (likely, HW does not support it).
* We are trying to guess the state. But here comes the problem:
@@ -2530,21 +2461,21 @@ static int disable_triggers(struct target *target, riscv_reg_t *state)
if (r->manual_hwbp_set) {
/* Look at every trigger that may have been set. */
riscv_reg_t tselect;
- if (riscv_get_register(target, &tselect, GDB_REGNO_TSELECT) != ERROR_OK)
+ if (riscv_reg_get(target, &tselect, GDB_REGNO_TSELECT) != ERROR_OK)
return ERROR_FAIL;
for (unsigned int t = 0; t < r->trigger_count; t++) {
- if (riscv_set_register(target, GDB_REGNO_TSELECT, t) != ERROR_OK)
+ if (riscv_reg_set(target, GDB_REGNO_TSELECT, t) != ERROR_OK)
return ERROR_FAIL;
riscv_reg_t tdata1;
- if (riscv_get_register(target, &tdata1, GDB_REGNO_TDATA1) != ERROR_OK)
+ if (riscv_reg_get(target, &tdata1, GDB_REGNO_TDATA1) != ERROR_OK)
return ERROR_FAIL;
if (tdata1 & CSR_TDATA1_DMODE(riscv_xlen(target))) {
state[t] = tdata1;
- if (riscv_set_register(target, GDB_REGNO_TDATA1, 0) != ERROR_OK)
+ if (riscv_reg_set(target, GDB_REGNO_TDATA1, 0) != ERROR_OK)
return ERROR_FAIL;
}
}
- if (riscv_set_register(target, GDB_REGNO_TSELECT, tselect) != ERROR_OK)
+ if (riscv_reg_set(target, GDB_REGNO_TSELECT, tselect) != ERROR_OK)
return ERROR_FAIL;
} else {
@@ -2573,17 +2504,17 @@ static int enable_triggers(struct target *target, riscv_reg_t *state)
if (r->manual_hwbp_set) {
/* Look at every trigger that may have been set. */
riscv_reg_t tselect;
- if (riscv_get_register(target, &tselect, GDB_REGNO_TSELECT) != ERROR_OK)
+ if (riscv_reg_get(target, &tselect, GDB_REGNO_TSELECT) != ERROR_OK)
return ERROR_FAIL;
for (unsigned int t = 0; t < r->trigger_count; t++) {
if (state[t] != 0) {
- if (riscv_set_register(target, GDB_REGNO_TSELECT, t) != ERROR_OK)
+ if (riscv_reg_set(target, GDB_REGNO_TSELECT, t) != ERROR_OK)
return ERROR_FAIL;
- if (riscv_set_register(target, GDB_REGNO_TDATA1, state[t]) != ERROR_OK)
+ if (riscv_reg_set(target, GDB_REGNO_TDATA1, state[t]) != ERROR_OK)
return ERROR_FAIL;
}
}
- if (riscv_set_register(target, GDB_REGNO_TSELECT, tselect) != ERROR_OK)
+ if (riscv_reg_set(target, GDB_REGNO_TSELECT, tselect) != ERROR_OK)
return ERROR_FAIL;
} else {
@@ -2612,7 +2543,7 @@ static int resume_prep(struct target *target, int current,
assert(target->state == TARGET_HALTED);
RISCV_INFO(r);
- if (!current && riscv_set_register(target, GDB_REGNO_PC, address) != ERROR_OK)
+ if (!current && riscv_reg_set(target, GDB_REGNO_PC, address) != ERROR_OK)
return ERROR_FAIL;
if (handle_breakpoints) {
@@ -2753,14 +2684,14 @@ static int riscv_target_resume(struct target *target, int current,
static int riscv_effective_privilege_mode(struct target *target, int *v_mode, int *effective_mode)
{
riscv_reg_t priv;
- if (riscv_get_register(target, &priv, GDB_REGNO_PRIV) != ERROR_OK) {
+ if (riscv_reg_get(target, &priv, GDB_REGNO_PRIV) != ERROR_OK) {
LOG_TARGET_ERROR(target, "Failed to read priv register.");
return ERROR_FAIL;
}
*v_mode = get_field(priv, VIRT_PRIV_V);
riscv_reg_t mstatus;
- if (riscv_get_register(target, &mstatus, GDB_REGNO_MSTATUS) != ERROR_OK) {
+ if (riscv_reg_get(target, &mstatus, GDB_REGNO_MSTATUS) != ERROR_OK) {
LOG_TARGET_ERROR(target, "Failed to read mstatus register.");
return ERROR_FAIL;
}
@@ -2784,7 +2715,7 @@ static int riscv_mmu(struct target *target, int *enabled)
/* Don't use MMU in explicit or effective M (machine) mode */
riscv_reg_t priv;
- if (riscv_get_register(target, &priv, GDB_REGNO_PRIV) != ERROR_OK) {
+ if (riscv_reg_get(target, &priv, GDB_REGNO_PRIV) != ERROR_OK) {
LOG_TARGET_ERROR(target, "Failed to read priv register.");
return ERROR_FAIL;
}
@@ -2802,7 +2733,7 @@ static int riscv_mmu(struct target *target, int *enabled)
* effective privilege mode is U and hstatus.HU=0. */
if (effective_mode == PRV_U) {
riscv_reg_t hstatus;
- if (riscv_get_register(target, &hstatus, GDB_REGNO_HSTATUS) != ERROR_OK) {
+ if (riscv_reg_get(target, &hstatus, GDB_REGNO_HSTATUS) != ERROR_OK) {
LOG_TARGET_ERROR(target, "Failed to read hstatus register.");
return ERROR_FAIL;
}
@@ -2815,7 +2746,7 @@ static int riscv_mmu(struct target *target, int *enabled)
}
riscv_reg_t vsatp;
- if (riscv_get_register(target, &vsatp, GDB_REGNO_VSATP) != ERROR_OK) {
+ if (riscv_reg_get(target, &vsatp, GDB_REGNO_VSATP) != ERROR_OK) {
LOG_TARGET_ERROR(target, "Failed to read vsatp register; priv=0x%" PRIx64,
priv);
return ERROR_FAIL;
@@ -2828,7 +2759,7 @@ static int riscv_mmu(struct target *target, int *enabled)
}
riscv_reg_t hgatp;
- if (riscv_get_register(target, &hgatp, GDB_REGNO_HGATP) != ERROR_OK) {
+ if (riscv_reg_get(target, &hgatp, GDB_REGNO_HGATP) != ERROR_OK) {
LOG_TARGET_ERROR(target, "Failed to read hgatp register; priv=0x%" PRIx64,
priv);
return ERROR_FAIL;
@@ -2850,7 +2781,7 @@ static int riscv_mmu(struct target *target, int *enabled)
}
riscv_reg_t satp;
- if (riscv_get_register(target, &satp, GDB_REGNO_SATP) != ERROR_OK) {
+ if (riscv_reg_get(target, &satp, GDB_REGNO_SATP) != ERROR_OK) {
LOG_TARGET_DEBUG(target, "Couldn't read SATP.");
/* If we can't read SATP, then there must not be an MMU. */
return ERROR_OK;
@@ -2961,7 +2892,7 @@ static int riscv_address_translate(struct target *target,
static int riscv_virt2phys_v(struct target *target, target_addr_t virtual, target_addr_t *physical)
{
riscv_reg_t vsatp;
- if (riscv_get_register(target, &vsatp, GDB_REGNO_VSATP) != ERROR_OK) {
+ if (riscv_reg_get(target, &vsatp, GDB_REGNO_VSATP) != ERROR_OK) {
LOG_TARGET_ERROR(target, "Failed to read vsatp register.");
return ERROR_FAIL;
}
@@ -2970,7 +2901,7 @@ static int riscv_virt2phys_v(struct target *target, target_addr_t virtual, targe
int vsatp_mode = get_field(vsatp, RISCV_SATP_MODE(xlen));
LOG_TARGET_DEBUG(target, "VS-stage translation mode: %d", vsatp_mode);
riscv_reg_t hgatp;
- if (riscv_get_register(target, &hgatp, GDB_REGNO_HGATP) != ERROR_OK) {
+ if (riscv_reg_get(target, &hgatp, GDB_REGNO_HGATP) != ERROR_OK) {
LOG_TARGET_ERROR(target, "Failed to read hgatp register.");
return ERROR_FAIL;
}
@@ -3080,7 +3011,7 @@ static int riscv_virt2phys(struct target *target, target_addr_t virtual, target_
}
riscv_reg_t priv;
- if (riscv_get_register(target, &priv, GDB_REGNO_PRIV) != ERROR_OK) {
+ if (riscv_reg_get(target, &priv, GDB_REGNO_PRIV) != ERROR_OK) {
LOG_TARGET_ERROR(target, "Failed to read priv register.");
return ERROR_FAIL;
}
@@ -3089,7 +3020,7 @@ static int riscv_virt2phys(struct target *target, target_addr_t virtual, target_
return riscv_virt2phys_v(target, virtual, physical);
riscv_reg_t satp_value;
- if (riscv_get_register(target, &satp_value, GDB_REGNO_SATP) != ERROR_OK) {
+ if (riscv_reg_get(target, &satp_value, GDB_REGNO_SATP) != ERROR_OK) {
LOG_TARGET_ERROR(target, "Failed to read SATP register.");
return ERROR_FAIL;
}
@@ -3366,10 +3297,10 @@ static int riscv_run_algorithm(struct target *target, int num_mem_params,
for (unsigned i = 0; i < ARRAY_SIZE(regnums); i++) {
enum gdb_regno regno = regnums[i];
riscv_reg_t reg_value;
- if (riscv_get_register(target, &reg_value, regno) != ERROR_OK)
+ if (riscv_reg_get(target, &reg_value, regno) != ERROR_OK)
break;
- LOG_TARGET_ERROR(target, "%s = 0x%" PRIx64, gdb_regno_name(target, regno), reg_value);
+ LOG_TARGET_ERROR(target, "%s = 0x%" PRIx64, riscv_reg_gdb_regno_name(target, regno), reg_value);
}
return ERROR_TARGET_TIMEOUT;
}
@@ -3578,7 +3509,7 @@ static int riscv_poll_hart(struct target *target, enum riscv_next_action *next_a
if (state == RISCV_STATE_HALTED && timeval_ms() - r->last_activity > 100) {
/* If we've been idle for a while, flush the register cache. Just in case
* OpenOCD is going to be disconnected without shutting down cleanly. */
- if (riscv_flush_registers(target) != ERROR_OK)
+ if (riscv_reg_flush_all(target) != ERROR_OK)
return ERROR_FAIL;
}
@@ -3829,7 +3760,7 @@ static int riscv_openocd_step_impl(struct target *target, int current,
LOG_TARGET_DEBUG(target, "stepping hart");
if (!current) {
- if (riscv_set_register(target, GDB_REGNO_PC, address) != ERROR_OK)
+ if (riscv_reg_set(target, GDB_REGNO_PC, address) != ERROR_OK)
return ERROR_FAIL;
}
@@ -3837,7 +3768,7 @@ static int riscv_openocd_step_impl(struct target *target, int current,
/* the front-end may request us not to handle breakpoints */
if (handle_breakpoints) {
if (current) {
- if (riscv_get_register(target, &address, GDB_REGNO_PC) != ERROR_OK)
+ if (riscv_reg_get(target, &address, GDB_REGNO_PC) != ERROR_OK)
return ERROR_FAIL;
}
breakpoint = breakpoint_find(target, address);
@@ -5036,7 +4967,7 @@ COMMAND_HANDLER(riscv_exec_progbuf)
return ERROR_FAIL;
}
- if (riscv_flush_registers(target) != ERROR_OK)
+ if (riscv_reg_flush_all(target) != ERROR_OK)
return ERROR_FAIL;
int error = riscv_program_exec(&prog, target);
riscv_invalidate_register_cache(target);
@@ -5608,253 +5539,6 @@ static void riscv_invalidate_register_cache(struct target *target)
register_cache_invalidate(target->reg_cache);
}
-
-/**
- * If write is true:
- * return true iff we are guaranteed that the register will contain exactly
- * the value we just wrote when it's read.
- * If write is false:
- * return true iff we are guaranteed that the register will read the same
- * value in the future as the value we just read.
- */
-static bool gdb_regno_cacheable(enum gdb_regno regno, bool is_write)
-{
- if (regno == GDB_REGNO_ZERO)
- return !is_write;
-
- /* GPRs, FPRs, vector registers are just normal data stores. */
- if (regno <= GDB_REGNO_XPR31 ||
- (regno >= GDB_REGNO_FPR0 && regno <= GDB_REGNO_FPR31) ||
- (regno >= GDB_REGNO_V0 && regno <= GDB_REGNO_V31))
- return true;
-
- /* Most CSRs won't change value on us, but we can't assume it about arbitrary
- * CSRs. */
- switch (regno) {
- case GDB_REGNO_DPC:
- case GDB_REGNO_VSTART:
- case GDB_REGNO_VXSAT:
- case GDB_REGNO_VXRM:
- case GDB_REGNO_VLENB:
- case GDB_REGNO_VL:
- case GDB_REGNO_VTYPE:
- case GDB_REGNO_MISA:
- case GDB_REGNO_DCSR:
- case GDB_REGNO_DSCRATCH0:
- case GDB_REGNO_MSTATUS:
- case GDB_REGNO_MEPC:
- case GDB_REGNO_MCAUSE:
- case GDB_REGNO_SATP:
- /*
- * WARL registers might not contain the value we just wrote, but
- * these ones won't spontaneously change their value either. *
- */
- return !is_write;
-
- case GDB_REGNO_TSELECT: /* I think this should be above, but then it doesn't work. */
- case GDB_REGNO_TDATA1: /* Changes value when tselect is changed. */
- case GDB_REGNO_TDATA2: /* Changes value when tselect is changed. */
- default:
- return false;
- }
-}
-
-/**
- * This function is used internally by functions that change register values.
- * If `write_through` is true, it is ensured that the value of the target's
- * register is set to be equal to the `value` argument. The cached value is
- * updated if the register is cacheable.
- */
-static int riscv_set_or_write_register(struct target *target,
- enum gdb_regno regid, riscv_reg_t value, bool write_through)
-{
- RISCV_INFO(r);
- assert(r);
- assert(r->set_register);
- if (r->dtm_version == DTM_DTMCS_VERSION_0_11)
- return r->set_register(target, regid, value);
-
- keep_alive();
-
- if (regid == GDB_REGNO_PC) {
- return riscv_set_or_write_register(target, GDB_REGNO_DPC, value, write_through);
- } else if (regid == GDB_REGNO_PRIV) {
- riscv_reg_t dcsr;
-
- if (riscv_get_register(target, &dcsr, GDB_REGNO_DCSR) != ERROR_OK)
- return ERROR_FAIL;
- dcsr = set_field(dcsr, CSR_DCSR_PRV, get_field(value, VIRT_PRIV_PRV));
- dcsr = set_field(dcsr, CSR_DCSR_V, get_field(value, VIRT_PRIV_V));
- 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,
- gdb_regno_name(target, regid), value);
- return r->set_register(target, regid, value);
- }
-
- struct reg *reg = get_reg_cache_entry(target, regid);
-
- if (!reg->exist) {
- LOG_TARGET_DEBUG(target, "Register %s does not exist.", reg->name);
- return ERROR_FAIL;
- }
-
- if (target->state != TARGET_HALTED) {
- LOG_TARGET_DEBUG(target,
- "Target not halted, writing to target: %s <- 0x%" PRIx64,
- reg->name, value);
- return r->set_register(target, regid, value);
- }
-
- const bool need_to_write = !reg->valid || reg->dirty ||
- value != buf_get_u64(reg->value, 0, reg->size);
- const bool cacheable = gdb_regno_cacheable(regid, need_to_write);
-
- if (!cacheable || (write_through && need_to_write)) {
- LOG_TARGET_DEBUG(target,
- "Writing to target: %s <- 0x%" PRIx64 " (cacheable=%s, valid=%s, dirty=%s)",
- reg->name, value, cacheable ? "true" : "false",
- reg->valid ? "true" : "false",
- reg->dirty ? "true" : "false");
- if (r->set_register(target, regid, value) != ERROR_OK)
- return ERROR_FAIL;
-
- reg->dirty = false;
- } else {
- reg->dirty = need_to_write;
- }
-
- buf_set_u64(reg->value, 0, reg->size, value);
- reg->valid = cacheable;
-
- LOG_TARGET_DEBUG(target,
- "Wrote 0x%" PRIx64 " to %s (cacheable=%s, valid=%s, dirty=%s)",
- value, reg->name, cacheable ? "true" : "false",
- reg->valid ? "true" : "false",
- reg->dirty ? "true" : "false");
- return ERROR_OK;
-}
-
-/**
- * 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_set_register(struct target *target, enum gdb_regno regid,
- riscv_reg_t value)
-{
- return riscv_set_or_write_register(target, regid, value,
- /* write_through */ false);
-}
-
-/**
- * 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_write_register(struct target *target, enum gdb_regno regid,
- riscv_reg_t value)
-{
- return riscv_set_or_write_register(target, regid, value,
- /* write_through */ true);
-}
-
-/**
- * This function is used to get the value of a register. If possible, the value
- * in cache will be updated.
- */
-int riscv_get_register(struct target *target, riscv_reg_t *value,
- enum gdb_regno regid)
-{
- RISCV_INFO(r);
- assert(r);
- assert(r->get_register);
- if (r->dtm_version == DTM_DTMCS_VERSION_0_11)
- return r->get_register(target, value, regid);
-
- keep_alive();
-
- if (regid == GDB_REGNO_PC)
- return riscv_get_register(target, value, GDB_REGNO_DPC);
-
- if (!target->reg_cache) {
- assert(!target_was_examined(target));
- LOG_TARGET_DEBUG(target, "No cache, reading %s from target",
- gdb_regno_name(target, regid));
- return r->get_register(target, value, regid);
- }
-
- struct reg *reg = get_reg_cache_entry(target, regid);
- if (!reg->exist) {
- LOG_TARGET_DEBUG(target, "Register %s does not exist.", reg->name);
- return ERROR_FAIL;
- }
-
- if (reg->valid) {
- *value = buf_get_u64(reg->value, 0, reg->size);
- LOG_TARGET_DEBUG(target, "Read %s: 0x%" PRIx64 " (cached)", reg->name,
- *value);
- return ERROR_OK;
- }
-
- LOG_TARGET_DEBUG(target, "Reading %s from target", reg->name);
- if (r->get_register(target, value, regid) != ERROR_OK)
- return ERROR_FAIL;
-
- buf_set_u64(reg->value, 0, reg->size, *value);
- reg->valid = gdb_regno_cacheable(regid, /* is write? */ false) &&
- target->state == TARGET_HALTED;
- reg->dirty = false;
-
- LOG_TARGET_DEBUG(target, "Read %s: 0x%" PRIx64, reg->name, *value);
- return ERROR_OK;
-}
-
-/**
- * This function is used to save the value of a register in cache. The register
- * is marked as dirty, and writeback is delayed for as long as possible.
- */
-int riscv_save_register(struct target *target, enum gdb_regno regid)
-{
- if (target->state != TARGET_HALTED) {
- LOG_TARGET_ERROR(target, "Can't save register %s on a hart that is not halted.",
- gdb_regno_name(target, regid));
- return ERROR_FAIL;
- }
- assert(gdb_regno_cacheable(regid, /* is write? */ false) &&
- "Only cacheable registers can be saved.");
-
- RISCV_INFO(r);
- riscv_reg_t value;
- if (!target->reg_cache) {
- assert(!target_was_examined(target));
- /* To create register cache it is needed to examine the target first,
- * therefore during examine, any changed register needs to be saved
- * and restored manually.
- */
- return ERROR_OK;
- }
-
- struct reg *reg = get_reg_cache_entry(target, regid);
-
- LOG_TARGET_DEBUG(target, "Saving %s", reg->name);
- if (riscv_get_register(target, &value, regid) != ERROR_OK)
- return ERROR_FAIL;
-
- assert(reg->valid &&
- "The register is cacheable, so the cache entry must be valid now.");
- /* Mark the register dirty. We assume that this function is called
- * because the caller is about to mess with the underlying value of the
- * register. */
- reg->dirty = true;
-
- r->last_activity = timeval_ms();
-
- return ERROR_OK;
-}
-
int riscv_get_hart_state(struct target *target, enum riscv_hart_state *state)
{
RISCV_INFO(r);
@@ -5924,10 +5608,10 @@ int riscv_get_dmi_scan_length(struct target *target)
static int check_if_trigger_exists(struct target *target, unsigned int index)
{
/* If we can't write tselect, then this hart does not support triggers. */
- if (riscv_set_register(target, GDB_REGNO_TSELECT, index) != ERROR_OK)
+ if (riscv_reg_set(target, GDB_REGNO_TSELECT, index) != ERROR_OK)
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
riscv_reg_t tselect_rb;
- if (riscv_get_register(target, &tselect_rb, GDB_REGNO_TSELECT) != ERROR_OK)
+ if (riscv_reg_get(target, &tselect_rb, GDB_REGNO_TSELECT) != ERROR_OK)
return ERROR_FAIL;
/* Mask off the top bit, which is used as tdrmode in legacy RISC-V Debug Spec
* (old revisions of v0.11 spec). */
@@ -5947,7 +5631,7 @@ static int get_trigger_types(struct target *target, unsigned int *trigger_tinfo,
{
assert(trigger_tinfo);
riscv_reg_t tinfo;
- if (riscv_get_register(target, &tinfo, GDB_REGNO_TINFO) == ERROR_OK) {
+ if (riscv_reg_get(target, &tinfo, GDB_REGNO_TINFO) == ERROR_OK) {
/* tinfo.INFO == 1: trigger doesn’t exist
* tinfo == 0 or tinfo.INFO != 1 and tinfo LSB is set: invalid tinfo */
if (tinfo == 0 || tinfo & 0x1)
@@ -5990,7 +5674,7 @@ static int disable_trigger_if_dmode(struct target *target, riscv_reg_t tdata1)
if (!dmode_is_set)
/* Nothing to do */
return ERROR_OK;
- return riscv_set_register(target, GDB_REGNO_TDATA1, 0);
+ return riscv_reg_set(target, GDB_REGNO_TDATA1, 0);
}
/**
@@ -6013,7 +5697,7 @@ int riscv_enumerate_triggers(struct target *target)
}
riscv_reg_t orig_tselect;
- int result = riscv_get_register(target, &orig_tselect, GDB_REGNO_TSELECT);
+ int result = riscv_reg_get(target, &orig_tselect, GDB_REGNO_TSELECT);
/* If tselect is not readable, the trigger module is likely not
* implemented. */
if (result != ERROR_OK) {
@@ -6033,7 +5717,7 @@ int riscv_enumerate_triggers(struct target *target)
break;
riscv_reg_t tdata1;
- if (riscv_get_register(target, &tdata1, GDB_REGNO_TDATA1) != ERROR_OK)
+ if (riscv_reg_get(target, &tdata1, GDB_REGNO_TDATA1) != ERROR_OK)
return ERROR_FAIL;
result = get_trigger_types(target, &r->trigger_tinfo[t], tdata1);
@@ -6049,7 +5733,7 @@ int riscv_enumerate_triggers(struct target *target)
return ERROR_FAIL;
}
- if (riscv_set_register(target, GDB_REGNO_TSELECT, orig_tselect) != ERROR_OK)
+ if (riscv_reg_set(target, GDB_REGNO_TSELECT, orig_tselect) != ERROR_OK)
return ERROR_FAIL;
r->triggers_enumerated = true;
@@ -6059,1006 +5743,6 @@ int riscv_enumerate_triggers(struct target *target)
return ERROR_OK;
}
-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 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;
-}
-
-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_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 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);
- }
-}
-
-const char *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;
-}
-
-static struct target *get_target_from_reg(const struct reg *reg);
-
-/**
- * This function is the handler of user's request to read a register.
- */
-static int riscv013_reg_get(struct reg *reg)
-{
- struct target *target = get_target_from_reg(reg);
- RISCV_INFO(r);
- assert(r);
- assert(r->dtm_version == DTM_DTMCS_VERSION_1_0);
-
- /* TODO: Hack to deal with gdb that thinks these registers still exist. */
- if (reg->number > GDB_REGNO_XPR15 && reg->number <= GDB_REGNO_XPR31 &&
- riscv_supports_extension(target, 'E')) {
- buf_set_u64(reg->value, 0, reg->size, 0);
- return ERROR_OK;
- }
-
- if (reg->number >= GDB_REGNO_V0 && reg->number <= GDB_REGNO_V31) {
- if (!r->get_register_buf) {
- LOG_TARGET_ERROR(target,
- "Reading register %s not supported on this target.",
- reg->name);
- return ERROR_FAIL;
- }
-
- if (r->get_register_buf(target, reg->value, reg->number) != ERROR_OK)
- return ERROR_FAIL;
-
- reg->valid = gdb_regno_cacheable(reg->number, /* is write? */ false);
- } else {
- uint64_t value;
- int result = riscv_get_register(target, &value, reg->number);
- if (result != ERROR_OK)
- return result;
- buf_set_u64(reg->value, 0, reg->size, value);
- }
- char *str = buf_to_hex_str(reg->value, reg->size);
- LOG_TARGET_DEBUG(target, "Read 0x%s from %s (valid=%d).", str, reg->name,
- reg->valid);
- free(str);
- return ERROR_OK;
-}
-
-/**
- * This function is the handler of user's request to write a register.
- */
-static int riscv013_reg_set(struct reg *reg, uint8_t *buf)
-{
- struct target *target = get_target_from_reg(reg);
- RISCV_INFO(r);
- assert(r);
- assert(r->dtm_version == DTM_DTMCS_VERSION_1_0);
-
- char *str = buf_to_hex_str(buf, reg->size);
- LOG_TARGET_DEBUG(target, "Write 0x%s to %s (valid=%d).", str, reg->name,
- reg->valid);
- free(str);
-
- /* TODO: Hack to deal with gdb that thinks these registers still exist. */
- if (reg->number > GDB_REGNO_XPR15 && reg->number <= GDB_REGNO_XPR31 &&
- riscv_supports_extension(target, 'E') &&
- buf_get_u64(buf, 0, reg->size) == 0)
- return ERROR_OK;
-
- if (reg->number == GDB_REGNO_TDATA1 ||
- reg->number == GDB_REGNO_TDATA2) {
- r->manual_hwbp_set = true;
- /* When enumerating triggers, we clear any triggers with DMODE set,
- * assuming they were left over from a previous debug session. So make
- * sure that is done before a user might be setting their own triggers.
- */
- if (riscv_enumerate_triggers(target) != ERROR_OK)
- return ERROR_FAIL;
- }
-
- if (reg->number >= GDB_REGNO_V0 && reg->number <= GDB_REGNO_V31) {
- if (!r->set_register_buf) {
- LOG_TARGET_ERROR(target,
- "Writing register %s not supported on this target.",
- reg->name);
- return ERROR_FAIL;
- }
-
- if (r->set_register_buf(target, reg->number, buf) != ERROR_OK)
- return ERROR_FAIL;
-
- memcpy(reg->value, buf, DIV_ROUND_UP(reg->size, 8));
- reg->valid = gdb_regno_cacheable(reg->number, /* is write? */ true);
- } else {
- const riscv_reg_t value = buf_get_u64(buf, 0, reg->size);
- if (riscv_set_register(target, reg->number, value) != ERROR_OK)
- return ERROR_FAIL;
- }
-
- return ERROR_OK;
-}
-
-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;
-}
-
-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];
-}
-
-static bool reg_is_initialized(const struct reg *reg)
-{
- assert(reg);
- if (!reg->feature) {
- const struct reg default_reg = {0};
- assert(!memcmp(&default_reg, reg, sizeof(*reg)));
- return false;
- }
- 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 target *get_target_from_reg(const struct reg *reg)
-{
- assert(reg_is_initialized(reg));
- return ((const riscv_reg_info_t *)reg->arch_info)->target;
-}
-
-static int riscv011_reg_get(struct reg *reg)
-{
- struct target * const target = get_target_from_reg(reg);
- RISCV_INFO(r);
- assert(r);
- assert(r->dtm_version == DTM_DTMCS_VERSION_0_11);
- riscv_reg_t value;
- const int result = r->get_register(target, &value, reg->number);
- if (result != ERROR_OK)
- return result;
- buf_set_u64(reg->value, 0, reg->size, value);
- return ERROR_OK;
-}
-
-static int riscv011_reg_set(struct reg *reg, uint8_t *buf)
-{
- const riscv_reg_t value = buf_get_u64(buf, 0, reg->size);
- struct target * const target = get_target_from_reg(reg);
- RISCV_INFO(r);
- assert(r);
- assert(r->dtm_version == DTM_DTMCS_VERSION_0_11);
- if (reg->number == GDB_REGNO_TDATA1 || reg->number == GDB_REGNO_TDATA2) {
- r->manual_hwbp_set = true;
- /* When enumerating triggers, we clear any triggers with DMODE set,
- * assuming they were left over from a previous debug session. So make
- * sure that is done before a user might be setting their own triggers.
- */
- if (riscv_enumerate_triggers(target) != ERROR_OK)
- return ERROR_FAIL;
- }
- return r->set_register(target, reg->number, value);
-}
-
-static struct reg_arch_type *gdb_regno_reg_type(const struct target *target,
- uint32_t regno)
-{
- RISCV_INFO(info);
- assert(info);
- if (info->dtm_version == DTM_DTMCS_VERSION_0_11) {
- static struct reg_arch_type riscv011_reg_type = {
- .get = riscv011_reg_get,
- .set = riscv011_reg_set
- };
- return &riscv011_reg_type;
- }
-
- static struct reg_arch_type riscv013_reg_type = {
- .get = riscv013_reg_get,
- .set = riscv013_reg_set
- };
- return &riscv013_reg_type;
-}
-
-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 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:
- 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;
-}
-
-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;
- }
- } else {
- reg->value = NULL;
- }
- 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 = gdb_regno_reg_type(target, regno);
- 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_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;
- }
- }
-}
-
-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, const struct scan_field *field,
riscv_bscan_tunneled_scan_context_t *ctxt)
{
diff --git a/src/target/riscv/riscv.h b/src/target/riscv/riscv.h
index d3703d1..c27e931 100644
--- a/src/target/riscv/riscv.h
+++ b/src/target/riscv/riscv.h
@@ -9,8 +9,9 @@ struct riscv_program;
#include "opcodes.h"
#include "gdb_regs.h"
#include "jtag/jtag.h"
-#include "target/register.h"
#include "target/semihosting_common.h"
+#include "target/target.h"
+#include "target/register.h"
#include <helper/command.h>
#include <helper/bits.h>
@@ -83,9 +84,11 @@ enum riscv_hart_state {
RISCV_STATE_UNAVAILABLE
};
+/* RISC-V-specific data assigned to a register. */
typedef struct {
struct target *target;
- unsigned custom_number;
+ /* Abstract command's regno for a custom register. */
+ unsigned int custom_number;
} riscv_reg_info_t;
#define RISCV_SAMPLE_BUF_TIMESTAMP_BEFORE 0x80
@@ -190,14 +193,6 @@ struct riscv_info {
/* Helper functions that target the various RISC-V debug spec
* implementations. */
- int (*get_register)(struct target *target, riscv_reg_t *value,
- enum gdb_regno regno);
- int (*set_register)(struct target *target, enum gdb_regno regno,
- riscv_reg_t value);
- int (*get_register_buf)(struct target *target, uint8_t *buf,
- enum gdb_regno regno);
- int (*set_register_buf)(struct target *target, enum gdb_regno regno,
- const uint8_t *buf);
int (*select_target)(struct target *target);
int (*get_hart_state)(struct target *target, enum riscv_hart_state *state);
/* Resume this target, as well as every other prepped target that can be
@@ -404,25 +399,6 @@ unsigned int riscv_vlenb(const struct target *target);
/*** Support functions for the RISC-V 'RTOS', which provides multihart support
* without requiring multiple targets. */
-/**
- * Set the register value. For cacheable registers, only the cache is updated
- * (write-back mode).
- */
-int riscv_set_register(struct target *target, enum gdb_regno i, riscv_reg_t v);
-/**
- * Set the register value and immediately write it to the target
- * (write-through mode).
- */
-int riscv_write_register(struct target *target, enum gdb_regno i, riscv_reg_t v);
-/** Get register, from the cache if it's in there. */
-int riscv_get_register(struct target *target, riscv_reg_t *value,
- enum gdb_regno r);
-/** Read the register into the cache, and mark it dirty so it will be restored
- * before resuming. */
-int riscv_save_register(struct target *target, enum gdb_regno regid);
-/** Write all dirty registers to the target. */
-int riscv_flush_registers(struct target *target);
-
/* Checks the state of the current hart -- "is_halted" checks the actual
* on-device register. */
int riscv_get_hart_state(struct target *target, enum riscv_hart_state *state);
@@ -448,8 +424,6 @@ int riscv_add_watchpoint(struct target *target, struct watchpoint *watchpoint);
int riscv_remove_watchpoint(struct target *target,
struct watchpoint *watchpoint);
-int riscv_init_registers(struct target *target);
-
void riscv_semihosting_init(struct target *target);
enum semihosting_result riscv_semihosting(struct target *target, int *retval);
diff --git a/src/target/riscv/riscv_reg.c b/src/target/riscv/riscv_reg.c
new file mode 100644
index 0000000..f8e5dfe
--- /dev/null
+++ b/src/target/riscv/riscv_reg.c
@@ -0,0 +1,968 @@
+// 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"
+/**
+ * TODO: Currently `reg->get/set` is implemented in terms of
+ * `riscv_get/set_register`. However, the intention behind
+ * `riscv_get/set_register` is to work with the cache, therefore it accesses
+ * and modifyes register cache directly. The idea is to implement
+ * `riscv_get/set_register` in terms of `riscv_reg_impl_cache_entry` and
+ * `reg->get/set`.
+ * Once this is done, the following includes should be removed.
+ */
+#include "debug_defines.h"
+#include "riscv-011.h"
+#include "riscv-013.h"
+#include "field_helpers.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];
+}
+
+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:
+ 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;
+}
+
+static int set_reg_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)
+{
+ 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, gdb_regno_exist(target, regno),
+ 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 (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;
+}
+
+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_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;
+ }
+ }
+}
+
+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.
+ */
+ for (unsigned int number = target->reg_cache->num_regs; number-- > 0; ) {
+ struct reg *reg = riscv_reg_impl_cache_entry(target, number);
+ if (reg->valid && reg->dirty) {
+ riscv_reg_t value = buf_get_u64(reg->value, 0, reg->size);
+
+ LOG_TARGET_DEBUG(target, "%s is dirty; write back 0x%" PRIx64,
+ reg->name, value);
+ if (riscv_reg_write(target, number, value) != ERROR_OK)
+ return ERROR_FAIL;
+ }
+ }
+ LOG_TARGET_DEBUG(target, "Flush of register cache completed");
+ return ERROR_OK;
+}
+
+/**
+ * This function is used internally by functions that change register values.
+ * If `write_through` is true, it is ensured that the value of the target's
+ * register is set to be equal to the `value` argument. The cached value is
+ * updated if the register is cacheable.
+ * TODO: Currently `reg->get/set` is implemented in terms of
+ * `riscv_get/set_register`. However, the intention behind
+ * `riscv_get/set_register` is to work with the cache, therefore it accesses
+ * and modifyes register cache directly. The idea is to implement
+ * `riscv_get/set_register` in terms of `riscv_reg_impl_cache_entry` and
+ * `reg->get/set`.
+ */
+static int riscv_set_or_write_register(struct target *target,
+ enum gdb_regno regid, riscv_reg_t value, bool write_through)
+{
+ RISCV_INFO(r);
+ assert(r);
+ if (r->dtm_version == DTM_DTMCS_VERSION_0_11)
+ return riscv011_set_register(target, regid, value);
+
+ keep_alive();
+
+ if (regid == GDB_REGNO_PC) {
+ return riscv_set_or_write_register(target, GDB_REGNO_DPC, value, write_through);
+ } else if (regid == GDB_REGNO_PRIV) {
+ riscv_reg_t dcsr;
+
+ if (riscv_reg_get(target, &dcsr, GDB_REGNO_DCSR) != ERROR_OK)
+ return ERROR_FAIL;
+ dcsr = set_field(dcsr, CSR_DCSR_PRV, get_field(value, VIRT_PRIV_PRV));
+ dcsr = set_field(dcsr, CSR_DCSR_V, get_field(value, VIRT_PRIV_V));
+ 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);
+
+ if (!reg->exist) {
+ LOG_TARGET_DEBUG(target, "Register %s does not exist.", reg->name);
+ return ERROR_FAIL;
+ }
+
+ if (target->state != TARGET_HALTED) {
+ LOG_TARGET_DEBUG(target,
+ "Target not halted, writing to target: %s <- 0x%" PRIx64,
+ reg->name, value);
+ return riscv013_set_register(target, regid, value);
+ }
+
+ const bool need_to_write = !reg->valid || reg->dirty ||
+ value != buf_get_u64(reg->value, 0, reg->size);
+ const bool cacheable = riscv_reg_impl_gdb_regno_cacheable(regid, need_to_write);
+
+ if (!cacheable || (write_through && need_to_write)) {
+ LOG_TARGET_DEBUG(target,
+ "Writing to target: %s <- 0x%" PRIx64 " (cacheable=%s, valid=%s, dirty=%s)",
+ reg->name, value, cacheable ? "true" : "false",
+ reg->valid ? "true" : "false",
+ reg->dirty ? "true" : "false");
+ if (riscv013_set_register(target, regid, value) != ERROR_OK)
+ return ERROR_FAIL;
+
+ reg->dirty = false;
+ } else {
+ reg->dirty = need_to_write;
+ }
+
+ buf_set_u64(reg->value, 0, reg->size, value);
+ reg->valid = cacheable;
+
+ LOG_TARGET_DEBUG(target,
+ "Wrote 0x%" PRIx64 " to %s (cacheable=%s, valid=%s, dirty=%s)",
+ value, reg->name, cacheable ? "true" : "false",
+ reg->valid ? "true" : "false",
+ reg->dirty ? "true" : "false");
+ return ERROR_OK;
+}
+
+/**
+ * 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.
+ * TODO: Currently `reg->get/set` is implemented in terms of
+ * `riscv_get/set_register`. However, the intention behind
+ * `riscv_get/set_register` is to work with the cache, therefore it accesses
+ * and modifyes register cache directly. The idea is to implement
+ * `riscv_get/set_register` in terms of `riscv_reg_impl_cache_entry` and
+ * `reg->get/set`.
+ */
+int riscv_reg_set(struct target *target, enum gdb_regno regid,
+ riscv_reg_t value)
+{
+ return riscv_set_or_write_register(target, regid, value,
+ /* write_through */ false);
+}
+
+/**
+ * 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.
+ * TODO: Currently `reg->get/set` is implemented in terms of
+ * `riscv_get/set_register`. However, the intention behind
+ * `riscv_get/set_register` is to work with the cache, therefore it accesses
+ * and modifyes register cache directly. The idea is to implement
+ * `riscv_get/set_register` in terms of `riscv_reg_impl_cache_entry` and
+ * `reg->get/set`.
+ */
+int riscv_reg_write(struct target *target, enum gdb_regno regid,
+ riscv_reg_t value)
+{
+ return riscv_set_or_write_register(target, regid, value,
+ /* write_through */ true);
+}
+
+/**
+ * This function is used to get the value of a register. If possible, the value
+ * in cache will be updated.
+ * TODO: Currently `reg->get/set` is implemented in terms of
+ * `riscv_get/set_register`. However, the intention behind
+ * `riscv_get/set_register` is to work with the cache, therefore it accesses
+ * and modifyes register cache directly. The idea is to implement
+ * `riscv_get/set_register` in terms of `riscv_reg_impl_cache_entry` and
+ * `reg->get/set`.
+ */
+int riscv_reg_get(struct target *target, riscv_reg_t *value,
+ enum gdb_regno regid)
+{
+ RISCV_INFO(r);
+ assert(r);
+ if (r->dtm_version == DTM_DTMCS_VERSION_0_11)
+ return riscv013_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);
+ if (!reg->exist) {
+ LOG_TARGET_DEBUG(target, "Register %s does not exist.", reg->name);
+ return ERROR_FAIL;
+ }
+
+ if (reg->valid) {
+ *value = buf_get_u64(reg->value, 0, reg->size);
+ LOG_TARGET_DEBUG(target, "Read %s: 0x%" PRIx64 " (cached)", reg->name,
+ *value);
+ return ERROR_OK;
+ }
+
+ LOG_TARGET_DEBUG(target, "Reading %s from target", reg->name);
+ if (riscv013_get_register(target, value, regid) != ERROR_OK)
+ return ERROR_FAIL;
+
+ buf_set_u64(reg->value, 0, reg->size, *value);
+ reg->valid = riscv_reg_impl_gdb_regno_cacheable(regid, /* is write? */ false) &&
+ target->state == TARGET_HALTED;
+ reg->dirty = false;
+
+ LOG_TARGET_DEBUG(target, "Read %s: 0x%" PRIx64, reg->name, *value);
+ return ERROR_OK;
+}
diff --git a/src/target/riscv/riscv_reg.h b/src/target/riscv/riscv_reg.h
new file mode 100644
index 0000000..264addd
--- /dev/null
+++ b/src/target/riscv/riscv_reg.h
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#ifndef OPENOCD_TARGET_RISCV_RISCV_REG_H
+#define OPENOCD_TARGET_RISCV_RISCV_REG_H
+
+#include "target/target.h"
+#include "target/register.h"
+
+/**
+ * This file describes the register cache interface available to the RISC-V
+ * target. Functions declared here should be safe to use once register cache is
+ * completely initialized and may be used with caution during register cache
+ * initialization.
+ */
+
+/** Return the name of the register by it's number in register cache. */
+const char *riscv_reg_gdb_regno_name(const struct target *target, enum gdb_regno regno);
+
+/** Free register cache and associated structures. */
+void riscv_reg_free_all(struct target *target);
+
+/** Write all dirty registers to the target. */
+int riscv_reg_flush_all(struct target *target);
+/**
+ * Set the register value. For cacheable registers, only the cache is updated
+ * (write-back mode).
+ */
+int riscv_reg_set(struct target *target, enum gdb_regno i, riscv_reg_t v);
+/**
+ * Set the register value and immediately write it to the target
+ * (write-through mode).
+ */
+int riscv_reg_write(struct target *target, enum gdb_regno i, riscv_reg_t v);
+/** Get register, from the cache if it's in there. */
+int riscv_reg_get(struct target *target, riscv_reg_t *value,
+ enum gdb_regno r);
+
+#endif /* OPENOCD_TARGET_RISCV_RISCV_REG_H */
diff --git a/src/target/riscv/riscv_reg_impl.h b/src/target/riscv/riscv_reg_impl.h
new file mode 100644
index 0000000..906a5b6
--- /dev/null
+++ b/src/target/riscv/riscv_reg_impl.h
@@ -0,0 +1,214 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#ifndef OPENOCD_TARGET_RISCV_RISCV_REG_IMPL_H
+#define OPENOCD_TARGET_RISCV_RISCV_REG_IMPL_H
+
+#include "gdb_regs.h"
+#include "riscv.h"
+#include "target/target.h"
+#include "target/register.h"
+#include <assert.h>
+
+/**
+ * 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.
+ * - initialized until `riscv_reg_free_all()` is called.
+ */
+static inline bool riscv_reg_impl_is_initialized(const struct reg *reg)
+{
+ assert(reg);
+ if (!reg->feature) {
+ const struct reg default_reg = {0};
+ assert(!memcmp(&default_reg, reg, sizeof(*reg)));
+ return false;
+ }
+ 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;
+}
+/**
+ * Initialize register cache. Note, that each specific register cache entry is
+ * not initialized by this function.
+ */
+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);
+
+/** Return the entry in the register cache of the target. */
+struct reg *riscv_reg_impl_cache_entry(const struct target *target,
+ uint32_t number);
+
+/** Return the target that owns the cache entry. */
+struct target *riscv_reg_impl_get_target(const struct reg *reg);
+
+static inline void init_shared_reg_info(struct target *target)
+{
+ RISCV_INFO(info);
+ info->shared_reg_info.target = target;
+ info->shared_reg_info.custom_number = 0;
+}
+
+/** TODO: vector register type description can be moved into `riscv013_info_t`,
+ * since 0.11 targets do not support access to vector registers. */
+static inline void riscv_reg_impl_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;
+}
+
+/** Expose additional CSRs, as specified by `riscv_info_t::expose_csr` list. */
+int riscv_reg_impl_expose_csrs(const struct target *target);
+
+/** Hide additional CSRs, as specified by `riscv_info_t::hide_csr` list. */
+void riscv_reg_impl_hide_csrs(const struct target *target);
+
+/**
+ * If write is true:
+ * return true iff we are guaranteed that the register will contain exactly
+ * the value we just wrote when it's read.
+ * If write is false:
+ * return true iff we are guaranteed that the register will read the same
+ * value in the future as the value we just read.
+ */
+static inline bool riscv_reg_impl_gdb_regno_cacheable(enum gdb_regno regno,
+ bool is_write)
+{
+ if (regno == GDB_REGNO_ZERO)
+ return !is_write;
+
+ /* GPRs, FPRs, vector registers are just normal data stores. */
+ if (regno <= GDB_REGNO_XPR31 ||
+ (regno >= GDB_REGNO_FPR0 && regno <= GDB_REGNO_FPR31) ||
+ (regno >= GDB_REGNO_V0 && regno <= GDB_REGNO_V31))
+ return true;
+
+ /* Most CSRs won't change value on us, but we can't assume it about arbitrary
+ * CSRs. */
+ switch (regno) {
+ case GDB_REGNO_DPC:
+ case GDB_REGNO_VSTART:
+ case GDB_REGNO_VXSAT:
+ case GDB_REGNO_VXRM:
+ case GDB_REGNO_VLENB:
+ case GDB_REGNO_VL:
+ case GDB_REGNO_VTYPE:
+ case GDB_REGNO_MISA:
+ case GDB_REGNO_DCSR:
+ case GDB_REGNO_DSCRATCH0:
+ case GDB_REGNO_MSTATUS:
+ case GDB_REGNO_MEPC:
+ case GDB_REGNO_MCAUSE:
+ case GDB_REGNO_SATP:
+ /*
+ * WARL registers might not contain the value we just wrote, but
+ * these ones won't spontaneously change their value either. *
+ */
+ return !is_write;
+
+ case GDB_REGNO_TSELECT: /* I think this should be above, but then it doesn't work. */
+ case GDB_REGNO_TDATA1: /* Changes value when tselect is changed. */
+ case GDB_REGNO_TDATA2: /* Changes value when tselect is changed. */
+ default:
+ return false;
+ }
+}
+#endif /* OPENOCD_TARGET_RISCV_RISCV_REG_IMPL_H */
diff --git a/src/target/riscv/riscv_semihosting.c b/src/target/riscv/riscv_semihosting.c
index 9c708c8..0f4f6b5 100644
--- a/src/target/riscv/riscv_semihosting.c
+++ b/src/target/riscv/riscv_semihosting.c
@@ -32,6 +32,7 @@
#include "target/target.h"
#include "riscv.h"
+#include "riscv_reg.h"
static int riscv_semihosting_setup(struct target *target, int enable);
static int riscv_semihosting_post_result(struct target *target);
@@ -67,7 +68,7 @@ enum semihosting_result riscv_semihosting(struct target *target, int *retval)
}
riscv_reg_t pc;
- int result = riscv_get_register(target, &pc, GDB_REGNO_PC);
+ int result = riscv_reg_get(target, &pc, GDB_REGNO_PC);
if (result != ERROR_OK)
return SEMIHOSTING_ERROR;
@@ -107,13 +108,13 @@ enum semihosting_result riscv_semihosting(struct target *target, int *retval)
riscv_reg_t r0;
riscv_reg_t r1;
- result = riscv_get_register(target, &r0, GDB_REGNO_A0);
+ result = riscv_reg_get(target, &r0, GDB_REGNO_A0);
if (result != ERROR_OK) {
LOG_TARGET_ERROR(target, "Could not read semihosting operation code (register a0)");
return SEMIHOSTING_ERROR;
}
- result = riscv_get_register(target, &r1, GDB_REGNO_A1);
+ result = riscv_reg_get(target, &r1, GDB_REGNO_A1);
if (result != ERROR_OK) {
LOG_TARGET_ERROR(target, "Could not read semihosting operation code (register a1)");
return SEMIHOSTING_ERROR;
@@ -140,7 +141,7 @@ enum semihosting_result riscv_semihosting(struct target *target, int *retval)
}
/* Resume right after the EBREAK 4 bytes instruction. */
- *retval = riscv_set_register(target, GDB_REGNO_PC, pc + 4);
+ *retval = riscv_reg_set(target, GDB_REGNO_PC, pc + 4);
if (*retval != ERROR_OK)
return SEMIHOSTING_ERROR;
@@ -184,6 +185,6 @@ static int riscv_semihosting_post_result(struct target *target)
}
LOG_TARGET_DEBUG(target, "Result: 0x%" PRIx64, semihosting->result);
- riscv_set_register(target, GDB_REGNO_A0, semihosting->result);
+ riscv_reg_set(target, GDB_REGNO_A0, semihosting->result);
return 0;
}