Compare commits

...

20 Commits

Author SHA1 Message Date
Palmer Dabbelt 4af702c9c4 stw 2017-04-14 19:55:32 -07:00
Palmer Dabbelt 763e5cb44c memoize 2017-04-14 17:44:47 -07:00
Palmer Dabbelt 6c74c1c6f3 runs hart0 2017-04-14 17:25:50 -07:00
Palmer Dabbelt 79fb51fe7b read byte 2017-04-14 16:23:29 -07:00
Palmer Dabbelt 9a04773c75 endian 2017-04-14 16:23:16 -07:00
Palmer Dabbelt 62659905ca regaddr 2017-04-14 16:22:56 -07:00
Palmer Dabbelt 02012f2604 fence.i 2017-04-14 14:34:01 -07:00
Palmer Dabbelt 747f769f7d unhalt 2017-04-14 14:19:40 -07:00
Palmer Dabbelt 57d08889ce later 2017-04-14 14:17:27 -07:00
Palmer Dabbelt d652e6ea16 off by one 2017-04-13 22:11:07 -07:00
Palmer Dabbelt 22d102e3c1 Fix a buffer overflow 2017-04-13 18:52:11 -07:00
Palmer Dabbelt 98df29b8d1 Check for step 2017-04-13 17:22:59 -07:00
Palmer Dabbelt e481524362 Replace the 0.13-specific "program_t" with a generic one
This new version changes how we handle temporary registers: rather than
tracking them by hand (like in the old code), they're now tracked as
part of assembling programs.  The register save/restore assertion that
used to fire all the time no longer exists.
2017-04-13 14:37:20 -07:00
mwachs5 0e54044ac7 Remove Off By 1 FIXME because HW is fixed 2017-04-10 14:24:02 -07:00
Palmer Dabbelt 3c5cb81a09 Implement the "vCont" GDB packet 2017-04-07 18:32:54 -07:00
Megan Wachs ad157ec0f6 typo in comment 2017-04-07 15:43:23 -07:00
Palmer Dabbelt a4d3a8d415 Determine the trigger count dynamically 2017-04-07 15:26:24 -07:00
Palmer Dabbelt dd4677c3df Determine the hart count dynamically
This assumes that v0.11 targets have one hart, and that v0.13 targets
have harts IDs that start at 0 and are concecutive.
2017-04-07 15:26:17 -07:00
Palmer Dabbelt 74095335dc Call riscv_xlen() to support 32-bit FESPI targets 2017-04-06 15:23:45 -07:00
Palmer Dabbelt d0fcbc9b51 Add a RISC-V RTOS, which natievly supports multiple harts
This is a work in progress, but it's at the point where you can actually
debug multi-hart programs now.
2017-04-06 15:23:40 -07:00
19 changed files with 2532 additions and 1282 deletions

View File

@ -42,6 +42,7 @@
#include <jtag/jtag.h>
#include <helper/time_support.h>
#include <target/algorithm.h>
#include "target/riscv/riscv.h"
/* Register offsets */
@ -767,12 +768,13 @@ static int steps_execute(struct algorithm_steps *as,
struct fespi_flash_bank *fespi_info = bank->driver_priv;
uint32_t ctrl_base = fespi_info->ctrl_base;
uint8_t *data_buf = malloc(data_wa->size);
int xlen = riscv_xlen(target);
struct reg_param reg_params[2];
init_reg_param(&reg_params[0], "x10", 32, PARAM_OUT);
init_reg_param(&reg_params[1], "x11", 32, PARAM_OUT);
buf_set_u32(reg_params[0].value, 0, 32, ctrl_base);
buf_set_u32(reg_params[1].value, 0, 32, data_wa->address);
init_reg_param(&reg_params[0], "x10", xlen, PARAM_OUT);
init_reg_param(&reg_params[1], "x11", xlen, PARAM_OUT);
buf_set_u64(reg_params[0].value, 0, xlen, ctrl_base);
buf_set_u64(reg_params[1].value, 0, xlen, data_wa->address);
while (!as_empty(as)) {
keep_alive();
unsigned bytes = as_compile(as, data_buf, data_wa->size);

View File

@ -20,8 +20,8 @@ include $(top_srcdir)/common.mk
METASOURCES = AUTO
noinst_LTLIBRARIES = librtos.la
noinst_HEADERS = rtos.h rtos_standard_stackings.h rtos_ecos_stackings.h linux_header.h rtos_chibios_stackings.h rtos_embkernel_stackings.h rtos_mqx_stackings.h
librtos_la_SOURCES = rtos.c rtos_standard_stackings.c rtos_ecos_stackings.c rtos_chibios_stackings.c rtos_embkernel_stackings.c rtos_mqx_stackings.c FreeRTOS.c ThreadX.c eCos.c linux.c ChibiOS.c embKernel.c mqx.c
noinst_HEADERS = rtos.h rtos_standard_stackings.h rtos_ecos_stackings.h linux_header.h rtos_chibios_stackings.h rtos_embkernel_stackings.h rtos_mqx_stackings.h riscv_debug.h
librtos_la_SOURCES = rtos.c rtos_standard_stackings.c rtos_ecos_stackings.c rtos_chibios_stackings.c rtos_embkernel_stackings.c rtos_mqx_stackings.c FreeRTOS.c ThreadX.c eCos.c linux.c ChibiOS.c embKernel.c mqx.c riscv_debug.c
librtos_la_CFLAGS =
if IS_MINGW

315
src/rtos/riscv_debug.c Normal file
View File

@ -0,0 +1,315 @@
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "riscv_debug.h"
#include "target/target.h"
#include "target/riscv/riscv.h"
#include "rtos.h"
#include "server/gdb_server.h"
static int riscv_update_threads(struct rtos *rtos);
static int riscv_gdb_thread_packet(struct connection *connection, const char *packet, int packet_size);
static int riscv_gdb_v_packet(struct connection *connection, const char *packet, int packet_size);
static int riscv_detect_rtos(struct target *target)
{
LOG_ERROR("riscv_detect_rtos() unimplemented");
return -1;
}
static int riscv_create_rtos(struct target *target)
{
LOG_DEBUG("RISC-V Debug 'RTOS' created: this doesn't mean you're running an RTOS, just that you have multi-hart support on RISC-V");
struct riscv_rtos *r = calloc(1, sizeof(*r));
target->rtos->rtos_specific_params = r;
#if 0
r->target_hartid = 0;
r->target_any_hart = true;
r->target_every_hart = true;
#endif
target->rtos->current_threadid = 1;
target->rtos->current_thread = 1;
riscv_update_threads(target->rtos);
target->rtos->gdb_thread_packet = riscv_gdb_thread_packet;
target->rtos->gdb_v_packet = riscv_gdb_v_packet;
return JIM_OK;
}
static int riscv_update_threads(struct rtos *rtos)
{
LOG_DEBUG("Updating the RISC-V Hart List");
/* Figures out how many harts there are on the system. */
int hart_count = riscv_count_harts(rtos->target);
if (rtos->thread_count != hart_count) {
rtos_free_threadlist(rtos);
rtos->thread_count = hart_count;
rtos->thread_details = calloc(rtos->thread_count, sizeof(*rtos->thread_details));
for (int i = 0; i < rtos->thread_count; ++i) {
LOG_DEBUG(" Setting up Hart %d", i);
rtos->thread_details[i].threadid = i + 1;
rtos->thread_details[i].exists = true;
if (asprintf(&rtos->thread_details[i].thread_name_str, "Hart %d", i) < 0)
LOG_ERROR("riscv_update_threads() failed asprintf");
if (asprintf(&rtos->thread_details[i].extra_info_str, "RV64") < 0)
LOG_ERROR("riscv_update_threads() failed asprintf");
}
}
return JIM_OK;
}
static int riscv_gdb_thread_packet(struct connection *connection, const char *packet, int packet_size)
{
struct target *target = get_target_from_connection(connection);
struct rtos *rtos = target->rtos;
struct riscv_rtos *r = (struct riscv_rtos *)(target->rtos->rtos_specific_params);
char *packet_stttrr = malloc(packet_size + 1);
memset(packet_stttrr, '\0', packet_size + 1);
memcpy(packet_stttrr, packet, packet_size);
LOG_DEBUG("handling packet '%s'", packet_stttrr);
switch (packet[0]) {
case 'q':
if (strncmp(packet, "qfThreadInfo", 12) == 0) {
riscv_update_threads(target->rtos);
r->qs_thread_info_offset = 1;
char m[16];
snprintf(m, 16, "m%08x", (int)rtos->thread_details[0].threadid);
gdb_put_packet(connection, m, strlen(m));
return ERROR_OK;
}
if (strncmp(packet, "qsThreadInfo", 12) == 0) {
if (r->qs_thread_info_offset >= rtos->thread_count) {
gdb_put_packet(connection, "l", 1);
return ERROR_OK;
}
int tid = r->qs_thread_info_offset++;
char m[16];
snprintf(m, 16, "m%08x", (int)rtos->thread_details[tid].threadid);
gdb_put_packet(connection, m, strlen(m));
return ERROR_OK;
}
if (strncmp(packet, "qAttached", 9) == 0) {
gdb_put_packet(connection, "1", 1);
return ERROR_OK;
}
if (strncmp(packet, "qThreadExtraInfo", 16) == 0) {
char tid_str[32];
memcpy(tid_str, packet + 17, packet_size - 17);
tid_str[packet_size - 17] = '\0';
char *end;
int tid = strtol(tid_str, &end, 16);
if (*end != '\0') {
LOG_ERROR("Got qThreadExtraInfo with non-numeric TID: '%s'", tid_str);
gdb_put_packet(connection, NULL, 0);
return ERROR_FAIL;
}
char m[16];
snprintf(m, 16, "hart %d", tid);
char h[33];
h[0] = '\0';
for (size_t i = 0; i < strlen(m); ++i) {
char byte[3];
snprintf(byte, 3, "%02x", m[i]);
strncat(h, byte, 32);
}
gdb_put_packet(connection, h, strlen(h));
return ERROR_OK;
}
return GDB_THREAD_PACKET_NOT_CONSUMED;
case 'Q':
return GDB_THREAD_PACKET_NOT_CONSUMED;
case 'H':
/* H op thread-id
*
* Set thread for subsequent operations (m, M, g, G,
* et.al.). Depending on the operation to be performed, op
* should be c for step and continue operations (note that
* this is deprecated, supporting the vCont command is a
* better option), and g for other operations. The thread
* designator thread-id has the format and interpretation
* described in thread-id syntax.
*
* Reply:
* OK for success
* E NN for an error
*/
{
char tid_str[32];
memcpy(tid_str, packet + 2, packet_size - 2);
tid_str[packet_size - 2] = '\0';
char *entptr;
int tid = strtol(tid_str, &entptr, 16);
if (*entptr != '\0') {
LOG_ERROR("Got H packet, but without integer: %s", tid_str);
return GDB_THREAD_PACKET_NOT_CONSUMED;
}
riscv_enable_rtos(target);
switch (tid) {
case 0:
case -1:
riscv_set_all_rtos_harts(target);
break;
default:
riscv_set_rtos_hartid(target, tid - 1);
break;
}
switch (packet[1]) {
case 'g':
case 'c':
gdb_put_packet(connection, "OK", 2);
return ERROR_OK;
default:
LOG_ERROR("Unknown H packet subtype %2x\n", packet[1]);
gdb_put_packet(connection, NULL, 0);
return ERROR_FAIL;
}
}
case 'T':
{
char tid_str[32];
memcpy(tid_str, packet + 1, packet_size - 1);
tid_str[packet_size - 1] = '\0';
char *end;
int tid = strtol(tid_str, &end, 16);
if (*end != '\0') {
LOG_ERROR("T packet with non-numeric tid %s", tid_str);
gdb_put_packet(connection, NULL, 0);
return ERROR_FAIL;
}
riscv_enable_rtos(target);
riscv_update_threads(target->rtos);
if (tid <= target->rtos->thread_count) {
gdb_put_packet(connection, "OK", 2);
return ERROR_OK;
} else {
gdb_put_packet(connection, "E00", 3);
return ERROR_OK;
}
}
case 'c':
case 's':
target->state = TARGET_HALTED;
return JIM_OK;
case 'R':
gdb_put_packet(connection, "E00", 3);
return JIM_OK;
default:
LOG_ERROR("Unknown packet of type 0x%2.2x", packet[0]);
gdb_put_packet(connection, NULL, 0);
return JIM_OK;
}
}
static int riscv_gdb_v_packet(struct connection *connection, const char *packet, int packet_size)
{
char *packet_stttrr = malloc(packet_size + 1);
memset(packet_stttrr, '\0', packet_size + 1);
memcpy(packet_stttrr, packet, packet_size);
LOG_DEBUG("handling packet '%s'", packet_stttrr);
struct target *target = get_target_from_connection(connection);
if (strcmp(packet_stttrr, "vCont?") == 0) {
static const char *message = "OK";
gdb_put_packet(connection, message, strlen(message));
return JIM_OK;
}
int threadid;
if (sscanf(packet_stttrr, "vCont;s:%d;c", &threadid) == 1) {
riscv_set_rtos_hartid(target, threadid - 1);
riscv_step_rtos_hart(target);
gdb_put_packet(connection, "S02", 3);
return JIM_OK;
}
if (strcmp(packet_stttrr, "vCont;c") == 0) {
riscv_resume_all_harts(target);
target->state = TARGET_RUNNING;
target_call_event_callbacks(target, TARGET_EVENT_GDB_START);
gdb_set_frontend_state_running(connection);
return JIM_OK;
}
if (strncmp(packet_stttrr, "vCont", 5) == 0)
LOG_ERROR("Got unknown vCont-type packet");
return GDB_THREAD_PACKET_NOT_CONSUMED;
}
static int riscv_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, char **hex_reg_list)
{
LOG_DEBUG("Updating RISC-V regiser list for hart %d", (int)(thread_id - 1));
#if 0
LOG_ERROR(" Not actually updating");
*hex_reg_list = 0;
return JIM_OK;
#endif
size_t n_regs = 32;
size_t xlen = 64;
size_t reg_chars = xlen / 8 * 2;
ssize_t hex_reg_list_length = n_regs * reg_chars + 2;
*hex_reg_list = malloc(hex_reg_list_length);
*hex_reg_list[0] = '\0';
for (size_t i = 0; i < n_regs; ++i) {
if (riscv_has_register(rtos->target, thread_id, i)) {
uint64_t reg_value = riscv_get_register_on_hart(rtos->target, thread_id - 1, i);
for (size_t byte = 0; byte < xlen / 8; ++byte) {
uint8_t reg_byte = reg_value >> (byte * 8);
char hex[3] = {'x', 'x', 'x'};
snprintf(hex, 3, "%02x", reg_byte);
strncat(*hex_reg_list, hex, hex_reg_list_length);
}
} else {
for (size_t byte = 0; byte < xlen / 8; ++byte)
strncat(*hex_reg_list, "xx", hex_reg_list_length);
}
}
LOG_DEBUG(*hex_reg_list);
return JIM_OK;
}
static int riscv_get_symbol_list_to_lookup(symbol_table_elem_t *symbol_list[])
{
*symbol_list = calloc(1, sizeof(symbol_table_elem_t));
(*symbol_list)[0].symbol_name = NULL;
(*symbol_list)[0].optional = false;
return JIM_OK;
}
const struct rtos_type riscv_rtos =
{
.name = "riscv",
.detect_rtos = riscv_detect_rtos,
.create = riscv_create_rtos,
.update_threads = riscv_update_threads,
.get_thread_reg_list = riscv_get_thread_reg_list,
.get_symbol_list_to_lookup = riscv_get_symbol_list_to_lookup,
};

9
src/rtos/riscv_debug.h Normal file
View File

@ -0,0 +1,9 @@
#ifndef RTOS__RISCV_H
#define RTOS__RISCV_H
struct riscv_rtos {
/* The index into the thread list used to handle */
int qs_thread_info_offset;
};
#endif

View File

@ -34,6 +34,7 @@ extern struct rtos_type Linux_os;
extern struct rtos_type ChibiOS_rtos;
extern struct rtos_type embKernel_rtos;
extern struct rtos_type mqx_rtos;
extern struct rtos_type riscv_rtos;
static struct rtos_type *rtos_types[] = {
&ThreadX_rtos,
@ -43,6 +44,7 @@ static struct rtos_type *rtos_types[] = {
&ChibiOS_rtos,
&embKernel_rtos,
&mqx_rtos,
&riscv_rtos,
NULL
};
@ -70,6 +72,7 @@ static int os_alloc(struct target *target, struct rtos_type *ostype)
/* RTOS drivers can override the packet handler in _create(). */
os->gdb_thread_packet = rtos_thread_packet;
os->gdb_v_packet = NULL;
return JIM_OK;
}
@ -418,7 +421,7 @@ int rtos_get_gdb_reg_list(struct connection *connection)
(target->smp))) { /* in smp several current thread are possible */
char *hex_reg_list;
LOG_DEBUG("RTOS: getting register list for thread 0x%" PRIx64
LOG_INFO("RTOS: getting register list for thread 0x%" PRIx64
", target->rtos->current_thread=0x%" PRIx64 "\r\n",
current_threadid,
target->rtos->current_thread);

View File

@ -54,6 +54,7 @@ struct rtos {
struct thread_detail *thread_details;
int thread_count;
int (*gdb_thread_packet)(struct connection *connection, char const *packet, int packet_size);
int (*gdb_v_packet)(struct connection *connection, char const *packet, int packet_size);
void *rtos_specific_params;
};

View File

@ -2445,6 +2445,13 @@ static int gdb_v_packet(struct connection *connection,
struct gdb_service *gdb_service = connection->service->priv;
int result;
struct target *target = get_target_from_connection(connection);
if (target->rtos != NULL && target->rtos->gdb_v_packet != NULL) {
int out = target->rtos->gdb_v_packet(connection, packet, packet_size);
if (out != GDB_THREAD_PACKET_NOT_CONSUMED)
return out;
}
/* if flash programming disabled - send a empty reply */
if (gdb_flash_program == 0) {
@ -2643,7 +2650,7 @@ static void gdb_log_callback(void *priv, const char *file, unsigned line,
gdb_output_con(connection, string);
}
static void gdb_sig_halted(struct connection *connection)
void gdb_sig_halted(struct connection *connection)
{
char sig_reply[4];
snprintf(sig_reply, 4, "T%2.2x", 2);
@ -3195,3 +3202,9 @@ int gdb_register_commands(struct command_context *cmd_ctx)
gdb_port_next = strdup("3333");
return register_commands(cmd_ctx, NULL, gdb_command_handlers);
}
void gdb_set_frontend_state_running(struct connection *connection)
{
struct gdb_connection *gdb_con = connection->priv;
gdb_con->frontend_state = TARGET_RUNNING;
}

View File

@ -45,6 +45,9 @@ static inline struct target *get_target_from_connection(struct connection *conne
return gdb_service->target;
}
void gdb_set_frontend_state_running(struct connection *connection);
void gdb_sig_halted(struct connection *connection);
#define ERROR_GDB_BUFFER_TOO_SMALL (-800)
#define ERROR_GDB_TIMEOUT (-801)

View File

@ -138,7 +138,8 @@ INTEL_IA32_SRC = \
RISCV_SRC = \
riscv/riscv-011.c \
riscv/riscv-013.c \
riscv/riscv.c
riscv/riscv.c \
riscv/program.c
noinst_HEADERS = \
algorithm.h \

36
src/target/riscv/asm.h Normal file
View File

@ -0,0 +1,36 @@
#ifndef TARGET__RISCV__ASM_H
#define TARGET__RISCV__ASM_H
#include "riscv.h"
/*** Version-independent functions that we don't want in the main address space. ***/
static uint32_t load(const struct target *target, unsigned int rd,
unsigned int base, uint16_t offset) __attribute__ ((unused));
static uint32_t load(const struct target *target, unsigned int rd,
unsigned int base, uint16_t offset)
{
switch (riscv_xlen(target)) {
case 32:
return lw(rd, base, offset);
case 64:
return ld(rd, base, offset);
}
assert(0);
}
static uint32_t store(const struct target *target, unsigned int src,
unsigned int base, uint16_t offset) __attribute__ ((unused));
static uint32_t store(const struct target *target, unsigned int src,
unsigned int base, uint16_t offset)
{
switch (riscv_xlen(target)) {
case 32:
return sw(src, base, offset);
case 64:
return sd(src, base, offset);
}
assert(0);
}
#endif

View File

@ -26,15 +26,28 @@
#define DTM_IDCODE_1 (0x1 << DTM_IDCODE_1_OFFSET)
#define DTM_DTMCS 0x10
/*
* Writing 1 to this bit resets the DMI controller, clearing any
* sticky error state.
* Writing 1 to this bit does a hard reset of the DTM,
* causing the DTM to forget about any outstanding DMI transactions.
* In general this should only be used when the Debugger has
* reason to expect that the outstanding DMI transaction will never
* complete (e.g. a reset condition caused an inflight DMI transaction to
* be cancelled).
*/
#define DTM_DTMCS_DMIHARDRESET_OFFSET 17
#define DTM_DTMCS_DMIHARDRESET_LENGTH 1
#define DTM_DTMCS_DMIHARDRESET (0x1 << DTM_DTMCS_DMIHARDRESET_OFFSET)
/*
* Writing 1 to this bit clears the sticky error state
* and allows the DTM to retry or complete the previous
* transaction.
*/
#define DTM_DTMCS_DMIRESET_OFFSET 16
#define DTM_DTMCS_DMIRESET_LENGTH 1
#define DTM_DTMCS_DMIRESET (0x1 << DTM_DTMCS_DMIRESET_OFFSET)
/*
* This is the minimum number of cycles a debugger should spend in
* Run-Test/Idle after every DMI scan to avoid a 'busy'
* This is a hint to the debugger of the minimum number of
* cycles a debugger should spend in
* Run-Test/Idle after every DMI scan to avoid a `busy'
* return code (\Fdmistat of 3). A debugger must still
* check \Fdmistat when necessary.
*
@ -146,26 +159,26 @@
#define CSR_DCSR_XDEBUGVER_LENGTH 2
#define CSR_DCSR_XDEBUGVER (0x3 << CSR_DCSR_XDEBUGVER_OFFSET)
/*
* When 1, {\tt ebreak} instructions in Machine Mode enter Halt Mode.
* When 1, {\tt ebreak} instructions in Machine Mode enter Debug Mode.
*/
#define CSR_DCSR_EBREAKM_OFFSET 15
#define CSR_DCSR_EBREAKM_LENGTH 1
#define CSR_DCSR_EBREAKM (0x1 << CSR_DCSR_EBREAKM_OFFSET)
/*
* When 1, {\tt ebreak} instructions in Hypervisor Mode enter Halt Mode.
* When 1, {\tt ebreak} instructions in Hypervisor Mode enter Debug Mode.
*/
#define CSR_DCSR_EBREAKH_OFFSET 14
#define CSR_DCSR_EBREAKH_LENGTH 1
#define CSR_DCSR_EBREAKH (0x1 << CSR_DCSR_EBREAKH_OFFSET)
/*
* When 1, {\tt ebreak} instructions in Supervisor Mode enter Halt Mode.
* When 1, {\tt ebreak} instructions in Supervisor Mode enter Debug Mode.
*/
#define CSR_DCSR_EBREAKS_OFFSET 13
#define CSR_DCSR_EBREAKS_LENGTH 1
#define CSR_DCSR_EBREAKS (0x1 << CSR_DCSR_EBREAKS_OFFSET)
/*
* When 1, {\tt ebreak} instructions in User/Application Mode enter
* Halt Mode.
* Debug Mode.
*/
#define CSR_DCSR_EBREAKU_OFFSET 12
#define CSR_DCSR_EBREAKU_LENGTH 1
@ -173,7 +186,7 @@
/*
* 0: Increment counters as usual.
*
* 1: Don't increment any counters while in Halt Mode. This includes
* 1: Don't increment any counters while in Debug Mode. This includes
* the {\tt cycle} and {\tt instret} CSRs. This is preferred for most
* debugging scenarios.
*
@ -187,7 +200,7 @@
/*
* 0: Increment timers as usual.
*
* 1: Don't increment any hart-local timers while in Halt Mode.
* 1: Don't increment any hart-local timers while in Debug Mode.
*
* An implementation may choose not to support writing to this bit.
* The debugger must read back the value it writes to check whether
@ -197,29 +210,27 @@
#define CSR_DCSR_STOPTIME_LENGTH 1
#define CSR_DCSR_STOPTIME (0x1 << CSR_DCSR_STOPTIME_OFFSET)
/*
* Explains why Halt Mode was entered.
* Explains why Debug Mode was entered.
*
* When there are multiple reasons to enter Halt Mode in a single
* When there are multiple reasons to enter Debug Mode in a single
* cycle, the cause with the highest priority is the one written.
*
* 1: A software breakpoint was hit. (priority 3)
* 1: An {\tt ebreak} instruction was executed. (priority 3)
*
* 2: The Trigger Module caused a halt. (priority 4)
*
* 3: The debug interrupt was asserted by the Debug Module. (priority 2)
* 3: \Fhaltreq was set. (priority 2)
*
* 4: The hart single stepped because \Fstep was set. (priority 1)
*
* 5: \Fhaltreq was set. (priority 0)
*
* Other values are reserved for future use.
*/
#define CSR_DCSR_CAUSE_OFFSET 6
#define CSR_DCSR_CAUSE_LENGTH 3
#define CSR_DCSR_CAUSE (0x7 << CSR_DCSR_CAUSE_OFFSET)
/*
* When set and not in Halt Mode, the hart will only execute a single
* instruction, and then enter Halt Mode. Interrupts are disabled
* When set and not in Debug Mode, the hart will only execute a single
* instruction, and then enter Debug Mode. Interrupts are disabled
* when this bit is set.
*/
#define CSR_DCSR_STEP_OFFSET 2
@ -227,9 +238,9 @@
#define CSR_DCSR_STEP (0x1 << CSR_DCSR_STEP_OFFSET)
/*
* Contains the privilege level the hart was operating in when Debug
* Mode was entered. The encoding is describe in Table
* Mode was entered. The encoding is described in Table
* \ref{tab:privlevel}. A debugger can change this value to change
* the hart's privilege level when exiting Halt Mode.
* the hart's privilege level when exiting Debug Mode.
*
* Not all privilege levels are supported on all harts. If the
* encoding written is not supported or the debugger is not allowed to
@ -247,9 +258,9 @@
#define CSR_PRIV virtual
/*
* Contains the privilege level the hart was operating in when Debug
* Mode was entered. The encoding is describe in Table
* Mode was entered. The encoding is described in Table
* \ref{tab:privlevel}. A user can write this value to change the
* hart's privilege level when exiting Halt Mode.
* hart's privilege level when exiting Debug Mode.
*/
#define CSR_PRIV_PRV_OFFSET 0
#define CSR_PRIV_PRV_LENGTH 2
@ -283,10 +294,10 @@
* 0: Both Debug and M Mode can write the {\tt tdata} registers at the
* selected \Rtselect.
*
* 1: Only Halt Mode can write the {\tt tdata} registers at the
* 1: Only Debug Mode can write the {\tt tdata} registers at the
* selected \Rtselect. Writes from other modes are ignored.
*
* This bit is only writable from Halt Mode.
* This bit is only writable from Debug Mode.
*/
#define CSR_TDATA1_HMODE_OFFSET XLEN-5
#define CSR_TDATA1_HMODE_LENGTH 1
@ -366,7 +377,7 @@
* 0: Raise a breakpoint exception. (Used when software wants to use
* the trigger module without an external debugger attached.)
*
* 1: Enter Halt Mode. (Only supported when \Fhmode is 1.)
* 1: Enter Debug Mode. (Only supported when \Fhmode is 1.)
*
* 2: Start tracing.
*
@ -504,10 +515,10 @@
/*
* Determines what happens when this trigger matches.
*
* 0: Raise a debug exception. (Used when software wants to use the
* 0: Raise a breakpoint exception. (Used when software wants to use the
* trigger module without an external debugger attached.)
*
* 1: Enter Halt Mode. (Only supported when \Fhmode is 1.)
* 1: Enter Debug Mode. (Only supported when \Fhmode is 1.)
*
* 2: Start tracing.
*
@ -524,6 +535,18 @@
#define CSR_ICOUNT_ACTION (0x3fL << CSR_ICOUNT_ACTION_OFFSET)
#define DMI_DMSTATUS 0x11
/*
* This field is 1 when all currently selected harts have acknowledged the previous \Fresumereq.
*/
#define DMI_DMSTATUS_ALLRESUMEACK_OFFSET 17
#define DMI_DMSTATUS_ALLRESUMEACK_LENGTH 1
#define DMI_DMSTATUS_ALLRESUMEACK (0x1 << DMI_DMSTATUS_ALLRESUMEACK_OFFSET)
/*
* This field is 1 when any currently selected hart has acknowledged the previous \Fresumereq.
*/
#define DMI_DMSTATUS_ANYRESUMEACK_OFFSET 16
#define DMI_DMSTATUS_ANYRESUMEACK_LENGTH 1
#define DMI_DMSTATUS_ANYRESUMEACK (0x1 << DMI_DMSTATUS_ANYRESUMEACK_OFFSET)
/*
* This field is 1 when all currently selected harts do not exist in this system.
*/
#define DMI_DMSTATUS_ALLNONEXISTENT_OFFSET 15
@ -617,7 +640,7 @@
#define DMI_DMSTATUS_VERSIONLO (0x3 << DMI_DMSTATUS_VERSIONLO_OFFSET)
#define DMI_DMCONTROL 0x10
/*
* Halt request signal for all currently selected harts. When 1, the
* Halt request signal for all currently selected harts. When set to 1, the
* hart will halt if it is not currently halted.
* Setting both \Fhaltreq and \Fresumereq leads to undefined behavior.
*
@ -627,7 +650,7 @@
#define DMI_DMCONTROL_HALTREQ_LENGTH 1
#define DMI_DMCONTROL_HALTREQ (0x1 << DMI_DMCONTROL_HALTREQ_OFFSET)
/*
* Resume request signal for all currently selected harts. When 1,
* Resume request signal for all currently selected harts. When set to 1,
* the hart will resume if it is currently halted.
* Setting both \Fhaltreq and \Fresumereq leads to undefined behavior.
*
@ -942,31 +965,12 @@
#define DMI_DATA0_DATA_OFFSET 0
#define DMI_DATA0_DATA_LENGTH 32
#define DMI_DATA0_DATA (0xffffffff << DMI_DATA0_DATA_OFFSET)
#define DMI_DATA1 0x05
#define DMI_DATA2 0x06
#define DMI_DATA3 0x07
#define DMI_DATA4 0x08
#define DMI_DATA5 0x09
#define DMI_DATA6 0x0a
#define DMI_DATA7 0x0b
#define DMI_DATA8 0x0c
#define DMI_DATA9 0x0d
#define DMI_DATA10 0x0e
#define DMI_DATA11 0x0f
#define DMI_PROGBUF0 0x20
#define DMI_PROGBUF0_DATA_OFFSET 0
#define DMI_PROGBUF0_DATA_LENGTH 32
#define DMI_PROGBUF0_DATA (0xffffffff << DMI_PROGBUF0_DATA_OFFSET)
#define DMI_PROGBUF1 0x21
#define DMI_PROGBUF2 0x22
#define DMI_PROGBUF3 0x23
#define DMI_PROGBUF4 0x24
#define DMI_PROGBUF5 0x25
#define DMI_PROGBUF6 0x26
#define DMI_PROGBUF7 0x27
#define DMI_PROGBUF8 0x28
#define DMI_PROGBUF9 0x29
#define DMI_PROGBUF10 0x2a
#define DMI_PROGBUF15 0x2f
#define DMI_AUTHDATA 0x30
#define DMI_AUTHDATA_DATA_OFFSET 0
#define DMI_AUTHDATA_DATA_LENGTH 32
@ -1233,93 +1237,6 @@
#define DMI_SBDATA3_DATA_OFFSET 0
#define DMI_SBDATA3_DATA_LENGTH 32
#define DMI_SBDATA3_DATA (0xffffffff << DMI_SBDATA3_DATA_OFFSET)
#define SERINFO 0x280
/*
* Like \Fserialzero.
*/
#define SERINFO_SERIAL7_OFFSET 7
#define SERINFO_SERIAL7_LENGTH 1
#define SERINFO_SERIAL7 (0x1 << SERINFO_SERIAL7_OFFSET)
/*
* Like \Fserialzero.
*/
#define SERINFO_SERIAL6_OFFSET 6
#define SERINFO_SERIAL6_LENGTH 1
#define SERINFO_SERIAL6 (0x1 << SERINFO_SERIAL6_OFFSET)
/*
* Like \Fserialzero.
*/
#define SERINFO_SERIAL5_OFFSET 5
#define SERINFO_SERIAL5_LENGTH 1
#define SERINFO_SERIAL5 (0x1 << SERINFO_SERIAL5_OFFSET)
/*
* Like \Fserialzero.
*/
#define SERINFO_SERIAL4_OFFSET 4
#define SERINFO_SERIAL4_LENGTH 1
#define SERINFO_SERIAL4 (0x1 << SERINFO_SERIAL4_OFFSET)
/*
* Like \Fserialzero.
*/
#define SERINFO_SERIAL3_OFFSET 3
#define SERINFO_SERIAL3_LENGTH 1
#define SERINFO_SERIAL3 (0x1 << SERINFO_SERIAL3_OFFSET)
/*
* Like \Fserialzero.
*/
#define SERINFO_SERIAL2_OFFSET 2
#define SERINFO_SERIAL2_LENGTH 1
#define SERINFO_SERIAL2 (0x1 << SERINFO_SERIAL2_OFFSET)
/*
* Like \Fserialzero.
*/
#define SERINFO_SERIAL1_OFFSET 1
#define SERINFO_SERIAL1_LENGTH 1
#define SERINFO_SERIAL1 (0x1 << SERINFO_SERIAL1_OFFSET)
/*
* 1 means serial interface 0 is supported.
*/
#define SERINFO_SERIAL0_OFFSET 0
#define SERINFO_SERIAL0_LENGTH 1
#define SERINFO_SERIAL0 (0x1 << SERINFO_SERIAL0_OFFSET)
#define SERSEND0 0x200
#define SERRECV0 0x204
#define SERSTAT0 0x208
/*
* Send ready. 1 when the core-to-debugger queue is not full. 0
* otherwise.
*/
#define SERSTAT0_SENDR_OFFSET 1
#define SERSTAT0_SENDR_LENGTH 1
#define SERSTAT0_SENDR (0x1 << SERSTAT0_SENDR_OFFSET)
/*
* Receive ready. 1 when the debugger-to-core queue is not empty. 0
* otherwise.
*/
#define SERSTAT0_RECVR_OFFSET 0
#define SERSTAT0_RECVR_LENGTH 1
#define SERSTAT0_RECVR (0x1 << SERSTAT0_RECVR_OFFSET)
#define SERSEND1 0x210
#define SERRECV1 0x214
#define SERSTAT1 0x218
#define SERSEND2 0x220
#define SERRECV2 0x224
#define SERSTAT2 0x228
#define SERSEND3 0x230
#define SERRECV3 0x234
#define SERSTAT3 0x238
#define SERSEND4 0x240
#define SERRECV4 0x244
#define SERSTAT4 0x248
#define SERSEND5 0x250
#define SERRECV5 0x254
#define SERSTAT5 0x258
#define SERSEND6 0x260
#define SERRECV6 0x264
#define SERSTAT6 0x268
#define SERSEND7 0x274
#define SERRECV7 0x278
#define SERSTAT7 0x27c
#define TRACE 0x728
/*
* 1 if the trace buffer has wrapped since the last time \Fdiscard was
@ -1454,14 +1371,6 @@
#define AC_ACCESS_REGISTER_SIZE (0x7 << AC_ACCESS_REGISTER_SIZE_OFFSET)
/*
* When 1, execute the program in the Program Buffer exactly once
* before performing the transfer.
* \textbf{WARNING: preexec is considered for removal.}
*/
#define AC_ACCESS_REGISTER_PREEXEC_OFFSET 19
#define AC_ACCESS_REGISTER_PREEXEC_LENGTH 1
#define AC_ACCESS_REGISTER_PREEXEC (0x1 << AC_ACCESS_REGISTER_PREEXEC_OFFSET)
/*
* When 1, execute the program in the Program Buffer exactly once
* after performing the transfer, if any.
*/
#define AC_ACCESS_REGISTER_POSTEXEC_OFFSET 18

View File

@ -0,0 +1,28 @@
#ifndef TARGET__RISCV__GDB_REGS_H
#define TARGET__RISCV__GDB_REGS_H
enum gdb_regno {
GDB_REGNO_XPR0 = 0,
GDB_REGNO_X0 = GDB_REGNO_XPR0 + 0,
GDB_REGNO_ZERO = GDB_REGNO_XPR0 + 0,
GDB_REGNO_S0 = GDB_REGNO_XPR0 + 8,
GDB_REGNO_S1 = GDB_REGNO_XPR0 + 9,
GDB_REGNO_XPR31 = GDB_REGNO_XPR0 + 31,
GDB_REGNO_PC = 32,
GDB_REGNO_FPR0 = 33,
GDB_REGNO_FPR31 = GDB_REGNO_FPR0 + 31,
GDB_REGNO_CSR0 = 65,
GDB_REGNO_TSELECT = CSR_TSELECT + GDB_REGNO_CSR0,
GDB_REGNO_TDATA1 = CSR_TDATA1 + GDB_REGNO_CSR0,
GDB_REGNO_TDATA2 = CSR_TDATA2 + GDB_REGNO_CSR0,
GDB_REGNO_MISA = CSR_MISA + GDB_REGNO_CSR0,
GDB_REGNO_DPC = CSR_DPC + GDB_REGNO_CSR0,
GDB_REGNO_DCSR = CSR_DCSR + GDB_REGNO_CSR0,
GDB_REGNO_DSCRATCH = CSR_DSCRATCH + GDB_REGNO_CSR0,
GDB_REGNO_MSTATUS = CSR_MSTATUS + GDB_REGNO_CSR0,
GDB_REGNO_CSR4095 = GDB_REGNO_CSR0 + 4095,
GDB_REGNO_PRIV = 4161,
GDB_REGNO_COUNT
};
#endif

View File

@ -125,6 +125,16 @@ static uint32_t csrr(unsigned int rd, unsigned int csr) {
return (csr << 20) | (rd << 7) | MATCH_CSRRS;
}
static uint32_t csrrs(unsigned int rd, unsigned int rs, unsigned int csr) __attribute__ ((unused));
static uint32_t csrrs(unsigned int rd, unsigned int rs, unsigned int csr) {
return (csr << 20) | (rs << 15) | (rd << 7) | MATCH_CSRRS;
}
static uint32_t csrrw(unsigned int rd, unsigned int rs, unsigned int csr) __attribute__ ((unused));
static uint32_t csrrw(unsigned int rd, unsigned int rs, unsigned int csr) {
return (csr << 20) | (rs << 15) | (rd << 7) | MATCH_CSRRW;
}
static uint32_t fsw(unsigned int src, unsigned int base, uint16_t offset) __attribute__ ((unused));
static uint32_t fsw(unsigned int src, unsigned int base, uint16_t offset)
{
@ -206,7 +216,6 @@ static uint32_t fence_i(void)
return MATCH_FENCE_I;
}
/*
static uint32_t lui(unsigned int dest, uint32_t imm) __attribute__ ((unused));
static uint32_t lui(unsigned int dest, uint32_t imm)
{
@ -215,6 +224,7 @@ static uint32_t lui(unsigned int dest, uint32_t imm)
MATCH_LUI;
}
/*
static uint32_t csrci(unsigned int csr, uint16_t imm) __attribute__ ((unused));
static uint32_t csrci(unsigned int csr, uint16_t imm) {
return (csr << 20) |
@ -271,3 +281,15 @@ static uint32_t srli(unsigned int dest, unsigned int src, uint8_t shamt)
(dest << 7) |
MATCH_SRLI;
}
static uint32_t fence(void) __attribute__((unused));
static uint32_t fence(void)
{
return MATCH_FENCE;
}
static uint32_t auipc(unsigned int dest) __attribute__((unused));
static uint32_t auipc(unsigned int dest)
{
return MATCH_AUIPC | (dest << 7);
}

459
src/target/riscv/program.c Normal file
View File

@ -0,0 +1,459 @@
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "target/target.h"
#include "riscv.h"
#include "program.h"
#include "helper/log.h"
#include "asm.h"
#include "encoding.h"
riscv_addr_t riscv_program_gah(struct riscv_program *p, riscv_addr_t addr);
riscv_addr_t riscv_program_gal(struct riscv_program *p, riscv_addr_t addr);
int riscv_program_lah(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr);
int riscv_program_lal(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr);
/* Program interface. */
int riscv_program_init(struct riscv_program *p, struct target *target)
{
LOG_DEBUG("riscv_program_init: p=0x%016lx", p);
memset(p, 0, sizeof(*p));
p->target = target;
p->instruction_count = 0;
p->data_count = 0;
p->writes_memory = 0;
p->target_xlen = riscv_xlen(target);
for (size_t i = 0; i < RISCV_REGISTER_COUNT; ++i) {
p->writes_xreg[i] = 0;
p->in_use[i] = 0;
}
for(size_t i = 0; i < RISCV_MAX_DEBUG_BUFFER_SIZE; ++i)
p->debug_buffer[i] = -1;
if (riscv_debug_buffer_enter(target, p) != ERROR_OK) {
LOG_ERROR("unable to write progam buffer enter code");
return ERROR_FAIL;
}
return ERROR_OK;
}
int riscv_program_exec(struct riscv_program *p, struct target *t)
{
if (riscv_debug_buffer_leave(t, p) != ERROR_OK) {
LOG_ERROR("unable to write program buffer exit code");
return ERROR_FAIL;
}
riscv_reg_t saved_registers[GDB_REGNO_XPR31 + 1];
for (size_t i = GDB_REGNO_XPR0 + 1; i <= GDB_REGNO_XPR31; ++i) {
if (p->writes_xreg[i]) {
LOG_DEBUG("Saving register %d as used by program", i);
saved_registers[i] = riscv_get_register(t, i);
}
}
if (p->writes_memory && (riscv_program_fence(p) != ERROR_OK)) {
LOG_ERROR("Unable to write fence");
for(size_t i = 0; i < riscv_debug_buffer_size(p->target); ++i)
LOG_ERROR("ram[%02x]: DASM(0x%08lx) [0x%08lx]", i, p->debug_buffer[i], p->debug_buffer[i]);
abort();
return ERROR_FAIL;
}
if (riscv_program_ebreak(p) != ERROR_OK) {
LOG_ERROR("Unable to write ebreak");
for(size_t i = 0; i < riscv_debug_buffer_size(p->target); ++i)
LOG_ERROR("ram[%02x]: DASM(0x%08lx) [0x%08lx]", i, p->debug_buffer[i], p->debug_buffer[i]);
abort();
return ERROR_FAIL;
}
for (size_t i = 0; i < riscv_debug_buffer_size(p->target); ++i) {
LOG_DEBUG("Executing program 0x%016lx: debug_buffer[%02x] = DASM(0x%08lx)", p, i, p->debug_buffer[i]);
riscv_write_debug_buffer(t, i, p->debug_buffer[i]);
}
riscv_execute_debug_buffer(t);
for (size_t i = 0; i < riscv_debug_buffer_size(p->target); ++i)
p->debug_buffer[i] = riscv_read_debug_buffer(t, i);
for (size_t i = GDB_REGNO_XPR0; i <= GDB_REGNO_XPR31; ++i)
if (p->writes_xreg[i])
riscv_set_register(t, i, saved_registers[i]);
return ERROR_OK;
}
riscv_addr_t riscv_program_alloc_data(struct riscv_program *p, size_t bytes)
{
LOG_DEBUG("allocating %d bytes of data", bytes);
riscv_addr_t addr =
riscv_debug_buffer_addr(p->target)
+ riscv_debug_buffer_size(p->target) * sizeof(p->debug_buffer[0])
- p->data_count * sizeof(p->debug_buffer[0])
- bytes;
while (addr % bytes != 0) addr--;
riscv_addr_t ptop =
riscv_debug_buffer_addr(p->target)
+ p->instruction_count * sizeof(p->debug_buffer[0]);
if (addr <= ptop) {
LOG_DEBUG("unable to allocate %d bytes", bytes);
return RISCV_PROGRAM_ALLOC_FAIL;
}
LOG_DEBUG("allocated %d bytes at 0x%08lx", bytes, addr);
p->data_count =
+ riscv_debug_buffer_size(p->target)
- (addr - riscv_debug_buffer_addr(p->target)) / sizeof(p->debug_buffer[0]);
return addr;
}
riscv_addr_t riscv_program_alloc_x(struct riscv_program *p)
{
return riscv_program_alloc_data(p, p->target_xlen / 8);
}
riscv_addr_t riscv_program_alloc_d(struct riscv_program *p)
{
return riscv_program_alloc_data(p, 8);
}
riscv_addr_t riscv_program_alloc_w(struct riscv_program *p)
{
return riscv_program_alloc_data(p, 4);
}
riscv_addr_t riscv_program_alloc_h(struct riscv_program *p)
{
return riscv_program_alloc_data(p, 2);
}
riscv_addr_t riscv_program_alloc_b(struct riscv_program *p)
{
return riscv_program_alloc_data(p, 1);
}
riscv_insn_t riscv_program_read_ram(struct riscv_program *p, riscv_addr_t addr)
{
if (addr < riscv_debug_buffer_addr(p->target))
return -1;
if (addr > riscv_debug_buffer_addr(p->target) + (riscv_debug_buffer_size(p->target) * sizeof(p->debug_buffer[0])))
return -1;
int off = (addr - riscv_debug_buffer_addr(p->target)) / sizeof(p->debug_buffer[0]);
return p->debug_buffer[off];
}
void riscv_program_write_ram(struct riscv_program *p, riscv_addr_t addr, uint64_t d)
{
if (addr < riscv_debug_buffer_addr(p->target))
return;
if (addr > riscv_debug_buffer_addr(p->target) + (riscv_debug_buffer_size(p->target) * sizeof(p->debug_buffer[0])))
return;
int off = (addr - riscv_debug_buffer_addr(p->target)) / sizeof(p->debug_buffer[0]);
p->debug_buffer[off] = d;
}
int riscv_program_swr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int offset)
{
p->writes_memory = 1;
return riscv_program_insert(p, sw(d, b, offset));
}
int riscv_program_shr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int offset)
{
p->writes_memory = 1;
return riscv_program_insert(p, sh(d, b, offset));
}
int riscv_program_sbr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int offset)
{
p->writes_memory = 1;
return riscv_program_insert(p, sb(d, b, offset));
}
int riscv_program_lwr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int offset)
{
p->writes_memory = 1;
return riscv_program_insert(p, lw(d, b, offset));
}
int riscv_program_lhr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int offset)
{
p->writes_memory = 1;
return riscv_program_insert(p, lh(d, b, offset));
}
int riscv_program_lbr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int offset)
{
p->writes_memory = 1;
return riscv_program_insert(p, lb(d, b, offset));
}
int riscv_program_lx(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr)
{
switch (p->target_xlen) {
case 64: return riscv_program_ld(p, d, addr);
case 32: return riscv_program_lw(p, d, addr);
}
LOG_ERROR("unknown xlen %d", p->target_xlen);
abort();
return -1;
}
int riscv_program_ld(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr)
{
enum gdb_regno t = riscv_program_gah(p, addr) == 0 ? GDB_REGNO_X0 : d;
if (riscv_program_lah(p, d, addr) != ERROR_OK)
return ERROR_FAIL;
if (riscv_program_insert(p, ld(d, t, riscv_program_gal(p, addr))) != ERROR_OK)
return ERROR_FAIL;
return ERROR_OK;
}
int riscv_program_lw(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr)
{
enum gdb_regno t = riscv_program_gah(p, addr) == 0 ? GDB_REGNO_X0 : d;
if (riscv_program_lah(p, d, addr) != ERROR_OK)
return ERROR_FAIL;
if (riscv_program_insert(p, lw(d, t, riscv_program_gal(p, addr))) != ERROR_OK)
return ERROR_FAIL;
return ERROR_OK;
}
int riscv_program_lh(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr)
{
enum gdb_regno t = riscv_program_gah(p, addr) == 0 ? GDB_REGNO_X0 : d;
if (riscv_program_lah(p, d, addr) != ERROR_OK)
return ERROR_FAIL;
if (riscv_program_insert(p, lh(d, t, riscv_program_gal(p, addr))) != ERROR_OK)
return ERROR_FAIL;
return ERROR_OK;
}
int riscv_program_lb(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr)
{
enum gdb_regno t = riscv_program_gah(p, addr) == 0 ? GDB_REGNO_X0 : d;
if (riscv_program_lah(p, t, addr) != ERROR_OK)
return ERROR_FAIL;
if (riscv_program_insert(p, lb(d, t, riscv_program_gal(p, addr))) != ERROR_OK)
return ERROR_FAIL;
return ERROR_OK;
}
int riscv_program_sx(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr)
{
switch (p->target_xlen) {
case 64: return riscv_program_sd(p, d, addr);
case 32: return riscv_program_sw(p, d, addr);
}
LOG_ERROR("unknown xlen %d", p->target_xlen);
abort();
return -1;
}
int riscv_program_sd(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr)
{
enum gdb_regno t = riscv_program_gah(p, addr) == 0
? GDB_REGNO_X0
: riscv_program_gettemp(p);
if (riscv_program_lah(p, t, addr) != ERROR_OK)
return ERROR_FAIL;
if (riscv_program_insert(p, sd(d, t, riscv_program_gal(p, addr))) != ERROR_OK)
return ERROR_FAIL;
riscv_program_puttemp(p, t);
p->writes_memory = true;
return ERROR_OK;
}
int riscv_program_sw(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr)
{
enum gdb_regno t = riscv_program_gah(p, addr) == 0
? GDB_REGNO_X0
: riscv_program_gettemp(p);
if (riscv_program_lah(p, t, addr) != ERROR_OK)
return ERROR_FAIL;
if (riscv_program_insert(p, sw(d, t, riscv_program_gal(p, addr))) != ERROR_OK)
return ERROR_FAIL;
riscv_program_puttemp(p, t);
p->writes_memory = true;
return ERROR_OK;
}
int riscv_program_sh(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr)
{
enum gdb_regno t = riscv_program_gah(p, addr) == 0
? GDB_REGNO_X0
: riscv_program_gettemp(p);
if (riscv_program_lah(p, t, addr) != ERROR_OK)
return ERROR_FAIL;
if (riscv_program_insert(p, sh(d, t, riscv_program_gal(p, addr))) != ERROR_OK)
return ERROR_FAIL;
riscv_program_puttemp(p, t);
p->writes_memory = true;
return ERROR_OK;
}
int riscv_program_sb(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr)
{
enum gdb_regno t = riscv_program_gah(p, addr) == 0
? GDB_REGNO_X0
: riscv_program_gettemp(p);
if (riscv_program_lah(p, t, addr) != ERROR_OK)
return ERROR_FAIL;
if (riscv_program_insert(p, sb(d, t, riscv_program_gal(p, addr))) != ERROR_OK)
return ERROR_FAIL;
riscv_program_puttemp(p, t);
p->writes_memory = true;
return ERROR_OK;
}
int riscv_program_csrr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno csr)
{
assert(csr >= GDB_REGNO_CSR0);
return riscv_program_insert(p, csrrs(d, GDB_REGNO_X0, csr - GDB_REGNO_CSR0));
}
int riscv_program_csrw(struct riscv_program *p, enum gdb_regno s, enum gdb_regno csr)
{
assert(csr >= GDB_REGNO_CSR0);
return riscv_program_insert(p, csrrw(GDB_REGNO_X0, s, csr - GDB_REGNO_CSR0));
}
int riscv_program_csrrw(struct riscv_program *p, enum gdb_regno d, enum gdb_regno s, enum gdb_regno csr)
{
assert(csr >= GDB_REGNO_CSR0);
return riscv_program_insert(p, csrrw(d, s, csr - GDB_REGNO_CSR0));
}
int riscv_program_fence_i(struct riscv_program *p)
{
return riscv_program_insert(p, fence_i());
}
int riscv_program_fence(struct riscv_program *p)
{
return riscv_program_insert(p, fence());
}
int riscv_program_ebreak(struct riscv_program *p)
{
return riscv_program_insert(p, ebreak());
}
int riscv_program_lui(struct riscv_program *p, enum gdb_regno d, int32_t u)
{
return riscv_program_insert(p, lui(d, u));
}
int riscv_program_addi(struct riscv_program *p, enum gdb_regno d, enum gdb_regno s, int16_t u)
{
return riscv_program_insert(p, addi(d, s, u));
}
int riscv_program_li(struct riscv_program *p, enum gdb_regno d, riscv_reg_t c)
{
if (riscv_program_lui(p, d, c >> 12) != ERROR_OK)
return ERROR_FAIL;
if (riscv_program_addi(p, d, d, c & 0xFFF) != ERROR_OK)
return ERROR_FAIL;
return ERROR_OK;
}
int riscv_program_dont_restore_register(struct riscv_program *p, enum gdb_regno r)
{
assert(r < RISCV_REGISTER_COUNT);
p->writes_xreg[r] = 0;
return ERROR_OK;
}
int riscv_program_do_restore_register(struct riscv_program *p, enum gdb_regno r)
{
assert(r < RISCV_REGISTER_COUNT);
p->writes_xreg[r] = 1;
return ERROR_OK;
}
void riscv_program_reserve_register(struct riscv_program *p, enum gdb_regno r)
{
assert(r < RISCV_REGISTER_COUNT);
assert(p->in_use[r] == 0);
p->in_use[r] = 1;
}
enum gdb_regno riscv_program_gettemp(struct riscv_program *p)
{
for (size_t i = GDB_REGNO_S0; i <= GDB_REGNO_XPR31; ++i) {
if (p->in_use[i]) continue;
riscv_program_do_restore_register(p, i);
p->in_use[i] = 1;
return i;
}
LOG_ERROR("You've run out of temporary registers. This is impossible.");
abort();
}
void riscv_program_puttemp(struct riscv_program *p, enum gdb_regno r)
{
assert(r < RISCV_REGISTER_COUNT);
p->in_use[r] = 0;
}
/* Helper functions. */
riscv_addr_t riscv_program_gah(struct riscv_program *p, riscv_addr_t addr)
{
return addr >> 12;
}
riscv_addr_t riscv_program_gal(struct riscv_program *p, riscv_addr_t addr)
{
return ((addr > 0) ? 1 : 0) * (abs(addr) & 0x7FF);
}
int riscv_program_lah(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr)
{
riscv_addr_t ah = riscv_program_gah(p, addr);
if (ah == 0)
return ERROR_OK;
return riscv_program_lui(p, d, ah);
}
int riscv_program_lal(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr)
{
riscv_addr_t al = riscv_program_gal(p, addr);
if (al == 0)
return ERROR_OK;
return riscv_program_addi(p, d, d, al);
}
int riscv_program_insert(struct riscv_program *p, riscv_insn_t i)
{
LOG_DEBUG("instruction_count: %d (p=0x%016lx)", p->instruction_count, p);
if (p->instruction_count + p->data_count + 1 > riscv_debug_buffer_size(p->target)) {
LOG_DEBUG("Unable to insert instruction:");
LOG_DEBUG(" instruction_count=%d", p->instruction_count);
LOG_DEBUG(" data_count =%d", p->data_count);
LOG_DEBUG(" buffer size =%d", riscv_debug_buffer_size(p->target));
return ERROR_FAIL;
}
LOG_DEBUG("PROGBUF[%d] = DASM(0x%08x) [0x%08x]", p->instruction_count, i, i);
p->debug_buffer[p->instruction_count] = i;
p->instruction_count++;
return ERROR_OK;
}

135
src/target/riscv/program.h Normal file
View File

@ -0,0 +1,135 @@
#ifndef TARGET__RISCV__PROGRAM_H
#define TARGET__RISCV__PROGRAM_H
#include "riscv.h"
#define RISCV_MAX_DEBUG_BUFFER_SIZE 32
#define RISCV_REGISTER_COUNT 32
#define RISCV_DSCRATCH_COUNT 2
/* The various RISC-V debug specifications all revolve around setting up
* program buffers and executing them on the target. This structure contains a
* single program, which can then be executed on targets. */
struct riscv_program {
struct target *target;
uint32_t debug_buffer[RISCV_MAX_DEBUG_BUFFER_SIZE];
/* The debug buffer is allocated in two directions: instructions go at
* the start, while data goes at the end. When they meet in the middle
* this blows up. */
size_t instruction_count;
size_t data_count;
/* Side effects of executing this program. These must be accounted for
* in order to maintain correct executing of the target system. */
bool writes_xreg[RISCV_REGISTER_COUNT];
bool writes_memory;
/* When a register is used it will be set in this array. */
bool in_use[RISCV_REGISTER_COUNT];
/* Remembers the registers that have been saved into dscratch
* registers. These are restored */
enum gdb_regno dscratch_saved[RISCV_DSCRATCH_COUNT];
/* XLEN on the target. */
int target_xlen;
};
/* Initializes a program with the header. */
int riscv_program_init(struct riscv_program *p, struct target *t);
/* Executes a program, returning 0 if the program successfully executed. Note
* that this may cause registers to be saved or restored, which could result to
* calls to things like riscv_save_register which itself could require a
* program to execute. That's OK, just make sure this eventually terminates.
* */
int riscv_program_exec(struct riscv_program *p, struct target *t);
/* Clears a program, removing all the state associated with it. */
int riscv_program_clear(struct riscv_program *p, struct target *t);
/* A lower level interface, you shouldn't use this unless you have a reason. */
int riscv_program_insert(struct riscv_program *p, riscv_insn_t i);
/* There is hardware support for saving at least one register. This register
* doesn't need to be saved/restored the usual way, which is useful during
* early initialization when we can't save/restore arbitrary registerrs to host
* memory. */
int riscv_program_save_to_dscratch(struct riscv_program *p, enum gdb_regno to_save);
/* Allocates data of various sizes. Either returns the absolute physical
* address or RISCV_PROGRAM_ALLOC_FAIL on failure. */
riscv_addr_t riscv_program_alloc_data(struct riscv_program *p, size_t bytes);
riscv_addr_t riscv_program_alloc_x(struct riscv_program *p);
riscv_addr_t riscv_program_alloc_d(struct riscv_program *p);
riscv_addr_t riscv_program_alloc_w(struct riscv_program *p);
riscv_addr_t riscv_program_alloc_h(struct riscv_program *p);
riscv_addr_t riscv_program_alloc_b(struct riscv_program *p);
#define RISCV_PROGRAM_ALLOC_FAIL ((riscv_addr_t)(-1))
/* Reads a word of memory from this program's internal view of the debug RAM.
* This is what you want to use to get data back from the program after it
* executes. */
riscv_insn_t riscv_program_read_ram(struct riscv_program *p, riscv_addr_t addr);
void riscv_program_write_ram(struct riscv_program *p, riscv_addr_t a, uint64_t d);
/* Helpers to assembly various instructions. Return 0 on success. These might
* assembly into a multi-instruction sequence that overwrites some other
* register, but those will be properly saved and restored. */
int riscv_program_lx(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr);
int riscv_program_ld(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr);
int riscv_program_lw(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr);
int riscv_program_lh(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr);
int riscv_program_lb(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr);
int riscv_program_sx(struct riscv_program *p, enum gdb_regno s, riscv_addr_t addr);
int riscv_program_sd(struct riscv_program *p, enum gdb_regno s, riscv_addr_t addr);
int riscv_program_sw(struct riscv_program *p, enum gdb_regno s, riscv_addr_t addr);
int riscv_program_sh(struct riscv_program *p, enum gdb_regno s, riscv_addr_t addr);
int riscv_program_sb(struct riscv_program *p, enum gdb_regno s, riscv_addr_t addr);
int riscv_program_lxr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno a, int o);
int riscv_program_ldr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno a, int o);
int riscv_program_lwr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno a, int o);
int riscv_program_lhr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno a, int o);
int riscv_program_lbr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno a, int o);
int riscv_program_sxr(struct riscv_program *p, enum gdb_regno s, enum gdb_regno a, int o);
int riscv_program_sdr(struct riscv_program *p, enum gdb_regno s, enum gdb_regno a, int o);
int riscv_program_swr(struct riscv_program *p, enum gdb_regno s, enum gdb_regno a, int o);
int riscv_program_shr(struct riscv_program *p, enum gdb_regno s, enum gdb_regno a, int o);
int riscv_program_sbr(struct riscv_program *p, enum gdb_regno s, enum gdb_regno a, int o);
int riscv_program_csrr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno csr);
int riscv_program_csrw(struct riscv_program *p, enum gdb_regno s, enum gdb_regno csr);
int riscv_program_csrrw(struct riscv_program *p, enum gdb_regno d, enum gdb_regno s, enum gdb_regno csr);
int riscv_program_fence_i(struct riscv_program *p);
int riscv_program_fence(struct riscv_program *p);
int riscv_program_ebreak(struct riscv_program *p);
int riscv_program_lui(struct riscv_program *p, enum gdb_regno d, int32_t u);
int riscv_program_addi(struct riscv_program *p, enum gdb_regno d, enum gdb_regno s, int16_t i);
/* Assembler macros. */
int riscv_program_li(struct riscv_program *p, enum gdb_regno d, riscv_reg_t c);
int riscv_program_la(struct riscv_program *p, enum gdb_regno d, riscv_addr_t a);
/* Register allocation. The user is expected to have obtained temporary
* registers using these fuctions. Additionally, there is an interface for
* reserving registers -- it's expected that this has been called as the first
* thing in the program's execution to reserve registers that can't be touched
* by the program's execution. */
void riscv_program_reserve_register(struct riscv_program *p, enum gdb_regno r);
enum gdb_regno riscv_program_gettemp(struct riscv_program *p);
void riscv_program_puttemp(struct riscv_program *p, enum gdb_regno r);
/* Executing a program usually causes the registers that get overwritten to be
* saved and restored. Calling this prevents the given register from actually
* being restored as a result of all activity in this program. */
int riscv_program_dont_restore_register(struct riscv_program *p, enum gdb_regno r);
int riscv_program_do_restore_register(struct riscv_program *p, enum gdb_regno r);
#endif

