aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/flash/nor/fespi.c36
-rw-r--r--src/rtos/Makefile.am4
-rw-r--r--src/rtos/riscv_debug.c315
-rw-r--r--src/rtos/riscv_debug.h9
-rw-r--r--src/rtos/rtos.c5
-rw-r--r--src/rtos/rtos.h1
-rw-r--r--src/server/gdb_server.c15
-rw-r--r--src/server/gdb_server.h3
-rw-r--r--src/target/Makefile.am4
-rw-r--r--src/target/riscv/asm.h36
-rw-r--r--src/target/riscv/batch.c156
-rw-r--r--src/target/riscv/batch.h64
-rw-r--r--src/target/riscv/debug_defines.h197
-rw-r--r--src/target/riscv/gdb_regs.h28
-rw-r--r--src/target/riscv/opcodes.h24
-rw-r--r--src/target/riscv/program.c491
-rw-r--r--src/target/riscv/program.h142
-rw-r--r--src/target/riscv/riscv-011.c67
-rw-r--r--src/target/riscv/riscv-013.c2322
-rw-r--r--src/target/riscv/riscv.c505
-rw-r--r--src/target/riscv/riscv.h221
-rw-r--r--src/target/target.c16
22 files changed, 3210 insertions, 1451 deletions
diff --git a/src/flash/nor/fespi.c b/src/flash/nor/fespi.c
index 2437bf3..1320043 100644
--- a/src/flash/nor/fespi.c
+++ b/src/flash/nor/fespi.c
@@ -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);
diff --git a/src/rtos/Makefile.am b/src/rtos/Makefile.am
index fdca394..a7dab00 100644
--- a/src/rtos/Makefile.am
+++ b/src/rtos/Makefile.am
@@ -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
diff --git a/src/rtos/riscv_debug.c b/src/rtos/riscv_debug.c
new file mode 100644
index 0000000..0be869e
--- /dev/null
+++ b/src/rtos/riscv_debug.c
@@ -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,
+};
diff --git a/src/rtos/riscv_debug.h b/src/rtos/riscv_debug.h
new file mode 100644
index 0000000..bcc7411
--- /dev/null
+++ b/src/rtos/riscv_debug.h
@@ -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
diff --git a/src/rtos/rtos.c b/src/rtos/rtos.c
index 4c99ad2..9bf218e 100644
--- a/src/rtos/rtos.c
+++ b/src/rtos/rtos.c
@@ -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);
diff --git a/src/rtos/rtos.h b/src/rtos/rtos.h
index 70c1193..70cec87 100644
--- a/src/rtos/rtos.h
+++ b/src/rtos/rtos.h
@@ -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;
};
diff --git a/src/server/gdb_server.c b/src/server/gdb_server.c
index 2387496..4828c21 100644
--- a/src/server/gdb_server.c
+++ b/src/server/gdb_server.c
@@ -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;
+}
diff --git a/src/server/gdb_server.h b/src/server/gdb_server.h
index 2b4ac4e..1dc30e0 100644
--- a/src/server/gdb_server.h
+++ b/src/server/gdb_server.h
@@ -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)
diff --git a/src/target/Makefile.am b/src/target/Makefile.am
index 44299fc..ba15300 100644
--- a/src/target/Makefile.am
+++ b/src/target/Makefile.am
@@ -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 \
diff --git a/src/target/riscv/asm.h b/src/target/riscv/asm.h
new file mode 100644
index 0000000..051e0f9
--- /dev/null
+++ b/src/target/riscv/asm.h
@@ -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
diff --git a/src/target/riscv/batch.c b/src/target/riscv/batch.c
new file mode 100644
index 0000000..d48b34a
--- /dev/null
+++ b/src/target/riscv/batch.c
@@ -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);
+ }
+}
diff --git a/src/target/riscv/batch.h b/src/target/riscv/batch.h
new file mode 100644
index 0000000..776d93a
--- /dev/null
+++ b/src/target/riscv/batch.h
@@ -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
diff --git a/src/target/riscv/debug_defines.h b/src/target/riscv/debug_defines.h
index ec535db..2fb541b 100644
--- a/src/target/riscv/debug_defines.h
+++ b/src/target/riscv/debug_defines.h
@@ -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
diff --git a/src/target/riscv/gdb_regs.h b/src/target/riscv/gdb_regs.h
new file mode 100644
index 0000000..e366f7d
--- /dev/null
+++ b/src/target/riscv/gdb_regs.h
@@ -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
diff --git a/src/target/riscv/opcodes.h b/src/target/riscv/opcodes.h
index 5a47e08..3e1f8a2 100644
--- a/src/target/riscv/opcodes.h
+++ b/src/target/riscv/opcodes.h
@@ -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);
+}
diff --git a/src/target/riscv/program.c b/src/target/riscv/program.c
new file mode 100644
index 0000000..1487316
--- /dev/null
+++ b/src/target/riscv/program.c
@@ -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;
+}
diff --git a/src/target/riscv/program.h b/src/target/riscv/program.h
new file mode 100644
index 0000000..1efdb12
--- /dev/null
+++ b/src/target/riscv/program.h
@@ -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
diff --git a/src/target/riscv/riscv-011.c b/src/target/riscv/riscv-011.c
index e940d20..10612e2 100644
--- a/src/target/riscv/riscv-011.c
+++ b/src/target/riscv/riscv-011.c
@@ -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)
diff --git a/src/target/riscv/riscv-013.c b/src/target/riscv/riscv-013.c
index 0957bdc..72e1e2c 100644
--- a/src/target/riscv/riscv-013.c
+++ b/src/target/riscv/riscv-013.c
@@ -21,6 +21,44 @@
#include "helper/time_support.h"
#include "riscv.h"
#include "debug_defines.h"
+#include "rtos/rtos.h"
+#include "program.h"
+#include "asm.h"
+#include "batch.h"
+
+#define DMI_DATA1 (DMI_DATA0 + 1)
+
+static void riscv013_on_step_or_resume(struct target *target, bool step);
+static void riscv013_step_or_resume_current_hart(struct target *target, bool step);
+static riscv_addr_t riscv013_progbuf_addr(struct target *target);
+static riscv_addr_t riscv013_progbuf_size(struct target *target);
+static riscv_addr_t riscv013_data_size(struct target *target);
+static riscv_addr_t riscv013_data_addr(struct target *target);
+static void riscv013_set_autoexec(struct target *target, int offset, bool enabled);
+static int riscv013_debug_buffer_register(struct target *target, riscv_addr_t addr);
+static void riscv013_clear_abstract_error(struct target *target);
+
+/* Implementations of the functions in riscv_info_t. */
+static riscv_reg_t riscv013_get_register(struct target *target, int hartid, int regid);
+static void riscv013_set_register(struct target *target, int hartid, int regid, uint64_t value);
+static void riscv013_select_current_hart(struct target *target);
+static void riscv013_halt_current_hart(struct target *target);
+static void riscv013_resume_current_hart(struct target *target);
+static void riscv013_step_current_hart(struct target *target);
+static void riscv013_on_halt(struct target *target);
+static void riscv013_on_step(struct target *target);
+static void riscv013_on_resume(struct target *target);
+static bool riscv013_is_halted(struct target *target);
+static enum riscv_halt_reason riscv013_halt_reason(struct target *target);
+static void riscv013_debug_buffer_enter(struct target *target, struct riscv_program *p);
+static void riscv013_debug_buffer_leave(struct target *target, struct riscv_program *p);
+static void riscv013_write_debug_buffer(struct target *target, int i, riscv_insn_t d);
+static riscv_insn_t riscv013_read_debug_buffer(struct target *target, int i);
+static int riscv013_execute_debug_buffer(struct target *target);
+static void riscv013_fill_dmi_write_u64(struct target *target, char *buf, int a, uint64_t d);
+static void riscv013_fill_dmi_read_u64(struct target *target, char *buf, int a);
+static int riscv013_dmi_write_u64_bits(struct target *target);
+static void riscv013_fill_dmi_nop_u64(struct target *target, char *buf);
/**
* Since almost everything can be accomplish by scanning the dbus register, all
@@ -40,6 +78,8 @@
#define CSR_DCSR_CAUSE_STEP 4
#define CSR_DCSR_CAUSE_HALT 5
+#define RISCV013_INFO(r) riscv013_info_t *r = get_info(target)
+
/*** JTAG registers. ***/
typedef enum {
@@ -78,27 +118,6 @@ typedef enum slot {
#define WALL_CLOCK_TIMEOUT 2
-// gdb's register list is defined in riscv_gdb_reg_names gdb/riscv-tdep.c in
-// its source tree. We must interpret the numbers the same here.
-enum {
- REG_XPR0 = 0,
- REG_XPR31 = 31,
- REG_PC = 32,
- REG_FPR0 = 33,
- REG_FPR31 = 64,
- REG_CSR0 = 65,
- REG_TSELECT = CSR_TSELECT + REG_CSR0,
- REG_TDATA1 = CSR_TDATA1 + REG_CSR0,
- REG_TDATA2 = CSR_TDATA2 + REG_CSR0,
- REG_MISA = CSR_MISA + REG_CSR0,
- REG_DPC = CSR_DPC + REG_CSR0,
- REG_DCSR = CSR_DCSR + REG_CSR0,
- REG_MSTATUS = CSR_MSTATUS + REG_CSR0,
- REG_CSR4095 = 4160,
- REG_PRIV = 4161,
- REG_COUNT
-};
-
#define MAX_HWBPS 16
struct trigger {
@@ -125,8 +144,6 @@ typedef struct {
unsigned progsize;
/* Number of Program Buffer registers. */
/* Number of words in Debug RAM. */
- uint64_t dcsr;
- uint64_t dpc;
uint64_t misa;
uint64_t tselect;
bool tselect_dirty;
@@ -145,8 +162,6 @@ typedef struct {
// unique_id of the breakpoint/watchpoint that is using it.
int trigger_unique_id[MAX_HWBPS];
- unsigned int trigger_count;
-
// Number of run-test/idle cycles the target requests we do after each dbus
// access.
unsigned int dtmcontrol_idle;
@@ -164,13 +179,10 @@ typedef struct {
unsigned int ac_busy_delay;
bool need_strict_step;
- bool never_halted;
-} riscv013_info_t;
-typedef struct {
- bool haltnot;
- bool interrupt;
-} bits_t;
+ // Some memoized values
+ int progbuf_size, progbuf_addr, data_addr, data_size;
+} riscv013_info_t;
static void dump_field(const struct scan_field *field)
{
@@ -210,106 +222,8 @@ static riscv013_info_t *get_info(const struct target *target)
return (riscv013_info_t *) info->version_specific;
}
-/*** scans "class" ***/
-
-typedef struct {
- // Number of scans that space is reserved for.
- unsigned int scan_count;
- // Size reserved in memory for each scan, in bytes.
- unsigned int scan_size;
- unsigned int next_scan;
- uint8_t *in;
- uint8_t *out;
- struct scan_field *field;
- const struct target *target;
-} scans_t;
-
-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;
- else
- scans->scan_size = 2 + 128 / 8;
- scans->next_scan = 0;
- scans->in = calloc(scans->scan_size, scans->scan_count);
- scans->out = calloc(scans->scan_size, scans->scan_count);
- scans->field = calloc(scans->scan_count, sizeof(struct scan_field));
- scans->target = target;
- return scans;
-}
-
-static scans_t *scans_delete(scans_t *scans)
-{
- assert(scans);
- free(scans->field);
- free(scans->out);
- free(scans->in);
- free(scans);
- return NULL;
-}
-
-static void scans_dump(scans_t *scans)
-{
- for (unsigned int i = 0; i < scans->next_scan; i++) {
- dump_field(&scans->field[i]);
- }
-}
-
-static int scans_execute(scans_t *scans)
-{
- int retval = jtag_execute_queue();
- if (retval != ERROR_OK) {
- LOG_ERROR("failed jtag scan: %d", retval);
- return retval;
- }
-
- scans_dump(scans);
-
- return ERROR_OK;
-}
-
-static void scans_add_dmi_write(scans_t *scans, unsigned address,
- uint32_t value, bool exec)
-{
- riscv013_info_t *info = get_info(scans->target);
- assert(scans->next_scan < scans->scan_count);
- const unsigned int i = scans->next_scan;
- int data_offset = scans->scan_size * i;
- struct scan_field *field = scans->field + i;
-
- uint8_t *out = scans->out + data_offset;
- field->num_bits = info->abits + DTM_DMI_OP_LENGTH + DTM_DMI_DATA_LENGTH;
- // We gain a lot of speed in remote bitbang by not looking at the return
- // value.
- field->in_value = NULL;
- field->out_value = out;
-
- buf_set_u64(out, DTM_DMI_OP_OFFSET, DTM_DMI_OP_LENGTH, DMI_OP_WRITE);
- buf_set_u64(out, DTM_DMI_DATA_OFFSET, DTM_DMI_DATA_LENGTH, value);
- buf_set_u64(out, DTM_DMI_ADDRESS_OFFSET, info->abits, address);
-
- /* Assume dbus is already selected. */
- jtag_add_dr_scan(scans->target->tap, 1, field, TAP_IDLE);
-
- int idle_count = info->dtmcontrol_idle + info->dmi_busy_delay;
- if (exec)
- idle_count += info->ac_busy_delay;
-
- if (idle_count) {
- jtag_add_runtest(idle_count, TAP_IDLE);
- }
-
- scans->next_scan++;
-}
-
-/*** end of scans class ***/
/*** Necessary prototypes. ***/
-static int poll_target(struct target *target, bool announce);
-static int riscv013_poll(struct target *target);
static int register_get(struct reg *reg);
/*** Utility functions. ***/
@@ -372,19 +286,21 @@ static uint32_t dtmcontrol_scan(struct target *target, uint32_t out)
return in;
}
-static void increase_ac_busy_delay(struct target *target)
+static void increase_dmi_busy_delay(struct target *target)
{
riscv013_info_t *info = get_info(target);
- info->ac_busy_delay += info->ac_busy_delay / 10 + 1;
+ info->dmi_busy_delay += info->dmi_busy_delay / 10 + 1;
LOG_INFO("dtmcontrol_idle=%d, dmi_busy_delay=%d, ac_busy_delay=%d",
info->dtmcontrol_idle, info->dmi_busy_delay,
info->ac_busy_delay);
+
+ dtmcontrol_scan(target, DTM_DTMCS_DMIRESET);
}
-static void increase_dmi_busy_delay(struct target *target)
+static void increase_ac_busy_delay(struct target *target)
{
riscv013_info_t *info = get_info(target);
- info->dmi_busy_delay += info->dmi_busy_delay / 10 + 1;
+ info->ac_busy_delay += info->ac_busy_delay / 10 + 1;
LOG_INFO("dtmcontrol_idle=%d, dmi_busy_delay=%d, ac_busy_delay=%d",
info->dtmcontrol_idle, info->dmi_busy_delay,
info->ac_busy_delay);
@@ -421,7 +337,7 @@ static dmi_status_t dmi_scan(struct target *target, uint16_t *address_in,
/* Assume dbus is already selected. */
jtag_add_dr_scan(target->tap, 1, &field, TAP_IDLE);
- int idle_count = info->dtmcontrol_idle + info->dmi_busy_delay;
+ int idle_count = info->dmi_busy_delay;
if (exec)
idle_count += info->ac_busy_delay;
@@ -450,27 +366,56 @@ static dmi_status_t dmi_scan(struct target *target, uint16_t *address_in,
static uint64_t dmi_read(struct target *target, uint16_t address)
{
+ select_dmi(target);
+
uint64_t value;
dmi_status_t status;
uint16_t address_in;
unsigned i = 0;
+
+ // This first loop ensures that the read request was actually sent
+ // to the target. Note that if for some reason this stays busy,
+ // it is actually due to the Previous dmi_read or dmi_write.
for (i = 0; i < 256; i++) {
status = dmi_scan(target, NULL, NULL, DMI_OP_READ, address, 0,
false);
if (status == DMI_STATUS_BUSY) {
increase_dmi_busy_delay(target);
+ } else if (status == DMI_STATUS_SUCCESS) {
+ break;
} else {
+ LOG_ERROR("failed read from 0x%x, status=%d\n", address, status);
break;
}
}
- status = dmi_scan(target, &address_in, &value, DMI_OP_NOP, address, 0,
- false);
+ if (status != DMI_STATUS_SUCCESS) {
+ LOG_ERROR("Failed read from 0x%x; value=0x%" PRIx64 ", status=%d\n",
+ address, value, status);
+ abort();
+ }
+
+ // This second loop ensures that we got the read
+ // data back. Note that NOP can result in a 'busy' result as well, but
+ // that would be noticed on the next DMI access we do.
+ for (i = 0; i < 256; i++) {
+ status = dmi_scan(target, &address_in, &value, DMI_OP_NOP, address, 0,
+ false);
+ if (status == DMI_STATUS_BUSY) {
+ increase_dmi_busy_delay(target);
+ } else if (status == DMI_STATUS_SUCCESS) {
+ break;
+ } else {
+ LOG_ERROR("failed read (NOP) at 0x%x, status=%d\n", address, status);
+ break;
+ }
+ }
if (status != DMI_STATUS_SUCCESS) {
- LOG_ERROR("failed read from 0x%x; value=0x%" PRIx64 ", status=%d\n",
+ LOG_ERROR("Failed read (NOP) from 0x%x; value=0x%" PRIx64 ", status=%d\n",
address, value, status);
+ abort();
}
return value;
@@ -478,33 +423,47 @@ static uint64_t dmi_read(struct target *target, uint16_t address)
static void dmi_write(struct target *target, uint16_t address, uint64_t value)
{
+ select_dmi(target);
dmi_status_t status = DMI_STATUS_BUSY;
unsigned i = 0;
- while (status == DMI_STATUS_BUSY && i++ < 256) {
- dmi_scan(target, NULL, NULL, DMI_OP_WRITE, address, value,
- address == DMI_COMMAND);
- status = dmi_scan(target, NULL, NULL, DMI_OP_NOP, 0, 0, false);
- if (status == DMI_STATUS_BUSY) {
- increase_dmi_busy_delay(target);
- }
+
+ // The first loop ensures that we successfully sent the write request.
+ for (i = 0; i < 256; i++) {
+ status = dmi_scan(target, NULL, NULL, DMI_OP_WRITE, address, value,
+ address == DMI_COMMAND);
+ if (status == DMI_STATUS_BUSY) {
+ increase_dmi_busy_delay(target);
+ } else if (status == DMI_STATUS_SUCCESS) {
+ break;
+ } else {
+ LOG_ERROR("failed write to 0x%x, status=%d\n", address, status);
+ break;
+ }
+ }
+
+ if (status != DMI_STATUS_SUCCESS) {
+ LOG_ERROR("Failed write to 0x%x;, status=%d\n",
+ address, status);
+ abort();
+ }
+
+ // The second loop isn't strictly necessary, but would ensure that
+ // the write is complete/ has no non-busy errors before returning from this function.
+ for (i = 0; i < 256; i++) {
+ status = dmi_scan(target, NULL, NULL, DMI_OP_NOP, address, 0,
+ false);
+ if (status == DMI_STATUS_BUSY) {
+ increase_dmi_busy_delay(target);
+ } else if (status == DMI_STATUS_SUCCESS) {
+ break;
+ } else {
+ LOG_ERROR("failed write (NOP) at 0x%x, status=%d\n", address, status);
+ break;
+ }
}
if (status != DMI_STATUS_SUCCESS) {
- LOG_ERROR("failed to write 0x%" PRIx64 " to 0x%x; status=%d\n", value, address, status);
- }
-}
-
-/** Convert register number (internal OpenOCD number) to the number expected by
- * the abstract command interface. */
-static unsigned reg_number_to_no(unsigned reg_num)
-{
- if (reg_num <= REG_XPR31) {
- return reg_num + 0x1000 - REG_XPR0;
- } else if (reg_num >= REG_CSR0 && reg_num <= REG_CSR4095) {
- return reg_num - REG_CSR0;
- } else if (reg_num >= REG_FPR0 && reg_num <= REG_FPR31) {
- return reg_num + 0x1020 - REG_FPR0;
- } else {
- return ~0;
+ LOG_ERROR("failed to write (NOP) 0x%" PRIx64 " to 0x%x; status=%d\n", value, address, status);
+ abort();
}
}
@@ -530,10 +489,28 @@ static int wait_for_idle(struct target *target, uint32_t *abstractcs)
time_t start = time(NULL);
while (1) {
*abstractcs = dmi_read(target, DMI_ABSTRACTCS);
+
if (get_field(*abstractcs, DMI_ABSTRACTCS_BUSY) == 0) {
return ERROR_OK;
}
+
if (time(NULL) - start > WALL_CLOCK_TIMEOUT) {
+ if (get_field(*abstractcs, DMI_ABSTRACTCS_CMDERR) != CMDERR_NONE) {
+ const char *errors[8] = {
+ "none",
+ "busy",
+ "not supported",
+ "exception",
+ "halt/resume",
+ "reserved",
+ "reserved",
+ "other" };
+
+ LOG_ERROR("Abstract command ended in error '%s' (abstractcs=0x%x)",
+ errors[get_field(*abstractcs, DMI_ABSTRACTCS_CMDERR)],
+ *abstractcs);
+ }
+
LOG_ERROR("Timed out waiting for busy to go low. (abstractcs=0x%x)",
*abstractcs);
return ERROR_FAIL;
@@ -541,301 +518,82 @@ static int wait_for_idle(struct target *target, uint32_t *abstractcs)
}
}
-static int execute_abstract_command(struct target *target, uint32_t command)
-{
- dmi_write(target, DMI_COMMAND, command);
-
- uint32_t abstractcs;
- if (wait_for_idle(target, &abstractcs) != ERROR_OK)
- return ERROR_FAIL;
+static int register_read_direct(struct target *target, uint64_t *value, uint32_t number);
- if (get_field(abstractcs, DMI_ABSTRACTCS_CMDERR) != CMDERR_NONE) {
- const char *errors[8] = {
- "none",
- "busy",
- "not supported",
- "exception",
- "halt/resume",
- "reserved",
- "reserved",
- "other" };
- LOG_DEBUG("Abstract command 0x%x ended in error '%s' (abstractcs=0x%x)",
- command, errors[get_field(abstractcs, DMI_ABSTRACTCS_CMDERR)],
- abstractcs);
- // Clear the error.
- dmi_write(target, DMI_ABSTRACTCS, DMI_ABSTRACTCS_CMDERR);
- return ERROR_FAIL;
- }
-
- return ERROR_OK;
-}
-
-/*** program "class" ***/
-/* This class allows a debug program to be built up piecemeal, and then be
- * executed. If necessary, the program is split up to fit in the program
- * buffer. */
-
-typedef struct {
- uint8_t code[12 * 4];
- unsigned length;
- bool write;
- unsigned regno;
- uint64_t write_value;
-} program_t;
-
-static program_t *program_new(void)
-{
- program_t *program = malloc(sizeof(program_t));
- if (program) {
- program->length = 0;
- // Default to read zero.
- program->write = false;
- program->regno = 0x1000;
- }
- return program;
-}
-
-static void program_delete(program_t *program)
-{
- free(program);
-}
-
-static void program_add32(program_t *program, uint32_t instruction)
-{
- assert(program->length + 4 < sizeof(program->code));
- program->code[program->length++] = instruction & 0xff;
- program->code[program->length++] = (instruction >> 8) & 0xff;
- program->code[program->length++] = (instruction >> 16) & 0xff;
- program->code[program->length++] = (instruction >> 24) & 0xff;
-}
-
-static void program_set_read(program_t *program, unsigned reg_num)
-{
- program->write = false;
- program->regno = reg_number_to_no(reg_num);
-}
-
-static void program_set_write(program_t *program, unsigned reg_num, uint64_t value)
-{
- program->write = true;
- program->regno = reg_number_to_no(reg_num);
- program->write_value = value;
-}
-
-/*** end of program class ***/
-
-static void write_program(struct target *target, const program_t *program)
-{
- riscv013_info_t *info = get_info(target);
-
- assert(program->length <= info->progsize * 4);
- for (unsigned i = 0; i < program->length; i += 4) {
- uint32_t value =
- program->code[i] |
- ((uint32_t) program->code[i+1] << 8) |
- ((uint32_t) program->code[i+2] << 16) |
- ((uint32_t) program->code[i+3] << 24);
- dmi_write(target, DMI_PROGBUF0 + i / 4, value);
- }
-}
-
-static int execute_program(struct target *target, const program_t *program)
+static int register_write_direct(struct target *target, unsigned number,
+ uint64_t value)
{
- write_program(target, program);
-
- uint32_t command = 0;
- if (program->write) {
- if (get_field(command, AC_ACCESS_REGISTER_SIZE) > 2) {
- dmi_write(target, DMI_DATA1, program->write_value >> 32);
- }
- dmi_write(target, DMI_DATA0, program->write_value);
- command |= AC_ACCESS_REGISTER_WRITE | AC_ACCESS_REGISTER_POSTEXEC;
+ struct riscv_program program;
+ riscv_program_init(&program, target);
+
+ riscv_addr_t input = riscv_program_alloc_d(&program);
+ riscv_program_write_ram(&program, input + 4, value >> 32);
+ riscv_program_write_ram(&program, input, value);
+
+ assert(GDB_REGNO_XPR0 == 0);
+ if (number <= GDB_REGNO_XPR31) {
+ riscv_program_lx(&program, number, input);
+ } else if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) {
+ riscv_program_fld(&program, number, input);
+ } else if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095) {
+ enum gdb_regno temp = riscv_program_gettemp(&program);
+ riscv_program_lx(&program, temp, input);
+ riscv_program_csrw(&program, temp, number);
} else {
- command |= AC_ACCESS_REGISTER_PREEXEC;
+ LOG_ERROR("Unsupported register (enum gdb_regno)(%d)", number);
+ abort();
}
- command |= abstract_register_size(xlen(target));
- command |= program->regno;
-
- return execute_abstract_command(target, command);
-}
-
-static int abstract_read_register(struct target *target,
- uint64_t *value,
- uint32_t reg_number,
- unsigned width)
-{
- uint32_t command = abstract_register_size(width);
- command |= reg_number_to_no(reg_number);
-
- int result = execute_abstract_command(target, command);
- if (result != ERROR_OK) {
- return result;
+ int exec_out = riscv_program_exec(&program, target);
+ if (exec_out != ERROR_OK) {
+ LOG_ERROR("Unable to execute program");
+ return exec_out;
}
- if (value) {
- *value = 0;
- switch (width) {
- case 128:
- LOG_ERROR("Ignoring top 64 bits from 128-bit register read.");
- case 64:
- *value |= ((uint64_t) dmi_read(target, DMI_DATA1)) << 32;
- case 32:
- *value |= dmi_read(target, DMI_DATA0);
- break;
- }
+ assert(GDB_REGNO_XPR0 == 0);
+ if (number <= GDB_REGNO_XPR31) {
+ uint64_t written_value;
+ register_read_direct(target, &written_value, number);
+ LOG_DEBUG("attempted to write 0x%016lx, actually wrote 0x%016lx", value, written_value);
+ assert(value == written_value);
}
return ERROR_OK;
}
-static int abstract_write_register(struct target *target,
- unsigned reg_number,
- unsigned width,
- uint64_t value)
-{
- uint32_t command = abstract_register_size(width);
-
- command |= reg_number_to_no(reg_number);
- command |= AC_ACCESS_REGISTER_WRITE;
-
- switch (width) {
- case 128:
- LOG_ERROR("Ignoring top 64 bits from 128-bit register write.");
- case 64:
- dmi_write(target, DMI_DATA1, value >> 32);
- case 32:
- dmi_write(target, DMI_DATA0, value);
- break;
- }
-
- int result = execute_abstract_command(target, command);
- if (result != ERROR_OK) {
- return result;
- }
-
- return ERROR_OK;
-}
-
-static int update_mstatus_actual(struct target *target)
-{
- struct reg *mstatus_reg = &target->reg_cache->reg_list[REG_MSTATUS];
- if (mstatus_reg->valid) {
- // We previously made it valid.
- return ERROR_OK;
- }
-
- LOG_DEBUG("Reading mstatus");
-
- // Force reading the register. In that process mstatus_actual will be
- // updated.
- return register_get(&target->reg_cache->reg_list[REG_MSTATUS]);
-}
-
-static int register_write_direct(struct target *target, unsigned number,
- uint64_t value)
-{
- riscv013_info_t *info = get_info(target);
- LOG_DEBUG("register 0x%x <- 0x%" PRIx64, number, value);
-
- if (number == REG_MSTATUS) {
- info->mstatus_actual = value;
- }
-
- int result = abstract_write_register(target, number, xlen(target), value);
- if (result == ERROR_OK)
- return result;
-
- // Fall back to program buffer.
- if (number >= REG_FPR0 && number <= REG_FPR31) {
- result = update_mstatus_actual(target);
- if (result != ERROR_OK) {
- return result;
- }
- if ((info->mstatus_actual & MSTATUS_FS) == 0) {
- result = register_write_direct(target, REG_MSTATUS,
- set_field(info->mstatus_actual, MSTATUS_FS, 1));
- if (result != ERROR_OK)
- return result;
- }
-
- program_t *program = program_new();
- // TODO: Fully support D extension on RV32.
- if (supports_extension(target, 'D') && xlen(target) >= 64) {
- program_add32(program, fmv_d_x(number - REG_FPR0, S0));
- } else {
- program_add32(program, fmv_s_x(number - REG_FPR0, S0));
- }
- program_add32(program, ebreak());
- program_set_write(program, S0, value);
- result = execute_program(target, program);
- program_delete(program);
- } else if (number >= REG_CSR0 && number <= REG_CSR4095) {
- program_t *program = program_new();
- program_add32(program, csrw(S0, number - REG_CSR0));
- program_add32(program, ebreak());
- program_set_write(program, S0, value);
- result = execute_program(target, program);
- program_delete(program);
- } else {
- return result;
- }
-
- return result;
-}
-
/** Actually read registers from the target right now. */
static int register_read_direct(struct target *target, uint64_t *value, uint32_t number)
{
- riscv013_info_t *info = get_info(target);
- int result = abstract_read_register(target, value, number, xlen(target));
- if (result == ERROR_OK)
- return result;
-
- // Fall back to program buffer.
- if (number >= REG_FPR0 && number <= REG_FPR31) {
- result = update_mstatus_actual(target);
- if (result != ERROR_OK) {
- return result;
- }
- if ((info->mstatus_actual & MSTATUS_FS) == 0) {
- result = register_write_direct(target, REG_MSTATUS,
- set_field(info->mstatus_actual, MSTATUS_FS, 1));
- if (result != ERROR_OK)
- return result;
- }
- LOG_DEBUG("mstatus_actual=0x%lx", info->mstatus_actual);
-
- program_t *program = program_new();
- if (supports_extension(target, 'D') && xlen(target) >= 64) {
- program_add32(program, fmv_x_d(S0, number - REG_FPR0));
- } else {
- program_add32(program, fmv_x_s(S0, number - REG_FPR0));
- }
- program_add32(program, ebreak());
- program_set_read(program, S0);
- result = execute_program(target, program);
- program_delete(program);
- } else if (number >= REG_CSR0 && number <= REG_CSR4095) {
- program_t *program = program_new();
- program_add32(program, csrr(S0, number - REG_CSR0));
- program_add32(program, ebreak());
- program_set_read(program, S0);
- result = execute_program(target, program);
- program_delete(program);
+ struct riscv_program program;
+ riscv_program_init(&program, target);
+ riscv_addr_t output = riscv_program_alloc_d(&program);
+ riscv_program_write_ram(&program, output + 4, 0);
+ riscv_program_write_ram(&program, output, 0);
+
+ assert(GDB_REGNO_XPR0 == 0);
+ if (number <= GDB_REGNO_XPR31) {
+ riscv_program_sx(&program, number, output);
+ } else if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) {
+ riscv_program_fsd(&program, number, output);
+ } else if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095) {
+ enum gdb_regno temp = riscv_program_gettemp(&program);
+ riscv_program_csrr(&program, temp, number);
+ riscv_program_sx(&program, temp, output);
} else {
- return result;
+ LOG_ERROR("Unsupported register (enum gdb_regno)(%d)", number);
+ abort();
}
- if (result != ERROR_OK)
- return result;
-
- result = register_read_direct(target, value, S0);
- if (result != ERROR_OK)
- return result;
+ int exec_out = riscv_program_exec(&program, target);
+ if (exec_out != ERROR_OK) {
+ LOG_ERROR("Unable to execute program");
+ return exec_out;
+ }
+ *value = 0;
+ *value |= ((uint64_t)(riscv_program_read_ram(&program, output + 4))) << 32;
+ *value |= riscv_program_read_ram(&program, output);
LOG_DEBUG("register 0x%x = 0x%" PRIx64, number, *value);
-
return ERROR_OK;
}
@@ -844,7 +602,7 @@ static int maybe_read_tselect(struct target *target)
riscv013_info_t *info = get_info(target);
if (info->tselect_dirty) {
- int result = register_read_direct(target, &info->tselect, REG_TSELECT);
+ int result = register_read_direct(target, &info->tselect, GDB_REGNO_TSELECT);
if (result != ERROR_OK)
return result;
info->tselect_dirty = false;
@@ -853,219 +611,20 @@ static int maybe_read_tselect(struct target *target)
return ERROR_OK;
}
-static int maybe_write_tselect(struct target *target)
-{
- riscv013_info_t *info = get_info(target);
-
- if (!info->tselect_dirty) {
- int result = register_write_direct(target, REG_TSELECT, info->tselect);
- if (result != ERROR_OK)
- return result;
- info->tselect_dirty = true;
- }
-
- return ERROR_OK;
-}
-
-static void reg_cache_set(struct target *target, unsigned int number,
- uint64_t value)
-{
- struct reg *r = &target->reg_cache->reg_list[number];
- LOG_DEBUG("%s <= 0x%" PRIx64, r->name, value);
- r->valid = true;
- buf_set_u64(r->value, 0, r->size, value);
-}
-
-static uint64_t reg_cache_get(struct target *target, unsigned int number)
-{
- struct reg *r = &target->reg_cache->reg_list[number];
- if (!r->valid) {
- LOG_ERROR("Register cache entry for %d is invalid!", number);
- assert(r->valid);
- }
- uint64_t value = buf_get_u64(r->value, 0, r->size);
- LOG_DEBUG("%s = 0x%" PRIx64, r->name, value);
- return value;
-}
-
-static int execute_resume(struct target *target, bool step)
-{
- riscv013_info_t *info = get_info(target);
-
- LOG_DEBUG("step=%d", step);
-
- maybe_write_tselect(target);
-
- // TODO: check if dpc is dirty (which also is true if an exception was hit
- // at any time)
- if (register_write_direct(target, REG_DPC, info->dpc) != ERROR_OK) {
- return ERROR_FAIL;
- }
-
- 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));
- if (mstatus_user != info->mstatus_actual) {
- if (register_write_direct(target, REG_MSTATUS, mstatus_user) != ERROR_OK) {
- return ERROR_FAIL;
- }
- }
- }
-
- info->dcsr |= CSR_DCSR_EBREAKM | CSR_DCSR_EBREAKH | CSR_DCSR_EBREAKS |
- CSR_DCSR_EBREAKU;
-
- if (step) {
- info->dcsr |= CSR_DCSR_STEP;
- } else {
- info->dcsr &= ~CSR_DCSR_STEP;
- }
-
- if (register_write_direct(target, REG_DCSR, info->dcsr) != ERROR_OK) {
- return ERROR_FAIL;
- }
-
- // Restore GPRs
- if (register_write_direct(target, S0, reg_cache_get(target, S0)) != ERROR_OK) {
- return ERROR_FAIL;
- }
- if (register_write_direct(target, S1, reg_cache_get(target, S1)) != ERROR_OK) {
- return ERROR_FAIL;
- }
-
- program_t *program = program_new();
- program_add32(program, fence_i());
- program_add32(program, ebreak());
- if (execute_program(target, program) != ERROR_OK) {
- return ERROR_FAIL;
- }
- program_delete(program);
-
- dmi_write(target, DMI_DMCONTROL, DMI_DMCONTROL_DMACTIVE |
- DMI_DMCONTROL_RESUMEREQ);
-
- target->state = TARGET_RUNNING;
- register_cache_invalidate(target->reg_cache);
- reg_cache_set(target, ZERO, 0);
-
- return ERROR_OK;
-}
-
-// Execute a step, and wait for reentry into Debug Mode.
-static int full_step(struct target *target, bool announce)
-{
- int result = execute_resume(target, true);
- if (result != ERROR_OK)
- return result;
- time_t start = time(NULL);
- while (1) {
- result = poll_target(target, announce);
- if (result != ERROR_OK)
- return result;
- if (target->state != TARGET_DEBUG_RUNNING)
- break;
- if (time(NULL) - start > WALL_CLOCK_TIMEOUT) {
- LOG_ERROR("Timed out waiting for step to complete.");
- return ERROR_FAIL;
- }
- }
- return ERROR_OK;
-}
-
-static int resume(struct target *target, int debug_execution, bool step)
-{
- if (debug_execution) {
- LOG_ERROR("TODO: debug_execution is true");
- return ERROR_FAIL;
- }
-
- return execute_resume(target, step);
-}
-
-/** Update register sizes based on xlen. */
-static void update_reg_list(struct target *target)
-{
- riscv013_info_t *info = get_info(target);
- if (info->reg_values) {
- free(info->reg_values);
- }
- info->reg_values = malloc(REG_COUNT * 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;
- 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);
- }
- if (i == ZERO) {
- r->valid = true;
- } else {
- r->valid = false;
- }
- }
-}
-
/*** OpenOCD target functions. ***/
static int register_get(struct reg *reg)
{
struct target *target = (struct target *) reg->arch_info;
- riscv013_info_t *info = get_info(target);
-
- maybe_write_tselect(target);
-
- if (reg->number <= REG_XPR31) {
- buf_set_u64(reg->value, 0, 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) {
- buf_set_u32(reg->value, 0, 32, info->dpc);
- reg->valid = true;
- LOG_DEBUG("%s=0x%" PRIx64 " (cached)", reg->name, info->dpc);
- return ERROR_OK;
- } else if (reg->number == REG_PRIV) {
- buf_set_u64(reg->value, 0, 8, get_field(info->dcsr, CSR_DCSR_PRV));
- LOG_DEBUG("%s=%d (cached)", reg->name,
- (int) get_field(info->dcsr, CSR_DCSR_PRV));
- return ERROR_OK;
- } else {
- uint64_t value;
- int result = register_read_direct(target, &value, reg->number);
- if (result != ERROR_OK) {
- return result;
- }
- LOG_DEBUG("%s=0x%" PRIx64, reg->name, value);
- buf_set_u64(reg->value, 0, xlen(target), value);
-
- if (reg->number == REG_MSTATUS) {
- info->mstatus_actual = value;
- reg->valid = true;
- }
- }
-
+ uint64_t value = riscv_get_register(target, reg->number);
+ buf_set_u64(reg->value, 0, 64, value);
return ERROR_OK;
}
static int register_write(struct target *target, unsigned int number,
uint64_t value)
{
- riscv013_info_t *info = get_info(target);
-
- maybe_write_tselect(target);
-
- if (number == REG_PC) {
- info->dpc = value;
- } else if (number == REG_PRIV) {
- info->dcsr = set_field(info->dcsr, CSR_DCSR_PRV, value);
- } else {
- return register_write_direct(target, number, value);
- }
-
+ riscv_set_register(target, number, value);
return ERROR_OK;
}
@@ -1073,7 +632,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];
@@ -1088,39 +647,59 @@ static struct reg_arch_type riscv_reg_arch_type = {
.set = register_set
};
-static int halt(struct target *target)
-{
- LOG_DEBUG("riscv_halt()");
- select_dmi(target);
-
- dmi_write(target, DMI_DMCONTROL, DMI_DMCONTROL_HALTREQ |
- DMI_DMCONTROL_DMACTIVE);
-
- return ERROR_OK;
-}
-
static int init_target(struct command_context *cmd_ctx,
struct target *target)
{
LOG_DEBUG("init");
riscv_info_t *generic_info = (riscv_info_t *) target->arch_info;
+
+ riscv_info_init(generic_info);
+ generic_info->get_register = &riscv013_get_register;
+ generic_info->set_register = &riscv013_set_register;
+ generic_info->select_current_hart = &riscv013_select_current_hart;
+ generic_info->is_halted = &riscv013_is_halted;
+ generic_info->halt_current_hart = &riscv013_halt_current_hart;
+ generic_info->resume_current_hart = &riscv013_resume_current_hart;
+ generic_info->step_current_hart = &riscv013_step_current_hart;
+ generic_info->on_halt = &riscv013_on_halt;
+ generic_info->on_resume = &riscv013_on_resume;
+ generic_info->on_step = &riscv013_on_step;
+ generic_info->halt_reason = &riscv013_halt_reason;
+ generic_info->debug_buffer_enter = &riscv013_debug_buffer_enter;
+ generic_info->debug_buffer_leave = &riscv013_debug_buffer_leave;
+ generic_info->read_debug_buffer = &riscv013_read_debug_buffer;
+ generic_info->write_debug_buffer = &riscv013_write_debug_buffer;
+ generic_info->execute_debug_buffer = &riscv013_execute_debug_buffer;
+ generic_info->fill_dmi_write_u64 = &riscv013_fill_dmi_write_u64;
+ generic_info->fill_dmi_read_u64 = &riscv013_fill_dmi_read_u64;
+ generic_info->fill_dmi_nop_u64 = &riscv013_fill_dmi_nop_u64;
+ generic_info->dmi_write_u64_bits = &riscv013_dmi_write_u64_bits;
+
generic_info->version_specific = calloc(1, sizeof(riscv013_info_t));
if (!generic_info->version_specific)
return ERROR_FAIL;
riscv013_info_t *info = get_info(target);
+ info->progbuf_size = -1;
+ info->progbuf_addr = -1;
+ info->data_size = -1;
+ info->data_addr = -1;
+
+ info->dmi_busy_delay = 0;
+ info->ac_busy_delay = 0;
+
target->reg_cache = calloc(1, sizeof(*target->reg_cache));
target->reg_cache->name = "RISC-V registers";
- target->reg_cache->num_regs = REG_COUNT;
+ target->reg_cache->num_regs = GDB_REGNO_COUNT;
- target->reg_cache->reg_list = calloc(REG_COUNT, sizeof(struct reg));
+ target->reg_cache->reg_list = calloc(GDB_REGNO_COUNT, sizeof(struct reg));
const unsigned int max_reg_name_len = 12;
- info->reg_names = calloc(1, REG_COUNT * max_reg_name_len);
+ info->reg_names = calloc(1, GDB_REGNO_COUNT * max_reg_name_len);
char *reg_name = info->reg_names;
info->reg_values = NULL;
- for (unsigned int i = 0; i < REG_COUNT; i++) {
+ for (unsigned int i = 0; i < GDB_REGNO_COUNT; i++) {
struct reg *r = &target->reg_cache->reg_list[i];
r->number = i;
r->caller_save = true;
@@ -1129,24 +708,26 @@ static int init_target(struct command_context *cmd_ctx,
r->exist = true;
r->type = &riscv_reg_arch_type;
r->arch_info = target;
- if (i <= REG_XPR31) {
+ if (i <= GDB_REGNO_XPR31) {
sprintf(reg_name, "x%d", i);
- } else if (i == REG_PC) {
+ } else if (i == GDB_REGNO_PC) {
sprintf(reg_name, "pc");
- } else if (i >= REG_FPR0 && i <= REG_FPR31) {
- sprintf(reg_name, "f%d", i - REG_FPR0);
- } else if (i >= REG_CSR0 && i <= REG_CSR4095) {
- sprintf(reg_name, "csr%d", i - REG_CSR0);
- } else if (i == REG_PRIV) {
+ } else if (i >= GDB_REGNO_FPR0 && i <= GDB_REGNO_FPR31) {
+ sprintf(reg_name, "f%d", i - GDB_REGNO_FPR0);
+ } else if (i >= GDB_REGNO_CSR0 && i <= GDB_REGNO_CSR4095) {
+ sprintf(reg_name, "csr%d", i - GDB_REGNO_CSR0);
+ } else if (i == GDB_REGNO_PRIV) {
sprintf(reg_name, "priv");
}
if (reg_name[0]) {
r->name = reg_name;
}
reg_name += strlen(reg_name) + 1;
- assert(reg_name < info->reg_names + REG_COUNT * max_reg_name_len);
+ assert(reg_name < info->reg_names + GDB_REGNO_COUNT * max_reg_name_len);
}
+#if 0
update_reg_list(target);
+#endif
memset(info->trigger_unique_id, 0xff, sizeof(info->trigger_unique_id));
@@ -1164,20 +745,19 @@ static void deinit_target(struct target *target)
static int add_trigger(struct target *target, struct trigger *trigger)
{
riscv013_info_t *info = get_info(target);
-
maybe_read_tselect(target);
- unsigned int i;
- for (i = 0; i < info->trigger_count; i++) {
+ int i;
+ for (i = 0; i < riscv_count_triggers(target); i++) {
if (info->trigger_unique_id[i] != -1) {
continue;
}
- register_write_direct(target, REG_TSELECT, i);
+ register_write_direct(target, GDB_REGNO_TSELECT, i);
uint64_t tdata1;
- register_read_direct(target, &tdata1, REG_TDATA1);
- int type = get_field(tdata1, MCONTROL_TYPE(xlen(target)));
+ register_read_direct(target, &tdata1, GDB_REGNO_TDATA1);
+ int type = get_field(tdata1, MCONTROL_TYPE(riscv_xlen(target)));
if (type != 2) {
continue;
@@ -1189,7 +769,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);
@@ -1208,28 +788,28 @@ static int add_trigger(struct target *target, struct trigger *trigger)
if (trigger->write)
tdata1 |= MCONTROL_STORE;
- register_write_direct(target, REG_TDATA1, tdata1);
+ register_write_direct(target, GDB_REGNO_TDATA1, tdata1);
uint64_t tdata1_rb;
- register_read_direct(target, &tdata1_rb, REG_TDATA1);
+ register_read_direct(target, &tdata1_rb, GDB_REGNO_TDATA1);
LOG_DEBUG("tdata1=0x%" PRIx64, tdata1_rb);
if (tdata1 != tdata1_rb) {
LOG_DEBUG("Trigger %d doesn't support what we need; After writing 0x%"
PRIx64 " to tdata1 it contains 0x%" PRIx64,
i, tdata1, tdata1_rb);
- register_write_direct(target, REG_TDATA1, 0);
+ register_write_direct(target, GDB_REGNO_TDATA1, 0);
continue;
}
- register_write_direct(target, REG_TDATA2, trigger->address);
+ register_write_direct(target, GDB_REGNO_TDATA2, trigger->address);
LOG_DEBUG("Using resource %d for bp %d", i,
trigger->unique_id);
info->trigger_unique_id[i] = trigger->unique_id;
break;
}
- if (i >= info->trigger_count) {
+ if (i >= riscv_count_triggers(target)) {
LOG_ERROR("Couldn't find an available hardware trigger.");
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
}
@@ -1243,20 +823,20 @@ static int remove_trigger(struct target *target, struct trigger *trigger)
maybe_read_tselect(target);
- unsigned int i;
- for (i = 0; i < info->trigger_count; i++) {
+ int i;
+ for (i = 0; i < riscv_count_triggers(target); i++) {
if (info->trigger_unique_id[i] == trigger->unique_id) {
break;
}
}
- if (i >= info->trigger_count) {
+ if (i >= riscv_count_triggers(target)) {
LOG_ERROR("Couldn't find the hardware resources used by hardware "
"trigger.");
return ERROR_FAIL;
}
LOG_DEBUG("Stop using resource %d for bp %d", i, trigger->unique_id);
- register_write_direct(target, REG_TSELECT, i);
- register_write_direct(target, REG_TDATA1, 0);
+ register_write_direct(target, GDB_REGNO_TSELECT, i);
+ register_write_direct(target, GDB_REGNO_TDATA1, 0);
info->trigger_unique_id[i] = -1;
return ERROR_OK;
@@ -1319,7 +899,6 @@ static int add_breakpoint(struct target *target,
if (result != ERROR_OK) {
return result;
}
-
} else {
LOG_INFO("OpenOCD only supports hardware and software breakpoints.");
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
@@ -1348,7 +927,6 @@ static int remove_breakpoint(struct target *target,
if (result != ERROR_OK) {
return result;
}
-
} else {
LOG_INFO("OpenOCD only supports hardware and software breakpoints.");
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
@@ -1389,73 +967,6 @@ static int remove_watchpoint(struct target *target,
return ERROR_OK;
}
-static int strict_step(struct target *target, bool announce)
-{
- riscv013_info_t *info = get_info(target);
-
- LOG_DEBUG("enter");
-
- struct breakpoint *breakpoint = target->breakpoints;
- while (breakpoint) {
- remove_breakpoint(target, breakpoint);
- breakpoint = breakpoint->next;
- }
-
- struct watchpoint *watchpoint = target->watchpoints;
- while (watchpoint) {
- remove_watchpoint(target, watchpoint);
- watchpoint = watchpoint->next;
- }
-
- int result = full_step(target, announce);
- if (result != ERROR_OK)
- return result;
-
- breakpoint = target->breakpoints;
- while (breakpoint) {
- add_breakpoint(target, breakpoint);
- breakpoint = breakpoint->next;
- }
-
- watchpoint = target->watchpoints;
- while (watchpoint) {
- add_watchpoint(target, watchpoint);
- watchpoint = watchpoint->next;
- }
-
- info->need_strict_step = false;
-
- return ERROR_OK;
-}
-
-static int step(struct target *target, int current, uint32_t address,
- int handle_breakpoints)
-{
- riscv013_info_t *info = get_info(target);
-
- select_dmi(target);
-
- if (!current) {
- if (xlen(target) > 32) {
- LOG_WARNING("Asked to resume at 32-bit PC on %d-bit target.",
- xlen(target));
- }
- int result = register_write(target, REG_PC, address);
- if (result != ERROR_OK)
- return result;
- }
-
- if (info->need_strict_step || handle_breakpoints) {
- int result = strict_step(target, true);
- if (result != ERROR_OK)
- return result;
- } else {
- return resume(target, 0, true);
- }
-
- return ERROR_OK;
-}
-
static int examine(struct target *target)
{
// Don't need to select dbus, since the first thing we do is read dtmcontrol.
@@ -1521,541 +1032,594 @@ static int examine(struct target *target)
// Check that abstract data registers are accessible.
uint32_t abstractcs = dmi_read(target, DMI_ABSTRACTCS);
- LOG_DEBUG("abstractcs=0x%x", abstractcs);
info->datacount = get_field(abstractcs, DMI_ABSTRACTCS_DATACOUNT);
info->progsize = get_field(abstractcs, DMI_ABSTRACTCS_PROGSIZE);
- uint32_t value = 0x53467665;
- for (unsigned i = 0; i < info->datacount; i++) {
- dmi_write(target, DMI_DATA0 + i, value);
- value += 0x52534335;
- }
-
- for (unsigned i = 0; i < info->progsize; i++) {
- dmi_write(target, DMI_PROGBUF0 + i, value);
- value += 0x52534335;
- }
-
- value = 0x53467665;
- for (unsigned i = 0; i < info->datacount; i++) {
- uint32_t check = dmi_read(target, DMI_DATA0 + i);
- if (check != value) {
- LOG_ERROR("Wrote 0x%x to dbus address 0x%x but got back 0x%x",
- value, DMI_DATA0 + i, check);
- return ERROR_FAIL;
- }
- value += 0x52534335;
- }
- for (unsigned i = 0; i < info->progsize; i++) {
- uint32_t check = dmi_read(target, DMI_PROGBUF0 + i);
- if (check != value) {
- LOG_ERROR("Wrote 0x%x to dbus address 0x%x but got back 0x%x",
- value, DMI_PROGBUF0 + i, check);
- return ERROR_FAIL;
- }
- value += 0x52534335;
- }
-
- bool should_attempt_resume = false;
- if (get_field(dmstatus, DMI_DMSTATUS_ANYRUNNING)) {
- should_attempt_resume = true;
- LOG_DEBUG("Hart currently running. Requesting halt.\n");
- dmi_write(target, DMI_DMCONTROL, DMI_DMCONTROL_HALTREQ |
- DMI_DMCONTROL_DMACTIVE);
- for (unsigned i = 0; i < 256; i++) {
- dmcontrol = dmi_read(target, DMI_DMCONTROL);
- dmstatus = dmi_read(target, DMI_DMSTATUS);
- if (get_field(dmstatus, DMI_DMSTATUS_ALLHALTED))
+ /* Before doing anything else we must first enumerate the harts. */
+ RISCV_INFO(r);
+ if (riscv_rtos_enabled(target)) {
+ for (int i = 0; i < RISCV_MAX_HARTS; ++i) {
+ riscv_set_current_hartid(target, i);
+ uint32_t s = dmi_read(target, DMI_DMSTATUS);
+ if (get_field(s, DMI_DMSTATUS_ANYNONEXISTENT))
break;
+ r->hart_count = i + 1;
}
- if (!get_field(dmstatus, DMI_DMSTATUS_ALLHALTED)) {
- LOG_ERROR("hart didn't halt; dmstatus=0x%x", dmstatus);
- return ERROR_FAIL;
- }
+ } else {
+ r->hart_count = 1;
}
- // TODO: do this using Quick Access, if supported.
+ LOG_DEBUG("Enumerated %d harts", r->hart_count);
- riscv_info_t *generic_info = (riscv_info_t *) target->arch_info;
- if (abstract_read_register(target, NULL, S0, 128) == ERROR_OK) {
- generic_info->xlen = 128;
- } else if (abstract_read_register(target, NULL, S0, 64) == ERROR_OK) {
- generic_info->xlen = 64;
- } else if (abstract_read_register(target, NULL, S0, 32) == ERROR_OK) {
- generic_info->xlen = 32;
- } else {
- LOG_ERROR("Failed to discover size using abstract register reads.");
- return ERROR_FAIL;
- }
+ /* Halt every hart so we can probe them. */
+ riscv_halt_all_harts(target);
- LOG_DEBUG("Discovered XLEN is %d", xlen(target));
+ /* Find the address of the program buffer, which must be done without
+ * knowing anything about the target. */
+ for (int i = 0; i < riscv_count_harts(target); ++i) {
+ riscv_set_current_hartid(target, i);
- // Update register list to match discovered XLEN.
- update_reg_list(target);
+ /* Without knowing anything else we can at least mess with the
+ * program buffer. */
+ r->debug_buffer_size[i] = riscv013_progbuf_size(target);
- if (register_read_direct(target, &info->misa, REG_MISA) != ERROR_OK) {
- LOG_ERROR("Failed to read misa.");
- return ERROR_FAIL;
- }
+ /* Guess this is a 32-bit system, we're probing it. */
+ r->xlen[i] = 32;
- if (should_attempt_resume) {
- LOG_DEBUG("Resuming hart.\n");
- // Resume if the hart had been running.
- dmi_write(target, DMI_DMCONTROL, DMI_DMCONTROL_DMACTIVE |
- DMI_DMCONTROL_RESUMEREQ);
- for (unsigned i = 0; i < 256; i++) {
- dmcontrol = dmi_read(target, DMI_DMCONTROL);
- dmstatus = dmi_read(target, DMI_DMSTATUS);
- if (get_field(dmstatus, DMI_DMSTATUS_ALLRUNNING))
- break;
+ /* First find the low 32 bits of the program buffer. This is
+ * used to check for alignment. */
+ struct riscv_program program32;
+ riscv_program_init(&program32, target);
+ riscv_program_csrrw(&program32, GDB_REGNO_S0, GDB_REGNO_S0, GDB_REGNO_DSCRATCH);
+ riscv_program_insert(&program32, auipc(GDB_REGNO_S0));
+ riscv_program_insert(&program32, sw(GDB_REGNO_S0, GDB_REGNO_S0, -4));
+ riscv_program_csrrw(&program32, GDB_REGNO_S0, GDB_REGNO_S0, GDB_REGNO_DSCRATCH);
+ riscv_program_fence(&program32);
+ riscv_program_exec(&program32, target);
+
+ riscv_addr_t progbuf_addr = dmi_read(target, DMI_PROGBUF0) - 4;
+ if (get_field(dmi_read(target, DMI_ABSTRACTCS), DMI_ABSTRACTCS_CMDERR) != 0) {
+ LOG_ERROR("Unable to find the address of the program buffer on hart %d", i);
+ r->xlen[i] = -1;
+ continue;
}
- if (!get_field(dmstatus, DMI_DMSTATUS_ALLRUNNING)) {
- LOG_ERROR("hart didn't resume; dmstatus=0x%x", dmstatus);
- return ERROR_FAIL;
+ r->debug_buffer_addr[i] = progbuf_addr;
+
+ /* Check to see if the core can execute 64 bit instructions.
+ * In order to make this work we first need to */
+ int offset = (progbuf_addr % 8 == 0) ? -4 : 0;
+
+ struct riscv_program program64;
+ riscv_program_init(&program64, target);
+ riscv_program_csrrw(&program64, GDB_REGNO_S0, GDB_REGNO_S0, GDB_REGNO_DSCRATCH);
+ riscv_program_insert(&program64, auipc(GDB_REGNO_S0));
+ riscv_program_insert(&program64, sd(GDB_REGNO_S0, GDB_REGNO_S0, offset));
+ riscv_program_csrrw(&program64, GDB_REGNO_S0, GDB_REGNO_S0, GDB_REGNO_DSCRATCH);
+ riscv_program_fence(&program64);
+ riscv_program_exec(&program64, target);
+
+ if (get_field(dmi_read(target, DMI_ABSTRACTCS), DMI_ABSTRACTCS_CMDERR) == 0) {
+ r->debug_buffer_addr[i] =
+ (dmi_read(target, DMI_PROGBUF0 + (8 + offset) / 4) << 32)
+ + dmi_read(target, DMI_PROGBUF0 + (4 + offset) / 4)
+ - 4;
+ r->xlen[i] = 64;
+ }
+
+ LOG_DEBUG("hart %d has XLEN=%d", i, r->xlen[i]);
+ LOG_DEBUG("found program buffer at 0x%08lx", (long)(r->debug_buffer_addr[i]));
+
+ if (riscv_program_gah(&program64, r->debug_buffer_addr[i])) {
+ LOG_ERROR("This implementation will not work with hart %d with debug_buffer_addr of 0x%lx\n", i,
+ (long)r->debug_buffer_addr[i]);
+ abort();
+ }
+
+ /* Check to see if we can use the data words as an extended
+ * program buffer or not. */
+ if (r->debug_buffer_addr[i] + (4 * r->debug_buffer_size[i]) == riscv013_data_addr(target)) {
+ r->debug_buffer_size[i] += riscv013_data_size(target);
+ LOG_DEBUG("extending the debug buffer using data words, total size %d", r->debug_buffer_size[i]);
}
}
- info->never_halted = true;
+ /* Then we check the number of triggers availiable to each hart. */
+ for (int i = 0; i < riscv_count_harts(target); ++i) {
+ for (uint32_t t = 0; t < RISCV_MAX_TRIGGERS; ++t) {
+ riscv_set_current_hartid(target, i);
- int result = riscv013_poll(target);
- if (result != ERROR_OK) {
- return result;
+ r->trigger_count[i] = t;
+ register_write_direct(target, GDB_REGNO_TSELECT, t);
+ uint64_t tselect = t+1;
+ register_read_direct(target, &tselect, GDB_REGNO_TSELECT);
+ if (tselect != t)
+ break;
+ }
}
+ /* Resumes all the harts, so the debugger can later pause them. */
+ riscv_resume_all_harts(target);
target_set_examined(target);
- LOG_INFO("Examined RISCV core; XLEN=%d, misa=0x%" PRIx64, xlen(target), info->misa);
-
+
+ // This print is used by some regression suites to know when
+ // they can connect with gdb/telnet.
+ // We will need to update those suites if we want to remove this line.
+ LOG_INFO("Examined RISC-V core");
return ERROR_OK;
}
-static riscv_error_t handle_halt_routine(struct target *target)
+static int assert_reset(struct target *target)
{
- riscv013_info_t *info = get_info(target);
-
- // Read all GPRs as fast as we can, because gdb is going to ask for them
- // anyway. Reading them one at a time is much slower.
+ /*FIXME -- this only works for single-hart.*/
+ assert(!riscv_rtos_enabled(target));
+ RISCV_INFO(r);
+ assert(r->current_hartid == 0);
- for (int reg = 1; reg < 32; reg++) {
- uint64_t value;
- int result = abstract_read_register(target, &value, reg, xlen(target));
- if (result != ERROR_OK)
- return result;
- reg_cache_set(target, reg, value);
- }
-
- unsigned int csr[] = {REG_DPC, REG_DCSR};
- for (unsigned int i = 0; i < DIM(csr); i++) {
- uint64_t value;
- int reg = csr[i];
- int result = register_read_direct(target, &value, reg);
- if (result != ERROR_OK)
- return result;
- reg_cache_set(target, reg, value);
- }
+ select_dmi(target);
+ LOG_DEBUG("ASSERTING NDRESET");
+ uint32_t control = dmi_read(target, DMI_DMCONTROL);
+ control = set_field(control, DMI_DMCONTROL_NDMRESET, 1);
+ if (target->reset_halt) {
+ LOG_DEBUG("TARGET RESET HALT SET, ensuring halt is set during reset.");
+ control = set_field(control, DMI_DMCONTROL_HALTREQ, 1);
+ } else {
+ LOG_DEBUG("TARGET RESET HALT NOT SET");
+ control = set_field(control, DMI_DMCONTROL_HALTREQ, 0);
+ }
- // TODO: get rid of those 2 variables and talk to the cache directly.
- info->dpc = reg_cache_get(target, REG_DPC);
- info->dcsr = reg_cache_get(target, REG_DCSR);
+ dmi_write(target, DMI_DMCONTROL,
+ control);
- return RE_OK;
+ return ERROR_OK;
}
-static int handle_halt(struct target *target, bool announce)
+static int deassert_reset(struct target *target)
{
- riscv013_info_t *info = get_info(target);
- target->state = TARGET_HALTED;
-
- riscv_error_t re;
- do {
- re = handle_halt_routine(target);
- } while (re == RE_AGAIN);
- if (re != RE_OK) {
- LOG_ERROR("handle_halt_routine failed");
- return ERROR_FAIL;
- }
+ RISCV_INFO(r);
+ RISCV013_INFO(info);
- int cause = get_field(info->dcsr, CSR_DCSR_CAUSE);
- switch (cause) {
- case CSR_DCSR_CAUSE_SWBP:
- target->debug_reason = DBG_REASON_BREAKPOINT;
- break;
- case CSR_DCSR_CAUSE_TRIGGER:
- target->debug_reason = DBG_REASON_WPTANDBKPT;
- // If we halted because of a data trigger, gdb doesn't know to do
- // the disable-breakpoints-step-enable-breakpoints dance.
- info->need_strict_step = true;
- break;
- case CSR_DCSR_CAUSE_DEBUGINT:
- target->debug_reason = DBG_REASON_DBGRQ;
- break;
- case CSR_DCSR_CAUSE_STEP:
- target->debug_reason = DBG_REASON_SINGLESTEP;
- break;
- case CSR_DCSR_CAUSE_HALT:
- target->debug_reason = DBG_REASON_DBGRQ;
- break;
- default:
- LOG_ERROR("Invalid halt cause %d in CSR_DCSR (0x%" PRIx64 ")",
- cause, info->dcsr);
- }
-
- if (info->never_halted) {
- info->never_halted = false;
+ select_dmi(target);
- // Disable any hardware triggers that have dmode set. We can't have set
- // them ourselves. Maybe they're left over from some killed debug
- // session.
- // Count the number of triggers while we're at it.
+ /*FIXME -- this only works for Single Hart*/
+ assert(!riscv_rtos_enabled(target));
+ assert(r->current_hartid == 0);
- int result = maybe_read_tselect(target);
- if (result != ERROR_OK)
- return result;
- for (info->trigger_count = 0; info->trigger_count < MAX_HWBPS; info->trigger_count++) {
- register_write_direct(target, REG_TSELECT, info->trigger_count);
- uint64_t tselect_rb;
- register_read_direct(target, &tselect_rb, REG_TSELECT);
- if (info->trigger_count != tselect_rb)
- break;
- uint64_t tdata1;
- register_read_direct(target, &tdata1, REG_TDATA1);
- if ((tdata1 & MCONTROL_DMODE(xlen(target))) &&
- (tdata1 & (MCONTROL_EXECUTE | MCONTROL_STORE | MCONTROL_LOAD))) {
- register_write_direct(target, REG_TDATA1, 0);
- }
- }
- }
+ /*FIXME -- is there bookkeeping we need to do here*/
+
+ uint32_t control = dmi_read(target, DMI_DMCONTROL);
- if (announce) {
- target_call_event_callbacks(target, TARGET_EVENT_HALTED);
- }
+ // Clear the reset, but make sure haltreq is still set
+ if (target->reset_halt) {
+ control = set_field(control, DMI_DMCONTROL_HALTREQ, 1);
+ }
- const char *cause_string[] = {
- "none",
- "software breakpoint",
- "hardware trigger",
- "debug interrupt",
- "step",
- "halt"
- };
- // This is logged to the user so that gdb will show it when a user types
- // 'monitor reset init'. At that time gdb appears to have the pc cached
- // still so if a user manually inspects the pc it will still have the old
- // value.
- LOG_USER("halted at 0x%" PRIx64 " due to %s", info->dpc, cause_string[cause]);
+ control = set_field(control, DMI_DMCONTROL_NDMRESET, 0);
+ dmi_write(target, DMI_DMCONTROL, control);
- return ERROR_OK;
+ uint32_t status;
+ int dmi_busy_delay = info->dmi_busy_delay;
+ if (target->reset_halt) {
+ LOG_DEBUG("DEASSERTING RESET, waiting for hart to be halted.");
+ do {
+ status = dmi_read(target, DMI_DMSTATUS);
+ } while (get_field(status, DMI_DMSTATUS_ALLHALTED) == 0);
+ } else {
+ LOG_DEBUG("DEASSERTING RESET, waiting for hart to be running.");
+ do {
+ status = dmi_read(target, DMI_DMSTATUS);
+ if (get_field(status, DMI_DMSTATUS_ANYHALTED) ||
+ get_field(status, DMI_DMSTATUS_ANYUNAVAIL)) {
+ LOG_ERROR("Unexpected hart status during reset.");
+ abort();
+ }
+ } while (get_field(status, DMI_DMSTATUS_ALLRUNNING) == 0);
+ }
+ info->dmi_busy_delay = dmi_busy_delay;
+ return ERROR_OK;
}
-static int poll_target(struct target *target, bool announce)
+static int read_memory(struct target *target, uint32_t address,
+ uint32_t size, uint32_t count, uint8_t *buffer)
{
- select_dmi(target);
+ RISCV013_INFO(info);
- // Inhibit debug logging during poll(), which isn't usually interesting and
- // just fills up the screen/logs with clutter.
- int old_debug_level = debug_level;
- if (debug_level >= LOG_LVL_DEBUG) {
- debug_level = LOG_LVL_INFO;
- }
- debug_level = old_debug_level;
+ LOG_DEBUG("writing %d words of %d bytes to 0x%08lx", count, size, (long)address);
- uint32_t dmstatus = dmi_read(target, DMI_DMSTATUS);
- if (get_field(dmstatus, DMI_DMSTATUS_ALLHALTED)) {
- if (target->state != TARGET_HALTED) {
- return handle_halt(target, announce);
- }
- return ERROR_OK;
+ select_dmi(target);
+ riscv_set_current_hartid(target, 0);
+
+ /* This program uses two temporary registers. A word of data and the
+ * associated address are stored at some location in memory. The
+ * program loads the word from that address and then increments the
+ * address. The debugger is expected to pull the memory word-by-word
+ * from the chip with AUTOEXEC set in order to trigger program
+ * execution on every word. */
+ uint64_t s0 = riscv_get_register(target, GDB_REGNO_S0);
+ uint64_t s1 = riscv_get_register(target, GDB_REGNO_S1);
+
+ struct riscv_program program;
+ riscv_program_init(&program, target);
+ riscv_addr_t r_data = riscv_program_alloc_w(&program);
+ riscv_addr_t r_addr = riscv_program_alloc_x(&program);
+ riscv_program_fence(&program);
+ riscv_program_lx(&program, GDB_REGNO_S0, r_addr);
+ riscv_program_addi(&program, GDB_REGNO_S0, GDB_REGNO_S0, size);
+ switch (size) {
+ case 1:
+ riscv_program_lbr(&program, GDB_REGNO_S1, GDB_REGNO_S0, 0);
+ break;
+ case 2:
+ riscv_program_lhr(&program, GDB_REGNO_S1, GDB_REGNO_S0, 0);
+ break;
+ case 4:
+ riscv_program_lwr(&program, GDB_REGNO_S1, GDB_REGNO_S0, 0);
+ break;
+ default:
+ LOG_ERROR("Unsupported size: %d", size);
+ return ERROR_FAIL;
+ }
+ riscv_program_sw(&program, GDB_REGNO_S1, r_data);
+ riscv_program_sx(&program, GDB_REGNO_S0, r_addr);
+
+#if 1
+ /* The first round through the program's execution we use the regular
+ * program execution mechanism. */
+ switch (riscv_xlen(target)) {
+ case 64:
+ riscv_program_write_ram(&program, r_addr + 4, ((riscv_addr_t)(address - size)) >> 32);
+ case 32:
+ riscv_program_write_ram(&program, r_addr, (riscv_addr_t)(address - size));
+ break;
+ default:
+ LOG_ERROR("unknown XLEN %d", riscv_xlen(target));
+ return ERROR_FAIL;
}
- if (get_field(dmstatus, DMI_DMSTATUS_ALLRUNNING)) {
- target->state = TARGET_RUNNING;
- return ERROR_OK;
+ if (riscv_program_exec(&program, target) != ERROR_OK) {
+ uint32_t acs = dmi_read(target, DMI_ABSTRACTCS);
+ LOG_ERROR("failed to execute program, abstractcs=0x%08x", acs);
+ riscv013_clear_abstract_error(target);
+ riscv_set_register(target, GDB_REGNO_S0, s0);
+ riscv_set_register(target, GDB_REGNO_S1, s1);
+ LOG_ERROR(" exiting with ERROR_FAIL");
+ return ERROR_FAIL;
}
- if (get_field(dmstatus, DMI_DMSTATUS_ANYUNAVAIL)) {
- target->state = TARGET_RESET;
- return ERROR_OK;
+ uint32_t value = riscv_program_read_ram(&program, r_data);
+ LOG_DEBUG("M[0x%08lx] reads 0x%08lx", (long)address, (long)value);
+ switch (size) {
+ case 1:
+ buffer[0] = value;
+ break;
+ case 2:
+ buffer[0] = value;
+ buffer[1] = value >> 8;
+ break;
+ case 4:
+ buffer[0] = value;
+ buffer[1] = value >> 8;
+ buffer[2] = value >> 16;
+ buffer[3] = value >> 24;
+ break;
+ default:
+ LOG_ERROR("unsupported access size: %d", size);
+ return ERROR_FAIL;
+ }
+#else
+ /* The first round through the program's execution we use the regular
+ * program execution mechanism. */
+ switch (riscv_xlen(target)) {
+ case 64:
+ riscv_program_write_ram(&program, r_addr + 4, ((riscv_addr_t)(address)) >> 32);
+ case 32:
+ riscv_program_write_ram(&program, r_addr, (riscv_addr_t)(address));
+ break;
+ default:
+ LOG_ERROR("unknown XLEN %d", riscv_xlen(target));
+ return ERROR_FAIL;
}
- if (get_field(dmstatus, DMI_DMSTATUS_ANYNONEXISTENT)) {
- LOG_ERROR("Hart disappeared!");
+ if (riscv_program_load(&program, target) != ERROR_OK) {
+ uint32_t acs = dmi_read(target, DMI_ABSTRACTCS);
+ LOG_ERROR("failed to execute program, abstractcs=0x%08x", acs);
+ riscv013_clear_abstract_error(target);
+ riscv_set_register(target, GDB_REGNO_S0, s0);
+ riscv_set_register(target, GDB_REGNO_S1, s1);
+ LOG_ERROR(" exiting with ERROR_FAIL");
return ERROR_FAIL;
}
+ uint32_t value;
+#endif
- return ERROR_OK;
-}
+ /* The rest of this program is designed to be fast so it reads various
+ * DMI registers directly. */
+ int d_data = (r_data - riscv_debug_buffer_addr(target)) / 4;
+ int d_addr = (r_addr - riscv_debug_buffer_addr(target)) / 4;
+
+ riscv013_set_autoexec(target, d_data, 1);
+
+ /* Copying memory might fail because we're going too quickly, in which
+ * case we need to back off a bit and try again. There's two
+ * termination conditions to this loop: a non-BUSY error message, or
+ * the data was all copied. */
+ riscv_addr_t cur_addr = 0xbadbeef;
+ riscv_addr_t fin_addr = address + (count * size);
+ riscv_addr_t prev_addr = 0;
+ LOG_DEBUG("writing until final address 0x%016lx", fin_addr);
+ while (count > 1 && (cur_addr = riscv_read_debug_buffer_x(target, d_addr)) < fin_addr) {
+ LOG_DEBUG("transferring burst starting at address 0x%016lx (previous burst was 0x%016lx)", cur_addr, prev_addr);
+ assert(prev_addr < cur_addr);
+ prev_addr = cur_addr;
+ riscv_addr_t start = (cur_addr - address) / size;
+ assert (cur_addr >= address);
+ struct riscv_batch *batch = riscv_batch_alloc(
+ target,
+ 1024,
+ info->dmi_busy_delay + info->ac_busy_delay);
+
+#if 0
+ size_t reads = 1;
+ riscv_batch_add_dmi_read(batch, riscv013_debug_buffer_register(target, r_data));
+#else
+ size_t reads = 0;
+#endif
+ size_t rereads = reads;
+ for (riscv_addr_t i = start; i < count; ++i) {
+ size_t index =
+ riscv_batch_add_dmi_read(
+ batch,
+ riscv013_debug_buffer_register(target, r_data));
+ assert(index == reads);
+ reads++;
+ if (riscv_batch_full(batch))
+ break;
+ }
-static int riscv013_poll(struct target *target)
-{
- return poll_target(target, true);
-}
+ riscv_batch_run(batch);
-static int riscv013_resume(struct target *target, int current, uint32_t address,
- int handle_breakpoints, int debug_execution)
-{
- riscv013_info_t *info = get_info(target);
+ for (size_t i = start; i < start + reads; ++i) {
+ riscv_addr_t offset = size*i;
+ riscv_addr_t t_addr = address + offset;
+ uint8_t *t_buffer = buffer + offset;
- select_dmi(target);
+ uint64_t dmi_out = riscv_batch_get_dmi_read(batch, rereads);
+ value = get_field(dmi_out, DTM_DMI_DATA);
+ rereads++;
+
+ switch (size) {
+ case 1:
+ t_buffer[0] = value;
+ break;
+ case 2:
+ t_buffer[0] = value;
+ t_buffer[1] = value >> 8;
+ break;
+ case 4:
+ t_buffer[0] = value;
+ t_buffer[1] = value >> 8;
+ t_buffer[2] = value >> 16;
+ t_buffer[3] = value >> 24;
+ break;
+ default:
+ LOG_ERROR("unsupported access size: %d", size);
+ return ERROR_FAIL;
+ }
- if (!current) {
- if (xlen(target) > 32) {
- LOG_WARNING("Asked to resume at 32-bit PC on %d-bit target.",
- xlen(target));
+ LOG_DEBUG("M[0x%08lx] reads 0x%08lx", (long)t_addr, (long)value);
}
- int result = register_write(target, REG_PC, address);
- if (result != ERROR_OK)
- return result;
- }
- if (info->need_strict_step || handle_breakpoints) {
- int result = strict_step(target, false);
- if (result != ERROR_OK)
- return result;
- }
+ riscv_batch_free(batch);
- return resume(target, debug_execution, false);
-}
+ // Note that if the scan resulted in a Busy DMI response, it
+ // is this read to abstractcs that will cause the dmi_busy_delay
+ // to be incremented if necessary. The loop condition above
+ // catches the case where no writes went through at all.
-static int assert_reset(struct target *target)
-{
- select_dmi(target);
- uint32_t control = DMI_DMCONTROL_DMACTIVE | DMI_DMCONTROL_NDMRESET;
- if (target->reset_halt) {
- LOG_DEBUG("TARGET RESET HALT SET, Requesting halt during reset.\n");
- control |= DMI_DMCONTROL_HALTREQ;
- }
-
- dmi_write(target, DMI_DMCONTROL,
- control);
+ uint32_t abstractcs = dmi_read(target, DMI_ABSTRACTCS);
+ switch (get_field(abstractcs, DMI_ABSTRACTCS_CMDERR)) {
+ case CMDERR_NONE:
+ LOG_DEBUG("successful (partial?) memory write");
+ break;
+ case CMDERR_BUSY:
+ LOG_DEBUG("memory write resulted in busy response");
+ riscv013_clear_abstract_error(target);
+ increase_ac_busy_delay(target);
+ break;
+ default:
+ LOG_ERROR("error when writing memory, abstractcs=0x%08lx", (long)abstractcs);
+ riscv013_set_autoexec(target, d_data, 0);
+ riscv_set_register(target, GDB_REGNO_S0, s0);
+ riscv_set_register(target, GDB_REGNO_S1, s1);
+ riscv013_clear_abstract_error(target);
+ return ERROR_FAIL;
+ }
+ }
- if (!target->reset_halt) {
- LOG_DEBUG("TARGET RESET HALT NOT SET, Requesting resume during reset.\n");
- control = DMI_DMCONTROL_DMACTIVE | DMI_DMCONTROL_RESUMEREQ;
- dmi_write(target, DMI_DMCONTROL, control);
- }
-
- return ERROR_OK;
+ riscv013_set_autoexec(target, d_data, 0);
+ riscv_set_register(target, GDB_REGNO_S0, s0);
+ riscv_set_register(target, GDB_REGNO_S1, s1);
+ return ERROR_OK;
}
-static int deassert_reset(struct target *target)
+static int write_memory(struct target *target, uint32_t address,
+ uint32_t size, uint32_t count, const uint8_t *buffer)
{
- select_dmi(target);
+ RISCV013_INFO(info);
- // Note that we don't need to keep asserting
- // haltreq since we already set it in assert_reset.
- dmi_write(target, DMI_DMCONTROL, DMI_DMCONTROL_DMACTIVE);
-
- if (target->reset_halt) {
- LOG_DEBUG("TARGET RESET HALT SET, waiting for hart to be halted.\n");
- while (get_field(dmi_read(target, DMI_DMSTATUS), DMI_DMSTATUS_ALLHALTED) == 0) {
- }
- // This is necessary to re-read all the registers.
- handle_halt(target, true);
- } else {
- LOG_DEBUG("TARGET RESET HALT NOT SET, waiting for hart to be running.\n");
- while (get_field(dmi_read(target, DMI_DMSTATUS), DMI_DMSTATUS_ALLRUNNING) == 0) {
- }
- }
- return ERROR_OK;
-}
+ LOG_DEBUG("writing %d words of %d bytes to 0x%08lx", count, size, (long)address);
-static int read_memory(struct target *target, uint32_t address,
- uint32_t size, uint32_t count, uint8_t *buffer)
-{
select_dmi(target);
+ riscv_set_current_hartid(target, 0);
+
+ /* This program uses two temporary registers. A word of data and the
+ * associated address are stored at some location in memory. The
+ * program stores the word to that address and then increments the
+ * address. The debugger is expected to feed the memory word-by-word
+ * into the chip with AUTOEXEC set in order to trigger program
+ * execution on every word. */
+ uint64_t s0 = riscv_get_register(target, GDB_REGNO_S0);
+ uint64_t s1 = riscv_get_register(target, GDB_REGNO_S1);
+
+ struct riscv_program program;
+ riscv_program_init(&program, target);
+ riscv_addr_t r_data = riscv_program_alloc_w(&program);
+ riscv_addr_t r_addr = riscv_program_alloc_x(&program);
+ riscv_program_fence(&program);
+ riscv_program_lx(&program, GDB_REGNO_S0, r_addr);
+ riscv_program_lw(&program, GDB_REGNO_S1, r_data);
+
+ switch (size) {
+ case 1:
+ riscv_program_sbr(&program, GDB_REGNO_S1, GDB_REGNO_S0, 0);
+ break;
+ case 2:
+ riscv_program_shr(&program, GDB_REGNO_S1, GDB_REGNO_S0, 0);
+ break;
+ case 4:
+ riscv_program_swr(&program, GDB_REGNO_S1, GDB_REGNO_S0, 0);
+ break;
+ default:
+ LOG_ERROR("Unsupported size: %d", size);
+ return ERROR_FAIL;
+ }
- while (1) {
- abstract_write_register(target, S0, xlen(target), address);
-
- program_t *program = program_new();
- switch (size) {
- case 1:
- program_add32(program, lb(S1, S0, 0));
- break;
- case 2:
- program_add32(program, lh(S1, S0, 0));
- break;
- case 4:
- program_add32(program, lw(S1, S0, 0));
- break;
- default:
- LOG_ERROR("Unsupported size: %d", size);
- return ERROR_FAIL;
- }
- program_add32(program, addi(S0, S0, size));
- program_add32(program, ebreak());
- write_program(target, program);
- program_delete(program);
-
- if (execute_abstract_command(target,
- AC_ACCESS_REGISTER_PREEXEC |
- abstract_register_size(xlen(target)) | reg_number_to_no(S1)) != ERROR_OK) {
- return ERROR_FAIL;
- }
-
- // Set up autoexec s.t. each read of the the result that was in S1
- // will start another run of reading the address pointed to by S0,
- // copying it to S1, and storing S1 into Data 0.
- if (count > 1) {
- dmi_write(target, DMI_ABSTRACTAUTO, 0x1 << DMI_ABSTRACTAUTO_AUTOEXECDATA_OFFSET);
- }
-
- uint32_t abstractcs;
- for (uint32_t i = 0; i < count; i++) {
-
- // On last iteration, turn off autoexec before reading the value
- // so that we don't inadvertently read too far into memory.
- if ((count > 1) && ((i + 1) == count)) {
- dmi_write(target, DMI_ABSTRACTAUTO, 0);
- }
-
- uint32_t value = dmi_read(target, DMI_DATA0);
- // If autoexec was set, the above dmi_read started an abstract command.
- // If we just immediately loop and do another read here,
- // we'll probably get a busy error. Wait for idle first,
- // or otherwise take ac_command_busy into account (this defeats the purpose
- // of autoexec, this whole code needs optimization).
- if ((count > 1) && ((i + 1) < count)) {
- if (wait_for_idle(target, &abstractcs) != ERROR_OK) {
- dmi_write(target, DMI_ABSTRACTAUTO, 0);
- return ERROR_FAIL;
- }
- }
- switch (size) {
- case 1:
- buffer[i] = value;
- break;
- case 2:
- buffer[2*i] = value;
- buffer[2*i+1] = value >> 8;
- break;
- case 4:
- buffer[4*i] = value;
- buffer[4*i+1] = value >> 8;
- buffer[4*i+2] = value >> 16;
- buffer[4*i+3] = value >> 24;
- break;
- default:
- return ERROR_FAIL;
- }
- }
-
- abstractcs = dmi_read(target, DMI_ABSTRACTCS);
- unsigned cmderr = get_field(abstractcs, DMI_ABSTRACTCS_CMDERR);
- if (cmderr == CMDERR_BUSY) {
- // Clear the error and wait longer.
- dmi_write(target, DMI_ABSTRACTCS, DMI_ABSTRACTCS_CMDERR);
- increase_ac_busy_delay(target);
- } else if (cmderr) {
- LOG_ERROR("cmderr=%d", get_field(abstractcs, DMI_ABSTRACTCS_CMDERR));
- return ERROR_FAIL;
- } else {
- return ERROR_OK;
- }
- }
- // Should not get here.
- assert(0);
- return ERROR_OK;
-}
+ riscv_program_addi(&program, GDB_REGNO_S0, GDB_REGNO_S0, size);
+ riscv_program_sx(&program, GDB_REGNO_S0, r_addr);
-/**
- * If there was a DMI error, clear that error and return 1.
- * Otherwise return 0.
- */
-static int check_dmi_error(struct target *target)
-{
- dmi_status_t status = dmi_scan(target, NULL, NULL, DMI_OP_NOP, 0, 0,
- false);
- if (status != DMI_STATUS_SUCCESS) {
- // Clear errors.
- dtmcontrol_scan(target, DTM_DTMCS_DMIRESET);
- increase_dmi_busy_delay(target);
- return 1;
+ /* The first round through the program's execution we use the regular
+ * program execution mechanism. */
+ uint32_t value;
+ switch (size) {
+ case 1:
+ value = buffer[0];
+ break;
+ case 2:
+ value = buffer[0]
+ | ((uint32_t) buffer[1] << 8);
+ break;
+ case 4:
+ value = buffer[0]
+ | ((uint32_t) buffer[1] << 8)
+ | ((uint32_t) buffer[2] << 16)
+ | ((uint32_t) buffer[3] << 24);
+ break;
+ default:
+ LOG_ERROR("unsupported access size: %d", size);
+ return ERROR_FAIL;
}
- return 0;
-}
-static int write_memory(struct target *target, uint32_t address,
- uint32_t size, uint32_t count, const uint8_t *buffer)
-{
- select_dmi(target);
+ switch (riscv_xlen(target)) {
+ case 64:
+ riscv_program_write_ram(&program, r_addr + 4, (uint64_t)address >> 32);
+ case 32:
+ riscv_program_write_ram(&program, r_addr, address);
+ break;
+ default:
+ LOG_ERROR("unknown XLEN %d", riscv_xlen(target));
+ return ERROR_FAIL;
+ }
+ riscv_program_write_ram(&program, r_data, value);
- while (1) {
- abstract_write_register(target, S0, xlen(target), address);
+ LOG_DEBUG("M[0x%08lx] writes 0x%08lx", (long)address, (long)value);
+
+ if (riscv_program_exec(&program, target) != ERROR_OK) {
+ uint32_t acs = dmi_read(target, DMI_ABSTRACTCS);
+ LOG_ERROR("failed to execute program, abstractcs=0x%08x", acs);
+ riscv013_clear_abstract_error(target);
+ riscv_set_register(target, GDB_REGNO_S0, s0);
+ riscv_set_register(target, GDB_REGNO_S1, s1);
+ LOG_ERROR(" exiting with ERROR_FAIL");
+ return ERROR_FAIL;
+ }
+
+ /* The rest of this program is designed to be fast so it reads various
+ * DMI registers directly. */
+ int d_data = (r_data - riscv_debug_buffer_addr(target)) / 4;
+ int d_addr = (r_addr - riscv_debug_buffer_addr(target)) / 4;
+
+ riscv013_set_autoexec(target, d_data, 1);
+
+ /* Copying memory might fail because we're going too quickly, in which
+ * case we need to back off a bit and try again. There's two
+ * termination conditions to this loop: a non-BUSY error message, or
+ * the data was all copied. */
+ riscv_addr_t cur_addr = 0xbadbeef;
+ riscv_addr_t fin_addr = address + (count * size);
+ LOG_DEBUG("writing until final address 0x%016lx", fin_addr);
+ while ((cur_addr = riscv_read_debug_buffer_x(target, d_addr)) < fin_addr) {
+ LOG_DEBUG("transferring burst starting at address 0x%016lx", cur_addr);
+ riscv_addr_t start = (cur_addr - address) / size;
+ assert (cur_addr > address);
+ struct riscv_batch *batch = riscv_batch_alloc(
+ target,
+ 1024,
+ info->dmi_busy_delay + info->ac_busy_delay);
+
+ for (riscv_addr_t i = start; i < count; ++i) {
+ riscv_addr_t offset = size*i;
+ riscv_addr_t t_addr = address + offset;
+ const uint8_t *t_buffer = buffer + offset;
- program_t *program = program_new();
- switch (size) {
- case 1:
- program_add32(program, sb(S1, S0, 0));
- break;
- case 2:
- program_add32(program, sh(S1, S0, 0));
- break;
- case 4:
- program_add32(program, sw(S1, S0, 0));
- break;
- default:
- LOG_ERROR("Unsupported size: %d", size);
- return ERROR_FAIL;
- }
- program_add32(program, addi(S0, S0, size));
- program_add32(program, ebreak());
- write_program(target, program);
- program_delete(program);
-
- scans_t *scans = scans_new(target, count + 10);
- for (uint32_t i = 0; i < count; i++) {
- uint32_t value;
switch (size) {
case 1:
- value = buffer[i];
+ value = t_buffer[0];
break;
case 2:
- value = buffer[2*i] | ((uint32_t) buffer[2*i+1] << 8);
+ value = t_buffer[0]
+ | ((uint32_t) t_buffer[1] << 8);
break;
case 4:
- value = buffer[4*i] |
- ((uint32_t) buffer[4*i+1] << 8) |
- ((uint32_t) buffer[4*i+2] << 16) |
- ((uint32_t) buffer[4*i+3] << 24);
+ value = t_buffer[0]
+ | ((uint32_t) t_buffer[1] << 8)
+ | ((uint32_t) t_buffer[2] << 16)
+ | ((uint32_t) t_buffer[3] << 24);
break;
default:
+ LOG_ERROR("unsupported access size: %d", size);
return ERROR_FAIL;
}
- scans_add_dmi_write(scans, DMI_DATA0, value, true);
-
- if (i == 0) {
- scans_add_dmi_write(scans, DMI_COMMAND,
- AC_ACCESS_REGISTER_WRITE | AC_ACCESS_REGISTER_POSTEXEC
- | abstract_register_size(xlen(target)) |
- reg_number_to_no(S1), true);
- scans_add_dmi_write(scans, DMI_ABSTRACTAUTO,
- 0x1 << DMI_ABSTRACTAUTO_AUTOEXECDATA_OFFSET,
- false);
- }
+
+ LOG_DEBUG("M[0x%08lx] writes 0x%08lx", (long)t_addr, (long)value);
+
+ riscv_batch_add_dmi_write(
+ batch,
+ riscv013_debug_buffer_register(target, r_data),
+ value);
+ if (riscv_batch_full(batch))
+ break;
}
- int result = scans_execute(scans);
- scans_delete(scans);
- scans = NULL;
- if (result != ERROR_OK)
- return result;
- int dmi_error = check_dmi_error(target);
+ riscv_batch_run(batch);
+ riscv_batch_free(batch);
+
+ // Note that if the scan resulted in a Busy DMI response, it
+ // is this read to abstractcs that will cause the dmi_busy_delay
+ // to be incremented if necessary. The loop condition above
+ // catches the case where no writes went through at all.
- // Clear autoexec.
- dmi_write(target, DMI_ABSTRACTAUTO, 0);
uint32_t abstractcs = dmi_read(target, DMI_ABSTRACTCS);
- unsigned cmderr = get_field(abstractcs, DMI_ABSTRACTCS_CMDERR);
- if (cmderr == CMDERR_BUSY) {
- dmi_write(target, DMI_ABSTRACTCS, DMI_ABSTRACTCS_CMDERR);
+ switch (get_field(abstractcs, DMI_ABSTRACTCS_CMDERR)) {
+ case CMDERR_NONE:
+ LOG_DEBUG("successful (partial?) memory write");
+ break;
+ case CMDERR_BUSY:
+ LOG_DEBUG("memory write resulted in busy response");
+ riscv013_clear_abstract_error(target);
increase_ac_busy_delay(target);
- } else if (cmderr) {
- LOG_ERROR("cmderr=%d", get_field(abstractcs, DMI_ABSTRACTCS_CMDERR));
- return ERROR_FAIL;
- } else if (!dmi_error) {
break;
+ default:
+ LOG_ERROR("error when writing memory, abstractcs=0x%08lx", (long)abstractcs);
+ riscv013_set_autoexec(target, d_data, 0);
+ riscv013_clear_abstract_error(target);
+ riscv_set_register(target, GDB_REGNO_S0, s0);
+ riscv_set_register(target, GDB_REGNO_S1, s1);
+ return ERROR_FAIL;
}
}
+ riscv013_set_autoexec(target, d_data, 0);
+ riscv_set_register(target, GDB_REGNO_S0, s0);
+ riscv_set_register(target, GDB_REGNO_S1, s1);
return ERROR_OK;
}
@@ -2072,12 +1636,10 @@ struct target_type riscv013_target =
.deinit_target = deinit_target,
.examine = examine,
- /* poll current target status */
- .poll = riscv013_poll,
-
- .halt = halt,
- .resume = riscv013_resume,
- .step = step,
+ .poll = &riscv_openocd_poll,
+ .halt = &riscv_openocd_halt,
+ .resume = &riscv_openocd_resume,
+ .step = &riscv_openocd_step,
.assert_reset = assert_reset,
.deassert_reset = deassert_reset,
@@ -2093,3 +1655,365 @@ struct target_type riscv013_target =
.arch_state = arch_state,
};
+
+/*** 0.13-specific implementations of various RISC-V hepler functions. ***/
+static riscv_reg_t riscv013_get_register(struct target *target, int hid, int rid)
+{
+ LOG_DEBUG("reading register 0x%08x on hart %d", rid, hid);
+
+ riscv_set_current_hartid(target, hid);
+
+ uint64_t out;
+ riscv013_info_t *info = get_info(target);
+
+ if (rid <= GDB_REGNO_XPR31) {
+ register_read_direct(target, &out, rid);
+ } else if (rid == GDB_REGNO_PC) {
+ register_read_direct(target, &out, GDB_REGNO_DPC);
+ LOG_DEBUG("read PC from DPC: 0x%016lx", out);
+ } else if (rid == GDB_REGNO_PRIV) {
+ uint64_t dcsr;
+ register_read_direct(target, &dcsr, CSR_DCSR);
+ buf_set_u64((unsigned char *)&out, 0, 8, get_field(dcsr, CSR_DCSR_PRV));
+ } else {
+ int result = register_read_direct(target, &out, rid);
+ if (result != ERROR_OK) {
+ LOG_ERROR("Unable to read register %d", rid);
+ out = -1;
+ }
+
+ if (rid == GDB_REGNO_MSTATUS)
+ info->mstatus_actual = out;
+ }
+
+ return out;
+}
+
+static void riscv013_set_register(struct target *target, int hid, int rid, uint64_t value)
+{
+ LOG_DEBUG("writing register 0x%08x on hart %d", rid, hid);
+
+ riscv_set_current_hartid(target, hid);
+
+ if (rid <= GDB_REGNO_XPR31) {
+ register_write_direct(target, rid, value);
+ } else if (rid == GDB_REGNO_PC) {
+ LOG_DEBUG("writing PC to DPC: 0x%016lx", value);
+ register_write_direct(target, GDB_REGNO_DPC, value);
+ uint64_t actual_value;
+ register_read_direct(target, &actual_value, GDB_REGNO_DPC);
+ LOG_DEBUG(" actual DPC written: 0x%016lx", actual_value);
+ assert(value == actual_value);
+ } else if (rid == GDB_REGNO_PRIV) {
+ uint64_t dcsr;
+ register_read_direct(target, &dcsr, CSR_DCSR);
+ dcsr = set_field(dcsr, CSR_DCSR_PRV, value);
+ register_write_direct(target, CSR_DCSR, dcsr);
+ } else {
+ register_write_direct(target, rid, value);
+ }
+}
+
+static void riscv013_select_current_hart(struct target *target)
+{
+ RISCV_INFO(r);
+
+ uint64_t dmcontrol = dmi_read(target, DMI_DMCONTROL);
+ dmcontrol = set_field(dmcontrol, DMI_DMCONTROL_HARTSEL, r->current_hartid);
+ dmi_write(target, DMI_DMCONTROL, dmcontrol);
+}
+
+static void riscv013_halt_current_hart(struct target *target)
+{
+ RISCV_INFO(r);
+ LOG_DEBUG("halting hart %d", r->current_hartid);
+ assert(!riscv_is_halted(target));
+
+ /* Issue the halt command, and then wait for the current hart to halt. */
+ uint32_t dmcontrol = dmi_read(target, DMI_DMCONTROL);
+ dmcontrol = set_field(dmcontrol, DMI_DMCONTROL_HALTREQ, 1);
+ dmi_write(target, DMI_DMCONTROL, dmcontrol);
+ for (size_t i = 0; i < 256; ++i)
+ if (riscv_is_halted(target))
+ break;
+
+ if (!riscv_is_halted(target)) {
+ uint32_t dmstatus = dmi_read(target, DMI_DMSTATUS);
+ dmcontrol = dmi_read(target, DMI_DMCONTROL);
+
+ LOG_ERROR("unable to halt hart %d", r->current_hartid);
+ LOG_ERROR(" dmcontrol=0x%08x", dmcontrol);
+ LOG_ERROR(" dmstatus =0x%08x", dmstatus);
+ abort();
+ }
+
+ dmcontrol = set_field(dmcontrol, DMI_DMCONTROL_HALTREQ, 0);
+ dmi_write(target, DMI_DMCONTROL, dmcontrol);
+}
+
+static void riscv013_resume_current_hart(struct target *target)
+{
+ return riscv013_step_or_resume_current_hart(target, false);
+}
+
+static void riscv013_step_current_hart(struct target *target)
+{
+ return riscv013_step_or_resume_current_hart(target, true);
+}
+
+static void riscv013_on_resume(struct target *target)
+{
+ return riscv013_on_step_or_resume(target, false);
+}
+
+static void riscv013_on_step(struct target *target)
+{
+ return riscv013_on_step_or_resume(target, true);
+}
+
+static void riscv013_on_halt(struct target *target)
+{
+}
+
+static bool riscv013_is_halted(struct target *target)
+{
+ uint32_t dmstatus = dmi_read(target, DMI_DMSTATUS);
+ if (get_field(dmstatus, DMI_DMSTATUS_ANYUNAVAIL))
+ LOG_ERROR("hart %d is unavailiable", riscv_current_hartid(target));
+ if (get_field(dmstatus, DMI_DMSTATUS_ANYNONEXISTENT))
+ LOG_ERROR("hart %d doesn't exist", riscv_current_hartid(target));
+ return get_field(dmstatus, DMI_DMSTATUS_ALLHALTED);
+}
+
+static enum riscv_halt_reason riscv013_halt_reason(struct target *target)
+{
+ uint64_t dcsr = riscv_get_register(target, GDB_REGNO_DCSR);
+ switch (get_field(dcsr, CSR_DCSR_CAUSE)) {
+ case CSR_DCSR_CAUSE_SWBP:
+ case CSR_DCSR_CAUSE_TRIGGER:
+ return RISCV_HALT_BREAKPOINT;
+ case CSR_DCSR_CAUSE_STEP:
+ return RISCV_HALT_SINGLESTEP;
+ case CSR_DCSR_CAUSE_DEBUGINT:
+ case CSR_DCSR_CAUSE_HALT:
+ return RISCV_HALT_INTERRUPT;
+ }
+
+ LOG_ERROR("Unknown DCSR cause field: %x", (int)get_field(dcsr, CSR_DCSR_CAUSE));
+ LOG_ERROR(" dcsr=0x%016lx", (long)dcsr);
+ abort();
+}
+
+void riscv013_debug_buffer_enter(struct target *target, struct riscv_program *program)
+{
+}
+
+void riscv013_debug_buffer_leave(struct target *target, struct riscv_program *program)
+{
+}
+
+void riscv013_write_debug_buffer(struct target *target, int index, riscv_insn_t data)
+{
+ if (index >= riscv013_progbuf_size(target))
+ return dmi_write(target, DMI_DATA0 + index - riscv013_progbuf_size(target), data);
+ return dmi_write(target, DMI_PROGBUF0 + index, data);
+}
+
+riscv_insn_t riscv013_read_debug_buffer(struct target *target, int index)
+{
+ if (index >= riscv013_progbuf_size(target))
+ return dmi_read(target, DMI_DATA0 + index - riscv013_progbuf_size(target));
+ return dmi_read(target, DMI_PROGBUF0 + index);
+}
+
+int riscv013_execute_debug_buffer(struct target *target)
+{
+ riscv013_clear_abstract_error(target);
+
+ uint32_t run_program = 0;
+ run_program = set_field(run_program, AC_ACCESS_REGISTER_SIZE, 2);
+ run_program = set_field(run_program, AC_ACCESS_REGISTER_POSTEXEC, 1);
+ run_program = set_field(run_program, AC_ACCESS_REGISTER_TRANSFER, 0);
+ run_program = set_field(run_program, AC_ACCESS_REGISTER_REGNO, 0x1000);
+ dmi_write(target, DMI_COMMAND, run_program);
+
+ {
+ uint32_t dmstatus = 0;
+ wait_for_idle(target, &dmstatus);
+ }
+
+ uint32_t cs = dmi_read(target, DMI_ABSTRACTCS);
+ if (get_field(cs, DMI_ABSTRACTCS_CMDERR) != 0) {
+ LOG_ERROR("unable to execute program: (abstractcs=0x%08x)", cs);
+ dmi_read(target, DMI_DMSTATUS);
+ return ERROR_FAIL;
+ }
+
+ return ERROR_OK;
+}
+
+void riscv013_fill_dmi_write_u64(struct target *target, char *buf, int a, uint64_t d)
+{
+ RISCV013_INFO(info);
+ buf_set_u64((unsigned char *)buf, DTM_DMI_OP_OFFSET, DTM_DMI_OP_LENGTH, DMI_OP_WRITE);
+ buf_set_u64((unsigned char *)buf, DTM_DMI_DATA_OFFSET, DTM_DMI_DATA_LENGTH, d);
+ buf_set_u64((unsigned char *)buf, DTM_DMI_ADDRESS_OFFSET, info->abits, a);
+}
+
+void riscv013_fill_dmi_read_u64(struct target *target, char *buf, int a)
+{
+ RISCV013_INFO(info);
+ buf_set_u64((unsigned char *)buf, DTM_DMI_OP_OFFSET, DTM_DMI_OP_LENGTH, DMI_OP_READ);
+ buf_set_u64((unsigned char *)buf, DTM_DMI_DATA_OFFSET, DTM_DMI_DATA_LENGTH, 0);
+ buf_set_u64((unsigned char *)buf, DTM_DMI_ADDRESS_OFFSET, info->abits, a);
+}
+
+void riscv013_fill_dmi_nop_u64(struct target *target, char *buf)
+{
+ RISCV013_INFO(info);
+ buf_set_u64((unsigned char *)buf, DTM_DMI_OP_OFFSET, DTM_DMI_OP_LENGTH, DMI_OP_NOP);
+ buf_set_u64((unsigned char *)buf, DTM_DMI_DATA_OFFSET, DTM_DMI_DATA_LENGTH, 0);
+ buf_set_u64((unsigned char *)buf, DTM_DMI_ADDRESS_OFFSET, info->abits, 0);
+}
+
+int riscv013_dmi_write_u64_bits(struct target *target)
+{
+ RISCV013_INFO(info);
+ return info->abits + DTM_DMI_DATA_LENGTH + DTM_DMI_OP_LENGTH;
+}
+
+/* Helper Functions. */
+static void riscv013_on_step_or_resume(struct target *target, bool step)
+{
+ struct riscv_program program;
+ riscv_program_init(&program, target);
+ riscv_program_fence_i(&program);
+ if (riscv_program_exec(&program, target) != ERROR_OK)
+ LOG_ERROR("Unable to execute fence.i");
+
+ /* We want to twiddle some bits in the debug CSR so debugging works. */
+ uint64_t dcsr = riscv_get_register(target, GDB_REGNO_DCSR);
+ dcsr = set_field(dcsr, CSR_DCSR_STEP, step);
+ dcsr = set_field(dcsr, CSR_DCSR_EBREAKM, 1);
+ dcsr = set_field(dcsr, CSR_DCSR_EBREAKH, 1);
+ dcsr = set_field(dcsr, CSR_DCSR_EBREAKS, 1);
+ dcsr = set_field(dcsr, CSR_DCSR_EBREAKU, 1);
+ riscv_set_register(target, GDB_REGNO_DCSR, dcsr);
+}
+
+static void riscv013_step_or_resume_current_hart(struct target *target, bool step)
+{
+ RISCV_INFO(r);
+ LOG_DEBUG("resuming hart %d (for step?=%d)", r->current_hartid, step);
+ assert(riscv_is_halted(target));
+
+ struct riscv_program program;
+ riscv_program_init(&program, target);
+ riscv_program_fence_i(&program);
+ if (riscv_program_exec(&program, target) != ERROR_OK)
+ abort();
+
+ /* Issue the halt command, and then wait for the current hart to halt. */
+ uint32_t dmcontrol = dmi_read(target, DMI_DMCONTROL);
+ dmcontrol = set_field(dmcontrol, DMI_DMCONTROL_RESUMEREQ, 1);
+ dmi_write(target, DMI_DMCONTROL, dmcontrol);
+
+ for (size_t i = 0; i < 256; ++i) {
+ usleep(10);
+ uint32_t dmstatus = dmi_read(target, DMI_DMSTATUS);
+ if (get_field(dmstatus, DMI_DMSTATUS_ALLRESUMEACK) == 0)
+ continue;
+ if (step && get_field(dmstatus, DMI_DMSTATUS_ALLHALTED) == 0)
+ continue;
+
+ dmcontrol = set_field(dmcontrol, DMI_DMCONTROL_RESUMEREQ, 0);
+ dmi_write(target, DMI_DMCONTROL, dmcontrol);
+ return;
+ }
+
+ uint32_t dmstatus = dmi_read(target, DMI_DMSTATUS);
+ dmcontrol = dmi_read(target, DMI_DMCONTROL);
+ LOG_ERROR("unable to resume hart %d", r->current_hartid);
+ LOG_ERROR(" dmcontrol=0x%08x", dmcontrol);
+ LOG_ERROR(" dmstatus =0x%08x", dmstatus);
+
+ if (step) {
+ LOG_ERROR(" was stepping, halting");
+ riscv013_halt_current_hart(target);
+ return;
+ }
+
+ abort();
+}
+
+riscv_addr_t riscv013_progbuf_addr(struct target *target)
+{
+ RISCV013_INFO(info);
+ assert(info->progbuf_addr != -1);
+ return info->progbuf_addr;
+}
+
+riscv_addr_t riscv013_progbuf_size(struct target *target)
+{
+ RISCV013_INFO(info);
+ if (info->progbuf_size == -1) {
+ uint32_t acs = dmi_read(target, DMI_ABSTRACTCS);
+ info->progbuf_size = get_field(acs, DMI_ABSTRACTCS_PROGSIZE);
+ }
+ return info->progbuf_size;
+}
+
+riscv_addr_t riscv013_data_size(struct target *target)
+{
+ RISCV013_INFO(info);
+ if (info->data_size == -1) {
+ uint32_t acs = dmi_read(target, DMI_HARTINFO);
+ info->data_size = get_field(acs, DMI_HARTINFO_DATASIZE);
+ }
+ return info->data_size;
+}
+
+riscv_addr_t riscv013_data_addr(struct target *target)
+{
+ RISCV013_INFO(info);
+ if (info->data_addr == -1) {
+ uint32_t acs = dmi_read(target, DMI_HARTINFO);
+ info->data_addr = get_field(acs, DMI_HARTINFO_DATAACCESS) ? get_field(acs, DMI_HARTINFO_DATAADDR) : 0;
+ }
+ return info->data_addr;
+}
+
+void riscv013_set_autoexec(struct target *target, int offset, bool enabled)
+{
+ if (offset >= riscv013_progbuf_size(target)) {
+ LOG_DEBUG("setting bit %d in AUTOEXECDATA to %d", offset, enabled);
+ uint32_t aa = dmi_read(target, DMI_ABSTRACTAUTO);
+ uint32_t aa_aed = get_field(aa, DMI_ABSTRACTAUTO_AUTOEXECDATA);
+ aa_aed &= ~(1 << (offset - riscv013_progbuf_size(target)));
+ aa_aed |= (enabled << (offset - riscv013_progbuf_size(target)));
+ aa = set_field(aa, DMI_ABSTRACTAUTO_AUTOEXECDATA, aa_aed);
+ dmi_write(target, DMI_ABSTRACTAUTO, aa);
+ } else {
+ LOG_DEBUG("setting bit %d in AUTOEXECPROGBUF to %d", offset, enabled);
+ uint32_t aa = dmi_read(target, DMI_ABSTRACTAUTO);
+ uint32_t aa_aed = get_field(aa, DMI_ABSTRACTAUTO_AUTOEXECPROGBUF);
+ aa_aed &= ~(1 << offset);
+ aa_aed |= (enabled << offset);
+ aa = set_field(aa, DMI_ABSTRACTAUTO_AUTOEXECPROGBUF, aa_aed);
+ dmi_write(target, DMI_ABSTRACTAUTO, aa);
+ }
+}
+
+int riscv013_debug_buffer_register(struct target *target, riscv_addr_t addr)
+{
+ if (addr >= riscv013_data_addr(target))
+ return DMI_DATA0 + (addr - riscv013_data_addr(target)) / 4;
+ else
+ return DMI_PROGBUF0 + (addr - riscv013_progbuf_addr(target)) / 4;
+}
+
+void riscv013_clear_abstract_error(struct target *target)
+{
+ uint32_t acs = dmi_read(target, DMI_ABSTRACTCS);
+ dmi_write(target, DMI_ABSTRACTCS, acs);
+}
diff --git a/src/target/riscv/riscv.c b/src/target/riscv/riscv.c
index ef6bca8..80859b6 100644
--- a/src/target/riscv/riscv.c
+++ b/src/target/riscv/riscv.c
@@ -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);
+}
diff --git a/src/target/riscv/riscv.h b/src/target/riscv/riscv.h
index 2589b64..a38d04e 100644
--- a/src/target/riscv/riscv.h
+++ b/src/target/riscv/riscv.h
@@ -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. ***/
-
-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);
-}
-
-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);
-}
-
-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;
-}
+/*** OpenOCD Interface */
+int riscv_openocd_poll(struct target *target);
+
+int riscv_openocd_halt(struct target *target);
+
+int riscv_openocd_resume(
+ struct target *target,
+ int current,
+ uint32_t address,
+ int handle_breakpoints,
+ int debug_execution
+);
+
+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
diff --git a/src/target/target.c b/src/target/target.c
index 42a8cac..7908a85 100644
--- a/src/target/target.c
+++ b/src/target/target.c
@@ -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,