Upstream a whole host of RISC-V changes.
[openocd.git] / src / target / riscv / riscv.c
index 02ba380553fb7057508a5c36da04126b07305d53..7579e0f0c3b2c1115434c60f8fc9373c9655c95c 100644 (file)
@@ -1,3 +1,5 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
 #include <assert.h>
 #include <stdlib.h>
 #include <time.h>
@@ -6,65 +8,23 @@
 #include "config.h"
 #endif
 
+#include <helper/log.h>
+#include <helper/time_support.h>
 #include "target/target.h"
 #include "target/algorithm.h"
 #include "target/target_type.h"
-#include "log.h"
 #include "jtag/jtag.h"
 #include "target/register.h"
 #include "target/breakpoints.h"
-#include "helper/time_support.h"
 #include "riscv.h"
 #include "gdb_regs.h"
 #include "rtos/rtos.h"
-
-/**
- * Since almost everything can be accomplish by scanning the dbus register, all
- * functions here assume dbus is already selected. The exception are functions
- * called directly by OpenOCD, which can't assume anything about what's
- * currently in IR. They should set IR to dbus explicitly.
- */
-
-/**
- * Code structure
- *
- * At the bottom of the stack are the OpenOCD JTAG functions:
- *             jtag_add_[id]r_scan
- *             jtag_execute_query
- *             jtag_add_runtest
- *
- * There are a few functions to just instantly shift a register and get its
- * value:
- *             dtmcontrol_scan
- *             idcode_scan
- *             dbus_scan
- *
- * Because doing one scan and waiting for the result is slow, most functions
- * batch up a bunch of dbus writes and then execute them all at once. They use
- * the scans "class" for this:
- *             scans_new
- *             scans_delete
- *             scans_execute
- *             scans_add_...
- * Usually you new(), call a bunch of add functions, then execute() and look
- * at the results by calling scans_get...()
- *
- * Optimized functions will directly use the scans class above, but slightly
- * lazier code will use the cache functions that in turn use the scans
- * functions:
- *             cache_get...
- *             cache_set...
- *             cache_write
- * cache_set... update a local structure, which is then synced to the target
- * with cache_write(). Only Debug RAM words that are actually changed are sent
- * to the target. Afterwards use cache_get... to read results.
- */
+#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,12 +68,6 @@ typedef enum {
 #define DBUS_DATA_SIZE                         34
 #define DBUS_ADDRESS_START                     36
 
