target/riscv: separate register cache stuff into files
This commit creates file structure for register cache related functions. Specifically: * `riscv_reg.h` -- general interface to registers. Safe to use after register cache initialization is successful. * `riscv_reg_impl.h` -- helper functions to use while implementing register cache initialization. * `riscv_reg.c` -- definitions of functions from `riscv_reg.h` and `riscv_reg_impl.h`. * `riscv-011_reg.h` -- register cache interface specific to 0.11 targets. * `riscv-013_reg.h` -- register cache interface specific to 0.13+ targets. * `riscv-011/0.13.h` -- version-specific methods used to access registers. Will be extended as needed once other functionality (not related to register access) is separated (e.g. DM/DTM specific stuff). Change-Id: I7918f78d0d79b97188c5703efd0296660e529f2a Signed-off-by: Evgeniy Naydanov <evgeniy.naydanov@syntacore.com>
This commit is contained in:
parent
cb87885c00
commit
3883b03aaa
|
@ -11,11 +11,18 @@ noinst_LTLIBRARIES += %D%/libriscv.la
|
||||||
%D%/opcodes.h \
|
%D%/opcodes.h \
|
||||||
%D%/program.h \
|
%D%/program.h \
|
||||||
%D%/riscv.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%/batch.c \
|
||||||
%D%/program.c \
|
%D%/program.c \
|
||||||
%D%/riscv-011.c \
|
%D%/riscv-011.c \
|
||||||
|
%D%/riscv-011_reg.c \
|
||||||
%D%/riscv-013.c \
|
%D%/riscv-013.c \
|
||||||
|
%D%/riscv-013_reg.c \
|
||||||
%D%/riscv.c \
|
%D%/riscv.c \
|
||||||
|
%D%/riscv_reg.c \
|
||||||
%D%/riscv_semihosting.c \
|
%D%/riscv_semihosting.c \
|
||||||
%D%/debug_defines.c \
|
%D%/debug_defines.c \
|
||||||
%D%/debug_reg_printer.c
|
%D%/debug_reg_printer.c
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
#ifndef TARGET__RISCV__GDB_REGS_H
|
#ifndef TARGET__RISCV__GDB_REGS_H
|
||||||
#define 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
|
/* 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. */
|
* its source tree. We must interpret the numbers the same here. */
|
||||||
enum gdb_regno {
|
enum gdb_regno {
|
||||||
|
@ -123,6 +125,4 @@ enum gdb_regno {
|
||||||
GDB_REGNO_COUNT
|
GDB_REGNO_COUNT
|
||||||
};
|
};
|
||||||
|
|
||||||
const char *gdb_regno_name(const struct target *target, enum gdb_regno regno);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -41,7 +41,7 @@ int riscv_program_write(struct riscv_program *program);
|
||||||
|
|
||||||
/* Executes a program, returning 0 if the program successfully executed. Note
|
/* Executes a program, returning 0 if the program successfully executed. Note
|
||||||
* that this may cause registers to be saved or restored, which could result to
|
* that this may cause registers to be saved or restored, which could result to
|
||||||
* calls to things like riscv_save_register which itself could require a
|
* calls to things like riscv013_reg_save which itself could require a
|
||||||
* program to execute. That's OK, just make sure this eventually terminates.
|
* program to execute. That's OK, just make sure this eventually terminates.
|
||||||
* */
|
* */
|
||||||
int riscv_program_exec(struct riscv_program *p, struct target *t);
|
int riscv_program_exec(struct riscv_program *p, struct target *t);
|
||||||
|
|
|
@ -13,6 +13,8 @@
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "riscv-011.h"
|
||||||
|
|
||||||
#include "target/target.h"
|
#include "target/target.h"
|
||||||
#include "target/algorithm.h"
|
#include "target/algorithm.h"
|
||||||
#include "target/target_type.h"
|
#include "target/target_type.h"
|
||||||
|
@ -22,6 +24,8 @@
|
||||||
#include "target/breakpoints.h"
|
#include "target/breakpoints.h"
|
||||||
#include "helper/time_support.h"
|
#include "helper/time_support.h"
|
||||||
#include "riscv.h"
|
#include "riscv.h"
|
||||||
|
#include "riscv_reg.h"
|
||||||
|
#include "riscv-011_reg.h"
|
||||||
#include "asm.h"
|
#include "asm.h"
|
||||||
#include "gdb_regs.h"
|
#include "gdb_regs.h"
|
||||||
#include "field_helpers.h"
|
#include "field_helpers.h"
|
||||||
|
@ -210,8 +214,6 @@ typedef struct {
|
||||||
|
|
||||||
static int poll_target(struct target *target, bool announce);
|
static int poll_target(struct target *target, bool announce);
|
||||||
static int riscv011_poll(struct target *target);
|
static int riscv011_poll(struct target *target);
|
||||||
static int get_register(struct target *target, riscv_reg_t *value,
|
|
||||||
enum gdb_regno regid);
|
|
||||||
|
|
||||||
/*** Utility functions. ***/
|
/*** Utility functions. ***/
|
||||||
|
|
||||||
|
@ -1045,7 +1047,7 @@ static int read_remote_csr(struct target *target, uint64_t *value, uint32_t csr)
|
||||||
uint32_t exception = cache_get32(target, info->dramsize-1);
|
uint32_t exception = cache_get32(target, info->dramsize-1);
|
||||||
if (exception) {
|
if (exception) {
|
||||||
LOG_WARNING("Got exception 0x%x when reading %s", exception,
|
LOG_WARNING("Got exception 0x%x when reading %s", exception,
|
||||||
gdb_regno_name(target, GDB_REGNO_CSR0 + csr));
|
riscv_reg_gdb_regno_name(target, GDB_REGNO_CSR0 + csr));
|
||||||
*value = ~0;
|
*value = ~0;
|
||||||
return ERROR_FAIL;
|
return ERROR_FAIL;
|
||||||
}
|
}
|
||||||
|
@ -1111,7 +1113,7 @@ static int execute_resume(struct target *target, bool step)
|
||||||
|
|
||||||
LOG_DEBUG("step=%d", step);
|
LOG_DEBUG("step=%d", step);
|
||||||
|
|
||||||
if (riscv_flush_registers(target) != ERROR_OK)
|
if (riscv_reg_flush_all(target) != ERROR_OK)
|
||||||
return ERROR_FAIL;
|
return ERROR_FAIL;
|
||||||
|
|
||||||
maybe_write_tselect(target);
|
maybe_write_tselect(target);
|
||||||
|
@ -1225,7 +1227,7 @@ static int update_mstatus_actual(struct target *target)
|
||||||
/* Force reading the register. In that process mstatus_actual will be
|
/* Force reading the register. In that process mstatus_actual will be
|
||||||
* updated. */
|
* updated. */
|
||||||
riscv_reg_t mstatus;
|
riscv_reg_t mstatus;
|
||||||
return get_register(target, &mstatus, GDB_REGNO_MSTATUS);
|
return riscv011_get_register(target, &mstatus, GDB_REGNO_MSTATUS);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*** OpenOCD target functions. ***/
|
/*** OpenOCD target functions. ***/
|
||||||
|
@ -1247,7 +1249,7 @@ static int register_read(struct target *target, riscv_reg_t *value, int regnum)
|
||||||
|
|
||||||
uint32_t exception = cache_get32(target, info->dramsize-1);
|
uint32_t exception = cache_get32(target, info->dramsize-1);
|
||||||
if (exception) {
|
if (exception) {
|
||||||
LOG_WARNING("Got exception 0x%x when reading %s", exception, gdb_regno_name(target, regnum));
|
LOG_WARNING("Got exception 0x%x when reading %s", exception, riscv_reg_gdb_regno_name(target, regnum));
|
||||||
*value = ~0;
|
*value = ~0;
|
||||||
return ERROR_FAIL;
|
return ERROR_FAIL;
|
||||||
}
|
}
|
||||||
|
@ -1322,14 +1324,14 @@ static int register_write(struct target *target, unsigned int number,
|
||||||
uint32_t exception = cache_get32(target, info->dramsize-1);
|
uint32_t exception = cache_get32(target, info->dramsize-1);
|
||||||
if (exception) {
|
if (exception) {
|
||||||
LOG_WARNING("Got exception 0x%x when writing %s", exception,
|
LOG_WARNING("Got exception 0x%x when writing %s", exception,
|
||||||
gdb_regno_name(target, number));
|
riscv_reg_gdb_regno_name(target, number));
|
||||||
return ERROR_FAIL;
|
return ERROR_FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ERROR_OK;
|
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)
|
enum gdb_regno regid)
|
||||||
{
|
{
|
||||||
riscv011_info_t *info = get_info(target);
|
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
|
/* This function is intended to handle accesses to registers through register
|
||||||
* cache. */
|
* 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)
|
riscv_reg_t value)
|
||||||
{
|
{
|
||||||
assert(target->reg_cache);
|
assert(target->reg_cache);
|
||||||
|
@ -1595,7 +1597,7 @@ static int examine(struct target *target)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Update register list to match discovered XLEN/supported extensions. */
|
/* Update register list to match discovered XLEN/supported extensions. */
|
||||||
riscv_init_registers(target);
|
riscv011_reg_init_all(target);
|
||||||
|
|
||||||
info->never_halted = true;
|
info->never_halted = true;
|
||||||
|
|
||||||
|
@ -2391,8 +2393,6 @@ static int init_target(struct command_context *cmd_ctx,
|
||||||
{
|
{
|
||||||
LOG_DEBUG("init");
|
LOG_DEBUG("init");
|
||||||
RISCV_INFO(generic_info);
|
RISCV_INFO(generic_info);
|
||||||
generic_info->get_register = get_register;
|
|
||||||
generic_info->set_register = set_register;
|
|
||||||
generic_info->read_memory = read_memory;
|
generic_info->read_memory = read_memory;
|
||||||
generic_info->authdata_read = &riscv011_authdata_read;
|
generic_info->authdata_read = &riscv011_authdata_read;
|
||||||
generic_info->authdata_write = &riscv011_authdata_write;
|
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(). */
|
/* Assume 32-bit until we discover the real value in examine(). */
|
||||||
generic_info->xlen = 32;
|
generic_info->xlen = 32;
|
||||||
riscv_init_registers(target);
|
riscv011_reg_init_all(target);
|
||||||
|
|
||||||
return ERROR_OK;
|
return ERROR_OK;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||||
|
|
||||||
|
#ifndef OPENOCD_TARGET_RISCV_RISCV_011_H
|
||||||
|
#define OPENOCD_TARGET_RISCV_RISCV_011_H
|
||||||
|
|
||||||
|
#include "riscv.h"
|
||||||
|
#include "gdb_regs.h"
|
||||||
|
#include "target/target.h"
|
||||||
|
|
||||||
|
int riscv011_get_register(struct target *target, riscv_reg_t *value,
|
||||||
|
enum gdb_regno regid);
|
||||||
|
int riscv011_set_register(struct target *target, enum gdb_regno regid,
|
||||||
|
riscv_reg_t value);
|
||||||
|
|
||||||
|
#endif /*OPENOCD_TARGET_RISCV_RISCV_011_H*/
|
|
@ -0,0 +1,61 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "riscv-011_reg.h"
|
||||||
|
|
||||||
|
#include "riscv_reg_impl.h"
|
||||||
|
#include "riscv-011.h"
|
||||||
|
|
||||||
|
static int riscv011_reg_get(struct reg *reg)
|
||||||
|
{
|
||||||
|
struct target * const target = riscv_reg_impl_get_target(reg);
|
||||||
|
riscv_reg_t value;
|
||||||
|
const int result = riscv011_get_register(target, &value, reg->number);
|
||||||
|
if (result != ERROR_OK)
|
||||||
|
return result;
|
||||||
|
buf_set_u64(reg->value, 0, reg->size, value);
|
||||||
|
return ERROR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int riscv011_reg_set(struct reg *reg, uint8_t *buf)
|
||||||
|
{
|
||||||
|
const riscv_reg_t value = buf_get_u64(buf, 0, reg->size);
|
||||||
|
struct target * const target = riscv_reg_impl_get_target(reg);
|
||||||
|
return riscv011_set_register(target, reg->number, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct reg_arch_type *riscv011_gdb_regno_reg_type(uint32_t regno)
|
||||||
|
{
|
||||||
|
static const struct reg_arch_type riscv011_reg_type = {
|
||||||
|
.get = riscv011_reg_get,
|
||||||
|
.set = riscv011_reg_set
|
||||||
|
};
|
||||||
|
return &riscv011_reg_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int riscv011_init_reg(struct target *target, uint32_t regno)
|
||||||
|
{
|
||||||
|
return riscv_reg_impl_init_one(target, regno, riscv011_gdb_regno_reg_type(regno));
|
||||||
|
}
|
||||||
|
|
||||||
|
int riscv011_reg_init_all(struct target *target)
|
||||||
|
{
|
||||||
|
if (riscv_reg_impl_init_cache(target) != ERROR_OK)
|
||||||
|
return ERROR_FAIL;
|
||||||
|
|
||||||
|
init_shared_reg_info(target);
|
||||||
|
|
||||||
|
for (uint32_t regno = 0; regno < target->reg_cache->num_regs; ++regno)
|
||||||
|
if (riscv011_init_reg(target, regno) != ERROR_OK)
|
||||||
|
return ERROR_FAIL;
|
||||||
|
|
||||||
|
if (riscv_reg_impl_expose_csrs(target) != ERROR_OK)
|
||||||
|
return ERROR_FAIL;
|
||||||
|
|
||||||
|
riscv_reg_impl_hide_csrs(target);
|
||||||
|
|
||||||
|
return ERROR_OK;
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||||
|
|
||||||
|
#ifndef OPENOCD_TARGET_RISCV_RISCV_REG_011_H
|
||||||
|
#define OPENOCD_TARGET_RISCV_RISCV_REG_011_H
|
||||||
|
|
||||||
|
#include "target/target.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This file describes additional register cache interface available to the
|
||||||
|
* RISC-V Debug Specification v0.11 targets.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize register cache. After this function all registers can be
|
||||||
|
* safely accessed via functions described here and in `riscv_reg.h`.
|
||||||
|
*/
|
||||||
|
int riscv011_reg_init_all(struct target *target);
|
||||||
|
|
||||||
|
#endif /*OPENOCD_TARGET_RISCV_RISCV_REG_011_H*/
|
|
@ -23,6 +23,9 @@
|
||||||
#include "helper/time_support.h"
|
#include "helper/time_support.h"
|
||||||
#include "helper/list.h"
|
#include "helper/list.h"
|
||||||
#include "riscv.h"
|
#include "riscv.h"
|
||||||
|
#include "riscv-013.h"
|
||||||
|
#include "riscv_reg.h"
|
||||||
|
#include "riscv-013_reg.h"
|
||||||
#include "debug_defines.h"
|
#include "debug_defines.h"
|
||||||
#include "rtos/rtos.h"
|
#include "rtos/rtos.h"
|
||||||
#include "program.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);
|
static int riscv013_clear_abstract_error(struct target *target);
|
||||||
|
|
||||||
/* Implementations of the functions in struct riscv_info. */
|
/* 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 dm013_select_hart(struct target *target, int hart_index);
|
||||||
static int riscv013_halt_prep(struct target *target);
|
static int riscv013_halt_prep(struct target *target);
|
||||||
static int riscv013_halt_go(struct target *target);
|
static int riscv013_halt_go(struct target *target);
|
||||||
|
@ -1050,7 +1049,7 @@ static uint32_t access_register_command(struct target *target, uint32_t number,
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
LOG_TARGET_ERROR(target, "%d-bit register %s not supported.",
|
LOG_TARGET_ERROR(target, "%d-bit register %s not supported.",
|
||||||
size, gdb_regno_name(target, number));
|
size, riscv_reg_gdb_regno_name(target, number));
|
||||||
assert(0);
|
assert(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1218,7 +1217,7 @@ static int examine_progbuf(struct target *target)
|
||||||
return ERROR_OK;
|
return ERROR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (riscv_save_register(target, GDB_REGNO_S0) != ERROR_OK)
|
if (riscv013_reg_save(target, GDB_REGNO_S0) != ERROR_OK)
|
||||||
return ERROR_FAIL;
|
return ERROR_FAIL;
|
||||||
|
|
||||||
struct riscv_program program;
|
struct riscv_program program;
|
||||||
|
@ -1293,12 +1292,12 @@ static int prep_for_register_access(struct target *target,
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_TARGET_DEBUG(target, "Preparing mstatus to access %s",
|
LOG_TARGET_DEBUG(target, "Preparing mstatus to access %s",
|
||||||
gdb_regno_name(target, regno));
|
riscv_reg_gdb_regno_name(target, regno));
|
||||||
|
|
||||||
assert(target->state == TARGET_HALTED &&
|
assert(target->state == TARGET_HALTED &&
|
||||||
"The target must be halted to modify and then restore mstatus");
|
"The target must be halted to modify and then restore mstatus");
|
||||||
|
|
||||||
if (riscv_get_register(target, orig_mstatus, GDB_REGNO_MSTATUS) != ERROR_OK)
|
if (riscv_reg_get(target, orig_mstatus, GDB_REGNO_MSTATUS) != ERROR_OK)
|
||||||
return ERROR_FAIL;
|
return ERROR_FAIL;
|
||||||
|
|
||||||
riscv_reg_t new_mstatus = *orig_mstatus;
|
riscv_reg_t new_mstatus = *orig_mstatus;
|
||||||
|
@ -1309,11 +1308,11 @@ static int prep_for_register_access(struct target *target,
|
||||||
|
|
||||||
new_mstatus = set_field(new_mstatus, field_mask, 1);
|
new_mstatus = set_field(new_mstatus, field_mask, 1);
|
||||||
|
|
||||||
if (riscv_write_register(target, GDB_REGNO_MSTATUS, new_mstatus) != ERROR_OK)
|
if (riscv_reg_write(target, GDB_REGNO_MSTATUS, new_mstatus) != ERROR_OK)
|
||||||
return ERROR_FAIL;
|
return ERROR_FAIL;
|
||||||
|
|
||||||
LOG_TARGET_DEBUG(target, "Prepared to access %s (mstatus=0x%" PRIx64 ")",
|
LOG_TARGET_DEBUG(target, "Prepared to access %s (mstatus=0x%" PRIx64 ")",
|
||||||
gdb_regno_name(target, regno), new_mstatus);
|
riscv_reg_gdb_regno_name(target, regno), new_mstatus);
|
||||||
return ERROR_OK;
|
return ERROR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1325,7 +1324,7 @@ static int cleanup_after_register_access(struct target *target,
|
||||||
return ERROR_OK;
|
return ERROR_OK;
|
||||||
|
|
||||||
LOG_TARGET_DEBUG(target, "Restoring mstatus to 0x%" PRIx64, mstatus);
|
LOG_TARGET_DEBUG(target, "Restoring mstatus to 0x%" PRIx64, mstatus);
|
||||||
return riscv_write_register(target, GDB_REGNO_MSTATUS, mstatus);
|
return riscv_reg_write(target, GDB_REGNO_MSTATUS, mstatus);
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
@ -1544,7 +1543,7 @@ static int fpr_read_progbuf(struct target *target, uint64_t *value,
|
||||||
|
|
||||||
const unsigned int freg = number - GDB_REGNO_FPR0;
|
const unsigned int freg = number - GDB_REGNO_FPR0;
|
||||||
|
|
||||||
if (riscv_save_register(target, GDB_REGNO_S0) != ERROR_OK)
|
if (riscv013_reg_save(target, GDB_REGNO_S0) != ERROR_OK)
|
||||||
return ERROR_FAIL;
|
return ERROR_FAIL;
|
||||||
|
|
||||||
struct riscv_program program;
|
struct riscv_program program;
|
||||||
|
@ -1574,7 +1573,7 @@ static int csr_read_progbuf(struct target *target, uint64_t *value,
|
||||||
assert(target->state == TARGET_HALTED);
|
assert(target->state == TARGET_HALTED);
|
||||||
assert(number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095);
|
assert(number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095);
|
||||||
|
|
||||||
if (riscv_save_register(target, GDB_REGNO_S0) != ERROR_OK)
|
if (riscv013_reg_save(target, GDB_REGNO_S0) != ERROR_OK)
|
||||||
return ERROR_FAIL;
|
return ERROR_FAIL;
|
||||||
|
|
||||||
struct riscv_program program;
|
struct riscv_program program;
|
||||||
|
@ -1602,7 +1601,7 @@ static int register_read_progbuf(struct target *target, uint64_t *value,
|
||||||
return csr_read_progbuf(target, value, number);
|
return csr_read_progbuf(target, value, number);
|
||||||
|
|
||||||
LOG_TARGET_ERROR(target, "Unexpected read of %s via program buffer.",
|
LOG_TARGET_ERROR(target, "Unexpected read of %s via program buffer.",
|
||||||
gdb_regno_name(target, number));
|
riscv_reg_gdb_regno_name(target, number));
|
||||||
return ERROR_FAIL;
|
return ERROR_FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1642,7 +1641,7 @@ static int fpr_write_progbuf(struct target *target, enum gdb_regno number,
|
||||||
assert(number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31);
|
assert(number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31);
|
||||||
const unsigned int freg = number - GDB_REGNO_FPR0;
|
const unsigned int freg = number - GDB_REGNO_FPR0;
|
||||||
|
|
||||||
if (riscv_save_register(target, GDB_REGNO_S0) != ERROR_OK)
|
if (riscv013_reg_save(target, GDB_REGNO_S0) != ERROR_OK)
|
||||||
return ERROR_FAIL;
|
return ERROR_FAIL;
|
||||||
|
|
||||||
struct riscv_program program;
|
struct riscv_program program;
|
||||||
|
@ -1672,11 +1671,11 @@ static int vtype_write_progbuf(struct target *target, riscv_reg_t value)
|
||||||
{
|
{
|
||||||
assert(target->state == TARGET_HALTED);
|
assert(target->state == TARGET_HALTED);
|
||||||
|
|
||||||
if (riscv_save_register(target, GDB_REGNO_S0) != ERROR_OK)
|
if (riscv013_reg_save(target, GDB_REGNO_S0) != ERROR_OK)
|
||||||
return ERROR_FAIL;
|
return ERROR_FAIL;
|
||||||
if (register_write_abstract(target, GDB_REGNO_S0, value) != ERROR_OK)
|
if (register_write_abstract(target, GDB_REGNO_S0, value) != ERROR_OK)
|
||||||
return ERROR_FAIL;
|
return ERROR_FAIL;
|
||||||
if (riscv_save_register(target, GDB_REGNO_S1) != ERROR_OK)
|
if (riscv013_reg_save(target, GDB_REGNO_S1) != ERROR_OK)
|
||||||
return ERROR_FAIL;
|
return ERROR_FAIL;
|
||||||
|
|
||||||
struct riscv_program program;
|
struct riscv_program program;
|
||||||
|
@ -1693,11 +1692,11 @@ static int vl_write_progbuf(struct target *target, riscv_reg_t value)
|
||||||
{
|
{
|
||||||
assert(target->state == TARGET_HALTED);
|
assert(target->state == TARGET_HALTED);
|
||||||
|
|
||||||
if (riscv_save_register(target, GDB_REGNO_S0) != ERROR_OK)
|
if (riscv013_reg_save(target, GDB_REGNO_S0) != ERROR_OK)
|
||||||
return ERROR_FAIL;
|
return ERROR_FAIL;
|
||||||
if (register_write_abstract(target, GDB_REGNO_S0, value) != ERROR_OK)
|
if (register_write_abstract(target, GDB_REGNO_S0, value) != ERROR_OK)
|
||||||
return ERROR_FAIL;
|
return ERROR_FAIL;
|
||||||
if (riscv_save_register(target, GDB_REGNO_S1) != ERROR_OK)
|
if (riscv013_reg_save(target, GDB_REGNO_S1) != ERROR_OK)
|
||||||
return ERROR_FAIL;
|
return ERROR_FAIL;
|
||||||
|
|
||||||
struct riscv_program program;
|
struct riscv_program program;
|
||||||
|
@ -1716,7 +1715,7 @@ static int csr_write_progbuf(struct target *target, enum gdb_regno number,
|
||||||
assert(target->state == TARGET_HALTED);
|
assert(target->state == TARGET_HALTED);
|
||||||
assert(number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095);
|
assert(number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095);
|
||||||
|
|
||||||
if (riscv_save_register(target, GDB_REGNO_S0) != ERROR_OK)
|
if (riscv013_reg_save(target, GDB_REGNO_S0) != ERROR_OK)
|
||||||
return ERROR_FAIL;
|
return ERROR_FAIL;
|
||||||
if (register_write_abstract(target, GDB_REGNO_S0, value) != ERROR_OK)
|
if (register_write_abstract(target, GDB_REGNO_S0, value) != ERROR_OK)
|
||||||
return ERROR_FAIL;
|
return ERROR_FAIL;
|
||||||
|
@ -1748,7 +1747,7 @@ static int register_write_progbuf(struct target *target, enum gdb_regno number,
|
||||||
return csr_write_progbuf(target, number, value);
|
return csr_write_progbuf(target, number, value);
|
||||||
|
|
||||||
LOG_TARGET_ERROR(target, "Unexpected write to %s via program buffer.",
|
LOG_TARGET_ERROR(target, "Unexpected write to %s via program buffer.",
|
||||||
gdb_regno_name(target, number));
|
riscv_reg_gdb_regno_name(target, number));
|
||||||
return ERROR_FAIL;
|
return ERROR_FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1760,7 +1759,7 @@ static int register_write_direct(struct target *target, enum gdb_regno number,
|
||||||
riscv_reg_t value)
|
riscv_reg_t value)
|
||||||
{
|
{
|
||||||
LOG_TARGET_DEBUG(target, "Writing 0x%" PRIx64 " to %s", value,
|
LOG_TARGET_DEBUG(target, "Writing 0x%" PRIx64 " to %s", value,
|
||||||
gdb_regno_name(target, number));
|
riscv_reg_gdb_regno_name(target, number));
|
||||||
|
|
||||||
if (target->state != TARGET_HALTED)
|
if (target->state != TARGET_HALTED)
|
||||||
return register_write_abstract(target, number, value);
|
return register_write_abstract(target, number, value);
|
||||||
|
@ -1778,7 +1777,7 @@ static int register_write_direct(struct target *target, enum gdb_regno number,
|
||||||
return ERROR_FAIL;
|
return ERROR_FAIL;
|
||||||
|
|
||||||
if (result == ERROR_OK)
|
if (result == ERROR_OK)
|
||||||
LOG_TARGET_DEBUG(target, "%s <- 0x%" PRIx64, gdb_regno_name(target, number),
|
LOG_TARGET_DEBUG(target, "%s <- 0x%" PRIx64, riscv_reg_gdb_regno_name(target, number),
|
||||||
value);
|
value);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
@ -1788,7 +1787,7 @@ static int register_write_direct(struct target *target, enum gdb_regno number,
|
||||||
static int register_read_direct(struct target *target, riscv_reg_t *value,
|
static int register_read_direct(struct target *target, riscv_reg_t *value,
|
||||||
enum gdb_regno number)
|
enum gdb_regno number)
|
||||||
{
|
{
|
||||||
LOG_TARGET_DEBUG(target, "Reading %s", gdb_regno_name(target, number));
|
LOG_TARGET_DEBUG(target, "Reading %s", riscv_reg_gdb_regno_name(target, number));
|
||||||
|
|
||||||
if (target->state != TARGET_HALTED)
|
if (target->state != TARGET_HALTED)
|
||||||
return register_read_abstract(target, value, number);
|
return register_read_abstract(target, value, number);
|
||||||
|
@ -1807,7 +1806,7 @@ static int register_read_direct(struct target *target, riscv_reg_t *value,
|
||||||
return ERROR_FAIL;
|
return ERROR_FAIL;
|
||||||
|
|
||||||
if (result == ERROR_OK)
|
if (result == ERROR_OK)
|
||||||
LOG_TARGET_DEBUG(target, "%s = 0x%" PRIx64, gdb_regno_name(target, number),
|
LOG_TARGET_DEBUG(target, "%s = 0x%" PRIx64, riscv_reg_gdb_regno_name(target, number),
|
||||||
*value);
|
*value);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
@ -1847,7 +1846,7 @@ static int set_dcsr_ebreak(struct target *target, bool step)
|
||||||
RISCV013_INFO(info);
|
RISCV013_INFO(info);
|
||||||
riscv_reg_t original_dcsr, dcsr;
|
riscv_reg_t original_dcsr, dcsr;
|
||||||
/* We want to twiddle some bits in the debug CSR so debugging works. */
|
/* We want to twiddle some bits in the debug CSR so debugging works. */
|
||||||
if (riscv_get_register(target, &dcsr, GDB_REGNO_DCSR) != ERROR_OK)
|
if (riscv_reg_get(target, &dcsr, GDB_REGNO_DCSR) != ERROR_OK)
|
||||||
return ERROR_FAIL;
|
return ERROR_FAIL;
|
||||||
original_dcsr = dcsr;
|
original_dcsr = dcsr;
|
||||||
dcsr = set_field(dcsr, CSR_DCSR_STEP, step);
|
dcsr = set_field(dcsr, CSR_DCSR_STEP, step);
|
||||||
|
@ -1857,7 +1856,7 @@ static int set_dcsr_ebreak(struct target *target, bool step)
|
||||||
dcsr = set_field(dcsr, CSR_DCSR_EBREAKVS, r->riscv_ebreaku && riscv_supports_extension(target, 'H'));
|
dcsr = set_field(dcsr, CSR_DCSR_EBREAKVS, r->riscv_ebreaku && riscv_supports_extension(target, 'H'));
|
||||||
dcsr = set_field(dcsr, CSR_DCSR_EBREAKVU, r->riscv_ebreaku && riscv_supports_extension(target, 'H'));
|
dcsr = set_field(dcsr, CSR_DCSR_EBREAKVU, r->riscv_ebreaku && riscv_supports_extension(target, 'H'));
|
||||||
if (dcsr != original_dcsr &&
|
if (dcsr != original_dcsr &&
|
||||||
riscv_set_register(target, GDB_REGNO_DCSR, dcsr) != ERROR_OK)
|
riscv_reg_set(target, GDB_REGNO_DCSR, dcsr) != ERROR_OK)
|
||||||
return ERROR_FAIL;
|
return ERROR_FAIL;
|
||||||
info->dcsr_ebreak_is_set = true;
|
info->dcsr_ebreak_is_set = true;
|
||||||
return ERROR_OK;
|
return ERROR_OK;
|
||||||
|
@ -2332,7 +2331,7 @@ static int examine(struct target *target)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Now init registers based on what we discovered. */
|
/* Now init registers based on what we discovered. */
|
||||||
if (riscv_init_registers(target) != ERROR_OK)
|
if (riscv013_reg_init_all(target) != ERROR_OK)
|
||||||
return ERROR_FAIL;
|
return ERROR_FAIL;
|
||||||
|
|
||||||
if (set_dcsr_ebreak(target, false) != ERROR_OK)
|
if (set_dcsr_ebreak(target, false) != ERROR_OK)
|
||||||
|
@ -2492,14 +2491,14 @@ static int try_set_vsew(struct target *target, unsigned int *debug_vsew)
|
||||||
/* Set standard element width to match XLEN, for vmv instruction to move
|
/* Set standard element width to match XLEN, for vmv instruction to move
|
||||||
* the least significant bits into a GPR.
|
* the least significant bits into a GPR.
|
||||||
*/
|
*/
|
||||||
if (riscv_write_register(target, GDB_REGNO_VTYPE, encoded_vsew << 3) != ERROR_OK)
|
if (riscv_reg_write(target, GDB_REGNO_VTYPE, encoded_vsew << 3) != ERROR_OK)
|
||||||
return ERROR_FAIL;
|
return ERROR_FAIL;
|
||||||
|
|
||||||
if (encoded_vsew == 3 && r->vsew64_supported == YNM_MAYBE) {
|
if (encoded_vsew == 3 && r->vsew64_supported == YNM_MAYBE) {
|
||||||
/* Check that it's supported. */
|
/* Check that it's supported. */
|
||||||
riscv_reg_t vtype;
|
riscv_reg_t vtype;
|
||||||
|
|
||||||
if (riscv_get_register(target, &vtype, GDB_REGNO_VTYPE) != ERROR_OK)
|
if (riscv_reg_get(target, &vtype, GDB_REGNO_VTYPE) != ERROR_OK)
|
||||||
return ERROR_FAIL;
|
return ERROR_FAIL;
|
||||||
if (vtype >> (riscv_xlen(target) - 1)) {
|
if (vtype >> (riscv_xlen(target) - 1)) {
|
||||||
r->vsew64_supported = YNM_NO;
|
r->vsew64_supported = YNM_NO;
|
||||||
|
@ -2532,9 +2531,9 @@ static int prep_for_vector_access(struct target *target,
|
||||||
return ERROR_FAIL;
|
return ERROR_FAIL;
|
||||||
|
|
||||||
/* Save vtype and vl. */
|
/* Save vtype and vl. */
|
||||||
if (riscv_get_register(target, orig_vtype, GDB_REGNO_VTYPE) != ERROR_OK)
|
if (riscv_reg_get(target, orig_vtype, GDB_REGNO_VTYPE) != ERROR_OK)
|
||||||
return ERROR_FAIL;
|
return ERROR_FAIL;
|
||||||
if (riscv_get_register(target, orig_vl, GDB_REGNO_VL) != ERROR_OK)
|
if (riscv_reg_get(target, orig_vl, GDB_REGNO_VL) != ERROR_OK)
|
||||||
return ERROR_FAIL;
|
return ERROR_FAIL;
|
||||||
|
|
||||||
if (try_set_vsew(target, debug_vsew) != ERROR_OK)
|
if (try_set_vsew(target, debug_vsew) != ERROR_OK)
|
||||||
|
@ -2543,22 +2542,22 @@ static int prep_for_vector_access(struct target *target,
|
||||||
* instruction, for the vslide1down instruction.
|
* instruction, for the vslide1down instruction.
|
||||||
* Set it so the entire V register is updated. */
|
* Set it so the entire V register is updated. */
|
||||||
*debug_vl = DIV_ROUND_UP(r->vlenb * 8, *debug_vsew);
|
*debug_vl = DIV_ROUND_UP(r->vlenb * 8, *debug_vsew);
|
||||||
return riscv_write_register(target, GDB_REGNO_VL, *debug_vl);
|
return riscv_reg_write(target, GDB_REGNO_VL, *debug_vl);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int cleanup_after_vector_access(struct target *target,
|
static int cleanup_after_vector_access(struct target *target,
|
||||||
riscv_reg_t mstatus, riscv_reg_t vtype, riscv_reg_t vl)
|
riscv_reg_t mstatus, riscv_reg_t vtype, riscv_reg_t vl)
|
||||||
{
|
{
|
||||||
/* Restore vtype and vl. */
|
/* Restore vtype and vl. */
|
||||||
if (riscv_write_register(target, GDB_REGNO_VTYPE, vtype) != ERROR_OK)
|
if (riscv_reg_write(target, GDB_REGNO_VTYPE, vtype) != ERROR_OK)
|
||||||
return ERROR_FAIL;
|
return ERROR_FAIL;
|
||||||
if (riscv_write_register(target, GDB_REGNO_VL, vl) != ERROR_OK)
|
if (riscv_reg_write(target, GDB_REGNO_VL, vl) != ERROR_OK)
|
||||||
return ERROR_FAIL;
|
return ERROR_FAIL;
|
||||||
return cleanup_after_register_access(target, mstatus, GDB_REGNO_VL);
|
return cleanup_after_register_access(target, mstatus, GDB_REGNO_VL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int riscv013_get_register_buf(struct target *target,
|
int riscv013_get_register_buf(struct target *target, uint8_t *value,
|
||||||
uint8_t *value, enum gdb_regno regno)
|
enum gdb_regno regno)
|
||||||
{
|
{
|
||||||
assert(regno >= GDB_REGNO_V0 && regno <= GDB_REGNO_V31);
|
assert(regno >= GDB_REGNO_V0 && regno <= GDB_REGNO_V31);
|
||||||
|
|
||||||
|
@ -2572,7 +2571,7 @@ static int riscv013_get_register_buf(struct target *target,
|
||||||
&debug_vl, &debug_vsew) != ERROR_OK)
|
&debug_vl, &debug_vsew) != ERROR_OK)
|
||||||
return ERROR_FAIL;
|
return ERROR_FAIL;
|
||||||
|
|
||||||
if (riscv_save_register(target, GDB_REGNO_S0) != ERROR_OK)
|
if (riscv013_reg_save(target, GDB_REGNO_S0) != ERROR_OK)
|
||||||
return ERROR_FAIL;
|
return ERROR_FAIL;
|
||||||
|
|
||||||
unsigned int vnum = regno - GDB_REGNO_V0;
|
unsigned int vnum = regno - GDB_REGNO_V0;
|
||||||
|
@ -2601,7 +2600,7 @@ static int riscv013_get_register_buf(struct target *target,
|
||||||
} else {
|
} else {
|
||||||
LOG_TARGET_ERROR(target,
|
LOG_TARGET_ERROR(target,
|
||||||
"Failed to execute vmv/vslide1down while reading %s",
|
"Failed to execute vmv/vslide1down while reading %s",
|
||||||
gdb_regno_name(target, regno));
|
riscv_reg_gdb_regno_name(target, regno));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2612,8 +2611,8 @@ static int riscv013_get_register_buf(struct target *target,
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int riscv013_set_register_buf(struct target *target,
|
int riscv013_set_register_buf(struct target *target, enum gdb_regno regno,
|
||||||
enum gdb_regno regno, const uint8_t *value)
|
const uint8_t *value)
|
||||||
{
|
{
|
||||||
assert(regno >= GDB_REGNO_V0 && regno <= GDB_REGNO_V31);
|
assert(regno >= GDB_REGNO_V0 && regno <= GDB_REGNO_V31);
|
||||||
|
|
||||||
|
@ -2627,7 +2626,7 @@ static int riscv013_set_register_buf(struct target *target,
|
||||||
&debug_vl, &debug_vsew) != ERROR_OK)
|
&debug_vl, &debug_vsew) != ERROR_OK)
|
||||||
return ERROR_FAIL;
|
return ERROR_FAIL;
|
||||||
|
|
||||||
if (riscv_save_register(target, GDB_REGNO_S0) != ERROR_OK)
|
if (riscv013_reg_save(target, GDB_REGNO_S0) != ERROR_OK)
|
||||||
return ERROR_FAIL;
|
return ERROR_FAIL;
|
||||||
|
|
||||||
unsigned int vnum = regno - GDB_REGNO_V0;
|
unsigned int vnum = regno - GDB_REGNO_V0;
|
||||||
|
@ -3002,10 +3001,6 @@ static int init_target(struct command_context *cmd_ctx,
|
||||||
LOG_TARGET_DEBUG(target, "Init.");
|
LOG_TARGET_DEBUG(target, "Init.");
|
||||||
RISCV_INFO(generic_info);
|
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->select_target = &dm013_select_target;
|
||||||
generic_info->get_hart_state = &riscv013_get_hart_state;
|
generic_info->get_hart_state = &riscv013_get_hart_state;
|
||||||
generic_info->resume_go = &riscv013_resume_go;
|
generic_info->resume_go = &riscv013_resume_go;
|
||||||
|
@ -4338,11 +4333,11 @@ static int read_memory_progbuf_inner_fill_progbuf(struct target *target,
|
||||||
{
|
{
|
||||||
const bool is_repeated_read = increment == 0;
|
const bool is_repeated_read = increment == 0;
|
||||||
|
|
||||||
if (riscv_save_register(target, GDB_REGNO_S0) != ERROR_OK)
|
if (riscv013_reg_save(target, GDB_REGNO_S0) != ERROR_OK)
|
||||||
return ERROR_FAIL;
|
return ERROR_FAIL;
|
||||||
if (riscv_save_register(target, GDB_REGNO_S1) != ERROR_OK)
|
if (riscv013_reg_save(target, GDB_REGNO_S1) != ERROR_OK)
|
||||||
return ERROR_FAIL;
|
return ERROR_FAIL;
|
||||||
if (is_repeated_read && riscv_save_register(target, GDB_REGNO_A0) != ERROR_OK)
|
if (is_repeated_read && riscv013_reg_save(target, GDB_REGNO_A0) != ERROR_OK)
|
||||||
return ERROR_FAIL;
|
return ERROR_FAIL;
|
||||||
|
|
||||||
struct riscv_program program;
|
struct riscv_program program;
|
||||||
|
@ -4435,7 +4430,7 @@ static int read_memory_progbuf_inner(struct target *target,
|
||||||
static int read_memory_progbuf_inner_one(struct target *target,
|
static int read_memory_progbuf_inner_one(struct target *target,
|
||||||
struct memory_access_info access, bool mprven)
|
struct memory_access_info access, bool mprven)
|
||||||
{
|
{
|
||||||
if (riscv_save_register(target, GDB_REGNO_S1) != ERROR_OK)
|
if (riscv013_reg_save(target, GDB_REGNO_S1) != ERROR_OK)
|
||||||
return ERROR_FAIL;
|
return ERROR_FAIL;
|
||||||
|
|
||||||
struct riscv_program program;
|
struct riscv_program program;
|
||||||
|
@ -4975,9 +4970,9 @@ static int riscv_program_store_mprv(struct riscv_program *p, enum gdb_regno d,
|
||||||
static int write_memory_progbuf_fill_progbuf(struct target *target,
|
static int write_memory_progbuf_fill_progbuf(struct target *target,
|
||||||
uint32_t size, bool mprven)
|
uint32_t size, bool mprven)
|
||||||
{
|
{
|
||||||
if (riscv_save_register(target, GDB_REGNO_S0) != ERROR_OK)
|
if (riscv013_reg_save(target, GDB_REGNO_S0) != ERROR_OK)
|
||||||
return ERROR_FAIL;
|
return ERROR_FAIL;
|
||||||
if (riscv_save_register(target, GDB_REGNO_S1) != ERROR_OK)
|
if (riscv013_reg_save(target, GDB_REGNO_S1) != ERROR_OK)
|
||||||
return ERROR_FAIL;
|
return ERROR_FAIL;
|
||||||
|
|
||||||
struct riscv_program program;
|
struct riscv_program program;
|
||||||
|
@ -5148,7 +5143,7 @@ struct target_type riscv013_target = {
|
||||||
};
|
};
|
||||||
|
|
||||||
/*** 0.13-specific implementations of various RISC-V helper functions. ***/
|
/*** 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)
|
riscv_reg_t *value, enum gdb_regno rid)
|
||||||
{
|
{
|
||||||
/* It would be beneficial to move this redirection to the
|
/* It would be beneficial to move this redirection to the
|
||||||
|
@ -5157,14 +5152,14 @@ static int riscv013_get_register(struct target *target,
|
||||||
*/
|
*/
|
||||||
if (rid == GDB_REGNO_PRIV) {
|
if (rid == GDB_REGNO_PRIV) {
|
||||||
uint64_t dcsr;
|
uint64_t dcsr;
|
||||||
if (riscv_get_register(target, &dcsr, GDB_REGNO_DCSR) != ERROR_OK)
|
if (riscv_reg_get(target, &dcsr, GDB_REGNO_DCSR) != ERROR_OK)
|
||||||
return ERROR_FAIL;
|
return ERROR_FAIL;
|
||||||
*value = set_field(0, VIRT_PRIV_V, get_field(dcsr, CSR_DCSR_V));
|
*value = set_field(0, VIRT_PRIV_V, get_field(dcsr, CSR_DCSR_V));
|
||||||
*value = set_field(*value, VIRT_PRIV_PRV, get_field(dcsr, CSR_DCSR_PRV));
|
*value = set_field(*value, VIRT_PRIV_PRV, get_field(dcsr, CSR_DCSR_PRV));
|
||||||
return ERROR_OK;
|
return ERROR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_TARGET_DEBUG(target, "reading register %s", gdb_regno_name(target, rid));
|
LOG_TARGET_DEBUG(target, "reading register %s", riscv_reg_gdb_regno_name(target, rid));
|
||||||
|
|
||||||
if (dm013_select_target(target) != ERROR_OK)
|
if (dm013_select_target(target) != ERROR_OK)
|
||||||
return ERROR_FAIL;
|
return ERROR_FAIL;
|
||||||
|
@ -5177,11 +5172,11 @@ static int riscv013_get_register(struct target *target,
|
||||||
return ERROR_OK;
|
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)
|
riscv_reg_t value)
|
||||||
{
|
{
|
||||||
LOG_TARGET_DEBUG(target, "writing 0x%" PRIx64 " to register %s",
|
LOG_TARGET_DEBUG(target, "writing 0x%" PRIx64 " to register %s",
|
||||||
value, gdb_regno_name(target, rid));
|
value, riscv_reg_gdb_regno_name(target, rid));
|
||||||
|
|
||||||
if (dm013_select_target(target) != ERROR_OK)
|
if (dm013_select_target(target) != ERROR_OK)
|
||||||
return ERROR_FAIL;
|
return ERROR_FAIL;
|
||||||
|
@ -5545,7 +5540,7 @@ static int riscv013_on_step_or_resume(struct target *target, bool step)
|
||||||
if (set_dcsr_ebreak(target, step) != ERROR_OK)
|
if (set_dcsr_ebreak(target, step) != ERROR_OK)
|
||||||
return ERROR_FAIL;
|
return ERROR_FAIL;
|
||||||
|
|
||||||
if (riscv_flush_registers(target) != ERROR_OK)
|
if (riscv_reg_flush_all(target) != ERROR_OK)
|
||||||
return ERROR_FAIL;
|
return ERROR_FAIL;
|
||||||
return ERROR_OK;
|
return ERROR_OK;
|
||||||
}
|
}
|
||||||
|
@ -5559,7 +5554,7 @@ static int riscv013_step_or_resume_current_hart(struct target *target,
|
||||||
}
|
}
|
||||||
LOG_TARGET_DEBUG(target, "resuming (for step?=%d)", step);
|
LOG_TARGET_DEBUG(target, "resuming (for step?=%d)", step);
|
||||||
|
|
||||||
if (riscv_flush_registers(target) != ERROR_OK)
|
if (riscv_reg_flush_all(target) != ERROR_OK)
|
||||||
return ERROR_FAIL;
|
return ERROR_FAIL;
|
||||||
|
|
||||||
dm013_info_t *dm = get_dm(target);
|
dm013_info_t *dm = get_dm(target);
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||||
|
|
||||||
|
#ifndef OPENOCD_TARGET_RISCV_RISCV_013_H
|
||||||
|
#define OPENOCD_TARGET_RISCV_RISCV_013_H
|
||||||
|
|
||||||
|
#include "riscv.h"
|
||||||
|
|
||||||
|
/* TODO: These functions should be replaced here by access methods that can be
|
||||||
|
* reused by other modules (e.g. a function writing an abstract commands, a
|
||||||
|
* function filling/executing program buffer, etc.), while the specifics on how
|
||||||
|
* to use these general-purpose version-specific methods to get a register's
|
||||||
|
* value will be in `riscv-013_reg.c`.
|
||||||
|
*/
|
||||||
|
int riscv013_get_register(struct target *target,
|
||||||
|
riscv_reg_t *value, enum gdb_regno rid);
|
||||||
|
int riscv013_get_register_buf(struct target *target, uint8_t *value,
|
||||||
|
enum gdb_regno regno);
|
||||||
|
int riscv013_set_register(struct target *target, enum gdb_regno rid,
|
||||||
|
riscv_reg_t value);
|
||||||
|
int riscv013_set_register_buf(struct target *target, enum gdb_regno regno,
|
||||||
|
const uint8_t *value);
|
||||||
|
|
||||||
|
#endif /*OPENOCD_TARGET_RISCV_RISCV_013_H*/
|
|
@ -0,0 +1,162 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "riscv-013_reg.h"
|
||||||
|
|
||||||
|
#include "riscv_reg.h"
|
||||||
|
#include "riscv_reg_impl.h"
|
||||||
|
#include "riscv-013.h"
|
||||||
|
#include <helper/time_support.h>
|
||||||
|
|
||||||
|
static int riscv013_reg_get(struct reg *reg)
|
||||||
|
{
|
||||||
|
struct target *target = riscv_reg_impl_get_target(reg);
|
||||||
|
|
||||||
|
/* TODO: Hack to deal with gdb that thinks these registers still exist. */
|
||||||
|
if (reg->number > GDB_REGNO_XPR15 && reg->number <= GDB_REGNO_XPR31 &&
|
||||||
|
riscv_supports_extension(target, 'E')) {
|
||||||
|
buf_set_u64(reg->value, 0, reg->size, 0);
|
||||||
|
return ERROR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reg->number >= GDB_REGNO_V0 && reg->number <= GDB_REGNO_V31) {
|
||||||
|
if (riscv013_get_register_buf(target, reg->value, reg->number) != ERROR_OK)
|
||||||
|
return ERROR_FAIL;
|
||||||
|
|
||||||
|
reg->valid = riscv_reg_impl_gdb_regno_cacheable(reg->number, /* is write? */ false);
|
||||||
|
} else {
|
||||||
|
uint64_t value;
|
||||||
|
int result = riscv_reg_get(target, &value, reg->number);
|
||||||
|
if (result != ERROR_OK)
|
||||||
|
return result;
|
||||||
|
buf_set_u64(reg->value, 0, reg->size, value);
|
||||||
|
}
|
||||||
|
char *str = buf_to_hex_str(reg->value, reg->size);
|
||||||
|
LOG_TARGET_DEBUG(target, "Read 0x%s from %s (valid=%d).", str, reg->name,
|
||||||
|
reg->valid);
|
||||||
|
free(str);
|
||||||
|
return ERROR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int riscv013_reg_set(struct reg *reg, uint8_t *buf)
|
||||||
|
{
|
||||||
|
struct target *target = riscv_reg_impl_get_target(reg);
|
||||||
|
RISCV_INFO(r);
|
||||||
|
|
||||||
|
char *str = buf_to_hex_str(buf, reg->size);
|
||||||
|
LOG_TARGET_DEBUG(target, "Write 0x%s to %s (valid=%d).", str, reg->name,
|
||||||
|
reg->valid);
|
||||||
|
free(str);
|
||||||
|
|
||||||
|
/* TODO: Hack to deal with gdb that thinks these registers still exist. */
|
||||||
|
if (reg->number > GDB_REGNO_XPR15 && reg->number <= GDB_REGNO_XPR31 &&
|
||||||
|
riscv_supports_extension(target, 'E') &&
|
||||||
|
buf_get_u64(buf, 0, reg->size) == 0)
|
||||||
|
return ERROR_OK;
|
||||||
|
|
||||||
|
if (reg->number == GDB_REGNO_TDATA1 ||
|
||||||
|
reg->number == GDB_REGNO_TDATA2) {
|
||||||
|
r->manual_hwbp_set = true;
|
||||||
|
/* When enumerating triggers, we clear any triggers with DMODE set,
|
||||||
|
* assuming they were left over from a previous debug session. So make
|
||||||
|
* sure that is done before a user might be setting their own triggers.
|
||||||
|
*/
|
||||||
|
if (riscv_enumerate_triggers(target) != ERROR_OK)
|
||||||
|
return ERROR_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reg->number >= GDB_REGNO_V0 && reg->number <= GDB_REGNO_V31) {
|
||||||
|
if (riscv013_set_register_buf(target, reg->number, buf) != ERROR_OK)
|
||||||
|
return ERROR_FAIL;
|
||||||
|
|
||||||
|
memcpy(reg->value, buf, DIV_ROUND_UP(reg->size, 8));
|
||||||
|
reg->valid = riscv_reg_impl_gdb_regno_cacheable(reg->number, /* is write? */ true);
|
||||||
|
} else {
|
||||||
|
const riscv_reg_t value = buf_get_u64(buf, 0, reg->size);
|
||||||
|
if (riscv_reg_set(target, reg->number, value) != ERROR_OK)
|
||||||
|
return ERROR_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ERROR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct reg_arch_type *riscv013_gdb_regno_reg_type(uint32_t regno)
|
||||||
|
{
|
||||||
|
static const struct reg_arch_type riscv011_reg_type = {
|
||||||
|
.get = riscv013_reg_get,
|
||||||
|
.set = riscv013_reg_set
|
||||||
|
};
|
||||||
|
return &riscv011_reg_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int riscv013_init_reg(struct target *target, uint32_t regno)
|
||||||
|
{
|
||||||
|
return riscv_reg_impl_init_one(target, regno, riscv013_gdb_regno_reg_type(regno));
|
||||||
|
}
|
||||||
|
|
||||||
|
int riscv013_reg_init_all(struct target *target)
|
||||||
|
{
|
||||||
|
if (riscv_reg_impl_init_cache(target) != ERROR_OK)
|
||||||
|
return ERROR_FAIL;
|
||||||
|
|
||||||
|
init_shared_reg_info(target);
|
||||||
|
|
||||||
|
riscv_reg_impl_init_vector_reg_type(target);
|
||||||
|
|
||||||
|
for (uint32_t regno = 0; regno < target->reg_cache->num_regs; ++regno)
|
||||||
|
if (riscv013_init_reg(target, regno) != ERROR_OK)
|
||||||
|
return ERROR_FAIL;
|
||||||
|
|
||||||
|
if (riscv_reg_impl_expose_csrs(target) != ERROR_OK)
|
||||||
|
return ERROR_FAIL;
|
||||||
|
|
||||||
|
riscv_reg_impl_hide_csrs(target);
|
||||||
|
|
||||||
|
return ERROR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is used to save the value of a register in cache. The register
|
||||||
|
* is marked as dirty, and writeback is delayed for as long as possible.
|
||||||
|
*/
|
||||||
|
int riscv013_reg_save(struct target *target, enum gdb_regno regid)
|
||||||
|
{
|
||||||
|
if (target->state != TARGET_HALTED) {
|
||||||
|
LOG_TARGET_ERROR(target, "Can't save register %s on a hart that is not halted.",
|
||||||
|
riscv_reg_gdb_regno_name(target, regid));
|
||||||
|
return ERROR_FAIL;
|
||||||
|
}
|
||||||
|
assert(riscv_reg_impl_gdb_regno_cacheable(regid, /* is write? */ false) &&
|
||||||
|
"Only cacheable registers can be saved.");
|
||||||
|
|
||||||
|
RISCV_INFO(r);
|
||||||
|
riscv_reg_t value;
|
||||||
|
if (!target->reg_cache) {
|
||||||
|
assert(!target_was_examined(target));
|
||||||
|
/* To create register cache it is needed to examine the target first,
|
||||||
|
* therefore during examine, any changed register needs to be saved
|
||||||
|
* and restored manually.
|
||||||
|
*/
|
||||||
|
return ERROR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct reg *reg = riscv_reg_impl_cache_entry(target, regid);
|
||||||
|
|
||||||
|
LOG_TARGET_DEBUG(target, "Saving %s", reg->name);
|
||||||
|
if (riscv_reg_get(target, &value, regid) != ERROR_OK)
|
||||||
|
return ERROR_FAIL;
|
||||||
|
|
||||||
|
assert(reg->valid &&
|
||||||
|
"The register is cacheable, so the cache entry must be valid now.");
|
||||||
|
/* Mark the register dirty. We assume that this function is called
|
||||||
|
* because the caller is about to mess with the underlying value of the
|
||||||
|
* register. */
|
||||||
|
reg->dirty = true;
|
||||||
|
|
||||||
|
r->last_activity = timeval_ms();
|
||||||
|
|
||||||
|
return ERROR_OK;
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||||
|
|
||||||
|
#ifndef OPENOCD_TARGET_RISCV_RISCV_REG_013_H
|
||||||
|
#define OPENOCD_TARGET_RISCV_RISCV_REG_013_H
|
||||||
|
|
||||||
|
#include "target/target.h"
|
||||||
|
#include "gdb_regs.h"
|
||||||
|
#include "riscv.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This file describes additional register cache interface available to the
|
||||||
|
* RISC-V Debug Specification v0.13+ targets.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Init initialize register cache. After this function all registers can be
|
||||||
|
* safely accessed via functions described here and in `riscv_reg.h`.
|
||||||
|
*/
|
||||||
|
int riscv013_reg_init_all(struct target *target);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is used to save the value of a register in cache. The register
|
||||||
|
* is marked as dirty, and writeback is delayed for as long as possible.
|
||||||
|
* Generally used to save registers before program buffer execution.
|
||||||
|
*
|
||||||
|
* TODO: The interface should be restricted in such a way that only GPRs can be
|
||||||
|
* saved.
|
||||||
|
*/
|
||||||
|
int riscv013_reg_save(struct target *target, enum gdb_regno regid);
|
||||||
|
|
||||||
|
#endif /*OPENOCD_TARGET_RISCV_RISCV_REG_013_H*/
|
File diff suppressed because it is too large
Load Diff
|
@ -9,8 +9,9 @@ struct riscv_program;
|
||||||
#include "opcodes.h"
|
#include "opcodes.h"
|
||||||
#include "gdb_regs.h"
|
#include "gdb_regs.h"
|
||||||
#include "jtag/jtag.h"
|
#include "jtag/jtag.h"
|
||||||
#include "target/register.h"
|
|
||||||
#include "target/semihosting_common.h"
|
#include "target/semihosting_common.h"
|
||||||
|
#include "target/target.h"
|
||||||
|
#include "target/register.h"
|
||||||
#include <helper/command.h>
|
#include <helper/command.h>
|
||||||
#include <helper/bits.h>
|
#include <helper/bits.h>
|
||||||
|
|
||||||
|
@ -83,9 +84,11 @@ enum riscv_hart_state {
|
||||||
RISCV_STATE_UNAVAILABLE
|
RISCV_STATE_UNAVAILABLE
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* RISC-V-specific data assigned to a register. */
|
||||||
typedef struct {
|
typedef struct {
|
||||||
struct target *target;
|
struct target *target;
|
||||||
unsigned custom_number;
|
/* Abstract command's regno for a custom register. */
|
||||||
|
unsigned int custom_number;
|
||||||
} riscv_reg_info_t;
|
} riscv_reg_info_t;
|
||||||
|
|
||||||
#define RISCV_SAMPLE_BUF_TIMESTAMP_BEFORE 0x80
|
#define RISCV_SAMPLE_BUF_TIMESTAMP_BEFORE 0x80
|
||||||
|
@ -190,14 +193,6 @@ struct riscv_info {
|
||||||
|
|
||||||
/* Helper functions that target the various RISC-V debug spec
|
/* Helper functions that target the various RISC-V debug spec
|
||||||
* implementations. */
|
* 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 (*select_target)(struct target *target);
|
||||||
int (*get_hart_state)(struct target *target, enum riscv_hart_state *state);
|
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
|
/* 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
|
/*** Support functions for the RISC-V 'RTOS', which provides multihart support
|
||||||
* without requiring multiple targets. */
|
* 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
|
/* Checks the state of the current hart -- "is_halted" checks the actual
|
||||||
* on-device register. */
|
* on-device register. */
|
||||||
int riscv_get_hart_state(struct target *target, enum riscv_hart_state *state);
|
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,
|
int riscv_remove_watchpoint(struct target *target,
|
||||||
struct watchpoint *watchpoint);
|
struct watchpoint *watchpoint);
|
||||||
|
|
||||||
int riscv_init_registers(struct target *target);
|
|
||||||
|
|
||||||
void riscv_semihosting_init(struct target *target);
|
void riscv_semihosting_init(struct target *target);
|
||||||
|
|
||||||
enum semihosting_result riscv_semihosting(struct target *target, int *retval);
|
enum semihosting_result riscv_semihosting(struct target *target, int *retval);
|
||||||
|
|
|
@ -0,0 +1,968 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "gdb_regs.h"
|
||||||
|
#include "riscv.h"
|
||||||
|
#include "riscv_reg.h"
|
||||||
|
#include "riscv_reg_impl.h"
|
||||||
|
/**
|
||||||
|
* TODO: Currently `reg->get/set` is implemented in terms of
|
||||||
|
* `riscv_get/set_register`. However, the intention behind
|
||||||
|
* `riscv_get/set_register` is to work with the cache, therefore it accesses
|
||||||
|
* and modifyes register cache directly. The idea is to implement
|
||||||
|
* `riscv_get/set_register` in terms of `riscv_reg_impl_cache_entry` and
|
||||||
|
* `reg->get/set`.
|
||||||
|
* Once this is done, the following includes should be removed.
|
||||||
|
*/
|
||||||
|
#include "debug_defines.h"
|
||||||
|
#include "riscv-011.h"
|
||||||
|
#include "riscv-013.h"
|
||||||
|
#include "field_helpers.h"
|
||||||
|
|
||||||
|
static const char * const default_reg_names[GDB_REGNO_COUNT] = {
|
||||||
|
[GDB_REGNO_ZERO] = "zero",
|
||||||
|
[GDB_REGNO_RA] = "ra",
|
||||||
|
[GDB_REGNO_SP] = "sp",
|
||||||
|
[GDB_REGNO_GP] = "gp",
|
||||||
|
[GDB_REGNO_TP] = "tp",
|
||||||
|
[GDB_REGNO_T0] = "t0",
|
||||||
|
[GDB_REGNO_T1] = "t1",
|
||||||
|
[GDB_REGNO_T2] = "t2",
|
||||||
|
[GDB_REGNO_FP] = "fp",
|
||||||
|
[GDB_REGNO_S1] = "s1",
|
||||||
|
[GDB_REGNO_A0] = "a0",
|
||||||
|
[GDB_REGNO_A1] = "a1",
|
||||||
|
[GDB_REGNO_A2] = "a2",
|
||||||
|
[GDB_REGNO_A3] = "a3",
|
||||||
|
[GDB_REGNO_A4] = "a4",
|
||||||
|
[GDB_REGNO_A5] = "a5",
|
||||||
|
[GDB_REGNO_A6] = "a6",
|
||||||
|
[GDB_REGNO_A7] = "a7",
|
||||||
|
[GDB_REGNO_S2] = "s2",
|
||||||
|
[GDB_REGNO_S3] = "s3",
|
||||||
|
[GDB_REGNO_S4] = "s4",
|
||||||
|
[GDB_REGNO_S5] = "s5",
|
||||||
|
[GDB_REGNO_S6] = "s6",
|
||||||
|
[GDB_REGNO_S7] = "s7",
|
||||||
|
[GDB_REGNO_S8] = "s8",
|
||||||
|
[GDB_REGNO_S9] = "s9",
|
||||||
|
[GDB_REGNO_S10] = "s10",
|
||||||
|
[GDB_REGNO_S11] = "s11",
|
||||||
|
[GDB_REGNO_T3] = "t3",
|
||||||
|
[GDB_REGNO_T4] = "t4",
|
||||||
|
[GDB_REGNO_T5] = "t5",
|
||||||
|
[GDB_REGNO_T6] = "t6",
|
||||||
|
[GDB_REGNO_PC] = "pc",
|
||||||
|
[GDB_REGNO_CSR0] = "csr0",
|
||||||
|
[GDB_REGNO_PRIV] = "priv",
|
||||||
|
[GDB_REGNO_FT0] = "ft0",
|
||||||
|
[GDB_REGNO_FT1] = "ft1",
|
||||||
|
[GDB_REGNO_FT2] = "ft2",
|
||||||
|
[GDB_REGNO_FT3] = "ft3",
|
||||||
|
[GDB_REGNO_FT4] = "ft4",
|
||||||
|
[GDB_REGNO_FT5] = "ft5",
|
||||||
|
[GDB_REGNO_FT6] = "ft6",
|
||||||
|
[GDB_REGNO_FT7] = "ft7",
|
||||||
|
[GDB_REGNO_FS0] = "fs0",
|
||||||
|
[GDB_REGNO_FS1] = "fs1",
|
||||||
|
[GDB_REGNO_FA0] = "fa0",
|
||||||
|
[GDB_REGNO_FA1] = "fa1",
|
||||||
|
[GDB_REGNO_FA2] = "fa2",
|
||||||
|
[GDB_REGNO_FA3] = "fa3",
|
||||||
|
[GDB_REGNO_FA4] = "fa4",
|
||||||
|
[GDB_REGNO_FA5] = "fa5",
|
||||||
|
[GDB_REGNO_FA6] = "fa6",
|
||||||
|
[GDB_REGNO_FA7] = "fa7",
|
||||||
|
[GDB_REGNO_FS2] = "fs2",
|
||||||
|
[GDB_REGNO_FS3] = "fs3",
|
||||||
|
[GDB_REGNO_FS4] = "fs4",
|
||||||
|
[GDB_REGNO_FS5] = "fs5",
|
||||||
|
[GDB_REGNO_FS6] = "fs6",
|
||||||
|
[GDB_REGNO_FS7] = "fs7",
|
||||||
|
[GDB_REGNO_FS8] = "fs8",
|
||||||
|
[GDB_REGNO_FS9] = "fs9",
|
||||||
|
[GDB_REGNO_FS10] = "fs10",
|
||||||
|
[GDB_REGNO_FS11] = "fs11",
|
||||||
|
[GDB_REGNO_FT8] = "ft8",
|
||||||
|
[GDB_REGNO_FT9] = "ft9",
|
||||||
|
[GDB_REGNO_FT10] = "ft10",
|
||||||
|
[GDB_REGNO_FT11] = "ft11",
|
||||||
|
|
||||||
|
#define DECLARE_CSR(csr_name, number)[(number) + GDB_REGNO_CSR0] = #csr_name,
|
||||||
|
#include "encoding.h"
|
||||||
|
#undef DECLARE_CSR
|
||||||
|
};
|
||||||
|
|
||||||
|
static void free_custom_register_names(struct target *target)
|
||||||
|
{
|
||||||
|
RISCV_INFO(info);
|
||||||
|
|
||||||
|
if (!info->custom_register_names.reg_names)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < info->custom_register_names.num_entries; i++)
|
||||||
|
free(info->custom_register_names.reg_names[i]);
|
||||||
|
free(info->custom_register_names.reg_names);
|
||||||
|
info->custom_register_names.reg_names = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void free_reg_names(struct target *target)
|
||||||
|
{
|
||||||
|
RISCV_INFO(info);
|
||||||
|
|
||||||
|
if (!info->reg_names)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < GDB_REGNO_COUNT; ++i)
|
||||||
|
free(info->reg_names[i]);
|
||||||
|
free(info->reg_names);
|
||||||
|
info->reg_names = NULL;
|
||||||
|
|
||||||
|
free_custom_register_names(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *init_reg_name(const char *name)
|
||||||
|
{
|
||||||
|
const int size_buf = strlen(name) + 1;
|
||||||
|
|
||||||
|
char * const buf = calloc(size_buf, sizeof(char));
|
||||||
|
if (!buf) {
|
||||||
|
LOG_ERROR("Failed to allocate memory for a register name.");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
strcpy(buf, name);
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void init_custom_csr_names(const struct target *target)
|
||||||
|
{
|
||||||
|
RISCV_INFO(info);
|
||||||
|
range_list_t *entry;
|
||||||
|
|
||||||
|
list_for_each_entry(entry, &info->expose_csr, list) {
|
||||||
|
if (!entry->name)
|
||||||
|
continue;
|
||||||
|
assert(entry->low == entry->high);
|
||||||
|
const unsigned int regno = entry->low + GDB_REGNO_CSR0;
|
||||||
|
assert(regno <= GDB_REGNO_CSR4095);
|
||||||
|
if (info->reg_names[regno])
|
||||||
|
return;
|
||||||
|
info->reg_names[regno] = init_reg_name(entry->name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *init_reg_name_with_prefix(const char *name_prefix,
|
||||||
|
unsigned int num)
|
||||||
|
{
|
||||||
|
const int size_buf = snprintf(NULL, 0, "%s%d", name_prefix, num) + 1;
|
||||||
|
|
||||||
|
char * const buf = calloc(size_buf, sizeof(char));
|
||||||
|
if (!buf) {
|
||||||
|
LOG_ERROR("Failed to allocate memory for a register name.");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
int result = snprintf(buf, size_buf, "%s%d", name_prefix, num);
|
||||||
|
assert(result > 0 && result <= (size_buf - 1));
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *riscv_reg_gdb_regno_name(const struct target *target, enum gdb_regno regno)
|
||||||
|
{
|
||||||
|
RISCV_INFO(info);
|
||||||
|
|
||||||
|
if (regno >= GDB_REGNO_COUNT) {
|
||||||
|
assert(info->custom_register_names.reg_names);
|
||||||
|
assert(regno - GDB_REGNO_COUNT <= info->custom_register_names.num_entries);
|
||||||
|
return info->custom_register_names.reg_names[regno - GDB_REGNO_COUNT];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!info->reg_names)
|
||||||
|
info->reg_names = calloc(GDB_REGNO_COUNT, sizeof(char *));
|
||||||
|
|
||||||
|
if (info->reg_names[regno])
|
||||||
|
return info->reg_names[regno];
|
||||||
|
if (default_reg_names[regno])
|
||||||
|
return default_reg_names[regno];
|
||||||
|
if (regno <= GDB_REGNO_XPR31) {
|
||||||
|
info->reg_names[regno] = init_reg_name_with_prefix("x", regno - GDB_REGNO_ZERO);
|
||||||
|
return info->reg_names[regno];
|
||||||
|
}
|
||||||
|
if (regno <= GDB_REGNO_V31 && regno >= GDB_REGNO_V0) {
|
||||||
|
info->reg_names[regno] = init_reg_name_with_prefix("v", regno - GDB_REGNO_V0);
|
||||||
|
return info->reg_names[regno];
|
||||||
|
}
|
||||||
|
if (regno >= GDB_REGNO_CSR0 && regno <= GDB_REGNO_CSR4095) {
|
||||||
|
init_custom_csr_names(target);
|
||||||
|
info->reg_names[regno] = init_reg_name_with_prefix("csr", regno - GDB_REGNO_CSR0);
|
||||||
|
return info->reg_names[regno];
|
||||||
|
}
|
||||||
|
assert(!"Encountered uninitialized entry in reg_names table");
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct target *riscv_reg_impl_get_target(const struct reg *reg)
|
||||||
|
{
|
||||||
|
assert(riscv_reg_impl_is_initialized(reg));
|
||||||
|
return ((const riscv_reg_info_t *)reg->arch_info)->target;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct reg_feature *gdb_regno_feature(uint32_t regno)
|
||||||
|
{
|
||||||
|
if (regno <= GDB_REGNO_XPR31 || regno == GDB_REGNO_PC) {
|
||||||
|
static struct reg_feature feature_cpu = {
|
||||||
|
.name = "org.gnu.gdb.riscv.cpu"
|
||||||
|
};
|
||||||
|
return &feature_cpu;
|
||||||
|
}
|
||||||
|
if ((regno >= GDB_REGNO_FPR0 && regno <= GDB_REGNO_FPR31) ||
|
||||||
|
regno == GDB_REGNO_FFLAGS ||
|
||||||
|
regno == GDB_REGNO_FRM ||
|
||||||
|
regno == GDB_REGNO_FCSR) {
|
||||||
|
static struct reg_feature feature_fpu = {
|
||||||
|
.name = "org.gnu.gdb.riscv.fpu"
|
||||||
|
};
|
||||||
|
return &feature_fpu;
|
||||||
|
}
|
||||||
|
if (regno >= GDB_REGNO_V0 && regno <= GDB_REGNO_V31) {
|
||||||
|
static struct reg_feature feature_vector = {
|
||||||
|
.name = "org.gnu.gdb.riscv.vector"
|
||||||
|
};
|
||||||
|
return &feature_vector;
|
||||||
|
}
|
||||||
|
if (regno >= GDB_REGNO_CSR0 && regno <= GDB_REGNO_CSR4095) {
|
||||||
|
static struct reg_feature feature_csr = {
|
||||||
|
.name = "org.gnu.gdb.riscv.csr"
|
||||||
|
};
|
||||||
|
return &feature_csr;
|
||||||
|
}
|
||||||
|
if (regno == GDB_REGNO_PRIV) {
|
||||||
|
static struct reg_feature feature_virtual = {
|
||||||
|
.name = "org.gnu.gdb.riscv.virtual"
|
||||||
|
};
|
||||||
|
return &feature_virtual;
|
||||||
|
}
|
||||||
|
assert(regno >= GDB_REGNO_COUNT);
|
||||||
|
static struct reg_feature feature_custom = {
|
||||||
|
.name = "org.gnu.gdb.riscv.custom"
|
||||||
|
};
|
||||||
|
return &feature_custom;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool gdb_regno_caller_save(uint32_t regno)
|
||||||
|
{
|
||||||
|
return regno <= GDB_REGNO_XPR31 ||
|
||||||
|
regno == GDB_REGNO_PC ||
|
||||||
|
(regno >= GDB_REGNO_FPR0 && regno <= GDB_REGNO_FPR31);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct reg_data_type *gdb_regno_reg_data_type(const struct target *target,
|
||||||
|
uint32_t regno)
|
||||||
|
{
|
||||||
|
if (regno >= GDB_REGNO_FPR0 && regno <= GDB_REGNO_FPR31) {
|
||||||
|
static struct reg_data_type type_ieee_single = {
|
||||||
|
.type = REG_TYPE_IEEE_SINGLE,
|
||||||
|
.id = "ieee_single"
|
||||||
|
};
|
||||||
|
static struct reg_data_type type_ieee_double = {
|
||||||
|
.type = REG_TYPE_IEEE_DOUBLE,
|
||||||
|
.id = "ieee_double"
|
||||||
|
};
|
||||||
|
static struct reg_data_type_union_field single_double_fields[] = {
|
||||||
|
{"float", &type_ieee_single, single_double_fields + 1},
|
||||||
|
{"double", &type_ieee_double, NULL},
|
||||||
|
};
|
||||||
|
static struct reg_data_type_union single_double_union = {
|
||||||
|
.fields = single_double_fields
|
||||||
|
};
|
||||||
|
static struct reg_data_type type_ieee_single_double = {
|
||||||
|
.type = REG_TYPE_ARCH_DEFINED,
|
||||||
|
.id = "FPU_FD",
|
||||||
|
.type_class = REG_TYPE_CLASS_UNION,
|
||||||
|
{.reg_type_union = &single_double_union}
|
||||||
|
};
|
||||||
|
return riscv_supports_extension(target, 'D') ?
|
||||||
|
&type_ieee_single_double :
|
||||||
|
&type_ieee_single;
|
||||||
|
}
|
||||||
|
if (regno >= GDB_REGNO_V0 && regno <= GDB_REGNO_V31) {
|
||||||
|
RISCV_INFO(info);
|
||||||
|
return &info->type_vector;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *gdb_regno_group(uint32_t regno)
|
||||||
|
{
|
||||||
|
if (regno <= GDB_REGNO_XPR31 ||
|
||||||
|
regno == GDB_REGNO_PC ||
|
||||||
|
regno == GDB_REGNO_PRIV)
|
||||||
|
return "general";
|
||||||
|
if ((regno >= GDB_REGNO_FPR0 && regno <= GDB_REGNO_FPR31) ||
|
||||||
|
regno == GDB_REGNO_FFLAGS ||
|
||||||
|
regno == GDB_REGNO_FRM ||
|
||||||
|
regno == GDB_REGNO_FCSR)
|
||||||
|
return "float";
|
||||||
|
if (regno >= GDB_REGNO_CSR0 && regno <= GDB_REGNO_CSR4095)
|
||||||
|
return "csr";
|
||||||
|
if (regno >= GDB_REGNO_V0 && regno <= GDB_REGNO_V31)
|
||||||
|
return "vector";
|
||||||
|
assert(regno >= GDB_REGNO_COUNT);
|
||||||
|
return "custom";
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t gdb_regno_size(const struct target *target, uint32_t regno)
|
||||||
|
{
|
||||||
|
if (regno >= GDB_REGNO_FPR0 && regno <= GDB_REGNO_FPR31)
|
||||||
|
return riscv_supports_extension(target, 'D') ? 64 : 32;
|
||||||
|
if (regno >= GDB_REGNO_V0 && regno <= GDB_REGNO_V31)
|
||||||
|
return riscv_vlenb(target) * 8;
|
||||||
|
if (regno == GDB_REGNO_PRIV)
|
||||||
|
return 8;
|
||||||
|
if (regno >= GDB_REGNO_CSR0 && regno <= GDB_REGNO_CSR4095) {
|
||||||
|
const unsigned int csr_number = regno - GDB_REGNO_CSR0;
|
||||||
|
switch (csr_number) {
|
||||||
|
case CSR_DCSR:
|
||||||
|
case CSR_MVENDORID:
|
||||||
|
case CSR_MCOUNTINHIBIT:
|
||||||
|
|
||||||
|
case CSR_FFLAGS:
|
||||||
|
case CSR_FRM:
|
||||||
|
case CSR_FCSR:
|
||||||
|
|
||||||
|
case CSR_SCOUNTEREN:
|
||||||
|
case CSR_MCOUNTEREN:
|
||||||
|
return 32;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return riscv_xlen(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool vlenb_exists(const struct target *target)
|
||||||
|
{
|
||||||
|
return riscv_vlenb(target) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool mtopi_exists(const struct target *target)
|
||||||
|
{
|
||||||
|
RISCV_INFO(info)
|
||||||
|
/* TODO: The naming is quite unfortunate here. `mtopi_readable` refers
|
||||||
|
* to how the fact that `mtopi` exists was deduced during examine.
|
||||||
|
*/
|
||||||
|
return info->mtopi_readable;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool mtopei_exists(const struct target *target)
|
||||||
|
{
|
||||||
|
RISCV_INFO(info)
|
||||||
|
/* TODO: The naming is quite unfortunate here. `mtopei_readable` refers
|
||||||
|
* to how the fact that `mtopei` exists was deduced during examine.
|
||||||
|
*/
|
||||||
|
return info->mtopei_readable;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool is_known_standard_csr(unsigned int csr_num)
|
||||||
|
{
|
||||||
|
static const bool is_csr_in_buf[GDB_REGNO_CSR4095 - GDB_REGNO_CSR0 + 1] = {
|
||||||
|
#define DECLARE_CSR(csr_name, number)[number] = true,
|
||||||
|
#include "encoding.h"
|
||||||
|
#undef DECLARE_CSR
|
||||||
|
};
|
||||||
|
assert(csr_num < ARRAY_SIZE(is_csr_in_buf));
|
||||||
|
|
||||||
|
return is_csr_in_buf[csr_num];
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool gdb_regno_exist(const struct target *target, uint32_t regno)
|
||||||
|
{
|
||||||
|
if (regno <= GDB_REGNO_XPR15 ||
|
||||||
|
regno == GDB_REGNO_PC ||
|
||||||
|
regno == GDB_REGNO_PRIV)
|
||||||
|
return true;
|
||||||
|
if (regno > GDB_REGNO_XPR15 && regno <= GDB_REGNO_XPR31)
|
||||||
|
return !riscv_supports_extension(target, 'E');
|
||||||
|
if (regno >= GDB_REGNO_FPR0 && regno <= GDB_REGNO_FPR31)
|
||||||
|
return riscv_supports_extension(target, 'F');
|
||||||
|
if (regno >= GDB_REGNO_V0 && regno <= GDB_REGNO_V31)
|
||||||
|
return vlenb_exists(target);
|
||||||
|
if (regno >= GDB_REGNO_COUNT)
|
||||||
|
return true;
|
||||||
|
assert(regno >= GDB_REGNO_CSR0 && regno <= GDB_REGNO_CSR4095);
|
||||||
|
const unsigned int csr_number = regno - GDB_REGNO_CSR0;
|
||||||
|
switch (csr_number) {
|
||||||
|
case CSR_FFLAGS:
|
||||||
|
case CSR_FRM:
|
||||||
|
case CSR_FCSR:
|
||||||
|
return riscv_supports_extension(target, 'F');
|
||||||
|
case CSR_SCOUNTEREN:
|
||||||
|
case CSR_SSTATUS:
|
||||||
|
case CSR_STVEC:
|
||||||
|
case CSR_SIP:
|
||||||
|
case CSR_SIE:
|
||||||
|
case CSR_SSCRATCH:
|
||||||
|
case CSR_SEPC:
|
||||||
|
case CSR_SCAUSE:
|
||||||
|
case CSR_STVAL:
|
||||||
|
case CSR_SATP:
|
||||||
|
return riscv_supports_extension(target, 'S');
|
||||||
|
case CSR_MEDELEG:
|
||||||
|
case CSR_MIDELEG:
|
||||||
|
/* "In systems with only M-mode, or with both M-mode and
|
||||||
|
* U-mode but without U-mode trap support, the medeleg and
|
||||||
|
* mideleg registers should not exist." */
|
||||||
|
return riscv_supports_extension(target, 'S') ||
|
||||||
|
riscv_supports_extension(target, 'N');
|
||||||
|
|
||||||
|
case CSR_PMPCFG1:
|
||||||
|
case CSR_PMPCFG3:
|
||||||
|
case CSR_CYCLEH:
|
||||||
|
case CSR_TIMEH:
|
||||||
|
case CSR_INSTRETH:
|
||||||
|
case CSR_HPMCOUNTER3H:
|
||||||
|
case CSR_HPMCOUNTER4H:
|
||||||
|
case CSR_HPMCOUNTER5H:
|
||||||
|
case CSR_HPMCOUNTER6H:
|
||||||
|
case CSR_HPMCOUNTER7H:
|
||||||
|
case CSR_HPMCOUNTER8H:
|
||||||
|
case CSR_HPMCOUNTER9H:
|
||||||
|
case CSR_HPMCOUNTER10H:
|
||||||
|
case CSR_HPMCOUNTER11H:
|
||||||
|
case CSR_HPMCOUNTER12H:
|
||||||
|
case CSR_HPMCOUNTER13H:
|
||||||
|
case CSR_HPMCOUNTER14H:
|
||||||
|
case CSR_HPMCOUNTER15H:
|
||||||
|
case CSR_HPMCOUNTER16H:
|
||||||
|
case CSR_HPMCOUNTER17H:
|
||||||
|
case CSR_HPMCOUNTER18H:
|
||||||
|
case CSR_HPMCOUNTER19H:
|
||||||
|
case CSR_HPMCOUNTER20H:
|
||||||
|
case CSR_HPMCOUNTER21H:
|
||||||
|
case CSR_HPMCOUNTER22H:
|
||||||
|
case CSR_HPMCOUNTER23H:
|
||||||
|
case CSR_HPMCOUNTER24H:
|
||||||
|
case CSR_HPMCOUNTER25H:
|
||||||
|
case CSR_HPMCOUNTER26H:
|
||||||
|
case CSR_HPMCOUNTER27H:
|
||||||
|
case CSR_HPMCOUNTER28H:
|
||||||
|
case CSR_HPMCOUNTER29H:
|
||||||
|
case CSR_HPMCOUNTER30H:
|
||||||
|
case CSR_HPMCOUNTER31H:
|
||||||
|
case CSR_MCYCLEH:
|
||||||
|
case CSR_MINSTRETH:
|
||||||
|
case CSR_MHPMCOUNTER4H:
|
||||||
|
case CSR_MHPMCOUNTER5H:
|
||||||
|
case CSR_MHPMCOUNTER6H:
|
||||||
|
case CSR_MHPMCOUNTER7H:
|
||||||
|
case CSR_MHPMCOUNTER8H:
|
||||||
|
case CSR_MHPMCOUNTER9H:
|
||||||
|
case CSR_MHPMCOUNTER10H:
|
||||||
|
case CSR_MHPMCOUNTER11H:
|
||||||
|
case CSR_MHPMCOUNTER12H:
|
||||||
|
case CSR_MHPMCOUNTER13H:
|
||||||
|
case CSR_MHPMCOUNTER14H:
|
||||||
|
case CSR_MHPMCOUNTER15H:
|
||||||
|
case CSR_MHPMCOUNTER16H:
|
||||||
|
case CSR_MHPMCOUNTER17H:
|
||||||
|
case CSR_MHPMCOUNTER18H:
|
||||||
|
case CSR_MHPMCOUNTER19H:
|
||||||
|
case CSR_MHPMCOUNTER20H:
|
||||||
|
case CSR_MHPMCOUNTER21H:
|
||||||
|
case CSR_MHPMCOUNTER22H:
|
||||||
|
case CSR_MHPMCOUNTER23H:
|
||||||
|
case CSR_MHPMCOUNTER24H:
|
||||||
|
case CSR_MHPMCOUNTER25H:
|
||||||
|
case CSR_MHPMCOUNTER26H:
|
||||||
|
case CSR_MHPMCOUNTER27H:
|
||||||
|
case CSR_MHPMCOUNTER28H:
|
||||||
|
case CSR_MHPMCOUNTER29H:
|
||||||
|
case CSR_MHPMCOUNTER30H:
|
||||||
|
case CSR_MHPMCOUNTER31H:
|
||||||
|
return riscv_xlen(target) == 32;
|
||||||
|
case CSR_MCOUNTEREN:
|
||||||
|
return riscv_supports_extension(target, 'U');
|
||||||
|
/* Interrupts M-Mode CSRs. */
|
||||||
|
case CSR_MISELECT:
|
||||||
|
case CSR_MIREG:
|
||||||
|
case CSR_MVIEN:
|
||||||
|
case CSR_MVIP:
|
||||||
|
case CSR_MIEH:
|
||||||
|
case CSR_MIPH:
|
||||||
|
return mtopi_exists(target);
|
||||||
|
case CSR_MIDELEGH:
|
||||||
|
case CSR_MVIENH:
|
||||||
|
case CSR_MVIPH:
|
||||||
|
return mtopi_exists(target) &&
|
||||||
|
riscv_xlen(target) == 32 &&
|
||||||
|
riscv_supports_extension(target, 'S');
|
||||||
|
/* Interrupts S-Mode CSRs. */
|
||||||
|
case CSR_SISELECT:
|
||||||
|
case CSR_SIREG:
|
||||||
|
case CSR_STOPI:
|
||||||
|
return mtopi_exists(target) &&
|
||||||
|
riscv_supports_extension(target, 'S');
|
||||||
|
case CSR_STOPEI:
|
||||||
|
return mtopei_exists(target) &&
|
||||||
|
riscv_supports_extension(target, 'S');
|
||||||
|
case CSR_SIEH:
|
||||||
|
case CSR_SIPH:
|
||||||
|
return mtopi_exists(target) &&
|
||||||
|
riscv_xlen(target) == 32 &&
|
||||||
|
riscv_supports_extension(target, 'S');
|
||||||
|
/* Interrupts Hypervisor and VS CSRs. */
|
||||||
|
case CSR_HVIEN:
|
||||||
|
case CSR_HVICTL:
|
||||||
|
case CSR_HVIPRIO1:
|
||||||
|
case CSR_HVIPRIO2:
|
||||||
|
case CSR_VSISELECT:
|
||||||
|
case CSR_VSIREG:
|
||||||
|
case CSR_VSTOPI:
|
||||||
|
return mtopi_exists(target) &&
|
||||||
|
riscv_supports_extension(target, 'H');
|
||||||
|
case CSR_VSTOPEI:
|
||||||
|
return mtopei_exists(target) &&
|
||||||
|
riscv_supports_extension(target, 'H');
|
||||||
|
case CSR_HIDELEGH:
|
||||||
|
case CSR_HVIENH:
|
||||||
|
case CSR_HVIPH:
|
||||||
|
case CSR_HVIPRIO1H:
|
||||||
|
case CSR_HVIPRIO2H:
|
||||||
|
case CSR_VSIEH:
|
||||||
|
case CSR_VSIPH:
|
||||||
|
return mtopi_exists(target) &&
|
||||||
|
riscv_xlen(target) == 32 &&
|
||||||
|
riscv_supports_extension(target, 'H');
|
||||||
|
}
|
||||||
|
return is_known_standard_csr(csr_number);
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned int gdb_regno_custom_number(const struct target *target, uint32_t regno)
|
||||||
|
{
|
||||||
|
if (regno < GDB_REGNO_COUNT)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
RISCV_INFO(info);
|
||||||
|
assert(!list_empty(&info->expose_custom));
|
||||||
|
range_list_t *range;
|
||||||
|
unsigned int regno_start = GDB_REGNO_COUNT;
|
||||||
|
unsigned int start = 0;
|
||||||
|
unsigned int offset = 0;
|
||||||
|
list_for_each_entry(range, &info->expose_custom, list) {
|
||||||
|
start = range->low;
|
||||||
|
assert(regno >= regno_start);
|
||||||
|
offset = regno - regno_start;
|
||||||
|
const unsigned int regs_in_range = range->high - range->low + 1;
|
||||||
|
if (offset < regs_in_range)
|
||||||
|
break;
|
||||||
|
regno_start += regs_in_range;
|
||||||
|
}
|
||||||
|
return start + offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct reg *riscv_reg_impl_cache_entry(const struct target *target,
|
||||||
|
uint32_t number)
|
||||||
|
{
|
||||||
|
assert(target->reg_cache);
|
||||||
|
assert(target->reg_cache->reg_list);
|
||||||
|
assert(number < target->reg_cache->num_regs);
|
||||||
|
return &target->reg_cache->reg_list[number];
|
||||||
|
}
|
||||||
|
|
||||||
|
static int resize_reg(const struct target *target, uint32_t regno, bool exist,
|
||||||
|
uint32_t size)
|
||||||
|
{
|
||||||
|
struct reg *reg = riscv_reg_impl_cache_entry(target, regno);
|
||||||
|
assert(riscv_reg_impl_is_initialized(reg));
|
||||||
|
free(reg->value);
|
||||||
|
reg->size = size;
|
||||||
|
reg->exist = exist;
|
||||||
|
if (reg->exist) {
|
||||||
|
reg->value = malloc(DIV_ROUND_UP(reg->size, 8));
|
||||||
|
if (!reg->value) {
|
||||||
|
LOG_ERROR("Failed to allocate memory.");
|
||||||
|
return ERROR_FAIL;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
reg->value = NULL;
|
||||||
|
}
|
||||||
|
assert(riscv_reg_impl_is_initialized(reg));
|
||||||
|
return ERROR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_reg_exist(const struct target *target, uint32_t regno, bool exist)
|
||||||
|
{
|
||||||
|
const struct reg *reg = riscv_reg_impl_cache_entry(target, regno);
|
||||||
|
assert(riscv_reg_impl_is_initialized(reg));
|
||||||
|
return resize_reg(target, regno, exist, reg->size);
|
||||||
|
}
|
||||||
|
|
||||||
|
int riscv_reg_impl_init_one(struct target *target, uint32_t regno, const struct reg_arch_type *reg_type)
|
||||||
|
{
|
||||||
|
struct reg * const reg = riscv_reg_impl_cache_entry(target, regno);
|
||||||
|
if (riscv_reg_impl_is_initialized(reg))
|
||||||
|
return ERROR_OK;
|
||||||
|
reg->number = regno;
|
||||||
|
reg->type = reg_type;
|
||||||
|
reg->dirty = false;
|
||||||
|
reg->valid = false;
|
||||||
|
reg->hidden = false;
|
||||||
|
reg->name = riscv_reg_gdb_regno_name(target, regno);
|
||||||
|
reg->feature = gdb_regno_feature(regno);
|
||||||
|
reg->caller_save = gdb_regno_caller_save(regno);
|
||||||
|
reg->reg_data_type = gdb_regno_reg_data_type(target, regno);
|
||||||
|
reg->group = gdb_regno_group(regno);
|
||||||
|
if (regno < GDB_REGNO_COUNT) {
|
||||||
|
RISCV_INFO(info);
|
||||||
|
reg->arch_info = &info->shared_reg_info;
|
||||||
|
} else {
|
||||||
|
reg->arch_info = calloc(1, sizeof(riscv_reg_info_t));
|
||||||
|
if (!reg->arch_info) {
|
||||||
|
LOG_ERROR("Out of memory.");
|
||||||
|
return ERROR_FAIL;
|
||||||
|
}
|
||||||
|
riscv_reg_info_t * const reg_arch_info = reg->arch_info;
|
||||||
|
reg_arch_info->target = target;
|
||||||
|
reg_arch_info->custom_number = gdb_regno_custom_number(target, regno);
|
||||||
|
}
|
||||||
|
return resize_reg(target, regno, gdb_regno_exist(target, regno),
|
||||||
|
gdb_regno_size(target, regno));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int init_custom_register_names(struct list_head *expose_custom,
|
||||||
|
struct reg_name_table *custom_register_names)
|
||||||
|
{
|
||||||
|
unsigned int custom_regs_num = 0;
|
||||||
|
if (!list_empty(expose_custom)) {
|
||||||
|
range_list_t *entry;
|
||||||
|
list_for_each_entry(entry, expose_custom, list)
|
||||||
|
custom_regs_num += entry->high - entry->low + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!custom_regs_num)
|
||||||
|
return ERROR_OK;
|
||||||
|
|
||||||
|
custom_register_names->reg_names = calloc(custom_regs_num, sizeof(char *));
|
||||||
|
if (!custom_register_names->reg_names) {
|
||||||
|
LOG_ERROR("Failed to allocate memory for custom_register_names->reg_names");
|
||||||
|
return ERROR_FAIL;
|
||||||
|
}
|
||||||
|
custom_register_names->num_entries = custom_regs_num;
|
||||||
|
char **reg_names = custom_register_names->reg_names;
|
||||||
|
range_list_t *range;
|
||||||
|
unsigned int next_custom_reg_index = 0;
|
||||||
|
list_for_each_entry(range, expose_custom, list) {
|
||||||
|
for (unsigned int custom_number = range->low; custom_number <= range->high; ++custom_number) {
|
||||||
|
if (range->name)
|
||||||
|
reg_names[next_custom_reg_index] = init_reg_name(range->name);
|
||||||
|
else
|
||||||
|
reg_names[next_custom_reg_index] =
|
||||||
|
init_reg_name_with_prefix("custom", custom_number);
|
||||||
|
|
||||||
|
if (!reg_names[next_custom_reg_index])
|
||||||
|
return ERROR_FAIL;
|
||||||
|
++next_custom_reg_index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ERROR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int riscv_reg_impl_init_cache(struct target *target)
|
||||||
|
{
|
||||||
|
RISCV_INFO(info);
|
||||||
|
|
||||||
|
riscv_reg_free_all(target);
|
||||||
|
|
||||||
|
target->reg_cache = calloc(1, sizeof(*target->reg_cache));
|
||||||
|
if (!target->reg_cache) {
|
||||||
|
LOG_TARGET_ERROR(target, "Failed to allocate memory for target->reg_cache");
|
||||||
|
return ERROR_FAIL;
|
||||||
|
}
|
||||||
|
target->reg_cache->name = "RISC-V Registers";
|
||||||
|
|
||||||
|
if (init_custom_register_names(&info->expose_custom, &info->custom_register_names) != ERROR_OK) {
|
||||||
|
LOG_TARGET_ERROR(target, "init_custom_register_names failed");
|
||||||
|
return ERROR_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
target->reg_cache->num_regs = GDB_REGNO_COUNT + info->custom_register_names.num_entries;
|
||||||
|
LOG_TARGET_DEBUG(target, "create register cache for %d registers",
|
||||||
|
target->reg_cache->num_regs);
|
||||||
|
|
||||||
|
target->reg_cache->reg_list =
|
||||||
|
calloc(target->reg_cache->num_regs, sizeof(struct reg));
|
||||||
|
if (!target->reg_cache->reg_list) {
|
||||||
|
LOG_TARGET_ERROR(target, "Failed to allocate memory for target->reg_cache->reg_list");
|
||||||
|
return ERROR_FAIL;
|
||||||
|
}
|
||||||
|
return ERROR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int riscv_reg_impl_expose_csrs(const struct target *target)
|
||||||
|
{
|
||||||
|
RISCV_INFO(info);
|
||||||
|
range_list_t *entry;
|
||||||
|
list_for_each_entry(entry, &info->expose_csr, list) {
|
||||||
|
assert(entry->low <= entry->high);
|
||||||
|
assert(entry->high <= GDB_REGNO_CSR4095 - GDB_REGNO_CSR0);
|
||||||
|
const enum gdb_regno last_regno = GDB_REGNO_CSR0 + entry->high;
|
||||||
|
for (enum gdb_regno regno = GDB_REGNO_CSR0 + entry->low;
|
||||||
|
regno <= last_regno; ++regno) {
|
||||||
|
struct reg * const reg = riscv_reg_impl_cache_entry(target, regno);
|
||||||
|
const unsigned int csr_number = regno - GDB_REGNO_CSR0;
|
||||||
|
if (reg->exist) {
|
||||||
|
LOG_TARGET_WARNING(target,
|
||||||
|
"Not exposing CSR %d: register already exists.",
|
||||||
|
csr_number);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (set_reg_exist(target, regno, /*exist*/ true) != ERROR_OK)
|
||||||
|
return ERROR_FAIL;
|
||||||
|
LOG_TARGET_DEBUG(target, "Exposing additional CSR %d (name=%s)",
|
||||||
|
csr_number, reg->name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ERROR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void riscv_reg_impl_hide_csrs(const struct target *target)
|
||||||
|
{
|
||||||
|
RISCV_INFO(info);
|
||||||
|
range_list_t *entry;
|
||||||
|
list_for_each_entry(entry, &info->hide_csr, list) {
|
||||||
|
assert(entry->high <= GDB_REGNO_CSR4095 - GDB_REGNO_CSR0);
|
||||||
|
const enum gdb_regno last_regno = GDB_REGNO_CSR0 + entry->high;
|
||||||
|
for (enum gdb_regno regno = GDB_REGNO_CSR0 + entry->low;
|
||||||
|
regno <= last_regno; ++regno) {
|
||||||
|
struct reg * const reg = riscv_reg_impl_cache_entry(target, regno);
|
||||||
|
const unsigned int csr_number = regno - GDB_REGNO_CSR0;
|
||||||
|
if (!reg->exist) {
|
||||||
|
LOG_TARGET_WARNING(target,
|
||||||
|
"Not hiding CSR %d: register does not exist.",
|
||||||
|
csr_number);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
LOG_TARGET_DEBUG(target, "Hiding CSR %d (name=%s).", csr_number, reg->name);
|
||||||
|
reg->hidden = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void riscv_reg_free_all(struct target *target)
|
||||||
|
{
|
||||||
|
free_reg_names(target);
|
||||||
|
/* Free the shared structure use for most registers. */
|
||||||
|
if (!target->reg_cache)
|
||||||
|
return;
|
||||||
|
if (target->reg_cache->reg_list) {
|
||||||
|
for (unsigned int i = GDB_REGNO_COUNT; i < target->reg_cache->num_regs; i++)
|
||||||
|
free(target->reg_cache->reg_list[i].arch_info);
|
||||||
|
for (unsigned int i = 0; i < target->reg_cache->num_regs; i++)
|
||||||
|
free(target->reg_cache->reg_list[i].value);
|
||||||
|
free(target->reg_cache->reg_list);
|
||||||
|
}
|
||||||
|
free(target->reg_cache);
|
||||||
|
target->reg_cache = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int riscv_reg_flush_all(struct target *target)
|
||||||
|
{
|
||||||
|
if (!target->reg_cache)
|
||||||
|
return ERROR_OK;
|
||||||
|
|
||||||
|
LOG_TARGET_DEBUG(target, "Flushing register cache");
|
||||||
|
|
||||||
|
/* Writing non-GPR registers may require progbuf execution, and some GPRs
|
||||||
|
* may become dirty in the process (e.g. S0, S1). For that reason, flush
|
||||||
|
* registers in reverse order, so that GPRs are flushed last.
|
||||||
|
*/
|
||||||
|
for (unsigned int number = target->reg_cache->num_regs; number-- > 0; ) {
|
||||||
|
struct reg *reg = riscv_reg_impl_cache_entry(target, number);
|
||||||
|
if (reg->valid && reg->dirty) {
|
||||||
|
riscv_reg_t value = buf_get_u64(reg->value, 0, reg->size);
|
||||||
|
|
||||||
|
LOG_TARGET_DEBUG(target, "%s is dirty; write back 0x%" PRIx64,
|
||||||
|
reg->name, value);
|
||||||
|
if (riscv_reg_write(target, number, value) != ERROR_OK)
|
||||||
|
return ERROR_FAIL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LOG_TARGET_DEBUG(target, "Flush of register cache completed");
|
||||||
|
return ERROR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is used internally by functions that change register values.
|
||||||
|
* If `write_through` is true, it is ensured that the value of the target's
|
||||||
|
* register is set to be equal to the `value` argument. The cached value is
|
||||||
|
* updated if the register is cacheable.
|
||||||
|
* TODO: Currently `reg->get/set` is implemented in terms of
|
||||||
|
* `riscv_get/set_register`. However, the intention behind
|
||||||
|
* `riscv_get/set_register` is to work with the cache, therefore it accesses
|
||||||
|
* and modifyes register cache directly. The idea is to implement
|
||||||
|
* `riscv_get/set_register` in terms of `riscv_reg_impl_cache_entry` and
|
||||||
|
* `reg->get/set`.
|
||||||
|
*/
|
||||||
|
static int riscv_set_or_write_register(struct target *target,
|
||||||
|
enum gdb_regno regid, riscv_reg_t value, bool write_through)
|
||||||
|
{
|
||||||
|
RISCV_INFO(r);
|
||||||
|
assert(r);
|
||||||
|
if (r->dtm_version == DTM_DTMCS_VERSION_0_11)
|
||||||
|
return riscv011_set_register(target, regid, value);
|
||||||
|
|
||||||
|
keep_alive();
|
||||||
|
|
||||||
|
if (regid == GDB_REGNO_PC) {
|
||||||
|
return riscv_set_or_write_register(target, GDB_REGNO_DPC, value, write_through);
|
||||||
|
} else if (regid == GDB_REGNO_PRIV) {
|
||||||
|
riscv_reg_t dcsr;
|
||||||
|
|
||||||
|
if (riscv_reg_get(target, &dcsr, GDB_REGNO_DCSR) != ERROR_OK)
|
||||||
|
return ERROR_FAIL;
|
||||||
|
dcsr = set_field(dcsr, CSR_DCSR_PRV, get_field(value, VIRT_PRIV_PRV));
|
||||||
|
dcsr = set_field(dcsr, CSR_DCSR_V, get_field(value, VIRT_PRIV_V));
|
||||||
|
return riscv_set_or_write_register(target, GDB_REGNO_DCSR, dcsr, write_through);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!target->reg_cache) {
|
||||||
|
assert(!target_was_examined(target));
|
||||||
|
LOG_TARGET_DEBUG(target,
|
||||||
|
"No cache, writing to target: %s <- 0x%" PRIx64,
|
||||||
|
riscv_reg_gdb_regno_name(target, regid), value);
|
||||||
|
return riscv013_set_register(target, regid, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct reg *reg = riscv_reg_impl_cache_entry(target, regid);
|
||||||
|
|
||||||
|
if (!reg->exist) {
|
||||||
|
LOG_TARGET_DEBUG(target, "Register %s does not exist.", reg->name);
|
||||||
|
return ERROR_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (target->state != TARGET_HALTED) {
|
||||||
|
LOG_TARGET_DEBUG(target,
|
||||||
|
"Target not halted, writing to target: %s <- 0x%" PRIx64,
|
||||||
|
reg->name, value);
|
||||||
|
return riscv013_set_register(target, regid, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool need_to_write = !reg->valid || reg->dirty ||
|
||||||
|
value != buf_get_u64(reg->value, 0, reg->size);
|
||||||
|
const bool cacheable = riscv_reg_impl_gdb_regno_cacheable(regid, need_to_write);
|
||||||
|
|
||||||
|
if (!cacheable || (write_through && need_to_write)) {
|
||||||
|
LOG_TARGET_DEBUG(target,
|
||||||
|
"Writing to target: %s <- 0x%" PRIx64 " (cacheable=%s, valid=%s, dirty=%s)",
|
||||||
|
reg->name, value, cacheable ? "true" : "false",
|
||||||
|
reg->valid ? "true" : "false",
|
||||||
|
reg->dirty ? "true" : "false");
|
||||||
|
if (riscv013_set_register(target, regid, value) != ERROR_OK)
|
||||||
|
return ERROR_FAIL;
|
||||||
|
|
||||||
|
reg->dirty = false;
|
||||||
|
} else {
|
||||||
|
reg->dirty = need_to_write;
|
||||||
|
}
|
||||||
|
|
||||||
|
buf_set_u64(reg->value, 0, reg->size, value);
|
||||||
|
reg->valid = cacheable;
|
||||||
|
|
||||||
|
LOG_TARGET_DEBUG(target,
|
||||||
|
"Wrote 0x%" PRIx64 " to %s (cacheable=%s, valid=%s, dirty=%s)",
|
||||||
|
value, reg->name, cacheable ? "true" : "false",
|
||||||
|
reg->valid ? "true" : "false",
|
||||||
|
reg->dirty ? "true" : "false");
|
||||||
|
return ERROR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is used to change the value of a register. The new value may
|
||||||
|
* be cached, and may not be written until the hart is resumed.
|
||||||
|
* TODO: Currently `reg->get/set` is implemented in terms of
|
||||||
|
* `riscv_get/set_register`. However, the intention behind
|
||||||
|
* `riscv_get/set_register` is to work with the cache, therefore it accesses
|
||||||
|
* and modifyes register cache directly. The idea is to implement
|
||||||
|
* `riscv_get/set_register` in terms of `riscv_reg_impl_cache_entry` and
|
||||||
|
* `reg->get/set`.
|
||||||
|
*/
|
||||||
|
int riscv_reg_set(struct target *target, enum gdb_regno regid,
|
||||||
|
riscv_reg_t value)
|
||||||
|
{
|
||||||
|
return riscv_set_or_write_register(target, regid, value,
|
||||||
|
/* write_through */ false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is used to change the value of a register. The new value may
|
||||||
|
* be cached, but it will be written to hart immediately.
|
||||||
|
* TODO: Currently `reg->get/set` is implemented in terms of
|
||||||
|
* `riscv_get/set_register`. However, the intention behind
|
||||||
|
* `riscv_get/set_register` is to work with the cache, therefore it accesses
|
||||||
|
* and modifyes register cache directly. The idea is to implement
|
||||||
|
* `riscv_get/set_register` in terms of `riscv_reg_impl_cache_entry` and
|
||||||
|
* `reg->get/set`.
|
||||||
|
*/
|
||||||
|
int riscv_reg_write(struct target *target, enum gdb_regno regid,
|
||||||
|
riscv_reg_t value)
|
||||||
|
{
|
||||||
|
return riscv_set_or_write_register(target, regid, value,
|
||||||
|
/* write_through */ true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is used to get the value of a register. If possible, the value
|
||||||
|
* in cache will be updated.
|
||||||
|
* TODO: Currently `reg->get/set` is implemented in terms of
|
||||||
|
* `riscv_get/set_register`. However, the intention behind
|
||||||
|
* `riscv_get/set_register` is to work with the cache, therefore it accesses
|
||||||
|
* and modifyes register cache directly. The idea is to implement
|
||||||
|
* `riscv_get/set_register` in terms of `riscv_reg_impl_cache_entry` and
|
||||||
|
* `reg->get/set`.
|
||||||
|
*/
|
||||||
|
int riscv_reg_get(struct target *target, riscv_reg_t *value,
|
||||||
|
enum gdb_regno regid)
|
||||||
|
{
|
||||||
|
RISCV_INFO(r);
|
||||||
|
assert(r);
|
||||||
|
if (r->dtm_version == DTM_DTMCS_VERSION_0_11)
|
||||||
|
return riscv013_get_register(target, value, regid);
|
||||||
|
|
||||||
|
keep_alive();
|
||||||
|
|
||||||
|
if (regid == GDB_REGNO_PC)
|
||||||
|
return riscv_reg_get(target, value, GDB_REGNO_DPC);
|
||||||
|
|
||||||
|
if (!target->reg_cache) {
|
||||||
|
assert(!target_was_examined(target));
|
||||||
|
LOG_TARGET_DEBUG(target, "No cache, reading %s from target",
|
||||||
|
riscv_reg_gdb_regno_name(target, regid));
|
||||||
|
return riscv013_get_register(target, value, regid);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct reg *reg = riscv_reg_impl_cache_entry(target, regid);
|
||||||
|
if (!reg->exist) {
|
||||||
|
LOG_TARGET_DEBUG(target, "Register %s does not exist.", reg->name);
|
||||||
|
return ERROR_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reg->valid) {
|
||||||
|
*value = buf_get_u64(reg->value, 0, reg->size);
|
||||||
|
LOG_TARGET_DEBUG(target, "Read %s: 0x%" PRIx64 " (cached)", reg->name,
|
||||||
|
*value);
|
||||||
|
return ERROR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_TARGET_DEBUG(target, "Reading %s from target", reg->name);
|
||||||
|
if (riscv013_get_register(target, value, regid) != ERROR_OK)
|
||||||
|
return ERROR_FAIL;
|
||||||
|
|
||||||
|
buf_set_u64(reg->value, 0, reg->size, *value);
|
||||||
|
reg->valid = riscv_reg_impl_gdb_regno_cacheable(regid, /* is write? */ false) &&
|
||||||
|
target->state == TARGET_HALTED;
|
||||||
|
reg->dirty = false;
|
||||||
|
|
||||||
|
LOG_TARGET_DEBUG(target, "Read %s: 0x%" PRIx64, reg->name, *value);
|
||||||
|
return ERROR_OK;
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||||
|
|
||||||
|
#ifndef OPENOCD_TARGET_RISCV_RISCV_REG_H
|
||||||
|
#define OPENOCD_TARGET_RISCV_RISCV_REG_H
|
||||||
|
|
||||||
|
#include "target/target.h"
|
||||||
|
#include "target/register.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This file describes the register cache interface available to the RISC-V
|
||||||
|
* target. Functions declared here should be safe to use once register cache is
|
||||||
|
* completely initialized and may be used with caution during register cache
|
||||||
|
* initialization.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** Return the name of the register by it's number in register cache. */
|
||||||
|
const char *riscv_reg_gdb_regno_name(const struct target *target, enum gdb_regno regno);
|
||||||
|
|
||||||
|
/** Free register cache and associated structures. */
|
||||||
|
void riscv_reg_free_all(struct target *target);
|
||||||
|
|
||||||
|
/** Write all dirty registers to the target. */
|
||||||
|
int riscv_reg_flush_all(struct target *target);
|
||||||
|
/**
|
||||||
|
* Set the register value. For cacheable registers, only the cache is updated
|
||||||
|
* (write-back mode).
|
||||||
|
*/
|
||||||
|
int riscv_reg_set(struct target *target, enum gdb_regno i, riscv_reg_t v);
|
||||||
|
/**
|
||||||
|
* Set the register value and immediately write it to the target
|
||||||
|
* (write-through mode).
|
||||||
|
*/
|
||||||
|
int riscv_reg_write(struct target *target, enum gdb_regno i, riscv_reg_t v);
|
||||||
|
/** Get register, from the cache if it's in there. */
|
||||||
|
int riscv_reg_get(struct target *target, riscv_reg_t *value,
|
||||||
|
enum gdb_regno r);
|
||||||
|
|
||||||
|
#endif /* OPENOCD_TARGET_RISCV_RISCV_REG_H */
|
|
@ -0,0 +1,214 @@
|
||||||
|
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||||
|
|
||||||
|
#ifndef OPENOCD_TARGET_RISCV_RISCV_REG_IMPL_H
|
||||||
|
#define OPENOCD_TARGET_RISCV_RISCV_REG_IMPL_H
|
||||||
|
|
||||||
|
#include "gdb_regs.h"
|
||||||
|
#include "riscv.h"
|
||||||
|
#include "target/target.h"
|
||||||
|
#include "target/register.h"
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This file describes the helpers to use during register cache initialization
|
||||||
|
* of a RISC-V target. Each cache entry proceedes through the following stages:
|
||||||
|
* - not allocated before `riscv_reg_impl_init_cache()`
|
||||||
|
* - not initialized before the call to `riscv_reg_impl_init_one()` with appropriate regno.
|
||||||
|
* - initialized until `riscv_reg_free_all()` is called.
|
||||||
|
*/
|
||||||
|
static inline bool riscv_reg_impl_is_initialized(const struct reg *reg)
|
||||||
|
{
|
||||||
|
assert(reg);
|
||||||
|
if (!reg->feature) {
|
||||||
|
const struct reg default_reg = {0};
|
||||||
|
assert(!memcmp(&default_reg, reg, sizeof(*reg)));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
assert(reg->arch_info);
|
||||||
|
assert(((riscv_reg_info_t *)reg->arch_info)->target);
|
||||||
|
assert((!reg->exist && !reg->value) || (reg->exist && reg->value));
|
||||||
|
assert(reg->valid || !reg->dirty);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Initialize register cache. Note, that each specific register cache entry is
|
||||||
|
* not initialized by this function.
|
||||||
|
*/
|
||||||
|
int riscv_reg_impl_init_cache(struct target *target);
|
||||||
|
|
||||||
|
/** Initialize register. */
|
||||||
|
int riscv_reg_impl_init_one(struct target *target, uint32_t regno,
|
||||||
|
const struct reg_arch_type *reg_type);
|
||||||
|
|
||||||
|
/** Return the entry in the register cache of the target. */
|
||||||
|
struct reg *riscv_reg_impl_cache_entry(const struct target *target,
|
||||||
|
uint32_t number);
|
||||||
|
|
||||||
|
/** Return the target that owns the cache entry. */
|
||||||
|
struct target *riscv_reg_impl_get_target(const struct reg *reg);
|
||||||
|
|
||||||
|
static inline void init_shared_reg_info(struct target *target)
|
||||||
|
{
|
||||||
|
RISCV_INFO(info);
|
||||||
|
info->shared_reg_info.target = target;
|
||||||
|
info->shared_reg_info.custom_number = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** TODO: vector register type description can be moved into `riscv013_info_t`,
|
||||||
|
* since 0.11 targets do not support access to vector registers. */
|
||||||
|
static inline void riscv_reg_impl_init_vector_reg_type(const struct target *target)
|
||||||
|
{
|
||||||
|
RISCV_INFO(info);
|
||||||
|
static struct reg_data_type type_uint8 = { .type = REG_TYPE_UINT8, .id = "uint8" };
|
||||||
|
static struct reg_data_type type_uint16 = { .type = REG_TYPE_UINT16, .id = "uint16" };
|
||||||
|
static struct reg_data_type type_uint32 = { .type = REG_TYPE_UINT32, .id = "uint32" };
|
||||||
|
static struct reg_data_type type_uint64 = { .type = REG_TYPE_UINT64, .id = "uint64" };
|
||||||
|
static struct reg_data_type type_uint128 = { .type = REG_TYPE_UINT128, .id = "uint128" };
|
||||||
|
|
||||||
|
/* This is roughly the XML we want:
|
||||||
|
* <vector id="bytes" type="uint8" count="16"/>
|
||||||
|
* <vector id="shorts" type="uint16" count="8"/>
|
||||||
|
* <vector id="words" type="uint32" count="4"/>
|
||||||
|
* <vector id="longs" type="uint64" count="2"/>
|
||||||
|
* <vector id="quads" type="uint128" count="1"/>
|
||||||
|
* <union id="riscv_vector_type">
|
||||||
|
* <field name="b" type="bytes"/>
|
||||||
|
* <field name="s" type="shorts"/>
|
||||||
|
* <field name="w" type="words"/>
|
||||||
|
* <field name="l" type="longs"/>
|
||||||
|
* <field name="q" type="quads"/>
|
||||||
|
* </union>
|
||||||
|
*/
|
||||||
|
|
||||||
|
info->vector_uint8.type = &type_uint8;
|
||||||
|
info->vector_uint8.count = riscv_vlenb(target);
|
||||||
|
info->type_uint8_vector.type = REG_TYPE_ARCH_DEFINED;
|
||||||
|
info->type_uint8_vector.id = "bytes";
|
||||||
|
info->type_uint8_vector.type_class = REG_TYPE_CLASS_VECTOR;
|
||||||
|
info->type_uint8_vector.reg_type_vector = &info->vector_uint8;
|
||||||
|
|
||||||
|
info->vector_uint16.type = &type_uint16;
|
||||||
|
info->vector_uint16.count = riscv_vlenb(target) / 2;
|
||||||
|
info->type_uint16_vector.type = REG_TYPE_ARCH_DEFINED;
|
||||||
|
info->type_uint16_vector.id = "shorts";
|
||||||
|
info->type_uint16_vector.type_class = REG_TYPE_CLASS_VECTOR;
|
||||||
|
info->type_uint16_vector.reg_type_vector = &info->vector_uint16;
|
||||||
|
|
||||||
|
info->vector_uint32.type = &type_uint32;
|
||||||
|
info->vector_uint32.count = riscv_vlenb(target) / 4;
|
||||||
|
info->type_uint32_vector.type = REG_TYPE_ARCH_DEFINED;
|
||||||
|
info->type_uint32_vector.id = "words";
|
||||||
|
info->type_uint32_vector.type_class = REG_TYPE_CLASS_VECTOR;
|
||||||
|
info->type_uint32_vector.reg_type_vector = &info->vector_uint32;
|
||||||
|
|
||||||
|
info->vector_uint64.type = &type_uint64;
|
||||||
|
info->vector_uint64.count = riscv_vlenb(target) / 8;
|
||||||
|
info->type_uint64_vector.type = REG_TYPE_ARCH_DEFINED;
|
||||||
|
info->type_uint64_vector.id = "longs";
|
||||||
|
info->type_uint64_vector.type_class = REG_TYPE_CLASS_VECTOR;
|
||||||
|
info->type_uint64_vector.reg_type_vector = &info->vector_uint64;
|
||||||
|
|
||||||
|
info->vector_uint128.type = &type_uint128;
|
||||||
|
info->vector_uint128.count = riscv_vlenb(target) / 16;
|
||||||
|
info->type_uint128_vector.type = REG_TYPE_ARCH_DEFINED;
|
||||||
|
info->type_uint128_vector.id = "quads";
|
||||||
|
info->type_uint128_vector.type_class = REG_TYPE_CLASS_VECTOR;
|
||||||
|
info->type_uint128_vector.reg_type_vector = &info->vector_uint128;
|
||||||
|
|
||||||
|
info->vector_fields[0].name = "b";
|
||||||
|
info->vector_fields[0].type = &info->type_uint8_vector;
|
||||||
|
if (riscv_vlenb(target) >= 2) {
|
||||||
|
info->vector_fields[0].next = info->vector_fields + 1;
|
||||||
|
info->vector_fields[1].name = "s";
|
||||||
|
info->vector_fields[1].type = &info->type_uint16_vector;
|
||||||
|
} else {
|
||||||
|
info->vector_fields[0].next = NULL;
|
||||||
|
}
|
||||||
|
if (riscv_vlenb(target) >= 4) {
|
||||||
|
info->vector_fields[1].next = info->vector_fields + 2;
|
||||||
|
info->vector_fields[2].name = "w";
|
||||||
|
info->vector_fields[2].type = &info->type_uint32_vector;
|
||||||
|
} else {
|
||||||
|
info->vector_fields[1].next = NULL;
|
||||||
|
}
|
||||||
|
if (riscv_vlenb(target) >= 8) {
|
||||||
|
info->vector_fields[2].next = info->vector_fields + 3;
|
||||||
|
info->vector_fields[3].name = "l";
|
||||||
|
info->vector_fields[3].type = &info->type_uint64_vector;
|
||||||
|
} else {
|
||||||
|
info->vector_fields[2].next = NULL;
|
||||||
|
}
|
||||||
|
if (riscv_vlenb(target) >= 16) {
|
||||||
|
info->vector_fields[3].next = info->vector_fields + 4;
|
||||||
|
info->vector_fields[4].name = "q";
|
||||||
|
info->vector_fields[4].type = &info->type_uint128_vector;
|
||||||
|
} else {
|
||||||
|
info->vector_fields[3].next = NULL;
|
||||||
|
}
|
||||||
|
info->vector_fields[4].next = NULL;
|
||||||
|
|
||||||
|
info->vector_union.fields = info->vector_fields;
|
||||||
|
|
||||||
|
info->type_vector.type = REG_TYPE_ARCH_DEFINED;
|
||||||
|
info->type_vector.id = "riscv_vector";
|
||||||
|
info->type_vector.type_class = REG_TYPE_CLASS_UNION;
|
||||||
|
info->type_vector.reg_type_union = &info->vector_union;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Expose additional CSRs, as specified by `riscv_info_t::expose_csr` list. */
|
||||||
|
int riscv_reg_impl_expose_csrs(const struct target *target);
|
||||||
|
|
||||||
|
/** Hide additional CSRs, as specified by `riscv_info_t::hide_csr` list. */
|
||||||
|
void riscv_reg_impl_hide_csrs(const struct target *target);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If write is true:
|
||||||
|
* return true iff we are guaranteed that the register will contain exactly
|
||||||
|
* the value we just wrote when it's read.
|
||||||
|
* If write is false:
|
||||||
|
* return true iff we are guaranteed that the register will read the same
|
||||||
|
* value in the future as the value we just read.
|
||||||
|
*/
|
||||||
|
static inline bool riscv_reg_impl_gdb_regno_cacheable(enum gdb_regno regno,
|
||||||
|
bool is_write)
|
||||||
|
{
|
||||||
|
if (regno == GDB_REGNO_ZERO)
|
||||||
|
return !is_write;
|
||||||
|
|
||||||
|
/* GPRs, FPRs, vector registers are just normal data stores. */
|
||||||
|
if (regno <= GDB_REGNO_XPR31 ||
|
||||||
|
(regno >= GDB_REGNO_FPR0 && regno <= GDB_REGNO_FPR31) ||
|
||||||
|
(regno >= GDB_REGNO_V0 && regno <= GDB_REGNO_V31))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
/* Most CSRs won't change value on us, but we can't assume it about arbitrary
|
||||||
|
* CSRs. */
|
||||||
|
switch (regno) {
|
||||||
|
case GDB_REGNO_DPC:
|
||||||
|
case GDB_REGNO_VSTART:
|
||||||
|
case GDB_REGNO_VXSAT:
|
||||||
|
case GDB_REGNO_VXRM:
|
||||||
|
case GDB_REGNO_VLENB:
|
||||||
|
case GDB_REGNO_VL:
|
||||||
|
case GDB_REGNO_VTYPE:
|
||||||
|
case GDB_REGNO_MISA:
|
||||||
|
case GDB_REGNO_DCSR:
|
||||||
|
case GDB_REGNO_DSCRATCH0:
|
||||||
|
case GDB_REGNO_MSTATUS:
|
||||||
|
case GDB_REGNO_MEPC:
|
||||||
|
case GDB_REGNO_MCAUSE:
|
||||||
|
case GDB_REGNO_SATP:
|
||||||
|
/*
|
||||||
|
* WARL registers might not contain the value we just wrote, but
|
||||||
|
* these ones won't spontaneously change their value either. *
|
||||||
|
*/
|
||||||
|
return !is_write;
|
||||||
|
|
||||||
|
case GDB_REGNO_TSELECT: /* I think this should be above, but then it doesn't work. */
|
||||||
|
case GDB_REGNO_TDATA1: /* Changes value when tselect is changed. */
|
||||||
|
case GDB_REGNO_TDATA2: /* Changes value when tselect is changed. */
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif /* OPENOCD_TARGET_RISCV_RISCV_REG_IMPL_H */
|
|
@ -32,6 +32,7 @@
|
||||||
|
|
||||||
#include "target/target.h"
|
#include "target/target.h"
|
||||||
#include "riscv.h"
|
#include "riscv.h"
|
||||||
|
#include "riscv_reg.h"
|
||||||
|
|
||||||
static int riscv_semihosting_setup(struct target *target, int enable);
|
static int riscv_semihosting_setup(struct target *target, int enable);
|
||||||
static int riscv_semihosting_post_result(struct target *target);
|
static int riscv_semihosting_post_result(struct target *target);
|
||||||
|
@ -67,7 +68,7 @@ enum semihosting_result riscv_semihosting(struct target *target, int *retval)
|
||||||
}
|
}
|
||||||
|
|
||||||
riscv_reg_t pc;
|
riscv_reg_t pc;
|
||||||
int result = riscv_get_register(target, &pc, GDB_REGNO_PC);
|
int result = riscv_reg_get(target, &pc, GDB_REGNO_PC);
|
||||||
if (result != ERROR_OK)
|
if (result != ERROR_OK)
|
||||||
return SEMIHOSTING_ERROR;
|
return SEMIHOSTING_ERROR;
|
||||||
|
|
||||||
|
@ -107,13 +108,13 @@ enum semihosting_result riscv_semihosting(struct target *target, int *retval)
|
||||||
riscv_reg_t r0;
|
riscv_reg_t r0;
|
||||||
riscv_reg_t r1;
|
riscv_reg_t r1;
|
||||||
|
|
||||||
result = riscv_get_register(target, &r0, GDB_REGNO_A0);
|
result = riscv_reg_get(target, &r0, GDB_REGNO_A0);
|
||||||
if (result != ERROR_OK) {
|
if (result != ERROR_OK) {
|
||||||
LOG_TARGET_ERROR(target, "Could not read semihosting operation code (register a0)");
|
LOG_TARGET_ERROR(target, "Could not read semihosting operation code (register a0)");
|
||||||
return SEMIHOSTING_ERROR;
|
return SEMIHOSTING_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
result = riscv_get_register(target, &r1, GDB_REGNO_A1);
|
result = riscv_reg_get(target, &r1, GDB_REGNO_A1);
|
||||||
if (result != ERROR_OK) {
|
if (result != ERROR_OK) {
|
||||||
LOG_TARGET_ERROR(target, "Could not read semihosting operation code (register a1)");
|
LOG_TARGET_ERROR(target, "Could not read semihosting operation code (register a1)");
|
||||||
return SEMIHOSTING_ERROR;
|
return SEMIHOSTING_ERROR;
|
||||||
|
@ -140,7 +141,7 @@ enum semihosting_result riscv_semihosting(struct target *target, int *retval)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Resume right after the EBREAK 4 bytes instruction. */
|
/* Resume right after the EBREAK 4 bytes instruction. */
|
||||||
*retval = riscv_set_register(target, GDB_REGNO_PC, pc + 4);
|
*retval = riscv_reg_set(target, GDB_REGNO_PC, pc + 4);
|
||||||
if (*retval != ERROR_OK)
|
if (*retval != ERROR_OK)
|
||||||
return SEMIHOSTING_ERROR;
|
return SEMIHOSTING_ERROR;
|
||||||
|
|
||||||
|
@ -184,6 +185,6 @@ static int riscv_semihosting_post_result(struct target *target)
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_TARGET_DEBUG(target, "Result: 0x%" PRIx64, semihosting->result);
|
LOG_TARGET_DEBUG(target, "Result: 0x%" PRIx64, semihosting->result);
|
||||||
riscv_set_register(target, GDB_REGNO_A0, semihosting->result);
|
riscv_reg_set(target, GDB_REGNO_A0, semihosting->result);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue