Add 64-bit and multihart support

This is a major rewrite of the RISC-V v0.13 OpenOCD port.  This
shouldn't have any meaningful effect on the v0.11 support, but it does
add generic versions of many functions that will allow me to later
refactor the v0.11 support so it's easier to maintain both ports.  This
started as an emergency feature branch and went on for a long time, so
it's all been squashed down into one commit so there isn't a big set of
broken commits lying around.  The changes are:

 * You can pass "-rtos riscv" to the target in OpenOCD's configuration
   file, which enables multi-hart mode.  This uses OpenOCD's RTOS
   support to control all the harts from the debug module using commands
   like "info threads" in GDB.  This support is still expermental.

 * There is support for RV64I, but due to OpenOCD limitations we only
   support 32-bit physical addresses.  I hope to remedy this by rebasing
   onto the latest OpenOCD release, which I've heard should fix this.

 * This matches the latest draft version of the RISC-V debug spec, as of
   April 26th.  This version fixes a number of spec bugs and should be
   close to the final debug spec.
race
Palmer Dabbelt 2017-03-24 18:21:56 -07:00
parent 3dc066382b
commit 8dea2908b7
22 changed files with 3226 additions and 1467 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 */
@ -121,24 +122,28 @@
#define FESPI_READ_REG(a) (_FESPI_READ_REG(a))
#define _FESPI_READ_REG(a) \
{ \
#define _FESPI_READ_REG(a) \
{ \
int __a; \
uint32_t __v; \
\
__a = target_read_u32(target, ctrl_base + (a), &__v); \
if (__a != ERROR_OK) \
uint32_t __v; \
\
__a = target_read_u32(target, ctrl_base + (a), &__v); \
if (__a != ERROR_OK) { \
LOG_ERROR("FESPI_READ_REG error"); \
return __a; \
} \
__v; \
}
#define FESPI_WRITE_REG(a, v) \
{ \
#define FESPI_WRITE_REG(a, v) \
{ \
int __r; \
\
__r = target_write_u32(target, ctrl_base + (a), (v)); \
if (__r != ERROR_OK) \
\
__r = target_write_u32(target, ctrl_base + (a), (v)); \
if (__r != ERROR_OK) { \
LOG_ERROR("FESPI_WRITE_REG error"); \
return __r; \
} \
}
#define FESPI_DISABLE_HW_MODE() FESPI_WRITE_REG(FESPI_REG_FCTRL, \
@ -779,12 +784,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;
}
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_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, (char *)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) {
target->state = TARGET_RUNNING;
gdb_set_frontend_state_running(connection);
target_call_event_callbacks(target, TARGET_EVENT_GDB_START);
target_call_event_callbacks(target, TARGET_EVENT_RESUME_START);
riscv_resume_all_harts(target);
target_call_event_callbacks(target, TARGET_EVENT_RESUME_END);
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("%s", *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,9 @@ INTEL_IA32_SRC = \
RISCV_SRC = \
riscv/riscv-011.c \
riscv/riscv-013.c \
riscv/riscv.c
riscv/riscv.c \
riscv/program.c \
riscv/batch.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

156
src/target/riscv/batch.c Normal file
View File

@ -0,0 +1,156 @@
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "batch.h"
#include "debug_defines.h"
#include "riscv.h"
#define get_field(reg, mask) (((reg) & (mask)) / ((mask) & ~((mask) << 1)))
#define set_field(reg, mask, val) (((reg) & ~(mask)) | (((val) * ((mask) & ~((mask) << 1))) & (mask)))
static void dump_field(const struct scan_field *field);
struct riscv_batch *riscv_batch_alloc(struct target *target, size_t scans, size_t idle)
{
scans += 4;
struct riscv_batch *out = malloc(sizeof(*out));
memset(out, 0, sizeof(*out));
out->target = target;
out->allocated_scans = scans;
out->used_scans = 0;
out->idle_count = idle;
out->data_out = malloc(sizeof(*out->data_out) * (scans) * sizeof(uint64_t));
out->data_in = malloc(sizeof(*out->data_in) * (scans) * sizeof(uint64_t));
out->fields = malloc(sizeof(*out->fields) * (scans));
out->last_scan = RISCV_SCAN_TYPE_INVALID;
out->read_keys = malloc(sizeof(*out->read_keys) * (scans));
out->read_keys_used = 0;
return out;
}
void riscv_batch_free(struct riscv_batch *batch)
{
free(batch->data_in);
free(batch->data_out);
free(batch->fields);
free(batch);
}
bool riscv_batch_full(struct riscv_batch *batch)
{
return batch->used_scans > (batch->allocated_scans - 4);
}
void riscv_batch_run(struct riscv_batch *batch)
{
LOG_DEBUG("running a batch of %ld scans", (long)batch->used_scans);
riscv_batch_add_nop(batch);
for (size_t i = 0; i < batch->used_scans; ++i) {
dump_field(batch->fields + i);
jtag_add_dr_scan(batch->target->tap, 1, batch->fields + i, TAP_IDLE);
if (batch->idle_count > 0)
jtag_add_runtest(batch->idle_count, TAP_IDLE);
}
LOG_DEBUG("executing queue");
if (jtag_execute_queue() != ERROR_OK) {
LOG_ERROR("Unable to execute JTAG queue");
abort();
}
for (size_t i = 0; i < batch->used_scans; ++i)
dump_field(batch->fields + i);
}
void riscv_batch_add_dmi_write(struct riscv_batch *batch, unsigned address, uint64_t data)
{
assert(batch->used_scans < batch->allocated_scans);
struct scan_field *field = batch->fields + batch->used_scans;
field->num_bits = riscv_dmi_write_u64_bits(batch->target);
field->out_value = (void *)(batch->data_out + batch->used_scans * sizeof(uint64_t));
field->in_value = (void *)(batch->data_in + batch->used_scans * sizeof(uint64_t));
riscv_fill_dmi_write_u64(batch->target, (char *)field->out_value, address, data);
riscv_fill_dmi_nop_u64(batch->target, (char *)field->in_value);
batch->last_scan = RISCV_SCAN_TYPE_WRITE;
batch->used_scans++;
}
size_t riscv_batch_add_dmi_read(struct riscv_batch *batch, unsigned address)
{
assert(batch->used_scans < batch->allocated_scans);
struct scan_field *field = batch->fields + batch->used_scans;
field->num_bits = riscv_dmi_write_u64_bits(batch->target);
field->out_value = (void *)(batch->data_out + batch->used_scans * sizeof(uint64_t));
field->in_value = (void *)(batch->data_in + batch->used_scans * sizeof(uint64_t));
riscv_fill_dmi_read_u64(batch->target, (char *)field->out_value, address);
riscv_fill_dmi_nop_u64(batch->target, (char *)field->in_value);
batch->last_scan = RISCV_SCAN_TYPE_READ;
batch->used_scans++;
/* FIXME We get the read response back on the next scan. For now I'm
* just sticking a NOP in there, but this should be coelesced away. */
riscv_batch_add_nop(batch);
batch->read_keys[batch->read_keys_used] = batch->used_scans - 1;
LOG_DEBUG("read key %ld for batch 0x%p is %ld (0x%p)", batch->read_keys_used, batch, batch->used_scans - 1, (uint64_t*)batch->data_in + (batch->used_scans + 1));
return batch->read_keys_used++;
}
uint64_t riscv_batch_get_dmi_read(struct riscv_batch *batch, size_t key)
{
assert(key < batch->read_keys_used);
size_t index = batch->read_keys[key];
assert(index <= batch->used_scans);
uint64_t *addr = ((uint64_t *)(batch->data_in) + index);
return *addr;
}
void riscv_batch_add_nop(struct riscv_batch *batch)
{
assert(batch->used_scans < batch->allocated_scans);
struct scan_field *field = batch->fields + batch->used_scans;
field->num_bits = riscv_dmi_write_u64_bits(batch->target);
field->out_value = (void *)(batch->data_out + batch->used_scans * sizeof(uint64_t));
field->in_value = (void *)(batch->data_in + batch->used_scans * sizeof(uint64_t));
riscv_fill_dmi_nop_u64(batch->target, (char *)field->out_value);
riscv_fill_dmi_nop_u64(batch->target, (char *)field->in_value);
batch->last_scan = RISCV_SCAN_TYPE_NOP;
batch->used_scans++;
LOG_DEBUG(" added NOP with in_value=0x%p", field->in_value);
}
void dump_field(const struct scan_field *field)
{
static const char *op_string[] = {"-", "r", "w", "?"};
static const char *status_string[] = {"+", "?", "F", "b"};
if (debug_level < LOG_LVL_DEBUG)
return;
assert(field->out_value != NULL);
uint64_t out = buf_get_u64(field->out_value, 0, field->num_bits);
unsigned int out_op = get_field(out, DTM_DMI_OP);
unsigned int out_data = get_field(out, DTM_DMI_DATA);
unsigned int out_address = out >> DTM_DMI_ADDRESS_OFFSET;
if (field->in_value) {
uint64_t in = buf_get_u64(field->in_value, 0, field->num_bits);
unsigned int in_op = get_field(in, DTM_DMI_OP);
unsigned int in_data = get_field(in, DTM_DMI_DATA);
unsigned int in_address = in >> DTM_DMI_ADDRESS_OFFSET;
log_printf_lf(LOG_LVL_DEBUG,
__FILE__, __LINE__, __PRETTY_FUNCTION__,
"%db %s %08x @%02x -> %s %08x @%02x [0x%p -> 0x%p]",
field->num_bits,
op_string[out_op], out_data, out_address,
status_string[in_op], in_data, in_address,
field->out_value, field->in_value);
} else {
log_printf_lf(LOG_LVL_DEBUG,
__FILE__, __LINE__, __PRETTY_FUNCTION__, "%db %s %08x @%02x -> ?",
field->num_bits, op_string[out_op], out_data, out_address);
}
}

64
src/target/riscv/batch.h Normal file
View File

@ -0,0 +1,64 @@
#ifndef TARGET__RISCV__SCANS_H
#define TARGET__RISCV__SCANS_H
#include "target/target.h"
#include "jtag/jtag.h"
enum riscv_scan_type {
RISCV_SCAN_TYPE_INVALID,
RISCV_SCAN_TYPE_NOP,
RISCV_SCAN_TYPE_READ,
RISCV_SCAN_TYPE_WRITE,
};
/* A batch of multiple JTAG scans, which are grouped together to avoid the
* overhead of some JTAG adapters when sending single commands. This is
* designed to support block copies, as that's what we actually need to go
* fast. */
struct riscv_batch {
struct target *target;
size_t allocated_scans;
size_t used_scans;
size_t idle_count;
char *data_out;
char *data_in;
struct scan_field *fields;
/* In JTAG we scan out the previous value's output when performing a
* scan. This is a pain for users, so we just provide them the
* illusion of not having to do this by eliding all but the last NOP.
* */
enum riscv_scan_type last_scan;
/* The read keys. */
size_t *read_keys;
size_t read_keys_used;
};
/* Allocates (or frees) a new scan set. "scans" is the maximum number of JTAG
* scans that can be issued to this object, and idle is the number of JTAG idle
* cycles between every real scan. */
struct riscv_batch *riscv_batch_alloc(struct target *target, size_t scans, size_t idle);
void riscv_batch_free(struct riscv_batch *batch);
/* Checks to see if this batch is full. */
bool riscv_batch_full(struct riscv_batch *batch);
/* Executes this scan batch. */
void riscv_batch_run(struct riscv_batch *batch);
/* Adds a DMI write to this batch. */
void riscv_batch_add_dmi_write(struct riscv_batch *batch, unsigned address, uint64_t data);
/* DMI reads must be handled in two parts: the first one schedules a read and
* provides a key, the second one actually obtains the value of that read .*/
size_t riscv_batch_add_dmi_read(struct riscv_batch *batch, unsigned address);
uint64_t riscv_batch_get_dmi_read(struct riscv_batch *batch, size_t key);
/* Scans in a NOP. */
void riscv_batch_add_nop(struct riscv_batch *batch);
#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);
}

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

@ -0,0 +1,491 @@
#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_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%p", 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", (int)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]", (int)i, (long)p->debug_buffer[i], (long)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]", (int)i, (long)p->debug_buffer[i], (long)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%p: debug_buffer[%02x] = DASM(0x%08lx)", p, (int)i, (long)p->debug_buffer[i]);
if (i <= p->instruction_count || i >= riscv_debug_buffer_size(p->target) - p->data_count)
riscv_write_debug_buffer(t, i, p->debug_buffer[i]);
}
if (riscv_execute_debug_buffer(t) != ERROR_OK) {
LOG_DEBUG("Unable to execute program 0x%p", p);
return ERROR_FAIL;
}
for (size_t i = 0; i < riscv_debug_buffer_size(p->target); ++i)
if (i >= riscv_debug_buffer_size(p->target) - p->data_count)
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", (int)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", (int)bytes);
return RISCV_PROGRAM_ALLOC_FAIL;
}
LOG_DEBUG("allocated %d bytes at 0x%08lx", (int)bytes, (long)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 ((size_t)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 ((size_t)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_fsd(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr)
{
assert(d >= GDB_REGNO_FPR0);
assert(d <= GDB_REGNO_FPR31);
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, fsd(d - GDB_REGNO_FPR0, 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_fld(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr)
{
assert(d >= GDB_REGNO_FPR0);
assert(d <= GDB_REGNO_FPR31);
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, fld(d - GDB_REGNO_FPR0, t, riscv_program_gal(p, addr))) != ERROR_OK)
return ERROR_FAIL;
return ERROR_OK;
}
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%p)", (int)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", (int)p->instruction_count);
LOG_DEBUG(" data_count =%d", (int)p->data_count);
LOG_DEBUG(" buffer size =%d", (int)riscv_debug_buffer_size(p->target));
return ERROR_FAIL;
}
LOG_DEBUG("PROGBUF[%d] = DASM(0x%08x) [0x%08x]", (int)p->instruction_count, i, i);
p->debug_buffer[p->instruction_count] = i;
p->instruction_count++;
return ERROR_OK;
}

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

@ -0,0 +1,142 @@
#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);
int riscv_program_load(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);
int riscv_program_fsd(struct riscv_program *p, enum gdb_regno s, riscv_addr_t addr);
int riscv_program_fld(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr);
/* 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);
/* Addressing functions. */
riscv_addr_t riscv_program_gah(struct riscv_program *p, riscv_addr_t addr);
#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
@ -333,7 +335,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);
@ -379,7 +381,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:
@ -398,6 +406,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];
}
@ -477,7 +486,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);
@ -494,12 +503,14 @@ static int riscv_run_algorithm(struct target *target, int num_mem_params,
int64_t now = timeval_ms();
if (now - start > timeout_ms) {
LOG_ERROR("Algorithm timed out after %d ms.", timeout_ms);
LOG_ERROR(" now = 0x%08x", now);
LOG_ERROR(" start = 0x%08x", start);
riscv_halt(target);
riscv_poll(target);
riscv_openocd_poll(target);
return ERROR_TARGET_TIMEOUT;
}
int result = riscv_poll(target);
int result = riscv_openocd_poll(target);
if (result != ERROR_OK) {
return result;
}
@ -517,12 +528,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;
}
@ -530,7 +541,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;
}
@ -575,7 +586,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,
@ -602,3 +613,481 @@ 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);
riscv_set_current_hartid(target, hartid);
LOG_DEBUG("polling hart %d, target->state=%d (TARGET_HALTED=%d)", hartid, target->state, TARGET_HALTED);
/* If OpenOCD this we're running but this hart is halted then it's time
* to raise an event. */
if (target->state != TARGET_HALTED && riscv_is_halted(target)) {
LOG_DEBUG(" triggered a halt");
r->on_halt(target);
return 1;
}
return 0;
}
/*** OpenOCD Interface ***/
int riscv_openocd_poll(struct target *target)
{
LOG_DEBUG("polling all harts");
int triggered_hart = -1;
if (riscv_rtos_enabled(target)) {
/* Check every hart for an event. */
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 just halted, target->state=%d", target->state);
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);
} else {
if (riscv_poll_hart(target, riscv_current_hartid(target)) == 0)
return ERROR_OK;
triggered_hart = riscv_current_hartid(target);
LOG_DEBUG(" hart %d halted", triggered_hart);
}
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;
}
if (riscv_rtos_enabled(target)) {
target->rtos->current_threadid = triggered_hart + 1;
target->rtos->current_thread = triggered_hart + 1;
}
target->state = TARGET_HALTED;
target_call_event_callbacks(target, TARGET_EVENT_HALTED);
return ERROR_OK;
}
int riscv_openocd_halt(struct target *target)
{
LOG_DEBUG("halting all harts");
int out = riscv_halt_all_harts(target);
if (out != ERROR_OK) {
LOG_ERROR("Unable to halt all harts");
return out;
}
register_cache_invalidate(target->reg_cache);
target->state = TARGET_HALTED;
target->debug_reason = DBG_REASON_DBGRQ;
target_call_event_callbacks(target, TARGET_EVENT_HALTED);
return out;
}
int riscv_openocd_resume(
struct target *target,
int current,
uint32_t address,
int handle_breakpoints,
int debug_execution
) {
LOG_DEBUG("resuming all harts");
if (!current) {
riscv_set_register(target, GDB_REGNO_PC, address);
}
int out = riscv_resume_all_harts(target);
if (out != ERROR_OK) {
LOG_ERROR("unable to resume all harts");
return out;
}
register_cache_invalidate(target->reg_cache);
target->state = TARGET_RUNNING;
target_call_event_callbacks(target, TARGET_EVENT_RESUMED);
return out;
}
int riscv_openocd_step(
struct target *target,
int current,
uint32_t address,
int handle_breakpoints
) {
LOG_DEBUG("stepping rtos hart");
if (!current) {
riscv_set_register(target, GDB_REGNO_PC, address);
}
int out = riscv_step_rtos_hart(target);
if (out != ERROR_OK) {
LOG_ERROR("unable to step rtos hart");
return out;
}
register_cache_invalidate(target->reg_cache);
target->state = TARGET_RUNNING;
target_call_event_callbacks(target, TARGET_EVENT_RESUMED);
target->state = TARGET_HALTED;
target->debug_reason = DBG_REASON_SINGLESTEP;
target_call_event_callbacks(target, TARGET_EVENT_HALTED);
return out;
}
/*** RISC-V Interface ***/
void riscv_info_init(riscv_info_t *r)
{
memset(r, 0, sizeof(*r));
r->dtm_version = 1;
r->registers_initialized = false;
for (size_t h = 0; h < RISCV_MAX_HARTS; ++h) {
r->xlen[h] = -1;
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 (riscv_is_halted(target)) {
LOG_DEBUG(" hart %d requested halt, but was already halted", hartid);
return ERROR_OK;
}
r->halt_current_hart(target);
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 (!riscv_is_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);
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(riscv_is_halted(target));
r->on_step(target);
r->step_current_hart(target);
r->on_halt(target);
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];
}
bool riscv_rtos_enabled(const struct target *target)
{
return target->rtos != NULL;
}
void riscv_set_current_hartid(struct target *target, int hartid)
{
RISCV_INFO(r);
r->current_hartid = hartid;
assert(riscv_rtos_enabled(target) || target->coreid == hartid);
int previous_hartid = riscv_current_hartid(target);
if (riscv_rtos_enabled(target))
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;
/* Avoid invalidating the register cache all the time. */
if (r->registers_initialized && (!riscv_rtos_enabled(target) || (previous_hartid == hartid)) && target->reg_cache->reg_list[GDB_REGNO_XPR0].size == (long)riscv_xlen(target)) {
LOG_DEBUG("registers already initialized, skipping");
return;
} else
LOG_DEBUG("Initializing registers: xlen=%d", riscv_xlen(target));
/* 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;
}
}
r->registers_initialized = true;
}
int riscv_current_hartid(const struct target *target)
{
RISCV_INFO(r);
if (riscv_rtos_enabled(target))
return r->current_hartid;
else
return target->coreid;
}
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);
}
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);
}
riscv_addr_t riscv_read_debug_buffer_x(struct target *target, int index)
{
riscv_addr_t out = 0;
switch (riscv_xlen(target)) {
case 64:
out |= (uint64_t)riscv_read_debug_buffer(target, index + 1) << 32;
case 32:
out |= riscv_read_debug_buffer(target, index + 0) << 0;
break;
default:
LOG_ERROR("unsupported XLEN %d", riscv_xlen(target));
abort();
}
return out;
}
int riscv_execute_debug_buffer(struct target *target)
{
RISCV_INFO(r);
return r->execute_debug_buffer(target);
}
void riscv_fill_dmi_write_u64(struct target *target, char *buf, int a, uint64_t d)
{
RISCV_INFO(r);
r->fill_dmi_write_u64(target, buf, a, d);
}
void riscv_fill_dmi_read_u64(struct target *target, char *buf, int a)
{
RISCV_INFO(r);
r->fill_dmi_read_u64(target, buf, a);
}
void riscv_fill_dmi_nop_u64(struct target *target, char *buf)
{
RISCV_INFO(r);
r->fill_dmi_nop_u64(target, buf);
}
int riscv_dmi_write_u64_bits(struct target *target)
{
RISCV_INFO(r);
return r->dmi_write_u64_bits(target);
}

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,87 @@ 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_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;
/* 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 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];
/* This avoids invalidating the register cache too often. */
bool registers_initialized;
/* 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);
int (*execute_debug_buffer)(struct target *target);
int (*dmi_write_u64_bits)(struct target *target);
void (*fill_dmi_write_u64)(struct target *target, char *buf, int a, uint64_t d);
void (*fill_dmi_read_u64)(struct target *target, char *buf, int a);
void (*fill_dmi_nop_u64)(struct target *target, char *buf);
} 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 +106,103 @@ 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);
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. */
bool riscv_is_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);
riscv_addr_t riscv_read_debug_buffer_x(struct target *target, int index);
int riscv_write_debug_buffer(struct target *target, int index, riscv_insn_t insn);
int riscv_write_debug_buffer_x(struct target *target, int index, riscv_addr_t data);
int riscv_execute_debug_buffer(struct target *target);
void riscv_fill_dmi_nop_u64(struct target *target, char *buf);
void riscv_fill_dmi_write_u64(struct target *target, char *buf, int a, uint64_t d);
void riscv_fill_dmi_read_u64(struct target *target, char *buf, int a);
int riscv_dmi_write_u64_bits(struct target *target);
#endif

View File

@ -1088,7 +1088,7 @@ int target_add_breakpoint(struct target *target,
struct breakpoint *breakpoint)
{
if ((target->state != TARGET_HALTED) && (breakpoint->type != BKPT_HARD)) {
LOG_WARNING("target %s is not halted", target_name(target));
LOG_WARNING("target %s is not halted (add breakpoint)", target_name(target));
return ERROR_TARGET_NOT_HALTED;
}
return target->type->add_breakpoint(target, breakpoint);
@ -1098,7 +1098,7 @@ int target_add_context_breakpoint(struct target *target,
struct breakpoint *breakpoint)
{
if (target->state != TARGET_HALTED) {
LOG_WARNING("target %s is not halted", target_name(target));
LOG_WARNING("target %s is not halted (add context breakpoint)", target_name(target));
return ERROR_TARGET_NOT_HALTED;
}
return target->type->add_context_breakpoint(target, breakpoint);
@ -1108,7 +1108,7 @@ int target_add_hybrid_breakpoint(struct target *target,
struct breakpoint *breakpoint)
{
if (target->state != TARGET_HALTED) {
LOG_WARNING("target %s is not halted", target_name(target));
LOG_WARNING("target %s is not halted (add hybrid breakpoint)", target_name(target));
return ERROR_TARGET_NOT_HALTED;
}
return target->type->add_hybrid_breakpoint(target, breakpoint);
@ -1124,7 +1124,7 @@ int target_add_watchpoint(struct target *target,
struct watchpoint *watchpoint)
{
if (target->state != TARGET_HALTED) {
LOG_WARNING("target %s is not halted", target_name(target));
LOG_WARNING("target %s is not halted (add watchpoint)", target_name(target));
return ERROR_TARGET_NOT_HALTED;
}
return target->type->add_watchpoint(target, watchpoint);
@ -1138,7 +1138,7 @@ int target_hit_watchpoint(struct target *target,
struct watchpoint **hit_watchpoint)
{
if (target->state != TARGET_HALTED) {
LOG_WARNING("target %s is not halted", target->cmd_name);
LOG_WARNING("target %s is not halted (hit watchpoint)", target->cmd_name);
return ERROR_TARGET_NOT_HALTED;
}
@ -1167,7 +1167,7 @@ int target_step(struct target *target,
int target_get_gdb_fileio_info(struct target *target, struct gdb_fileio_info *fileio_info)
{
if (target->state != TARGET_HALTED) {
LOG_WARNING("target %s is not halted", target->cmd_name);
LOG_WARNING("target %s is not halted (gdb fileio)", target->cmd_name);
return ERROR_TARGET_NOT_HALTED;
}
return target->type->get_gdb_fileio_info(target, fileio_info);
@ -1176,7 +1176,7 @@ int target_get_gdb_fileio_info(struct target *target, struct gdb_fileio_info *fi
int target_gdb_fileio_end(struct target *target, int retcode, int fileio_errno, bool ctrl_c)
{
if (target->state != TARGET_HALTED) {
LOG_WARNING("target %s is not halted", target->cmd_name);
LOG_WARNING("target %s is not halted (gdb fileio end)", target->cmd_name);
return ERROR_TARGET_NOT_HALTED;
}
return target->type->gdb_fileio_end(target, retcode, fileio_errno, ctrl_c);
@ -1186,7 +1186,7 @@ int target_profiling(struct target *target, uint32_t *samples,
uint32_t max_num_samples, uint32_t *num_samples, uint32_t seconds)
{
if (target->state != TARGET_HALTED) {
LOG_WARNING("target %s is not halted", target->cmd_name);
LOG_WARNING("target %s is not halted (profiling)", target->cmd_name);
return ERROR_TARGET_NOT_HALTED;
}
return target->type->profiling(target, samples, max_num_samples,