-typedef enum {
-       RE_OK,
-       RE_FAIL,
-       RE_AGAIN
-} riscv_error_t;
-
 typedef enum slot {
        SLOT0,
        SLOT1,
@@ -154,22 +108,87 @@ typedef enum slot {
 #define MAX_HWBPS                      16
 #define DRAM_CACHE_SIZE                16
 
-uint8_t ir_dtmcontrol[1] = {DTMCONTROL};
+uint8_t ir_dtmcontrol[4] = {DTMCONTROL};
 struct scan_field select_dtmcontrol = {
        .in_value = NULL,
        .out_value = ir_dtmcontrol
 };
-uint8_t ir_dbus[1] = {DBUS};
+uint8_t ir_dbus[4] = {DBUS};
 struct scan_field select_dbus = {
        .in_value = NULL,
        .out_value = ir_dbus
 };
-uint8_t ir_idcode[1] = {0x1};
+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;
+int bscan_tunnel_ir_width; /* if zero, then tunneling is not present/active */
+
+static const uint8_t bscan_zero[4] = {0};
+static const uint8_t bscan_one[4] = {1};
+
+uint8_t ir_user4[4];
+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[] = {
+               {
+                       .num_bits = 3,
+                       .out_value = bscan_zero,
+                       .in_value = NULL,
+               },
+               {
+                       .num_bits = 5, /* initialized in riscv_init_target to ir width of DM */
+                       .out_value = ir_dbus,
+                       .in_value = NULL,
+               },
+               {
+                       .num_bits = 7,
+                       .out_value = bscan_tunneled_ir_width,
+                       .in_value = NULL,
+               },
+               {
+                       .num_bits = 1,
+                       .out_value = bscan_zero,
+                       .in_value = NULL,
+               }
+};
+
+struct scan_field _bscan_tunnel_nested_tap_select_dmi[] = {
+               {
+                       .num_bits = 1,
+                       .out_value = bscan_zero,
+                       .in_value = NULL,
+               },
+               {
+                       .num_bits = 7,
+                       .out_value = bscan_tunneled_ir_width,
+                       .in_value = NULL,
+               },
+               {
+                       .num_bits = 0, /* initialized in riscv_init_target to ir width of DM */
+                       .out_value = ir_dbus,
+                       .in_value = NULL,
+               },
+               {
+                       .num_bits = 3,
+                       .out_value = bscan_zero,
+                       .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 = 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 = ARRAY_SIZE(_bscan_tunnel_data_register_select_dmi);
+
 struct trigger {
        uint64_t address;
        uint32_t length;
@@ -185,21 +204,179 @@ int riscv_command_timeout_sec = DEFAULT_COMMAND_TIMEOUT_SEC;
 /* Wall-clock timeout after reset. Settable via RISC-V Target commands.*/
 int riscv_reset_timeout_sec = DEFAULT_RESET_TIMEOUT_SEC;
 
-bool riscv_prefer_sba;
+bool riscv_enable_virt2phys = true;
+bool riscv_ebreakm = true;
+bool riscv_ebreaks = true;
+bool riscv_ebreaku = true;
+
+bool riscv_enable_virtual;
+
+static enum {
+       RO_NORMAL,
+       RO_REVERSED
+} resume_order;
+
+const virt2phys_info_t sv32 = {
+       .name = "Sv32",
+       .va_bits = 32,
+       .level = 2,
+       .pte_shift = 2,
+       .vpn_shift = {12, 22},
+       .vpn_mask = {0x3ff, 0x3ff},
+       .pte_ppn_shift = {10, 20},
+       .pte_ppn_mask = {0x3ff, 0xfff},
+       .pa_ppn_shift = {12, 22},
+       .pa_ppn_mask = {0x3ff, 0xfff},
+};
+
+const virt2phys_info_t sv39 = {
+       .name = "Sv39",
+       .va_bits = 39,
+       .level = 3,
+       .pte_shift = 3,
+       .vpn_shift = {12, 21, 30},
+       .vpn_mask = {0x1ff, 0x1ff, 0x1ff},
+       .pte_ppn_shift = {10, 19, 28},
+       .pte_ppn_mask = {0x1ff, 0x1ff, 0x3ffffff},
+       .pa_ppn_shift = {12, 21, 30},
+       .pa_ppn_mask = {0x1ff, 0x1ff, 0x3ffffff},
+};
+
+const virt2phys_info_t sv48 = {
+       .name = "Sv48",
+       .va_bits = 48,
+       .level = 4,
+       .pte_shift = 3,
+       .vpn_shift = {12, 21, 30, 39},
+       .vpn_mask = {0x1ff, 0x1ff, 0x1ff, 0x1ff},
+       .pte_ppn_shift = {10, 19, 28, 37},
+       .pte_ppn_mask = {0x1ff, 0x1ff, 0x1ff, 0x1ffff},
+       .pa_ppn_shift = {12, 21, 30, 39},
+       .pa_ppn_mask = {0x1ff, 0x1ff, 0x1ff, 0x1ffff},
+};
+
+void riscv_sample_buf_maybe_add_timestamp(struct target *target, bool before)
+{
+       RISCV_INFO(r);
+       uint32_t now = timeval_ms() & 0xffffffff;
+       if (r->sample_buf.used + 5 < r->sample_buf.size) {
+               if (before)
+                       r->sample_buf.buf[r->sample_buf.used++] = RISCV_SAMPLE_BUF_TIMESTAMP_BEFORE;
+               else
+                       r->sample_buf.buf[r->sample_buf.used++] = RISCV_SAMPLE_BUF_TIMESTAMP_AFTER;
+               r->sample_buf.buf[r->sample_buf.used++] = now & 0xff;
+               r->sample_buf.buf[r->sample_buf.used++] = (now >> 8) & 0xff;
+               r->sample_buf.buf[r->sample_buf.used++] = (now >> 16) & 0xff;
+               r->sample_buf.buf[r->sample_buf.used++] = (now >> 24) & 0xff;
+       }
+}
+
+static int riscv_resume_go_all_harts(struct target *target);
+
+void select_dmi_via_bscan(struct target *target)
+{
+       jtag_add_ir_scan(target->tap, &select_user4, TAP_IDLE);
+       if (bscan_tunnel_type == BSCAN_TUNNEL_DATA_REGISTER)
+               jtag_add_dr_scan(target->tap, bscan_tunnel_data_register_select_dmi_num_fields,
+                                                                               bscan_tunnel_data_register_select_dmi, TAP_IDLE);
+       else /* BSCAN_TUNNEL_NESTED_TAP */
+               jtag_add_dr_scan(target->tap, bscan_tunnel_nested_tap_select_dmi_num_fields,
+                                                                               bscan_tunnel_nested_tap_select_dmi, TAP_IDLE);
+}
+
+uint32_t dtmcontrol_scan_via_bscan(struct target *target, uint32_t out)
+{
+       /* On BSCAN TAP: Select IR=USER4, issue tunneled IR scan via BSCAN TAP's DR */
+       uint8_t tunneled_ir_width[4] = {bscan_tunnel_ir_width};
+       uint8_t tunneled_dr_width[4] = {32};
+       uint8_t out_value[5] = {0};
+       uint8_t in_value[5] = {0};
+
+       buf_set_u32(out_value, 0, 32, out);
+       struct scan_field tunneled_ir[4] = {};
+       struct scan_field tunneled_dr[4] = {};
+
+       if (bscan_tunnel_type == BSCAN_TUNNEL_DATA_REGISTER) {
+               tunneled_ir[0].num_bits = 3;
+               tunneled_ir[0].out_value = bscan_zero;
+               tunneled_ir[0].in_value = NULL;
+               tunneled_ir[1].num_bits = bscan_tunnel_ir_width;
+               tunneled_ir[1].out_value = ir_dtmcontrol;
+               tunneled_ir[1].in_value = NULL;
+               tunneled_ir[2].num_bits = 7;
+               tunneled_ir[2].out_value = tunneled_ir_width;
+               tunneled_ir[2].in_value = NULL;
+               tunneled_ir[3].num_bits = 1;
+               tunneled_ir[3].out_value = bscan_zero;
+               tunneled_ir[3].in_value = NULL;
+
+               tunneled_dr[0].num_bits = 3;
+               tunneled_dr[0].out_value = bscan_zero;
+               tunneled_dr[0].in_value = NULL;
+               tunneled_dr[1].num_bits = 32 + 1;
+               tunneled_dr[1].out_value = out_value;
+               tunneled_dr[1].in_value = in_value;
+               tunneled_dr[2].num_bits = 7;
+               tunneled_dr[2].out_value = tunneled_dr_width;
+               tunneled_dr[2].in_value = NULL;
+               tunneled_dr[3].num_bits = 1;
+               tunneled_dr[3].out_value = bscan_one;
+               tunneled_dr[3].in_value = NULL;
+       } else {
+               /* BSCAN_TUNNEL_NESTED_TAP */
+               tunneled_ir[3].num_bits = 3;
+               tunneled_ir[3].out_value = bscan_zero;
+               tunneled_ir[3].in_value = NULL;
+               tunneled_ir[2].num_bits = bscan_tunnel_ir_width;
+               tunneled_ir[2].out_value = ir_dtmcontrol;
+               tunneled_ir[1].in_value = NULL;
+               tunneled_ir[1].num_bits = 7;
+               tunneled_ir[1].out_value = tunneled_ir_width;
+               tunneled_ir[2].in_value = NULL;
+               tunneled_ir[0].num_bits = 1;
+               tunneled_ir[0].out_value = bscan_zero;
+               tunneled_ir[0].in_value = NULL;
+
+               tunneled_dr[3].num_bits = 3;
+               tunneled_dr[3].out_value = bscan_zero;
+               tunneled_dr[3].in_value = NULL;
+               tunneled_dr[2].num_bits = 32 + 1;
+               tunneled_dr[2].out_value = out_value;
+               tunneled_dr[2].in_value = in_value;
+               tunneled_dr[1].num_bits = 7;
+               tunneled_dr[1].out_value = tunneled_dr_width;
+               tunneled_dr[1].in_value = NULL;
+               tunneled_dr[0].num_bits = 1;
+               tunneled_dr[0].out_value = bscan_one;
+               tunneled_dr[0].in_value = NULL;
+       }
+       jtag_add_ir_scan(target->tap, &select_user4, 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();
+       if (retval != ERROR_OK) {
+               LOG_ERROR("failed jtag scan: %d", retval);
+               return retval;
+       }
+       /* Note the starting offset is bit 1, not bit 0.  In BSCAN tunnel, there is a one-bit TCK skew between
+          output and input */
+       uint32_t in = buf_get_u32(in_value, 1, 32);
+       LOG_DEBUG("DTMCS: 0x%x -> 0x%x", out, in);
 
-/* 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. */
-struct {
-       uint16_t low, high;
-} *expose_csr;
+       return in;
+}
 
 static uint32_t dtmcontrol_scan(struct target *target, uint32_t out)
 {
        struct scan_field field;
        uint8_t in_value[4];
-       uint8_t out_value[4];
+       uint8_t out_value[4] = { 0 };
+
+       if (bscan_tunnel_ir_width != 0)
+               return dtmcontrol_scan_via_bscan(target, out);
+
 
        buf_set_u32(out_value, 0, 32, out);
 
@@ -245,42 +422,93 @@ static struct target_type *get_target_type(struct target *target)
        }
 }
 
+static int riscv_create_target(struct target *target, Jim_Interp *interp)
+{
+       LOG_DEBUG("riscv_create_target()");
+       target->arch_info = calloc(1, sizeof(riscv_info_t));
+       if (!target->arch_info) {
+               LOG_ERROR("Failed to allocate RISC-V target structure.");
+               return ERROR_FAIL;
+       }
+       riscv_info_init(target, target->arch_info);
+       return ERROR_OK;
+}
+
 static int riscv_init_target(struct command_context *cmd_ctx,
                struct target *target)
 {
        LOG_DEBUG("riscv_init_target()");
-       target->arch_info = calloc(1, sizeof(riscv_info_t));
-       if (!target->arch_info)
-               return ERROR_FAIL;
-       riscv_info_t *info = (riscv_info_t *) target->arch_info;
-       riscv_info_init(target, info);
+       RISCV_INFO(info);
        info->cmd_ctx = cmd_ctx;
 
        select_dtmcontrol.num_bits = target->tap->ir_length;
        select_dbus.num_bits = target->tap->ir_length;
        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)
+                       bscan_tunnel_data_register_select_dmi[1].num_bits = bscan_tunnel_ir_width;
+               else /* BSCAN_TUNNEL_NESTED_TAP */
+                       bscan_tunnel_nested_tap_select_dmi[2].num_bits = bscan_tunnel_ir_width;
+       }
+
        riscv_semihosting_init(target);
 
+       target->debug_reason = DBG_REASON_DBGRQ;
+
        return ERROR_OK;
 }
 
+static void riscv_free_registers(struct target *target)
+{
+       /* Free the shared structure use for most registers. */
+       if (target->reg_cache) {
+               if (target->reg_cache->reg_list) {
+                       free(target->reg_cache->reg_list[0].arch_info);
+                       /* 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);
+                       free(target->reg_cache->reg_list);
+               }
+               free(target->reg_cache);
+       }
+}
+
 static void riscv_deinit_target(struct target *target)
 {
        LOG_DEBUG("riscv_deinit_target()");
+
+       riscv_info_t *info = target->arch_info;
        struct target_type *tt = get_target_type(target);
-       if (tt) {
+
+       if (tt && info->version_specific)
                tt->deinit_target(target);
-               riscv_info_t *info = (riscv_info_t *) target->arch_info;
-               free(info);
+
+       riscv_free_registers(target);
+
+       range_list_t *entry, *tmp;
+       list_for_each_entry_safe(entry, tmp, &info->expose_csr, list) {
+               free(entry->name);
+               free(entry);
        }
-       target->arch_info = NULL;
-}
 
-static int oldriscv_halt(struct target *target)
-{
-       struct target_type *tt = get_target_type(target);
-       return tt->halt(target);
+       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;
 }
 
 static void trigger_from_breakpoint(struct trigger *trigger,
@@ -296,7 +524,7 @@ static void trigger_from_breakpoint(struct trigger *trigger,
        trigger->unique_id = breakpoint->unique_id;
 }
 
-static int maybe_add_trigger_t1(struct target *target, unsigned hartid,
+static int maybe_add_trigger_t1(struct target *target,
                struct trigger *trigger, uint64_t tdata1)
 {
        RISCV_INFO(r);
@@ -320,20 +548,19 @@ static int maybe_add_trigger_t1(struct target *target, unsigned hartid,
        tdata1 = set_field(tdata1, bpcontrol_w, trigger->write);
        tdata1 = set_field(tdata1, bpcontrol_x, trigger->execute);
        tdata1 = set_field(tdata1, bpcontrol_u,
-                       !!(r->misa[hartid] & (1 << ('U' - 'A'))));
+                       !!(r->misa & BIT('U' - 'A')));
        tdata1 = set_field(tdata1, bpcontrol_s,
-                       !!(r->misa[hartid] & (1 << ('S' - 'A'))));
+                       !!(r->misa & BIT('S' - 'A')));
        tdata1 = set_field(tdata1, bpcontrol_h,
-                       !!(r->misa[hartid] & (1 << ('H' - 'A'))));
+                       !!(r->misa & BIT('H' - 'A')));
        tdata1 |= bpcontrol_m;
        tdata1 = set_field(tdata1, bpcontrol_bpmatch, 0); /* exact match */
        tdata1 = set_field(tdata1, bpcontrol_bpaction, 0); /* cause bp exception */
 
-       riscv_set_register_on_hart(target, hartid, GDB_REGNO_TDATA1, tdata1);
+       riscv_set_register(target, GDB_REGNO_TDATA1, tdata1);
 
        riscv_reg_t tdata1_rb;
-       if (riscv_get_register_on_hart(target, &tdata1_rb, hartid,
-                               GDB_REGNO_TDATA1) != ERROR_OK)
+       if (riscv_get_register(target, &tdata1_rb, GDB_REGNO_TDATA1) != ERROR_OK)
                return ERROR_FAIL;
        LOG_DEBUG("tdata1=0x%" PRIx64, tdata1_rb);
 
@@ -341,16 +568,16 @@ static int maybe_add_trigger_t1(struct target *target, unsigned hartid,
                LOG_DEBUG("Trigger doesn't support what we need; After writing 0x%"
                                PRIx64 " to tdata1 it contains 0x%" PRIx64,
                                tdata1, tdata1_rb);
-               riscv_set_register_on_hart(target, hartid, GDB_REGNO_TDATA1, 0);
+               riscv_set_register(target, GDB_REGNO_TDATA1, 0);
                return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
        }
 
-       riscv_set_register_on_hart(target, hartid, GDB_REGNO_TDATA2, trigger->address);
+       riscv_set_register(target, GDB_REGNO_TDATA2, trigger->address);
 
        return ERROR_OK;
 }
 
-static int maybe_add_trigger_t2(struct target *target, unsigned hartid,
+static int maybe_add_trigger_t2(struct target *target,
                struct trigger *trigger, uint64_t tdata1)
 {
        RISCV_INFO(r);
@@ -367,11 +594,9 @@ static int maybe_add_trigger_t2(struct target *target, unsigned hartid,
                        MCONTROL_ACTION_DEBUG_MODE);
        tdata1 = set_field(tdata1, MCONTROL_MATCH, MCONTROL_MATCH_EQUAL);
        tdata1 |= MCONTROL_M;
-       if (r->misa[hartid] & (1 << ('H' - 'A')))
-               tdata1 |= MCONTROL_H;
-       if (r->misa[hartid] & (1 << ('S' - 'A')))
+       if (r->misa & (1 << ('S' - 'A')))
                tdata1 |= MCONTROL_S;
-       if (r->misa[hartid] & (1 << ('U' - 'A')))
+       if (r->misa & (1 << ('U' - 'A')))
                tdata1 |= MCONTROL_U;
 
        if (trigger->execute)
@@ -381,10 +606,10 @@ static int maybe_add_trigger_t2(struct target *target, unsigned hartid,
        if (trigger->write)
                tdata1 |= MCONTROL_STORE;
 
-       riscv_set_register_on_hart(target, hartid, GDB_REGNO_TDATA1, tdata1);
+       riscv_set_register(target, GDB_REGNO_TDATA1, tdata1);
 
        uint64_t tdata1_rb;
-       int result = riscv_get_register_on_hart(target, &tdata1_rb, hartid, GDB_REGNO_TDATA1);
+       int result = riscv_get_register(target, &tdata1_rb, GDB_REGNO_TDATA1);
        if (result != ERROR_OK)
                return result;
        LOG_DEBUG("tdata1=0x%" PRIx64, tdata1_rb);
@@ -393,97 +618,119 @@ 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;
                }
 
                if (result != ERROR_OK)
                        continue;
 
-               LOG_DEBUG("Using trigger %d (type %d) for bp %d", i, type,
-                               trigger->unique_id);
+               LOG_DEBUG("[%d] Using trigger %d (type %d) for bp %d", target->coreid,
+                               i, type, trigger->unique_id);
                r->trigger_unique_id[i] = trigger->unique_id;
                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;
        }
@@ -491,22 +738,152 @@ static int add_trigger(struct target *target, struct trigger *trigger)
        return ERROR_OK;
 }
 
+/**
+ * Write one memory item of given "size". Use memory access of given "access_size".
+ * Utilize read-modify-write, if needed.
+ * */
+static int write_by_given_size(struct target *target, target_addr_t address,
+               uint32_t size, uint8_t *buffer, uint32_t access_size)
+{
+       assert(size == 1 || size == 2 || size == 4 || size == 8);
+       assert(access_size == 1 || access_size == 2 || access_size == 4 || access_size == 8);
+
+       if (access_size <= size && address % access_size == 0)
+               /* Can do the memory access directly without a helper buffer. */
+               return target_write_memory(target, address, access_size, size / access_size, buffer);
+
+       unsigned int offset_head = address % access_size;
+       unsigned int n_blocks = ((size + offset_head) <= access_size) ? 1 : 2;
+       uint8_t helper_buf[n_blocks * access_size];
+
+       /* Read from memory */
+       if (target_read_memory(target, address - offset_head, access_size, n_blocks, helper_buf) != ERROR_OK)
+               return ERROR_FAIL;
+
+       /* Modify and write back */
+       memcpy(helper_buf + offset_head, buffer, size);
+       return target_write_memory(target, address - offset_head, access_size, n_blocks, helper_buf);
+}
+
+/**
+ * Read one memory item of given "size". Use memory access of given "access_size".
+ * Read larger section of memory and pick out the required portion, if needed.
+ * */
+static int read_by_given_size(struct target *target, target_addr_t address,
+       uint32_t size, uint8_t *buffer, uint32_t access_size)
+{
+       assert(size == 1 || size == 2 || size == 4 || size == 8);
+       assert(access_size == 1 || access_size == 2 || access_size == 4 || access_size == 8);
+
+       if (access_size <= size && address % access_size == 0)
+               /* Can do the memory access directly without a helper buffer. */
+               return target_read_memory(target, address, access_size, size / access_size, buffer);
+
+       unsigned int offset_head = address % access_size;
+       unsigned int n_blocks = ((size + offset_head) <= access_size) ? 1 : 2;
+       uint8_t helper_buf[n_blocks * access_size];
+
+       /* Read from memory */
+       if (target_read_memory(target, address - offset_head, access_size, n_blocks, helper_buf) != ERROR_OK)
+               return ERROR_FAIL;
+
+       /* Pick the requested portion from the buffer */
+       memcpy(buffer, helper_buf + offset_head, size);
+       return ERROR_OK;
+}
+
+/**
+ * Write one memory item using any memory access size that will work.
+ * Utilize read-modify-write, if needed.
+ * */
+int riscv_write_by_any_size(struct target *target, target_addr_t address, uint32_t size, uint8_t *buffer)
+{
+       assert(size == 1 || size == 2 ||  size == 4 || size == 8);
+
+       /* Find access size that correspond to data size and the alignment. */
+       unsigned int preferred_size = size;
+       while (address % preferred_size != 0)
+               preferred_size /= 2;
+
+       /* First try the preferred (most natural) access size. */
+       if (write_by_given_size(target, address, size, buffer, preferred_size) == ERROR_OK)
+               return ERROR_OK;
+
+       /* On failure, try other access sizes.
+          Minimize the number of accesses by trying first the largest size. */
+       for (unsigned int access_size = 8; access_size > 0; access_size /= 2) {
+               if (access_size == preferred_size)
+                       /* Already tried this size. */
+                       continue;
+
+               if (write_by_given_size(target, address, size, buffer, access_size) == ERROR_OK)
+                       return ERROR_OK;
+       }
+
+       /* No access attempt succeeded. */
+       return ERROR_FAIL;
+}
+
+/**
+ * Read one memory item using any memory access size that will work.
+ * Read larger section of memory and pick out the required portion, if needed.
+ * */
+int riscv_read_by_any_size(struct target *target, target_addr_t address, uint32_t size, uint8_t *buffer)
+{
+       assert(size == 1 || size == 2 ||  size == 4 || size == 8);
+
+       /* Find access size that correspond to data size and the alignment. */
+       unsigned int preferred_size = size;
+       while (address % preferred_size != 0)
+               preferred_size /= 2;
+
+       /* First try the preferred (most natural) access size. */
+       if (read_by_given_size(target, address, size, buffer, preferred_size) == ERROR_OK)
+               return ERROR_OK;
+
+       /* On failure, try other access sizes.
+          Minimize the number of accesses by trying first the largest size. */
+       for (unsigned int access_size = 8; access_size > 0; access_size /= 2) {
+               if (access_size == preferred_size)
+                       /* Already tried this size. */
+                       continue;
+
+               if (read_by_given_size(target, address, size, buffer, access_size) == ERROR_OK)
+                       return ERROR_OK;
+       }
+
+       /* No access attempt succeeded. */
+       return ERROR_FAIL;
+}
+
 int riscv_add_breakpoint(struct target *target, struct breakpoint *breakpoint)
 {
+       LOG_DEBUG("[%d] @0x%" TARGET_PRIxADDR, target->coreid, breakpoint->address);
+       assert(breakpoint);
        if (breakpoint->type == BKPT_SOFT) {
-               if (target_read_memory(target, breakpoint->address, breakpoint->length, 1,
-                                       breakpoint->orig_instr) != ERROR_OK) {
+               /** @todo check RVC for size/alignment */
+               if (!(breakpoint->length == 4 || breakpoint->length == 2)) {
+                       LOG_ERROR("Invalid breakpoint length %d", breakpoint->length);
+                       return ERROR_FAIL;
+               }
+
+               if (0 != (breakpoint->address % 2)) {
+                       LOG_ERROR("Invalid breakpoint alignment for address 0x%" TARGET_PRIxADDR, breakpoint->address);
+                       return ERROR_FAIL;
+               }
+
+               /* 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;
                }
 
-               int retval;
-               if (breakpoint->length == 4)
-                       retval = target_write_u32(target, breakpoint->address, ebreak());
-               else
-                       retval = target_write_u16(target, breakpoint->address, ebreak_c());
-               if (retval != ERROR_OK) {
+               uint8_t buff[4] = { 0 };
+               buf_set_u32(buff, 0, breakpoint->length * CHAR_BIT, breakpoint->length == 4 ? ebreak() : ebreak_c());
+               /* 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;
@@ -515,17 +892,15 @@ int riscv_add_breakpoint(struct target *target, struct breakpoint *breakpoint)
        } else if (breakpoint->type == BKPT_HARD) {
                struct trigger trigger;
                trigger_from_breakpoint(&trigger, breakpoint);
-               int result = add_trigger(target, &trigger);
+               int const result = add_trigger(target, &trigger);
                if (result != ERROR_OK)
                        return result;
-
        } else {
                LOG_INFO("OpenOCD only supports hardware and software breakpoints.");
                return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
        }
 
        breakpoint->set = true;
-
        return ERROR_OK;
 }
 
@@ -536,39 +911,26 @@ static int remove_trigger(struct target *target, struct trigger *trigger)
        if (riscv_enumerate_triggers(target) != ERROR_OK)
                return ERROR_FAIL;
 
-       int first_hart = -1;
-       for (int hartid = 0; hartid < riscv_count_harts(target); ++hartid) {
-               if (!riscv_hart_enabled(target, hartid))
-                       continue;
-               if (first_hart < 0) {
-                       first_hart = hartid;
-                       break;
-               }
-       }
-       assert(first_hart >= 0);
-
        unsigned int i;
-       for (i = 0; i < r->trigger_count[first_hart]; i++) {
+       for (i = 0; i < r->trigger_count; i++) {
                if (r->trigger_unique_id[i] == trigger->unique_id)
                        break;
        }
-       if (i >= r->trigger_count[first_hart]) {
+       if (i >= r->trigger_count) {
                LOG_ERROR("Couldn't find the hardware resources used by hardware "
                                "trigger.");
                return ERROR_FAIL;
        }
-       LOG_DEBUG("Stop using resource %d for bp %d", 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);
-       }
+       LOG_DEBUG("[%d] Stop using resource %d for bp %d", target->coreid, i,
+                       trigger->unique_id);
+
+       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;
@@ -578,8 +940,9 @@ int riscv_remove_breakpoint(struct target *target,
                struct breakpoint *breakpoint)
 {
        if (breakpoint->type == BKPT_SOFT) {
-               if (target_write_memory(target, breakpoint->address, breakpoint->length, 1,
-                                       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;
@@ -632,6 +995,8 @@ int riscv_add_watchpoint(struct target *target, struct watchpoint *watchpoint)
 int riscv_remove_watchpoint(struct target *target,
                struct watchpoint *watchpoint)
 {
+       LOG_DEBUG("[%d] @0x%" TARGET_PRIxADDR, target->coreid, watchpoint->address);
+
        struct trigger trigger;
        trigger_from_watchpoint(&trigger, watchpoint);
 
@@ -643,6 +1008,89 @@ int riscv_remove_watchpoint(struct target *target,
        return ERROR_OK;
 }
 
+/* Sets *hit_watchpoint to the first watchpoint identified as causing the
+ * current halt.
+ *
+ * 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)
+{
+       struct watchpoint *wp = target->watchpoints;
+
+       LOG_DEBUG("Current hartid = %d", riscv_current_hartid(target));
+
+       /*TODO instead of disassembling the instruction that we think caused the
+        * trigger, check the hit bit of each watchpoint first. The hit bit is
+        * simpler and more reliable to check but as it is optional and relatively
+        * new, not all hardware will implement it  */
+       riscv_reg_t dpc;
+       riscv_get_register(target, &dpc, GDB_REGNO_DPC);
+       const uint8_t length = 4;
+       LOG_DEBUG("dpc is 0x%" PRIx64, dpc);
+
+       /* fetch the instruction at dpc */
+       uint8_t buffer[length];
+       if (target_read_buffer(target, dpc, length, buffer) != ERROR_OK) {
+               LOG_ERROR("Failed to read instruction at dpc 0x%" PRIx64, dpc);
+               return ERROR_FAIL;
+       }
+
+       uint32_t instruction = 0;
+
+       for (int i = 0; i < length; i++) {
+               LOG_DEBUG("Next byte is %x", buffer[i]);
+               instruction += (buffer[i] << 8 * i);
+       }
+       LOG_DEBUG("Full instruction is %x", instruction);
+
+       /* find out which memory address is accessed by the instruction at dpc */
+       /* opcode is first 7 bits of the instruction */
+       uint8_t opcode = instruction & 0x7F;
+       uint32_t rs1;
+       int16_t imm;
+       riscv_reg_t mem_addr;
+
+       if (opcode == MATCH_LB || opcode == MATCH_SB) {
+               rs1 = (instruction & 0xf8000) >> 15;
+               riscv_get_register(target, &mem_addr, rs1);
+
+               if (opcode == MATCH_SB) {
+                       LOG_DEBUG("%x is store instruction", instruction);
+                       imm = ((instruction & 0xf80) >> 7) | ((instruction & 0xfe000000) >> 20);
+               } else {
+                       LOG_DEBUG("%x is load instruction", instruction);
+                       imm = (instruction & 0xfff00000) >> 20;
+               }
+               /* sign extend 12-bit imm to 16-bits */
+               if (imm & (1 << 11))
+                       imm |= 0xf000;
+               mem_addr += imm;
+               LOG_DEBUG("memory address=0x%" PRIx64, mem_addr);
+       } else {
+               LOG_DEBUG("%x is not a RV32I load or store", instruction);
+               return ERROR_FAIL;
+       }
+
+       while (wp) {
+               /*TODO support length/mask */
+               if (wp->address == mem_addr) {
+                       *hit_watchpoint = wp;
+                       LOG_DEBUG("Hit address=%" TARGET_PRIxADDR, wp->address);
+                       return ERROR_OK;
+               }
+               wp = wp->next;
+       }
+
+       /* No match found - either we hit a watchpoint caused by an instruction that
+        * this function does not yet disassemble, or we hit a breakpoint.
+        *
+        * OpenOCD will behave as if this function had never been implemented i.e.
+        * report the halt to GDB with no address information. */
+       return ERROR_FAIL;
+}
+
+
 static int oldriscv_step(struct target *target, int current, uint32_t address,
                int handle_breakpoints)
 {
@@ -650,15 +1098,12 @@ static int oldriscv_step(struct target *target, int current, uint32_t address,
        return tt->step(target, current, address, handle_breakpoints);
 }
 
-static int old_or_new_riscv_step(
-               struct target *target,
-               int current,
-               target_addr_t address,
-               int handle_breakpoints
-){
+static int old_or_new_riscv_step(struct target *target, int current,
+               target_addr_t address, int handle_breakpoints)
+{
        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);
@@ -675,14 +1120,14 @@ static int riscv_examine(struct target *target)
 
        /* Don't need to select dbus, since the first thing we do is read dtmcontrol. */
 
-       riscv_info_t *info = (riscv_info_t *) target->arch_info;
+       RISCV_INFO(info);
        uint32_t dtmcontrol = dtmcontrol_scan(target, 0);
        LOG_DEBUG("dtmcontrol=0x%x", dtmcontrol);
        info->dtm_version = get_field(dtmcontrol, DTMCONTROL_VERSION);
        LOG_DEBUG("  version=0x%x", info->dtm_version);
 
        struct target_type *tt = get_target_type(target);
-       if (tt == NULL)
+       if (!tt)
                return ERROR_FAIL;
 
        int result = tt->init_target(info->cmd_ctx, target);
@@ -701,92 +1146,611 @@ 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);
 }
 
-static int old_or_new_riscv_halt(struct target *target)
+int riscv_select_current_hart(struct target *target)
 {
-       RISCV_INFO(r);
-       if (r->is_halted == NULL)
-               return oldriscv_halt(target);
-       else
-               return riscv_openocd_halt(target);
+       return riscv_set_current_hartid(target, target->coreid);
 }
 
-static int riscv_assert_reset(struct target *target)
+int halt_prep(struct target *target)
 {
-       struct target_type *tt = get_target_type(target);
-       return tt->assert_reset(target);
+       RISCV_INFO(r);
+
+       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;
+               r->prepped = true;
+       }
+
+       return ERROR_OK;
 }
 
-static int riscv_deassert_reset(struct target *target)
+int riscv_halt_go_all_harts(struct target *target)
 {
-       LOG_DEBUG("RISCV DEASSERT RESET");
+       RISCV_INFO(r);
+
+       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;
+       }
+
+       riscv_invalidate_register_cache(target);
+
+       return ERROR_OK;
+}
+
+int halt_go(struct target *target)
+{
+       riscv_info_t *r = riscv_info(target);
+       int result;
+       if (!r->is_halted) {
+               struct target_type *tt = get_target_type(target);
+               result = tt->halt(target);
+       } else {
+               result = riscv_halt_go_all_harts(target);
+       }
+       target->state = TARGET_HALTED;
+       if (target->debug_reason == DBG_REASON_NOTHALTED)
+               target->debug_reason = DBG_REASON_DBGRQ;
+
+       return result;
+}
+
+static int halt_finish(struct target *target)
+{
+       return target_call_event_callbacks(target, TARGET_EVENT_HALTED);
+}
+
+int riscv_halt(struct target *target)
+{
+       RISCV_INFO(r);
+
+       if (!r->is_halted) {
+               struct target_type *tt = get_target_type(target);
+               return tt->halt(target);
+       }
+
+       LOG_DEBUG("[%d] halting all harts", target->coreid);
+
+       int result = ERROR_OK;
+       if (target->smp) {
+               for (struct target_list *tlist = target->head; tlist; tlist = tlist->next) {
+                       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) {
+                       struct target *t = tlist->target;
+                       riscv_info_t *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) {
+                       struct target *t = tlist->target;
+                       if (halt_finish(t) != ERROR_OK)
+                               return ERROR_FAIL;
+               }
+
+       } else {
+               if (halt_prep(target) != ERROR_OK)
+                       result = ERROR_FAIL;
+               if (halt_go(target) != ERROR_OK)
+                       result = ERROR_FAIL;
+               if (halt_finish(target) != ERROR_OK)
+                       return ERROR_FAIL;
+       }
+
+       return result;
+}
+
+static int riscv_assert_reset(struct target *target)
+{
+       LOG_DEBUG("[%d]", target->coreid);
+       struct target_type *tt = get_target_type(target);
+       riscv_invalidate_register_cache(target);
+       return tt->assert_reset(target);
+}
+
+static int riscv_deassert_reset(struct target *target)
+{
+       LOG_DEBUG("[%d]", target->coreid);
        struct target_type *tt = get_target_type(target);
        return tt->deassert_reset(target);
 }
 
+int riscv_resume_prep_all_harts(struct target *target)
+{
+       RISCV_INFO(r);
 
-static int oldriscv_resume(struct target *target, int current, uint32_t address,
-               int handle_breakpoints, int debug_execution)
+       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;
+       } else {
+               LOG_DEBUG("[%s] hart requested resume, but was already resumed",
+                               target_name(target));
+       }
+
+       LOG_DEBUG("[%s] mark as prepped", target_name(target));
+       r->prepped = true;
+
+       return ERROR_OK;
+}
+
+/* state must be riscv_reg_t state[RISCV_MAX_HWBPS] = {0}; */
+static int disable_triggers(struct target *target, riscv_reg_t *state)
 {
-       struct target_type *tt = get_target_type(target);
-       return tt->resume(target, current, address, handle_breakpoints,
-                       debug_execution);
+       RISCV_INFO(r);
+
+       LOG_DEBUG("deal with triggers");
+
+       if (riscv_enumerate_triggers(target) != ERROR_OK)
+               return ERROR_FAIL;
+
+       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 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;
+                       if (riscv_get_register(target, &tdata1, GDB_REGNO_TDATA1) != ERROR_OK)
+                               return ERROR_FAIL;
+                       if (tdata1 & MCONTROL_DMODE(riscv_xlen(target))) {
+                               state[t] = tdata1;
+                               if (riscv_set_register(target, GDB_REGNO_TDATA1, 0) != ERROR_OK)
+                                       return ERROR_FAIL;
+                       }
+               }
+               if (riscv_set_register(target, GDB_REGNO_TSELECT, tselect) != ERROR_OK)
+                       return ERROR_FAIL;
+
+       } else {
+               /* Just go through the triggers we manage. */
+               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) {
+                               if (riscv_remove_watchpoint(target, watchpoint) != ERROR_OK)
+                                       return ERROR_FAIL;
+                       }
+                       watchpoint = watchpoint->next;
+                       i++;
+               }
+       }
+
+       return ERROR_OK;
+}
+
+static int enable_triggers(struct target *target, riscv_reg_t *state)
+{
+       RISCV_INFO(r);
+
+       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 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;
+                               if (riscv_set_register(target, GDB_REGNO_TDATA1, state[t]) != ERROR_OK)
+                                       return ERROR_FAIL;
+                       }
+               }
+               if (riscv_set_register(target, GDB_REGNO_TSELECT, tselect) != ERROR_OK)
+                       return ERROR_FAIL;
+
+       } else {
+               struct watchpoint *watchpoint = target->watchpoints;
+               int i = 0;
+               while (watchpoint) {
+                       LOG_DEBUG("watchpoint %d: cleared=%" PRId64, i, state[i]);
+                       if (state[i]) {
+                               if (riscv_add_watchpoint(target, watchpoint) != ERROR_OK)
+                                       return ERROR_FAIL;
+                       }
+                       watchpoint = watchpoint->next;
+                       i++;
+               }
+       }
+
+       return ERROR_OK;
+}
+
+/**
+ * Get everything ready to resume.
+ */
+static int resume_prep(struct target *target, int current,
+               target_addr_t address, int handle_breakpoints, int debug_execution)
+{
+       RISCV_INFO(r);
+       LOG_DEBUG("[%d]", target->coreid);
+
+       if (!current)
+               riscv_set_register(target, GDB_REGNO_PC, address);
+
+       if (target->debug_reason == DBG_REASON_WATCHPOINT) {
+               /* To be able to run off a trigger, disable all the triggers, step, and
+                * then resume as usual. */
+               riscv_reg_t trigger_state[RISCV_MAX_HWBPS] = {0};
+
+               if (disable_triggers(target, trigger_state) != ERROR_OK)
+                       return ERROR_FAIL;
+
+               if (old_or_new_riscv_step(target, true, 0, false) != ERROR_OK)
+                       return ERROR_FAIL;
+
+               if (enable_triggers(target, trigger_state) != ERROR_OK)
+                       return ERROR_FAIL;
+       }
+
+       if (r->is_halted) {
+               if (riscv_resume_prep_all_harts(target) != ERROR_OK)
+                       return ERROR_FAIL;
+       }
+
+       LOG_DEBUG("[%d] mark as prepped", target->coreid);
+       r->prepped = true;
+
+       return ERROR_OK;
 }
 
