Upstream a whole host of RISC-V changes.
[openocd.git] / src / target / riscv / riscv.c
index 0d1cee1bfa27c244e22c7218d522ca163d643b3c..7579e0f0c3b2c1115434c60f8fc9373c9655c95c 100644 (file)
@@ -8,23 +8,23 @@
 #include "config.h"
 #endif
 
+#include <helper/log.h>
+#include <helper/time_support.h>
 #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 "riscv.h"
 #include "gdb_regs.h"
 #include "rtos/rtos.h"
+#include "debug_defines.h"
+#include <helper/bits.h>
 
 #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))
-
 /* Constants for legacy SiFive hardware breakpoints. */
 #define CSR_BPCONTROL_X                        (1<<0)
 #define CSR_BPCONTROL_W                        (1<<1)
@@ -127,10 +127,10 @@ struct scan_field select_idcode = {
 bscan_tunnel_type_t bscan_tunnel_type;
 int bscan_tunnel_ir_width; /* if zero, then tunneling is not present/active */
 
-static uint8_t bscan_zero[4] = {0};
-static uint8_t bscan_one[4] = {1};
+static const uint8_t bscan_zero[4] = {0};
+static const uint8_t bscan_one[4] = {1};
 
-uint8_t ir_user4[4] = {0x23};
+uint8_t ir_user4[4];
 struct scan_field select_user4 = {
        .in_value = NULL,
        .out_value = ir_user4
@@ -184,10 +184,10 @@ struct scan_field _bscan_tunnel_nested_tap_select_dmi[] = {
                }
 };
 struct scan_field *bscan_tunnel_nested_tap_select_dmi = _bscan_tunnel_nested_tap_select_dmi;
-uint32_t bscan_tunnel_nested_tap_select_dmi_num_fields = DIM(_bscan_tunnel_nested_tap_select_dmi);
+uint32_t bscan_tunnel_nested_tap_select_dmi_num_fields = ARRAY_SIZE(_bscan_tunnel_nested_tap_select_dmi);
 
 struct scan_field *bscan_tunnel_data_register_select_dmi = _bscan_tunnel_data_register_select_dmi;
-uint32_t bscan_tunnel_data_register_select_dmi_num_fields = DIM(_bscan_tunnel_data_register_select_dmi);
+uint32_t bscan_tunnel_data_register_select_dmi_num_fields = ARRAY_SIZE(_bscan_tunnel_data_register_select_dmi);
 
 struct trigger {
        uint64_t address;
@@ -204,7 +204,6 @@ int riscv_command_timeout_sec = DEFAULT_COMMAND_TIMEOUT_SEC;
 /* Wall-clock timeout after reset. Settable via RISC-V Target commands.*/
 int riscv_reset_timeout_sec = DEFAULT_RESET_TIMEOUT_SEC;
 
-bool riscv_prefer_sba;
 bool riscv_enable_virt2phys = true;
 bool riscv_ebreakm = true;
 bool riscv_ebreaks = true;
@@ -212,24 +211,12 @@ bool riscv_ebreaku = true;
 
 bool riscv_enable_virtual;
 
-typedef struct {
-       uint16_t low, high;
-} range_t;
-
-/* In addition to the ones in the standard spec, we'll also expose additional
- * CSRs in this list.
- * The list is either NULL, or a series of ranges (inclusive), terminated with
- * 1,0. */
-range_t *expose_csr;
-/* Same, but for custom registers. */
-range_t *expose_custom;
-
 static enum {
        RO_NORMAL,
        RO_REVERSED
 } resume_order;
 
-virt2phys_info_t sv32 = {
+const virt2phys_info_t sv32 = {
        .name = "Sv32",
        .va_bits = 32,
        .level = 2,
@@ -242,7 +229,7 @@ virt2phys_info_t sv32 = {
        .pa_ppn_mask = {0x3ff, 0xfff},
 };
 
-virt2phys_info_t sv39 = {
+const virt2phys_info_t sv39 = {
        .name = "Sv39",
        .va_bits = 39,
        .level = 3,
@@ -255,7 +242,7 @@ virt2phys_info_t sv39 = {
        .pa_ppn_mask = {0x1ff, 0x1ff, 0x3ffffff},
 };
 
-virt2phys_info_t sv48 = {
+const virt2phys_info_t sv48 = {
        .name = "Sv48",
        .va_bits = 48,
        .level = 4,
@@ -268,6 +255,22 @@ virt2phys_info_t sv48 = {
        .pa_ppn_mask = {0x1ff, 0x1ff, 0x1ff, 0x1ffff},
 };
 
+void riscv_sample_buf_maybe_add_timestamp(struct target *target, bool before)
+{
+       RISCV_INFO(r);
+       uint32_t now = timeval_ms() & 0xffffffff;
+       if (r->sample_buf.used + 5 < r->sample_buf.size) {
+               if (before)
+                       r->sample_buf.buf[r->sample_buf.used++] = RISCV_SAMPLE_BUF_TIMESTAMP_BEFORE;
+               else
+                       r->sample_buf.buf[r->sample_buf.used++] = RISCV_SAMPLE_BUF_TIMESTAMP_AFTER;
+               r->sample_buf.buf[r->sample_buf.used++] = now & 0xff;
+               r->sample_buf.buf[r->sample_buf.used++] = (now >> 8) & 0xff;
+               r->sample_buf.buf[r->sample_buf.used++] = (now >> 16) & 0xff;
+               r->sample_buf.buf[r->sample_buf.used++] = (now >> 24) & 0xff;
+       }
+}
+
 static int riscv_resume_go_all_harts(struct target *target);
 
 void select_dmi_via_bscan(struct target *target)
@@ -348,8 +351,8 @@ uint32_t dtmcontrol_scan_via_bscan(struct target *target, uint32_t out)
                tunneled_dr[0].in_value = NULL;
        }
        jtag_add_ir_scan(target->tap, &select_user4, TAP_IDLE);
-       jtag_add_dr_scan(target->tap, DIM(tunneled_ir), tunneled_ir, TAP_IDLE);
-       jtag_add_dr_scan(target->tap, DIM(tunneled_dr), tunneled_dr, TAP_IDLE);
+       jtag_add_dr_scan(target->tap, ARRAY_SIZE(tunneled_ir), tunneled_ir, TAP_IDLE);
+       jtag_add_dr_scan(target->tap, ARRAY_SIZE(tunneled_dr), tunneled_dr, TAP_IDLE);
        select_dmi_via_bscan(target);
 
        int retval = jtag_execute_queue();
@@ -365,8 +368,6 @@ uint32_t dtmcontrol_scan_via_bscan(struct target *target, uint32_t out)
        return in;
 }
 
-
-
 static uint32_t dtmcontrol_scan(struct target *target, uint32_t out)
 {
        struct scan_field field;
@@ -421,15 +422,23 @@ static struct target_type *get_target_type(struct target *target)
        }
 }
 
+static int riscv_create_target(struct target *target, Jim_Interp *interp)
+{
+       LOG_DEBUG("riscv_create_target()");
+       target->arch_info = calloc(1, sizeof(riscv_info_t));
+       if (!target->arch_info) {
+               LOG_ERROR("Failed to allocate RISC-V target structure.");
+               return ERROR_FAIL;
+       }
+       riscv_info_init(target, target->arch_info);
+       return ERROR_OK;
+}
+
 static int riscv_init_target(struct command_context *cmd_ctx,
                struct target *target)
 {
        LOG_DEBUG("riscv_init_target()");
-       target->arch_info = calloc(1, sizeof(riscv_info_t));
-       if (!target->arch_info)
-               return ERROR_FAIL;
-       riscv_info_t *info = (riscv_info_t *) target->arch_info;
-       riscv_info_init(target, info);
+       RISCV_INFO(info);
        info->cmd_ctx = cmd_ctx;
 
        select_dtmcontrol.num_bits = target->tap->ir_length;
@@ -437,6 +446,12 @@ static int riscv_init_target(struct command_context *cmd_ctx,
        select_idcode.num_bits = target->tap->ir_length;
 
        if (bscan_tunnel_ir_width != 0) {
+               assert(target->tap->ir_length >= 6);
+               uint32_t ir_user4_raw = 0x23 << (target->tap->ir_length - 6);
+               ir_user4[0] = (uint8_t)ir_user4_raw;
+               ir_user4[1] = (uint8_t)(ir_user4_raw >>= 8);
+               ir_user4[2] = (uint8_t)(ir_user4_raw >>= 8);
+               ir_user4[3] = (uint8_t)(ir_user4_raw >>= 8);
                select_user4.num_bits = target->tap->ir_length;
                bscan_tunneled_ir_width[0] = bscan_tunnel_ir_width;
                if (bscan_tunnel_type == BSCAN_TUNNEL_DATA_REGISTER)
@@ -470,16 +485,29 @@ static void riscv_free_registers(struct target *target)
 static void riscv_deinit_target(struct target *target)
 {
        LOG_DEBUG("riscv_deinit_target()");
+
+       riscv_info_t *info = target->arch_info;
        struct target_type *tt = get_target_type(target);
-       if (tt) {
+
+       if (tt && info->version_specific)
                tt->deinit_target(target);
-               riscv_info_t *info = (riscv_info_t *) target->arch_info;
-               free(info->reg_names);
-               free(info);
-       }
 
        riscv_free_registers(target);
 
+       range_list_t *entry, *tmp;
+       list_for_each_entry_safe(entry, tmp, &info->expose_csr, list) {
+               free(entry->name);
+               free(entry);
+       }
+
+       list_for_each_entry_safe(entry, tmp, &info->expose_custom, list) {
+               free(entry->name);
+               free(entry);
+       }
+
+       free(info->reg_names);
+       free(target->arch_info);
+
        target->arch_info = NULL;
 }
 
@@ -496,7 +524,7 @@ static void trigger_from_breakpoint(struct trigger *trigger,
        trigger->unique_id = breakpoint->unique_id;
 }
 
-static int maybe_add_trigger_t1(struct target *target, unsigned hartid,
+static int maybe_add_trigger_t1(struct target *target,
                struct trigger *trigger, uint64_t tdata1)
 {
        RISCV_INFO(r);
@@ -520,20 +548,19 @@ static int maybe_add_trigger_t1(struct target *target, unsigned hartid,
        tdata1 = set_field(tdata1, bpcontrol_w, trigger->write);
        tdata1 = set_field(tdata1, bpcontrol_x, trigger->execute);
        tdata1 = set_field(tdata1, bpcontrol_u,
-                       !!(r->misa[hartid] & (1 << ('U' - 'A'))));
+                       !!(r->misa & BIT('U' - 'A')));
        tdata1 = set_field(tdata1, bpcontrol_s,
-                       !!(r->misa[hartid] & (1 << ('S' - 'A'))));
+                       !!(r->misa & BIT('S' - 'A')));
        tdata1 = set_field(tdata1, bpcontrol_h,
-                       !!(r->misa[hartid] & (1 << ('H' - 'A'))));
+                       !!(r->misa & BIT('H' - 'A')));
        tdata1 |= bpcontrol_m;
        tdata1 = set_field(tdata1, bpcontrol_bpmatch, 0); /* exact match */
        tdata1 = set_field(tdata1, bpcontrol_bpaction, 0); /* cause bp exception */
 
-       riscv_set_register_on_hart(target, hartid, GDB_REGNO_TDATA1, tdata1);
+       riscv_set_register(target, GDB_REGNO_TDATA1, tdata1);
 
        riscv_reg_t tdata1_rb;
-       if (riscv_get_register_on_hart(target, &tdata1_rb, hartid,
-                               GDB_REGNO_TDATA1) != ERROR_OK)
+       if (riscv_get_register(target, &tdata1_rb, GDB_REGNO_TDATA1) != ERROR_OK)
                return ERROR_FAIL;
        LOG_DEBUG("tdata1=0x%" PRIx64, tdata1_rb);
 
@@ -541,16 +568,16 @@ static int maybe_add_trigger_t1(struct target *target, unsigned hartid,
                LOG_DEBUG("Trigger doesn't support what we need; After writing 0x%"
                                PRIx64 " to tdata1 it contains 0x%" PRIx64,
                                tdata1, tdata1_rb);
-               riscv_set_register_on_hart(target, hartid, GDB_REGNO_TDATA1, 0);
+               riscv_set_register(target, GDB_REGNO_TDATA1, 0);
                return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
        }
 
-       riscv_set_register_on_hart(target, hartid, GDB_REGNO_TDATA2, trigger->address);
+       riscv_set_register(target, GDB_REGNO_TDATA2, trigger->address);
 
        return ERROR_OK;
 }
 
-static int maybe_add_trigger_t2(struct target *target, unsigned hartid,
+static int maybe_add_trigger_t2(struct target *target,
                struct trigger *trigger, uint64_t tdata1)
 {
        RISCV_INFO(r);
@@ -567,11 +594,9 @@ static int maybe_add_trigger_t2(struct target *target, unsigned hartid,
                        MCONTROL_ACTION_DEBUG_MODE);
        tdata1 = set_field(tdata1, MCONTROL_MATCH, MCONTROL_MATCH_EQUAL);
        tdata1 |= MCONTROL_M;
-       if (r->misa[hartid] & (1 << ('H' - 'A')))
-               tdata1 |= MCONTROL_H;
-       if (r->misa[hartid] & (1 << ('S' - 'A')))
+       if (r->misa & (1 << ('S' - 'A')))
                tdata1 |= MCONTROL_S;
-       if (r->misa[hartid] & (1 << ('U' - 'A')))
+       if (r->misa & (1 << ('U' - 'A')))
                tdata1 |= MCONTROL_U;
 
        if (trigger->execute)
@@ -581,10 +606,10 @@ static int maybe_add_trigger_t2(struct target *target, unsigned hartid,
        if (trigger->write)
                tdata1 |= MCONTROL_STORE;
 
-       riscv_set_register_on_hart(target, hartid, GDB_REGNO_TDATA1, tdata1);
+       riscv_set_register(target, GDB_REGNO_TDATA1, tdata1);
 
        uint64_t tdata1_rb;
-       int result = riscv_get_register_on_hart(target, &tdata1_rb, hartid, GDB_REGNO_TDATA1);
+       int result = riscv_get_register(target, &tdata1_rb, GDB_REGNO_TDATA1);
        if (result != ERROR_OK)
                return result;
        LOG_DEBUG("tdata1=0x%" PRIx64, tdata1_rb);
@@ -593,77 +618,104 @@ static int maybe_add_trigger_t2(struct target *target, unsigned hartid,
                LOG_DEBUG("Trigger doesn't support what we need; After writing 0x%"
                                PRIx64 " to tdata1 it contains 0x%" PRIx64,
                                tdata1, tdata1_rb);
-               riscv_set_register_on_hart(target, hartid, GDB_REGNO_TDATA1, 0);
+               riscv_set_register(target, GDB_REGNO_TDATA1, 0);
                return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
        }
 
-       riscv_set_register_on_hart(target, hartid, GDB_REGNO_TDATA2, trigger->address);
+       riscv_set_register(target, GDB_REGNO_TDATA2, trigger->address);
 
        return ERROR_OK;
 }
 
-static int add_trigger(struct target *target, struct trigger *trigger)
+static int maybe_add_trigger_t6(struct target *target,
+               struct trigger *trigger, uint64_t tdata1)
 {
        RISCV_INFO(r);
 
-       if (riscv_enumerate_triggers(target) != ERROR_OK)
-               return ERROR_FAIL;
+       /* tselect is already set */
+       if (tdata1 & (CSR_MCONTROL6_EXECUTE | CSR_MCONTROL6_STORE | CSR_MCONTROL6_LOAD)) {
+               /* Trigger is already in use, presumably by user code. */
+               return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+       }
 
-       /* In RTOS mode, we need to set the same trigger in the same slot on every
-        * hart, to keep up the illusion that each hart is a thread running on the
-        * same core. */
+       /* address/data match trigger */
+       tdata1 |= MCONTROL_DMODE(riscv_xlen(target));
+       tdata1 = set_field(tdata1, CSR_MCONTROL6_ACTION,
+                       MCONTROL_ACTION_DEBUG_MODE);
+       tdata1 = set_field(tdata1, CSR_MCONTROL6_MATCH, MCONTROL_MATCH_EQUAL);
+       tdata1 |= CSR_MCONTROL6_M;
+       if (r->misa & (1 << ('H' - 'A')))
+               tdata1 |= CSR_MCONTROL6_VS | CSR_MCONTROL6_VU;
+       if (r->misa & (1 << ('S' - 'A')))
+               tdata1 |= CSR_MCONTROL6_S;
+       if (r->misa & (1 << ('U' - 'A')))
+               tdata1 |= CSR_MCONTROL6_U;
 
-       /* Otherwise, we just set the trigger on the one hart this target deals
-        * with. */
+       if (trigger->execute)
+               tdata1 |= CSR_MCONTROL6_EXECUTE;
+       if (trigger->read)
+               tdata1 |= CSR_MCONTROL6_LOAD;
+       if (trigger->write)
+               tdata1 |= CSR_MCONTROL6_STORE;
 
-       riscv_reg_t tselect[RISCV_MAX_HARTS];
+       riscv_set_register(target, GDB_REGNO_TDATA1, tdata1);
 
-       int first_hart = -1;
-       for (int hartid = 0; hartid < riscv_count_harts(target); ++hartid) {
-               if (!riscv_hart_enabled(target, hartid))
-                       continue;
-               if (first_hart < 0)
-                       first_hart = hartid;
-               int result = riscv_get_register_on_hart(target, &tselect[hartid],
-                               hartid, GDB_REGNO_TSELECT);
-               if (result != ERROR_OK)
-                       return result;
+       uint64_t tdata1_rb;
+       int result = riscv_get_register(target, &tdata1_rb, GDB_REGNO_TDATA1);
+       if (result != ERROR_OK)
+               return result;
+       LOG_DEBUG("tdata1=0x%" PRIx64, tdata1_rb);
+
+       if (tdata1 != tdata1_rb) {
+               LOG_DEBUG("Trigger doesn't support what we need; After writing 0x%"
+                               PRIx64 " to tdata1 it contains 0x%" PRIx64,
+                               tdata1, tdata1_rb);
+               riscv_set_register(target, GDB_REGNO_TDATA1, 0);
+               return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
        }
-       assert(first_hart >= 0);
+
+       riscv_set_register(target, GDB_REGNO_TDATA2, trigger->address);
+
+       return ERROR_OK;
+}
+
+static int add_trigger(struct target *target, struct trigger *trigger)
+{
+       RISCV_INFO(r);
+
+       if (riscv_enumerate_triggers(target) != ERROR_OK)
+               return ERROR_FAIL;
+
+       riscv_reg_t tselect;
+       if (riscv_get_register(target, &tselect, GDB_REGNO_TSELECT) != ERROR_OK)
+               return ERROR_FAIL;
 
        unsigned int i;
-       for (i = 0; i < r->trigger_count[first_hart]; i++) {
+       for (i = 0; i < r->trigger_count; i++) {
                if (r->trigger_unique_id[i] != -1)
                        continue;
 
-               riscv_set_register_on_hart(target, first_hart, GDB_REGNO_TSELECT, i);
+               riscv_set_register(target, GDB_REGNO_TSELECT, i);
 
                uint64_t tdata1;
-               int result = riscv_get_register_on_hart(target, &tdata1, first_hart,
-                               GDB_REGNO_TDATA1);
+               int result = riscv_get_register(target, &tdata1, GDB_REGNO_TDATA1);
                if (result != ERROR_OK)
                        return result;
                int type = get_field(tdata1, MCONTROL_TYPE(riscv_xlen(target)));
 
                result = ERROR_OK;
-               for (int hartid = first_hart; hartid < riscv_count_harts(target); ++hartid) {
-                       if (!riscv_hart_enabled(target, hartid))
-                               continue;
-                       if (hartid > first_hart)
-                               riscv_set_register_on_hart(target, hartid, GDB_REGNO_TSELECT, i);
-                       switch (type) {
-                               case 1:
-                                       result = maybe_add_trigger_t1(target, hartid, trigger, tdata1);
-                                       break;
-                               case 2:
-                                       result = maybe_add_trigger_t2(target, hartid, trigger, tdata1);
-                                       break;
-                               default:
-                                       LOG_DEBUG("trigger %d has unknown type %d", i, type);
-                                       continue;
-                       }
-
-                       if (result != ERROR_OK)
+               switch (type) {
+                       case 1:
+                               result = maybe_add_trigger_t1(target, trigger, tdata1);
+                               break;
+                       case 2:
+                               result = maybe_add_trigger_t2(target, trigger, tdata1);
+                               break;
+                       case 6:
+                               result = maybe_add_trigger_t6(target, trigger, tdata1);
+                               break;
+                       default:
+                               LOG_DEBUG("trigger %d has unknown type %d", i, type);
                                continue;
                }
 
@@ -676,14 +728,9 @@ static int add_trigger(struct target *target, struct trigger *trigger)
                break;
        }
 
-       for (int hartid = first_hart; hartid < riscv_count_harts(target); ++hartid) {
-               if (!riscv_hart_enabled(target, hartid))
-                       continue;
-               riscv_set_register_on_hart(target, hartid, GDB_REGNO_TSELECT,
-                               tselect[hartid]);
-       }
+       riscv_set_register(target, GDB_REGNO_TSELECT, tselect);
 
-       if (i >= r->trigger_count[first_hart]) {
+       if (i >= r->trigger_count) {
                LOG_ERROR("Couldn't find an available hardware trigger.");
                return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
        }
@@ -691,6 +738,124 @@ static int add_trigger(struct target *target, struct trigger *trigger)
        return ERROR_OK;
 }
 
+/**
+ * Write one memory item of given "size". Use memory access of given "access_size".
+ * Utilize read-modify-write, if needed.
+ * */
+static int write_by_given_size(struct target *target, target_addr_t address,
+               uint32_t size, uint8_t *buffer, uint32_t access_size)
+{
+       assert(size == 1 || size == 2 || size == 4 || size == 8);
+       assert(access_size == 1 || access_size == 2 || access_size == 4 || access_size == 8);
+
+       if (access_size <= size && address % access_size == 0)
+               /* Can do the memory access directly without a helper buffer. */
+               return target_write_memory(target, address, access_size, size / access_size, buffer);
+
+       unsigned int offset_head = address % access_size;
+       unsigned int n_blocks = ((size + offset_head) <= access_size) ? 1 : 2;
+       uint8_t helper_buf[n_blocks * access_size];
+
+       /* Read from memory */
+       if (target_read_memory(target, address - offset_head, access_size, n_blocks, helper_buf) != ERROR_OK)
+               return ERROR_FAIL;
+
+       /* Modify and write back */
+       memcpy(helper_buf + offset_head, buffer, size);
+       return target_write_memory(target, address - offset_head, access_size, n_blocks, helper_buf);
+}
+
+/**
+ * Read one memory item of given "size". Use memory access of given "access_size".
+ * Read larger section of memory and pick out the required portion, if needed.
+ * */
+static int read_by_given_size(struct target *target, target_addr_t address,
+       uint32_t size, uint8_t *buffer, uint32_t access_size)
+{
+       assert(size == 1 || size == 2 || size == 4 || size == 8);
+       assert(access_size == 1 || access_size == 2 || access_size == 4 || access_size == 8);
+
+       if (access_size <= size && address % access_size == 0)
+               /* Can do the memory access directly without a helper buffer. */
+               return target_read_memory(target, address, access_size, size / access_size, buffer);
+
+       unsigned int offset_head = address % access_size;
+       unsigned int n_blocks = ((size + offset_head) <= access_size) ? 1 : 2;
+       uint8_t helper_buf[n_blocks * access_size];
+
+       /* Read from memory */
+       if (target_read_memory(target, address - offset_head, access_size, n_blocks, helper_buf) != ERROR_OK)
+               return ERROR_FAIL;
+
+       /* Pick the requested portion from the buffer */
+       memcpy(buffer, helper_buf + offset_head, size);
+       return ERROR_OK;
+}
+
+/**
+ * Write one memory item using any memory access size that will work.
+ * Utilize read-modify-write, if needed.
+ * */
+int riscv_write_by_any_size(struct target *target, target_addr_t address, uint32_t size, uint8_t *buffer)
+{
+       assert(size == 1 || size == 2 ||  size == 4 || size == 8);
+
+       /* Find access size that correspond to data size and the alignment. */
+       unsigned int preferred_size = size;
+       while (address % preferred_size != 0)
+               preferred_size /= 2;
+
+       /* First try the preferred (most natural) access size. */
+       if (write_by_given_size(target, address, size, buffer, preferred_size) == ERROR_OK)
+               return ERROR_OK;
+
+       /* On failure, try other access sizes.
+          Minimize the number of accesses by trying first the largest size. */
+       for (unsigned int access_size = 8; access_size > 0; access_size /= 2) {
+               if (access_size == preferred_size)
+                       /* Already tried this size. */
+                       continue;
+
+               if (write_by_given_size(target, address, size, buffer, access_size) == ERROR_OK)
+                       return ERROR_OK;
+       }
+
+       /* No access attempt succeeded. */
+       return ERROR_FAIL;
+}
+
+/**
+ * Read one memory item using any memory access size that will work.
+ * Read larger section of memory and pick out the required portion, if needed.
+ * */
+int riscv_read_by_any_size(struct target *target, target_addr_t address, uint32_t size, uint8_t *buffer)
+{
+       assert(size == 1 || size == 2 ||  size == 4 || size == 8);
+
+       /* Find access size that correspond to data size and the alignment. */
+       unsigned int preferred_size = size;
+       while (address % preferred_size != 0)
+               preferred_size /= 2;
+
+       /* First try the preferred (most natural) access size. */
+       if (read_by_given_size(target, address, size, buffer, preferred_size) == ERROR_OK)
+               return ERROR_OK;
+
+       /* On failure, try other access sizes.
+          Minimize the number of accesses by trying first the largest size. */
+       for (unsigned int access_size = 8; access_size > 0; access_size /= 2) {
+               if (access_size == preferred_size)
+                       /* Already tried this size. */
+                       continue;
+
+               if (read_by_given_size(target, address, size, buffer, access_size) == ERROR_OK)
+                       return ERROR_OK;
+       }
+
+       /* No access attempt succeeded. */
+       return ERROR_FAIL;
+}
+
 int riscv_add_breakpoint(struct target *target, struct breakpoint *breakpoint)
 {
        LOG_DEBUG("[%d] @0x%" TARGET_PRIxADDR, target->coreid, breakpoint->address);
@@ -707,8 +872,9 @@ int riscv_add_breakpoint(struct target *target, struct breakpoint *breakpoint)
                        return ERROR_FAIL;
                }
 
-               if (target_read_memory(target, breakpoint->address, 2, breakpoint->length / 2,
-                                       breakpoint->orig_instr) != ERROR_OK) {
+               /* Read the original instruction. */
+               if (riscv_read_by_any_size(
+                               target, breakpoint->address, breakpoint->length, breakpoint->orig_instr) != ERROR_OK) {
                        LOG_ERROR("Failed to read original instruction at 0x%" TARGET_PRIxADDR,
                                        breakpoint->address);
                        return ERROR_FAIL;
@@ -716,9 +882,8 @@ int riscv_add_breakpoint(struct target *target, struct breakpoint *breakpoint)
 
                uint8_t buff[4] = { 0 };
                buf_set_u32(buff, 0, breakpoint->length * CHAR_BIT, breakpoint->length == 4 ? ebreak() : ebreak_c());
-               int const retval = target_write_memory(target, breakpoint->address, 2, breakpoint->length / 2, buff);
-
-               if (retval != ERROR_OK) {
+               /* Write the ebreak instruction. */
+               if (riscv_write_by_any_size(target, breakpoint->address, breakpoint->length, buff) != ERROR_OK) {
                        LOG_ERROR("Failed to write %d-byte breakpoint instruction at 0x%"
                                        TARGET_PRIxADDR, breakpoint->length, breakpoint->address);
                        return ERROR_FAIL;
@@ -746,40 +911,26 @@ static int remove_trigger(struct target *target, struct trigger *trigger)
        if (riscv_enumerate_triggers(target) != ERROR_OK)
                return ERROR_FAIL;
 
-       int first_hart = -1;
-       for (int hartid = 0; hartid < riscv_count_harts(target); ++hartid) {
-               if (!riscv_hart_enabled(target, hartid))
-                       continue;
-               if (first_hart < 0) {
-                       first_hart = hartid;
-                       break;
-               }
-       }
-       assert(first_hart >= 0);
-
        unsigned int i;
-       for (i = 0; i < r->trigger_count[first_hart]; i++) {
+       for (i = 0; i < r->trigger_count; i++) {
                if (r->trigger_unique_id[i] == trigger->unique_id)
                        break;
        }
-       if (i >= r->trigger_count[first_hart]) {
+       if (i >= r->trigger_count) {
                LOG_ERROR("Couldn't find the hardware resources used by hardware "
                                "trigger.");
                return ERROR_FAIL;
        }
        LOG_DEBUG("[%d] Stop using resource %d for bp %d", target->coreid, i,
                        trigger->unique_id);
-       for (int hartid = first_hart; hartid < riscv_count_harts(target); ++hartid) {
-               if (!riscv_hart_enabled(target, hartid))
-                       continue;
-               riscv_reg_t tselect;
-               int result = riscv_get_register_on_hart(target, &tselect, hartid, GDB_REGNO_TSELECT);
-               if (result != ERROR_OK)
-                       return result;
-               riscv_set_register_on_hart(target, hartid, GDB_REGNO_TSELECT, i);
-               riscv_set_register_on_hart(target, hartid, GDB_REGNO_TDATA1, 0);
-               riscv_set_register_on_hart(target, hartid, GDB_REGNO_TSELECT, tselect);
-       }
+
+       riscv_reg_t tselect;
+       int result = riscv_get_register(target, &tselect, GDB_REGNO_TSELECT);
+       if (result != ERROR_OK)
+               return result;
+       riscv_set_register(target, GDB_REGNO_TSELECT, i);
+       riscv_set_register(target, GDB_REGNO_TDATA1, 0);
+       riscv_set_register(target, GDB_REGNO_TSELECT, tselect);
        r->trigger_unique_id[i] = -1;
 
        return ERROR_OK;
@@ -789,8 +940,9 @@ int riscv_remove_breakpoint(struct target *target,
                struct breakpoint *breakpoint)
 {
        if (breakpoint->type == BKPT_SOFT) {
-               if (target_write_memory(target, breakpoint->address, 2, breakpoint->length / 2,
-                                       breakpoint->orig_instr) != ERROR_OK) {
+               /* Write the original instruction. */
+               if (riscv_write_by_any_size(
+                               target, breakpoint->address, breakpoint->length, breakpoint->orig_instr) != ERROR_OK) {
                        LOG_ERROR("Failed to restore instruction for %d-byte breakpoint at "
                                        "0x%" TARGET_PRIxADDR, breakpoint->length, breakpoint->address);
                        return ERROR_FAIL;
@@ -866,8 +1018,6 @@ int riscv_hit_watchpoint(struct target *target, struct watchpoint **hit_watchpoi
 {
        struct watchpoint *wp = target->watchpoints;
 
-       if (riscv_rtos_enabled(target))
-               riscv_set_current_hartid(target, target->rtos->current_thread - 1);
        LOG_DEBUG("Current hartid = %d", riscv_current_hartid(target));
 
        /*TODO instead of disassembling the instruction that we think caused the
@@ -953,7 +1103,7 @@ static int old_or_new_riscv_step(struct target *target, int current,
 {
        RISCV_INFO(r);
        LOG_DEBUG("handle_breakpoints=%d", handle_breakpoints);
-       if (r->is_halted == NULL)
+       if (!r->is_halted)
                return oldriscv_step(target, current, address, handle_breakpoints);
        else
                return riscv_openocd_step(target, current, address, handle_breakpoints);
@@ -970,14 +1120,14 @@ static int riscv_examine(struct target *target)
 
        /* Don't need to select dbus, since the first thing we do is read dtmcontrol. */
 
-       riscv_info_t *info = (riscv_info_t *) target->arch_info;
+       RISCV_INFO(info);
        uint32_t dtmcontrol = dtmcontrol_scan(target, 0);
        LOG_DEBUG("dtmcontrol=0x%x", dtmcontrol);
        info->dtm_version = get_field(dtmcontrol, DTMCONTROL_VERSION);
        LOG_DEBUG("  version=0x%x", info->dtm_version);
 
        struct target_type *tt = get_target_type(target);
-       if (tt == NULL)
+       if (!tt)
                return ERROR_FAIL;
 
        int result = tt->init_target(info->cmd_ctx, target);
@@ -996,50 +1146,48 @@ static int oldriscv_poll(struct target *target)
 static int old_or_new_riscv_poll(struct target *target)
 {
        RISCV_INFO(r);
-       if (r->is_halted == NULL)
+       if (!r->is_halted)
                return oldriscv_poll(target);
        else
                return riscv_openocd_poll(target);
 }
 
+int riscv_select_current_hart(struct target *target)
+{
+       return riscv_set_current_hartid(target, target->coreid);
+}
+
 int halt_prep(struct target *target)
 {
        RISCV_INFO(r);
-       for (int i = 0; i < riscv_count_harts(target); ++i) {
-               if (!riscv_hart_enabled(target, i))
-                       continue;
 
-               LOG_DEBUG("[%s] prep hart, debug_reason=%d", target_name(target),
-                                 target->debug_reason);
-               if (riscv_set_current_hartid(target, i) != ERROR_OK)
+       LOG_DEBUG("[%s] prep hart, debug_reason=%d", target_name(target),
+                               target->debug_reason);
+       if (riscv_select_current_hart(target) != ERROR_OK)
+               return ERROR_FAIL;
+       if (riscv_is_halted(target)) {
+               LOG_DEBUG("[%s] Hart is already halted (reason=%d).",
+                               target_name(target), target->debug_reason);
+       } else {
+               if (r->halt_prep(target) != ERROR_OK)
                        return ERROR_FAIL;
-               if (riscv_is_halted(target)) {
-                       LOG_DEBUG("Hart %d is already halted (reason=%d).", i,
-                                         target->debug_reason);
-               } else {
-                       if (r->halt_prep(target) != ERROR_OK)
-                               return ERROR_FAIL;
-                       r->prepped = true;
-               }
+               r->prepped = true;
        }
+
        return ERROR_OK;
 }
 
 int riscv_halt_go_all_harts(struct target *target)
 {
        RISCV_INFO(r);
-       for (int i = 0; i < riscv_count_harts(target); ++i) {
-               if (!riscv_hart_enabled(target, i))
-                       continue;
 
-               if (riscv_set_current_hartid(target, i) != ERROR_OK)
+       if (riscv_select_current_hart(target) != ERROR_OK)
+               return ERROR_FAIL;
+       if (riscv_is_halted(target)) {
+               LOG_DEBUG("[%s] Hart is already halted.", target_name(target));
+       } else {
+               if (r->halt_go(target) != ERROR_OK)
                        return ERROR_FAIL;
-               if (riscv_is_halted(target)) {
-                       LOG_DEBUG("Hart %d is already halted.", i);
-               } else {
-                       if (r->halt_go(target) != ERROR_OK)
-                               return ERROR_FAIL;
-               }
        }
 
        riscv_invalidate_register_cache(target);
@@ -1051,7 +1199,7 @@ int halt_go(struct target *target)
 {
        riscv_info_t *r = riscv_info(target);
        int result;
-       if (r->is_halted == NULL) {
+       if (!r->is_halted) {
                struct target_type *tt = get_target_type(target);
                result = tt->halt(target);
        } else {
@@ -1073,7 +1221,7 @@ int riscv_halt(struct target *target)
 {
        RISCV_INFO(r);
 
-       if (r->is_halted == NULL) {
+       if (!r->is_halted) {
                struct target_type *tt = get_target_type(target);
                return tt->halt(target);
        }
@@ -1112,15 +1260,6 @@ int riscv_halt(struct target *target)
                        return ERROR_FAIL;
        }
 
-       if (riscv_rtos_enabled(target)) {
-               if (r->rtos_hartid != -1) {
-                       LOG_DEBUG("halt requested on RTOS hartid %d", r->rtos_hartid);
-                       target->rtos->current_threadid = r->rtos_hartid + 1;
-                       target->rtos->current_thread = r->rtos_hartid + 1;
-               } else
-                       LOG_DEBUG("halt requested, but no known RTOS hartid");
-       }
-
        return result;
 }
 
@@ -1142,22 +1281,19 @@ static int riscv_deassert_reset(struct target *target)
 int riscv_resume_prep_all_harts(struct target *target)
 {
        RISCV_INFO(r);
-       for (int i = 0; i < riscv_count_harts(target); ++i) {
-               if (!riscv_hart_enabled(target, i))
-                       continue;
 
-               LOG_DEBUG("prep hart %d", i);
-               if (riscv_set_current_hartid(target, i) != ERROR_OK)
+       LOG_DEBUG("[%s] prep hart", target_name(target));
+       if (riscv_select_current_hart(target) != ERROR_OK)
+               return ERROR_FAIL;
+       if (riscv_is_halted(target)) {
+               if (r->resume_prep(target) != ERROR_OK)
                        return ERROR_FAIL;
-               if (riscv_is_halted(target)) {
-                       if (r->resume_prep(target) != ERROR_OK)
-                               return ERROR_FAIL;
-               } else {
-                       LOG_DEBUG("  hart %d requested resume, but was already resumed", i);
-               }
+       } else {
+               LOG_DEBUG("[%s] hart requested resume, but was already resumed",
+                               target_name(target));
        }
 
-       LOG_DEBUG("[%d] mark as prepped", target->coreid);
+       LOG_DEBUG("[%s] mark as prepped", target_name(target));
        r->prepped = true;
 
        return ERROR_OK;
@@ -1173,13 +1309,12 @@ static int disable_triggers(struct target *target, riscv_reg_t *state)
        if (riscv_enumerate_triggers(target) != ERROR_OK)
                return ERROR_FAIL;
 
-       int hartid = riscv_current_hartid(target);
        if (r->manual_hwbp_set) {
                /* Look at every trigger that may have been set. */
                riscv_reg_t tselect;
                if (riscv_get_register(target, &tselect, GDB_REGNO_TSELECT) != ERROR_OK)
                        return ERROR_FAIL;
-               for (unsigned t = 0; t < r->trigger_count[hartid]; t++) {
+               for (unsigned int t = 0; t < r->trigger_count; t++) {
                        if (riscv_set_register(target, GDB_REGNO_TSELECT, t) != ERROR_OK)
                                return ERROR_FAIL;
                        riscv_reg_t tdata1;
@@ -1217,14 +1352,12 @@ static int enable_triggers(struct target *target, riscv_reg_t *state)
 {
        RISCV_INFO(r);
 
-       int hartid = riscv_current_hartid(target);
-
        if (r->manual_hwbp_set) {
                /* Look at every trigger that may have been set. */
                riscv_reg_t tselect;
                if (riscv_get_register(target, &tselect, GDB_REGNO_TSELECT) != ERROR_OK)
                        return ERROR_FAIL;
-               for (unsigned t = 0; t < r->trigger_count[hartid]; t++) {
+               for (unsigned int t = 0; t < r->trigger_count; t++) {
                        if (state[t] != 0) {
                                if (riscv_set_register(target, GDB_REGNO_TSELECT, t) != ERROR_OK)
                                        return ERROR_FAIL;
@@ -1299,7 +1432,7 @@ static int resume_go(struct target *target, int current,
 {
        riscv_info_t *r = riscv_info(target);
        int result;
-       if (r->is_halted == NULL) {
+       if (!r->is_halted) {
                struct target_type *tt = get_target_type(target);
                result = tt->resume(target, current, address, handle_breakpoints,
                                debug_execution);
@@ -1378,17 +1511,6 @@ static int riscv_target_resume(struct target *target, int current, target_addr_t
                        debug_execution, false);
 }
 
-static int riscv_select_current_hart(struct target *target)
-{
-       RISCV_INFO(r);
-       if (riscv_rtos_enabled(target)) {
-               if (r->rtos_hartid == -1)
-                       r->rtos_hartid = target->rtos->current_threadid - 1;
-               return riscv_set_current_hartid(target, r->rtos_hartid);
-       } else
-               return riscv_set_current_hartid(target, target->coreid);
-}
-
 static int riscv_mmu(struct target *target, int *enabled)
 {
        if (!riscv_enable_virt2phys) {
@@ -1396,9 +1518,6 @@ static int riscv_mmu(struct target *target, int *enabled)
                return ERROR_OK;
        }
 
-       if (riscv_rtos_enabled(target))
-               riscv_set_current_hartid(target, target->rtos->current_thread - 1);
-
        /* Don't use MMU in explicit or effective M (machine) mode */
        riscv_reg_t priv;
        if (riscv_get_register(target, &priv, GDB_REGNO_PRIV) != ERROR_OK) {
@@ -1445,13 +1564,10 @@ static int riscv_address_translate(struct target *target,
        int mode;
        uint64_t ppn_value;
        target_addr_t table_address;
-       virt2phys_info_t *info;
-       uint64_t pte;
+       const virt2phys_info_t *info;
+       uint64_t pte = 0;
        int i;
 
-       if (riscv_rtos_enabled(target))
-               riscv_set_current_hartid(target, target->rtos->current_thread - 1);
-
        int result = riscv_get_register(target, &satp_value, GDB_REGNO_SATP);
        if (result != ERROR_OK)
                return result;
@@ -1616,13 +1732,25 @@ static int riscv_write_memory(struct target *target, target_addr_t address,
        return tt->write_memory(target, address, size, count, buffer);
 }
 
+const char *riscv_get_gdb_arch(struct target *target)
+{
+       switch (riscv_xlen(target)) {
+               case 32:
+                       return "riscv:rv32";
+               case 64:
+                       return "riscv:rv64";
+       }
+       LOG_ERROR("Unsupported xlen: %d", riscv_xlen(target));
+       return NULL;
+}
+
 static int riscv_get_gdb_reg_list_internal(struct target *target,
                struct reg **reg_list[], int *reg_list_size,
                enum target_register_class reg_class, bool read)
 {
        RISCV_INFO(r);
-       LOG_DEBUG("rtos_hartid=%d, current_hartid=%d, reg_class=%d, read=%d",
-                       r->rtos_hartid, r->current_hartid, reg_class, read);
+       LOG_DEBUG("current_hartid=%d, reg_class=%d, read=%d",
+                       r->current_hartid, reg_class, read);
 
        if (!target->reg_cache) {
                LOG_ERROR("Target not initialized. Return ERROR_FAIL.");
@@ -1692,8 +1820,7 @@ static int riscv_run_algorithm(struct target *target, int num_mem_params,
                struct reg_param *reg_params, target_addr_t entry_point,
                target_addr_t exit_point, int timeout_ms, void *arch_info)
 {
-       riscv_info_t *info = (riscv_info_t *) target->arch_info;
-       int hartid = riscv_current_hartid(target);
+       RISCV_INFO(info);
 
        if (num_mem_params > 0) {
                LOG_ERROR("Memory parameters are not supported for RISC-V algorithms.");
@@ -1706,7 +1833,7 @@ static int riscv_run_algorithm(struct target *target, int num_mem_params,
        }
 
        /* Save registers */
-       struct reg *reg_pc = register_get_by_name(target->reg_cache, "pc", 1);
+       struct reg *reg_pc = register_get_by_name(target->reg_cache, "pc", true);
        if (!reg_pc || reg_pc->type->get(reg_pc) != ERROR_OK)
                return ERROR_FAIL;
        uint64_t saved_pc = buf_get_u64(reg_pc->value, 0, reg_pc->size);
@@ -1715,7 +1842,7 @@ static int riscv_run_algorithm(struct target *target, int num_mem_params,
        uint64_t saved_regs[32];
        for (int i = 0; i < num_reg_params; i++) {
                LOG_DEBUG("save %s", reg_params[i].reg_name);
-               struct reg *r = register_get_by_name(target->reg_cache, reg_params[i].reg_name, 0);
+               struct reg *r = register_get_by_name(target->reg_cache, reg_params[i].reg_name, false);
                if (!r) {
                        LOG_ERROR("Couldn't find register named '%s'", reg_params[i].reg_name);
                        return ERROR_FAIL;
@@ -1749,7 +1876,7 @@ static int riscv_run_algorithm(struct target *target, int num_mem_params,
 
        LOG_DEBUG("Disabling Interrupts");
        struct reg *reg_mstatus = register_get_by_name(target->reg_cache,
-                       "mstatus", 1);
+                       "mstatus", true);
        if (!reg_mstatus) {
                LOG_ERROR("Couldn't find mstatus!");
                return ERROR_FAIL;
@@ -1758,7 +1885,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[0], set_field(current_mstatus,
+       buf_set_u64(mstatus_bytes, 0, info->xlen, set_field(current_mstatus,
                                ie_mask, 0));
 
        reg_mstatus->type->set(reg_mstatus, mstatus_bytes);
@@ -1788,7 +1915,7 @@ static int riscv_run_algorithm(struct target *target, int num_mem_params,
                                GDB_REGNO_PC,
                                GDB_REGNO_MSTATUS, GDB_REGNO_MEPC, GDB_REGNO_MCAUSE,
                        };
-                       for (unsigned i = 0; i < DIM(regnums); i++) {
+                       for (unsigned i = 0; i < ARRAY_SIZE(regnums); i++) {
                                enum gdb_regno regno = regnums[i];
                                riscv_reg_t reg_value;
                                if (riscv_get_register(target, &reg_value, regno) != ERROR_OK)
@@ -1804,7 +1931,7 @@ static int riscv_run_algorithm(struct target *target, int num_mem_params,
        }
 
        /* The current hart id might have been changed in poll(). */
-       if (riscv_set_current_hartid(target, hartid) != ERROR_OK)
+       if (riscv_select_current_hart(target) != ERROR_OK)
                return ERROR_FAIL;
 
        if (reg_pc->type->get(reg_pc) != ERROR_OK)
@@ -1818,19 +1945,19 @@ 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[0], current_mstatus);
+       buf_set_u64(mstatus_bytes, 0, info->xlen, current_mstatus);
        reg_mstatus->type->set(reg_mstatus, mstatus_bytes);
 
        /* Restore registers */
        uint8_t buf[8] = { 0 };
-       buf_set_u64(buf, 0, info->xlen[0], saved_pc);
+       buf_set_u64(buf, 0, info->xlen, saved_pc);
        if (reg_pc->type->set(reg_pc, buf) != ERROR_OK)
                return ERROR_FAIL;
 
        for (int i = 0; i < num_reg_params; i++) {
                if (reg_params[i].direction == PARAM_IN ||
                                reg_params[i].direction == PARAM_IN_OUT) {
-                       struct reg *r = register_get_by_name(target->reg_cache, reg_params[i].reg_name, 0);
+                       struct reg *r = register_get_by_name(target->reg_cache, reg_params[i].reg_name, false);
                        if (r->type->get(r) != ERROR_OK) {
                                LOG_ERROR("get(%s) failed", r->name);
                                return ERROR_FAIL;
@@ -1838,8 +1965,8 @@ static int riscv_run_algorithm(struct target *target, int num_mem_params,
                        buf_cpy(r->value, reg_params[i].value, reg_params[i].size);
                }
                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[0], saved_regs[r->number]);
+               struct reg *r = register_get_by_name(target->reg_cache, reg_params[i].reg_name, false);
+               buf_set_u64(buf, 0, info->xlen, saved_regs[r->number]);
                if (r->type->set(r, buf) != ERROR_OK) {
                        LOG_ERROR("set(%s) failed", r->name);
                        return ERROR_FAIL;
@@ -1853,7 +1980,86 @@ static int riscv_checksum_memory(struct target *target,
                target_addr_t address, uint32_t count,
                uint32_t *checksum)
 {
-       return ERROR_FAIL;
+       struct working_area *crc_algorithm;
+       struct reg_param reg_params[2];
+       int retval;
+
+       LOG_DEBUG("address=0x%" TARGET_PRIxADDR "; count=0x%" PRIx32, address, count);
+
+       static const uint8_t riscv32_crc_code[] = {
+#include "contrib/loaders/checksum/riscv32_crc.inc"
+       };
+       static const uint8_t riscv64_crc_code[] = {
+#include "contrib/loaders/checksum/riscv64_crc.inc"
+       };
+
+       static const uint8_t *crc_code;
+
+       unsigned xlen = riscv_xlen(target);
+       unsigned crc_code_size;
+       if (xlen == 32) {
+               crc_code = riscv32_crc_code;
+               crc_code_size = sizeof(riscv32_crc_code);
+       } else {
+               crc_code = riscv64_crc_code;
+               crc_code_size = sizeof(riscv64_crc_code);
+       }
+
+       if (count < crc_code_size * 4) {
+               /* Don't use the algorithm for relatively small buffers. It's faster
+                * just to read the memory.  target_checksum_memory() will take care of
+                * that if we fail. */
+               return ERROR_FAIL;
+       }
+
+       retval = target_alloc_working_area(target, crc_code_size, &crc_algorithm);
+       if (retval != ERROR_OK)
+               return retval;
+
+       if (crc_algorithm->address + crc_algorithm->size > address &&
+                       crc_algorithm->address < address + count) {
+               /* Region to checksum overlaps with the work area we've been assigned.
+                * Bail. (Would be better to manually checksum what we read there, and
+                * use the algorithm for the rest.) */
+               target_free_working_area(target, crc_algorithm);
+               return ERROR_FAIL;
+       }
+
+       retval = target_write_buffer(target, crc_algorithm->address, crc_code_size,
+                       crc_code);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("Failed to write code to " TARGET_ADDR_FMT ": %d",
+                               crc_algorithm->address, retval);
+               target_free_working_area(target, crc_algorithm);
+               return retval;
+       }
+
+       init_reg_param(&reg_params[0], "a0", xlen, PARAM_IN_OUT);
+       init_reg_param(&reg_params[1], "a1", xlen, PARAM_OUT);
+       buf_set_u64(reg_params[0].value, 0, xlen, address);
+       buf_set_u64(reg_params[1].value, 0, xlen, count);
+
+       /* 20 second timeout/megabyte */
+       int timeout = 20000 * (1 + (count / (1024 * 1024)));
+
+       retval = target_run_algorithm(target, 0, NULL, 2, reg_params,
+                       crc_algorithm->address,
+                       0,      /* Leave exit point unspecified because we don't know. */
+                       timeout, NULL);
+
+       if (retval == ERROR_OK)
+               *checksum = buf_get_u32(reg_params[0].value, 0, 32);
+       else
+               LOG_ERROR("error executing RISC-V CRC algorithm");
+
+       destroy_reg_param(&reg_params[0]);
+       destroy_reg_param(&reg_params[1]);
+
+       target_free_working_area(target, crc_algorithm);
+
+       LOG_DEBUG("checksum=0x%" PRIx32 ", result=%d", *checksum, retval);
+
+       return retval;
 }
 
 /*** OpenOCD Helper Functions ***/
@@ -1915,62 +2121,68 @@ int set_debug_reason(struct target *target, enum riscv_halt_reason halt_reason)
        return ERROR_OK;
 }
 
-/*** OpenOCD Interface ***/
-int riscv_openocd_poll(struct target *target)
+int sample_memory(struct target *target)
 {
-       LOG_DEBUG("polling all harts");
-       int halted_hart = -1;
-       if (riscv_rtos_enabled(target)) {
-               /* Check every hart for an event. */
-               for (int i = 0; i < riscv_count_harts(target); ++i) {
-                       enum riscv_poll_hart out = riscv_poll_hart(target, i);
-                       switch (out) {
-                       case RPH_NO_CHANGE:
-                       case RPH_DISCOVERED_RUNNING:
-                               continue;
-                       case RPH_DISCOVERED_HALTED:
-                               halted_hart = i;
-                               break;
-                       case RPH_ERROR:
-                               return ERROR_FAIL;
+       RISCV_INFO(r);
+
+       if (!r->sample_buf.buf || !r->sample_config.enabled)
+               return ERROR_OK;
+
+       LOG_DEBUG("buf used/size: %d/%d", r->sample_buf.used, r->sample_buf.size);
+
+       uint64_t start = timeval_ms();
+       riscv_sample_buf_maybe_add_timestamp(target, true);
+       int result = ERROR_OK;
+       if (r->sample_memory) {
+               result = r->sample_memory(target, &r->sample_buf, &r->sample_config,
+                                                                         start + TARGET_DEFAULT_POLLING_INTERVAL);
+               if (result != ERROR_NOT_IMPLEMENTED)
+                       goto exit;
+       }
+
+       /* Default slow path. */
+       while (timeval_ms() - start < TARGET_DEFAULT_POLLING_INTERVAL) {
+               for (unsigned int i = 0; i < ARRAY_SIZE(r->sample_config.bucket); i++) {
+                       if (r->sample_config.bucket[i].enabled &&
+                                       r->sample_buf.used + 1 + r->sample_config.bucket[i].size_bytes < r->sample_buf.size) {
+                               assert(i < RISCV_SAMPLE_BUF_TIMESTAMP_BEFORE);
+                               r->sample_buf.buf[r->sample_buf.used] = i;
+                               result = riscv_read_phys_memory(
+                                       target, r->sample_config.bucket[i].address,
+                                       r->sample_config.bucket[i].size_bytes, 1,
+                                       r->sample_buf.buf + r->sample_buf.used + 1);
+                               if (result == ERROR_OK)
+                                       r->sample_buf.used += 1 + r->sample_config.bucket[i].size_bytes;
+                               else
+                                       goto exit;
                        }
                }
-               if (halted_hart == -1) {
-                       LOG_DEBUG("  no harts just halted, target->state=%d", target->state);
-                       return ERROR_OK;
-               }
-               LOG_DEBUG("  hart %d halted", halted_hart);
-
-               target->state = TARGET_HALTED;
-               enum riscv_halt_reason halt_reason = riscv_halt_reason(target, halted_hart);
-               if (set_debug_reason(target, halt_reason) != ERROR_OK)
-                       return ERROR_FAIL;
+       }
 
-               target->rtos->current_threadid = halted_hart + 1;
-               target->rtos->current_thread = halted_hart + 1;
-               riscv_set_rtos_hartid(target, halted_hart);
+exit:
+       riscv_sample_buf_maybe_add_timestamp(target, false);
+       if (result != ERROR_OK) {
+               LOG_INFO("Turning off memory sampling because it failed.");
+               r->sample_config.enabled = false;
+       }
+       return result;
+}
 
-               /* If we're here then at least one hart triggered.  That means we want
-                * to go and halt _every_ hart (configured with -rtos riscv) 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.
-                * riscv_halt() will do all that for us. */
-               riscv_halt(target);
+/*** OpenOCD Interface ***/
+int riscv_openocd_poll(struct target *target)
+{
+       LOG_DEBUG("polling all harts");
+       int halted_hart = -1;
 
-       } else if (target->smp) {
+       if (target->smp) {
                unsigned halts_discovered = 0;
-               unsigned total_targets = 0;
-               bool newly_halted[RISCV_MAX_HARTS] = {0};
                unsigned should_remain_halted = 0;
                unsigned should_resume = 0;
                unsigned i = 0;
-               for (struct target_list *list = target->head; list != NULL;
+               for (struct target_list *list = target->head; list;
                                list = list->next, i++) {
-                       total_targets++;
                        struct target *t = list->target;
                        riscv_info_t *r = riscv_info(t);
-                       assert(i < DIM(newly_halted));
                        enum riscv_poll_hart out = riscv_poll_hart(t, r->current_hartid);
                        switch (out) {
                        case RPH_NO_CHANGE:
@@ -1981,7 +2193,6 @@ int riscv_openocd_poll(struct target *target)
                                break;
                        case RPH_DISCOVERED_HALTED:
                                halts_discovered++;
-                               newly_halted[i] = true;
                                t->state = TARGET_HALTED;
                                enum riscv_halt_reason halt_reason =
                                        riscv_halt_reason(t, r->current_hartid);
@@ -2027,15 +2238,29 @@ int riscv_openocd_poll(struct target *target)
                        LOG_DEBUG("resume all");
                        riscv_resume(target, true, 0, 0, 0, false);
                }
+
+               /* Sample memory if any target is running. */
+               for (struct target_list *list = target->head; list;
+                               list = list->next, i++) {
+                       struct target *t = list->target;
+                       if (t->state == TARGET_RUNNING) {
+                               sample_memory(target);
+                               break;
+                       }
+               }
+
                return ERROR_OK;
 
        } else {
                enum riscv_poll_hart out = riscv_poll_hart(target,
                                riscv_current_hartid(target));
-               if (out == RPH_NO_CHANGE || out == RPH_DISCOVERED_RUNNING)
+               if (out == RPH_NO_CHANGE || out == RPH_DISCOVERED_RUNNING) {
+                       if (target->state == TARGET_RUNNING)
+                               sample_memory(target);
                        return ERROR_OK;
-               else if (out == RPH_ERROR)
+               } else if (out == RPH_ERROR) {
                        return ERROR_FAIL;
+               }
 
                halted_hart = riscv_current_hartid(target);
                LOG_DEBUG("  hart %d halted", halted_hart);
@@ -2132,32 +2357,86 @@ COMMAND_HANDLER(riscv_set_reset_timeout_sec)
        return ERROR_OK;
 }
 
-COMMAND_HANDLER(riscv_test_compliance) {
-
+COMMAND_HANDLER(riscv_set_prefer_sba)
+{
        struct target *target = get_current_target(CMD_CTX);
-
        RISCV_INFO(r);
-
-       if (CMD_ARGC > 0) {
-               LOG_ERROR("Command does not take any parameters.");
+       bool prefer_sba;
+       LOG_WARNING("`riscv set_prefer_sba` is deprecated. Please use `riscv set_mem_access` instead.");
+       if (CMD_ARGC != 1) {
+               LOG_ERROR("Command takes exactly 1 parameter");
                return ERROR_COMMAND_SYNTAX_ERROR;
        }
-
-       if (r->test_compliance) {
-               return r->test_compliance(target);
+       COMMAND_PARSE_ON_OFF(CMD_ARGV[0], prefer_sba);
+       if (prefer_sba) {
+               /* Use system bus with highest priority */
+               r->mem_access_methods[0] = RISCV_MEM_ACCESS_SYSBUS;
+               r->mem_access_methods[1] = RISCV_MEM_ACCESS_PROGBUF;
+               r->mem_access_methods[2] = RISCV_MEM_ACCESS_ABSTRACT;
        } else {
-               LOG_ERROR("This target does not support this command (may implement an older version of the spec).");
-               return ERROR_FAIL;
+               /* Use progbuf with highest priority */
+               r->mem_access_methods[0] = RISCV_MEM_ACCESS_PROGBUF;
+               r->mem_access_methods[1] = RISCV_MEM_ACCESS_SYSBUS;
+               r->mem_access_methods[2] = RISCV_MEM_ACCESS_ABSTRACT;
        }
+
+       /* Reset warning flags */
+       r->mem_access_progbuf_warn = true;
+       r->mem_access_sysbus_warn = true;
+       r->mem_access_abstract_warn = true;
+
+       return ERROR_OK;
 }
 
-COMMAND_HANDLER(riscv_set_prefer_sba)
+COMMAND_HANDLER(riscv_set_mem_access)
 {
-       if (CMD_ARGC != 1) {
-               LOG_ERROR("Command takes exactly 1 parameter");
+       struct target *target = get_current_target(CMD_CTX);
+       RISCV_INFO(r);
+       int progbuf_cnt = 0;
+       int sysbus_cnt = 0;
+       int abstract_cnt = 0;
+
+       if (CMD_ARGC < 1 || CMD_ARGC > RISCV_NUM_MEM_ACCESS_METHODS) {
+               LOG_ERROR("Command takes 1 to %d parameters", RISCV_NUM_MEM_ACCESS_METHODS);
+               return ERROR_COMMAND_SYNTAX_ERROR;
+       }
+
+       /* Check argument validity */
+       for (unsigned int i = 0; i < CMD_ARGC; i++) {
+               if (strcmp("progbuf", CMD_ARGV[i]) == 0) {
+                       progbuf_cnt++;
+               } else if (strcmp("sysbus", CMD_ARGV[i]) == 0) {
+                       sysbus_cnt++;
+               } else if (strcmp("abstract", CMD_ARGV[i]) == 0) {
+                       abstract_cnt++;
+               } else {
+                       LOG_ERROR("Unknown argument '%s'. "
+                               "Must be one of: 'progbuf', 'sysbus' or 'abstract'.", CMD_ARGV[i]);
+                       return ERROR_COMMAND_SYNTAX_ERROR;
+               }
+       }
+       if (progbuf_cnt > 1 || sysbus_cnt > 1 || abstract_cnt > 1) {
+               LOG_ERROR("Syntax error - duplicate arguments to `riscv set_mem_access`.");
                return ERROR_COMMAND_SYNTAX_ERROR;
        }
-       COMMAND_PARSE_ON_OFF(CMD_ARGV[0], riscv_prefer_sba);
+
+       /* Args are valid, store them */
+       for (unsigned int i = 0; i < RISCV_NUM_MEM_ACCESS_METHODS; i++)
+               r->mem_access_methods[i] = RISCV_MEM_ACCESS_UNSPECIFIED;
+       for (unsigned int i = 0; i < CMD_ARGC; i++) {
+               if (strcmp("progbuf", CMD_ARGV[i]) == 0)
+                       r->mem_access_methods[i] = RISCV_MEM_ACCESS_PROGBUF;
+               else if (strcmp("sysbus", CMD_ARGV[i]) == 0)
+                       r->mem_access_methods[i] = RISCV_MEM_ACCESS_SYSBUS;
+               else if (strcmp("abstract", CMD_ARGV[i]) == 0)
+                       r->mem_access_methods[i] = RISCV_MEM_ACCESS_ABSTRACT;
+       }
+
+       /* Reset warning flags */
+       r->mem_access_progbuf_warn = true;
+       r->mem_access_sysbus_warn = true;
+       r->mem_access_abstract_warn = true;
+
        return ERROR_OK;
 }
 
@@ -2171,109 +2450,180 @@ COMMAND_HANDLER(riscv_set_enable_virtual)
        return ERROR_OK;
 }
 
-void parse_error(const char *string, char c, unsigned position)
+int parse_ranges(struct list_head *ranges, const char *tcl_arg, const char *reg_type, unsigned int max_val)
 {
-       char buf[position+2];
-       for (unsigned i = 0; i < position; i++)
-               buf[i] = ' ';
-       buf[position] = '^';
-       buf[position + 1] = 0;
-
-       LOG_ERROR("Parse error at character %c in:", c);
-       LOG_ERROR("%s", string);
-       LOG_ERROR("%s", buf);
-}
+       char *args = strdup(tcl_arg);
+       if (!args)
+               return ERROR_FAIL;
 
-int parse_ranges(range_t **ranges, const char **argv)
-{
-       for (unsigned pass = 0; pass < 2; pass++) {
-               unsigned range = 0;
+       /* For backward compatibility, allow multiple parameters within one TCL argument, separated by ',' */
+       char *arg = strtok(args, ",");
+       while (arg) {
                unsigned low = 0;
-               bool parse_low = true;
                unsigned high = 0;
-               for (unsigned i = 0; i == 0 || argv[0][i-1]; i++) {
-                       char c = argv[0][i];
-                       if (isspace(c)) {
-                               /* Ignore whitespace. */
-                               continue;
+               char *name = NULL;
+
+               char *dash = strchr(arg, '-');
+               char *equals = strchr(arg, '=');
+               unsigned int pos;
+
+               if (!dash && !equals) {
+                       /* Expecting single register number. */
+                       if (sscanf(arg, "%u%n", &low, &pos) != 1 || pos != strlen(arg)) {
+                               LOG_ERROR("Failed to parse single register number from '%s'.", arg);
+                               free(args);
+                               return ERROR_COMMAND_SYNTAX_ERROR;
+                       }
+               } else if (dash && !equals) {
+                       /* Expecting register range - two numbers separated by a dash: ##-## */
+                       *dash = 0;
+                       dash++;
+                       if (sscanf(arg, "%u%n", &low, &pos) != 1 || pos != strlen(arg)) {
+                               LOG_ERROR("Failed to parse single register number from '%s'.", arg);
+                               free(args);
+                               return ERROR_COMMAND_SYNTAX_ERROR;
+                       }
+                       if (sscanf(dash, "%u%n", &high, &pos) != 1 || pos != strlen(dash)) {
+                               LOG_ERROR("Failed to parse single register number from '%s'.", dash);
+                               free(args);
+                               return ERROR_COMMAND_SYNTAX_ERROR;
+                       }
+                       if (high < low) {
+                               LOG_ERROR("Incorrect range encountered [%u, %u].", low, high);
+                               free(args);
+                               return ERROR_FAIL;
+                       }
+               } else if (!dash && equals) {
+                       /* Expecting single register number with textual name specified: ##=name */
+                       *equals = 0;
+                       equals++;
+                       if (sscanf(arg, "%u%n", &low, &pos) != 1 || pos != strlen(arg)) {
+                               LOG_ERROR("Failed to parse single register number from '%s'.", arg);
+                               free(args);
+                               return ERROR_COMMAND_SYNTAX_ERROR;
                        }
 
-                       if (parse_low) {
-                               if (isdigit(c)) {
-                                       low *= 10;
-                                       low += c - '0';
-                               } else if (c == '-') {
-                                       parse_low = false;
-                               } else if (c == ',' || c == 0) {
-                                       if (pass == 1) {
-                                               (*ranges)[range].low = low;
-                                               (*ranges)[range].high = low;
-                                       }
-                                       low = 0;
-                                       range++;
-                               } else {
-                                       parse_error(argv[0], c, i);
-                                       return ERROR_COMMAND_SYNTAX_ERROR;
-                               }
+                       name = calloc(1, strlen(equals) + strlen(reg_type) + 2);
+                       if (!name) {
+                               LOG_ERROR("Failed to allocate register name.");
+                               free(args);
+                               return ERROR_FAIL;
+                       }
 
-                       } else {
-                               if (isdigit(c)) {
-                                       high *= 10;
-                                       high += c - '0';
-                               } else if (c == ',' || c == 0) {
-                                       parse_low = true;
-                                       if (pass == 1) {
-                                               (*ranges)[range].low = low;
-                                               (*ranges)[range].high = high;
-                                       }
-                                       low = 0;
-                                       high = 0;
-                                       range++;
-                               } else {
-                                       parse_error(argv[0], c, i);
-                                       return ERROR_COMMAND_SYNTAX_ERROR;
-                               }
+                       /* Register prefix: "csr_" or "custom_" */
+                       strcpy(name, reg_type);
+                       name[strlen(reg_type)] = '_';
+
+                       if (sscanf(equals, "%[_a-zA-Z0-9]%n", name + strlen(reg_type) + 1, &pos) != 1 || pos != strlen(equals)) {
+                               LOG_ERROR("Failed to parse register name from '%s'.", equals);
+                               free(args);
+                               free(name);
+                               return ERROR_COMMAND_SYNTAX_ERROR;
                        }
+               } else {
+                       LOG_ERROR("Invalid argument '%s'.", arg);
+                       free(args);
+                       return ERROR_COMMAND_SYNTAX_ERROR;
+               }
+
+               high = high > low ? high : low;
+
+               if (high > max_val) {
+                       LOG_ERROR("Cannot expose %s register number %u, maximum allowed value is %u.", reg_type, high, max_val);
+                       free(name);
+                       free(args);
+                       return ERROR_FAIL;
                }
 
-               if (pass == 0) {
-                       free(*ranges);
-                       *ranges = calloc(range + 2, sizeof(range_t));
-                       if (!*ranges)
+               /* Check for overlap, name uniqueness. */
+               range_list_t *entry;
+               list_for_each_entry(entry, ranges, list) {
+                       if ((entry->low <= high) && (low <= entry->high)) {
+                               if (low == high)
+                                       LOG_WARNING("Duplicate %s register number - "
+                                                       "Register %u has already been exposed previously", reg_type, low);
+                               else
+                                       LOG_WARNING("Overlapping register ranges - Register range starting from %u overlaps "
+                                                       "with already exposed register/range at %u.", low, entry->low);
+                       }
+
+                       if (entry->name && name && (strcasecmp(entry->name, name) == 0)) {
+                               LOG_ERROR("Duplicate register name \"%s\" found.", name);
+                               free(name);
+                               free(args);
                                return ERROR_FAIL;
-               } else {
-                       (*ranges)[range].low = 1;
-                       (*ranges)[range].high = 0;
+                       }
                }
+
+               range_list_t *range = calloc(1, sizeof(range_list_t));
+               if (!range) {
+                       LOG_ERROR("Failed to allocate range list.");
+                       free(name);
+                       free(args);
+                       return ERROR_FAIL;
+               }
+
+               range->low = low;
+               range->high = high;
+               range->name = name;
+               list_add(&range->list, ranges);
+
+               arg = strtok(NULL, ",");
        }
 
+       free(args);
        return ERROR_OK;
 }
 
 COMMAND_HANDLER(riscv_set_expose_csrs)
 {
-       if (CMD_ARGC != 1) {
-               LOG_ERROR("Command takes exactly 1 parameter");
+       if (CMD_ARGC == 0) {
+               LOG_ERROR("Command expects parameters");
                return ERROR_COMMAND_SYNTAX_ERROR;
        }
 
-       return parse_ranges(&expose_csr, CMD_ARGV);
+       struct target *target = get_current_target(CMD_CTX);
+       RISCV_INFO(info);
+       int ret = ERROR_OK;
+
+       for (unsigned int i = 0; i < CMD_ARGC; i++) {
+               ret = parse_ranges(&info->expose_csr, CMD_ARGV[i], "csr", 0xfff);
+               if (ret != ERROR_OK)
+                       break;
+       }
+
+       return ret;
 }
 
 COMMAND_HANDLER(riscv_set_expose_custom)
 {
-       if (CMD_ARGC != 1) {
-               LOG_ERROR("Command takes exactly 1 parameter");
+       if (CMD_ARGC == 0) {
+               LOG_ERROR("Command expects parameters");
                return ERROR_COMMAND_SYNTAX_ERROR;
        }
 
-       return parse_ranges(&expose_custom, CMD_ARGV);
+       struct target *target = get_current_target(CMD_CTX);
+       RISCV_INFO(info);
+       int ret = ERROR_OK;
+
+       for (unsigned int i = 0; i < CMD_ARGC; i++) {
+               ret = parse_ranges(&info->expose_custom, CMD_ARGV[i], "custom", 0x3fff);
+               if (ret != ERROR_OK)
+                       break;
+       }
+
+       return ret;
 }
 
 COMMAND_HANDLER(riscv_authdata_read)
 {
-       if (CMD_ARGC != 0) {
-               LOG_ERROR("Command takes no parameters");
+       unsigned int index = 0;
+       if (CMD_ARGC == 0) {
+               /* nop */
+       } else if (CMD_ARGC == 1) {
+               COMMAND_PARSE_NUMBER(uint, CMD_ARGV[0], index);
+       } else {
+               LOG_ERROR("Command takes at most one parameter");
                return ERROR_COMMAND_SYNTAX_ERROR;
        }
 
@@ -2291,9 +2641,9 @@ COMMAND_HANDLER(riscv_authdata_read)
 
        if (r->authdata_read) {
                uint32_t value;
-               if (r->authdata_read(target, &value) != ERROR_OK)
+               if (r->authdata_read(target, &value, index) != ERROR_OK)
                        return ERROR_FAIL;
-               command_print(CMD, "0x%" PRIx32, value);
+               command_print_sameline(CMD, "0x%08" PRIx32, value);
                return ERROR_OK;
        } else {
                LOG_ERROR("authdata_read is not implemented for this target.");
@@ -2303,19 +2653,26 @@ COMMAND_HANDLER(riscv_authdata_read)
 
 COMMAND_HANDLER(riscv_authdata_write)
 {
-       if (CMD_ARGC != 1) {
-               LOG_ERROR("Command takes exactly 1 argument");
+       uint32_t value;
+       unsigned int index = 0;
+
+       if (CMD_ARGC == 0) {
+               /* nop */
+       } else if (CMD_ARGC == 1) {
+               COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], value);
+       } else if (CMD_ARGC == 2) {
+               COMMAND_PARSE_NUMBER(uint, CMD_ARGV[0], index);
+               COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], value);
+       } else {
+               LOG_ERROR("Command takes at most 2 arguments");
                return ERROR_COMMAND_SYNTAX_ERROR;
        }
 
        struct target *target = get_current_target(CMD_CTX);
        RISCV_INFO(r);
 
-       uint32_t value;
-       COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], value);
-
        if (r->authdata_write) {
-               return r->authdata_write(target, value);
+               return r->authdata_write(target, value, index);
        } else {
                LOG_ERROR("authdata_write is not implemented for this target.");
                return ERROR_FAIL;
@@ -2531,13 +2888,40 @@ COMMAND_HANDLER(riscv_set_ebreaku)
        return ERROR_OK;
 }
 
+COMMAND_HELPER(riscv_print_info_line, const char *section, const char *key,
+                          unsigned int value)
+{
+       char full_key[80];
+       snprintf(full_key, sizeof(full_key), "%s.%s", section, key);
+       command_print(CMD, "%-21s %3d", full_key, value);
+       return 0;
+}
+
+COMMAND_HANDLER(handle_info)
+{
+       struct target *target = get_current_target(CMD_CTX);
+       RISCV_INFO(r);
+
+       /* This output format can be fed directly into TCL's "array set". */
+
+       riscv_print_info_line(CMD, "hart", "xlen", riscv_xlen(target));
+       riscv_enumerate_triggers(target);
+       riscv_print_info_line(CMD, "hart", "trigger_count",
+                                                 r->trigger_count);
+
+       if (r->print_info)
+               return CALL_COMMAND_HANDLER(r->print_info, target);
+
+       return 0;
+}
+
 static const struct command_registration riscv_exec_command_handlers[] = {
        {
-               .name = "test_compliance",
-               .handler = riscv_test_compliance,
-               .usage = "",
+               .name = "info",
+               .handler = handle_info,
                .mode = COMMAND_EXEC,
-               .help = "Runs a basic compliance test suite against the RISC-V Debug Spec."
+               .usage = "",
+               .help = "Displays some information OpenOCD detected about the target."
        },
        {
                .name = "set_command_timeout_sec",
@@ -2561,6 +2945,14 @@ static const struct command_registration riscv_exec_command_handlers[] = {
                .help = "When on, prefer to use System Bus Access to access memory. "
                        "When off (default), prefer to use the Program Buffer to access memory."
        },
+       {
+               .name = "set_mem_access",
+               .handler = riscv_set_mem_access,
+               .mode = COMMAND_ANY,
+               .usage = "method1 [method2] [method3]",
+               .help = "Set which memory access methods shall be used and in which order "
+                       "of priority. Method can be one of: 'progbuf', 'sysbus' or 'abstract'."
+       },
        {
                .name = "set_enable_virtual",
                .handler = riscv_set_enable_virtual,
@@ -2573,8 +2965,8 @@ static const struct command_registration riscv_exec_command_handlers[] = {
        {
                .name = "expose_csrs",
                .handler = riscv_set_expose_csrs,
-               .mode = COMMAND_ANY,
-               .usage = "n0[-m0][,n1[-m1]]...",
+               .mode = COMMAND_CONFIG,
+               .usage = "n0[-m0|=name0][,n1[-m1|=name1]]...",
                .help = "Configure a list of inclusive ranges for CSRs to expose in "
                                "addition to the standard ones. This must be executed before "
                                "`init`."
@@ -2582,8 +2974,8 @@ static const struct command_registration riscv_exec_command_handlers[] = {
        {
                .name = "expose_custom",
                .handler = riscv_set_expose_custom,
-               .mode = COMMAND_ANY,
-               .usage = "n0[-m0][,n1[-m1]]...",
+               .mode = COMMAND_CONFIG,
+               .usage = "n0[-m0|=name0][,n1[-m1|=name1]]...",
                .help = "Configure a list of inclusive ranges for custom registers to "
                        "expose. custom0 is accessed as abstract register number 0xc000, "
                        "etc. This must be executed before `init`."
@@ -2591,16 +2983,18 @@ static const struct command_registration riscv_exec_command_handlers[] = {
        {
                .name = "authdata_read",
                .handler = riscv_authdata_read,
-               .usage = "",
+               .usage = "[index]",
                .mode = COMMAND_ANY,
-               .help = "Return the 32-bit value read from authdata."
+               .help = "Return the 32-bit value read from authdata or authdata0 "
+                               "(index=0), or authdata1 (index=1)."
        },
        {
                .name = "authdata_write",
                .handler = riscv_authdata_write,
                .mode = COMMAND_ANY,
-               .usage = "value",
-               .help = "Write the 32-bit value to authdata."
+               .usage = "[index] value",
+               .help = "Write the 32-bit value to authdata or authdata0 (index=0), "
+                               "or authdata1 (index=1)."
        },
        {
                .name = "dmi_read",
@@ -2739,9 +3133,18 @@ static unsigned riscv_xlen_nonconst(struct target *target)
        return riscv_xlen(target);
 }
 
+static unsigned int riscv_data_bits(struct target *target)
+{
+       RISCV_INFO(r);
+       if (r->data_bits)
+               return r->data_bits(target);
+       return riscv_xlen(target);
+}
+
 struct target_type riscv_target = {
        .name = "riscv",
 
+       .target_create = riscv_create_target,
        .init_target = riscv_init_target,
        .deinit_target = riscv_deinit_target,
        .examine = riscv_examine,
@@ -2766,6 +3169,7 @@ struct target_type riscv_target = {
        .mmu = riscv_mmu,
        .virt2phys = riscv_virt2phys,
 
+       .get_gdb_arch = riscv_get_gdb_arch,
        .get_gdb_reg_list = riscv_get_gdb_reg_list,
        .get_gdb_reg_list_noread = riscv_get_gdb_reg_list_noread,
 
@@ -2783,6 +3187,7 @@ struct target_type riscv_target = {
        .commands = riscv_command_handlers,
 
        .address_bits = riscv_xlen_nonconst,
+       .data_bits = riscv_data_bits
 };
 
 /*** RISC-V Interface ***/
@@ -2793,49 +3198,37 @@ void riscv_info_init(struct target *target, riscv_info_t *r)
        r->dtm_version = 1;
        r->registers_initialized = false;
        r->current_hartid = target->coreid;
+       r->version_specific = NULL;
 
        memset(r->trigger_unique_id, 0xff, sizeof(r->trigger_unique_id));
 
-       for (size_t h = 0; h < RISCV_MAX_HARTS; ++h)
-               r->xlen[h] = -1;
+       r->xlen = -1;
+
+       r->mem_access_methods[0] = RISCV_MEM_ACCESS_PROGBUF;
+       r->mem_access_methods[1] = RISCV_MEM_ACCESS_SYSBUS;
+       r->mem_access_methods[2] = RISCV_MEM_ACCESS_ABSTRACT;
+
+       r->mem_access_progbuf_warn = true;
+       r->mem_access_sysbus_warn = true;
+       r->mem_access_abstract_warn = true;
+
+       INIT_LIST_HEAD(&r->expose_csr);
+       INIT_LIST_HEAD(&r->expose_custom);
 }
 
 static int riscv_resume_go_all_harts(struct target *target)
 {
        RISCV_INFO(r);
 
-       /* Dummy variables to make mingw32-gcc happy. */
-       int first = 0;
-       int last = 1;
-       int step = 1;
-       switch (resume_order) {
-               case RO_NORMAL:
-                       first = 0;
-                       last = riscv_count_harts(target) - 1;
-                       step = 1;
-                       break;
-               case RO_REVERSED:
-                       first = riscv_count_harts(target) - 1;
-                       last = 0;
-                       step = -1;
-                       break;
-               default:
-                       assert(0);
-       }
-
-       for (int i = first; i != last + step; i += step) {
-               if (!riscv_hart_enabled(target, i))
-                       continue;
-
-               LOG_DEBUG("resuming hart %d", i);
-               if (riscv_set_current_hartid(target, i) != ERROR_OK)
+       LOG_DEBUG("[%s] resuming hart", target_name(target));
+       if (riscv_select_current_hart(target) != ERROR_OK)
+               return ERROR_FAIL;
+       if (riscv_is_halted(target)) {
+               if (r->resume_go(target) != ERROR_OK)
                        return ERROR_FAIL;
-               if (riscv_is_halted(target)) {
-                       if (r->resume_go(target) != ERROR_OK)
-                               return ERROR_FAIL;
-               } else {
-                       LOG_DEBUG("  hart %d requested resume, but was already resumed", i);
-               }
+       } else {
+               LOG_DEBUG("[%s] hart requested resume, but was already resumed",
+                               target_name(target));
        }
 
        riscv_invalidate_register_cache(target);
@@ -2845,17 +3238,9 @@ static int riscv_resume_go_all_harts(struct target *target)
 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_DEBUG("GDB has asked me to step \"any\" thread, so I'm stepping hart 0.");
-                       hartid = 0;
-               }
-       }
-       if (riscv_set_current_hartid(target, hartid) != ERROR_OK)
+       if (riscv_select_current_hart(target) != ERROR_OK)
                return ERROR_FAIL;
-       LOG_DEBUG("stepping hart %d", hartid);
+       LOG_DEBUG("[%s] stepping", target_name(target));
 
        if (!riscv_is_halted(target)) {
                LOG_ERROR("Hart isn't halted before single step!");
@@ -2874,7 +3259,7 @@ int riscv_step_rtos_hart(struct target *target)
        return ERROR_OK;
 }
 
-bool riscv_supports_extension(struct target *target, int hartid, char letter)
+bool riscv_supports_extension(struct target *target, char letter)
 {
        RISCV_INFO(r);
        unsigned num;
@@ -2884,24 +3269,13 @@ bool riscv_supports_extension(struct target *target, int hartid, char letter)
                num = letter - 'A';
        else
                return false;
-       return r->misa[hartid] & (1 << num);
+       return r->misa & BIT(num);
 }
 
 unsigned 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 false;
+       return r->xlen;
 }
 
 int riscv_set_current_hartid(struct target *target, int hartid)
@@ -2912,16 +3286,10 @@ int riscv_set_current_hartid(struct target *target, int hartid)
 
        int previous_hartid = riscv_current_hartid(target);
        r->current_hartid = hartid;
-       assert(riscv_hart_enabled(target, hartid));
        LOG_DEBUG("setting hartid to %d, was %d", hartid, previous_hartid);
        if (r->select_current_hart(target) != ERROR_OK)
                return ERROR_FAIL;
 
-       /* This might get called during init, in which case we shouldn't be
-        * setting up the register cache. */
-       if (target_was_examined(target) && riscv_rtos_enabled(target))
-               riscv_invalidate_register_cache(target);
-
        return ERROR_OK;
 }
 
@@ -2945,34 +3313,16 @@ int riscv_current_hartid(const struct target *target)
        return r->current_hartid;
 }
 
-void riscv_set_all_rtos_harts(struct target *target)
-{
-       RISCV_INFO(r);
-       r->rtos_hartid = -1;
-}
-
-void riscv_set_rtos_hartid(struct target *target, int hartid)
-{
-       LOG_DEBUG("setting RTOS hartid %d", hartid);
-       RISCV_INFO(r);
-       r->rtos_hartid = hartid;
-}
-
 int riscv_count_harts(struct target *target)
 {
-       if (target == NULL)
+       if (!target)
                return 1;
        RISCV_INFO(r);
-       if (r == NULL || r->hart_count == NULL)
+       if (!r || !r->hart_count)
                return 1;
        return r->hart_count(target);
 }
 
-bool riscv_has_register(struct target *target, int hartid, int regid)
-{
-       return 1;
-}
-
 /**
  * If write is true:
  *   return true iff we are guaranteed that the register will contain exactly
@@ -2989,7 +3339,7 @@ static bool gdb_regno_cacheable(enum gdb_regno regno, bool write)
                        (regno >= GDB_REGNO_V0 && regno <= GDB_REGNO_V31))
                return true;
 
-       /* Most CSRs won't change value on us, but we can't assume it about rbitrary
+       /* Most CSRs won't change value on us, but we can't assume it about arbitrary
         * CSRs. */
        switch (regno) {
                case GDB_REGNO_DPC:
@@ -3026,75 +3376,67 @@ static bool gdb_regno_cacheable(enum gdb_regno regno, bool write)
  * This function is called when the debug user wants to change the value of a
  * register. The new value may be cached, and may not be written until the hart
  * is resumed. */
-int 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);
-}
-
-int riscv_set_register_on_hart(struct target *target, int hartid,
-               enum gdb_regno regid, uint64_t value)
+int riscv_set_register(struct target *target, enum gdb_regno regid, riscv_reg_t value)
 {
        RISCV_INFO(r);
-       LOG_DEBUG("{%d} %s <- %" PRIx64, hartid, gdb_regno_name(regid), value);
+       LOG_DEBUG("[%s] %s <- %" PRIx64, target_name(target), gdb_regno_name(regid), value);
        assert(r->set_register);
 
+       keep_alive();
+
        /* TODO: Hack to deal with gdb that thinks these registers still exist. */
        if (regid > GDB_REGNO_XPR15 && regid <= GDB_REGNO_XPR31 && value == 0 &&
-                       riscv_supports_extension(target, hartid, 'E'))
+                       riscv_supports_extension(target, 'E'))
                return ERROR_OK;
 
        struct reg *reg = &target->reg_cache->reg_list[regid];
        buf_set_u64(reg->value, 0, reg->size, value);
 
-       int result = r->set_register(target, hartid, regid, value);
+       int result = r->set_register(target, regid, value);
        if (result == ERROR_OK)
                reg->valid = gdb_regno_cacheable(regid, true);
        else
                reg->valid = false;
-       LOG_DEBUG("[%s]{%d} wrote 0x%" PRIx64 " to %s valid=%d",
-                         target_name(target), hartid, value, reg->name, reg->valid);
+       LOG_DEBUG("[%s] wrote 0x%" PRIx64 " to %s valid=%d",
+                         target_name(target), value, reg->name, reg->valid);
        return result;
 }
 
 int riscv_get_register(struct target *target, riscv_reg_t *value,
-               enum gdb_regno r)
-{
-       return riscv_get_register_on_hart(target, value,
-                       riscv_current_hartid(target), r);
-}
-
-int riscv_get_register_on_hart(struct target *target, riscv_reg_t *value,
-               int hartid, enum gdb_regno regid)
+               enum gdb_regno regid)
 {
        RISCV_INFO(r);
 
+       keep_alive();
+
        struct reg *reg = &target->reg_cache->reg_list[regid];
        if (!reg->exist) {
-               LOG_DEBUG("[%s]{%d} %s does not exist.",
-                                 target_name(target), hartid, gdb_regno_name(regid));
+               LOG_DEBUG("[%s] %s does not exist.",
+                                 target_name(target), gdb_regno_name(regid));
                return ERROR_FAIL;
        }
 
-       if (reg && reg->valid && hartid == riscv_current_hartid(target)) {
+       if (reg && reg->valid) {
                *value = buf_get_u64(reg->value, 0, reg->size);
-               LOG_DEBUG("{%d} %s: %" PRIx64 " (cached)", hartid,
+               LOG_DEBUG("[%s] %s: %" PRIx64 " (cached)", target_name(target),
                                  gdb_regno_name(regid), *value);
                return ERROR_OK;
        }
 
        /* TODO: Hack to deal with gdb that thinks these registers still exist. */
        if (regid > GDB_REGNO_XPR15 && regid <= GDB_REGNO_XPR31 &&
-                       riscv_supports_extension(target, hartid, 'E')) {
+                       riscv_supports_extension(target, 'E')) {
                *value = 0;
                return ERROR_OK;
        }
 
-       int result = r->get_register(target, value, hartid, regid);
+       int result = r->get_register(target, value, regid);
 
        if (result == ERROR_OK)
                reg->valid = gdb_regno_cacheable(regid, false);
 
-       LOG_DEBUG("{%d} %s: %" PRIx64, hartid, gdb_regno_name(regid), *value);
+       LOG_DEBUG("[%s] %s: %" PRIx64, target_name(target),
+                       gdb_regno_name(regid), *value);
        return result;
 }
 
@@ -3120,7 +3462,7 @@ enum riscv_halt_reason riscv_halt_reason(struct target *target, int hartid)
 size_t riscv_debug_buffer_size(struct target *target)
 {
        RISCV_INFO(r);
-       return r->debug_buffer_size[riscv_current_hartid(target)];
+       return r->debug_buffer_size;
 }
 
 int riscv_write_debug_buffer(struct target *target, int index, riscv_insn_t insn)
@@ -3166,15 +3508,6 @@ int riscv_dmi_write_u64_bits(struct target *target)
        return r->dmi_write_u64_bits(target);
 }
 
-bool riscv_hart_enabled(struct target *target, int hartid)
-{
-       /* FIXME: Add a hart mask to the RTOS. */
-       if (riscv_rtos_enabled(target))
-               return hartid < riscv_count_harts(target);
-
-       return hartid == target->coreid;
-}
-
 /**
  * Count triggers, and initialize trigger_count for each hart.
  * trigger_count is initialized even if this function fails to discover
@@ -3191,58 +3524,57 @@ int riscv_enumerate_triggers(struct target *target)
 
        r->triggers_enumerated = true;  /* At the very least we tried. */
 
-       for (int hartid = 0; hartid < riscv_count_harts(target); ++hartid) {
-               if (!riscv_hart_enabled(target, hartid))
-                       continue;
+       riscv_reg_t tselect;
+       int result = riscv_get_register(target, &tselect, GDB_REGNO_TSELECT);
+       /* If tselect is not readable, the trigger module is likely not
+               * implemented. There are no triggers to enumerate then and no error
+               * should be thrown. */
+       if (result != ERROR_OK) {
+               LOG_DEBUG("[%s] Cannot access tselect register. "
+                               "Assuming that triggers are not implemented.", target_name(target));
+               r->trigger_count = 0;
+               return ERROR_OK;
+       }
 
-               riscv_reg_t tselect;
-               int result = riscv_get_register_on_hart(target, &tselect, hartid,
-                               GDB_REGNO_TSELECT);
+       for (unsigned int t = 0; t < RISCV_MAX_TRIGGERS; ++t) {
+               r->trigger_count = t;
+
+               /* If we can't write tselect, then this hart does not support triggers. */
+               if (riscv_set_register(target, GDB_REGNO_TSELECT, t) != ERROR_OK)
+                       break;
+               uint64_t tselect_rb;
+               result = riscv_get_register(target, &tselect_rb, GDB_REGNO_TSELECT);
+               if (result != ERROR_OK)
+                       return result;
+               /* Mask off the top bit, which is used as tdrmode in old
+                       * implementations. */
+               tselect_rb &= ~(1ULL << (riscv_xlen(target) - 1));
+               if (tselect_rb != t)
+                       break;
+               uint64_t tdata1;
+               result = riscv_get_register(target, &tdata1, GDB_REGNO_TDATA1);
                if (result != ERROR_OK)
                        return result;
 
-               for (unsigned t = 0; t < RISCV_MAX_TRIGGERS; ++t) {
-                       r->trigger_count[hartid] = t;
-
-                       /* If we can't write tselect, then this hart does not support triggers. */
-                       if (riscv_set_register_on_hart(target, hartid, GDB_REGNO_TSELECT, t) != ERROR_OK)
-                               break;
-                       uint64_t tselect_rb;
-                       result = riscv_get_register_on_hart(target, &tselect_rb, hartid,
-                                       GDB_REGNO_TSELECT);
-                       if (result != ERROR_OK)
-                               return result;
-                       /* Mask off the top bit, which is used as tdrmode in old
-                        * implementations. */
-                       tselect_rb &= ~(1ULL << (riscv_xlen(target)-1));
-                       if (tselect_rb != t)
+               int type = get_field(tdata1, MCONTROL_TYPE(riscv_xlen(target)));
+               if (type == 0)
+                       break;
+               switch (type) {
+                       case 1:
+                               /* On these older cores we don't support software using
+                                       * triggers. */
+                               riscv_set_register(target, GDB_REGNO_TDATA1, 0);
                                break;
-                       uint64_t tdata1;
-                       result = riscv_get_register_on_hart(target, &tdata1, hartid,
-                                       GDB_REGNO_TDATA1);
-                       if (result != ERROR_OK)
-                               return result;
-
-                       int type = get_field(tdata1, MCONTROL_TYPE(riscv_xlen(target)));
-                       if (type == 0)
+                       case 2:
+                               if (tdata1 & MCONTROL_DMODE(riscv_xlen(target)))
+                                       riscv_set_register(target, GDB_REGNO_TDATA1, 0);
                                break;
-                       switch (type) {
-                               case 1:
-                                       /* On these older cores we don't support software using
-                                        * triggers. */
-                                       riscv_set_register_on_hart(target, hartid, GDB_REGNO_TDATA1, 0);
-                                       break;
-                               case 2:
-                                       if (tdata1 & MCONTROL_DMODE(riscv_xlen(target)))
-                                               riscv_set_register_on_hart(target, hartid, GDB_REGNO_TDATA1, 0);
-                                       break;
-                       }
                }
+       }
 
-               riscv_set_register_on_hart(target, hartid, GDB_REGNO_TSELECT, tselect);
+       riscv_set_register(target, GDB_REGNO_TSELECT, tselect);
 
-               LOG_INFO("[%d] Found %d triggers", hartid, r->trigger_count[hartid]);
-       }
+       LOG_INFO("[%s] Found %d triggers", target_name(target), r->trigger_count);
 
        return ERROR_OK;
 }
@@ -3453,8 +3785,8 @@ static int register_get(struct reg *reg)
        }
        reg->valid = gdb_regno_cacheable(reg->number, false);
        char *str = buf_to_hex_str(reg->value, reg->size);
-       LOG_DEBUG("[%d]{%d} read 0x%s from %s (valid=%d)", target->coreid,
-                       riscv_current_hartid(target), str, reg->name, reg->valid);
+       LOG_DEBUG("[%s] read 0x%s from %s (valid=%d)", target_name(target),
+                       str, reg->name, reg->valid);
        free(str);
        return ERROR_OK;
 }
@@ -3466,10 +3798,15 @@ static int register_set(struct reg *reg, uint8_t *buf)
        RISCV_INFO(r);
 
        char *str = buf_to_hex_str(buf, reg->size);
-       LOG_DEBUG("[%d]{%d} write 0x%s to %s (valid=%d)", target->coreid,
-                       riscv_current_hartid(target), str, reg->name, reg->valid);
+       LOG_DEBUG("[%s] write 0x%s to %s (valid=%d)", target_name(target),
+                       str, reg->name, reg->valid);
        free(str);
 
+       /* Exit early for writing x0, which on the hardware would be ignored, and we
+        * don't want to update our cache. */
+       if (reg->number == GDB_REGNO_ZERO)
+               return ERROR_OK;
+
        memcpy(reg->value, buf, DIV_ROUND_UP(reg->size, 8));
        reg->valid = gdb_regno_cacheable(reg->number, true);
 
@@ -3529,13 +3866,10 @@ int riscv_init_registers(struct target *target)
        target->reg_cache->name = "RISC-V Registers";
        target->reg_cache->num_regs = GDB_REGNO_COUNT;
 
-       if (expose_custom) {
-               for (unsigned i = 0; expose_custom[i].low <= expose_custom[i].high; i++) {
-                       for (unsigned number = expose_custom[i].low;
-                                       number <= expose_custom[i].high;
-                                       number++)
-                               target->reg_cache->num_regs++;
-               }
+       if (!list_empty(&info->expose_custom)) {
+               range_list_t *entry;
+               list_for_each_entry(entry, &info->expose_custom, list)
+                       target->reg_cache->num_regs += entry->high - entry->low + 1;
        }
 
        LOG_DEBUG("create register cache for %d registers",
@@ -3554,8 +3888,6 @@ int riscv_init_registers(struct target *target)
                return ERROR_FAIL;
        char *reg_name = info->reg_names;
 
-       int hartid = riscv_current_hartid(target);
-
        static struct reg_feature feature_cpu = {
                .name = "org.gnu.gdb.riscv.cpu"
        };
@@ -3613,35 +3945,35 @@ int riscv_init_registers(struct target *target)
         */
 
        info->vector_uint8.type = &type_uint8;
-       info->vector_uint8.count = info->vlenb[hartid];
+       info->vector_uint8.count = info->vlenb;
        info->type_uint8_vector.type = REG_TYPE_ARCH_DEFINED;
        info->type_uint8_vector.id = "bytes";
        info->type_uint8_vector.type_class = REG_TYPE_CLASS_VECTOR;
        info->type_uint8_vector.reg_type_vector = &info->vector_uint8;
 
        info->vector_uint16.type = &type_uint16;
-       info->vector_uint16.count = info->vlenb[hartid] / 2;
+       info->vector_uint16.count = info->vlenb / 2;
        info->type_uint16_vector.type = REG_TYPE_ARCH_DEFINED;
        info->type_uint16_vector.id = "shorts";
        info->type_uint16_vector.type_class = REG_TYPE_CLASS_VECTOR;
        info->type_uint16_vector.reg_type_vector = &info->vector_uint16;
 
        info->vector_uint32.type = &type_uint32;
-       info->vector_uint32.count = info->vlenb[hartid] / 4;
+       info->vector_uint32.count = info->vlenb / 4;
        info->type_uint32_vector.type = REG_TYPE_ARCH_DEFINED;
        info->type_uint32_vector.id = "words";
        info->type_uint32_vector.type_class = REG_TYPE_CLASS_VECTOR;
        info->type_uint32_vector.reg_type_vector = &info->vector_uint32;
 
        info->vector_uint64.type = &type_uint64;
-       info->vector_uint64.count = info->vlenb[hartid] / 8;
+       info->vector_uint64.count = info->vlenb / 8;
        info->type_uint64_vector.type = REG_TYPE_ARCH_DEFINED;
        info->type_uint64_vector.id = "longs";
        info->type_uint64_vector.type_class = REG_TYPE_CLASS_VECTOR;
        info->type_uint64_vector.reg_type_vector = &info->vector_uint64;
 
        info->vector_uint128.type = &type_uint128;
-       info->vector_uint128.count = info->vlenb[hartid] / 16;
+       info->vector_uint128.count = info->vlenb / 16;
        info->type_uint128_vector.type = REG_TYPE_ARCH_DEFINED;
        info->type_uint128_vector.id = "quads";
        info->type_uint128_vector.type_class = REG_TYPE_CLASS_VECTOR;
@@ -3649,28 +3981,28 @@ int riscv_init_registers(struct target *target)
 
        info->vector_fields[0].name = "b";
        info->vector_fields[0].type = &info->type_uint8_vector;
-       if (info->vlenb[hartid] >= 2) {
+       if (info->vlenb >= 2) {
                info->vector_fields[0].next = info->vector_fields + 1;
                info->vector_fields[1].name = "s";
                info->vector_fields[1].type = &info->type_uint16_vector;
        } else {
                info->vector_fields[0].next = NULL;
        }
-       if (info->vlenb[hartid] >= 4) {
+       if (info->vlenb >= 4) {
                info->vector_fields[1].next = info->vector_fields + 2;
                info->vector_fields[2].name = "w";
                info->vector_fields[2].type = &info->type_uint32_vector;
        } else {
                info->vector_fields[1].next = NULL;
        }
-       if (info->vlenb[hartid] >= 8) {
+       if (info->vlenb >= 8) {
                info->vector_fields[2].next = info->vector_fields + 3;
                info->vector_fields[3].name = "l";
                info->vector_fields[3].type = &info->type_uint64_vector;
        } else {
                info->vector_fields[2].next = NULL;
        }
-       if (info->vlenb[hartid] >= 16) {
+       if (info->vlenb >= 16) {
                info->vector_fields[3].next = info->vector_fields + 4;
                info->vector_fields[4].name = "q";
                info->vector_fields[4].type = &info->type_uint128_vector;
@@ -3692,10 +4024,9 @@ int riscv_init_registers(struct target *target)
 #undef DECLARE_CSR
        };
        /* encoding.h does not contain the registers in sorted order. */
-       qsort(csr_info, DIM(csr_info), sizeof(*csr_info), cmp_csr_info);
+       qsort(csr_info, ARRAY_SIZE(csr_info), sizeof(*csr_info), cmp_csr_info);
        unsigned csr_info_index = 0;
 
-       unsigned custom_range_index = 0;
        int custom_within_range = 0;
 
        riscv_reg_info_t *shared_reg_info = calloc(1, sizeof(riscv_reg_info_t));
@@ -3722,7 +4053,7 @@ int riscv_init_registers(struct target *target)
                 * of other things to break in that case as well. */
                if (number <= GDB_REGNO_XPR31) {
                        r->exist = number <= GDB_REGNO_XPR15 ||
-                               !riscv_supports_extension(target, hartid, 'E');
+                               !riscv_supports_extension(target, 'E');
                        /* TODO: For now we fake that all GPRs exist because otherwise gdb
                         * doesn't work. */
                        r->exist = true;
@@ -3834,13 +4165,13 @@ int riscv_init_registers(struct target *target)
                        r->feature = &feature_cpu;
                } else if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) {
                        r->caller_save = true;
-                       if (riscv_supports_extension(target, hartid, 'D')) {
+                       if (riscv_supports_extension(target, 'D')) {
                                r->size = 64;
-                               if (riscv_supports_extension(target, hartid, 'F'))
+                               if (riscv_supports_extension(target, 'F'))
                                        r->reg_data_type = &type_ieee_single_double;
                                else
                                        r->reg_data_type = &type_ieee_double;
-                       } else if (riscv_supports_extension(target, hartid, 'F')) {
+                       } else if (riscv_supports_extension(target, 'F')) {
                                r->reg_data_type = &type_ieee_single;
                                r->size = 32;
                        } else {
@@ -3952,7 +4283,7 @@ int riscv_init_registers(struct target *target)
                        unsigned csr_number = number - GDB_REGNO_CSR0;
 
                        while (csr_info[csr_info_index].number < csr_number &&
-                                       csr_info_index < DIM(csr_info) - 1) {
+                                       csr_info_index < ARRAY_SIZE(csr_info) - 1) {
                                csr_info_index++;
                        }
                        if (csr_info[csr_info_index].number == csr_number) {
@@ -3971,7 +4302,7 @@ int riscv_init_registers(struct target *target)
                                case CSR_FFLAGS:
                                case CSR_FRM:
                                case CSR_FCSR:
-                                       r->exist = riscv_supports_extension(target, hartid, 'F');
+                                       r->exist = riscv_supports_extension(target, 'F');
                                        r->group = "float";
                                        r->feature = &feature_fpu;
                                        break;
@@ -3985,15 +4316,15 @@ int riscv_init_registers(struct target *target)
                                case CSR_SCAUSE:
                                case CSR_STVAL:
                                case CSR_SATP:
-                                       r->exist = riscv_supports_extension(target, hartid, 'S');
+                                       r->exist = riscv_supports_extension(target, 'S');
                                        break;
                                case CSR_MEDELEG:
                                case CSR_MIDELEG:
                                        /* "In systems with only M-mode, or with both M-mode and
                                         * U-mode but without U-mode trap support, the medeleg and
                                         * mideleg registers should not exist." */
-                                       r->exist = riscv_supports_extension(target, hartid, 'S') ||
-                                               riscv_supports_extension(target, hartid, 'N');
+                                       r->exist = riscv_supports_extension(target, 'S') ||
+                                               riscv_supports_extension(target, 'N');
                                        break;
 
                                case CSR_PMPCFG1:
@@ -4070,18 +4401,25 @@ int riscv_init_registers(struct target *target)
                                case CSR_VL:
                                case CSR_VTYPE:
                                case CSR_VLENB:
-                                       r->exist = riscv_supports_extension(target, hartid, 'V');
+                                       r->exist = riscv_supports_extension(target, 'V');
                                        break;
                        }
 
-                       if (!r->exist && expose_csr) {
-                               for (unsigned i = 0; expose_csr[i].low <= expose_csr[i].high; i++) {
-                                       if (csr_number >= expose_csr[i].low && csr_number <= expose_csr[i].high) {
-                                               LOG_INFO("Exposing additional CSR %d", csr_number);
+                       if (!r->exist && !list_empty(&info->expose_csr)) {
+                               range_list_t *entry;
+                               list_for_each_entry(entry, &info->expose_csr, list)
+                                       if ((entry->low <= csr_number) && (csr_number <= entry->high)) {
+                                               if (entry->name) {
+                                                       *reg_name = 0;
+                                                       r->name = entry->name;
+                                               }
+
+                                               LOG_DEBUG("Exposing additional CSR %d (name=%s)",
+                                                               csr_number, entry->name ? entry->name : reg_name);
+
                                                r->exist = true;
                                                break;
                                        }
-                               }
                        }
 
                } else if (number == GDB_REGNO_PRIV) {
@@ -4092,8 +4430,8 @@ int riscv_init_registers(struct target *target)
 
                } else if (number >= GDB_REGNO_V0 && number <= GDB_REGNO_V31) {
                        r->caller_save = false;
-                       r->exist = riscv_supports_extension(target, hartid, 'V') && info->vlenb[hartid];
-                       r->size = info->vlenb[hartid] * 8;
+                       r->exist = riscv_supports_extension(target, 'V') && info->vlenb;
+                       r->size = info->vlenb * 8;
                        sprintf(reg_name, "v%d", number - GDB_REGNO_V0);
                        r->group = "vector";
                        r->feature = &feature_vector;
@@ -4101,10 +4439,10 @@ int riscv_init_registers(struct target *target)
 
                } else if (number >= GDB_REGNO_COUNT) {
                        /* Custom registers. */
-                       assert(expose_custom);
+                       assert(!list_empty(&info->expose_custom));
+
+                       range_list_t *range = list_first_entry(&info->expose_custom, range_list_t, list);
 
-                       range_t *range = &expose_custom[custom_range_index];
-                       assert(range->low <= range->high);
                        unsigned custom_number = range->low + custom_within_range;
 
                        r->group = "custom";
@@ -4116,18 +4454,27 @@ int riscv_init_registers(struct target *target)
                        ((riscv_reg_info_t *) r->arch_info)->custom_number = custom_number;
                        sprintf(reg_name, "custom%d", custom_number);
 
+                       if (range->name) {
+                               *reg_name = 0;
+                               r->name = range->name;
+                       }
+
+                       LOG_DEBUG("Exposing additional custom register %d (name=%s)",
+                                       number, range->name ? range->name : reg_name);
+
                        custom_within_range++;
                        if (custom_within_range > range->high - range->low) {
                                custom_within_range = 0;
-                               custom_range_index++;
+                               list_rotate_left(&info->expose_custom);
                        }
                }
 
-               if (reg_name[0])
+               if (reg_name[0]) {
                        r->name = reg_name;
-               reg_name += strlen(reg_name) + 1;
-               assert(reg_name < info->reg_names + target->reg_cache->num_regs *
-                               max_reg_name_len);
+                       reg_name += strlen(reg_name) + 1;
+                       assert(reg_name < info->reg_names + target->reg_cache->num_regs *
+                                       max_reg_name_len);
+               }
                r->value = info->reg_cache_values[number];
        }
 

Linking to existing account procedure

If you already have an account and want to add another login method you MUST first sign in with your existing account and then change URL to read https://review.openocd.org/login/?link to get to this page again but this time it'll work for linking. Thank you.

SSH host keys fingerprints

1024 SHA256:YKx8b7u5ZWdcbp7/4AeXNaqElP49m6QrwfXaqQGJAOk gerrit-code-review@openocd.zylin.com (DSA)
384 SHA256:jHIbSQa4REvwCFG4cq5LBlBLxmxSqelQPem/EXIrxjk gerrit-code-review@openocd.org (ECDSA)
521 SHA256:UAOPYkU9Fjtcao0Ul/Rrlnj/OsQvt+pgdYSZ4jOYdgs gerrit-code-review@openocd.org (ECDSA)
256 SHA256:A13M5QlnozFOvTllybRZH6vm7iSt0XLxbA48yfc2yfY gerrit-code-review@openocd.org (ECDSA)
256 SHA256:spYMBqEYoAOtK7yZBrcwE8ZpYt6b68Cfh9yEVetvbXg gerrit-code-review@openocd.org (ED25519)
+--[ED25519 256]--+
|=..              |
|+o..   .         |
|*.o   . .        |
|+B . . .         |
|Bo. = o S        |
|Oo.+ + =         |
|oB=.* = . o      |
| =+=.+   + E     |
|. .=o   . o      |
+----[SHA256]-----+
2048 SHA256:0Onrb7/PHjpo6iVZ7xQX2riKN83FJ3KGU0TvI0TaFG4 gerrit-code-review@openocd.zylin.com (RSA)