+/*
+ * Support for RISC-V, debug version 0.13, which is currently (2/4/17) the
+ * latest draft.
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+#include <time.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "target/target.h"
+#include "target/algorithm.h"
+#include "target/target_type.h"
+#include "log.h"
+#include "jtag/jtag.h"
+#include "target/register.h"
+#include "target/breakpoints.h"
+#include "helper/time_support.h"
+#include "helper/list.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)
+#define DMI_PROGBUF1 (DMI_PROGBUF0 + 1)
+
+static int riscv013_on_step_or_resume(struct target *target, bool step);
+static int riscv013_step_or_resume_current_hart(struct target *target, bool step);
+static void riscv013_clear_abstract_error(struct target *target);
+
+/* Implementations of the functions in riscv_info_t. */
+static int riscv013_get_register(struct target *target,
+ riscv_reg_t *value, int hid, int rid);
+static int riscv013_set_register(struct target *target, int hartid, int regid, uint64_t value);
+static int riscv013_select_current_hart(struct target *target);
+static int riscv013_halt_current_hart(struct target *target);
+static int riscv013_resume_current_hart(struct target *target);
+static int riscv013_step_current_hart(struct target *target);
+static int riscv013_on_halt(struct target *target);
+static int riscv013_on_step(struct target *target);
+static int 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 int riscv013_write_debug_buffer(struct target *target, unsigned index,
+ riscv_insn_t d);
+static riscv_insn_t riscv013_read_debug_buffer(struct target *target, unsigned
+ index);
+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);
+static int register_read(struct target *target, uint64_t *value, uint32_t number);
+static int register_read_direct(struct target *target, uint64_t *value, uint32_t number);
+static int register_write_direct(struct target *target, unsigned number,
+ uint64_t value);
+static int read_memory(struct target *target, target_addr_t address,
+ uint32_t size, uint32_t count, uint8_t *buffer);
+static int write_memory(struct target *target, target_addr_t address,
+ uint32_t size, uint32_t count, const uint8_t *buffer);
+
+/**
+ * Since almost everything can be accomplish by scanning the dbus register, all
+ * functions here assume dbus is already selected. The exception are functions
+ * called directly by OpenOCD, which can't assume anything about what's
+ * currently in IR. They should set IR to dbus explicitly.
+ */
+
+#define get_field(reg, mask) (((reg) & (mask)) / ((mask) & ~((mask) << 1)))
+#define set_field(reg, mask, val) (((reg) & ~(mask)) | (((val) * ((mask) & ~((mask) << 1))) & (mask)))
+
+#define DIM(x) (sizeof(x)/sizeof(*x))
+
+#define CSR_DCSR_CAUSE_SWBP 1
+#define CSR_DCSR_CAUSE_TRIGGER 2
+#define CSR_DCSR_CAUSE_DEBUGINT 3
+#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 {
+ DMI_OP_NOP = 0,
+ DMI_OP_READ = 1,
+ DMI_OP_WRITE = 2
+} dmi_op_t;
+typedef enum {
+ DMI_STATUS_SUCCESS = 0,
+ DMI_STATUS_FAILED = 2,
+ DMI_STATUS_BUSY = 3
+} dmi_status_t;
+
+typedef enum {
+ RE_OK,
+ RE_FAIL,
+ RE_AGAIN
+} riscv_error_t;
+
+typedef enum slot {
+ SLOT0,
+ SLOT1,
+ SLOT_LAST,
+} slot_t;
+
+/*** Debug Bus registers. ***/
+
+#define CMDERR_NONE 0
+#define CMDERR_BUSY 1
+#define CMDERR_NOT_SUPPORTED 2
+#define CMDERR_EXCEPTION 3
+#define CMDERR_HALT_RESUME 4
+#define CMDERR_OTHER 7
+
+/*** Info about the core being debugged. ***/
+
+struct trigger {
+ uint64_t address;
+ uint32_t length;
+ uint64_t mask;
+ uint64_t value;
+ bool read, write, execute;
+ int unique_id;
+};
+
+typedef enum {
+ YNM_MAYBE,
+ YNM_YES,
+ YNM_NO
+} yes_no_maybe_t;
+
+typedef struct {
+ struct list_head list;
+ int abs_chain_position;
+ /* Indicates we already reset this DM, so don't need to do it again. */
+ bool was_reset;
+ /* Targets that are connected to this DM. */
+ struct list_head target_list;
+ /* The currently selected hartid on this DM. */
+ int current_hartid;
+} dm013_info_t;
+
+typedef struct {
+ struct list_head list;
+ struct target *target;
+} target_list_t;
+
+typedef struct {
+ /* Number of address bits in the dbus register. */
+ unsigned abits;
+ /* Number of abstract command data registers. */
+ unsigned datacount;
+ /* Number of words in the Program Buffer. */
+ unsigned progbufsize;
+
+ /* We cache the read-only bits of sbcs here. */
+ uint32_t sbcs;
+
+ yes_no_maybe_t progbuf_writable;
+ /* We only need the address so that we know the alignment of the buffer. */
+ riscv_addr_t progbuf_address;
+
+ /* Number of run-test/idle cycles the target requests we do after each dbus
+ * access. */
+ unsigned int dtmcontrol_idle;
+
+ /* This value is incremented every time a dbus access comes back as "busy".
+ * It's used to determine how many run-test/idle cycles to feed the target
+ * in between accesses. */
+ unsigned int dmi_busy_delay;
+
+ /* Number of run-test/idle cycles to add between consecutive bus master
+ * reads/writes respectively. */
+ unsigned int bus_master_write_delay, bus_master_read_delay;
+
+ /* This value is increased every time we tried to execute two commands
+ * consecutively, and the second one failed because the previous hadn't
+ * completed yet. It's used to add extra run-test/idle cycles after
+ * starting a command, so we don't have to waste time checking for busy to
+ * go low. */
+ unsigned int ac_busy_delay;
+
+ bool need_strict_step;
+
+ bool abstract_read_csr_supported;
+ bool abstract_write_csr_supported;
+ bool abstract_read_fpr_supported;
+ bool abstract_write_fpr_supported;
+
+ /* When a function returns some error due to a failure indicated by the
+ * target in cmderr, the caller can look here to see what that error was.
+ * (Compare with errno.) */
+ uint8_t cmderr;
+
+ /* Some fields from hartinfo. */
+ uint8_t datasize;
+ uint8_t dataaccess;
+ int16_t dataaddr;
+
+ /* The width of the hartsel field. */
+ unsigned hartsellen;
+
+ /* DM that provides access to this target. */
+ dm013_info_t *dm;
+} riscv013_info_t;
+
+LIST_HEAD(dm_list);
+
+static riscv013_info_t *get_info(const struct target *target)
+{
+ riscv_info_t *info = (riscv_info_t *) target->arch_info;
+ return (riscv013_info_t *) info->version_specific;
+}
+
+/**
+ * Return the DM structure for this target. If there isn't one, find it in the
+ * global list of DMs. If it's not in there, then create one and initialize it
+ * to 0.
+ */
+static dm013_info_t *get_dm(struct target *target)
+{
+ RISCV013_INFO(info);
+ if (info->dm)
+ return info->dm;
+
+ int abs_chain_position = target->tap->abs_chain_position;
+
+ dm013_info_t *entry;
+ dm013_info_t *dm = NULL;
+ list_for_each_entry(entry, &dm_list, list) {
+ if (entry->abs_chain_position == abs_chain_position) {
+ dm = entry;
+ break;
+ }
+ }
+
+ if (!dm) {
+ dm = calloc(1, sizeof(dm013_info_t));
+ dm->abs_chain_position = abs_chain_position;
+ dm->current_hartid = -1;
+ INIT_LIST_HEAD(&dm->target_list);
+ list_add(&dm->list, &dm_list);
+ }
+
+ info->dm = dm;
+ target_list_t *target_entry;
+ list_for_each_entry(target_entry, &dm->target_list, list) {
+ if (target_entry->target == target)
+ return dm;
+ }
+ target_entry = calloc(1, sizeof(*target_entry));
+ target_entry->target = target;
+ list_add(&target_entry->list, &dm->target_list);
+
+ return dm;
+}
+
+static uint32_t set_hartsel(uint32_t initial, uint32_t index)
+{
+ initial &= ~DMI_DMCONTROL_HARTSELLO;
+ initial &= ~DMI_DMCONTROL_HARTSELHI;
+
+ uint32_t index_lo = index & ((1 << DMI_DMCONTROL_HARTSELLO_LENGTH) - 1);
+ initial |= index_lo << DMI_DMCONTROL_HARTSELLO_OFFSET;
+ uint32_t index_hi = index >> DMI_DMCONTROL_HARTSELLO_LENGTH;
+ assert(index_hi < 1 << DMI_DMCONTROL_HARTSELHI_LENGTH);
+ initial |= index_hi << DMI_DMCONTROL_HARTSELHI_OFFSET;
+
+ return initial;
+}
+
+static void decode_dmi(char *text, unsigned address, unsigned data)
+{
+ static const struct {
+ unsigned address;
+ uint64_t mask;
+ const char *name;
+ } description[] = {
+ { DMI_DMCONTROL, DMI_DMCONTROL_HALTREQ, "haltreq" },
+ { DMI_DMCONTROL, DMI_DMCONTROL_RESUMEREQ, "resumereq" },
+ { DMI_DMCONTROL, DMI_DMCONTROL_HARTRESET, "hartreset" },
+ { DMI_DMCONTROL, DMI_DMCONTROL_HASEL, "hasel" },
+ { DMI_DMCONTROL, DMI_DMCONTROL_HARTSELHI, "hartselhi" },
+ { DMI_DMCONTROL, DMI_DMCONTROL_HARTSELLO, "hartsello" },
+ { DMI_DMCONTROL, DMI_DMCONTROL_NDMRESET, "ndmreset" },
+ { DMI_DMCONTROL, DMI_DMCONTROL_DMACTIVE, "dmactive" },
+ { DMI_DMCONTROL, DMI_DMCONTROL_ACKHAVERESET, "ackhavereset" },
+
+ { DMI_DMSTATUS, DMI_DMSTATUS_IMPEBREAK, "impebreak" },
+ { DMI_DMSTATUS, DMI_DMSTATUS_ALLHAVERESET, "allhavereset" },
+ { DMI_DMSTATUS, DMI_DMSTATUS_ANYHAVERESET, "anyhavereset" },
+ { DMI_DMSTATUS, DMI_DMSTATUS_ALLRESUMEACK, "allresumeack" },
+ { DMI_DMSTATUS, DMI_DMSTATUS_ANYRESUMEACK, "anyresumeack" },
+ { DMI_DMSTATUS, DMI_DMSTATUS_ALLNONEXISTENT, "allnonexistent" },
+ { DMI_DMSTATUS, DMI_DMSTATUS_ANYNONEXISTENT, "anynonexistent" },
+ { DMI_DMSTATUS, DMI_DMSTATUS_ALLUNAVAIL, "allunavail" },
+ { DMI_DMSTATUS, DMI_DMSTATUS_ANYUNAVAIL, "anyunavail" },
+ { DMI_DMSTATUS, DMI_DMSTATUS_ALLRUNNING, "allrunning" },
+ { DMI_DMSTATUS, DMI_DMSTATUS_ANYRUNNING, "anyrunning" },
+ { DMI_DMSTATUS, DMI_DMSTATUS_ALLHALTED, "allhalted" },
+ { DMI_DMSTATUS, DMI_DMSTATUS_ANYHALTED, "anyhalted" },
+ { DMI_DMSTATUS, DMI_DMSTATUS_AUTHENTICATED, "authenticated" },
+ { DMI_DMSTATUS, DMI_DMSTATUS_AUTHBUSY, "authbusy" },
+ { DMI_DMSTATUS, DMI_DMSTATUS_DEVTREEVALID, "devtreevalid" },
+ { DMI_DMSTATUS, DMI_DMSTATUS_VERSION, "version" },
+
+ { DMI_ABSTRACTCS, DMI_ABSTRACTCS_PROGBUFSIZE, "progbufsize" },
+ { DMI_ABSTRACTCS, DMI_ABSTRACTCS_BUSY, "busy" },
+ { DMI_ABSTRACTCS, DMI_ABSTRACTCS_CMDERR, "cmderr" },
+ { DMI_ABSTRACTCS, DMI_ABSTRACTCS_DATACOUNT, "datacount" },
+
+ { DMI_COMMAND, DMI_COMMAND_CMDTYPE, "cmdtype" },
+
+ { DMI_SBCS, DMI_SBCS_SBREADONADDR, "sbreadonaddr" },
+ { DMI_SBCS, DMI_SBCS_SBACCESS, "sbaccess" },
+ { DMI_SBCS, DMI_SBCS_SBAUTOINCREMENT, "sbautoincrement" },
+ { DMI_SBCS, DMI_SBCS_SBREADONDATA, "sbreadondata" },
+ { DMI_SBCS, DMI_SBCS_SBERROR, "sberror" },
+ { DMI_SBCS, DMI_SBCS_SBASIZE, "sbasize" },
+ { DMI_SBCS, DMI_SBCS_SBACCESS128, "sbaccess128" },
+ { DMI_SBCS, DMI_SBCS_SBACCESS64, "sbaccess64" },
+ { DMI_SBCS, DMI_SBCS_SBACCESS32, "sbaccess32" },
+ { DMI_SBCS, DMI_SBCS_SBACCESS16, "sbaccess16" },
+ { DMI_SBCS, DMI_SBCS_SBACCESS8, "sbaccess8" },
+ };
+
+ text[0] = 0;
+ for (unsigned i = 0; i < DIM(description); i++) {
+ if (description[i].address == address) {
+ uint64_t mask = description[i].mask;
+ unsigned value = get_field(data, mask);
+ if (value) {
+ if (i > 0)
+ *(text++) = ' ';
+ if (mask & (mask >> 1)) {
+ /* If the field is more than 1 bit wide. */
+ sprintf(text, "%s=%d", description[i].name, value);
+ } else {
+ strcpy(text, description[i].name);
+ }
+ text += strlen(text);
+ }
+ }
+ }
+}
+
+static void dump_field(const struct scan_field *field)
+{
+ static const char * const op_string[] = {"-", "r", "w", "?"};
+ static const char * const status_string[] = {"+", "?", "F", "b"};
+
+ if (debug_level < LOG_LVL_DEBUG)
+ return;
+
+ 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;
+
+ 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__, "scan",
+ "%db %s %08x @%02x -> %s %08x @%02x",
+ field->num_bits,
+ op_string[out_op], out_data, out_address,
+ status_string[in_op], in_data, in_address);
+
+ char out_text[500];
+ char in_text[500];
+ decode_dmi(out_text, out_address, out_data);
+ decode_dmi(in_text, in_address, in_data);
+ if (in_text[0] || out_text[0]) {
+ log_printf_lf(LOG_LVL_DEBUG, __FILE__, __LINE__, "scan", "%s -> %s",
+ out_text, in_text);
+ }
+}
+
+/*** Utility functions. ***/
+
+static void select_dmi(struct target *target)
+{
+ static uint8_t ir_dmi[1] = {DTM_DMI};
+ struct scan_field field = {
+ .num_bits = target->tap->ir_length,
+ .out_value = ir_dmi,
+ .in_value = NULL,
+ .check_value = NULL,
+ .check_mask = NULL
+ };
+
+ jtag_add_ir_scan(target->tap, &field, TAP_IDLE);
+}
+
+static uint32_t dtmcontrol_scan(struct target *target, uint32_t out)
+{
+ struct scan_field field;
+ uint8_t in_value[4];
+ uint8_t out_value[4];
+
+ buf_set_u32(out_value, 0, 32, out);
+
+ jtag_add_ir_scan(target->tap, &select_dtmcontrol, TAP_IDLE);
+
+ field.num_bits = 32;
+ field.out_value = out_value;
+ field.in_value = in_value;
+ jtag_add_dr_scan(target->tap, 1, &field, TAP_IDLE);
+
+ /* Always return to dmi. */
+ select_dmi(target);
+
+ int retval = jtag_execute_queue();
+ if (retval != ERROR_OK) {
+ LOG_ERROR("failed jtag scan: %d", retval);
+ return retval;
+ }
+
+ uint32_t in = buf_get_u32(field.in_value, 0, 32);
+ LOG_DEBUG("DTMCS: 0x%x -> 0x%x", out, in);
+
+ return in;
+}
+
+static void increase_dmi_busy_delay(struct target *target)
+{
+ riscv013_info_t *info = get_info(target);
+ info->dmi_busy_delay += info->dmi_busy_delay / 10 + 1;
+ LOG_DEBUG("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);
+}
+
+/**
+ * exec: If this is set, assume the scan results in an execution, so more
+ * run-test/idle cycles may be required.
+ */
+static dmi_status_t dmi_scan(struct target *target, uint32_t *address_in,
+ uint32_t *data_in, dmi_op_t op, uint32_t address_out, uint32_t data_out,
+ bool exec)
+{
+ riscv013_info_t *info = get_info(target);
+ uint8_t in[8] = {0};
+ uint8_t out[8];
+ struct scan_field field = {
+ .num_bits = info->abits + DTM_DMI_OP_LENGTH + DTM_DMI_DATA_LENGTH,
+ .out_value = out,
+ .in_value = in
+ };
+
+ assert(info->abits != 0);
+
+ buf_set_u32(out, DTM_DMI_OP_OFFSET, DTM_DMI_OP_LENGTH, op);
+ buf_set_u32(out, DTM_DMI_DATA_OFFSET, DTM_DMI_DATA_LENGTH, data_out);
+ buf_set_u32(out, DTM_DMI_ADDRESS_OFFSET, info->abits, address_out);
+
+ /* Assume dbus is already selected. */
+ jtag_add_dr_scan(target->tap, 1, &field, TAP_IDLE);
+
+ int idle_count = info->dmi_busy_delay;
+ if (exec)
+ idle_count += info->ac_busy_delay;
+
+ if (idle_count)
+ jtag_add_runtest(idle_count, TAP_IDLE);
+
+ int retval = jtag_execute_queue();
+ if (retval != ERROR_OK) {
+ LOG_ERROR("dmi_scan failed jtag scan");
+ return DMI_STATUS_FAILED;
+ }
+
+ if (data_in)
+ *data_in = buf_get_u32(in, DTM_DMI_DATA_OFFSET, DTM_DMI_DATA_LENGTH);
+
+ if (address_in)
+ *address_in = buf_get_u32(in, DTM_DMI_ADDRESS_OFFSET, info->abits);
+
+ dump_field(&field);
+
+ return buf_get_u32(in, DTM_DMI_OP_OFFSET, DTM_DMI_OP_LENGTH);
+}
+
+static int dmi_op_timeout(struct target *target, uint32_t *data_in, int dmi_op,
+ uint32_t address, uint32_t data_out, int timeout_sec)
+{
+ select_dmi(target);
+
+ dmi_status_t status;
+ uint32_t address_in;
+
+ const char *op_name;
+ switch (dmi_op) {
+ case DMI_OP_NOP:
+ op_name = "nop";
+ break;
+ case DMI_OP_READ:
+ op_name = "read";
+ break;
+ case DMI_OP_WRITE:
+ op_name = "write";
+ break;
+ default:
+ LOG_ERROR("Invalid DMI operation: %d", dmi_op);
+ return ERROR_FAIL;
+ }
+
+ time_t start = time(NULL);
+ /* This first loop performs the request. Note that if for some reason this
+ * stays busy, it is actually due to the previous access. */
+ while (1) {
+ status = dmi_scan(target, NULL, NULL, dmi_op, address, data_out,
+ false);
+ if (status == DMI_STATUS_BUSY) {
+ increase_dmi_busy_delay(target);
+ } else if (status == DMI_STATUS_SUCCESS) {
+ break;
+ } else {
+ LOG_ERROR("failed %s at 0x%x, status=%d", op_name, address, status);
+ return ERROR_FAIL;
+ }
+ if (time(NULL) - start > timeout_sec)
+ return ERROR_TIMEOUT_REACHED;
+ }
+
+ if (status != DMI_STATUS_SUCCESS) {
+ LOG_ERROR("Failed %s at 0x%x; status=%d", op_name, address, status);
+ return ERROR_FAIL;
+ }
+
+ /* This second loop ensures the request succeeded, and gets back data.
+ * Note that NOP can result in a 'busy' result as well, but that would be
+ * noticed on the next DMI access we do. */
+ while (1) {
+ status = dmi_scan(target, &address_in, data_in, 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 %s (NOP) at 0x%x, status=%d", op_name, address,
+ status);
+ return ERROR_FAIL;
+ }
+ if (time(NULL) - start > timeout_sec)
+ return ERROR_TIMEOUT_REACHED;
+ }
+
+ if (status != DMI_STATUS_SUCCESS) {
+ if (status == DMI_STATUS_FAILED || !data_in) {
+ LOG_ERROR("Failed %s (NOP) at 0x%x; status=%d", op_name, address,
+ status);
+ } else {
+ LOG_ERROR("Failed %s (NOP) at 0x%x; value=0x%x, status=%d",
+ op_name, address, *data_in, status);
+ }
+ return ERROR_FAIL;
+ }
+
+ return ERROR_OK;
+}
+
+static int dmi_op(struct target *target, uint32_t *data_in, int dmi_op,
+ uint32_t address, uint32_t data_out)
+{
+ int result = dmi_op_timeout(target, data_in, dmi_op, address, data_out,
+ riscv_command_timeout_sec);
+ if (result == ERROR_TIMEOUT_REACHED) {
+ LOG_ERROR("DMI operation didn't complete in %d seconds. The target is "
+ "either really slow or broken. You could increase the "
+ "timeout with riscv set_command_timeout_sec.",
+ riscv_command_timeout_sec);
+ return ERROR_FAIL;
+ }
+ return result;
+}
+
+static int dmi_read(struct target *target, uint32_t *value, uint32_t address)
+{
+ return dmi_op(target, value, DMI_OP_READ, address, 0);
+}
+
+static int dmi_write(struct target *target, uint32_t address, uint32_t value)
+{
+ return dmi_op(target, NULL, DMI_OP_WRITE, address, value);
+}
+
+int dmstatus_read_timeout(struct target *target, uint32_t *dmstatus,
+ bool authenticated, unsigned timeout_sec)
+{
+ int result = dmi_op_timeout(target, dmstatus, DMI_OP_READ, DMI_DMSTATUS, 0,
+ timeout_sec);
+ if (result != ERROR_OK)
+ return result;
+ if (authenticated && !get_field(*dmstatus, DMI_DMSTATUS_AUTHENTICATED)) {
+ LOG_ERROR("Debugger is not authenticated to target Debug Module. "
+ "(dmstatus=0x%x). Use `riscv authdata_read` and "
+ "`riscv authdata_write` commands to authenticate.", *dmstatus);
+ return ERROR_FAIL;
+ }
+ return ERROR_OK;
+}
+
+int dmstatus_read(struct target *target, uint32_t *dmstatus,
+ bool authenticated)
+{
+ return dmstatus_read_timeout(target, dmstatus, authenticated,
+ riscv_command_timeout_sec);
+}
+
+static void increase_ac_busy_delay(struct target *target)
+{
+ riscv013_info_t *info = get_info(target);
+ info->ac_busy_delay += info->ac_busy_delay / 10 + 1;
+ LOG_DEBUG("dtmcontrol_idle=%d, dmi_busy_delay=%d, ac_busy_delay=%d",
+ info->dtmcontrol_idle, info->dmi_busy_delay,
+ info->ac_busy_delay);
+}
+
+uint32_t abstract_register_size(unsigned width)
+{
+ switch (width) {
+ case 32:
+ return set_field(0, AC_ACCESS_REGISTER_SIZE, 2);
+ case 64:
+ return set_field(0, AC_ACCESS_REGISTER_SIZE, 3);
+ break;
+ case 128:
+ return set_field(0, AC_ACCESS_REGISTER_SIZE, 4);
+ break;
+ default:
+ LOG_ERROR("Unsupported register width: %d", width);
+ return 0;
+ }
+}
+
+static int wait_for_idle(struct target *target, uint32_t *abstractcs)
+{
+ RISCV013_INFO(info);
+ time_t start = time(NULL);
+ while (1) {
+ if (dmi_read(target, abstractcs, DMI_ABSTRACTCS) != ERROR_OK)
+ return ERROR_FAIL;
+
+ if (get_field(*abstractcs, DMI_ABSTRACTCS_BUSY) == 0)
+ return ERROR_OK;
+
+ if (time(NULL) - start > riscv_command_timeout_sec) {
+ info->cmderr = get_field(*abstractcs, DMI_ABSTRACTCS_CMDERR);
+ if (info->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[info->cmderr], *abstractcs);
+ }
+
+ LOG_ERROR("Timed out after %ds waiting for busy to go low (abstractcs=0x%x). "
+ "Increase the timeout with riscv set_command_timeout_sec.",
+ riscv_command_timeout_sec,
+ *abstractcs);
+ return ERROR_FAIL;
+ }
+ }
+}
+
+static int execute_abstract_command(struct target *target, uint32_t command)
+{
+ RISCV013_INFO(info);
+ LOG_DEBUG("command=0x%x", command);
+ dmi_write(target, DMI_COMMAND, command);
+
+ uint32_t abstractcs = 0;
+ wait_for_idle(target, &abstractcs);
+
+ info->cmderr = get_field(abstractcs, DMI_ABSTRACTCS_CMDERR);
+ if (info->cmderr != 0) {
+ LOG_DEBUG("command 0x%x failed; abstractcs=0x%x", command, abstractcs);
+ /* Clear the error. */
+ dmi_write(target, DMI_ABSTRACTCS, set_field(0, DMI_ABSTRACTCS_CMDERR,
+ info->cmderr));
+ return ERROR_FAIL;
+ }
+
+ return ERROR_OK;
+}
+
+static riscv_reg_t read_abstract_arg(struct target *target, unsigned index,
+ unsigned size_bits)
+{
+ riscv_reg_t value = 0;
+ uint32_t v;
+ unsigned offset = index * size_bits / 32;
+ switch (size_bits) {
+ default:
+ LOG_ERROR("Unsupported size: %d", size_bits);
+ return ~0;
+ case 64:
+ dmi_read(target, &v, DMI_DATA0 + offset + 1);
+ value |= ((uint64_t) v) << 32;
+ /* falls through */
+ case 32:
+ dmi_read(target, &v, DMI_DATA0 + offset);
+ value |= v;
+ }
+ return value;
+}
+
+static int write_abstract_arg(struct target *target, unsigned index,
+ riscv_reg_t value, unsigned size_bits)
+{
+ unsigned offset = index * size_bits / 32;
+ switch (size_bits) {
+ default:
+ LOG_ERROR("Unsupported size: %d", size_bits);
+ return ERROR_FAIL;
+ case 64:
+ dmi_write(target, DMI_DATA0 + offset + 1, value >> 32);
+ /* falls through */
+ case 32:
+ dmi_write(target, DMI_DATA0 + offset, value);
+ }
+ return ERROR_OK;
+}
+
+/**
+ * @size in bits
+ */
+static uint32_t access_register_command(uint32_t number, unsigned size,
+ uint32_t flags)
+{
+ uint32_t command = set_field(0, DMI_COMMAND_CMDTYPE, 0);
+ switch (size) {
+ case 32:
+ command = set_field(command, AC_ACCESS_REGISTER_SIZE, 2);
+ break;
+ case 64:
+ command = set_field(command, AC_ACCESS_REGISTER_SIZE, 3);
+ break;
+ default:
+ assert(0);
+ }
+
+ if (number <= GDB_REGNO_XPR31) {
+ command = set_field(command, AC_ACCESS_REGISTER_REGNO,
+ 0x1000 + number - GDB_REGNO_ZERO);
+ } else if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) {
+ command = set_field(command, AC_ACCESS_REGISTER_REGNO,
+ 0x1020 + number - GDB_REGNO_FPR0);
+ } else if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095) {
+ command = set_field(command, AC_ACCESS_REGISTER_REGNO,
+ number - GDB_REGNO_CSR0);
+ } else {
+ assert(0);
+ }
+
+ command |= flags;
+
+ return command;
+}
+
+static int register_read_abstract(struct target *target, uint64_t *value,
+ uint32_t number, unsigned size)
+{
+ RISCV013_INFO(info);
+
+ if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31 &&
+ !info->abstract_read_fpr_supported)
+ return ERROR_FAIL;
+ if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095 &&
+ !info->abstract_read_csr_supported)
+ return ERROR_FAIL;
+
+ uint32_t command = access_register_command(number, size,
+ AC_ACCESS_REGISTER_TRANSFER);
+
+ int result = execute_abstract_command(target, command);
+ if (result != ERROR_OK) {
+ if (info->cmderr == CMDERR_NOT_SUPPORTED) {
+ if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) {
+ info->abstract_read_fpr_supported = false;
+ LOG_INFO("Disabling abstract command reads from FPRs.");
+ } else if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095) {
+ info->abstract_read_csr_supported = false;
+ LOG_INFO("Disabling abstract command reads from CSRs.");
+ }
+ }
+ return result;
+ }
+
+ if (value)
+ *value = read_abstract_arg(target, 0, size);
+
+ return ERROR_OK;
+}
+
+static int register_write_abstract(struct target *target, uint32_t number,
+ uint64_t value, unsigned size)
+{
+ RISCV013_INFO(info);
+
+ if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31 &&
+ !info->abstract_write_fpr_supported)
+ return ERROR_FAIL;
+ if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095 &&
+ !info->abstract_write_csr_supported)
+ return ERROR_FAIL;
+
+ uint32_t command = access_register_command(number, size,
+ AC_ACCESS_REGISTER_TRANSFER |
+ AC_ACCESS_REGISTER_WRITE);
+
+ if (write_abstract_arg(target, 0, value, size) != ERROR_OK)
+ return ERROR_FAIL;
+
+ int result = execute_abstract_command(target, command);
+ if (result != ERROR_OK) {
+ if (info->cmderr == CMDERR_NOT_SUPPORTED) {
+ if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) {
+ info->abstract_write_fpr_supported = false;
+ LOG_INFO("Disabling abstract command writes to FPRs.");
+ } else if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095) {
+ info->abstract_write_csr_supported = false;
+ LOG_INFO("Disabling abstract command writes to CSRs.");
+ }
+ }
+ return result;
+ }
+
+ return ERROR_OK;
+}
+
+static int examine_progbuf(struct target *target)
+{
+ riscv013_info_t *info = get_info(target);
+
+ if (info->progbuf_writable != YNM_MAYBE)
+ return ERROR_OK;
+
+ /* Figure out if progbuf is writable. */
+
+ if (info->progbufsize < 1) {
+ info->progbuf_writable = YNM_NO;
+ LOG_INFO("No program buffer present.");
+ return ERROR_OK;
+ }
+
+ uint64_t s0;
+ if (register_read(target, &s0, GDB_REGNO_S0) != ERROR_OK)
+ return ERROR_FAIL;
+
+ struct riscv_program program;
+ riscv_program_init(&program, target);
+ riscv_program_insert(&program, auipc(S0));
+ if (riscv_program_exec(&program, target) != ERROR_OK)
+ return ERROR_FAIL;
+
+ if (register_read_direct(target, &info->progbuf_address, GDB_REGNO_S0) != ERROR_OK)
+ return ERROR_FAIL;
+
+ riscv_program_init(&program, target);
+ riscv_program_insert(&program, sw(S0, S0, 0));
+ int result = riscv_program_exec(&program, target);
+
+ if (register_write_direct(target, GDB_REGNO_S0, s0) != ERROR_OK)
+ return ERROR_FAIL;
+
+ if (result != ERROR_OK) {
+ /* This program might have failed if the program buffer is not
+ * writable. */
+ info->progbuf_writable = YNM_NO;
+ return ERROR_OK;
+ }
+
+ uint32_t written;
+ if (dmi_read(target, &written, DMI_PROGBUF0) != ERROR_OK)
+ return ERROR_FAIL;
+ if (written == (uint32_t) info->progbuf_address) {
+ LOG_INFO("progbuf is writable at 0x%" PRIx64,
+ info->progbuf_address);
+ info->progbuf_writable = YNM_YES;
+
+ } else {
+ LOG_INFO("progbuf is not writeable at 0x%" PRIx64,
+ info->progbuf_address);
+ info->progbuf_writable = YNM_NO;
+ }
+
+ return ERROR_OK;
+}
+
+typedef enum {
+ SPACE_DMI_DATA,
+ SPACE_DMI_PROGBUF,
+ SPACE_DMI_RAM
+} memory_space_t;
+
+typedef struct {
+ /* How can the debugger access this memory? */
+ memory_space_t memory_space;
+ /* Memory address to access the scratch memory from the hart. */
+ riscv_addr_t hart_address;
+ /* Memory address to access the scratch memory from the debugger. */
+ riscv_addr_t debug_address;
+ struct working_area *area;
+} scratch_mem_t;
+
+/**
+ * Find some scratch memory to be used with the given program.
+ */
+static int scratch_reserve(struct target *target,
+ scratch_mem_t *scratch,
+ struct riscv_program *program,
+ unsigned size_bytes)
+{
+ riscv_addr_t alignment = 1;
+ while (alignment < size_bytes)
+ alignment *= 2;
+
+ scratch->area = NULL;
+
+ riscv013_info_t *info = get_info(target);
+
+ if (info->dataaccess == 1) {
+ /* Sign extend dataaddr. */
+ scratch->hart_address = info->dataaddr;
+ if (info->dataaddr & (1<<11))
+ scratch->hart_address |= 0xfffffffffffff000ULL;
+ /* Align. */
+ scratch->hart_address = (scratch->hart_address + alignment - 1) & ~(alignment - 1);
+
+ if ((size_bytes + scratch->hart_address - info->dataaddr + 3) / 4 >=
+ info->datasize) {
+ scratch->memory_space = SPACE_DMI_DATA;
+ scratch->debug_address = (scratch->hart_address - info->dataaddr) / 4;
+ return ERROR_OK;
+ }
+ }
+
+ if (examine_progbuf(target) != ERROR_OK)
+ return ERROR_FAIL;
+
+ /* Allow for ebreak at the end of the program. */
+ unsigned program_size = (program->instruction_count + 1) * 4;
+ scratch->hart_address = (info->progbuf_address + program_size + alignment - 1) &
+ ~(alignment - 1);
+ if ((size_bytes + scratch->hart_address - info->progbuf_address + 3) / 4 >=
+ info->progbufsize) {
+ scratch->memory_space = SPACE_DMI_PROGBUF;
+ scratch->debug_address = (scratch->hart_address - info->progbuf_address) / 4;
+ return ERROR_OK;
+ }
+
+ if (target_alloc_working_area(target, size_bytes + alignment - 1,
+ &scratch->area) == ERROR_OK) {
+ scratch->hart_address = (scratch->area->address + alignment - 1) &
+ ~(alignment - 1);
+ scratch->memory_space = SPACE_DMI_RAM;
+ scratch->debug_address = scratch->hart_address;
+ return ERROR_OK;
+ }
+
+ LOG_ERROR("Couldn't find %d bytes of scratch RAM to use. Please configure "
+ "a work area with 'configure -work-area-phys'.", size_bytes);
+ return ERROR_FAIL;
+}
+
+static int scratch_release(struct target *target,
+ scratch_mem_t *scratch)
+{
+ if (scratch->area)
+ return target_free_working_area(target, scratch->area);
+
+ return ERROR_OK;
+}
+
+static int scratch_read64(struct target *target, scratch_mem_t *scratch,
+ uint64_t *value)
+{
+ uint32_t v;
+ switch (scratch->memory_space) {
+ case SPACE_DMI_DATA:
+ if (dmi_read(target, &v, DMI_DATA0 + scratch->debug_address) != ERROR_OK)
+ return ERROR_FAIL;
+ *value = v;
+ if (dmi_read(target, &v, DMI_DATA1 + scratch->debug_address) != ERROR_OK)
+ return ERROR_FAIL;
+ *value |= ((uint64_t) v) << 32;
+ break;
+ case SPACE_DMI_PROGBUF:
+ if (dmi_read(target, &v, DMI_PROGBUF0 + scratch->debug_address) != ERROR_OK)
+ return ERROR_FAIL;
+ *value = v;
+ if (dmi_read(target, &v, DMI_PROGBUF1 + scratch->debug_address) != ERROR_OK)
+ return ERROR_FAIL;
+ *value |= ((uint64_t) v) << 32;
+ break;
+ case SPACE_DMI_RAM:
+ {
+ uint8_t buffer[8];
+ if (read_memory(target, scratch->debug_address, 4, 2, buffer) != ERROR_OK)
+ return ERROR_FAIL;
+ *value = buffer[0] |
+ (((uint64_t) buffer[1]) << 8) |
+ (((uint64_t) buffer[2]) << 16) |
+ (((uint64_t) buffer[3]) << 24) |
+ (((uint64_t) buffer[4]) << 32) |
+ (((uint64_t) buffer[5]) << 40) |
+ (((uint64_t) buffer[6]) << 48) |
+ (((uint64_t) buffer[7]) << 56);
+ }
+ break;
+ }
+ return ERROR_OK;
+}
+
+static int scratch_write64(struct target *target, scratch_mem_t *scratch,
+ uint64_t value)
+{
+ switch (scratch->memory_space) {
+ case SPACE_DMI_DATA:
+ dmi_write(target, DMI_DATA0 + scratch->debug_address, value);
+ dmi_write(target, DMI_DATA1 + scratch->debug_address, value >> 32);
+ break;
+ case SPACE_DMI_PROGBUF:
+ dmi_write(target, DMI_PROGBUF0 + scratch->debug_address, value);
+ dmi_write(target, DMI_PROGBUF1 + scratch->debug_address, value >> 32);
+ break;
+ case SPACE_DMI_RAM:
+ {
+ uint8_t buffer[8] = {
+ value,
+ value >> 8,
+ value >> 16,
+ value >> 24,
+ value >> 32,
+ value >> 40,
+ value >> 48,
+ value >> 56
+ };
+ if (write_memory(target, scratch->debug_address, 4, 2, buffer) != ERROR_OK)
+ return ERROR_FAIL;
+ }
+ break;
+ }
+ return ERROR_OK;
+}
+
+/** Return register size in bits. */
+static unsigned register_size(struct target *target, unsigned number)
+{
+ /* If reg_cache hasn't been initialized yet, make a guess. We need this for
+ * when this function is called during examine(). */
+ if (target->reg_cache)
+ return target->reg_cache->reg_list[number].size;
+ else
+ return riscv_xlen(target);
+}
+
+/**
+ * Immediately write the new value to the requested register. This mechanism
+ * bypasses any caches.
+ */
+static int register_write_direct(struct target *target, unsigned number,
+ uint64_t value)
+{
+ RISCV013_INFO(info);
+ RISCV_INFO(r);
+
+ LOG_DEBUG("[%d] reg[0x%x] <- 0x%" PRIx64, riscv_current_hartid(target),
+ number, value);
+
+ int result = register_write_abstract(target, number, value,
+ register_size(target, number));
+ if (result == ERROR_OK && target->reg_cache) {
+ struct reg *reg = &target->reg_cache->reg_list[number];
+ buf_set_u64(reg->value, 0, reg->size, value);
+ reg->valid = true;
+ }
+ if (result == ERROR_OK || info->progbufsize + r->impebreak < 2 ||
+ !riscv_is_halted(target))
+ return result;
+
+ struct riscv_program program;
+ riscv_program_init(&program, target);
+
+ uint64_t s0;
+ if (register_read(target, &s0, GDB_REGNO_S0) != ERROR_OK)
+ return ERROR_FAIL;
+
+ scratch_mem_t scratch;
+ bool use_scratch = false;
+ if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31 &&
+ riscv_supports_extension(target, riscv_current_hartid(target), 'D') &&
+ riscv_xlen(target) < 64) {
+ /* There are no instructions to move all the bits from a register, so
+ * we need to use some scratch RAM. */
+ use_scratch = true;
+ riscv_program_insert(&program, fld(number - GDB_REGNO_FPR0, S0, 0));
+
+ if (scratch_reserve(target, &scratch, &program, 8) != ERROR_OK)
+ return ERROR_FAIL;
+
+ if (register_write_direct(target, GDB_REGNO_S0, scratch.hart_address)
+ != ERROR_OK) {
+ scratch_release(target, &scratch);
+ return ERROR_FAIL;
+ }
+
+ if (scratch_write64(target, &scratch, value) != ERROR_OK) {
+ scratch_release(target, &scratch);
+ return ERROR_FAIL;
+ }
+
+ } else {
+ if (register_write_direct(target, GDB_REGNO_S0, value) != ERROR_OK)
+ return ERROR_FAIL;
+
+ if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) {
+ if (riscv_supports_extension(target, riscv_current_hartid(target), 'D'))
+ riscv_program_insert(&program, fmv_d_x(number - GDB_REGNO_FPR0, S0));
+ else
+ riscv_program_insert(&program, fmv_w_x(number - GDB_REGNO_FPR0, S0));
+ } else if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095) {
+ riscv_program_csrw(&program, S0, number);
+ } else {
+ LOG_ERROR("Unsupported register (enum gdb_regno)(%d)", number);
+ return ERROR_FAIL;
+ }
+ }
+
+ int exec_out = riscv_program_exec(&program, target);
+ /* Don't message on error. Probably the register doesn't exist. */
+ if (exec_out == ERROR_OK && target->reg_cache) {
+ struct reg *reg = &target->reg_cache->reg_list[number];
+ buf_set_u64(reg->value, 0, reg->size, value);
+ reg->valid = true;
+ }
+
+ if (use_scratch)
+ scratch_release(target, &scratch);
+
+ /* Restore S0. */
+ if (register_write_direct(target, GDB_REGNO_S0, s0) != ERROR_OK)
+ return ERROR_FAIL;
+
+ return exec_out;
+}
+
+/** Return the cached value, or read from the target if necessary. */
+static int register_read(struct target *target, uint64_t *value, uint32_t number)
+{
+ if (number == GDB_REGNO_ZERO) {
+ *value = 0;
+ return ERROR_OK;
+ }
+ if (target->reg_cache &&
+ (number <= GDB_REGNO_XPR31 ||
+ (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31))) {
+ /* Only check the cache for registers that we know won't spontaneously
+ * change. */
+ struct reg *reg = &target->reg_cache->reg_list[number];
+ if (reg && reg->valid) {
+ *value = buf_get_u64(reg->value, 0, reg->size);
+ return ERROR_OK;
+ }
+ }
+ int result = register_read_direct(target, value, number);
+ if (result != ERROR_OK)
+ return ERROR_FAIL;
+ if (target->reg_cache) {
+ struct reg *reg = &target->reg_cache->reg_list[number];
+ buf_set_u64(reg->value, 0, reg->size, *value);
+ reg->valid = true;
+ }
+ return ERROR_OK;
+}
+
+/** Actually read registers from the target right now. */
+static int register_read_direct(struct target *target, uint64_t *value, uint32_t number)
+{
+ RISCV013_INFO(info);
+ RISCV_INFO(r);
+
+ int result = register_read_abstract(target, value, number,
+ register_size(target, number));
+
+ if (result != ERROR_OK &&
+ info->progbufsize + r->impebreak >= 2 &&
+ number > GDB_REGNO_XPR31) {
+ struct riscv_program program;
+ riscv_program_init(&program, target);
+
+ scratch_mem_t scratch;
+ bool use_scratch = false;
+
+ uint64_t s0;
+ if (register_read(target, &s0, GDB_REGNO_S0) != ERROR_OK)
+ return ERROR_FAIL;
+
+ /* Write program to move data into s0. */
+
+ uint64_t mstatus;
+ if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) {
+ if (register_read(target, &mstatus, GDB_REGNO_MSTATUS) != ERROR_OK)
+ return ERROR_FAIL;
+ if ((mstatus & MSTATUS_FS) == 0)
+ if (register_write_direct(target, GDB_REGNO_MSTATUS,
+ set_field(mstatus, MSTATUS_FS, 1)) != ERROR_OK)
+ return ERROR_FAIL;
+
+ if (riscv_supports_extension(target, riscv_current_hartid(target), 'D')
+ && riscv_xlen(target) < 64) {
+ /* There are no instructions to move all the bits from a
+ * register, so we need to use some scratch RAM. */
+ riscv_program_insert(&program, fsd(number - GDB_REGNO_FPR0, S0,
+ 0));
+
+ if (scratch_reserve(target, &scratch, &program, 8) != ERROR_OK)
+ return ERROR_FAIL;
+ use_scratch = true;
+
+ if (register_write_direct(target, GDB_REGNO_S0,
+ scratch.hart_address) != ERROR_OK) {
+ scratch_release(target, &scratch);
+ return ERROR_FAIL;
+ }
+ } else if (riscv_supports_extension(target,
+ riscv_current_hartid(target), 'D')) {
+ riscv_program_insert(&program, fmv_x_d(S0, number - GDB_REGNO_FPR0));
+ } else {
+ riscv_program_insert(&program, fmv_x_w(S0, number - GDB_REGNO_FPR0));
+ }
+ } else if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095) {
+ riscv_program_csrr(&program, S0, number);
+ } else {
+ LOG_ERROR("Unsupported register (enum gdb_regno)(%d)", number);
+ return ERROR_FAIL;
+ }
+
+ /* Execute program. */
+ result = riscv_program_exec(&program, target);
+ /* Don't message on error. Probably the register doesn't exist. */
+
+ if (use_scratch) {
+ result = scratch_read64(target, &scratch, value);
+ scratch_release(target, &scratch);
+ if (result != ERROR_OK)
+ return result;
+ } else {
+ /* Read S0 */
+ if (register_read_direct(target, value, GDB_REGNO_S0) != ERROR_OK)
+ return ERROR_FAIL;
+ }
+
+ if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31 &&
+ (mstatus & MSTATUS_FS) == 0)
+ if (register_write_direct(target, GDB_REGNO_MSTATUS, mstatus) != ERROR_OK)
+ return ERROR_FAIL;
+
+ /* Restore S0. */
+ if (register_write_direct(target, GDB_REGNO_S0, s0) != ERROR_OK)
+ return ERROR_FAIL;
+ }
+
+ if (result == ERROR_OK) {
+ LOG_DEBUG("[%d] reg[0x%x] = 0x%" PRIx64, riscv_current_hartid(target),
+ number, *value);
+ }
+
+ return result;
+}
+
+int wait_for_authbusy(struct target *target, uint32_t *dmstatus)
+{
+ time_t start = time(NULL);
+ while (1) {
+ uint32_t value;
+ if (dmstatus_read(target, &value, false) != ERROR_OK)
+ return ERROR_FAIL;
+ if (dmstatus)
+ *dmstatus = value;
+ if (!get_field(value, DMI_DMSTATUS_AUTHBUSY))
+ break;
+ if (time(NULL) - start > riscv_command_timeout_sec) {
+ LOG_ERROR("Timed out after %ds waiting for authbusy to go low (dmstatus=0x%x). "
+ "Increase the timeout with riscv set_command_timeout_sec.",
+ riscv_command_timeout_sec,
+ value);
+ return ERROR_FAIL;
+ }
+ }
+
+ return ERROR_OK;
+}
+
+/*** OpenOCD target functions. ***/
+
+static void deinit_target(struct target *target)
+{
+ LOG_DEBUG("riscv_deinit_target()");
+ riscv_info_t *info = (riscv_info_t *) target->arch_info;
+ free(info->version_specific);
+ info->version_specific = NULL;
+}
+
+static int examine(struct target *target)
+{
+ /* Don't need to select dbus, since the first thing we do is read dtmcontrol. */
+
+ uint32_t dtmcontrol = dtmcontrol_scan(target, 0);
+ LOG_DEBUG("dtmcontrol=0x%x", dtmcontrol);
+ LOG_DEBUG(" dmireset=%d", get_field(dtmcontrol, DTM_DTMCS_DMIRESET));
+ LOG_DEBUG(" idle=%d", get_field(dtmcontrol, DTM_DTMCS_IDLE));
+ LOG_DEBUG(" dmistat=%d", get_field(dtmcontrol, DTM_DTMCS_DMISTAT));
+ LOG_DEBUG(" abits=%d", get_field(dtmcontrol, DTM_DTMCS_ABITS));
+ LOG_DEBUG(" version=%d", get_field(dtmcontrol, DTM_DTMCS_VERSION));
+ if (dtmcontrol == 0) {
+ LOG_ERROR("dtmcontrol is 0. Check JTAG connectivity/board power.");
+ return ERROR_FAIL;
+ }
+ if (get_field(dtmcontrol, DTM_DTMCS_VERSION) != 1) {
+ LOG_ERROR("Unsupported DTM version %d. (dtmcontrol=0x%x)",
+ get_field(dtmcontrol, DTM_DTMCS_VERSION), dtmcontrol);
+ return ERROR_FAIL;
+ }
+
+ riscv013_info_t *info = get_info(target);
+ info->abits = get_field(dtmcontrol, DTM_DTMCS_ABITS);
+ info->dtmcontrol_idle = get_field(dtmcontrol, DTM_DTMCS_IDLE);
+
+ uint32_t dmstatus;
+ if (dmstatus_read(target, &dmstatus, false) != ERROR_OK)
+ return ERROR_FAIL;
+ LOG_DEBUG("dmstatus: 0x%08x", dmstatus);
+ if (get_field(dmstatus, DMI_DMSTATUS_VERSION) != 2) {
+ LOG_ERROR("OpenOCD only supports Debug Module version 2, not %d "
+ "(dmstatus=0x%x)", get_field(dmstatus, DMI_DMSTATUS_VERSION), dmstatus);
+ return ERROR_FAIL;
+ }
+
+ /* Reset the Debug Module. */
+ dm013_info_t *dm = get_dm(target);
+ if (!dm->was_reset) {
+ dmi_write(target, DMI_DMCONTROL, 0);
+ dmi_write(target, DMI_DMCONTROL, DMI_DMCONTROL_DMACTIVE);
+ dm->was_reset = true;
+ }
+
+ dmi_write(target, DMI_DMCONTROL, DMI_DMCONTROL_HARTSELLO |
+ DMI_DMCONTROL_HARTSELHI | DMI_DMCONTROL_DMACTIVE);
+ uint32_t dmcontrol;
+ if (dmi_read(target, &dmcontrol, DMI_DMCONTROL) != ERROR_OK)
+ return ERROR_FAIL;
+
+ if (!get_field(dmcontrol, DMI_DMCONTROL_DMACTIVE)) {
+ LOG_ERROR("Debug Module did not become active. dmcontrol=0x%x",
+ dmcontrol);
+ return ERROR_FAIL;
+ }
+
+ uint32_t hartsel =
+ (get_field(dmcontrol, DMI_DMCONTROL_HARTSELHI) <<
+ DMI_DMCONTROL_HARTSELLO_LENGTH) |
+ get_field(dmcontrol, DMI_DMCONTROL_HARTSELLO);
+ info->hartsellen = 0;
+ while (hartsel & 1) {
+ info->hartsellen++;
+ hartsel >>= 1;
+ }
+ LOG_DEBUG("hartsellen=%d", info->hartsellen);
+
+ uint32_t hartinfo;
+ if (dmi_read(target, &hartinfo, DMI_HARTINFO) != ERROR_OK)
+ return ERROR_FAIL;
+
+ info->datasize = get_field(hartinfo, DMI_HARTINFO_DATASIZE);
+ info->dataaccess = get_field(hartinfo, DMI_HARTINFO_DATAACCESS);
+ info->dataaddr = get_field(hartinfo, DMI_HARTINFO_DATAADDR);
+
+ if (!get_field(dmstatus, DMI_DMSTATUS_AUTHENTICATED)) {
+ LOG_ERROR("Debugger is not authenticated to target Debug Module. "
+ "(dmstatus=0x%x). Use `riscv authdata_read` and "
+ "`riscv authdata_write` commands to authenticate.", dmstatus);
+ /* If we return ERROR_FAIL here, then in a multicore setup the next
+ * core won't be examined, which means we won't set up the
+ * authentication commands for them, which means the config script
+ * needs to be a lot more complex. */
+ return ERROR_OK;
+ }
+
+ if (dmi_read(target, &info->sbcs, DMI_SBCS) != ERROR_OK)
+ return ERROR_FAIL;
+
+ /* Check that abstract data registers are accessible. */
+ uint32_t abstractcs;
+ if (dmi_read(target, &abstractcs, DMI_ABSTRACTCS) != ERROR_OK)
+ return ERROR_FAIL;
+ info->datacount = get_field(abstractcs, DMI_ABSTRACTCS_DATACOUNT);
+ info->progbufsize = get_field(abstractcs, DMI_ABSTRACTCS_PROGBUFSIZE);
+
+ LOG_INFO("datacount=%d progbufsize=%d", info->datacount, info->progbufsize);
+
+ RISCV_INFO(r);
+ r->impebreak = get_field(dmstatus, DMI_DMSTATUS_IMPEBREAK);
+
+ if (info->progbufsize + r->impebreak < 2) {
+ LOG_WARNING("We won't be able to execute fence instructions on this "
+ "target. Memory may not always appear consistent. "
+ "(progbufsize=%d, impebreak=%d)", info->progbufsize,
+ r->impebreak);
+ }
+
+ /* Before doing anything else we must first enumerate the harts. */
+
+ /* Don't call any riscv_* functions until after we've counted the number of
+ * cores and initialized registers. */
+ for (int i = 0; i < MIN(RISCV_MAX_HARTS, 1 << info->hartsellen); ++i) {
+ if (!riscv_rtos_enabled(target) && i != target->coreid)
+ continue;
+
+ r->current_hartid = i;
+ if (riscv013_select_current_hart(target) != ERROR_OK)
+ return ERROR_FAIL;
+
+ uint32_t s;
+ if (dmstatus_read(target, &s, true) != ERROR_OK)
+ return ERROR_FAIL;
+ if (get_field(s, DMI_DMSTATUS_ANYNONEXISTENT))
+ break;
+ r->hart_count = i + 1;
+
+ if (get_field(s, DMI_DMSTATUS_ANYHAVERESET))
+ dmi_write(target, DMI_DMCONTROL,
+ set_hartsel(DMI_DMCONTROL_DMACTIVE | DMI_DMCONTROL_ACKHAVERESET, i));
+
+ if (!riscv_is_halted(target)) {
+ if (riscv013_halt_current_hart(target) != ERROR_OK) {
+ LOG_ERROR("Fatal: Hart %d failed to halt during examine()", i);
+ return ERROR_FAIL;
+ }
+ }
+
+ /* Without knowing anything else we can at least mess with the
+ * program buffer. */
+ r->debug_buffer_size[i] = info->progbufsize;
+
+ int result = register_read_abstract(target, NULL, GDB_REGNO_S0, 64);
+ if (result == ERROR_OK)
+ r->xlen[i] = 64;
+ else
+ r->xlen[i] = 32;
+
+ if (register_read(target, &r->misa[i], GDB_REGNO_MISA)) {
+ LOG_ERROR("Fatal: Failed to read MISA from hart %d.", i);
+ return ERROR_FAIL;
+ }
+
+ /* Now init registers based on what we discovered. */
+ if (riscv_init_registers(target) != ERROR_OK)
+ return ERROR_FAIL;
+
+ /* Display this as early as possible to help people who are using
+ * really slow simulators. */
+ LOG_DEBUG(" hart %d: XLEN=%d, misa=0x%" PRIx64, i, r->xlen[i],
+ r->misa[i]);
+ }
+
+ LOG_DEBUG("Enumerated %d harts", r->hart_count);
+
+ if (r->hart_count == 0) {
+ LOG_ERROR("No harts found!");
+ return ERROR_FAIL;
+ }
+
+ /* Resumes all the harts, so the debugger can later pause them. */
+ /* TODO: Only do this if the harts were halted to start with. */
+ riscv_resume_all_harts(target);
+ target->state = TARGET_RUNNING;
+
+ target_set_examined(target);
+
+ /* Some regression suites rely on seeing 'Examined RISC-V core' to know
+ * when they can connect with gdb/telnet.
+ * We will need to update those suites if we want to change that text. */
+ LOG_INFO("Examined RISC-V core; found %d harts",
+ riscv_count_harts(target));
+ for (int i = 0; i < riscv_count_harts(target); ++i) {
+ if (riscv_hart_enabled(target, i)) {
+ LOG_INFO(" hart %d: XLEN=%d, misa=0x%" PRIx64, i, r->xlen[i],
+ r->misa[i]);
+ } else {
+ LOG_INFO(" hart %d: currently disabled", i);
+ }
+ }
+ return ERROR_OK;
+}
+
+int riscv013_authdata_read(struct target *target, uint32_t *value)
+{
+ if (wait_for_authbusy(target, NULL) != ERROR_OK)
+ return ERROR_FAIL;
+
+ return dmi_read(target, value, DMI_AUTHDATA);
+}
+
+int riscv013_authdata_write(struct target *target, uint32_t value)
+{
+ uint32_t before, after;
+ if (wait_for_authbusy(target, &before) != ERROR_OK)
+ return ERROR_FAIL;
+
+ dmi_write(target, DMI_AUTHDATA, value);
+
+ if (wait_for_authbusy(target, &after) != ERROR_OK)
+ return ERROR_FAIL;
+
+ if (!get_field(before, DMI_DMSTATUS_AUTHENTICATED) &&
+ get_field(after, DMI_DMSTATUS_AUTHENTICATED)) {
+ LOG_INFO("authdata_write resulted in successful authentication");
+ int result = ERROR_OK;
+ dm013_info_t *dm = get_dm(target);
+ target_list_t *entry;
+ list_for_each_entry(entry, &dm->target_list, list) {
+ if (examine(entry->target) != ERROR_OK)
+ result = ERROR_FAIL;
+ }
+ return result;
+ }
+
+ 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;
+
+ 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->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->authdata_read = &riscv013_authdata_read;
+ generic_info->authdata_write = &riscv013_authdata_write;
+ generic_info->dmi_read = &dmi_read;
+ generic_info->dmi_write = &dmi_write;
+ 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->progbufsize = -1;
+
+ info->dmi_busy_delay = 0;
+ info->bus_master_read_delay = 0;
+ info->bus_master_write_delay = 0;
+ info->ac_busy_delay = 0;
+
+ /* Assume all these abstract commands are supported until we learn
+ * otherwise.
+ * TODO: The spec allows eg. one CSR to be able to be accessed abstractly
+ * while another one isn't. We don't track that this closely here, but in
+ * the future we probably should. */
+ info->abstract_read_csr_supported = true;
+ info->abstract_write_csr_supported = true;
+ info->abstract_read_fpr_supported = true;
+ info->abstract_write_fpr_supported = true;
+
+ return ERROR_OK;
+}
+
+static int assert_reset(struct target *target)
+{
+ RISCV_INFO(r);
+
+ select_dmi(target);
+
+ uint32_t control_base = set_field(0, DMI_DMCONTROL_DMACTIVE, 1);
+
+ if (target->rtos) {
+ /* There's only one target, and OpenOCD thinks each hart is a thread.
+ * We must reset them all. */
+
+ /* TODO: Try to use hasel in dmcontrol */
+
+ /* Set haltreq for each hart. */
+ uint32_t control = control_base;
+ for (int i = 0; i < riscv_count_harts(target); ++i) {
+ if (!riscv_hart_enabled(target, i))
+ continue;
+
+ control = set_hartsel(control_base, i);
+ control = set_field(control, DMI_DMCONTROL_HALTREQ,
+ target->reset_halt ? 1 : 0);
+ dmi_write(target, DMI_DMCONTROL, control);
+ }
+ /* Assert ndmreset */
+ control = set_field(control, DMI_DMCONTROL_NDMRESET, 1);
+ dmi_write(target, DMI_DMCONTROL, control);
+
+ } else {
+ /* Reset just this hart. */
+ uint32_t control = set_hartsel(control_base, r->current_hartid);
+ control = set_field(control, DMI_DMCONTROL_HALTREQ,
+ target->reset_halt ? 1 : 0);
+ control = set_field(control, DMI_DMCONTROL_NDMRESET, 1);
+ dmi_write(target, DMI_DMCONTROL, control);
+ }
+
+ target->state = TARGET_RESET;
+
+ return ERROR_OK;
+}
+
+static int deassert_reset(struct target *target)
+{
+ RISCV_INFO(r);
+ RISCV013_INFO(info);
+ select_dmi(target);
+
+ /* Clear the reset, but make sure haltreq is still set */
+ uint32_t control = 0;
+ control = set_field(control, DMI_DMCONTROL_HALTREQ, target->reset_halt ? 1 : 0);
+ control = set_field(control, DMI_DMCONTROL_DMACTIVE, 1);
+ dmi_write(target, DMI_DMCONTROL,
+ set_hartsel(control, r->current_hartid));
+
+ uint32_t dmstatus;
+ int dmi_busy_delay = info->dmi_busy_delay;
+ time_t start = time(NULL);
+
+ for (int i = 0; i < riscv_count_harts(target); ++i) {
+ int index = i;
+ if (target->rtos) {
+ if (!riscv_hart_enabled(target, index))
+ continue;
+ dmi_write(target, DMI_DMCONTROL,
+ set_hartsel(control, index));
+ } else {
+ index = r->current_hartid;
+ }
+
+ char *operation;
+ uint32_t expected_field;
+ if (target->reset_halt) {
+ operation = "halt";
+ expected_field = DMI_DMSTATUS_ALLHALTED;
+ } else {
+ operation = "run";
+ expected_field = DMI_DMSTATUS_ALLRUNNING;
+ }
+ LOG_DEBUG("Waiting for hart %d to %s out of reset.", index, operation);
+ while (1) {
+ int result = dmstatus_read_timeout(target, &dmstatus, true,
+ riscv_reset_timeout_sec);
+ if (result == ERROR_TIMEOUT_REACHED)
+ LOG_ERROR("Hart %d didn't complete a DMI read coming out of "
+ "reset in %ds; Increase the timeout with riscv "
+ "set_reset_timeout_sec.",
+ index, riscv_reset_timeout_sec);
+ if (result != ERROR_OK)
+ return result;
+ if (get_field(dmstatus, expected_field))
+ break;
+ if (time(NULL) - start > riscv_reset_timeout_sec) {
+ LOG_ERROR("Hart %d didn't %s coming out of reset in %ds; "
+ "dmstatus=0x%x; "
+ "Increase the timeout with riscv set_reset_timeout_sec.",
+ index, operation, riscv_reset_timeout_sec, dmstatus);
+ return ERROR_FAIL;
+ }
+ }
+ target->state = TARGET_HALTED;
+
+ if (get_field(dmstatus, DMI_DMSTATUS_ALLHAVERESET)) {
+ /* Ack reset. */
+ dmi_write(target, DMI_DMCONTROL,
+ set_hartsel(control, index) |
+ DMI_DMCONTROL_ACKHAVERESET);
+ }
+
+ if (!target->rtos)
+ break;
+ }
+ info->dmi_busy_delay = dmi_busy_delay;
+ return ERROR_OK;
+}
+
+/**
+ * @size in bytes
+ */
+static void write_to_buf(uint8_t *buffer, uint64_t value, unsigned size)
+{
+ switch (size) {
+ case 8:
+ buffer[7] = value >> 56;
+ buffer[6] = value >> 48;
+ buffer[5] = value >> 40;
+ buffer[4] = value >> 32;
+ /* falls through */
+ case 4:
+ buffer[3] = value >> 24;
+ buffer[2] = value >> 16;
+ /* falls through */
+ case 2:
+ buffer[1] = value >> 8;
+ /* falls through */
+ case 1:
+ buffer[0] = value;
+ break;
+ default:
+ assert(false);
+ }
+}
+
+static int execute_fence(struct target *target)
+{
+ struct riscv_program program;
+ riscv_program_init(&program, target);
+ riscv_program_fence(&program);
+ int result = riscv_program_exec(&program, target);
+ if (result != ERROR_OK)
+ LOG_ERROR("Unable to execute fence");
+ return result;
+}
+
+static void log_memory_access(target_addr_t address, uint64_t value,
+ unsigned size_bytes, bool read)
+{
+ if (debug_level < LOG_LVL_DEBUG)
+ return;
+
+ char fmt[80];
+ sprintf(fmt, "M[0x%" TARGET_PRIxADDR "] %ss 0x%%0%d" PRIx64,
+ address, read ? "read" : "write", size_bytes * 2);
+ value &= (((uint64_t) 0x1) << (size_bytes * 8)) - 1;
+ LOG_DEBUG(fmt, value);
+}
+
+/* Read the relevant sbdata regs depending on size, and put the results into
+ * buffer. */
+static int read_memory_bus_word(struct target *target, target_addr_t address,
+ uint32_t size, uint8_t *buffer)
+{
+ uint32_t value;
+ if (size > 12) {
+ if (dmi_read(target, &value, DMI_SBDATA3) != ERROR_OK)
+ return ERROR_FAIL;
+ write_to_buf(buffer + 12, value, 4);
+ log_memory_access(address + 12, value, 4, true);
+ }
+ if (size > 8) {
+ if (dmi_read(target, &value, DMI_SBDATA2) != ERROR_OK)
+ return ERROR_FAIL;
+ write_to_buf(buffer + 8, value, 4);
+ log_memory_access(address + 8, value, 4, true);
+ }
+ if (size > 4) {
+ if (dmi_read(target, &value, DMI_SBDATA1) != ERROR_OK)
+ return ERROR_FAIL;
+ write_to_buf(buffer + 4, value, 4);
+ log_memory_access(address + 4, value, 4, true);
+ }
+ if (dmi_read(target, &value, DMI_SBDATA0) != ERROR_OK)
+ return ERROR_FAIL;
+ write_to_buf(buffer, value, MIN(size, 4));
+ log_memory_access(address, value, MIN(size, 4), true);
+ return ERROR_OK;
+}
+
+static uint32_t sb_sbaccess(unsigned size_bytes)
+{
+ switch (size_bytes) {
+ case 1:
+ return set_field(0, DMI_SBCS_SBACCESS, 0);
+ case 2:
+ return set_field(0, DMI_SBCS_SBACCESS, 1);
+ case 4:
+ return set_field(0, DMI_SBCS_SBACCESS, 2);
+ case 8:
+ return set_field(0, DMI_SBCS_SBACCESS, 3);
+ case 16:
+ return set_field(0, DMI_SBCS_SBACCESS, 4);
+ }
+ assert(0);
+ return 0; /* Make mingw happy. */
+}
+
+static target_addr_t sb_read_address(struct target *target)
+{
+ RISCV013_INFO(info);
+ unsigned sbasize = get_field(info->sbcs, DMI_SBCS_SBASIZE);
+ target_addr_t address = 0;
+ uint32_t v;
+ if (sbasize > 32) {
+#if BUILD_TARGET64
+ dmi_read(target, &v, DMI_SBADDRESS1);
+ address |= v;
+ address <<= 32;
+#endif
+ }
+ dmi_read(target, &v, DMI_SBADDRESS0);
+ address |= v;
+ return address;
+}
+
+static int sb_write_address(struct target *target, target_addr_t address)
+{
+ RISCV013_INFO(info);
+ unsigned sbasize = get_field(info->sbcs, DMI_SBCS_SBASIZE);
+ /* There currently is no support for >64-bit addresses in OpenOCD. */
+ if (sbasize > 96)
+ dmi_write(target, DMI_SBADDRESS3, 0);
+ if (sbasize > 64)
+ dmi_write(target, DMI_SBADDRESS2, 0);
+ if (sbasize > 32)
+#if BUILD_TARGET64
+ dmi_write(target, DMI_SBADDRESS1, address >> 32);
+#else
+ dmi_write(target, DMI_SBADDRESS1, 0);
+#endif
+ return dmi_write(target, DMI_SBADDRESS0, address);
+}
+
+static int read_sbcs_nonbusy(struct target *target, uint32_t *sbcs)
+{
+ time_t start = time(NULL);
+ while (1) {
+ if (dmi_read(target, sbcs, DMI_SBCS) != ERROR_OK)
+ return ERROR_FAIL;
+ if (!get_field(*sbcs, DMI_SBCS_SBBUSY))
+ return ERROR_OK;
+ if (time(NULL) - start > riscv_command_timeout_sec) {
+ LOG_ERROR("Timed out after %ds waiting for sbbusy to go low (sbcs=0x%x). "
+ "Increase the timeout with riscv set_command_timeout_sec.",
+ riscv_command_timeout_sec, *sbcs);
+ return ERROR_FAIL;
+ }
+ }
+}
+
+static int read_memory_bus_v0(struct target *target, target_addr_t address,
+ uint32_t size, uint32_t count, uint8_t *buffer)
+{
+ LOG_DEBUG("System Bus Access: size: %d\tcount:%d\tstart address: 0x%08"
+ TARGET_PRIxADDR, size, count, address);
+ uint8_t *t_buffer = buffer;
+ riscv_addr_t cur_addr = address;
+ riscv_addr_t fin_addr = address + (count * size);
+ uint32_t access = 0;
+
+ const int DMI_SBCS_SBSINGLEREAD_OFFSET = 20;
+ const uint32_t DMI_SBCS_SBSINGLEREAD = (0x1U << DMI_SBCS_SBSINGLEREAD_OFFSET);
+
+ const int DMI_SBCS_SBAUTOREAD_OFFSET = 15;
+ const uint32_t DMI_SBCS_SBAUTOREAD = (0x1U << DMI_SBCS_SBAUTOREAD_OFFSET);
+
+ /* ww favorise one off reading if there is an issue */
+ if (count == 1) {
+ for (uint32_t i = 0; i < count; i++) {
+ if (dmi_read(target, &access, DMI_SBCS) != ERROR_OK)
+ return ERROR_FAIL;
+ dmi_write(target, DMI_SBADDRESS0, cur_addr);
+ /* size/2 matching the bit access of the spec 0.13 */
+ access = set_field(access, DMI_SBCS_SBACCESS, size/2);
+ access = set_field(access, DMI_SBCS_SBSINGLEREAD, 1);
+ LOG_DEBUG("\r\nread_memory: sab: access: 0x%08x", access);
+ dmi_write(target, DMI_SBCS, access);
+ /* 3) read */
+ uint32_t value;
+ if (dmi_read(target, &value, DMI_SBDATA0) != ERROR_OK)
+ return ERROR_FAIL;
+ LOG_DEBUG("\r\nread_memory: sab: value: 0x%08x", value);
+ write_to_buf(t_buffer, value, size);
+ t_buffer += size;
+ cur_addr += size;
+ }
+ return ERROR_OK;
+ }
+
+ /* has to be the same size if we want to read a block */
+ LOG_DEBUG("reading block until final address 0x%" PRIx64, fin_addr);
+ if (dmi_read(target, &access, DMI_SBCS) != ERROR_OK)
+ return ERROR_FAIL;
+ /* set current address */
+ dmi_write(target, DMI_SBADDRESS0, cur_addr);
+ /* 2) write sbaccess=2, sbsingleread,sbautoread,sbautoincrement
+ * size/2 matching the bit access of the spec 0.13 */
+ access = set_field(access, DMI_SBCS_SBACCESS, size/2);
+ access = set_field(access, DMI_SBCS_SBAUTOREAD, 1);
+ access = set_field(access, DMI_SBCS_SBSINGLEREAD, 1);
+ access = set_field(access, DMI_SBCS_SBAUTOINCREMENT, 1);
+ LOG_DEBUG("\r\naccess: 0x%08x", access);
+ dmi_write(target, DMI_SBCS, access);
+
+ while (cur_addr < fin_addr) {
+ LOG_DEBUG("\r\nsab:autoincrement: \r\n size: %d\tcount:%d\taddress: 0x%08"
+ PRIx64, size, count, cur_addr);
+ /* read */
+ uint32_t value;
+ if (dmi_read(target, &value, DMI_SBDATA0) != ERROR_OK)
+ return ERROR_FAIL;
+ write_to_buf(t_buffer, value, size);
+ cur_addr += size;
+ t_buffer += size;
+
+ /* if we are reaching last address, we must clear autoread */
+ if (cur_addr == fin_addr && count != 1) {
+ dmi_write(target, DMI_SBCS, 0);
+ if (dmi_read(target, &value, DMI_SBDATA0) != ERROR_OK)
+ return ERROR_FAIL;
+ write_to_buf(t_buffer, value, size);
+ }
+ }
+
+ return ERROR_OK;
+}
+
+/**
+ * Read the requested memory using the system bus interface.
+ */
+static int read_memory_bus_v1(struct target *target, target_addr_t address,
+ uint32_t size, uint32_t count, uint8_t *buffer)
+{
+ RISCV013_INFO(info);
+ target_addr_t next_address = address;
+ target_addr_t end_address = address + count * size;
+
+ while (next_address < end_address) {
+ uint32_t sbcs = set_field(0, DMI_SBCS_SBREADONADDR, 1);
+ sbcs |= sb_sbaccess(size);
+ sbcs = set_field(sbcs, DMI_SBCS_SBAUTOINCREMENT, 1);
+ sbcs = set_field(sbcs, DMI_SBCS_SBREADONDATA, count > 1);
+ dmi_write(target, DMI_SBCS, sbcs);
+
+ /* This address write will trigger the first read. */
+ sb_write_address(target, next_address);
+
+ if (info->bus_master_read_delay) {
+ jtag_add_runtest(info->bus_master_read_delay, TAP_IDLE);
+ if (jtag_execute_queue() != ERROR_OK) {
+ LOG_ERROR("Failed to scan idle sequence");
+ return ERROR_FAIL;
+ }
+ }
+
+ for (uint32_t i = (next_address - address) / size; i < count - 1; i++) {
+ read_memory_bus_word(target, address + i * size, size,
+ buffer + i * size);
+ }
+
+ sbcs = set_field(sbcs, DMI_SBCS_SBREADONDATA, 0);
+ dmi_write(target, DMI_SBCS, sbcs);
+
+ read_memory_bus_word(target, address + (count - 1) * size, size,
+ buffer + (count - 1) * size);
+
+ if (read_sbcs_nonbusy(target, &sbcs) != ERROR_OK)
+ return ERROR_FAIL;
+
+ if (get_field(sbcs, DMI_SBCS_SBBUSYERROR)) {
+ /* We read while the target was busy. Slow down and try again. */
+ dmi_write(target, DMI_SBCS, DMI_SBCS_SBBUSYERROR);
+ next_address = sb_read_address(target);
+ info->bus_master_read_delay += info->bus_master_read_delay / 10 + 1;
+ continue;
+ }
+
+ unsigned error = get_field(sbcs, DMI_SBCS_SBERROR);
+ if (error == 0) {
+ next_address = end_address;
+ } else {
+ /* Some error indicating the bus access failed, but not because of
+ * something we did wrong. */
+ dmi_write(target, DMI_SBCS, DMI_SBCS_SBERROR);
+ return ERROR_FAIL;
+ }
+ }
+
+ return ERROR_OK;
+}
+
+/**
+ * Read the requested memory, taking care to execute every read exactly once,
+ * even if cmderr=busy is encountered.
+ */
+static int read_memory_progbuf(struct target *target, target_addr_t address,
+ uint32_t size, uint32_t count, uint8_t *buffer)
+{
+ RISCV013_INFO(info);
+
+ int result = ERROR_OK;
+
+ LOG_DEBUG("reading %d words of %d bytes from 0x%" TARGET_PRIxADDR, count,
+ size, address);
+
+ select_dmi(target);
+
+ /* s0 holds the next address to write to
+ * s1 holds the next data value to write
+ */
+ uint64_t s0, s1;
+ if (register_read(target, &s0, GDB_REGNO_S0) != ERROR_OK)
+ return ERROR_FAIL;
+ if (register_read(target, &s1, GDB_REGNO_S1) != ERROR_OK)
+ return ERROR_FAIL;
+
+ if (execute_fence(target) != ERROR_OK)
+ return ERROR_FAIL;
+
+ /* Write the program (load, increment) */
+ struct riscv_program program;
+ riscv_program_init(&program, target);
+ 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_addi(&program, GDB_REGNO_S0, GDB_REGNO_S0, size);
+
+ if (riscv_program_ebreak(&program) != ERROR_OK)
+ return ERROR_FAIL;
+ riscv_program_write(&program);
+
+ /* Write address to S0, and execute buffer. */
+ result = register_write_direct(target, GDB_REGNO_S0, address);
+ if (result != ERROR_OK)
+ goto error;
+ uint32_t command = access_register_command(GDB_REGNO_S1, riscv_xlen(target),
+ AC_ACCESS_REGISTER_TRANSFER |
+ AC_ACCESS_REGISTER_POSTEXEC);
+ result = execute_abstract_command(target, command);
+ if (result != ERROR_OK)
+ goto error;
+
+ /* First read has just triggered. Result is in s1. */
+
+ dmi_write(target, DMI_ABSTRACTAUTO,
+ 1 << DMI_ABSTRACTAUTO_AUTOEXECDATA_OFFSET);
+
+ /* read_addr is the next address that the hart will read from, which is the
+ * value in s0. */
+ riscv_addr_t read_addr = address + size;
+ /* The next address that we need to receive data for. */
+ riscv_addr_t receive_addr = address;
+ riscv_addr_t fin_addr = address + (count * size);
+ unsigned skip = 1;
+ while (read_addr < fin_addr) {
+ LOG_DEBUG("read_addr=0x%" PRIx64 ", receive_addr=0x%" PRIx64
+ ", fin_addr=0x%" PRIx64, read_addr, receive_addr, fin_addr);
+ /* The pipeline looks like this:
+ * memory -> s1 -> dm_data0 -> debugger
+ * It advances every time the debugger reads dmdata0.
+ * So at any time the debugger has just read mem[s0 - 3*size],
+ * dm_data0 contains mem[s0 - 2*size]
+ * s1 contains mem[s0-size] */
+
+ LOG_DEBUG("creating burst to read from 0x%" PRIx64
+ " up to 0x%" PRIx64, read_addr, fin_addr);
+ assert(read_addr >= address && read_addr < fin_addr);
+ struct riscv_batch *batch = riscv_batch_alloc(target, 32,
+ info->dmi_busy_delay + info->ac_busy_delay);
+
+ size_t reads = 0;
+ for (riscv_addr_t addr = read_addr; addr < fin_addr; addr += size) {
+ riscv_batch_add_dmi_read(batch, DMI_DATA0);
+
+ reads++;
+ if (riscv_batch_full(batch))
+ break;
+ }
+
+ riscv_batch_run(batch);
+
+ /* Wait for the target to finish performing the last abstract command,
+ * and update our copy of cmderr. */
+ uint32_t abstractcs;
+ if (dmi_read(target, &abstractcs, DMI_ABSTRACTCS) != ERROR_OK)
+ return ERROR_FAIL;
+ while (get_field(abstractcs, DMI_ABSTRACTCS_BUSY))
+ if (dmi_read(target, &abstractcs, DMI_ABSTRACTCS) != ERROR_OK)
+ return ERROR_FAIL;
+ info->cmderr = get_field(abstractcs, DMI_ABSTRACTCS_CMDERR);
+
+ unsigned cmderr = info->cmderr;
+ riscv_addr_t next_read_addr;
+ uint32_t dmi_data0 = -1;
+ switch (info->cmderr) {
+ case CMDERR_NONE:
+ LOG_DEBUG("successful (partial?) memory read");
+ next_read_addr = read_addr + reads * size;
+ break;
+ case CMDERR_BUSY:
+ LOG_DEBUG("memory read resulted in busy response");
+
+ /*
+ * If you want to exercise this code path, apply the following patch to spike:
+--- a/riscv/debug_module.cc
++++ b/riscv/debug_module.cc
+@@ -1,3 +1,5 @@
++#include <unistd.h>
++
+ #include <cassert>
+
+ #include "debug_module.h"
+@@ -398,6 +400,15 @@ bool debug_module_t::perform_abstract_command()
+ // Since the next instruction is what we will use, just use nother NOP
+ // to get there.
+ write32(debug_abstract, 1, addi(ZERO, ZERO, 0));
++
++ if (abstractauto.autoexecdata &&
++ program_buffer[0] == 0x83 &&
++ program_buffer[1] == 0x24 &&
++ program_buffer[2] == 0x04 &&
++ program_buffer[3] == 0 &&
++ rand() < RAND_MAX / 10) {
++ usleep(1000000);
++ }
+ } else {
+ write32(debug_abstract, 1, ebreak());
+ }
+ */
+ increase_ac_busy_delay(target);
+ riscv013_clear_abstract_error(target);
+
+ dmi_write(target, DMI_ABSTRACTAUTO, 0);
+
+ /* This is definitely a good version of the value that we
+ * attempted to read when we discovered that the target was
+ * busy. */
+ if (dmi_read(target, &dmi_data0, DMI_DATA0) != ERROR_OK) {
+ riscv_batch_free(batch);
+ goto error;
+ }
+
+ /* Clobbers DMI_DATA0. */
+ result = register_read_direct(target, &next_read_addr,
+ GDB_REGNO_S0);
+ if (result != ERROR_OK) {
+ riscv_batch_free(batch);
+ goto error;
+ }
+ /* Restore the command, and execute it.
+ * Now DMI_DATA0 contains the next value just as it would if no
+ * error had occurred. */
+ dmi_write(target, DMI_COMMAND, command);
+
+ dmi_write(target, DMI_ABSTRACTAUTO,
+ 1 << DMI_ABSTRACTAUTO_AUTOEXECDATA_OFFSET);
+ break;
+ default:
+ LOG_ERROR("error when reading memory, abstractcs=0x%08lx", (long)abstractcs);
+ riscv013_clear_abstract_error(target);
+ riscv_batch_free(batch);
+ result = ERROR_FAIL;
+ goto error;
+ }
+
+ /* Now read whatever we got out of the batch. */
+ for (size_t i = 0; i < reads; i++) {
+ if (read_addr >= next_read_addr)
+ break;
+
+ read_addr += size;
+
+ if (skip > 0) {
+ skip--;
+ continue;
+ }
+
+ riscv_addr_t offset = receive_addr - address;
+ uint64_t dmi_out = riscv_batch_get_dmi_read(batch, i);
+ uint32_t value = get_field(dmi_out, DTM_DMI_DATA);
+ write_to_buf(buffer + offset, value, size);
+ log_memory_access(receive_addr, value, size, true);
+
+ receive_addr += size;
+ }
+ riscv_batch_free(batch);
+
+ if (cmderr == CMDERR_BUSY) {
+ riscv_addr_t offset = receive_addr - address;
+ write_to_buf(buffer + offset, dmi_data0, size);
+ log_memory_access(receive_addr, dmi_data0, size, true);
+ read_addr += size;
+ receive_addr += size;
+ }
+ }
+
+ dmi_write(target, DMI_ABSTRACTAUTO, 0);
+
+ if (count > 1) {
+ /* Read the penultimate word. */
+ uint32_t value;
+ if (dmi_read(target, &value, DMI_DATA0) != ERROR_OK)
+ goto error;
+ write_to_buf(buffer + receive_addr - address, value, size);
+ log_memory_access(receive_addr, value, size, true);
+ receive_addr += size;
+ }
+
+ /* Read the last word. */
+ uint64_t value;
+ result = register_read_direct(target, &value, GDB_REGNO_S1);
+ if (result != ERROR_OK)
+ goto error;
+ write_to_buf(buffer + receive_addr - address, value, size);
+ log_memory_access(receive_addr, value, size, true);
+
+ riscv_set_register(target, GDB_REGNO_S0, s0);
+ riscv_set_register(target, GDB_REGNO_S1, s1);
+ return ERROR_OK;
+
+error:
+ dmi_write(target, DMI_ABSTRACTAUTO, 0);
+
+ riscv_set_register(target, GDB_REGNO_S0, s0);
+ riscv_set_register(target, GDB_REGNO_S1, s1);
+ return result;
+}
+
+static int read_memory(struct target *target, target_addr_t address,
+ uint32_t size, uint32_t count, uint8_t *buffer)
+{
+ RISCV013_INFO(info);
+ if (info->progbufsize >= 2 && !riscv_prefer_sba)
+ return read_memory_progbuf(target, address, size, count, buffer);
+
+ if ((get_field(info->sbcs, DMI_SBCS_SBACCESS8) && size == 1) ||
+ (get_field(info->sbcs, DMI_SBCS_SBACCESS16) && size == 2) ||
+ (get_field(info->sbcs, DMI_SBCS_SBACCESS32) && size == 4) ||
+ (get_field(info->sbcs, DMI_SBCS_SBACCESS64) && size == 8) ||
+ (get_field(info->sbcs, DMI_SBCS_SBACCESS128) && size == 16)) {
+ if (get_field(info->sbcs, DMI_SBCS_SBVERSION) == 0)
+ return read_memory_bus_v0(target, address, size, count, buffer);
+ else if (get_field(info->sbcs, DMI_SBCS_SBVERSION) == 1)
+ return read_memory_bus_v1(target, address, size, count, buffer);
+ }
+
+ if (info->progbufsize >= 2)
+ return read_memory_progbuf(target, address, size, count, buffer);
+
+ LOG_ERROR("Don't know how to read memory on this target.");
+ return ERROR_FAIL;
+}
+
+static int write_memory_bus_v0(struct target *target, target_addr_t address,
+ uint32_t size, uint32_t count, const uint8_t *buffer)
+{
+ /*1) write sbaddress: for singlewrite and autoincrement, we need to write the address once*/
+ LOG_DEBUG("System Bus Access: size: %d\tcount:%d\tstart address: 0x%08"
+ TARGET_PRIxADDR, size, count, address);
+ dmi_write(target, DMI_SBADDRESS0, address);
+ int64_t value = 0;
+ int64_t access = 0;
+ riscv_addr_t offset = 0;
+ riscv_addr_t t_addr = 0;
+ const uint8_t *t_buffer = buffer + offset;
+
+ /* B.8 Writing Memory, single write check if we write in one go */
+ if (count == 1) { /* count is in bytes here */
+ /* check the size */
+ switch (size) {
+ case 1:
+ value = t_buffer[0];
+ break;
+ case 2:
+ value = t_buffer[0]
+ | ((uint32_t) t_buffer[1] << 8);
+ break;
+ case 4:
+ 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;
+ }
+
+ access = 0;
+ access = set_field(access, DMI_SBCS_SBACCESS, size/2);
+ dmi_write(target, DMI_SBCS, access);
+ LOG_DEBUG("\r\naccess: 0x%08" PRIx64, access);
+ LOG_DEBUG("\r\nwrite_memory:SAB: ONE OFF: value 0x%08" PRIx64, value);
+ dmi_write(target, DMI_SBDATA0, value);
+ return ERROR_OK;
+ }
+
+ /*B.8 Writing Memory, using autoincrement*/
+
+ access = 0;
+ access = set_field(access, DMI_SBCS_SBACCESS, size/2);
+ access = set_field(access, DMI_SBCS_SBAUTOINCREMENT, 1);
+ LOG_DEBUG("\r\naccess: 0x%08" PRIx64, access);
+ dmi_write(target, DMI_SBCS, access);
+
+ /*2)set the value according to the size required and write*/
+ for (riscv_addr_t i = 0; i < count; ++i) {
+ offset = size*i;
+ /* for monitoring only */
+ t_addr = address + offset;
+ t_buffer = buffer + offset;
+
+ switch (size) {
+ case 1:
+ value = t_buffer[0];
+ break;
+ case 2:
+ value = t_buffer[0]
+ | ((uint32_t) t_buffer[1] << 8);
+ break;
+ case 4:
+ 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;
+ }
+ LOG_DEBUG("SAB:autoincrement: expected address: 0x%08x value: 0x%08x"
+ PRIx64, (uint32_t)t_addr, (uint32_t)value);
+ dmi_write(target, DMI_SBDATA0, value);
+ }
+ /*reset the autoincrement when finished (something weird is happening if this is not done at the end*/
+ access = set_field(access, DMI_SBCS_SBAUTOINCREMENT, 0);
+ dmi_write(target, DMI_SBCS, access);
+
+ return ERROR_OK;
+}
+
+static int write_memory_bus_v1(struct target *target, target_addr_t address,
+ uint32_t size, uint32_t count, const uint8_t *buffer)
+{
+ RISCV013_INFO(info);
+ uint32_t sbcs = sb_sbaccess(size);
+ sbcs = set_field(sbcs, DMI_SBCS_SBAUTOINCREMENT, 1);
+ dmi_write(target, DMI_SBCS, sbcs);
+
+ target_addr_t next_address = address;
+ target_addr_t end_address = address + count * size;
+
+ sb_write_address(target, next_address);
+ while (next_address < end_address) {
+ for (uint32_t i = (next_address - address) / size; i < count; i++) {
+ const uint8_t *p = buffer + i * size;
+ if (size > 12)
+ dmi_write(target, DMI_SBDATA3,
+ ((uint32_t) p[12]) |
+ (((uint32_t) p[13]) << 8) |
+ (((uint32_t) p[14]) << 16) |
+ (((uint32_t) p[15]) << 24));
+ if (size > 8)
+ dmi_write(target, DMI_SBDATA2,
+ ((uint32_t) p[8]) |
+ (((uint32_t) p[9]) << 8) |
+ (((uint32_t) p[10]) << 16) |
+ (((uint32_t) p[11]) << 24));
+ if (size > 4)
+ dmi_write(target, DMI_SBDATA1,
+ ((uint32_t) p[4]) |
+ (((uint32_t) p[5]) << 8) |
+ (((uint32_t) p[6]) << 16) |
+ (((uint32_t) p[7]) << 24));
+ uint32_t value = p[0];
+ if (size > 2) {
+ value |= ((uint32_t) p[2]) << 16;
+ value |= ((uint32_t) p[3]) << 24;
+ }
+ if (size > 1)
+ value |= ((uint32_t) p[1]) << 8;
+ dmi_write(target, DMI_SBDATA0, value);
+
+ log_memory_access(address + i * size, value, size, false);
+
+ if (info->bus_master_write_delay) {
+ jtag_add_runtest(info->bus_master_write_delay, TAP_IDLE);
+ if (jtag_execute_queue() != ERROR_OK) {
+ LOG_ERROR("Failed to scan idle sequence");
+ return ERROR_FAIL;
+ }
+ }
+ }
+
+ if (read_sbcs_nonbusy(target, &sbcs) != ERROR_OK)
+ return ERROR_FAIL;
+
+ if (get_field(sbcs, DMI_SBCS_SBBUSYERROR)) {
+ /* We wrote while the target was busy. Slow down and try again. */
+ dmi_write(target, DMI_SBCS, DMI_SBCS_SBBUSYERROR);
+ next_address = sb_read_address(target);
+ info->bus_master_write_delay += info->bus_master_write_delay / 10 + 1;
+ continue;
+ }
+
+ unsigned error = get_field(sbcs, DMI_SBCS_SBERROR);
+ if (error == 0) {
+ next_address = end_address;
+ } else {
+ /* Some error indicating the bus access failed, but not because of
+ * something we did wrong. */
+ dmi_write(target, DMI_SBCS, DMI_SBCS_SBERROR);
+ return ERROR_FAIL;
+ }
+ }
+
+ return ERROR_OK;
+}
+
+static int write_memory_progbuf(struct target *target, target_addr_t address,
+ uint32_t size, uint32_t count, const uint8_t *buffer)
+{
+ RISCV013_INFO(info);
+
+ LOG_DEBUG("writing %d words of %d bytes to 0x%08lx", count, size, (long)address);
+
+ select_dmi(target);
+
+ /* s0 holds the next address to write to
+ * s1 holds the next data value to write
+ */
+
+ int result = ERROR_OK;
+ uint64_t s0, s1;
+ if (register_read(target, &s0, GDB_REGNO_S0) != ERROR_OK)
+ return ERROR_FAIL;
+ if (register_read(target, &s1, GDB_REGNO_S1) != ERROR_OK)
+ return ERROR_FAIL;
+
+ /* Write the program (store, increment) */
+ struct riscv_program program;
+ riscv_program_init(&program, target);
+
+ 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);
+ result = ERROR_FAIL;
+ goto error;
+ }
+
+ riscv_program_addi(&program, GDB_REGNO_S0, GDB_REGNO_S0, size);
+
+ result = riscv_program_ebreak(&program);
+ if (result != ERROR_OK)
+ goto error;
+ riscv_program_write(&program);
+
+ riscv_addr_t cur_addr = address;
+ riscv_addr_t fin_addr = address + (count * size);
+ bool setup_needed = true;
+ LOG_DEBUG("writing until final address 0x%016" PRIx64, fin_addr);
+ while (cur_addr < fin_addr) {
+ LOG_DEBUG("transferring burst starting at address 0x%016" PRIx64,
+ cur_addr);
+
+ struct riscv_batch *batch = riscv_batch_alloc(
+ target,
+ 32,
+ info->dmi_busy_delay + info->ac_busy_delay);
+
+ /* To write another word, we put it in S1 and execute the program. */
+ unsigned start = (cur_addr - address) / size;
+ for (unsigned i = start; i < count; ++i) {
+ unsigned offset = size*i;
+ const uint8_t *t_buffer = buffer + offset;
+
+ uint32_t value;
+ switch (size) {
+ case 1:
+ value = t_buffer[0];
+ break;
+ case 2:
+ value = t_buffer[0]
+ | ((uint32_t) t_buffer[1] << 8);
+ break;
+ case 4:
+ 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);
+ riscv_batch_free(batch);
+ result = ERROR_FAIL;
+ goto error;
+ }
+
+ log_memory_access(address + offset, value, size, false);
+ cur_addr += size;
+
+ if (setup_needed) {
+ result = register_write_direct(target, GDB_REGNO_S0,
+ address + offset);
+ if (result != ERROR_OK) {
+ riscv_batch_free(batch);
+ goto error;
+ }
+
+ /* Write value. */
+ dmi_write(target, DMI_DATA0, value);
+
+ /* Write and execute command that moves value into S1 and
+ * executes program buffer. */
+ uint32_t command = access_register_command(GDB_REGNO_S1, 32,
+ AC_ACCESS_REGISTER_POSTEXEC |
+ AC_ACCESS_REGISTER_TRANSFER |
+ AC_ACCESS_REGISTER_WRITE);
+ result = execute_abstract_command(target, command);
+ if (result != ERROR_OK) {
+ riscv_batch_free(batch);
+ goto error;
+ }
+
+ /* Turn on autoexec */
+ dmi_write(target, DMI_ABSTRACTAUTO,
+ 1 << DMI_ABSTRACTAUTO_AUTOEXECDATA_OFFSET);
+
+ setup_needed = false;
+ } else {
+ riscv_batch_add_dmi_write(batch, DMI_DATA0, value);
+ if (riscv_batch_full(batch))
+ break;
+ }
+ }
+
+ result = riscv_batch_run(batch);
+ riscv_batch_free(batch);
+ if (result != ERROR_OK)
+ goto error;
+
+ /* 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. */
+
+ uint32_t abstractcs;
+ if (dmi_read(target, &abstractcs, DMI_ABSTRACTCS) != ERROR_OK)
+ goto error;
+ while (get_field(abstractcs, DMI_ABSTRACTCS_BUSY))
+ if (dmi_read(target, &abstractcs, DMI_ABSTRACTCS) != ERROR_OK)
+ return ERROR_FAIL;
+ info->cmderr = get_field(abstractcs, DMI_ABSTRACTCS_CMDERR);
+ switch (info->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);
+
+ dmi_write(target, DMI_ABSTRACTAUTO, 0);
+ result = register_read_direct(target, &cur_addr, GDB_REGNO_S0);
+ if (result != ERROR_OK)
+ goto error;
+ setup_needed = true;
+ break;
+
+ default:
+ LOG_ERROR("error when writing memory, abstractcs=0x%08lx", (long)abstractcs);
+ riscv013_clear_abstract_error(target);
+ result = ERROR_FAIL;
+ goto error;
+ }
+ }
+
+error:
+ dmi_write(target, DMI_ABSTRACTAUTO, 0);
+
+ if (register_write_direct(target, GDB_REGNO_S1, s1) != ERROR_OK)
+ return ERROR_FAIL;
+ if (register_write_direct(target, GDB_REGNO_S0, s0) != ERROR_OK)
+ return ERROR_FAIL;
+
+ if (execute_fence(target) != ERROR_OK)
+ return ERROR_FAIL;
+
+ return result;
+}
+
+static int write_memory(struct target *target, target_addr_t address,
+ uint32_t size, uint32_t count, const uint8_t *buffer)
+{
+ RISCV013_INFO(info);
+ if (info->progbufsize >= 2 && !riscv_prefer_sba)
+ return write_memory_progbuf(target, address, size, count, buffer);
+
+ if ((get_field(info->sbcs, DMI_SBCS_SBACCESS8) && size == 1) ||
+ (get_field(info->sbcs, DMI_SBCS_SBACCESS16) && size == 2) ||
+ (get_field(info->sbcs, DMI_SBCS_SBACCESS32) && size == 4) ||
+ (get_field(info->sbcs, DMI_SBCS_SBACCESS64) && size == 8) ||
+ (get_field(info->sbcs, DMI_SBCS_SBACCESS128) && size == 16)) {
+ if (get_field(info->sbcs, DMI_SBCS_SBVERSION) == 0)
+ return write_memory_bus_v0(target, address, size, count, buffer);
+ else if (get_field(info->sbcs, DMI_SBCS_SBVERSION) == 1)
+ return write_memory_bus_v1(target, address, size, count, buffer);
+ }
+
+ if (info->progbufsize >= 2)
+ return write_memory_progbuf(target, address, size, count, buffer);
+
+ LOG_ERROR("Don't know how to write memory on this target.");
+ return ERROR_FAIL;
+}
+
+static int arch_state(struct target *target)
+{
+ return ERROR_OK;
+}
+
+struct target_type riscv013_target = {
+ .name = "riscv",
+
+ .init_target = init_target,
+ .deinit_target = deinit_target,
+ .examine = examine,
+
+ .poll = &riscv_openocd_poll,
+ .halt = &riscv_openocd_halt,
+ .resume = &riscv_openocd_resume,
+ .step = &riscv_openocd_step,
+
+ .assert_reset = assert_reset,
+ .deassert_reset = deassert_reset,
+
+ .read_memory = read_memory,
+ .write_memory = write_memory,
+
+ .arch_state = arch_state,
+};
+
+/*** 0.13-specific implementations of various RISC-V helper functions. ***/
+static int riscv013_get_register(struct target *target,
+ riscv_reg_t *value, int hid, int rid)
+{
+ LOG_DEBUG("reading register %s on hart %d", gdb_regno_name(rid), hid);
+
+ riscv_set_current_hartid(target, hid);
+
+ int result = ERROR_OK;
+ if (rid == GDB_REGNO_PC) {
+ result = register_read(target, value, GDB_REGNO_DPC);
+ LOG_DEBUG("read PC from DPC: 0x%016" PRIx64, *value);
+ } else if (rid == GDB_REGNO_PRIV) {
+ uint64_t dcsr;
+ result = register_read(target, &dcsr, GDB_REGNO_DCSR);
+ *value = get_field(dcsr, CSR_DCSR_PRV);
+ } else {
+ result = register_read(target, value, rid);
+ if (result != ERROR_OK)
+ *value = -1;
+ }
+
+ return result;
+}
+
+static int riscv013_set_register(struct target *target, int hid, int rid, uint64_t value)
+{
+ LOG_DEBUG("writing 0x%" PRIx64 " to register %s on hart %d", value,
+ gdb_regno_name(rid), hid);
+
+ riscv_set_current_hartid(target, hid);
+
+ if (rid <= GDB_REGNO_XPR31) {
+ return register_write_direct(target, rid, value);
+ } else if (rid == GDB_REGNO_PC) {
+ LOG_DEBUG("writing PC to DPC: 0x%016" PRIx64, 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%016" PRIx64, actual_value);
+ if (value != actual_value) {
+ LOG_ERROR("Written PC (0x%" PRIx64 ") does not match read back "
+ "value (0x%" PRIx64 ")", value, actual_value);
+ return ERROR_FAIL;
+ }
+ } else if (rid == GDB_REGNO_PRIV) {
+ uint64_t dcsr;
+ register_read(target, &dcsr, GDB_REGNO_DCSR);
+ dcsr = set_field(dcsr, CSR_DCSR_PRV, value);
+ return register_write_direct(target, GDB_REGNO_DCSR, dcsr);
+ } else {
+ return register_write_direct(target, rid, value);
+ }
+
+ return ERROR_OK;
+}
+
+static int riscv013_select_current_hart(struct target *target)
+{
+ RISCV_INFO(r);
+
+ dm013_info_t *dm = get_dm(target);
+ if (r->current_hartid == dm->current_hartid)
+ return ERROR_OK;
+
+ uint32_t dmcontrol;
+ /* TODO: can't we just "dmcontrol = DMI_DMACTIVE"? */
+ if (dmi_read(target, &dmcontrol, DMI_DMCONTROL) != ERROR_OK)
+ return ERROR_FAIL;
+ dmcontrol = set_hartsel(dmcontrol, r->current_hartid);
+ int result = dmi_write(target, DMI_DMCONTROL, dmcontrol);
+ dm->current_hartid = r->current_hartid;
+ return result;
+}
+
+static int riscv013_halt_current_hart(struct target *target)
+{
+ RISCV_INFO(r);
+ LOG_DEBUG("halting hart %d", r->current_hartid);
+ if (riscv_is_halted(target))
+ LOG_ERROR("Hart %d is already halted!", r->current_hartid);
+
+ /* Issue the halt command, and then wait for the current hart to halt. */
+ uint32_t dmcontrol;
+ if (dmi_read(target, &dmcontrol, DMI_DMCONTROL) != ERROR_OK)
+ return ERROR_FAIL;
+ 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;
+ if (dmstatus_read(target, &dmstatus, true) != ERROR_OK)
+ return ERROR_FAIL;
+ if (dmi_read(target, &dmcontrol, DMI_DMCONTROL) != ERROR_OK)
+ return ERROR_FAIL;
+
+ LOG_ERROR("unable to halt hart %d", r->current_hartid);
+ LOG_ERROR(" dmcontrol=0x%08x", dmcontrol);
+ LOG_ERROR(" dmstatus =0x%08x", dmstatus);
+ return ERROR_FAIL;
+ }
+
+ dmcontrol = set_field(dmcontrol, DMI_DMCONTROL_HALTREQ, 0);
+ dmi_write(target, DMI_DMCONTROL, dmcontrol);
+
+ return ERROR_OK;
+}
+
+static int riscv013_resume_current_hart(struct target *target)
+{
+ return riscv013_step_or_resume_current_hart(target, false);
+}
+
+static int riscv013_step_current_hart(struct target *target)
+{
+ return riscv013_step_or_resume_current_hart(target, true);
+}
+
+static int riscv013_on_resume(struct target *target)
+{
+ return riscv013_on_step_or_resume(target, false);
+}
+
+static int riscv013_on_step(struct target *target)
+{
+ return riscv013_on_step_or_resume(target, true);
+}
+
+static int riscv013_on_halt(struct target *target)
+{
+ return ERROR_OK;
+}
+
+static bool riscv013_is_halted(struct target *target)
+{
+ uint32_t dmstatus;
+ if (dmstatus_read(target, &dmstatus, true) != ERROR_OK)
+ return false;
+ if (get_field(dmstatus, DMI_DMSTATUS_ANYUNAVAIL))
+ LOG_ERROR("Hart %d is unavailable.", riscv_current_hartid(target));
+ if (get_field(dmstatus, DMI_DMSTATUS_ANYNONEXISTENT))
+ LOG_ERROR("Hart %d doesn't exist.", riscv_current_hartid(target));
+ if (get_field(dmstatus, DMI_DMSTATUS_ANYHAVERESET)) {
+ int hartid = riscv_current_hartid(target);
+ LOG_INFO("Hart %d unexpectedly reset!", hartid);
+ /* TODO: Can we make this more obvious to eg. a gdb user? */
+ uint32_t dmcontrol = DMI_DMCONTROL_DMACTIVE |
+ DMI_DMCONTROL_ACKHAVERESET;
+ dmcontrol = set_hartsel(dmcontrol, hartid);
+ /* If we had been halted when we reset, request another halt. If we
+ * ended up running out of reset, then the user will (hopefully) get a
+ * message that a reset happened, that the target is running, and then
+ * that it is halted again once the request goes through.
+ */
+ if (target->state == TARGET_HALTED)
+ dmcontrol |= DMI_DMCONTROL_HALTREQ;
+ dmi_write(target, DMI_DMCONTROL, dmcontrol);
+ }
+ return get_field(dmstatus, DMI_DMSTATUS_ALLHALTED);
+}
+
+static enum riscv_halt_reason riscv013_halt_reason(struct target *target)
+{
+ riscv_reg_t dcsr;
+ int result = register_read(target, &dcsr, GDB_REGNO_DCSR);
+ if (result != ERROR_OK)
+ return RISCV_HALT_UNKNOWN;
+
+ switch (get_field(dcsr, CSR_DCSR_CAUSE)) {
+ case CSR_DCSR_CAUSE_SWBP:
+ return RISCV_HALT_BREAKPOINT;
+ case CSR_DCSR_CAUSE_TRIGGER:
+ /* We could get here before triggers are enumerated if a trigger was
+ * already set when we connected. Force enumeration now, which has the
+ * side effect of clearing any triggers we did not set. */
+ riscv_enumerate_triggers(target);
+ return RISCV_HALT_TRIGGER;
+ 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);
+ return RISCV_HALT_UNKNOWN;
+}
+
+int riscv013_write_debug_buffer(struct target *target, unsigned index, riscv_insn_t data)
+{
+ return dmi_write(target, DMI_PROGBUF0 + index, data);
+}
+
+riscv_insn_t riscv013_read_debug_buffer(struct target *target, unsigned index)
+{
+ uint32_t value;
+ dmi_read(target, &value, DMI_PROGBUF0 + index);
+ return value;
+}
+
+int riscv013_execute_debug_buffer(struct target *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);
+
+ return execute_abstract_command(target, run_program);
+}
+
+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;
+}
+
+static int maybe_execute_fence_i(struct target *target)
+{
+ RISCV013_INFO(info);
+ RISCV_INFO(r);
+ if (info->progbufsize + r->impebreak >= 2) {
+ struct riscv_program program;
+ riscv_program_init(&program, target);
+ if (riscv_program_fence_i(&program) != ERROR_OK)
+ return ERROR_FAIL;
+ if (riscv_program_exec(&program, target) != ERROR_OK) {
+ LOG_ERROR("Failed to execute fence.i");
+ return ERROR_FAIL;
+ }
+ }
+ return ERROR_OK;
+}
+
+/* Helper Functions. */
+static int riscv013_on_step_or_resume(struct target *target, bool step)
+{
+ if (maybe_execute_fence_i(target) != ERROR_OK)
+ return ERROR_FAIL;
+
+ /* We want to twiddle some bits in the debug CSR so debugging works. */
+ riscv_reg_t dcsr;
+ int result = register_read(target, &dcsr, GDB_REGNO_DCSR);
+ if (result != ERROR_OK)
+ return result;
+ dcsr = set_field(dcsr, CSR_DCSR_STEP, step);
+ dcsr = set_field(dcsr, CSR_DCSR_EBREAKM, 1);
+ dcsr = set_field(dcsr, CSR_DCSR_EBREAKS, 1);
+ dcsr = set_field(dcsr, CSR_DCSR_EBREAKU, 1);
+ return riscv_set_register(target, GDB_REGNO_DCSR, dcsr);
+}
+
+static int 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);
+ if (!riscv_is_halted(target)) {
+ LOG_ERROR("Hart %d is not halted!", r->current_hartid);
+ return ERROR_FAIL;
+ }
+
+ if (maybe_execute_fence_i(target) != ERROR_OK)
+ return ERROR_FAIL;
+
+ /* Issue the resume command, and then wait for the current hart to resume. */
+ uint32_t dmcontrol = DMI_DMCONTROL_DMACTIVE;
+ dmcontrol = set_hartsel(dmcontrol, r->current_hartid);
+ dmi_write(target, DMI_DMCONTROL, dmcontrol | DMI_DMCONTROL_RESUMEREQ);
+
+ uint32_t dmstatus;
+ for (size_t i = 0; i < 256; ++i) {
+ usleep(10);
+ if (dmstatus_read(target, &dmstatus, true) != ERROR_OK)
+ return ERROR_FAIL;
+ if (get_field(dmstatus, DMI_DMSTATUS_ALLRESUMEACK) == 0)
+ continue;
+ if (step && get_field(dmstatus, DMI_DMSTATUS_ALLHALTED) == 0)
+ continue;
+
+ dmi_write(target, DMI_DMCONTROL, dmcontrol);
+ return ERROR_OK;
+ }
+
+ LOG_ERROR("unable to resume hart %d", r->current_hartid);
+ if (dmi_read(target, &dmcontrol, DMI_DMCONTROL) != ERROR_OK)
+ return ERROR_FAIL;
+ LOG_ERROR(" dmcontrol=0x%08x", dmcontrol);
+ if (dmstatus_read(target, &dmstatus, true) != ERROR_OK)
+ return ERROR_FAIL;
+ LOG_ERROR(" dmstatus =0x%08x", dmstatus);
+
+ if (step) {
+ LOG_ERROR(" was stepping, halting");
+ riscv013_halt_current_hart(target);
+ return ERROR_OK;
+ }
+
+ return ERROR_FAIL;
+}
+
+void riscv013_clear_abstract_error(struct target *target)
+{
+ /* Wait for busy to go away. */
+ time_t start = time(NULL);
+ uint32_t abstractcs;
+ dmi_read(target, &abstractcs, DMI_ABSTRACTCS);
+ while (get_field(abstractcs, DMI_ABSTRACTCS_BUSY)) {
+ dmi_read(target, &abstractcs, DMI_ABSTRACTCS);
+
+ if (time(NULL) - start > riscv_command_timeout_sec) {
+ LOG_ERROR("abstractcs.busy is not going low after %d seconds "
+ "(abstractcs=0x%x). The target is either really slow or "
+ "broken. You could increase the timeout with riscv "
+ "set_command_timeout_sec.",
+ riscv_command_timeout_sec, abstractcs);
+ break;
+ }
+ }
+ /* Clear the error status. */
+ dmi_write(target, DMI_ABSTRACTCS, abstractcs & DMI_ABSTRACTCS_CMDERR);
+}