-static int old_or_new_riscv_resume(
+/**
+ * Resume all the harts that have been prepped, as close to instantaneous as
+ * possible.
+ */
+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);
+       int result;
+       if (!r->is_halted) {
+               struct target_type *tt = get_target_type(target);
+               result = tt->resume(target, current, address, handle_breakpoints,
+                               debug_execution);
+       } else {
+               result = riscv_resume_go_all_harts(target);
+       }
+
+       return result;
+}
+
+static int resume_finish(struct target *target)
+{
+       register_cache_invalidate(target->reg_cache);
+
+       target->state = TARGET_RUNNING;
+       target->debug_reason = DBG_REASON_NOTHALTED;
+       return target_call_event_callbacks(target, TARGET_EVENT_RESUMED);
+}
+
+/**
+ * @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(
                struct target *target,
                int current,
                target_addr_t address,
                int handle_breakpoints,
-               int debug_execution
-){
-       RISCV_INFO(r);
+               int debug_execution,
+               bool single_hart)
+{
        LOG_DEBUG("handle_breakpoints=%d", handle_breakpoints);
-       if (r->is_halted == NULL)
-               return oldriscv_resume(target, current, address, handle_breakpoints, debug_execution);
-       else
-               return riscv_openocd_resume(target, current, address, handle_breakpoints, debug_execution);
+       int result = ERROR_OK;
+       if (target->smp && !single_hart) {
+               for (struct target_list *tlist = target->head; tlist; tlist = tlist->next) {
+                       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) {
+                       struct target *t = tlist->target;
+                       riscv_info_t *i = riscv_info(t);
+                       if (i->prepped) {
+                               if (resume_go(t, current, address, handle_breakpoints,
+                                                       debug_execution) != ERROR_OK)
+                                       result = ERROR_FAIL;
+                       }
+               }
+
+               for (struct target_list *tlist = target->head; tlist; tlist = tlist->next) {
+                       struct target *t = tlist->target;
+                       if (resume_finish(t) != ERROR_OK)
+                               return ERROR_FAIL;
+               }
+
+       } else {
+               if (resume_prep(target, current, address, handle_breakpoints,
+                                       debug_execution) != ERROR_OK)
+                       result = ERROR_FAIL;
+               if (resume_go(target, current, address, handle_breakpoints,
+                                       debug_execution) != ERROR_OK)
+                       result = ERROR_FAIL;
+               if (resume_finish(target) != ERROR_OK)
+                       return ERROR_FAIL;
+       }
+
+       return result;
 }
 
-static int riscv_select_current_hart(struct target *target)
+static int riscv_target_resume(struct target *target, int current, target_addr_t address,
+               int handle_breakpoints, int debug_execution)
+{
+       return riscv_resume(target, current, address, handle_breakpoints,
+                       debug_execution, false);
+}
+
+static int riscv_mmu(struct target *target, int *enabled)
+{
+       if (!riscv_enable_virt2phys) {
+               *enabled = 0;
+               return ERROR_OK;
+       }
+
+       /* 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) {
+               LOG_ERROR("Failed to read priv register.");
+               return ERROR_FAIL;
+       }
+
+       riscv_reg_t mstatus;
+       if (riscv_get_register(target, &mstatus, GDB_REGNO_MSTATUS) != ERROR_OK) {
+               LOG_ERROR("Failed to read mstatus register.");
+               return ERROR_FAIL;
+       }
+
+       if ((get_field(mstatus, MSTATUS_MPRV) ? get_field(mstatus, MSTATUS_MPP) : priv) == PRV_M) {
+               LOG_DEBUG("SATP/MMU ignored in Machine mode (mstatus=0x%" PRIx64 ").", mstatus);
+               *enabled = 0;
+               return ERROR_OK;
+       }
+
+       riscv_reg_t satp;
+       if (riscv_get_register(target, &satp, GDB_REGNO_SATP) != ERROR_OK) {
+               LOG_DEBUG("Couldn't read SATP.");
+               /* If we can't read SATP, then there must not be an MMU. */
+               *enabled = 0;
+               return ERROR_OK;
+       }
+
+       if (get_field(satp, RISCV_SATP_MODE(riscv_xlen(target))) == SATP_MODE_OFF) {
+               LOG_DEBUG("MMU is disabled.");
+               *enabled = 0;
+       } else {
+               LOG_DEBUG("MMU is enabled.");
+               *enabled = 1;
+       }
+
+       return ERROR_OK;
+}
+
+static int riscv_address_translate(struct target *target,
+               target_addr_t virtual, target_addr_t *physical)
 {
        RISCV_INFO(r);
-       if (r->rtos_hartid != -1 && riscv_rtos_enabled(target))
-               return riscv_set_current_hartid(target, r->rtos_hartid);
-       else
-               return riscv_set_current_hartid(target, target->coreid);
+       riscv_reg_t satp_value;
+       int mode;
+       uint64_t ppn_value;
+       target_addr_t table_address;
+       const virt2phys_info_t *info;
+       uint64_t pte = 0;
+       int i;
+
+       int result = riscv_get_register(target, &satp_value, GDB_REGNO_SATP);
+       if (result != ERROR_OK)
+               return result;
+
+       unsigned xlen = riscv_xlen(target);
+       mode = get_field(satp_value, RISCV_SATP_MODE(xlen));
+       switch (mode) {
+               case SATP_MODE_SV32:
+                       info = &sv32;
+                       break;
+               case SATP_MODE_SV39:
+                       info = &sv39;
+                       break;
+               case SATP_MODE_SV48:
+                       info = &sv48;
+                       break;
+               case SATP_MODE_OFF:
+                       LOG_ERROR("No translation or protection." \
+                                     " (satp: 0x%" PRIx64 ")", satp_value);
+                       return ERROR_FAIL;
+               default:
+                       LOG_ERROR("The translation mode is not supported." \
+                                     " (satp: 0x%" PRIx64 ")", satp_value);
+                       return ERROR_FAIL;
+       }
+       LOG_DEBUG("virtual=0x%" TARGET_PRIxADDR "; mode=%s", virtual, info->name);
+
+       /* verify bits xlen-1:va_bits-1 are all equal */
+       target_addr_t mask = ((target_addr_t)1 << (xlen - (info->va_bits - 1))) - 1;
+       target_addr_t masked_msbs = (virtual >> (info->va_bits - 1)) & mask;
+       if (masked_msbs != 0 && masked_msbs != mask) {
+               LOG_ERROR("Virtual address 0x%" TARGET_PRIxADDR " is not sign-extended "
+                               "for %s mode.", virtual, info->name);
+               return ERROR_FAIL;
+       }
+
+       ppn_value = get_field(satp_value, RISCV_SATP_PPN(xlen));
+       table_address = ppn_value << RISCV_PGSHIFT;
+       i = info->level - 1;
+       while (i >= 0) {
+               uint64_t vpn = virtual >> info->vpn_shift[i];
+               vpn &= info->vpn_mask[i];
+               target_addr_t pte_address = table_address +
+                                                                       (vpn << info->pte_shift);
+               uint8_t buffer[8];
+               assert(info->pte_shift <= 3);
+               int retval = r->read_memory(target, pte_address,
+                               4, (1 << info->pte_shift) / 4, buffer, 4);
+               if (retval != ERROR_OK)
+                       return ERROR_FAIL;
+
+               if (info->pte_shift == 2)
+                       pte = buf_get_u32(buffer, 0, 32);
+               else
+                       pte = buf_get_u64(buffer, 0, 64);
+
+               LOG_DEBUG("i=%d; PTE @0x%" TARGET_PRIxADDR " = 0x%" PRIx64, i,
+                               pte_address, pte);
+
+               if (!(pte & PTE_V) || (!(pte & PTE_R) && (pte & PTE_W)))
+                       return ERROR_FAIL;
+
+               if ((pte & PTE_R) || (pte & PTE_X)) /* Found leaf PTE. */
+                       break;
+
+               i--;
+               if (i < 0)
+                       break;
+               ppn_value = pte >> PTE_PPN_SHIFT;
+               table_address = ppn_value << RISCV_PGSHIFT;
+       }
+
+       if (i < 0) {
+               LOG_ERROR("Couldn't find the PTE.");
+               return ERROR_FAIL;
+       }
+
+       /* Make sure to clear out the high bits that may be set. */
+       *physical = virtual & (((target_addr_t)1 << info->va_bits) - 1);
+
+       while (i < info->level) {
+               ppn_value = pte >> info->pte_ppn_shift[i];
+               ppn_value &= info->pte_ppn_mask[i];
+               *physical &= ~(((target_addr_t)info->pa_ppn_mask[i]) <<
+                               info->pa_ppn_shift[i]);
+               *physical |= (ppn_value << info->pa_ppn_shift[i]);
+               i++;
+       }
+       LOG_DEBUG("0x%" TARGET_PRIxADDR " -> 0x%" TARGET_PRIxADDR, virtual,
+                       *physical);
+
+       return ERROR_OK;
+}
+
+static int riscv_virt2phys(struct target *target, target_addr_t virtual, target_addr_t *physical)
+{
+       int enabled;
+       if (riscv_mmu(target, &enabled) == ERROR_OK) {
+               if (!enabled)
+                       return ERROR_FAIL;
+
+               if (riscv_address_translate(target, virtual, physical) == ERROR_OK)
+                       return ERROR_OK;
+       }
+
+       return ERROR_FAIL;
+}
+
+static int riscv_read_phys_memory(struct target *target, target_addr_t phys_address,
+                       uint32_t size, uint32_t count, uint8_t *buffer)
+{
+       RISCV_INFO(r);
+       if (riscv_select_current_hart(target) != ERROR_OK)
+               return ERROR_FAIL;
+       return r->read_memory(target, phys_address, size, count, buffer, size);
 }
 
 static int riscv_read_memory(struct target *target, target_addr_t address,
                uint32_t size, uint32_t count, uint8_t *buffer)
