From 84b1597bc543c4029954af57f928037b457a9d61 Mon Sep 17 00:00:00 2001 From: Evgeniy Naydanov Date: Fri, 19 Apr 2024 20:43:47 +0300 Subject: [PATCH] target/riscv: separate register cache stuff into files This commits 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). Checkpatch-ignore: SPDX_LICENSE_TAG Change-Id: I7918f78d0d79b97188c5703efd0296660e529f2a Signed-off-by: Evgeniy Naydanov --- src/target/riscv/Makefile.am | 7 + src/target/riscv/gdb_regs.h | 4 +- src/target/riscv/riscv-011.c | 18 +- src/target/riscv/riscv-011.h | 13 + src/target/riscv/riscv-011_reg.c | 60 ++ src/target/riscv/riscv-011_reg.h | 17 + src/target/riscv/riscv-013.c | 25 +- src/target/riscv/riscv-013.h | 20 + src/target/riscv/riscv-013_reg.c | 161 ++++ src/target/riscv/riscv-013_reg.h | 29 + src/target/riscv/riscv.c | 1318 +------------------------- src/target/riscv/riscv.h | 36 +- src/target/riscv/riscv_reg.c | 966 +++++++++++++++++++ src/target/riscv/riscv_reg.h | 36 + src/target/riscv/riscv_reg_impl.h | 211 +++++ src/target/riscv/riscv_semihosting.c | 1 + 16 files changed, 1548 insertions(+), 1374 deletions(-) create mode 100644 src/target/riscv/riscv-011.h create mode 100644 src/target/riscv/riscv-011_reg.c create mode 100644 src/target/riscv/riscv-011_reg.h create mode 100644 src/target/riscv/riscv-013.h create mode 100644 src/target/riscv/riscv-013_reg.c create mode 100644 src/target/riscv/riscv-013_reg.h create mode 100644 src/target/riscv/riscv_reg.c create mode 100644 src/target/riscv/riscv_reg.h create mode 100644 src/target/riscv/riscv_reg_impl.h diff --git a/src/target/riscv/Makefile.am b/src/target/riscv/Makefile.am index 7f25eca7bd..aa82f58119 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 92c8cc5f3d..d606f736b6 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/riscv-011.c b/src/target/riscv/riscv-011.c index df59a49e1b..ef5cd97040 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. ***/ @@ -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. ***/ @@ -1329,7 +1331,7 @@ static int register_write(struct target *target, unsigned int number, 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_init_registers(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_init_registers(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 0000000000..2eb9894919 --- /dev/null +++ b/src/target/riscv/riscv-011.h @@ -0,0 +1,13 @@ +#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 0000000000..8b43ffb45b --- /dev/null +++ b/src/target/riscv/riscv-011_reg.c @@ -0,0 +1,60 @@ +#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 = get_target_from_reg(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 = get_target_from_reg(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 init_reg(target, regno, riscv011_gdb_regno_reg_type(regno)); +} + +int riscv011_init_registers(struct target *target) +{ + //TODO move to init + if (riscv_init_reg_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 (expose_csrs(target) != ERROR_OK) + return ERROR_FAIL; + + 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 0000000000..150730c278 --- /dev/null +++ b/src/target/riscv/riscv-011_reg.h @@ -0,0 +1,17 @@ +#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_init_registers(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 191a17e871..17f307a0d6 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); @@ -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_init_registers(target) != ERROR_OK) return ERROR_FAIL; if (set_dcsr_ebreak(target, false) != ERROR_OK) @@ -2557,8 +2556,8 @@ static int cleanup_after_vector_access(struct target *target, 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); @@ -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); @@ -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; @@ -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 @@ -5177,7 +5172,7 @@ 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", diff --git a/src/target/riscv/riscv-013.h b/src/target/riscv/riscv-013.h new file mode 100644 index 0000000000..a2de6907c6 --- /dev/null +++ b/src/target/riscv/riscv-013.h @@ -0,0 +1,20 @@ +#ifndef OPENOCD_TARGET_RISCV_RISCV_013_H +#define OPENOCD_TARGET_RISCV_RISCV_013_H + +#include "riscv.h" + +/** + * TODO: This functions will be and replaced here by specific access methods + * (e.g. abstract commands, program buffer writes and so on.), while specifics + * on how to use them 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 0000000000..638f5fa88b --- /dev/null +++ b/src/target/riscv/riscv-013_reg.c @@ -0,0 +1,161 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "riscv-013_reg.h" + +#include "riscv_reg.h" // TODO: should be removed +#include "riscv_reg_impl.h" +#include "riscv-013.h" +#include + +static int riscv013_reg_get(struct reg *reg) +{ + struct target *target = get_target_from_reg(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 = 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; +} + +static int riscv013_reg_set(struct reg *reg, uint8_t *buf) +{ + struct target *target = get_target_from_reg(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 = 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 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 init_reg(target, regno, riscv013_gdb_regno_reg_type(regno)); +} + +int riscv013_init_registers(struct target *target) +{ + //TODO move to init + if (riscv_init_reg_cache(target) != ERROR_OK) + return ERROR_FAIL; + + init_shared_reg_info(target); + + 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 (expose_csrs(target) != ERROR_OK) + return ERROR_FAIL; + + 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 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; +} diff --git a/src/target/riscv/riscv-013_reg.h b/src/target/riscv/riscv-013_reg.h new file mode 100644 index 0000000000..407a8949ae --- /dev/null +++ b/src/target/riscv/riscv-013_reg.h @@ -0,0 +1,29 @@ +#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_init_registers(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 can be probably restricted in such a way that only GPRs can be + * saved. + */ +int riscv_save_register(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 b2612c2020..73b22f23eb 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); @@ -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) { @@ -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); @@ -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: - * - * - * - * - * - * - * - * - * - * - * - * - */ - - 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 d3703d1aeb..c27e931507 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 #include @@ -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 0000000000..adcbbd37b0 --- /dev/null +++ b/src/target/riscv/riscv_reg.c @@ -0,0 +1,966 @@ +#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 `get_reg_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 *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 *get_target_from_reg(const struct reg *reg) +{ + assert(reg_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 *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]; +} + +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); +} + +int init_reg(struct target *target, uint32_t regno, const struct reg_arch_type *reg_type) +{ + struct reg * const reg = get_reg_cache_entry(target, regno); + if (reg_is_initialized(reg)) + return ERROR_OK; + reg->number = regno; + reg->type = reg_type; + reg->dirty = false; + reg->valid = false; + reg->hidden = false; + reg->name = gdb_regno_name(target, regno); + reg->feature = gdb_regno_feature(regno); + reg->caller_save = gdb_regno_caller_save(regno); + reg->reg_data_type = gdb_regno_reg_data_type(target, regno); + reg->group = gdb_regno_group(regno); + if (regno < GDB_REGNO_COUNT) { + RISCV_INFO(info); + reg->arch_info = &info->shared_reg_info; + } else { + reg->arch_info = calloc(1, sizeof(riscv_reg_info_t)); + if (!reg->arch_info) { + LOG_ERROR("Out of memory."); + return ERROR_FAIL; + } + riscv_reg_info_t * const reg_arch_info = reg->arch_info; + reg_arch_info->target = target; + reg_arch_info->custom_number = gdb_regno_custom_number(target, regno); + } + return resize_reg(target, regno, gdb_regno_exist(target, regno), + gdb_regno_size(target, regno)); +} + +static int 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_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; +} + +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; +} + +void hide_csrs(const struct target *target) +{ + RISCV_INFO(info); + range_list_t *entry; + list_for_each_entry(entry, &info->hide_csr, list) { + assert(entry->high <= GDB_REGNO_CSR4095 - GDB_REGNO_CSR0); + const enum gdb_regno last_regno = GDB_REGNO_CSR0 + entry->high; + for (enum gdb_regno regno = GDB_REGNO_CSR0 + entry->low; + regno <= last_regno; ++regno) { + struct reg * const reg = get_reg_cache_entry(target, regno); + const unsigned int csr_number = regno - GDB_REGNO_CSR0; + if (!reg->exist) { + LOG_TARGET_WARNING(target, + "Not hiding CSR %d: register does not exist.", + csr_number); + continue; + } + LOG_TARGET_DEBUG(target, "Hiding CSR %d (name=%s).", csr_number, reg->name); + reg->hidden = true; + } + } +} + +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; +} + +int riscv_flush_registers(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 = 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 (riscv_write_register(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 `get_reg_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_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 riscv013_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 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 = 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 `get_reg_cache_entry` and + * `reg->get/set`. + */ +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. + * 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 `get_reg_cache_entry` and + * `reg->get/set`. + */ +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. + * 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 `get_reg_cache_entry` and + * `reg->get/set`. + */ +int riscv_get_register(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_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 riscv013_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 (riscv013_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; +} diff --git a/src/target/riscv/riscv_reg.h b/src/target/riscv/riscv_reg.h new file mode 100644 index 0000000000..71dacd4531 --- /dev/null +++ b/src/target/riscv/riscv_reg.h @@ -0,0 +1,36 @@ +#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 *gdb_regno_name(const struct target *target, enum gdb_regno regno); + +/** Free register cache and associated structures. */ +void riscv_free_registers(struct target *target); + +/** Write all dirty registers to the target. */ +int riscv_flush_registers(struct target *target); +/** + * 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); + +#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 0000000000..8df3036c4a --- /dev/null +++ b/src/target/riscv/riscv_reg_impl.h @@ -0,0 +1,211 @@ +#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 + +/** + * 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_init_reg_cache()` + * - not initialized before the call to `init_reg()` with appropriate regno. + * - initialized untill `riscv_free_registers()` is called. + */ +static inline 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; +} +/** + * Initialize register cache. Note, that each specific register cache entry is + * not initialized by this function. + */ +int riscv_init_reg_cache(struct target *target); + +/** Initialize register. */ +int init_reg(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 *get_reg_cache_entry(const struct target *target, + uint32_t number); + +/** Return the target that owns the cache entry. */ +struct target *get_target_from_reg(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 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: + * + * + * + * + * + * + * + * + * + * + * + * + */ + + 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 expose_csrs(const struct target *target); + +/** Hide additional CSRs, as specified by `riscv_info_t::hide_csr` list. */ +void 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 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 9c708c8a48..35590eebde 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);