View File

@ -20,6 +20,7 @@
#include "breakpoints.h"
#include "helper/time_support.h"
#include "riscv.h"
#include "asm.h"
/**
* Since almost everything can be accomplish by scanning the dbus register, all
@ -260,7 +261,7 @@ static riscv011_info_t *get_info(const struct target *target)
static unsigned int slot_offset(const struct target *target, slot_t slot)
{
riscv011_info_t *info = get_info(target);
switch (xlen(target)) {
switch (riscv_xlen(target)) {
case 32:
switch (slot) {
case SLOT0: return 4;
@ -275,7 +276,7 @@ static unsigned int slot_offset(const struct target *target, slot_t slot)
}
}
LOG_ERROR("slot_offset called with xlen=%d, slot=%d",
xlen(target), slot);
riscv_xlen(target), slot);
assert(0);
}
@ -537,8 +538,8 @@ static scans_t *scans_new(struct target *target, unsigned int scan_count)
scans_t *scans = malloc(sizeof(scans_t));
scans->scan_count = scan_count;
// This code also gets called before xlen is detected.
if (xlen(target))
scans->scan_size = 2 + xlen(target) / 8;
if (riscv_xlen(target))
scans->scan_size = 2 + riscv_xlen(target) / 8;
else
scans->scan_size = 2 + 128 / 8;
scans->next_scan = 0;
@ -641,7 +642,7 @@ static void scans_add_read32(scans_t *scans, uint16_t address, bool set_interrup
static void scans_add_read(scans_t *scans, slot_t slot, bool set_interrupt)
{
const struct target *target = scans->target;
switch (xlen(target)) {
switch (riscv_xlen(target)) {
case 32:
scans_add_read32(scans, slot_offset(target, slot), set_interrupt);
break;
@ -776,7 +777,7 @@ static void cache_set(struct target *target, slot_t slot, uint64_t data)
{
unsigned int offset = slot_offset(target, slot);
cache_set32(target, offset, data);
if (xlen(target) > 32) {
if (riscv_xlen(target) > 32) {
cache_set32(target, offset + 1, data >> 32);
}
}
@ -996,7 +997,7 @@ static uint64_t cache_get(struct target *target, slot_t slot)
{
unsigned int offset = slot_offset(target, slot);
uint64_t value = cache_get32(target, offset);
if (xlen(target) > 32) {
if (riscv_xlen(target) > 32) {
value |= ((uint64_t) cache_get32(target, offset + 1)) << 32;
}
return value;
@ -1117,7 +1118,7 @@ static int execute_resume(struct target *target, bool step)
struct reg *mstatus_reg = &target->reg_cache->reg_list[REG_MSTATUS];
if (mstatus_reg->valid) {
uint64_t mstatus_user = buf_get_u64(mstatus_reg->value, 0, xlen(target));
uint64_t mstatus_user = buf_get_u64(mstatus_reg->value, 0, riscv_xlen(target));
if (mstatus_user != info->mstatus_actual) {
cache_set_load(target, 0, S0, SLOT0);
cache_set32(target, 1, csrw(S0, CSR_MSTATUS));
@ -1198,18 +1199,18 @@ static void update_reg_list(struct target *target)
if (info->reg_values) {
free(info->reg_values);
}
info->reg_values = malloc(REG_COUNT * xlen(target) / 4);
info->reg_values = malloc(REG_COUNT * riscv_xlen(target) / 4);
for (unsigned int i = 0; i < REG_COUNT; i++) {
struct reg *r = &target->reg_cache->reg_list[i];
r->value = info->reg_values + i * xlen(target) / 4;
r->value = info->reg_values + i * riscv_xlen(target) / 4;
if (r->dirty) {
LOG_ERROR("Register %d was dirty. Its value is lost.", i);
}
if (i == REG_PRIV) {
r->size = 8;
} else {
r->size = xlen(target);
r->size = riscv_xlen(target);
}
r->valid = false;
}
@ -1259,7 +1260,7 @@ static int register_get(struct reg *reg)
maybe_write_tselect(target);
if (reg->number <= REG_XPR31) {
buf_set_u64(reg->value, 0, xlen(target), reg_cache_get(target, reg->number));
buf_set_u64(reg->value, 0, riscv_xlen(target), reg_cache_get(target, reg->number));
LOG_DEBUG("%s=0x%" PRIx64, reg->name, reg_cache_get(target, reg->number));
return ERROR_OK;
} else if (reg->number == REG_PC) {
@ -1280,7 +1281,7 @@ static int register_get(struct reg *reg)
cache_set(target, SLOT1, info->mstatus_actual);
}
if (xlen(target) == 32) {
if (riscv_xlen(target) == 32) {
cache_set32(target, i++, fsw(reg->number - REG_FPR0, 0, DEBUG_RAM_START + 16));
} else {
cache_set32(target, i++, fsd(reg->number - REG_FPR0, 0, DEBUG_RAM_START + 16));
@ -1308,13 +1309,13 @@ static int register_get(struct reg *reg)
if (exception) {
LOG_ERROR("Got exception 0x%x when reading register %d", exception,
reg->number);
buf_set_u64(reg->value, 0, xlen(target), ~0);
buf_set_u64(reg->value, 0, riscv_xlen(target), ~0);
return ERROR_FAIL;
}
uint64_t value = cache_get(target, SLOT0);
LOG_DEBUG("%s=0x%" PRIx64, reg->name, value);
buf_set_u64(reg->value, 0, xlen(target), value);
buf_set_u64(reg->value, 0, riscv_xlen(target), value);
if (reg->number == REG_MSTATUS) {
info->mstatus_actual = value;
@ -1358,7 +1359,7 @@ static int register_write(struct target *target, unsigned int number,
cache_set(target, SLOT1, info->mstatus_actual);
}
if (xlen(target) == 32) {
if (riscv_xlen(target) == 32) {
cache_set32(target, i++, flw(number - REG_FPR0, 0, DEBUG_RAM_START + 16));
} else {
cache_set32(target, i++, fld(number - REG_FPR0, 0, DEBUG_RAM_START + 16));
@ -1399,7 +1400,7 @@ static int register_set(struct reg *reg, uint8_t *buf)
{
struct target *target = (struct target *) reg->arch_info;
uint64_t value = buf_get_u64(buf, 0, xlen(target));
uint64_t value = buf_get_u64(buf, 0, riscv_xlen(target));
LOG_DEBUG("write 0x%" PRIx64 " to %s", value, reg->name);
struct reg *r = &target->reg_cache->reg_list[reg->number];
@ -1437,6 +1438,7 @@ static int init_target(struct command_context *cmd_ctx,
{
LOG_DEBUG("init");
riscv_info_t *generic_info = (riscv_info_t *) target->arch_info;
generic_info->get_register = NULL;
generic_info->version_specific = calloc(1, sizeof(riscv011_info_t));
if (!generic_info->version_specific)
return ERROR_FAIL;
@ -1510,7 +1512,7 @@ static int add_trigger(struct target *target, struct trigger *trigger)
uint64_t tdata1;
read_csr(target, &tdata1, CSR_TDATA1);
int type = get_field(tdata1, MCONTROL_TYPE(xlen(target)));
int type = get_field(tdata1, MCONTROL_TYPE(riscv_xlen(target)));
if (type != 2) {
continue;
@ -1522,7 +1524,7 @@ static int add_trigger(struct target *target, struct trigger *trigger)
}
// address/data match trigger
tdata1 |= MCONTROL_DMODE(xlen(target));
tdata1 |= MCONTROL_DMODE(riscv_xlen(target));
tdata1 = set_field(tdata1, MCONTROL_ACTION,
MCONTROL_ACTION_DEBUG_MODE);
tdata1 = set_field(tdata1, MCONTROL_MATCH, MCONTROL_MATCH_EQUAL);
@ -1769,9 +1771,9 @@ static int step(struct target *target, int current, uint32_t address,
jtag_add_ir_scan(target->tap, &select_dbus, TAP_IDLE);
if (!current) {
if (xlen(target) > 32) {
if (riscv_xlen(target) > 32) {
LOG_WARNING("Asked to resume at 32-bit PC on %d-bit target.",
xlen(target));
riscv_xlen(target));
}
int result = register_write(target, REG_PC, address);
if (result != ERROR_OK)
@ -1808,6 +1810,9 @@ static int examine(struct target *target)
return ERROR_FAIL;
}
RISCV_INFO(r);
r->hart_count = 1;
riscv011_info_t *info = get_info(target);
info->addrbits = get_field(dtmcontrol, DTMCONTROL_ADDRBITS);
info->dtmcontrol_idle = get_field(dtmcontrol, DTMCONTROL_IDLE);
@ -1875,11 +1880,11 @@ static int examine(struct target *target)
uint32_t word1 = cache_get32(target, 1);
riscv_info_t *generic_info = (riscv_info_t *) target->arch_info;
if (word0 == 1 && word1 == 0) {
generic_info->xlen = 32;
generic_info->xlen[0] = 32;
} else if (word0 == 0xffffffff && word1 == 3) {
generic_info->xlen = 64;
generic_info->xlen[0] = 64;
} else if (word0 == 0xffffffff && word1 == 0xffffffff) {
generic_info->xlen = 128;
generic_info->xlen[0] = 128;
} else {
uint32_t exception = cache_get32(target, info->dramsize-1);
LOG_ERROR("Failed to discover xlen; word0=0x%x, word1=0x%x, exception=0x%x",
@ -1887,7 +1892,7 @@ static int examine(struct target *target)
dump_debug_ram(target);
return ERROR_FAIL;
}
LOG_DEBUG("Discovered XLEN is %d", xlen(target));
LOG_DEBUG("Discovered XLEN is %d", riscv_xlen(target));
// Update register list to match discovered XLEN.
update_reg_list(target);
@ -1905,7 +1910,7 @@ static int examine(struct target *target)
}
target_set_examined(target);
LOG_INFO("Examined RISCV core; XLEN=%d, misa=0x%" PRIx64, xlen(target), info->misa);
LOG_INFO("Examined RISCV core; XLEN=%d, misa=0x%" PRIx64, riscv_xlen(target), info->misa);
return ERROR_OK;
}
@ -2030,10 +2035,10 @@ static riscv_error_t handle_halt_routine(struct target *target)
default:
assert(0);
}
if (xlen(target) == 32) {
if (riscv_xlen(target) == 32) {
reg_cache_set(target, reg, data & 0xffffffff);
result++;
} else if (xlen(target) == 64) {
} else if (riscv_xlen(target) == 64) {
if (address == 4) {
value = data & 0xffffffff;
} else if (address == 5) {
@ -2125,7 +2130,7 @@ static int handle_halt(struct target *target, bool announce)
break;
uint64_t tdata1;
read_csr(target, &tdata1, CSR_TDATA1);
if ((tdata1 & MCONTROL_DMODE(xlen(target))) &&
if ((tdata1 & MCONTROL_DMODE(riscv_xlen(target))) &&
(tdata1 & (MCONTROL_EXECUTE | MCONTROL_STORE | MCONTROL_LOAD))) {
write_csr(target, CSR_TDATA1, 0);
}
@ -2196,9 +2201,9 @@ static int riscv011_resume(struct target *target, int current, uint32_t address,
jtag_add_ir_scan(target->tap, &select_dbus, TAP_IDLE);
if (!current) {
if (xlen(target) > 32) {
if (riscv_xlen(target) > 32) {
LOG_WARNING("Asked to resume at 32-bit PC on %d-bit target.",
xlen(target));
riscv_xlen(target));
}
int result = register_write(target, REG_PC, address);
if (result != ERROR_OK)

File diff suppressed because it is too large Load Diff

View File

@ -15,6 +15,8 @@
#include "breakpoints.h"
#include "helper/time_support.h"
#include "riscv.h"
#include "gdb_regs.h"
#include "rtos/rtos.h"
/**
* Since almost everything can be accomplish by scanning the dbus register, all
@ -332,7 +334,7 @@ static int riscv_examine(struct target *target)
return tt->examine(target);
}
static int riscv_poll(struct target *target)
static int oldriscv_poll(struct target *target)
{
struct target_type *tt = get_target_type(target);
return tt->poll(target);
@ -376,7 +378,13 @@ static int riscv_get_gdb_reg_list(struct target *target,
struct reg **reg_list[], int *reg_list_size,
enum target_register_class reg_class)
{
RISCV_INFO(r);
LOG_DEBUG("reg_class=%d", reg_class);
LOG_DEBUG("riscv_get_gdb_reg_list: rtos_hartid=%d current_hartid=%d", r->rtos_hartid, r->current_hartid);
if (r->rtos_hartid != -1)
riscv_set_current_hartid(target, r->rtos_hartid);
else
riscv_set_current_hartid(target, 0);
switch (reg_class) {
case REG_CLASS_GENERAL:
@ -395,6 +403,7 @@ static int riscv_get_gdb_reg_list(struct target *target,
return ERROR_FAIL;
}
for (int i = 0; i < *reg_list_size; i++) {
assert(target->reg_cache->reg_list[i].size > 0);
(*reg_list)[i] = &target->reg_cache->reg_list[i];
}
@ -474,7 +483,7 @@ static int riscv_run_algorithm(struct target *target, int num_mem_params,
reg_mstatus->type->get(reg_mstatus);
current_mstatus = buf_get_u64(reg_mstatus->value, 0, reg_mstatus->size);
uint64_t ie_mask = MSTATUS_MIE | MSTATUS_HIE | MSTATUS_SIE | MSTATUS_UIE;
buf_set_u64(mstatus_bytes, 0, info->xlen, set_field(current_mstatus,
buf_set_u64(mstatus_bytes, 0, info->xlen[0], set_field(current_mstatus,
ie_mask, 0));
reg_mstatus->type->set(reg_mstatus, mstatus_bytes);
@ -492,11 +501,11 @@ static int riscv_run_algorithm(struct target *target, int num_mem_params,
if (now - start > timeout_ms) {
LOG_ERROR("Algorithm timed out after %d ms.", timeout_ms);
riscv_halt(target);
riscv_poll(target);
oldriscv_poll(target);
return ERROR_TARGET_TIMEOUT;
}
int result = riscv_poll(target);
int result = oldriscv_poll(target);
if (result != ERROR_OK) {
return result;
}
@ -514,12 +523,12 @@ static int riscv_run_algorithm(struct target *target, int num_mem_params,
// Restore Interrupts
LOG_DEBUG("Restoring Interrupts");
buf_set_u64(mstatus_bytes, 0, info->xlen, current_mstatus);
buf_set_u64(mstatus_bytes, 0, info->xlen[0], current_mstatus);
reg_mstatus->type->set(reg_mstatus, mstatus_bytes);
/// Restore registers
uint8_t buf[8];
buf_set_u64(buf, 0, info->xlen, saved_pc);
buf_set_u64(buf, 0, info->xlen[0], saved_pc);
if (reg_pc->type->set(reg_pc, buf) != ERROR_OK) {
return ERROR_FAIL;
}
@ -527,7 +536,7 @@ static int riscv_run_algorithm(struct target *target, int num_mem_params,
for (int i = 0; i < num_reg_params; i++) {
LOG_DEBUG("restore %s", reg_params[i].reg_name);
struct reg *r = register_get_by_name(target->reg_cache, reg_params[i].reg_name, 0);
buf_set_u64(buf, 0, info->xlen, saved_regs[r->number]);
buf_set_u64(buf, 0, info->xlen[0], saved_regs[r->number]);
if (r->type->set(r, buf) != ERROR_OK) {
return ERROR_FAIL;
}
@ -572,7 +581,7 @@ struct target_type riscv_target =
.examine = riscv_examine,
/* poll current target status */
.poll = riscv_poll,
.poll = oldriscv_poll,
.halt = riscv_halt,
.resume = riscv_resume,
@ -599,3 +608,460 @@ struct target_type riscv_target =
.run_algorithm = riscv_run_algorithm,
};
/*** OpenOCD Helper Functions ***/
/* 0 means nothing happened, 1 means the hart's state changed (and thus the
* poll should terminate), and -1 means there was an error. */
static int riscv_poll_hart(struct target *target, int hartid)
{
RISCV_INFO(r);
LOG_DEBUG("polling hart %d", hartid);
/* Polling can only detect one state change: a hart that was previously
* running but has gone to sleep. A state change in the other
* direction is invalid and indicates that one of the previous calls
* didn't correctly block. */
riscv_set_current_hartid(target, hartid);
if (riscv_was_halted(target) && !riscv_is_halted(target)) {
LOG_ERROR("unexpected wakeup on hart %d", hartid);
abort();
}
/* If there's no new event then there's nothing to do. */
if (riscv_was_halted(target) || !riscv_is_halted(target))
return 0;
/* If we got here then this must be the first poll during which this
* hart halted. We need to synchronize the hart's state with the
* debugger, and inform the outer polling loop that there's something
* to do. */
r->hart_state[hartid] = RISCV_HART_HALTED;
r->on_halt(target);
return 1;
}
/*** OpenOCD Interface ***/
int riscv_openocd_poll(struct target *target)
{
LOG_DEBUG("polling all harts");
if (riscv_rtos_enabled(target)) {
/* Check every hart for an event. */
int triggered_hart = -1;
for (int i = 0; i < riscv_count_harts(target); ++i) {
int out = riscv_poll_hart(target, i);
switch (out) {
case 0:
continue;
case 1:
triggered_hart = i;
break;
case -1:
return ERROR_FAIL;
}
}
if (triggered_hart == -1) {
LOG_DEBUG(" no harts halted");
return ERROR_OK;
}
LOG_DEBUG(" hart %d halted", triggered_hart);
/* If we're here then at least one hart triggered. That means
* we want to go and halt _every_ hart in the system, as that's
* the invariant we hold here. Some harts might have already
* halted (as we're either in single-step mode or they also
* triggered a breakpoint), so don't attempt to halt those
* harts. */
for (int i = 0; i < riscv_count_harts(target); ++i)
riscv_halt_one_hart(target, i);
target->state = TARGET_HALTED;
switch (riscv_halt_reason(target, triggered_hart)) {
case RISCV_HALT_BREAKPOINT:
target->debug_reason = DBG_REASON_BREAKPOINT;
break;
case RISCV_HALT_INTERRUPT:
target->debug_reason = DBG_REASON_DBGRQ;
break;
case RISCV_HALT_SINGLESTEP:
target->debug_reason = DBG_REASON_SINGLESTEP;
break;
}
target->rtos->current_threadid = triggered_hart + 1;
target->rtos->current_thread = triggered_hart + 1;
target_call_event_callbacks(target, TARGET_EVENT_HALTED);
return ERROR_OK;
} else {
return riscv_poll_hart(target, riscv_current_hartid(target));
}
}
int riscv_openocd_halt(struct target *target)
{
int out = riscv_halt_all_harts(target);
if (out != ERROR_OK)
return out;
/* Don't change the target state right here, it'll get updated by the
* poll. */
riscv_openocd_poll(target);
return out;
}
int riscv_openocd_resume(
struct target *target,
int current,
uint32_t address,
int handle_breakpoints,
int debug_execution
) {
if (!current) {
LOG_ERROR("resume-at-pc unimplemented");
return ERROR_FAIL;
}
int out = riscv_resume_all_harts(target);
if (out != ERROR_OK)
return out;
target->state = TARGET_RUNNING;
riscv_openocd_poll(target);
return out;
}
int riscv_openocd_step(
struct target *target,
int current,
uint32_t address,
int handle_breakpoints
) {
RISCV_INFO(r);
if (!current) {
LOG_ERROR("step-at-pc unimplemented");
return ERROR_FAIL;
}
int out = riscv_step_rtos_hart(target);
if (out != ERROR_OK)
return out;
/* step_rtos_hart blocks until the hart has actually stepped, but we
* need to cycle through OpenOCD to actually get this to trigger. */
target->state = TARGET_RUNNING;
riscv_openocd_poll(target);
return out;
}
/*** RISC-V Interface ***/
void riscv_info_init(riscv_info_t *r)
{
memset(r, 0, sizeof(*r));
r->dtm_version = 1;
/* FIXME: The RTOS gets enabled before the target gets initialized. */
r->rtos_enabled = true;
for (size_t h = 0; h < RISCV_MAX_HARTS; ++h) {
r->xlen[h] = -1;
r->hart_state[h] = RISCV_HART_UNKNOWN;
r->debug_buffer_addr[h] = -1;
for (size_t e = 0; e < RISCV_MAX_REGISTERS; ++e)
r->valid_saved_registers[h][e] = false;
}
}
int riscv_halt_all_harts(struct target *target)
{
if (riscv_rtos_enabled(target)) {
for (int i = 0; i < riscv_count_harts(target); ++i)
riscv_halt_one_hart(target, i);
} else {
riscv_halt_one_hart(target, riscv_current_hartid(target));
}
return ERROR_OK;
}
int riscv_halt_one_hart(struct target *target, int hartid)
{
RISCV_INFO(r);
LOG_DEBUG("halting hart %d", hartid);
riscv_set_current_hartid(target, hartid);
if (r->hart_state[hartid] == RISCV_HART_UNKNOWN) {
r->hart_state[hartid] = riscv_is_halted(target) ? RISCV_HART_HALTED : RISCV_HART_RUNNING;
if (riscv_was_halted(target)) {
LOG_WARNING("Connected to hart %d, which was halted. s0, s1, and pc were overwritten by your previous debugger session and cannot be restored.", hartid);
r->on_halt(target);
}
}
if (riscv_was_halted(target)) {
LOG_DEBUG(" hart %d requested halt, but was already halted", hartid);
return ERROR_OK;
}
r->halt_current_hart(target);
/* Here we don't actually update 'hart_state' because we want poll to
* pick that up. We can't actually wait until */
return ERROR_OK;
}
int riscv_resume_all_harts(struct target *target)
{
if (riscv_rtos_enabled(target)) {
for (int i = 0; i < riscv_count_harts(target); ++i)
riscv_resume_one_hart(target, i);
} else {
riscv_resume_one_hart(target, riscv_current_hartid(target));
}
return ERROR_OK;
}
int riscv_resume_one_hart(struct target *target, int hartid)
{
RISCV_INFO(r);
LOG_DEBUG("resuming hart %d", hartid);
riscv_set_current_hartid(target, hartid);
if (r->hart_state[hartid] == RISCV_HART_UNKNOWN) {
r->hart_state[hartid] = riscv_is_halted(target) ? RISCV_HART_HALTED : RISCV_HART_RUNNING;
if (!riscv_was_halted(target)) {
LOG_ERROR("Asked to resume hart %d, which was in an unknown state", hartid);
r->on_resume(target);
}
}
if (!riscv_was_halted(target)) {
LOG_DEBUG(" hart %d requested resume, but was already resumed", hartid);
return ERROR_OK;
}
r->on_resume(target);
r->resume_current_hart(target);
r->hart_state[hartid] = RISCV_HART_RUNNING;
return ERROR_OK;
}
int riscv_step_rtos_hart(struct target *target)
{
RISCV_INFO(r);
int hartid = r->current_hartid;
if (riscv_rtos_enabled(target)) {
hartid = r->rtos_hartid;
if (hartid == -1) {
LOG_USER("GDB has asked me to step \"any\" thread, so I'm stepping hart 0.");
hartid = 0;
}
}
riscv_set_current_hartid(target, hartid);
LOG_DEBUG("stepping hart %d", hartid);
assert(r->hart_state[hartid] == RISCV_HART_HALTED);
r->on_step(target);
r->step_current_hart(target);
r->hart_state[hartid] = RISCV_HART_RUNNING;
r->on_halt(target);
r->hart_state[hartid] = RISCV_HART_HALTED;
assert(riscv_is_halted(target));
return ERROR_OK;
}
int riscv_xlen(const struct target *target)
{
return riscv_xlen_of_hart(target, riscv_current_hartid(target));
}
int riscv_xlen_of_hart(const struct target *target, int hartid)
{
RISCV_INFO(r);
assert(r->xlen[hartid] != -1);
return r->xlen[hartid];
}
void riscv_enable_rtos(struct target *target)
{
RISCV_INFO(r);
r->rtos_enabled = true;
}
bool riscv_rtos_enabled(const struct target *target)
{
RISCV_INFO(r);
return r->rtos_enabled;
}
void riscv_set_current_hartid(struct target *target, int hartid)
{
RISCV_INFO(r);
r->current_hartid = hartid;
r->select_current_hart(target);
/* This might get called during init, in which case we shouldn't be
* setting up the register cache. */
if (!target_was_examined(target))
return;
/* Update the register list's widths. */
register_cache_invalidate(target->reg_cache);
for (size_t i = 0; i < GDB_REGNO_COUNT; ++i) {
struct reg *reg = &target->reg_cache->reg_list[i];
reg->value = &r->reg_cache_values[i];
reg->valid = false;
switch (i) {
case GDB_REGNO_PRIV:
reg->size = 8;
break;
default:
reg->size = riscv_xlen(target);
break;
}
}
}
int riscv_current_hartid(const struct target *target)
{
RISCV_INFO(r);
assert(riscv_rtos_enabled(target) || target->coreid == r->current_hartid);
return r->current_hartid;
}
void riscv_set_all_rtos_harts(struct target *target)
{
RISCV_INFO(r);
r->rtos_hartid = -1;
}
void riscv_set_rtos_hartid(struct target *target, int hartid)
{
LOG_DEBUG("setting RTOS hartid %d", hartid);
RISCV_INFO(r);
r->rtos_hartid = hartid;
}
int riscv_count_harts(struct target *target)
{
if (target == NULL) return 1;
RISCV_INFO(r);
if (r == NULL) return 1;
return r->hart_count;
}
bool riscv_has_register(struct target *target, int hartid, int regid)
{
return 1;
}
void riscv_set_register(struct target *target, enum gdb_regno r, riscv_reg_t v)
{
return riscv_set_register_on_hart(target, riscv_current_hartid(target), r, v);
}
void riscv_set_register_on_hart(struct target *target, int hartid, enum gdb_regno regid, uint64_t value)
{
RISCV_INFO(r);
LOG_DEBUG("writing register %d on hart %d", regid, hartid);
return r->set_register(target, hartid, regid, value);
}
riscv_reg_t riscv_get_register(struct target *target, enum gdb_regno r)
{
return riscv_get_register_on_hart(target, riscv_current_hartid(target), r);
}
uint64_t riscv_get_register_on_hart(struct target *target, int hartid, enum gdb_regno regid)
{
RISCV_INFO(r);
LOG_DEBUG("reading register %d on hart %d", regid, hartid);
return r->get_register(target, hartid, regid);
}
bool riscv_is_halted(struct target *target)
{
RISCV_INFO(r);
return r->is_halted(target);
}
bool riscv_was_halted(struct target *target)
{
RISCV_INFO(r);
assert(r->hart_state[r->current_hartid] != RISCV_HART_UNKNOWN);
return r->hart_state[r->current_hartid] == RISCV_HART_HALTED;
}
enum riscv_halt_reason riscv_halt_reason(struct target *target, int hartid)
{
RISCV_INFO(r);
riscv_set_current_hartid(target, hartid);
assert(riscv_is_halted(target));
return r->halt_reason(target);
}
int riscv_count_triggers(struct target *target)
{
return riscv_count_triggers_of_hart(target, riscv_current_hartid(target));
}
int riscv_count_triggers_of_hart(struct target *target, int hartid)
{
RISCV_INFO(r);
assert(hartid < riscv_count_harts(target));
return r->trigger_count[hartid];
}
size_t riscv_debug_buffer_size(struct target *target)
{
RISCV_INFO(r);
return r->debug_buffer_size[riscv_current_hartid(target)];
}
riscv_addr_t riscv_debug_buffer_addr(struct target *target)
{
RISCV_INFO(r);
riscv_addr_t out = r->debug_buffer_addr[riscv_current_hartid(target)];
assert(out != -1);
return out;
}
int riscv_debug_buffer_enter(struct target *target, struct riscv_program *program)
{
RISCV_INFO(r);
r->debug_buffer_enter(target, program);
return ERROR_OK;
}
int riscv_debug_buffer_leave(struct target *target, struct riscv_program *program)
{
RISCV_INFO(r);
r->debug_buffer_leave(target, program);
return ERROR_OK;
}
int riscv_write_debug_buffer(struct target *target, int index, riscv_insn_t insn)
{
RISCV_INFO(r);
r->write_debug_buffer(target, index, insn);
return ERROR_OK;
}
riscv_insn_t riscv_read_debug_buffer(struct target *target, int index)
{
RISCV_INFO(r);
return r->read_debug_buffer(target, index);
}
int riscv_execute_debug_buffer(struct target *target)
{
RISCV_INFO(r);
r->execute_debug_buffer(target);
return ERROR_OK;
}