+{
+       if (count == 0) {
+               LOG_WARNING("0-length read from 0x%" TARGET_PRIxADDR, address);
+               return ERROR_OK;
+       }
+
+       if (riscv_select_current_hart(target) != ERROR_OK)
+               return ERROR_FAIL;
+
+       target_addr_t physical_addr;
+       if (target->type->virt2phys(target, address, &physical_addr) == ERROR_OK)
+               address = physical_addr;
+
+       RISCV_INFO(r);
+       return r->read_memory(target, address, size, count, buffer, size);
+}
+
+static int riscv_write_phys_memory(struct target *target, target_addr_t phys_address,
+                       uint32_t size, uint32_t count, const uint8_t *buffer)
 {
        if (riscv_select_current_hart(target) != ERROR_OK)
                return ERROR_FAIL;
        struct target_type *tt = get_target_type(target);
-       return tt->read_memory(target, address, size, count, buffer);
+       return tt->write_memory(target, phys_address, size, count, buffer);
 }
 
 static int riscv_write_memory(struct target *target, target_addr_t address,
                uint32_t size, uint32_t count, const uint8_t *buffer)
 {
+       if (count == 0) {
+               LOG_WARNING("0-length write to 0x%" TARGET_PRIxADDR, address);
+               return ERROR_OK;
+       }
+
        if (riscv_select_current_hart(target) != ERROR_OK)
                return ERROR_FAIL;
+
+       target_addr_t physical_addr;
+       if (target->type->virt2phys(target, address, &physical_addr) == ERROR_OK)
+               address = physical_addr;
+
        struct target_type *tt = get_target_type(target);
        return tt->write_memory(target, address, size, count, buffer);
 }
 
-static int riscv_get_gdb_reg_list(struct target *target,
+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)
+               enum target_register_class reg_class, bool read)
 {
        RISCV_INFO(r);
-       LOG_DEBUG("reg_class=%d", reg_class);
-       LOG_DEBUG("rtos_hartid=%d current_hartid=%d", r->rtos_hartid, r->current_hartid);
+       LOG_DEBUG("current_hartid=%d, reg_class=%d, read=%d",
+                       r->current_hartid, reg_class, read);
 
        if (!target->reg_cache) {
                LOG_ERROR("Target not initialized. Return ERROR_FAIL.");
@@ -798,10 +1762,10 @@ static int riscv_get_gdb_reg_list(struct target *target,
 
        switch (reg_class) {
                case REG_CLASS_GENERAL:
-                       *reg_list_size = 32;
+                       *reg_list_size = 33;
                        break;
                case REG_CLASS_ALL:
-                       *reg_list_size = GDB_REGNO_COUNT;
+                       *reg_list_size = target->reg_cache->num_regs;
                        break;
                default:
                        LOG_ERROR("Unsupported reg_class: %d", reg_class);
@@ -816,11 +1780,34 @@ static int riscv_get_gdb_reg_list(struct target *target,
                assert(!target->reg_cache->reg_list[i].valid ||
                                target->reg_cache->reg_list[i].size > 0);
                (*reg_list)[i] = &target->reg_cache->reg_list[i];
+               if (read &&
+                               target->reg_cache->reg_list[i].exist &&
+                               !target->reg_cache->reg_list[i].valid) {
+                       if (target->reg_cache->reg_list[i].type->get(
+                                               &target->reg_cache->reg_list[i]) != ERROR_OK)
+                               return ERROR_FAIL;
+               }
        }
 
        return ERROR_OK;
 }
 
+static int riscv_get_gdb_reg_list_noread(struct target *target,
+               struct reg **reg_list[], int *reg_list_size,
+               enum target_register_class reg_class)
+{
+       return riscv_get_gdb_reg_list_internal(target, reg_list, reg_list_size,
+                       reg_class, false);
+}
+
+static int riscv_get_gdb_reg_list(struct target *target,
+               struct reg **reg_list[], int *reg_list_size,
+               enum target_register_class reg_class)
+{
+       return riscv_get_gdb_reg_list_internal(target, reg_list, reg_list_size,
+                       reg_class, true);
+}
+
 static int riscv_arch_state(struct target *target)
 {
        struct target_type *tt = get_target_type(target);
@@ -833,7 +1820,7 @@ static int riscv_run_algorithm(struct target *target, int num_mem_params,
                struct reg_param *reg_params, target_addr_t entry_point,
                target_addr_t exit_point, int timeout_ms, void *arch_info)
 {
-       riscv_info_t *info = (riscv_info_t *) target->arch_info;
+       RISCV_INFO(info);
 
        if (num_mem_params > 0) {
                LOG_ERROR("Memory parameters are not supported for RISC-V algorithms.");
@@ -846,18 +1833,16 @@ 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);
+       LOG_DEBUG("saved_pc=0x%" PRIx64, saved_pc);
 
        uint64_t saved_regs[32];
        for (int i = 0; i < num_reg_params; i++) {
-               if (mem_params[i].direction == PARAM_IN)
-                       continue;
-
                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;
@@ -877,18 +1862,21 @@ static int riscv_run_algorithm(struct target *target, int num_mem_params,
                if (r->type->get(r) != ERROR_OK)
                        return ERROR_FAIL;
                saved_regs[r->number] = buf_get_u64(r->value, 0, r->size);
-               if (r->type->set(r, reg_params[i].value) != ERROR_OK)
-                       return ERROR_FAIL;
+
+               if (reg_params[i].direction == PARAM_OUT || reg_params[i].direction == PARAM_IN_OUT) {
+                       if (r->type->set(r, reg_params[i].value) != ERROR_OK)
+                               return ERROR_FAIL;
+               }
        }
 
 
        /* Disable Interrupts before attempting to run the algorithm. */
        uint64_t current_mstatus;
-       uint8_t mstatus_bytes[8];
+       uint8_t mstatus_bytes[8] = { 0 };
 
        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;
@@ -897,14 +1885,14 @@ 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);
 
        /* Run algorithm */
        LOG_DEBUG("resume at 0x%" TARGET_PRIxADDR, entry_point);
-       if (oldriscv_resume(target, 0, entry_point, 0, 0) != ERROR_OK)
+       if (riscv_resume(target, 0, entry_point, 0, 0, true) != ERROR_OK)
                return ERROR_FAIL;
 
        int64_t start = timeval_ms();
@@ -912,11 +1900,28 @@ static int riscv_run_algorithm(struct target *target, int num_mem_params,
                LOG_DEBUG("poll()");
                int64_t now = timeval_ms();
                if (now - start > timeout_ms) {
-                       LOG_ERROR("Algorithm timed out after %d ms.", timeout_ms);
-                       LOG_ERROR("  now   = 0x%08x", (uint32_t) now);
-                       LOG_ERROR("  start = 0x%08x", (uint32_t) start);
-                       oldriscv_halt(target);
+                       LOG_ERROR("Algorithm timed out after %" PRId64 " ms.", now - start);
+                       riscv_halt(target);
                        old_or_new_riscv_poll(target);
+                       enum gdb_regno regnums[] = {
+                               GDB_REGNO_RA, GDB_REGNO_SP, GDB_REGNO_GP, GDB_REGNO_TP,
+                               GDB_REGNO_T0, GDB_REGNO_T1, GDB_REGNO_T2, GDB_REGNO_FP,
+                               GDB_REGNO_S1, GDB_REGNO_A0, GDB_REGNO_A1, GDB_REGNO_A2,
+                               GDB_REGNO_A3, GDB_REGNO_A4, GDB_REGNO_A5, GDB_REGNO_A6,
+                               GDB_REGNO_A7, GDB_REGNO_S2, GDB_REGNO_S3, GDB_REGNO_S4,
+                               GDB_REGNO_S5, GDB_REGNO_S6, GDB_REGNO_S7, GDB_REGNO_S8,
+                               GDB_REGNO_S9, GDB_REGNO_S10, GDB_REGNO_S11, GDB_REGNO_T3,
+                               GDB_REGNO_T4, GDB_REGNO_T5, GDB_REGNO_T6,
+                               GDB_REGNO_PC,
+                               GDB_REGNO_MSTATUS, GDB_REGNO_MEPC, GDB_REGNO_MCAUSE,
+                       };
+                       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)
+                                       break;
+                               LOG_ERROR("%s = 0x%" PRIx64, gdb_regno_name(regno), reg_value);
+                       }
                        return ERROR_TARGET_TIMEOUT;
                }
 
@@ -925,10 +1930,14 @@ static int riscv_run_algorithm(struct target *target, int num_mem_params,
                        return result;
        }
 
+       /* The current hart id might have been changed in poll(). */
+       if (riscv_select_current_hart(target) != ERROR_OK)
+               return ERROR_FAIL;
+
        if (reg_pc->type->get(reg_pc) != ERROR_OK)
                return ERROR_FAIL;
        uint64_t final_pc = buf_get_u64(reg_pc->value, 0, reg_pc->size);
-       if (final_pc != exit_point) {
+       if (exit_point && final_pc != exit_point) {
                LOG_ERROR("PC ended up at 0x%" PRIx64 " instead of 0x%"
                                TARGET_PRIxADDR, final_pc, exit_point);
                return ERROR_FAIL;
@@ -936,36 +1945,121 @@ 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];
-       buf_set_u64(buf, 0, info->xlen[0], saved_pc);
+       uint8_t buf[8] = { 0 };
+       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, false);
+                       if (r->type->get(r) != ERROR_OK) {
+                               LOG_ERROR("get(%s) failed", r->name);
+                               return ERROR_FAIL;
+                       }
+                       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]);
-               if (r->type->set(r, buf) != ERROR_OK)
+               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;
+               }
        }
 
        return ERROR_OK;
 }
 
-/* Should run code on the target to perform CRC of
-memory. Not yet implemented.
-*/
-
 static int riscv_checksum_memory(struct target *target,
                target_addr_t address, uint32_t count,
                uint32_t *checksum)
 {
-       *checksum = 0xFFFFFFFF;
-       return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+       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 ***/
@@ -994,193 +2088,222 @@ static enum riscv_poll_hart riscv_poll_hart(struct target *target, int hartid)
        } else if (target->state != TARGET_RUNNING && !halted) {
                LOG_DEBUG("  triggered running");
                target->state = TARGET_RUNNING;
+               target->debug_reason = DBG_REASON_NOTHALTED;
                return RPH_DISCOVERED_RUNNING;
        }
 
        return RPH_NO_CHANGE;
 }
 
-/*** OpenOCD Interface ***/
-int riscv_openocd_poll(struct target *target)
+int set_debug_reason(struct target *target, enum riscv_halt_reason halt_reason)
 {
-       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;
-                       }
-               }
-               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);
-
-               /* If we're here then at least one hart triggered.  That means
-                * we want to go and halt _every_ hart in the system, as that's
-                * the invariant we hold here.  Some harts might have already
-                * halted (as we're either in single-step mode or they also
-                * triggered a breakpoint), so don't attempt to halt those
-                * harts. */
-               for (int i = 0; i < riscv_count_harts(target); ++i)
-                       riscv_halt_one_hart(target, i);
-       } else {
-               enum riscv_poll_hart out = riscv_poll_hart(target,
-                               riscv_current_hartid(target));
-               if (out == RPH_NO_CHANGE || out == RPH_DISCOVERED_RUNNING)
-                       return ERROR_OK;
-               else if (out == RPH_ERROR)
+       switch (halt_reason) {
+               case RISCV_HALT_BREAKPOINT:
+                       target->debug_reason = DBG_REASON_BREAKPOINT;
+                       break;
+               case RISCV_HALT_TRIGGER:
+                       target->debug_reason = DBG_REASON_WATCHPOINT;
+                       break;
+               case RISCV_HALT_INTERRUPT:
+               case RISCV_HALT_GROUP:
+                       target->debug_reason = DBG_REASON_DBGRQ;
+                       break;
+               case RISCV_HALT_SINGLESTEP:
+                       target->debug_reason = DBG_REASON_SINGLESTEP;
+                       break;
+               case RISCV_HALT_UNKNOWN:
+                       target->debug_reason = DBG_REASON_UNDEFINED;
+                       break;
+               case RISCV_HALT_ERROR:
                        return ERROR_FAIL;
-
-               halted_hart = riscv_current_hartid(target);
-               LOG_DEBUG("  hart %d halted", halted_hart);
-       }
-
-       target->state = TARGET_HALTED;
-       switch (riscv_halt_reason(target, halted_hart)) {
-       case RISCV_HALT_BREAKPOINT:
-               target->debug_reason = DBG_REASON_BREAKPOINT;
-               break;
-       case RISCV_HALT_TRIGGER:
-               target->debug_reason = DBG_REASON_WATCHPOINT;
-               break;
-       case RISCV_HALT_INTERRUPT:
-               target->debug_reason = DBG_REASON_DBGRQ;
-               break;
-       case RISCV_HALT_SINGLESTEP:
-               target->debug_reason = DBG_REASON_SINGLESTEP;
-               break;
-       case RISCV_HALT_UNKNOWN:
-               target->debug_reason = DBG_REASON_UNDEFINED;
-               break;
-       case RISCV_HALT_ERROR:
-               return ERROR_FAIL;
        }
-
-       if (riscv_rtos_enabled(target)) {
-               target->rtos->current_threadid = halted_hart + 1;
-               target->rtos->current_thread = halted_hart + 1;
-       }
-
-       target->state = TARGET_HALTED;
-
-       if (target->debug_reason == DBG_REASON_BREAKPOINT) {
-               int retval;
-               if (riscv_semihosting(target, &retval) != 0)
-                       return retval;
-       }
-
-       target_call_event_callbacks(target, TARGET_EVENT_HALTED);
+       LOG_DEBUG("[%s] debug_reason=%d", target_name(target), target->debug_reason);
        return ERROR_OK;
 }
 
