target/riscv: fix unused initialization
[fw/openocd] / src / target / riscv / riscv.c
index 53af07ec31ad97fd5264f55fc1a62014abc578b6..c25efcde9a0d8cfbfe3004633dbfdc8be627de58 100644 (file)
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
+// SPDX-License-Identifier: GPL-2.0-or-later
 
 #include <assert.h>
 #include <stdlib.h>
@@ -8,23 +8,24 @@
 #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 <target/smp.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)
@@ -108,37 +109,37 @@ typedef enum slot {
 #define MAX_HWBPS                      16
 #define DRAM_CACHE_SIZE                16
 
-uint8_t ir_dtmcontrol[4] = {DTMCONTROL};
+static uint8_t ir_dtmcontrol[4] = {DTMCONTROL};
 struct scan_field select_dtmcontrol = {
        .in_value = NULL,
        .out_value = ir_dtmcontrol
 };
-uint8_t ir_dbus[4] = {DBUS};
+static uint8_t ir_dbus[4] = {DBUS};
 struct scan_field select_dbus = {
        .in_value = NULL,
        .out_value = ir_dbus
 };
-uint8_t ir_idcode[4] = {0x1};
+static uint8_t ir_idcode[4] = {0x1};
 struct scan_field select_idcode = {
        .in_value = NULL,
        .out_value = ir_idcode
 };
 
-bscan_tunnel_type_t bscan_tunnel_type;
+static 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};
-struct scan_field select_user4 = {
+static uint8_t ir_user4[4];
+static struct scan_field select_user4 = {
        .in_value = NULL,
        .out_value = ir_user4
 };
 
 
-uint8_t bscan_tunneled_ir_width[4] = {5};  /* overridden by assignment in riscv_init_target */
-struct scan_field _bscan_tunnel_data_register_select_dmi[] = {
+static uint8_t bscan_tunneled_ir_width[4] = {5};  /* overridden by assignment in riscv_init_target */
+static struct scan_field _bscan_tunnel_data_register_select_dmi[] = {
                {
                        .num_bits = 3,
                        .out_value = bscan_zero,
@@ -161,7 +162,7 @@ struct scan_field _bscan_tunnel_data_register_select_dmi[] = {
                }
 };
 
-struct scan_field _bscan_tunnel_nested_tap_select_dmi[] = {
+static struct scan_field _bscan_tunnel_nested_tap_select_dmi[] = {
                {
                        .num_bits = 1,
                        .out_value = bscan_zero,
@@ -183,11 +184,11 @@ struct scan_field _bscan_tunnel_nested_tap_select_dmi[] = {
                        .in_value = NULL,
                }
 };
-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);
+static struct scan_field *bscan_tunnel_nested_tap_select_dmi = _bscan_tunnel_nested_tap_select_dmi;
+static 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);
+static struct scan_field *bscan_tunnel_data_register_select_dmi = _bscan_tunnel_data_register_select_dmi;
+static uint32_t bscan_tunnel_data_register_select_dmi_num_fields = ARRAY_SIZE(_bscan_tunnel_data_register_select_dmi);
 
 struct trigger {
        uint64_t address;
@@ -204,32 +205,19 @@ 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;
+static bool riscv_enable_virt2phys = true;
 bool riscv_ebreakm = true;
 bool riscv_ebreaks = true;
 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 = {
+static const virt2phys_info_t sv32 = {
        .name = "Sv32",
        .va_bits = 32,
        .level = 2,
@@ -242,7 +230,7 @@ virt2phys_info_t sv32 = {
        .pa_ppn_mask = {0x3ff, 0xfff},
 };
 
-virt2phys_info_t sv39 = {
+static const virt2phys_info_t sv39 = {
        .name = "Sv39",
        .va_bits = 39,
        .level = 3,
@@ -255,7 +243,7 @@ virt2phys_info_t sv39 = {
        .pa_ppn_mask = {0x1ff, 0x1ff, 0x3ffffff},
 };
 
-virt2phys_info_t sv48 = {
+static const virt2phys_info_t sv48 = {
        .name = "Sv48",
        .va_bits = 48,
        .level = 4,
@@ -268,6 +256,27 @@ virt2phys_info_t sv48 = {
        .pa_ppn_mask = {0x1ff, 0x1ff, 0x1ff, 0x1ffff},
 };
 
+static enum riscv_halt_reason riscv_halt_reason(struct target *target, int hartid);
+static void riscv_info_init(struct target *target, struct riscv_info *r);
+static void riscv_invalidate_register_cache(struct target *target);
+static int riscv_step_rtos_hart(struct target *target);
+
+static 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 +357,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 +374,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;
@@ -403,13 +410,12 @@ static uint32_t dtmcontrol_scan(struct target *target, uint32_t out)
 
 static struct target_type *get_target_type(struct target *target)
 {
-       riscv_info_t *info = (riscv_info_t *) target->arch_info;
-
-       if (!info) {
+       if (!target->arch_info) {
                LOG_ERROR("Target has not been initialized");
                return NULL;
        }
 
+       RISCV_INFO(info);
        switch (info->dtm_version) {
                case 0:
                        return &riscv011_target;
@@ -421,15 +427,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(struct riscv_info));
+       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 +451,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)
@@ -461,6 +481,8 @@ static void riscv_free_registers(struct target *target)
                        /* Free the ones we allocated separately. */
                        for (unsigned i = GDB_REGNO_COUNT; i < target->reg_cache->num_regs; i++)
                                free(target->reg_cache->reg_list[i].arch_info);
+                       for (unsigned int i = 0; i < target->reg_cache->num_regs; i++)
+                               free(target->reg_cache->reg_list[i].value);
                        free(target->reg_cache->reg_list);
                }
                free(target->reg_cache);
@@ -470,16 +492,32 @@ static void riscv_free_registers(struct target *target)
 static void riscv_deinit_target(struct target *target)
 {
        LOG_DEBUG("riscv_deinit_target()");
+
+       struct riscv_info *info = target->arch_info;
        struct target_type *tt = get_target_type(target);
-       if (tt) {
+
+       if (tt && info && 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);
 
+       if (!info)
+               return;
+
+       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 +534,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 +558,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 +578,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 +604,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 +616,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 +628,103 @@ 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 +737,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,7 +747,125 @@ static int add_trigger(struct target *target, struct trigger *trigger)
        return ERROR_OK;
 }
 
-int riscv_add_breakpoint(struct target *target, struct breakpoint *breakpoint)
+/**
+ * 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;
+}
+
+static int riscv_add_breakpoint(struct target *target, struct breakpoint *breakpoint)
 {
        LOG_DEBUG("[%d] @0x%" TARGET_PRIxADDR, target->coreid, breakpoint->address);
        assert(breakpoint);
@@ -707,8 +881,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 +891,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;
@@ -735,7 +909,7 @@ int riscv_add_breakpoint(struct target *target, struct breakpoint *breakpoint)
                return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
        }
 
-       breakpoint->set = true;
+       breakpoint->is_set = true;
        return ERROR_OK;
 }
 
@@ -746,51 +920,38 @@ 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;
 }
 
-int riscv_remove_breakpoint(struct target *target,
+static 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;
@@ -808,7 +969,7 @@ int riscv_remove_breakpoint(struct target *target,
                return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
        }
 
-       breakpoint->set = false;
+       breakpoint->is_set = false;
 
        return ERROR_OK;
 }
@@ -835,7 +996,7 @@ int riscv_add_watchpoint(struct target *target, struct watchpoint *watchpoint)
        int result = add_trigger(target, &trigger);
        if (result != ERROR_OK)
                return result;
-       watchpoint->set = true;
+       watchpoint->is_set = true;
 
        return ERROR_OK;
 }
@@ -851,7 +1012,7 @@ int riscv_remove_watchpoint(struct target *target,
        int result = remove_trigger(target, &trigger);
        if (result != ERROR_OK)
                return result;
-       watchpoint->set = false;
+       watchpoint->is_set = false;
 
        return ERROR_OK;
 }
@@ -862,12 +1023,10 @@ int riscv_remove_watchpoint(struct target *target,
  * The GDB server uses this information to tell GDB what data address has
  * been hit, which enables GDB to print the hit variable along with its old
  * and new value. */
-int riscv_hit_watchpoint(struct target *target, struct watchpoint **hit_watchpoint)
+static int riscv_hit_watchpoint(struct target *target, struct watchpoint **hit_watchpoint)
 {
        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 +1112,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 +1129,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 +1155,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 halt_prep(struct target *target)
+int riscv_select_current_hart(struct target *target)
+{
+       return riscv_set_current_hartid(target, target->coreid);
+}
+
+static 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)
+static 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);
@@ -1047,11 +1204,11 @@ int riscv_halt_go_all_harts(struct target *target)
        return ERROR_OK;
 }
 
-int halt_go(struct target *target)
+static int halt_go(struct target *target)
 {
-       riscv_info_t *r = riscv_info(target);
+       RISCV_INFO(r);
        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 +1230,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);
        }
@@ -1082,22 +1239,23 @@ int riscv_halt(struct target *target)
 
        int result = ERROR_OK;
        if (target->smp) {
-               for (struct target_list *tlist = target->head; tlist; tlist = tlist->next) {
+               struct target_list *tlist;
+               foreach_smp_target(tlist, target->smp_targets) {
                        struct target *t = tlist->target;
                        if (halt_prep(t) != ERROR_OK)
                                result = ERROR_FAIL;
                }
 
-               for (struct target_list *tlist = target->head; tlist; tlist = tlist->next) {
+               foreach_smp_target(tlist, target->smp_targets) {
                        struct target *t = tlist->target;
-                       riscv_info_t *i = riscv_info(t);
+                       struct riscv_info *i = riscv_info(t);
                        if (i->prepped) {
                                if (halt_go(t) != ERROR_OK)
                                        result = ERROR_FAIL;
                        }
                }
 
-               for (struct target_list *tlist = target->head; tlist; tlist = tlist->next) {
+               foreach_smp_target(tlist, target->smp_targets) {
                        struct target *t = tlist->target;
                        if (halt_finish(t) != ERROR_OK)
                                return ERROR_FAIL;
@@ -1112,15 +1270,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;
 }
 
@@ -1139,25 +1288,22 @@ static int riscv_deassert_reset(struct target *target)
        return tt->deassert_reset(target);
 }
 
-int riscv_resume_prep_all_harts(struct target *target)
+static 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 +1319,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;
@@ -1199,9 +1344,9 @@ static int disable_triggers(struct target *target, riscv_reg_t *state)
                struct watchpoint *watchpoint = target->watchpoints;
                int i = 0;
                while (watchpoint) {
-                       LOG_DEBUG("watchpoint %d: set=%d", i, watchpoint->set);
-                       state[i] = watchpoint->set;
-                       if (watchpoint->set) {
+                       LOG_DEBUG("watchpoint %d: set=%d", i, watchpoint->is_set);
+                       state[i] = watchpoint->is_set;
+                       if (watchpoint->is_set) {
                                if (riscv_remove_watchpoint(target, watchpoint) != ERROR_OK)
                                        return ERROR_FAIL;
                        }
@@ -1217,14 +1362,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;
@@ -1297,9 +1440,9 @@ static int resume_prep(struct target *target, int current,
 static int resume_go(struct target *target, int current,
                target_addr_t address, int handle_breakpoints, int debug_execution)
 {
-       riscv_info_t *r = riscv_info(target);
+       RISCV_INFO(r);
        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);
@@ -1323,7 +1466,7 @@ static int resume_finish(struct target *target)
  * @par single_hart When true, only resume a single hart even if SMP is
  * configured.  This is used to run algorithms on just one hart.
  */
-int riscv_resume(
+static int riscv_resume(
                struct target *target,
                int current,
                target_addr_t address,
@@ -1334,16 +1477,19 @@ int riscv_resume(
        LOG_DEBUG("handle_breakpoints=%d", handle_breakpoints);
        int result = ERROR_OK;
        if (target->smp && !single_hart) {
-               for (struct target_list *tlist = target->head; tlist; tlist = tlist->next) {
+               struct target_list *tlist;
+               foreach_smp_target_direction(resume_order == RO_NORMAL,
+                                                                        tlist, target->smp_targets) {
                        struct target *t = tlist->target;
                        if (resume_prep(t, current, address, handle_breakpoints,
                                                debug_execution) != ERROR_OK)
                                result = ERROR_FAIL;
                }
 
-               for (struct target_list *tlist = target->head; tlist; tlist = tlist->next) {
+               foreach_smp_target_direction(resume_order == RO_NORMAL,
+                                                                        tlist, target->smp_targets) {
                        struct target *t = tlist->target;
-                       riscv_info_t *i = riscv_info(t);
+                       struct riscv_info *i = riscv_info(t);
                        if (i->prepped) {
                                if (resume_go(t, current, address, handle_breakpoints,
                                                        debug_execution) != ERROR_OK)
@@ -1351,7 +1497,8 @@ int riscv_resume(
                        }
                }
 
-               for (struct target_list *tlist = target->head; tlist; tlist = tlist->next) {
+               foreach_smp_target_direction(resume_order == RO_NORMAL,
+                                                                        tlist, target->smp_targets) {
                        struct target *t = tlist->target;
                        if (resume_finish(t) != ERROR_OK)
                                return ERROR_FAIL;
@@ -1378,17 +1525,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 +1532,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 +1578,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 +1746,25 @@ static int riscv_write_memory(struct target *target, target_addr_t address,
        return tt->write_memory(target, address, size, count, buffer);
 }
 
+static 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("[%s] {%d} reg_class=%d, read=%d",
+                       target_name(target), r->current_hartid, reg_class, read);
 
        if (!target->reg_cache) {
                LOG_ERROR("Target not initialized. Return ERROR_FAIL.");
@@ -1692,8 +1834,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 +1847,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 +1856,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 +1890,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 +1899,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 +1929,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 +1945,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 +1959,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 +1979,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 +1994,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 ***/
@@ -1889,7 +2109,7 @@ static enum riscv_poll_hart riscv_poll_hart(struct target *target, int hartid)
        return RPH_NO_CHANGE;
 }
 
-int set_debug_reason(struct target *target, enum riscv_halt_reason halt_reason)
+static int set_debug_reason(struct target *target, enum riscv_halt_reason halt_reason)
 {
        switch (halt_reason) {
                case RISCV_HALT_BREAKPOINT:
@@ -1915,62 +2135,67 @@ 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)
+static 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;
-                               list = list->next, i++) {
-                       total_targets++;
+               struct target_list *list;
+               foreach_smp_target(list, target->smp_targets) {
                        struct target *t = list->target;
-                       riscv_info_t *r = riscv_info(t);
-                       assert(i < DIM(newly_halted));
+                       struct riscv_info *r = riscv_info(t);
                        enum riscv_poll_hart out = riscv_poll_hart(t, r->current_hartid);
                        switch (out) {
                        case RPH_NO_CHANGE:
@@ -1981,7 +2206,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);
@@ -1991,17 +2215,17 @@ int riscv_openocd_poll(struct target *target)
                                if (halt_reason == RISCV_HALT_BREAKPOINT) {
                                        int retval;
                                        switch (riscv_semihosting(t, &retval)) {
-                                       case SEMI_NONE:
-                                       case SEMI_WAITING:
+                                       case SEMIHOSTING_NONE:
+                                       case SEMIHOSTING_WAITING:
                                                /* This hart should remain halted. */
                                                should_remain_halted++;
                                                break;
-                                       case SEMI_HANDLED:
+                                       case SEMIHOSTING_HANDLED:
                                                /* This hart should be resumed, along with any other
                                                         * harts that halted due to haltgroups. */
                                                should_resume++;
                                                break;
-                                       case SEMI_ERROR:
+                                       case SEMIHOSTING_ERROR:
                                                return retval;
                                        }
                                } else if (halt_reason != RISCV_HALT_GROUP) {
@@ -2027,15 +2251,28 @@ 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. */
+               foreach_smp_target(list, target->smp_targets) {
+                       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);
@@ -2049,15 +2286,15 @@ int riscv_openocd_poll(struct target *target)
        if (target->debug_reason == DBG_REASON_BREAKPOINT) {
                int retval;
                switch (riscv_semihosting(target, &retval)) {
-                       case SEMI_NONE:
-                       case SEMI_WAITING:
+                       case SEMIHOSTING_NONE:
+                       case SEMIHOSTING_WAITING:
                                target_call_event_callbacks(target, TARGET_EVENT_HALTED);
                                break;
-                       case SEMI_HANDLED:
+                       case SEMIHOSTING_HANDLED:
                                if (riscv_resume(target, true, 0, 0, 0, false) != ERROR_OK)
                                        return ERROR_FAIL;
                                break;
-                       case SEMI_ERROR:
+                       case SEMIHOSTING_ERROR:
                                return retval;
                }
        } else {
@@ -2132,32 +2369,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;
        }
-       COMMAND_PARSE_ON_OFF(CMD_ARGV[0], riscv_prefer_sba);
+
+       /* 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;
+       }
+
+       /* 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 +2462,180 @@ COMMAND_HANDLER(riscv_set_enable_virtual)
        return ERROR_OK;
 }
 
-void parse_error(const char *string, char c, unsigned position)
+static 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;
                }
 
-               if (pass == 0) {
-                       free(*ranges);
-                       *ranges = calloc(range + 2, sizeof(range_t));
-                       if (!*ranges)
+               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;
+               }
+
+               /* 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 +2653,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 +2665,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 +2900,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 +2957,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 +2977,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 +2986,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 +2995,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",
@@ -2714,9 +3120,8 @@ static const struct command_registration riscv_exec_command_handlers[] = {
  * protocol, then a command like `riscv semihosting enable` will make
  * sense, but for now all semihosting commands are prefixed with `arm`.
  */
-extern const struct command_registration semihosting_common_handlers[];
 
-const struct command_registration riscv_command_handlers[] = {
+static const struct command_registration riscv_command_handlers[] = {
        {
                .name = "riscv",
                .mode = COMMAND_ANY,
@@ -2739,9 +3144,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 +3180,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,79 +3198,65 @@ struct target_type riscv_target = {
        .commands = riscv_command_handlers,
 
        .address_bits = riscv_xlen_nonconst,
+       .data_bits = riscv_data_bits
 };
 
 /*** RISC-V Interface ***/
 
-void riscv_info_init(struct target *target, riscv_info_t *r)
+/* Initializes the shared RISC-V structure. */
+static void riscv_info_init(struct target *target, struct riscv_info *r)
 {
        memset(r, 0, sizeof(*r));
+
+       r->common_magic = RISCV_COMMON_MAGIC;
+
        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);
        return ERROR_OK;
 }
 
-int riscv_step_rtos_hart(struct target *target)
+/* Steps the hart that's currently selected in the RTOS, or if there is no RTOS
+ * then the only hart. */
+static 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 +3275,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 +3285,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,31 +3302,22 @@ 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;
 }
 
-void riscv_invalidate_register_cache(struct target *target)
+/* Invalidates the register cache. */
+static void riscv_invalidate_register_cache(struct target *target)
 {
-       RISCV_INFO(r);
-
        LOG_DEBUG("[%d]", target->coreid);
        register_cache_invalidate(target->reg_cache);
        for (size_t i = 0; i < target->reg_cache->num_regs; ++i) {
                struct reg *reg = &target->reg_cache->reg_list[i];
                reg->valid = false;
        }
-
-       r->registers_initialized = true;
 }
 
 int riscv_current_hartid(const struct target *target)
@@ -2945,34 +3326,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 +3352,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 +3389,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;
 }
 
@@ -3105,7 +3460,7 @@ bool riscv_is_halted(struct target *target)
        return r->is_halted(target);
 }
 
-enum riscv_halt_reason riscv_halt_reason(struct target *target, int hartid)
+static enum riscv_halt_reason riscv_halt_reason(struct target *target, int hartid)
 {
        RISCV_INFO(r);
        if (riscv_set_current_hartid(target, hartid) != ERROR_OK)
@@ -3120,7 +3475,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 +3521,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 +3537,61 @@ 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)
+               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 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)
+                       case 2:
+                               if (tdata1 & MCONTROL_DMODE(riscv_xlen(target)))
+                                       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 6:
+                               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 +3802,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 +3815,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 +3883,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 +3905,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 +3962,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 +3998,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 +4041,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 +4070,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 +4182,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 +4300,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 +4319,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 +4333,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 +4418,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 +4447,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 +4456,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,19 +4471,28 @@ 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);
-               r->value = &info->reg_cache_values[number];
+                       reg_name += strlen(reg_name) + 1;
+                       assert(reg_name < info->reg_names + target->reg_cache->num_regs *
+                                       max_reg_name_len);
+               }
+               r->value = calloc(1, DIV_ROUND_UP(r->size, 8));
        }
 
        return ERROR_OK;