View File

@ -1,7 +1,16 @@
#ifndef RISCV_H
#define RISCV_H
struct riscv_program;
#include <stdint.h>
#include "opcodes.h"
#include "gdb_regs.h"
/* The register cache is staticly allocated. */
#define RISCV_MAX_HARTS 32
#define RISCV_MAX_REGISTERS 5000
#define RISCV_MAX_TRIGGERS 32
extern struct target_type riscv011_target;
extern struct target_type riscv013_target;
@ -9,15 +18,94 @@ extern struct target_type riscv013_target;
/*
* Definitions shared by code supporting all RISC-V versions.
*/
typedef uint64_t riscv_reg_t;
typedef uint32_t riscv_insn_t;
typedef int64_t riscv_addr_t;
enum riscv_hart_state {
RISCV_HART_UNKNOWN,
RISCV_HART_HALTED,
RISCV_HART_RUNNING,
};
enum riscv_halt_reason {
RISCV_HALT_INTERRUPT,
RISCV_HALT_BREAKPOINT,
RISCV_HALT_SINGLESTEP,
};
typedef struct {
unsigned dtm_version;
struct command_context *cmd_ctx;
void *version_specific;
/* Width of a GPR (and many other things) in bits. */
uint8_t xlen;
/* The number of harts on this system. */
int hart_count;
/* When the RTOS is enabled this target is expected to handle all the
* harts in the system. When it's disabled this just uses the regular
* multi-target mode. */
bool rtos_enabled;
/* The hart that the RTOS thinks is currently being debugged. */
int rtos_hartid;
/* The hart that is currently being debugged. Note that this is
* different than the hartid that the RTOS is expected to use. This
* one will change all the time, it's more of a global argument to
* every function than an actual */
int current_hartid;
/* Enough space to store all the registers we might need to save. */
/* FIXME: This should probably be a bunch of register caches. */
uint64_t saved_registers[RISCV_MAX_HARTS][RISCV_MAX_REGISTERS];
bool valid_saved_registers[RISCV_MAX_HARTS][RISCV_MAX_REGISTERS];
/* The register cache points into here. */
uint64_t reg_cache_values[RISCV_MAX_REGISTERS];
/* It's possible that each core has a different supported ISA set. */
int xlen[RISCV_MAX_HARTS];
/* The state of every hart. */
enum riscv_hart_state hart_state[RISCV_MAX_HARTS];
/* The number of triggers per hart. */
int trigger_count[RISCV_MAX_HARTS];
/* The address of the debug RAM buffer. */
riscv_addr_t debug_buffer_addr[RISCV_MAX_HARTS];
/* The number of entries in the debug buffer. */
int debug_buffer_size[RISCV_MAX_HARTS];
/* Helper functions that target the various RISC-V debug spec
* implementations. */
riscv_reg_t (*get_register)(struct target *, int, int);
void (*set_register)(struct target *, int, int, uint64_t);
void (*select_current_hart)(struct target *);
bool (*is_halted)(struct target *target);
void (*halt_current_hart)(struct target *);
void (*resume_current_hart)(struct target *target);
void (*step_current_hart)(struct target *target);
void (*on_halt)(struct target *target);
void (*on_resume)(struct target *target);
void (*on_step)(struct target *target);
enum riscv_halt_reason (*halt_reason)(struct target *target);
void (*debug_buffer_enter)(struct target *target, struct riscv_program *program);
void (*debug_buffer_leave)(struct target *target, struct riscv_program *program);
void (*write_debug_buffer)(struct target *target, int i, riscv_insn_t d);
riscv_insn_t (*read_debug_buffer)(struct target *target, int i);
void (*execute_debug_buffer)(struct target *target);
} riscv_info_t;
/* Everything needs the RISC-V specific info structure, so here's a nice macro
* that provides that. */
static inline riscv_info_t *riscv_info(const struct target *target) __attribute__((unused));
static inline riscv_info_t *riscv_info(const struct target *target)
{ return target->arch_info; }
#define RISCV_INFO(R) riscv_info_t *R = riscv_info(target);
extern uint8_t ir_dtmcontrol[1];
extern struct scan_field select_dtmcontrol;
extern uint8_t ir_dbus[1];
@ -25,43 +113,101 @@ extern struct scan_field select_dbus;
extern uint8_t ir_idcode[1];
extern struct scan_field select_idcode;
/*** Version-independent functions that we don't want in the main address space. ***/
/*** OpenOCD Interface */
int riscv_openocd_poll(struct target *target);
static uint32_t load(const struct target *target, unsigned int rd,
unsigned int base, uint16_t offset) __attribute__ ((unused));
static uint32_t load(const struct target *target, unsigned int rd,
unsigned int base, uint16_t offset)
{
riscv_info_t *info = (riscv_info_t *) target->arch_info;
switch (info->xlen) {
case 32:
return lw(rd, base, offset);
case 64:
return ld(rd, base, offset);
}
assert(0);
}
int riscv_openocd_halt(struct target *target);
static uint32_t store(const struct target *target, unsigned int src,
unsigned int base, uint16_t offset) __attribute__ ((unused));
static uint32_t store(const struct target *target, unsigned int src,
unsigned int base, uint16_t offset)
{
riscv_info_t *info = (riscv_info_t *) target->arch_info;
switch (info->xlen) {
case 32:
return sw(src, base, offset);
case 64:
return sd(src, base, offset);
}
assert(0);
}
int riscv_openocd_resume(
struct target *target,
int current,
uint32_t address,
int handle_breakpoints,
int debug_execution
);
static unsigned xlen(const struct target *target) __attribute__ ((unused));
static unsigned xlen(const struct target *target)
{
riscv_info_t *info = (riscv_info_t *) target->arch_info;
return info->xlen;
}
int riscv_openocd_step(
struct target *target,
int current,
uint32_t address,
int handle_breakpoints
);
/*** RISC-V Interface ***/
/* Initializes the shared RISC-V structure. */
void riscv_info_init(riscv_info_t *r);
/* Run control, possibly for multiple harts. The _all_harts versions resume
* all the enabled harts, which when running in RTOS mode is all the harts on
* the system. */
int riscv_halt_all_harts(struct target *target);
int riscv_halt_one_hart(struct target *target, int hartid);
int riscv_resume_all_harts(struct target *target);
int riscv_resume_one_hart(struct target *target, int hartid);
/* Steps the hart that's currently selected in the RTOS, or if there is no RTOS
* then the only hart. */
int riscv_step_rtos_hart(struct target *target);
/* Returns XLEN for the given (or current) hart. */
int riscv_xlen(const struct target *target);
int riscv_xlen_of_hart(const struct target *target, int hartid);
/* Enables RISC-V RTOS support for this target. This causes the coreid field
* of the generic target struct to be ignored, and instead for operations to
* apply to all the harts in the system. */
void riscv_enable_rtos(struct target *target);
bool riscv_rtos_enabled(const struct target *target);
/* Sets the current hart, which is the hart that will actually be used when
* issuing debug commands. */
void riscv_set_current_hartid(struct target *target, int hartid);
int riscv_current_hartid(const struct target *target);
/*** Support functions for the RISC-V 'RTOS', which provides multihart support
* without requiring multiple targets. */
/* When using the RTOS to debug, this selects the hart that is currently being
* debugged. This doesn't propogate to the hardware. */
void riscv_set_all_rtos_harts(struct target *target);
void riscv_set_rtos_hartid(struct target *target, int hartid);
/* Lists the number of harts in the system, which are assumed to be
* concecutive and start with mhartid=0. */
int riscv_count_harts(struct target *target);
/* Returns TRUE if the target has the given register on the given hart. */
bool riscv_has_register(struct target *target, int hartid, int regid);
/* Returns the value of the given register on the given hart. 32-bit registers
* are zero extended to 64 bits. */
void riscv_set_register(struct target *target, enum gdb_regno i, riscv_reg_t v);
void riscv_set_register_on_hart(struct target *target, int hid, enum gdb_regno rid, uint64_t v);
riscv_reg_t riscv_get_register(struct target *target, enum gdb_regno i);
riscv_reg_t riscv_get_register_on_hart(struct target *target, int hid, enum gdb_regno rid);
/* Checks the state of the current hart -- "is_halted" checks the actual
* on-device register, while "was_halted" checks the machine's state. */
bool riscv_is_halted(struct target *target);
bool riscv_was_halted(struct target *target);
enum riscv_halt_reason riscv_halt_reason(struct target *target, int hartid);
/* Returns the number of triggers availiable to either the current hart or to
* the given hart. */
int riscv_count_triggers(struct target *target);
int riscv_count_triggers_of_hart(struct target *target, int hartid);
/* These helper functions let the generic program interface get target-specific
* information. */
size_t riscv_debug_buffer_size(struct target *target);
riscv_addr_t riscv_debug_buffer_addr(struct target *target);
int riscv_debug_buffer_enter(struct target *target, struct riscv_program *program);
int riscv_debug_buffer_leave(struct target *target, struct riscv_program *program);
riscv_insn_t riscv_read_debug_buffer(struct target *target, int index);
int riscv_write_debug_buffer(struct target *target, int index, riscv_insn_t insn);
int riscv_execute_debug_buffer(struct target *target);
#endif