-int riscv_openocd_halt(struct target *target)
+int sample_memory(struct target *target)
 {
        RISCV_INFO(r);
 
-       LOG_DEBUG("halting all harts");
+       if (!r->sample_buf.buf || !r->sample_config.enabled)
+               return ERROR_OK;
 
-       int out = riscv_halt_all_harts(target);
-       if (out != ERROR_OK) {
-               LOG_ERROR("Unable to halt all harts");
-               return out;
+       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;
        }
 
-       register_cache_invalidate(target->reg_cache);
-       if (riscv_rtos_enabled(target)) {
-               target->rtos->current_threadid = r->rtos_hartid + 1;
-               target->rtos->current_thread = r->rtos_hartid + 1;
+       /* 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;
+                       }
+               }
        }
 
-       target->state = TARGET_HALTED;
-       target->debug_reason = DBG_REASON_DBGRQ;
-       target_call_event_callbacks(target, TARGET_EVENT_HALTED);
-       return out;
+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;
 }
 
-int riscv_openocd_resume(
-               struct target *target,
-               int current,
-               target_addr_t address,
-               int handle_breakpoints,
-               int debug_execution)
+/*** OpenOCD Interface ***/
+int riscv_openocd_poll(struct target *target)
 {
-       LOG_DEBUG("debug_reason=%d", target->debug_reason);
+       LOG_DEBUG("polling all harts");
+       int halted_hart = -1;
+
+       if (target->smp) {
+               unsigned halts_discovered = 0;
+               unsigned should_remain_halted = 0;
+               unsigned should_resume = 0;
+               unsigned i = 0;
+               for (struct target_list *list = target->head; list;
+                               list = list->next, i++) {
+                       struct target *t = list->target;
+                       riscv_info_t *r = riscv_info(t);
+                       enum riscv_poll_hart out = riscv_poll_hart(t, r->current_hartid);
+                       switch (out) {
+                       case RPH_NO_CHANGE:
+                               break;
+                       case RPH_DISCOVERED_RUNNING:
+                               t->state = TARGET_RUNNING;
+                               t->debug_reason = DBG_REASON_NOTHALTED;
+                               break;
+                       case RPH_DISCOVERED_HALTED:
+                               halts_discovered++;
+                               t->state = TARGET_HALTED;
+                               enum riscv_halt_reason halt_reason =
+                                       riscv_halt_reason(t, r->current_hartid);
+                               if (set_debug_reason(t, halt_reason) != ERROR_OK)
+                                       return ERROR_FAIL;
+
+                               if (halt_reason == RISCV_HALT_BREAKPOINT) {
+                                       int retval;
+                                       switch (riscv_semihosting(t, &retval)) {
+                                       case SEMI_NONE:
+                                       case SEMI_WAITING:
+                                               /* This hart should remain halted. */
+                                               should_remain_halted++;
+                                               break;
+                                       case SEMI_HANDLED:
+                                               /* This hart should be resumed, along with any other
+                                                        * harts that halted due to haltgroups. */
+                                               should_resume++;
+                                               break;
+                                       case SEMI_ERROR:
+                                               return retval;
+                                       }
+                               } else if (halt_reason != RISCV_HALT_GROUP) {
+                                       should_remain_halted++;
+                               }
+                               break;
 
-       if (!current)
-               riscv_set_register(target, GDB_REGNO_PC, address);
+                       case RPH_ERROR:
+                               return ERROR_FAIL;
+                       }
+               }
 
-       if (target->debug_reason == DBG_REASON_WATCHPOINT) {
-               /* To be able to run off a trigger, disable all the triggers, step, and
-                * then resume as usual. */
-               struct watchpoint *watchpoint = target->watchpoints;
-               bool trigger_temporarily_cleared[RISCV_MAX_HWBPS] = {0};
+               LOG_DEBUG("should_remain_halted=%d, should_resume=%d",
+                                 should_remain_halted, should_resume);
+               if (should_remain_halted && should_resume) {
+                       LOG_WARNING("%d harts should remain halted, and %d should resume.",
+                                               should_remain_halted, should_resume);
+               }
+               if (should_remain_halted) {
+                       LOG_DEBUG("halt all");
+                       riscv_halt(target);
+               } else if (should_resume) {
+                       LOG_DEBUG("resume all");
+                       riscv_resume(target, true, 0, 0, 0, false);
+               }
 
-               int i = 0;
-               int result = ERROR_OK;
-               while (watchpoint && result == ERROR_OK) {
-                       LOG_DEBUG("watchpoint %d: set=%d", i, watchpoint->set);
-                       trigger_temporarily_cleared[i] = watchpoint->set;
-                       if (watchpoint->set)
-                               result = riscv_remove_watchpoint(target, watchpoint);
-                       watchpoint = watchpoint->next;
-                       i++;
+               /* Sample memory if any target is running. */
+               for (struct target_list *list = target->head; list;
+                               list = list->next, i++) {
+                       struct target *t = list->target;
+                       if (t->state == TARGET_RUNNING) {
+                               sample_memory(target);
+                               break;
+                       }
                }
 
-               if (result == ERROR_OK)
-                       result = riscv_step_rtos_hart(target);
+               return ERROR_OK;
 
-               watchpoint = target->watchpoints;
-               i = 0;
-               while (watchpoint) {
-                       LOG_DEBUG("watchpoint %d: cleared=%d", i, trigger_temporarily_cleared[i]);
-                       if (trigger_temporarily_cleared[i]) {
-                               if (result == ERROR_OK)
-                                       result = riscv_add_watchpoint(target, watchpoint);
-                               else
-                                       riscv_add_watchpoint(target, watchpoint);
-                       }
-                       watchpoint = watchpoint->next;
-                       i++;
+       } else {
+               enum riscv_poll_hart out = riscv_poll_hart(target,
+                               riscv_current_hartid(target));
+               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) {
+                       return ERROR_FAIL;
                }
 
-               if (result != ERROR_OK)
-                       return result;
+               halted_hart = riscv_current_hartid(target);
+               LOG_DEBUG("  hart %d halted", halted_hart);
+
+               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->state = TARGET_HALTED;
        }
 
-       int out = riscv_resume_all_harts(target);
-       if (out != ERROR_OK) {
-               LOG_ERROR("unable to resume all harts");
-               return out;
+       if (target->debug_reason == DBG_REASON_BREAKPOINT) {
+               int retval;
+               switch (riscv_semihosting(target, &retval)) {
+                       case SEMI_NONE:
+                       case SEMI_WAITING:
+                               target_call_event_callbacks(target, TARGET_EVENT_HALTED);
+                               break;
+                       case SEMI_HANDLED:
+                               if (riscv_resume(target, true, 0, 0, 0, false) != ERROR_OK)
+                                       return ERROR_FAIL;
+                               break;
+                       case SEMI_ERROR:
+                               return retval;
+               }
+       } else {
+               target_call_event_callbacks(target, TARGET_EVENT_HALTED);
        }
 
-       register_cache_invalidate(target->reg_cache);
-       target->state = TARGET_RUNNING;
-       target_call_event_callbacks(target, TARGET_EVENT_RESUMED);
-       return out;
+       return ERROR_OK;
 }
 
-int riscv_openocd_step(
-               struct target *target,
-               int current,
-               target_addr_t address,
-               int handle_breakpoints
-) {
+int riscv_openocd_step(struct target *target, int current,
+               target_addr_t address, int handle_breakpoints)
+{
        LOG_DEBUG("stepping rtos hart");
 
        if (!current)
                riscv_set_register(target, GDB_REGNO_PC, address);
 
+       riscv_reg_t trigger_state[RISCV_MAX_HWBPS] = {0};
+       if (disable_triggers(target, trigger_state) != ERROR_OK)
+               return ERROR_FAIL;
+
        int out = riscv_step_rtos_hart(target);
        if (out != ERROR_OK) {
                LOG_ERROR("unable to step rtos hart");
@@ -1188,6 +2311,10 @@ int riscv_openocd_step(
        }
 
        register_cache_invalidate(target->reg_cache);
+
+       if (enable_triggers(target, trigger_state) != ERROR_OK)
+               return ERROR_FAIL;
+
        target->state = TARGET_RUNNING;
        target_call_event_callbacks(target, TARGET_EVENT_RESUMED);
        target->state = TARGET_HALTED;
@@ -1232,100 +2359,271 @@ COMMAND_HANDLER(riscv_set_reset_timeout_sec)
 
 COMMAND_HANDLER(riscv_set_prefer_sba)
 {
+       struct target *target = get_current_target(CMD_CTX);
+       RISCV_INFO(r);
+       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;
        }
-       COMMAND_PARSE_ON_OFF(CMD_ARGV[0], riscv_prefer_sba);
+       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 {
+               /* 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;
 }
 
-void parse_error(const char *string, char c, unsigned position)
+COMMAND_HANDLER(riscv_set_mem_access)
 {
-       char buf[position+2];
-       for (unsigned i = 0; i < position; i++)
-               buf[i] = ' ';
-       buf[position] = '^';
-       buf[position + 1] = 0;
+       struct target *target = get_current_target(CMD_CTX);
+       RISCV_INFO(r);
+       int progbuf_cnt = 0;
+       int sysbus_cnt = 0;
+       int abstract_cnt = 0;
+
+       if (CMD_ARGC < 1 || CMD_ARGC > RISCV_NUM_MEM_ACCESS_METHODS) {
+               LOG_ERROR("Command takes 1 to %d parameters", RISCV_NUM_MEM_ACCESS_METHODS);
+               return ERROR_COMMAND_SYNTAX_ERROR;
+       }
+
+       /* Check argument validity */
+       for (unsigned int i = 0; i < CMD_ARGC; i++) {
+               if (strcmp("progbuf", CMD_ARGV[i]) == 0) {
+                       progbuf_cnt++;
+               } else if (strcmp("sysbus", CMD_ARGV[i]) == 0) {
+                       sysbus_cnt++;
+               } else if (strcmp("abstract", CMD_ARGV[i]) == 0) {
+                       abstract_cnt++;
+               } else {
+                       LOG_ERROR("Unknown argument '%s'. "
+                               "Must be one of: 'progbuf', 'sysbus' or 'abstract'.", CMD_ARGV[i]);
+                       return ERROR_COMMAND_SYNTAX_ERROR;
+               }
+       }
+       if (progbuf_cnt > 1 || sysbus_cnt > 1 || abstract_cnt > 1) {
+               LOG_ERROR("Syntax error - duplicate arguments to `riscv set_mem_access`.");
+               return ERROR_COMMAND_SYNTAX_ERROR;
+       }
+
+       /* 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;
+       }
 
-       LOG_ERROR("Parse error at character %c in:", c);
-       LOG_ERROR("%s", string);
-       LOG_ERROR("%s", buf);
+       /* 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_expose_csrs)
+COMMAND_HANDLER(riscv_set_enable_virtual)
 {
        if (CMD_ARGC != 1) {
                LOG_ERROR("Command takes exactly 1 parameter");
                return ERROR_COMMAND_SYNTAX_ERROR;
        }
+       COMMAND_PARSE_ON_OFF(CMD_ARGV[0], riscv_enable_virtual);
+       return ERROR_OK;
+}
+
+int parse_ranges(struct list_head *ranges, const char *tcl_arg, const char *reg_type, unsigned int max_val)
+{
+       char *args = strdup(tcl_arg);
+       if (!args)
+               return ERROR_FAIL;
 
-       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 || CMD_ARGV[0][i-1]; i++) {
-                       char c = CMD_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) {
-                                               expose_csr[range].low = low;
-                                               expose_csr[range].high = low;
-                                       }
-                                       low = 0;
-                                       range++;
-                               } else {
-                                       parse_error(CMD_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) {
-                                               expose_csr[range].low = low;
-                                               expose_csr[range].high = high;
-                                       }
-                                       low = 0;
-                                       high = 0;
-                                       range++;
-                               } else {
-                                       parse_error(CMD_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) {
-                       if (expose_csr)
-                               free(expose_csr);
-                       expose_csr = calloc(range + 2, sizeof(*expose_csr));
-               } else {
-                       expose_csr[range].low = 1;
-                       expose_csr[range].high = 0;
+               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;
+                       }
+               }
+
+               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 == 0) {
+               LOG_ERROR("Command expects parameters");
+               return ERROR_COMMAND_SYNTAX_ERROR;
+       }
+
+       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 == 0) {
+               LOG_ERROR("Command expects parameters");
+               return ERROR_COMMAND_SYNTAX_ERROR;
+       }
+
+       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;
        }
 
@@ -1343,9 +2641,9 @@ COMMAND_HANDLER(riscv_authdata_read)
 
        if (r->authdata_read) {
                uint32_t value;
-               if (r->authdata_read(target, &value) != ERROR_OK)
+               if (r->authdata_read(target, &value, index) != ERROR_OK)
                        return ERROR_FAIL;
-               command_print(CMD_CTX, "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.");
@@ -1355,19 +2653,26 @@ COMMAND_HANDLER(riscv_authdata_read)
 
 COMMAND_HANDLER(riscv_authdata_write)
 {
-       if (CMD_ARGC != 1) {
-               LOG_ERROR("Command takes exactly 1 argument");
+       uint32_t value;
+       unsigned int index = 0;
+
+       if (CMD_ARGC == 0) {
+               /* nop */
+       } else if (CMD_ARGC == 1) {
+               COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], value);
+       } else if (CMD_ARGC == 2) {
+               COMMAND_PARSE_NUMBER(uint, CMD_ARGV[0], index);
+               COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], value);
+       } else {
+               LOG_ERROR("Command takes at most 2 arguments");
                return ERROR_COMMAND_SYNTAX_ERROR;
        }
 
        struct target *target = get_current_target(CMD_CTX);
        RISCV_INFO(r);
 
-       uint32_t value;
-       COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], value);
-
        if (r->authdata_write) {
-               return r->authdata_write(target, value);
+               return r->authdata_write(target, value, index);
        } else {
                LOG_ERROR("authdata_write is not implemented for this target.");
                return ERROR_FAIL;
@@ -1398,7 +2703,7 @@ COMMAND_HANDLER(riscv_dmi_read)
                COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], address);
                if (r->dmi_read(target, &value, address) != ERROR_OK)
                        return ERROR_FAIL;
-               command_print(CMD_CTX, "0x%" PRIx32, value);
+               command_print(CMD, "0x%" PRIx32, value);
                return ERROR_OK;
        } else {
                LOG_ERROR("dmi_read is not implemented for this target.");
@@ -1429,74 +2734,367 @@ COMMAND_HANDLER(riscv_dmi_write)
        }
 }
 
+COMMAND_HANDLER(riscv_test_sba_config_reg)
+{
+       if (CMD_ARGC != 4) {
+               LOG_ERROR("Command takes exactly 4 arguments");
+               return ERROR_COMMAND_SYNTAX_ERROR;
+       }
+
+       struct target *target = get_current_target(CMD_CTX);
+       RISCV_INFO(r);
+
+       target_addr_t legal_address;
+       uint32_t num_words;
+       target_addr_t illegal_address;
+       bool run_sbbusyerror_test;
+
+       COMMAND_PARSE_NUMBER(target_addr, CMD_ARGV[0], legal_address);
+       COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], num_words);
+       COMMAND_PARSE_NUMBER(target_addr, CMD_ARGV[2], illegal_address);
+       COMMAND_PARSE_ON_OFF(CMD_ARGV[3], run_sbbusyerror_test);
+
+       if (r->test_sba_config_reg) {
+               return r->test_sba_config_reg(target, legal_address, num_words,
+                               illegal_address, run_sbbusyerror_test);
+       } else {
+               LOG_ERROR("test_sba_config_reg is not implemented for this target.");
+               return ERROR_FAIL;
+       }
+}
+
+COMMAND_HANDLER(riscv_reset_delays)
+{
+       int wait = 0;
+
+       if (CMD_ARGC > 1) {
+               LOG_ERROR("Command takes at most one argument");
+               return ERROR_COMMAND_SYNTAX_ERROR;
+       }
+
+       if (CMD_ARGC == 1)
+               COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], wait);
+
+       struct target *target = get_current_target(CMD_CTX);
+       RISCV_INFO(r);
+       r->reset_delays_wait = wait;
+       return ERROR_OK;
+}
+
+COMMAND_HANDLER(riscv_set_ir)
+{
+       if (CMD_ARGC != 2) {
+               LOG_ERROR("Command takes exactly 2 arguments");
+               return ERROR_COMMAND_SYNTAX_ERROR;
+       }
+
+       uint32_t value;
+       COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], value);
+
+       if (!strcmp(CMD_ARGV[0], "idcode"))
+               buf_set_u32(ir_idcode, 0, 32, value);
+       else if (!strcmp(CMD_ARGV[0], "dtmcs"))
+               buf_set_u32(ir_dtmcontrol, 0, 32, value);
+       else if (!strcmp(CMD_ARGV[0], "dmi"))
+               buf_set_u32(ir_dbus, 0, 32, value);
+       else
+               return ERROR_FAIL;
+
+       return ERROR_OK;
+}
+
+COMMAND_HANDLER(riscv_resume_order)
+{
+       if (CMD_ARGC > 1) {
+               LOG_ERROR("Command takes at most one argument");
+               return ERROR_COMMAND_SYNTAX_ERROR;
+       }
+
+       if (!strcmp(CMD_ARGV[0], "normal")) {
+               resume_order = RO_NORMAL;
+       } else if (!strcmp(CMD_ARGV[0], "reversed")) {
+               resume_order = RO_REVERSED;
+       } else {
+               LOG_ERROR("Unsupported resume order: %s", CMD_ARGV[0]);
+               return ERROR_FAIL;
+       }
+
+       return ERROR_OK;
+}
+
+COMMAND_HANDLER(riscv_use_bscan_tunnel)
+{
+       int irwidth = 0;
+       int tunnel_type = BSCAN_TUNNEL_NESTED_TAP;
+
+       if (CMD_ARGC > 2) {
+               LOG_ERROR("Command takes at most two arguments");
+               return ERROR_COMMAND_SYNTAX_ERROR;
+       } else if (CMD_ARGC == 1) {
+               COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], irwidth);
+       } else if (CMD_ARGC == 2) {
+               COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], irwidth);
+               COMMAND_PARSE_NUMBER(int, CMD_ARGV[1], tunnel_type);
+       }
+       if (tunnel_type == BSCAN_TUNNEL_NESTED_TAP)
+               LOG_INFO("Nested Tap based Bscan Tunnel Selected");
+       else if (tunnel_type == BSCAN_TUNNEL_DATA_REGISTER)
+               LOG_INFO("Simple Register based Bscan Tunnel Selected");
+       else
+               LOG_INFO("Invalid Tunnel type selected ! : selecting default Nested Tap Type");
+
+       bscan_tunnel_type = tunnel_type;
+       bscan_tunnel_ir_width = irwidth;
+       return ERROR_OK;
+}
+
+COMMAND_HANDLER(riscv_set_enable_virt2phys)
+{
+       if (CMD_ARGC != 1) {
+               LOG_ERROR("Command takes exactly 1 parameter");
+               return ERROR_COMMAND_SYNTAX_ERROR;
+       }
+       COMMAND_PARSE_ON_OFF(CMD_ARGV[0], riscv_enable_virt2phys);
+       return ERROR_OK;
+}
+
+COMMAND_HANDLER(riscv_set_ebreakm)
+{
+       if (CMD_ARGC != 1) {
+               LOG_ERROR("Command takes exactly 1 parameter");
+               return ERROR_COMMAND_SYNTAX_ERROR;
+       }
+       COMMAND_PARSE_ON_OFF(CMD_ARGV[0], riscv_ebreakm);
+       return ERROR_OK;
+}
+
+COMMAND_HANDLER(riscv_set_ebreaks)
+{
+       if (CMD_ARGC != 1) {
+               LOG_ERROR("Command takes exactly 1 parameter");
+               return ERROR_COMMAND_SYNTAX_ERROR;
+       }
+       COMMAND_PARSE_ON_OFF(CMD_ARGV[0], riscv_ebreaks);
+       return ERROR_OK;
+}
+
+COMMAND_HANDLER(riscv_set_ebreaku)
+{
+       if (CMD_ARGC != 1) {
+               LOG_ERROR("Command takes exactly 1 parameter");
+               return ERROR_COMMAND_SYNTAX_ERROR;
+       }
+       COMMAND_PARSE_ON_OFF(CMD_ARGV[0], riscv_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 = "info",
+               .handler = handle_info,
+               .mode = COMMAND_EXEC,
+               .usage = "",
+               .help = "Displays some information OpenOCD detected about the target."
+       },
        {
                .name = "set_command_timeout_sec",
                .handler = riscv_set_command_timeout_sec,
                .mode = COMMAND_ANY,
-               .usage = "riscv set_command_timeout_sec [sec]",
+               .usage = "[sec]",
                .help = "Set the wall-clock timeout (in seconds) for individual commands"
        },
        {
                .name = "set_reset_timeout_sec",
                .handler = riscv_set_reset_timeout_sec,
                .mode = COMMAND_ANY,
-               .usage = "riscv set_reset_timeout_sec [sec]",
+               .usage = "[sec]",
                .help = "Set the wall-clock timeout (in seconds) after reset is deasserted"
        },
        {
                .name = "set_prefer_sba",
                .handler = riscv_set_prefer_sba,
                .mode = COMMAND_ANY,
-               .usage = "riscv set_prefer_sba on|off",
+               .usage = "on|off",
                .help = "When on, prefer to use System Bus Access to access memory. "
-                       "When off, prefer to use the Program Buffer 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,
+               .mode = COMMAND_ANY,
+               .usage = "on|off",
+               .help = "When on, memory accesses are performed on physical or virtual "
+                               "memory depending on the current system configuration. "
+                               "When off (default), all memory accessses are performed on physical memory."
        },
        {
                .name = "expose_csrs",
                .handler = riscv_set_expose_csrs,
-               .mode = COMMAND_ANY,
-               .usage = "riscv expose_csrs 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`."
        },
+       {
+               .name = "expose_custom",
+               .handler = riscv_set_expose_custom,
+               .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`."
+       },
        {
                .name = "authdata_read",
                .handler = riscv_authdata_read,
+               .usage = "[index]",
                .mode = COMMAND_ANY,
-               .usage = "riscv authdata_read",
-               .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 = "riscv authdata_write 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",
                .handler = riscv_dmi_read,
                .mode = COMMAND_ANY,
-               .usage = "riscv dmi_read address",
+               .usage = "address",
                .help = "Perform a 32-bit DMI read at address, returning the value."
        },
        {
                .name = "dmi_write",
                .handler = riscv_dmi_write,
                .mode = COMMAND_ANY,
-               .usage = "riscv dmi_write address value",
+               .usage = "address value",
                .help = "Perform a 32-bit DMI write of value at address."
        },
+       {
+               .name = "test_sba_config_reg",
+               .handler = riscv_test_sba_config_reg,
+               .mode = COMMAND_ANY,
+               .usage = "legal_address num_words "
+                       "illegal_address run_sbbusyerror_test[on/off]",
+               .help = "Perform a series of tests on the SBCS register. "
+                       "Inputs are a legal, 128-byte aligned address and a number of words to "
+                       "read/write starting at that address (i.e., address range [legal address, "
+                       "legal_address+word_size*num_words) must be legally readable/writable), "
+                       "an illegal, 128-byte aligned address for error flag/handling cases, "
+                       "and whether sbbusyerror test should be run."
+       },
+       {
+               .name = "reset_delays",
+               .handler = riscv_reset_delays,
+               .mode = COMMAND_ANY,
+               .usage = "[wait]",
+               .help = "OpenOCD learns how many Run-Test/Idle cycles are required "
+                       "between scans to avoid encountering the target being busy. This "
+                       "command resets those learned values after `wait` scans. It's only "
+                       "useful for testing OpenOCD itself."
+       },
+       {
+               .name = "resume_order",
+               .handler = riscv_resume_order,
+               .mode = COMMAND_ANY,
+               .usage = "normal|reversed",
+               .help = "Choose the order that harts are resumed in when `hasel` is not "
+                       "supported. Normal order is from lowest hart index to highest. "
+                       "Reversed order is from highest hart index to lowest."
+       },
+       {
+               .name = "set_ir",
+               .handler = riscv_set_ir,
+               .mode = COMMAND_ANY,
+               .usage = "[idcode|dtmcs|dmi] value",
+               .help = "Set IR value for specified JTAG register."
+       },
+       {
+               .name = "use_bscan_tunnel",
+               .handler = riscv_use_bscan_tunnel,
+               .mode = COMMAND_ANY,
+               .usage = "value [type]",
+               .help = "Enable or disable use of a BSCAN tunnel to reach DM.  Supply "
+                       "the width of the DM transport TAP's instruction register to "
+                       "enable.  Supply a value of 0 to disable. Pass A second argument "
+                       "(optional) to indicate Bscan Tunnel Type {0:(default) NESTED_TAP , "
+                       "1: DATA_REGISTER}"
+       },
+       {
+               .name = "set_enable_virt2phys",
+               .handler = riscv_set_enable_virt2phys,
+               .mode = COMMAND_ANY,
+               .usage = "on|off",
+               .help = "When on (default), enable translation from virtual address to "
+                       "physical address."
+       },
+       {
+               .name = "set_ebreakm",
+               .handler = riscv_set_ebreakm,
+               .mode = COMMAND_ANY,
+               .usage = "on|off",
+               .help = "Control dcsr.ebreakm. When off, M-mode ebreak instructions "
+                       "don't trap to OpenOCD. Defaults to on."
+       },
+       {
+               .name = "set_ebreaks",
+               .handler = riscv_set_ebreaks,
+               .mode = COMMAND_ANY,
+               .usage = "on|off",
+               .help = "Control dcsr.ebreaks. When off, S-mode ebreak instructions "
+                       "don't trap to OpenOCD. Defaults to on."
+       },
+       {
+               .name = "set_ebreaku",
+               .handler = riscv_set_ebreaku,
+               .mode = COMMAND_ANY,
+               .usage = "on|off",
+               .help = "Control dcsr.ebreaku. When off, U-mode ebreak instructions "
+                       "don't trap to OpenOCD. Defaults to on."
+       },
        COMMAND_REGISTRATION_DONE
 };
 
-extern __COMMAND_HANDLER(handle_common_semihosting_command);
-extern __COMMAND_HANDLER(handle_common_semihosting_fileio_command);
-extern __COMMAND_HANDLER(handle_common_semihosting_resumable_exit_command);
-extern __COMMAND_HANDLER(handle_common_semihosting_cmdline);
-
 /*
  * To be noted that RISC-V targets use the same semihosting commands as
  * ARM targets.
@@ -1510,37 +3108,7 @@ extern __COMMAND_HANDLER(handle_common_semihosting_cmdline);
  * protocol, then a command like `riscv semihosting enable` will make
  * sense, but for now all semihosting commands are prefixed with `arm`.
  */
-static const struct command_registration arm_exec_command_handlers[] = {
-       {
-               "semihosting",
-               .handler = handle_common_semihosting_command,
-               .mode = COMMAND_EXEC,
-               .usage = "['enable'|'disable']",
-               .help = "activate support for semihosting operations",
-       },
-       {
-               "semihosting_cmdline",
-               .handler = handle_common_semihosting_cmdline,
-               .mode = COMMAND_EXEC,
-               .usage = "arguments",
-               .help = "command line arguments to be passed to program",
-       },
-       {
-               "semihosting_fileio",
-               .handler = handle_common_semihosting_fileio_command,
-               .mode = COMMAND_EXEC,
-               .usage = "['enable'|'disable']",
-               .help = "activate support for semihosting fileio operations",
-       },
-       {
-               "semihosting_resexit",
-               .handler = handle_common_semihosting_resumable_exit_command,
-               .mode = COMMAND_EXEC,
-               .usage = "['enable'|'disable']",
-               .help = "activate support for semihosting resumable exit",
-       },
-       COMMAND_REGISTRATION_DONE
-};
+extern const struct command_registration semihosting_common_handlers[];
 
 const struct command_registration riscv_command_handlers[] = {
        {
@@ -1555,14 +3123,28 @@ const struct command_registration riscv_command_handlers[] = {
                .mode = COMMAND_ANY,
                .help = "ARM Command Group",
                .usage = "",
-               .chain = arm_exec_command_handlers
+               .chain = semihosting_common_handlers
        },
        COMMAND_REGISTRATION_DONE
 };
 
+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,
@@ -1570,8 +3152,8 @@ struct target_type riscv_target = {
        /* poll current target status */
        .poll = old_or_new_riscv_poll,
 
-       .halt = old_or_new_riscv_halt,
-       .resume = old_or_new_riscv_resume,
+       .halt = riscv_halt,
+       .resume = riscv_target_resume,
        .step = old_or_new_riscv_step,
 
        .assert_reset = riscv_assert_reset,
@@ -1579,22 +3161,33 @@ struct target_type riscv_target = {
 
        .read_memory = riscv_read_memory,
        .write_memory = riscv_write_memory,
+       .read_phys_memory = riscv_read_phys_memory,
+       .write_phys_memory = riscv_write_phys_memory,
 
        .checksum_memory = riscv_checksum_memory,
 
+       .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,
 
        .add_breakpoint = riscv_add_breakpoint,
        .remove_breakpoint = riscv_remove_breakpoint,
 
        .add_watchpoint = riscv_add_watchpoint,
        .remove_watchpoint = riscv_remove_watchpoint,
+       .hit_watchpoint = riscv_hit_watchpoint,
 
        .arch_state = riscv_arch_state,
 
        .run_algorithm = riscv_run_algorithm,
 
-       .commands = riscv_command_handlers
+       .commands = riscv_command_handlers,
+
+       .address_bits = riscv_xlen_nonconst,
+       .data_bits = riscv_data_bits
 };
 
 /*** RISC-V Interface ***/
@@ -1605,85 +3198,49 @@ void riscv_info_init(struct target *target, riscv_info_t *r)
        r->dtm_version = 1;
        r->registers_initialized = false;
        r->current_hartid = target->coreid;
+       r->version_specific = NULL;
 
        memset(r->trigger_unique_id, 0xff, sizeof(r->trigger_unique_id));
 
-       for (size_t h = 0; h < RISCV_MAX_HARTS; ++h) {
-               r->xlen[h] = -1;
-
-               for (size_t e = 0; e < RISCV_MAX_REGISTERS; ++e)
-                       r->valid_saved_registers[h][e] = false;
-       }
-}
+       r->xlen = -1;
 
-int riscv_halt_all_harts(struct target *target)
-{
-       for (int i = 0; i < riscv_count_harts(target); ++i) {
-               if (!riscv_hart_enabled(target, i))
-                       continue;
+       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;
 
-               riscv_halt_one_hart(target, i);
-       }
+       r->mem_access_progbuf_warn = true;
+       r->mem_access_sysbus_warn = true;
+       r->mem_access_abstract_warn = true;
 
-       return ERROR_OK;
+       INIT_LIST_HEAD(&r->expose_csr);
+       INIT_LIST_HEAD(&r->expose_custom);
 }
 
-int riscv_halt_one_hart(struct target *target, int hartid)
+static int riscv_resume_go_all_harts(struct target *target)
 {
        RISCV_INFO(r);
-       LOG_DEBUG("halting hart %d", hartid);
-       if (riscv_set_current_hartid(target, hartid) != 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)) {
-               LOG_DEBUG("  hart %d requested halt, but was already halted", hartid);
-               return ERROR_OK;
-       }
-
-       return r->halt_current_hart(target);
-}
-
-int riscv_resume_all_harts(struct target *target)
-{
-       for (int i = 0; i < riscv_count_harts(target); ++i) {
-               if (!riscv_hart_enabled(target, i))
-                       continue;
-
-               riscv_resume_one_hart(target, i);
+               if (r->resume_go(target) != ERROR_OK)
+                       return ERROR_FAIL;
+       } else {
+               LOG_DEBUG("[%s] hart requested resume, but was already resumed",
+                               target_name(target));
        }
 
        riscv_invalidate_register_cache(target);
        return ERROR_OK;
 }
 
-int riscv_resume_one_hart(struct target *target, int hartid)
-{
-       RISCV_INFO(r);
-       LOG_DEBUG("resuming hart %d", hartid);
-       if (riscv_set_current_hartid(target, hartid) != ERROR_OK)
-               return ERROR_FAIL;
-       if (!riscv_is_halted(target)) {
-               LOG_DEBUG("  hart %d requested resume, but was already resumed", hartid);
-               return ERROR_OK;
-       }
-
-       r->on_resume(target);
-       return r->resume_current_hart(target);
-}
-
 int riscv_step_rtos_hart(struct target *target)
 {
        RISCV_INFO(r);
-       int hartid = r->current_hartid;
-       if (riscv_rtos_enabled(target)) {
-               hartid = r->rtos_hartid;
-               if (hartid == -1) {
-                       LOG_USER("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!");
@@ -1702,7 +3259,7 @@ int riscv_step_rtos_hart(struct target *target)
        return ERROR_OK;
 }
 
-bool riscv_supports_extension(struct target *target, int hartid, char letter)
+bool riscv_supports_extension(struct target *target, char letter)
 {
        RISCV_INFO(r);
        unsigned num;
@@ -1712,24 +3269,13 @@ bool riscv_supports_extension(struct target *target, int hartid, char letter)
                num = letter - 'A';
        else
                return false;
-       return r->misa[hartid] & (1 << num);
+       return r->misa & BIT(num);
 }
 
-int riscv_xlen(const struct target *target)
-{
-       return riscv_xlen_of_hart(target, riscv_current_hartid(target));
-}
-
-int riscv_xlen_of_hart(const struct target *target, int hartid)
+unsigned riscv_xlen(const struct target *target)
 {
        RISCV_INFO(r);
-       assert(r->xlen[hartid] != -1);
-       return r->xlen[hartid];
-}
-
-bool riscv_rtos_enabled(const struct target *target)
-{
-       return target->rtos != NULL;
+       return r->xlen;
 }
 
 int riscv_set_current_hartid(struct target *target, int hartid)
@@ -1740,26 +3286,10 @@ int riscv_set_current_hartid(struct target *target, int hartid)
 
        int previous_hartid = riscv_current_hartid(target);
        r->current_hartid = hartid;
-       assert(riscv_hart_enabled(target, hartid));
        LOG_DEBUG("setting hartid to %d, was %d", hartid, previous_hartid);
        if (r->select_current_hart(target) != ERROR_OK)
                return ERROR_FAIL;
 
-       /* This might get called during init, in which case we shouldn't be
-        * setting up the register cache. */
-       if (!target_was_examined(target))
-               return ERROR_OK;
-
-       /* Avoid invalidating the register cache all the time. */
-       if (r->registers_initialized
-                       && (!riscv_rtos_enabled(target) || (previous_hartid == hartid))
-                       && target->reg_cache->reg_list[GDB_REGNO_ZERO].size == (unsigned)riscv_xlen(target)
-                       && (!riscv_rtos_enabled(target) || (r->rtos_hartid != -1))) {
-               return ERROR_OK;
-       } else
-               LOG_DEBUG("Initializing registers: xlen=%d", riscv_xlen(target));
-
-       riscv_invalidate_register_cache(target);
        return ERROR_OK;
 }
 
@@ -1767,8 +3297,9 @@ 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 < GDB_REGNO_COUNT; ++i) {
+       for (size_t i = 0; i < target->reg_cache->num_regs; ++i) {
                struct reg *reg = &target->reg_cache->reg_list[i];
                reg->valid = false;
        }
@@ -1782,65 +3313,130 @@ 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)
+       if (!r || !r->hart_count)
                return 1;
-       return r->hart_count;
+       return r->hart_count(target);
 }
 
-bool riscv_has_register(struct target *target, int hartid, int regid)
+/**
+ * If write is true:
+ *   return true iff we are guaranteed that the register will contain exactly
+ *       the value we just wrote when it's read.
+ * If write is false:
+ *   return true iff we are guaranteed that the register will read the same
+ *       value in the future as the value we just read.
+ */
+static bool gdb_regno_cacheable(enum gdb_regno regno, bool write)
 {
-       return 1;
+       /* GPRs, FPRs, vector registers are just normal data stores. */
+       if (regno <= GDB_REGNO_XPR31 ||
+                       (regno >= GDB_REGNO_FPR0 && regno <= GDB_REGNO_FPR31) ||
+                       (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 arbitrary
+        * CSRs. */
+       switch (regno) {
+               case GDB_REGNO_DPC:
+                       return true;
+
+               case GDB_REGNO_VSTART:
+               case GDB_REGNO_VXSAT:
+               case GDB_REGNO_VXRM:
+               case GDB_REGNO_VLENB:
+               case GDB_REGNO_VL:
+               case GDB_REGNO_VTYPE:
+               case GDB_REGNO_MISA:
+               case GDB_REGNO_DCSR:
+               case GDB_REGNO_DSCRATCH0:
+               case GDB_REGNO_MSTATUS:
+               case GDB_REGNO_MEPC:
+               case GDB_REGNO_MCAUSE:
+               case GDB_REGNO_SATP:
+                       /*
+                        * WARL registers might not contain the value we just wrote, but
+                        * these ones won't spontaneously change their value either. *
+                        */
+                       return !write;
+
+               case GDB_REGNO_TSELECT: /* I think this should be above, but then it doesn't work. */
+               case GDB_REGNO_TDATA1:  /* Changes value when tselect is changed. */
+               case GDB_REGNO_TDATA2:  /* Changse value when tselect is changed. */
+               default:
+                       return false;
+       }
 }
 
 /**
  * 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);
-       return r->set_register(target, hartid, regid, value);
-}
 
-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);
+       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, '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, regid, value);
+       if (result == ERROR_OK)
+               reg->valid = gdb_regno_cacheable(regid, true);
+       else
+               reg->valid = false;
+       LOG_DEBUG("[%s] wrote 0x%" PRIx64 " to %s valid=%d",
+                         target_name(target), value, reg->name, reg->valid);
+       return result;
 }
 
-int riscv_get_register_on_hart(struct target *target, riscv_reg_t *value,
-               int hartid, enum gdb_regno regid)
+int riscv_get_register(struct target *target, riscv_reg_t *value,
+               enum gdb_regno regid)
 {
        RISCV_INFO(r);
-       int result = r->get_register(target, value, hartid, regid);
-       LOG_DEBUG("[%d] %s: %" PRIx64, hartid, gdb_regno_name(regid), *value);
+
+       keep_alive();
+
+       struct reg *reg = &target->reg_cache->reg_list[regid];
+       if (!reg->exist) {
+               LOG_DEBUG("[%s] %s does not exist.",
+                                 target_name(target), gdb_regno_name(regid));
+               return ERROR_FAIL;
+       }
+
+       if (reg && reg->valid) {
+               *value = buf_get_u64(reg->value, 0, reg->size);
+               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, 'E')) {
+               *value = 0;
+               return ERROR_OK;
+       }
+
+       int result = r->get_register(target, value, regid);
+
+       if (result == ERROR_OK)
+               reg->valid = gdb_regno_cacheable(regid, false);
+
+       LOG_DEBUG("[%s] %s: %" PRIx64, target_name(target),
+                       gdb_regno_name(regid), *value);
        return result;
 }
 
@@ -1866,7 +3462,7 @@ enum riscv_halt_reason riscv_halt_reason(struct target *target, int hartid)
 size_t riscv_debug_buffer_size(struct target *target)
 {
        RISCV_INFO(r);
-       return r->debug_buffer_size[riscv_current_hartid(target)];
+       return r->debug_buffer_size;
 }
 
 int riscv_write_debug_buffer(struct target *target, int index, riscv_insn_t insn)
@@ -1912,15 +3508,6 @@ int riscv_dmi_write_u64_bits(struct target *target)
        return r->dmi_write_u64_bits(target);
 }
 
-bool riscv_hart_enabled(struct target *target, int hartid)
-{
-       /* FIXME: Add a hart mask to the RTOS. */
-       if (riscv_rtos_enabled(target))
-               return hartid < riscv_count_harts(target);
-
-       return hartid == target->coreid;
-}
-
 /**
  * Count triggers, and initialize trigger_count for each hart.
  * trigger_count is initialized even if this function fails to discover
@@ -1937,54 +3524,57 @@ int riscv_enumerate_triggers(struct target *target)
 
        r->triggers_enumerated = true;  /* At the very least we tried. */
 
-       for (int hartid = 0; hartid < riscv_count_harts(target); ++hartid) {
-               if (!riscv_hart_enabled(target, hartid))
-                       continue;
+       riscv_reg_t tselect;
+       int result = riscv_get_register(target, &tselect, GDB_REGNO_TSELECT);
+       /* If tselect is not readable, the trigger module is likely not
+               * implemented. There are no triggers to enumerate then and no error
+               * should be thrown. */
+       if (result != ERROR_OK) {
+               LOG_DEBUG("[%s] Cannot access tselect register. "
+                               "Assuming that triggers are not implemented.", target_name(target));
+               r->trigger_count = 0;
+               return ERROR_OK;
+       }
 
-               riscv_reg_t tselect;
-               int result = riscv_get_register_on_hart(target, &tselect, hartid,
-                               GDB_REGNO_TSELECT);
+       for (unsigned int t = 0; t < RISCV_MAX_TRIGGERS; ++t) {
+               r->trigger_count = t;
+
+               /* If we can't write tselect, then this hart does not support triggers. */
+               if (riscv_set_register(target, GDB_REGNO_TSELECT, t) != ERROR_OK)
+                       break;
+               uint64_t tselect_rb;
+               result = riscv_get_register(target, &tselect_rb, GDB_REGNO_TSELECT);
+               if (result != ERROR_OK)
+                       return result;
+               /* Mask off the top bit, which is used as tdrmode in old
+                       * implementations. */
+               tselect_rb &= ~(1ULL << (riscv_xlen(target) - 1));
+               if (tselect_rb != t)
+                       break;
+               uint64_t tdata1;
+               result = riscv_get_register(target, &tdata1, GDB_REGNO_TDATA1);
                if (result != ERROR_OK)
                        return result;
 
-               for (unsigned t = 0; t < RISCV_MAX_TRIGGERS; ++t) {
-                       r->trigger_count[hartid] = t;
-
-                       riscv_set_register_on_hart(target, hartid, GDB_REGNO_TSELECT, t);
-                       uint64_t tselect_rb;
-                       result = riscv_get_register_on_hart(target, &tselect_rb, hartid,
-                                       GDB_REGNO_TSELECT);
-                       if (result != ERROR_OK)
-                               return result;
-                       /* Mask off the top bit, which is used as tdrmode in old
-                        * implementations. */
-                       tselect_rb &= ~(1ULL << (riscv_xlen(target)-1));
-                       if (tselect_rb != t)
+               int type = get_field(tdata1, MCONTROL_TYPE(riscv_xlen(target)));
+               if (type == 0)
+                       break;
+               switch (type) {
+                       case 1:
+                               /* On these older cores we don't support software using
+                                       * triggers. */
+                               riscv_set_register(target, GDB_REGNO_TDATA1, 0);
+                               break;
+                       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)));
-                       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;
 }
@@ -1996,10 +3586,68 @@ const char *gdb_regno_name(enum gdb_regno regno)
        switch (regno) {
                case GDB_REGNO_ZERO:
                        return "zero";
+               case GDB_REGNO_RA:
+                       return "ra";
+               case GDB_REGNO_SP:
+                       return "sp";
+               case GDB_REGNO_GP:
+                       return "gp";
+               case GDB_REGNO_TP:
+                       return "tp";
+               case GDB_REGNO_T0:
+                       return "t0";
+               case GDB_REGNO_T1:
+                       return "t1";
+               case GDB_REGNO_T2:
+                       return "t2";
                case GDB_REGNO_S0:
                        return "s0";
                case GDB_REGNO_S1:
                        return "s1";
+               case GDB_REGNO_A0:
+                       return "a0";
+               case GDB_REGNO_A1:
+                       return "a1";
+               case GDB_REGNO_A2:
+                       return "a2";
+               case GDB_REGNO_A3:
+                       return "a3";
+               case GDB_REGNO_A4:
+                       return "a4";
+               case GDB_REGNO_A5:
+                       return "a5";
+               case GDB_REGNO_A6:
+                       return "a6";
+               case GDB_REGNO_A7:
+                       return "a7";
+               case GDB_REGNO_S2:
+                       return "s2";
+               case GDB_REGNO_S3:
+                       return "s3";
+               case GDB_REGNO_S4:
+                       return "s4";
+               case GDB_REGNO_S5:
+                       return "s5";
+               case GDB_REGNO_S6:
+                       return "s6";
+               case GDB_REGNO_S7:
+                       return "s7";
+               case GDB_REGNO_S8:
+                       return "s8";
+               case GDB_REGNO_S9:
+                       return "s9";
+               case GDB_REGNO_S10:
+                       return "s10";
+               case GDB_REGNO_S11:
+                       return "s11";
+               case GDB_REGNO_T3:
+                       return "t3";
+               case GDB_REGNO_T4:
+                       return "t4";
+               case GDB_REGNO_T5:
+                       return "t5";
+               case GDB_REGNO_T6:
+                       return "t6";
                case GDB_REGNO_PC:
                        return "pc";
                case GDB_REGNO_FPR0:
@@ -2020,12 +3668,86 @@ const char *gdb_regno_name(enum gdb_regno regno)
                        return "dpc";
                case GDB_REGNO_DCSR:
                        return "dcsr";
-               case GDB_REGNO_DSCRATCH:
-                       return "dscratch";
+               case GDB_REGNO_DSCRATCH0:
+                       return "dscratch0";
                case GDB_REGNO_MSTATUS:
                        return "mstatus";
+               case GDB_REGNO_MEPC:
+                       return "mepc";
+               case GDB_REGNO_MCAUSE:
+                       return "mcause";
                case GDB_REGNO_PRIV:
                        return "priv";
+               case GDB_REGNO_SATP:
+                       return "satp";
+               case GDB_REGNO_VTYPE:
+                       return "vtype";
+               case GDB_REGNO_VL:
+                       return "vl";
+               case GDB_REGNO_V0:
+                       return "v0";
+               case GDB_REGNO_V1:
+                       return "v1";
+               case GDB_REGNO_V2:
+                       return "v2";
+               case GDB_REGNO_V3:
+                       return "v3";
+               case GDB_REGNO_V4:
+                       return "v4";
+               case GDB_REGNO_V5:
+                       return "v5";
+               case GDB_REGNO_V6:
+                       return "v6";
+               case GDB_REGNO_V7:
+                       return "v7";
+               case GDB_REGNO_V8:
+                       return "v8";
+               case GDB_REGNO_V9:
+                       return "v9";
+               case GDB_REGNO_V10:
+                       return "v10";
+               case GDB_REGNO_V11:
+                       return "v11";
+               case GDB_REGNO_V12:
+                       return "v12";
+               case GDB_REGNO_V13:
+                       return "v13";
+               case GDB_REGNO_V14:
+                       return "v14";
+               case GDB_REGNO_V15:
+                       return "v15";
+               case GDB_REGNO_V16:
+                       return "v16";
+               case GDB_REGNO_V17:
+                       return "v17";
+               case GDB_REGNO_V18:
+                       return "v18";
+               case GDB_REGNO_V19:
+                       return "v19";
+               case GDB_REGNO_V20:
+                       return "v20";
+               case GDB_REGNO_V21:
+                       return "v21";
+               case GDB_REGNO_V22:
+                       return "v22";
+               case GDB_REGNO_V23:
+                       return "v23";
+               case GDB_REGNO_V24:
+                       return "v24";
+               case GDB_REGNO_V25:
+                       return "v25";
+               case GDB_REGNO_V26:
+                       return "v26";
+               case GDB_REGNO_V27:
+                       return "v27";
+               case GDB_REGNO_V28:
+                       return "v28";
+               case GDB_REGNO_V29:
+                       return "v29";
+               case GDB_REGNO_V30:
+                       return "v30";
+               case GDB_REGNO_V31:
+                       return "v31";
                default:
                        if (regno <= GDB_REGNO_XPR31)
                                sprintf(buf, "x%d", regno - GDB_REGNO_ZERO);
@@ -2041,27 +3763,79 @@ const char *gdb_regno_name(enum gdb_regno regno)
 
 static int register_get(struct reg *reg)
 {
-       struct target *target = (struct target *) reg->arch_info;
-       uint64_t value;
-       int result = riscv_get_register(target, &value, reg->number);
-       if (result != ERROR_OK)
-               return result;
-       buf_set_u64(reg->value, 0, reg->size, value);
+       riscv_reg_info_t *reg_info = reg->arch_info;
+       struct target *target = reg_info->target;
+       RISCV_INFO(r);
+
+       if (reg->number >= GDB_REGNO_V0 && reg->number <= GDB_REGNO_V31) {
+               if (!r->get_register_buf) {
+                       LOG_ERROR("Reading register %s not supported on this RISC-V target.",
+                                       gdb_regno_name(reg->number));
+                       return ERROR_FAIL;
+               }
+
+               if (r->get_register_buf(target, reg->value, reg->number) != ERROR_OK)
+                       return ERROR_FAIL;
+       } else {
+               uint64_t value;
+               int result = riscv_get_register(target, &value, reg->number);
+               if (result != ERROR_OK)
+                       return result;
+               buf_set_u64(reg->value, 0, reg->size, value);
+       }
+       reg->valid = gdb_regno_cacheable(reg->number, false);
+       char *str = buf_to_hex_str(reg->value, reg->size);
+       LOG_DEBUG("[%s] read 0x%s from %s (valid=%d)", target_name(target),
+                       str, reg->name, reg->valid);
+       free(str);
        return ERROR_OK;
 }
 
 static int register_set(struct reg *reg, uint8_t *buf)
 {
-       struct target *target = (struct target *) reg->arch_info;
+       riscv_reg_info_t *reg_info = reg->arch_info;
+       struct target *target = reg_info->target;
+       RISCV_INFO(r);
 
-       uint64_t value = buf_get_u64(buf, 0, reg->size);
+       char *str = buf_to_hex_str(buf, reg->size);
+       LOG_DEBUG("[%s] write 0x%s to %s (valid=%d)", target_name(target),
+                       str, reg->name, reg->valid);
+       free(str);
 
-       LOG_DEBUG("write 0x%" PRIx64 " to %s", value, reg->name);
-       struct reg *r = &target->reg_cache->reg_list[reg->number];
-       r->valid = true;
-       memcpy(r->value, buf, (r->size + 7) / 8);
+       /* 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);
+
+       if (reg->number == GDB_REGNO_TDATA1 ||
+                       reg->number == GDB_REGNO_TDATA2) {
+               r->manual_hwbp_set = true;
+               /* When enumerating triggers, we clear any triggers with DMODE set,
+                * assuming they were left over from a previous debug session. So make
+                * sure that is done before a user might be setting their own triggers.
+                */
+               if (riscv_enumerate_triggers(target) != ERROR_OK)
+                       return ERROR_FAIL;
+       }
+
+       if (reg->number >= GDB_REGNO_V0 && reg->number <= GDB_REGNO_V31) {
+               if (!r->set_register_buf) {
+                       LOG_ERROR("Writing register %s not supported on this RISC-V target.",
+                                       gdb_regno_name(reg->number));
+                       return ERROR_FAIL;
+               }
+
+               if (r->set_register_buf(target, reg->number, reg->value) != ERROR_OK)
+                       return ERROR_FAIL;
+       } else {
+               uint64_t value = buf_get_u64(buf, 0, reg->size);
+               if (riscv_set_register(target, reg->number, value) != ERROR_OK)
+                       return ERROR_FAIL;
+       }
 
-       riscv_set_register(target, reg->number, value);
        return ERROR_OK;
 }
 
@@ -2084,22 +3858,34 @@ int riscv_init_registers(struct target *target)
 {
        RISCV_INFO(info);
 
-       if (target->reg_cache) {
-               if (target->reg_cache->reg_list)
-                       free(target->reg_cache->reg_list);
-               free(target->reg_cache);
-       }
+       riscv_free_registers(target);
 
        target->reg_cache = calloc(1, sizeof(*target->reg_cache));
+       if (!target->reg_cache)
+               return ERROR_FAIL;
        target->reg_cache->name = "RISC-V Registers";
        target->reg_cache->num_regs = GDB_REGNO_COUNT;
 
-       target->reg_cache->reg_list = calloc(GDB_REGNO_COUNT, sizeof(struct reg));
+       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",
+                       target->reg_cache->num_regs);
+
+       target->reg_cache->reg_list =
+               calloc(target->reg_cache->num_regs, sizeof(struct reg));
+       if (!target->reg_cache->reg_list)
+               return ERROR_FAIL;
 
        const unsigned int max_reg_name_len = 12;
-       if (info->reg_names)
-               free(info->reg_names);
-       info->reg_names = calloc(1, GDB_REGNO_COUNT * max_reg_name_len);
+       free(info->reg_names);
+       info->reg_names =
+               calloc(target->reg_cache->num_regs, max_reg_name_len);
+       if (!info->reg_names)
+               return ERROR_FAIL;
        char *reg_name = info->reg_names;
 
        static struct reg_feature feature_cpu = {
@@ -2111,45 +3897,166 @@ int riscv_init_registers(struct target *target)
        static struct reg_feature feature_csr = {
                .name = "org.gnu.gdb.riscv.csr"
        };
+       static struct reg_feature feature_vector = {
+               .name = "org.gnu.gdb.riscv.vector"
+       };
        static struct reg_feature feature_virtual = {
                .name = "org.gnu.gdb.riscv.virtual"
        };
+       static struct reg_feature feature_custom = {
+               .name = "org.gnu.gdb.riscv.custom"
+       };
 
-       static struct reg_data_type type_ieee_single = {
-               .type = REG_TYPE_IEEE_SINGLE,
-               .id = "ieee_single"
+       /* These types are built into gdb. */
+       static struct reg_data_type type_ieee_single = { .type = REG_TYPE_IEEE_SINGLE, .id = "ieee_single" };
+       static struct reg_data_type type_ieee_double = { .type = REG_TYPE_IEEE_DOUBLE, .id = "ieee_double" };
+       static struct reg_data_type_union_field single_double_fields[] = {
+               {"float", &type_ieee_single, single_double_fields + 1},
+               {"double", &type_ieee_double, NULL},
        };
-       static struct reg_data_type type_ieee_double = {
-               .type = REG_TYPE_IEEE_DOUBLE,
-               .id = "ieee_double"
+       static struct reg_data_type_union single_double_union = {
+               .fields = single_double_fields
        };
+       static struct reg_data_type type_ieee_single_double = {
+               .type = REG_TYPE_ARCH_DEFINED,
+               .id = "FPU_FD",
+               .type_class = REG_TYPE_CLASS_UNION,
+               .reg_type_union = &single_double_union
+       };
+       static struct reg_data_type type_uint8 = { .type = REG_TYPE_UINT8, .id = "uint8" };
+       static struct reg_data_type type_uint16 = { .type = REG_TYPE_UINT16, .id = "uint16" };
+       static struct reg_data_type type_uint32 = { .type = REG_TYPE_UINT32, .id = "uint32" };
+       static struct reg_data_type type_uint64 = { .type = REG_TYPE_UINT64, .id = "uint64" };
+       static struct reg_data_type type_uint128 = { .type = REG_TYPE_UINT128, .id = "uint128" };
+
+       /* This is roughly the XML we want:
+        * <vector id="bytes" type="uint8" count="16"/>
+        * <vector id="shorts" type="uint16" count="8"/>
+        * <vector id="words" type="uint32" count="4"/>
+        * <vector id="longs" type="uint64" count="2"/>
+        * <vector id="quads" type="uint128" count="1"/>
+        * <union id="riscv_vector_type">
+        *   <field name="b" type="bytes"/>
+        *   <field name="s" type="shorts"/>
+        *   <field name="w" type="words"/>
+        *   <field name="l" type="longs"/>
+        *   <field name="q" type="quads"/>
+        * </union>
+        */
+
+       info->vector_uint8.type = &type_uint8;
+       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 / 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 / 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 / 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 / 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;
+       info->type_uint128_vector.reg_type_vector = &info->vector_uint128;
+
+       info->vector_fields[0].name = "b";
+       info->vector_fields[0].type = &info->type_uint8_vector;
+       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 >= 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 >= 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 >= 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;
+       } else {
+               info->vector_fields[3].next = NULL;
+       }
+       info->vector_fields[4].next = NULL;
+
+       info->vector_union.fields = info->vector_fields;
+
+       info->type_vector.type = REG_TYPE_ARCH_DEFINED;
+       info->type_vector.id = "riscv_vector";
+       info->type_vector.type_class = REG_TYPE_CLASS_UNION;
+       info->type_vector.reg_type_union = &info->vector_union;
+
        struct csr_info csr_info[] = {
 #define DECLARE_CSR(name, number) { number, #name },
 #include "encoding.h"
 #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;
 
-       /* When gdb request register N, gdb_get_register_packet() assumes that this
+       int custom_within_range = 0;
+
+       riscv_reg_info_t *shared_reg_info = calloc(1, sizeof(riscv_reg_info_t));
+       if (!shared_reg_info)
+               return ERROR_FAIL;
+       shared_reg_info->target = target;
+
+       /* When gdb requests register N, gdb_get_register_packet() assumes that this
         * is register at index N in reg_list. So if there are certain registers
         * that don't exist, we need to leave holes in the list (or renumber, but
         * it would be nice not to have yet another set of numbers to translate
         * between). */
-       for (uint32_t number = 0; number < GDB_REGNO_COUNT; number++) {
+       for (uint32_t number = 0; number < target->reg_cache->num_regs; number++) {
                struct reg *r = &target->reg_cache->reg_list[number];
                r->dirty = false;
                r->valid = false;
                r->exist = true;
                r->type = &riscv_reg_arch_type;
-               r->arch_info = target;
+               r->arch_info = shared_reg_info;
                r->number = number;
                r->size = riscv_xlen(target);
                /* r->size is set in riscv_invalidate_register_cache, maybe because the
                 * target is in theory allowed to change XLEN on us. But I expect a lot
                 * 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, 'E');
+                       /* TODO: For now we fake that all GPRs exist because otherwise gdb
+                        * doesn't work. */
+                       r->exist = true;
                        r->caller_save = true;
                        switch (number) {
                                case GDB_REGNO_ZERO:
@@ -2258,12 +4165,13 @@ int riscv_init_registers(struct target *target)
                        r->feature = &feature_cpu;
                } else if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) {
                        r->caller_save = true;
-                       if (riscv_supports_extension(target, riscv_current_hartid(target),
-                                               'D')) {
-                               r->reg_data_type = &type_ieee_double;
+                       if (riscv_supports_extension(target, 'D')) {
                                r->size = 64;
-                       } else if (riscv_supports_extension(target,
-                                               riscv_current_hartid(target), '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, 'F')) {
                                r->reg_data_type = &type_ieee_single;
                                r->size = 32;
                        } else {
@@ -2375,7 +4283,7 @@ int riscv_init_registers(struct target *target)
                        unsigned csr_number = number - GDB_REGNO_CSR0;
 
                        while (csr_info[csr_info_index].number < csr_number &&
-                                       csr_info_index < DIM(csr_info) - 1) {
+                                       csr_info_index < ARRAY_SIZE(csr_info) - 1) {
                                csr_info_index++;
                        }
                        if (csr_info[csr_info_index].number == csr_number) {
@@ -2394,8 +4302,7 @@ int riscv_init_registers(struct target *target)
                                case CSR_FFLAGS:
                                case CSR_FRM:
                                case CSR_FCSR:
-                                       r->exist = riscv_supports_extension(target,
-                                                       riscv_current_hartid(target), 'F');
+                                       r->exist = riscv_supports_extension(target, 'F');
                                        r->group = "float";
                                        r->feature = &feature_fpu;
                                        break;
@@ -2409,18 +4316,19 @@ int riscv_init_registers(struct target *target)
                                case CSR_SCAUSE:
                                case CSR_STVAL:
                                case CSR_SATP:
-                                       r->exist = riscv_supports_extension(target,
-                                                       riscv_current_hartid(target), '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, riscv_current_hartid(target), 'S') ||
-                                               riscv_supports_extension(target, riscv_current_hartid(target), 'N');
+                                       r->exist = riscv_supports_extension(target, 'S') ||
+                                               riscv_supports_extension(target, 'N');
                                        break;
 
+                               case CSR_PMPCFG1:
+                               case CSR_PMPCFG3:
                                case CSR_CYCLEH:
                                case CSR_TIMEH:
                                case CSR_INSTRETH:
@@ -2486,16 +4394,32 @@ int riscv_init_registers(struct target *target)
                                case CSR_MHPMCOUNTER31H:
                                        r->exist = riscv_xlen(target) == 32;
                                        break;
+
+                               case CSR_VSTART:
+                               case CSR_VXSAT:
+                               case CSR_VXRM:
+                               case CSR_VL:
+                               case CSR_VTYPE:
+                               case CSR_VLENB:
+                                       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) {
@@ -2503,13 +4427,96 @@ int riscv_init_registers(struct target *target)
                        r->group = "general";
                        r->feature = &feature_virtual;
                        r->size = 8;
+
+               } else if (number >= GDB_REGNO_V0 && number <= GDB_REGNO_V31) {
+                       r->caller_save = false;
+                       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;
+                       r->reg_data_type = &info->type_vector;
+
+               } else if (number >= GDB_REGNO_COUNT) {
+                       /* Custom registers. */
+                       assert(!list_empty(&info->expose_custom));
+
+                       range_list_t *range = list_first_entry(&info->expose_custom, range_list_t, list);
+
+                       unsigned custom_number = range->low + custom_within_range;
+
+                       r->group = "custom";
+                       r->feature = &feature_custom;
+                       r->arch_info = calloc(1, sizeof(riscv_reg_info_t));
+                       if (!r->arch_info)
+                               return ERROR_FAIL;
+                       ((riscv_reg_info_t *) r->arch_info)->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;
+                               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 + GDB_REGNO_COUNT * 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 = info->reg_cache_values[number];
        }
 
        return ERROR_OK;
 }
+
+
+void riscv_add_bscan_tunneled_scan(struct target *target, struct scan_field *field,
+                                       riscv_bscan_tunneled_scan_context_t *ctxt)
+{
+       jtag_add_ir_scan(target->tap, &select_user4, TAP_IDLE);
+
+       memset(ctxt->tunneled_dr, 0, sizeof(ctxt->tunneled_dr));
+       if (bscan_tunnel_type == BSCAN_TUNNEL_DATA_REGISTER) {
+               ctxt->tunneled_dr[3].num_bits = 1;
+               ctxt->tunneled_dr[3].out_value = bscan_one;
+               ctxt->tunneled_dr[2].num_bits = 7;
+               ctxt->tunneled_dr_width = field->num_bits;
+               ctxt->tunneled_dr[2].out_value = &ctxt->tunneled_dr_width;
+               /* for BSCAN tunnel, there is a one-TCK skew between shift in and shift out, so
+                  scanning num_bits + 1, and then will right shift the input field after executing the queues */
+
+               ctxt->tunneled_dr[1].num_bits = field->num_bits + 1;
+               ctxt->tunneled_dr[1].out_value = field->out_value;
+               ctxt->tunneled_dr[1].in_value = field->in_value;
+
+               ctxt->tunneled_dr[0].num_bits = 3;
+               ctxt->tunneled_dr[0].out_value = bscan_zero;
+       } else {
+               /* BSCAN_TUNNEL_NESTED_TAP */
+               ctxt->tunneled_dr[0].num_bits = 1;
+               ctxt->tunneled_dr[0].out_value = bscan_one;
+               ctxt->tunneled_dr[1].num_bits = 7;
+               ctxt->tunneled_dr_width = field->num_bits;
+               ctxt->tunneled_dr[1].out_value = &ctxt->tunneled_dr_width;
+               /* for BSCAN tunnel, there is a one-TCK skew between shift in and shift out, so
+                  scanning num_bits + 1, and then will right shift the input field after executing the queues */
+               ctxt->tunneled_dr[2].num_bits = field->num_bits + 1;
+               ctxt->tunneled_dr[2].out_value = field->out_value;
+               ctxt->tunneled_dr[2].in_value = field->in_value;
+               ctxt->tunneled_dr[3].num_bits = 3;
+               ctxt->tunneled_dr[3].out_value = bscan_zero;
+       }
+       jtag_add_dr_scan(target->tap, ARRAY_SIZE(ctxt->tunneled_dr), ctxt->tunneled_dr, TAP_IDLE);
+}

Linking to existing account procedure

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

SSH host keys fingerprints

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