target/riscv: fix build error with -Werror=maybe-uninitialized
[openocd.git] / src / target / riscv / riscv.c
index 3b88e331379bd5a7a200ec99547a0eab7c6bddc7..c26e6358fde2245fcec8dfd578ff938bbb8d16d7 100644 (file)
@@ -1,3 +1,5 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
 #include <assert.h>
 #include <stdlib.h>
 #include <time.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.
- */
-
 #define get_field(reg, mask) (((reg) & (mask)) / ((mask) & ~((mask) << 1)))
 #define set_field(reg, mask, val) (((reg) & ~(mask)) | (((val) * ((mask) & ~((mask) << 1))) & (mask)))
 
@@ -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,
@@ -170,6 +124,71 @@ struct scan_field select_idcode = {
        .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 uint8_t bscan_zero[4] = {0};
+static uint8_t bscan_one[4] = {1};
+
+uint8_t ir_user4[4] = {0x23};
+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 = DIM(_bscan_tunnel_nested_tap_select_dmi);
+
+struct scan_field *bscan_tunnel_data_register_select_dmi = _bscan_tunnel_data_register_select_dmi;
+uint32_t bscan_tunnel_data_register_select_dmi_num_fields = DIM(_bscan_tunnel_data_register_select_dmi);
+
 struct trigger {
        uint64_t address;
        uint32_t length;
@@ -186,6 +205,12 @@ int riscv_command_timeout_sec = DEFAULT_COMMAND_TIMEOUT_SEC;
 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;
 
 typedef struct {
        uint16_t low, high;
@@ -199,11 +224,158 @@ range_t *expose_csr;
 /* Same, but for custom registers. */
 range_t *expose_custom;
 
+static enum {
+       RO_NORMAL,
+       RO_REVERSED
+} resume_order;
+
+virt2phys_info_t sv32 = {
+       .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},
+};
+
+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},
+};
+
+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},
+};
+
+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, DIM(tunneled_ir), tunneled_ir, TAP_IDLE);
+       jtag_add_dr_scan(target->tap, DIM(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);
+
+       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);
 
@@ -264,6 +436,15 @@ static int riscv_init_target(struct command_context *cmd_ctx,
        select_dbus.num_bits = target->tap->ir_length;
        select_idcode.num_bits = target->tap->ir_length;
 
+       if (bscan_tunnel_ir_width != 0) {
+               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;
@@ -271,21 +452,12 @@ static int riscv_init_target(struct command_context *cmd_ctx,
        return ERROR_OK;
 }
 
-static void riscv_deinit_target(struct target *target)
+static void riscv_free_registers(struct target *target)
 {
-       LOG_DEBUG("riscv_deinit_target()");
-       struct target_type *tt = get_target_type(target);
-       if (tt) {
-               tt->deinit_target(target);
-               riscv_info_t *info = (riscv_info_t *) target->arch_info;
-               free(info->reg_names);
-               free(info);
-       }
        /* Free the shared structure use for most registers. */
        if (target->reg_cache) {
                if (target->reg_cache->reg_list) {
-                       if (target->reg_cache->reg_list[0].arch_info)
-                               free(target->reg_cache->reg_list[0].arch_info);
+                       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);
@@ -293,13 +465,22 @@ static void riscv_deinit_target(struct target *target)
                }
                free(target->reg_cache);
        }
-       target->arch_info = NULL;
 }
 
-static int oldriscv_halt(struct target *target)
+static void riscv_deinit_target(struct target *target)
 {
+       LOG_DEBUG("riscv_deinit_target()");
        struct target_type *tt = get_target_type(target);
-       return tt->halt(target);
+       if (tt) {
+               tt->deinit_target(target);
+               riscv_info_t *info = (riscv_info_t *) target->arch_info;
+               free(info->reg_names);
+               free(info);
+       }
+
+       riscv_free_registers(target);
+
+       target->arch_info = NULL;
 }
 
 static void trigger_from_breakpoint(struct trigger *trigger,
@@ -533,7 +714,7 @@ int riscv_add_breakpoint(struct target *target, struct breakpoint *breakpoint)
                        return ERROR_FAIL;
                }
 
-               uint8_t buff[4];
+               uint8_t buff[4] = { 0 };
                buf_set_u32(buff, 0, breakpoint->length * CHAR_BIT, breakpoint->length == 4 ? ebreak() : ebreak_c());
                int const retval = target_write_memory(target, breakpoint->address, 2, breakpoint->length / 2, buff);
 
@@ -685,6 +866,8 @@ int riscv_hit_watchpoint(struct target *target, struct watchpoint **hit_watchpoi
 {
        struct watchpoint *wp = target->watchpoints;
 
+       if (riscv_rtos_enabled(target))
+               riscv_set_current_hartid(target, target->rtos->current_thread - 1);
        LOG_DEBUG("Current hartid = %d", riscv_current_hartid(target));
 
        /*TODO instead of disassembling the instruction that we think caused the
@@ -765,12 +948,9 @@ 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)
@@ -822,160 +1002,683 @@ static int old_or_new_riscv_poll(struct target *target)
                return riscv_openocd_poll(target);
 }
 
-static int old_or_new_riscv_halt(struct target *target)
+int halt_prep(struct target *target)
 {
        RISCV_INFO(r);
-       if (r->is_halted == NULL)
-               return oldriscv_halt(target);
-       else
-               return riscv_openocd_halt(target);
+       for (int i = 0; i < riscv_count_harts(target); ++i) {
+               if (!riscv_hart_enabled(target, i))
+                       continue;
+
+               LOG_DEBUG("[%s] prep hart, debug_reason=%d", target_name(target),
+                                 target->debug_reason);
+               if (riscv_set_current_hartid(target, i) != ERROR_OK)
+                       return ERROR_FAIL;
+               if (riscv_is_halted(target)) {
+                       LOG_DEBUG("Hart %d is already halted (reason=%d).", i,
+                                         target->debug_reason);
+               } else {
+                       if (r->halt_prep(target) != ERROR_OK)
+                               return ERROR_FAIL;
+                       r->prepped = true;
+               }
+       }
+       return ERROR_OK;
 }
 
-static int riscv_assert_reset(struct target *target)
+int riscv_halt_go_all_harts(struct target *target)
 {
-       LOG_DEBUG("[%d]", target->coreid);
-       struct target_type *tt = get_target_type(target);
+       RISCV_INFO(r);
+       for (int i = 0; i < riscv_count_harts(target); ++i) {
+               if (!riscv_hart_enabled(target, i))
+                       continue;
+
+               if (riscv_set_current_hartid(target, i) != ERROR_OK)
+                       return ERROR_FAIL;
+               if (riscv_is_halted(target)) {
+                       LOG_DEBUG("Hart %d is already halted.", i);
+               } else {
+                       if (r->halt_go(target) != ERROR_OK)
+                               return ERROR_FAIL;
+               }
+       }
+
        riscv_invalidate_register_cache(target);
-       return tt->assert_reset(target);
+
+       return ERROR_OK;
 }
 
-static int riscv_deassert_reset(struct target *target)
+int halt_go(struct target *target)
 {
-       LOG_DEBUG("[%d]", target->coreid);
-       struct target_type *tt = get_target_type(target);
-       return tt->deassert_reset(target);
-}
+       riscv_info_t *r = riscv_info(target);
+       int result;
+       if (r->is_halted == NULL) {
+               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 oldriscv_resume(struct target *target, int current, uint32_t address,
-               int handle_breakpoints, int debug_execution)
+static int halt_finish(struct target *target)
 {
-       struct target_type *tt = get_target_type(target);
-       return tt->resume(target, current, address, handle_breakpoints,
-                       debug_execution);
+       return target_call_event_callbacks(target, TARGET_EVENT_HALTED);
 }
 
-static int old_or_new_riscv_resume(
-               struct target *target,
-               int current,
-               target_addr_t address,
-               int handle_breakpoints,
-               int debug_execution
-){
-       LOG_DEBUG("handle_breakpoints=%d", handle_breakpoints);
+int riscv_halt(struct target *target)
+{
+       RISCV_INFO(r);
+
+       if (r->is_halted == NULL) {
+               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) {
-               struct target_list *targets = target->head;
-               int result = ERROR_OK;
-               while (targets) {
-                       struct target *t = targets->target;
-                       riscv_info_t *r = riscv_info(t);
-                       if (r->is_halted == NULL) {
-                               if (oldriscv_resume(t, current, address, handle_breakpoints,
-                                                       debug_execution) != ERROR_OK)
-                                       result = ERROR_FAIL;
-                       } else {
-                               if (riscv_openocd_resume(t, current, address,
-                                                       handle_breakpoints, debug_execution) != ERROR_OK)
+               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;
                        }
-                       targets = targets->next;
                }
-               return result;
-       }
 
-       RISCV_INFO(r);
-       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);
-}
+               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;
+       }
 
-static int riscv_select_current_hart(struct target *target)
-{
-       RISCV_INFO(r);
        if (riscv_rtos_enabled(target)) {
-               if (r->rtos_hartid == -1)
-                       r->rtos_hartid = target->rtos->current_threadid - 1;
-               return riscv_set_current_hartid(target, r->rtos_hartid);
-       } else
-               return riscv_set_current_hartid(target, target->coreid);
+               if (r->rtos_hartid != -1) {
+                       LOG_DEBUG("halt requested on RTOS hartid %d", r->rtos_hartid);
+                       target->rtos->current_threadid = r->rtos_hartid + 1;
+                       target->rtos->current_thread = r->rtos_hartid + 1;
+               } else
+                       LOG_DEBUG("halt requested, but no known RTOS hartid");
+       }
+
+       return result;
 }
 
-static int riscv_read_memory(struct target *target, target_addr_t address,
-               uint32_t size, uint32_t count, uint8_t *buffer)
+static int riscv_assert_reset(struct target *target)
 {
-       if (riscv_select_current_hart(target) != ERROR_OK)
-               return ERROR_FAIL;
+       LOG_DEBUG("[%d]", target->coreid);
        struct target_type *tt = get_target_type(target);
-       return tt->read_memory(target, address, size, count, buffer);
+       riscv_invalidate_register_cache(target);
+       return tt->assert_reset(target);
 }
 
-static int riscv_write_memory(struct target *target, target_addr_t address,
-               uint32_t size, uint32_t count, const uint8_t *buffer)
+static int riscv_deassert_reset(struct target *target)
 {
-       if (riscv_select_current_hart(target) != ERROR_OK)
-               return ERROR_FAIL;
+       LOG_DEBUG("[%d]", target->coreid);
        struct target_type *tt = get_target_type(target);
-       return tt->write_memory(target, address, size, count, buffer);
+       return tt->deassert_reset(target);
 }
 
-static int riscv_get_gdb_reg_list_internal(struct target *target,
-               struct reg **reg_list[], int *reg_list_size,
-               enum target_register_class reg_class, bool read)
+int riscv_resume_prep_all_harts(struct target *target)
 {
        RISCV_INFO(r);
-       LOG_DEBUG("rtos_hartid=%d, current_hartid=%d, reg_class=%d, read=%d",
-                       r->rtos_hartid, r->current_hartid, reg_class, read);
+       for (int i = 0; i < riscv_count_harts(target); ++i) {
+               if (!riscv_hart_enabled(target, i))
+                       continue;
 
-       if (!target->reg_cache) {
-               LOG_ERROR("Target not initialized. Return ERROR_FAIL.");
-               return ERROR_FAIL;
+               LOG_DEBUG("prep hart %d", i);
+               if (riscv_set_current_hartid(target, i) != ERROR_OK)
+                       return ERROR_FAIL;
+               if (riscv_is_halted(target)) {
+                       if (r->resume_prep(target) != ERROR_OK)
+                               return ERROR_FAIL;
+               } else {
+                       LOG_DEBUG("  hart %d requested resume, but was already resumed", i);
+               }
        }
 
-       if (riscv_select_current_hart(target) != ERROR_OK)
-               return ERROR_FAIL;
+       LOG_DEBUG("[%d] mark as prepped", target->coreid);
+       r->prepped = true;
 
-       switch (reg_class) {
-               case REG_CLASS_GENERAL:
-                       *reg_list_size = 33;
-                       break;
-               case REG_CLASS_ALL:
-                       *reg_list_size = target->reg_cache->num_regs;
-                       break;
-               default:
-                       LOG_ERROR("Unsupported reg_class: %d", reg_class);
-                       return ERROR_FAIL;
-       }
+       return ERROR_OK;
+}
 
-       *reg_list = calloc(*reg_list_size, sizeof(struct reg *));
-       if (!*reg_list)
+/* state must be riscv_reg_t state[RISCV_MAX_HWBPS] = {0}; */
+static int disable_triggers(struct target *target, riscv_reg_t *state)
+{
+       RISCV_INFO(r);
+
+       LOG_DEBUG("deal with triggers");
+
+       if (riscv_enumerate_triggers(target) != ERROR_OK)
                return ERROR_FAIL;
 
-       for (int i = 0; i < *reg_list_size; i++) {
-               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].valid) {
-                       if (target->reg_cache->reg_list[i].type->get(
-                                               &target->reg_cache->reg_list[i]) != ERROR_OK)
-                               /* This function is called when first connecting to gdb,
-                                * resulting in an attempt to read all kinds of registers which
-                                * probably will fail. Ignore these failures, and when
-                                * encountered stop reading to save time. */
-                               read = false;
+       int hartid = riscv_current_hartid(target);
+       if (r->manual_hwbp_set) {
+               /* Look at every trigger that may have been set. */
+               riscv_reg_t tselect;
+               if (riscv_get_register(target, &tselect, GDB_REGNO_TSELECT) != ERROR_OK)
+                       return ERROR_FAIL;
+               for (unsigned t = 0; t < r->trigger_count[hartid]; t++) {
+                       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 riscv_get_gdb_reg_list(struct target *target,
-               struct reg **reg_list[], int *reg_list_size,
-               enum target_register_class reg_class)
+static int enable_triggers(struct target *target, riscv_reg_t *state)
 {
-       return riscv_get_gdb_reg_list_internal(target, reg_list, reg_list_size,
-                       reg_class, true);
-}
+       RISCV_INFO(r);
+
+       int hartid = riscv_current_hartid(target);
+
+       if (r->manual_hwbp_set) {
+               /* Look at every trigger that may have been set. */
+               riscv_reg_t tselect;
+               if (riscv_get_register(target, &tselect, GDB_REGNO_TSELECT) != ERROR_OK)
+                       return ERROR_FAIL;
+               for (unsigned t = 0; t < r->trigger_count[hartid]; t++) {
+                       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;
+}
+
+/**
+ * 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 == NULL) {
+               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,
+               bool single_hart)
+{
+       LOG_DEBUG("handle_breakpoints=%d", handle_breakpoints);
+       int result = ERROR_OK;
+       if (target->smp && !single_hart) {
+               for (struct target_list *tlist = target->head; tlist; tlist = tlist->next) {
+                       struct target *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_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_select_current_hart(struct target *target)
+{
+       RISCV_INFO(r);
+       if (riscv_rtos_enabled(target)) {
+               if (r->rtos_hartid == -1)
+                       r->rtos_hartid = target->rtos->current_threadid - 1;
+               return riscv_set_current_hartid(target, r->rtos_hartid);
+       } else
+               return riscv_set_current_hartid(target, target->coreid);
+}
+
+static int riscv_mmu(struct target *target, int *enabled)
+{
+       if (!riscv_enable_virt2phys) {
+               *enabled = 0;
+               return ERROR_OK;
+       }
+
+       if (riscv_rtos_enabled(target))
+               riscv_set_current_hartid(target, target->rtos->current_thread - 1);
+
+       /* Don't use MMU in explicit or effective M (machine) mode */
+       riscv_reg_t priv;
+       if (riscv_get_register(target, &priv, GDB_REGNO_PRIV) != ERROR_OK) {
+               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);
+       riscv_reg_t satp_value;
+       int mode;
+       uint64_t ppn_value;
+       target_addr_t table_address;
+       virt2phys_info_t *info;
+       uint64_t pte = 0;
+       int i;
+
+       if (riscv_rtos_enabled(target))
+               riscv_set_current_hartid(target, target->rtos->current_thread - 1);
+
+       int result = riscv_get_register(target, &satp_value, GDB_REGNO_SATP);
+       if (result != ERROR_OK)
+               return result;
+
+       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->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_internal(struct target *target,
+               struct reg **reg_list[], int *reg_list_size,
+               enum target_register_class reg_class, bool read)
+{
+       RISCV_INFO(r);
+       LOG_DEBUG("rtos_hartid=%d, current_hartid=%d, reg_class=%d, read=%d",
+                       r->rtos_hartid, r->current_hartid, reg_class, read);
+
+       if (!target->reg_cache) {
+               LOG_ERROR("Target not initialized. Return ERROR_FAIL.");
+               return ERROR_FAIL;
+       }
+
+       if (riscv_select_current_hart(target) != ERROR_OK)
+               return ERROR_FAIL;
+
+       switch (reg_class) {
+               case REG_CLASS_GENERAL:
+                       *reg_list_size = 33;
+                       break;
+               case REG_CLASS_ALL:
+                       *reg_list_size = target->reg_cache->num_regs;
+                       break;
+               default:
+                       LOG_ERROR("Unsupported reg_class: %d", reg_class);
+                       return ERROR_FAIL;
+       }
+
+       *reg_list = calloc(*reg_list_size, sizeof(struct reg *));
+       if (!*reg_list)
+               return ERROR_FAIL;
+
+       for (int i = 0; i < *reg_list_size; i++) {
+               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)
 {
@@ -990,6 +1693,7 @@ static int riscv_run_algorithm(struct target *target, int num_mem_params,
                target_addr_t exit_point, int timeout_ms, void *arch_info)
 {
        riscv_info_t *info = (riscv_info_t *) target->arch_info;
+       int hartid = riscv_current_hartid(target);
 
        if (num_mem_params > 0) {
                LOG_ERROR("Memory parameters are not supported for RISC-V algorithms.");
@@ -1006,12 +1710,10 @@ static int riscv_run_algorithm(struct target *target, int num_mem_params,
        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 (reg_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);
                if (!r) {
@@ -1033,14 +1735,17 @@ 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,
@@ -1060,7 +1765,7 @@ static int riscv_run_algorithm(struct target *target, int num_mem_params,
 
        /* 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();
@@ -1068,11 +1773,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 < DIM(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;
                }
 
@@ -1081,10 +1803,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_set_current_hartid(target, hartid) != 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;
@@ -1096,32 +1822,38 @@ static int riscv_run_algorithm(struct target *target, int num_mem_params,
        reg_mstatus->type->set(reg_mstatus, mstatus_bytes);
 
        /* Restore registers */
-       uint8_t buf[8];
+       uint8_t buf[8] = { 0 };
        buf_set_u64(buf, 0, info->xlen[0], saved_pc);
        if (reg_pc->type->set(reg_pc, buf) != ERROR_OK)
                return ERROR_FAIL;
 
        for (int i = 0; i < num_reg_params; i++) {
+               if (reg_params[i].direction == PARAM_IN ||
+                               reg_params[i].direction == PARAM_IN_OUT) {
+                       struct reg *r = register_get_by_name(target->reg_cache, reg_params[i].reg_name, 0);
+                       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)
+               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;
+       return ERROR_FAIL;
 }
 
 /*** OpenOCD Helper Functions ***/
@@ -1150,15 +1882,16 @@ 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;
 }
 
-int set_debug_reason(struct target *target, int hartid)
+int set_debug_reason(struct target *target, enum riscv_halt_reason halt_reason)
 {
-       switch (riscv_halt_reason(target, hartid)) {
+       switch (halt_reason) {
                case RISCV_HALT_BREAKPOINT:
                        target->debug_reason = DBG_REASON_BREAKPOINT;
                        break;
@@ -1166,6 +1899,7 @@ int set_debug_reason(struct target *target, int hartid)
                        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:
@@ -1177,6 +1911,7 @@ int set_debug_reason(struct target *target, int hartid)
                case RISCV_HALT_ERROR:
                        return ERROR_FAIL;
        }
+       LOG_DEBUG("[%s] debug_reason=%d", target_name(target), target->debug_reason);
        return ERROR_OK;
 }
 
@@ -1206,70 +1941,92 @@ int riscv_openocd_poll(struct target *target)
                }
                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);
+               target->state = TARGET_HALTED;
+               enum riscv_halt_reason halt_reason = riscv_halt_reason(target, halted_hart);
+               if (set_debug_reason(target, halt_reason) != ERROR_OK)
+                       return ERROR_FAIL;
+
+               target->rtos->current_threadid = halted_hart + 1;
+               target->rtos->current_thread = halted_hart + 1;
+               riscv_set_rtos_hartid(target, halted_hart);
+
+               /* If we're here then at least one hart triggered.  That means we want
+                * to go and halt _every_ hart (configured with -rtos riscv) in the
+                * system, as that's the invariant we hold here.  Some harts might have
+                * already halted (as we're either in single-step mode or they also
+                * triggered a breakpoint), so don't attempt to halt those harts.
+                * riscv_halt() will do all that for us. */
+               riscv_halt(target);
 
        } else if (target->smp) {
-               bool halt_discovered = false;
-               bool newly_halted[128] = {0};
+               unsigned halts_discovered = 0;
+               unsigned total_targets = 0;
+               bool newly_halted[RISCV_MAX_HARTS] = {0};
+               unsigned should_remain_halted = 0;
+               unsigned should_resume = 0;
                unsigned i = 0;
                for (struct target_list *list = target->head; list != NULL;
                                list = list->next, i++) {
+                       total_targets++;
                        struct target *t = list->target;
                        riscv_info_t *r = riscv_info(t);
                        assert(i < DIM(newly_halted));
                        enum riscv_poll_hart out = riscv_poll_hart(t, r->current_hartid);
                        switch (out) {
-                               case RPH_NO_CHANGE:
-                                       break;
-                               case RPH_DISCOVERED_RUNNING:
-                                       t->state = TARGET_RUNNING;
-                                       break;
-                               case RPH_DISCOVERED_HALTED:
-                                       halt_discovered = true;
-                                       newly_halted[i] = true;
-                                       t->state = TARGET_HALTED;
-                                       if (set_debug_reason(t, r->current_hartid) != ERROR_OK)
-                                               return ERROR_FAIL;
-                                       break;
-                               case RPH_ERROR:
+                       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++;
+                               newly_halted[i] = true;
+                               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_discovered) {
-                       LOG_DEBUG("Halt other targets in this SMP group.");
-                       i = 0;
-                       for (struct target_list *list = target->head; list != NULL;
-                                       list = list->next, i++) {
-                               struct target *t = list->target;
-                               riscv_info_t *r = riscv_info(t);
-                               if (t->state != TARGET_HALTED) {
-                                       if (riscv_halt_one_hart(t, r->current_hartid) != ERROR_OK)
-                                               return ERROR_FAIL;
-                                       t->state = TARGET_HALTED;
-                                       if (set_debug_reason(t, r->current_hartid) != ERROR_OK)
-                                               return ERROR_FAIL;
-                                       newly_halted[i] = true;
+                               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;
 
-                       /* Now that we have all our ducks in a row, tell the higher layers
-                        * what just happened. */
-                       i = 0;
-                       for (struct target_list *list = target->head; list != NULL;
-                                       list = list->next, i++) {
-                               struct target *t = list->target;
-                               if (newly_halted[i])
-                                       target_call_event_callbacks(t, TARGET_EVENT_HALTED);
+                       case RPH_ERROR:
+                               return ERROR_FAIL;
                        }
                }
+
+               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);
+               }
                return ERROR_OK;
 
        } else {
@@ -1282,141 +2039,46 @@ int riscv_openocd_poll(struct target *target)
 
                halted_hart = riscv_current_hartid(target);
                LOG_DEBUG("  hart %d halted", halted_hart);
-       }
-
-       target->state = TARGET_HALTED;
-       if (set_debug_reason(target, halted_hart) != ERROR_OK)
-               return ERROR_FAIL;
 
-       if (riscv_rtos_enabled(target)) {
-               target->rtos->current_threadid = halted_hart + 1;
-               target->rtos->current_thread = halted_hart + 1;
-               riscv_set_rtos_hartid(target, 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;
        }
 
-       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);
-       return ERROR_OK;
-}
-
-int riscv_openocd_halt(struct target *target)
-{
-       RISCV_INFO(r);
-       int result;
-
-       LOG_DEBUG("[%d] halting all harts", target->coreid);
-
-       if (target->smp) {
-               LOG_DEBUG("Halt other targets in this SMP group.");
-               struct target_list *targets = target->head;
-               result = ERROR_OK;
-               while (targets) {
-                       struct target *t = targets->target;
-                       targets = targets->next;
-                       if (t->state != TARGET_HALTED) {
-                               if (riscv_halt_all_harts(t) != ERROR_OK)
-                                       result = ERROR_FAIL;
-                       }
-               }
-       } else {
-               result = riscv_halt_all_harts(target);
-       }
-
-       if (riscv_rtos_enabled(target)) {
-               if (r->rtos_hartid != -1) {
-                       LOG_DEBUG("halt requested on RTOS hartid %d", r->rtos_hartid);
-                       target->rtos->current_threadid = r->rtos_hartid + 1;
-                       target->rtos->current_thread = r->rtos_hartid + 1;
-               } else
-                       LOG_DEBUG("halt requested, but no known RTOS hartid");
-       }
-
-       target->state = TARGET_HALTED;
-       target->debug_reason = DBG_REASON_DBGRQ;
-       target_call_event_callbacks(target, TARGET_EVENT_HALTED);
-       return result;
-}
-
-int riscv_openocd_resume(
-               struct target *target,
-               int current,
-               target_addr_t address,
-               int handle_breakpoints,
-               int debug_execution)
-{
-       LOG_DEBUG("debug_reason=%d", target->debug_reason);
-
-       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. */
-               struct watchpoint *watchpoint = target->watchpoints;
-               bool trigger_temporarily_cleared[RISCV_MAX_HWBPS] = {0};
-
-               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++;
-               }
-
-               if (result == ERROR_OK)
-                       result = riscv_step_rtos_hart(target);
-
-               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++;
-               }
-
-               if (result != ERROR_OK)
-                       return result;
-       }
-
-       int out = riscv_resume_all_harts(target);
-       if (out != ERROR_OK) {
-               LOG_ERROR("unable to resume all harts");
-               return out;
+               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");
@@ -1424,6 +2086,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;
@@ -1495,6 +2161,16 @@ COMMAND_HANDLER(riscv_set_prefer_sba)
        return ERROR_OK;
 }
 
+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;
+}
+
 void parse_error(const char *string, char c, unsigned position)
 {
        char buf[position+2];
@@ -1561,9 +2237,10 @@ int parse_ranges(range_t **ranges, const char **argv)
                }
 
                if (pass == 0) {
-                       if (*ranges)
-                               free(*ranges);
+                       free(*ranges);
                        *ranges = calloc(range + 2, sizeof(range_t));
+                       if (!*ranges)
+                               return ERROR_FAIL;
                } else {
                        (*ranges)[range].low = 1;
                        (*ranges)[range].high = 0;
@@ -1616,7 +2293,7 @@ COMMAND_HANDLER(riscv_authdata_read)
                uint32_t value;
                if (r->authdata_read(target, &value) != ERROR_OK)
                        return ERROR_FAIL;
-               command_print(CMD_CTX, "0x%" PRIx32, value);
+               command_print(CMD, "0x%" PRIx32, value);
                return ERROR_OK;
        } else {
                LOG_ERROR("authdata_read is not implemented for this target.");
@@ -1669,7 +2346,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.");
@@ -1757,55 +2434,147 @@ COMMAND_HANDLER(riscv_set_ir)
        uint32_t value;
        COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], value);
 
-       if (!strcmp(CMD_ARGV[0], "idcode")) {
+       if (!strcmp(CMD_ARGV[0], "idcode"))
                buf_set_u32(ir_idcode, 0, 32, value);
-               return ERROR_OK;
-       } else if (!strcmp(CMD_ARGV[0], "dtmcs")) {
+       else if (!strcmp(CMD_ARGV[0], "dtmcs"))
                buf_set_u32(ir_dtmcontrol, 0, 32, value);
-               return ERROR_OK;
-       } else if (!strcmp(CMD_ARGV[0], "dmi")) {
+       else if (!strcmp(CMD_ARGV[0], "dmi"))
                buf_set_u32(ir_dbus, 0, 32, value);
-               return ERROR_OK;
+       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;
 }
 
 static const struct command_registration riscv_exec_command_handlers[] = {
        {
                .name = "test_compliance",
                .handler = riscv_test_compliance,
+               .usage = "",
                .mode = COMMAND_EXEC,
-               .usage = "riscv test_compliance",
                .help = "Runs a basic compliance test suite against the RISC-V Debug Spec."
        },
        {
                .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_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]]...",
+               .usage = "n0[-m0][,n1[-m1]]...",
                .help = "Configure a list of inclusive ranges for CSRs to expose in "
                                "addition to the standard ones. This must be executed before "
                                "`init`."
@@ -1814,7 +2583,7 @@ static const struct command_registration riscv_exec_command_handlers[] = {
                .name = "expose_custom",
                .handler = riscv_set_expose_custom,
                .mode = COMMAND_ANY,
-               .usage = "riscv expose_custom n0[-m0][,n1[-m1]]...",
+               .usage = "n0[-m0][,n1[-m1]]...",
                .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`."
@@ -1822,69 +2591,116 @@ static const struct command_registration riscv_exec_command_handlers[] = {
        {
                .name = "authdata_read",
                .handler = riscv_authdata_read,
+               .usage = "",
                .mode = COMMAND_ANY,
-               .usage = "riscv authdata_read",
                .help = "Return the 32-bit value read from authdata."
        },
        {
                .name = "authdata_write",
                .handler = riscv_authdata_write,
                .mode = COMMAND_ANY,
-               .usage = "riscv authdata_write value",
+               .usage = "value",
                .help = "Write the 32-bit value to authdata."
        },
        {
                .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 = "riscv test_sba_config_reg legal_address num_words"
+               .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,"
+               .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 = "reset_delays [wait]",
+               .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 = "riscv set_ir_idcode [idcode|dtmcs|dmi] value",
+               .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.
@@ -1898,37 +2714,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[] = {
-       {
-               .name = "semihosting",
-               .handler = handle_common_semihosting_command,
-               .mode = COMMAND_EXEC,
-               .usage = "['enable'|'disable']",
-               .help = "activate support for semihosting operations",
-       },
-       {
-               .name = "semihosting_cmdline",
-               .handler = handle_common_semihosting_cmdline,
-               .mode = COMMAND_EXEC,
-               .usage = "arguments",
-               .help = "command line arguments to be passed to program",
-       },
-       {
-               .name = "semihosting_fileio",
-               .handler = handle_common_semihosting_fileio_command,
-               .mode = COMMAND_EXEC,
-               .usage = "['enable'|'disable']",
-               .help = "activate support for semihosting fileio operations",
-       },
-       {
-               .name = "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[] = {
        {
@@ -1943,12 +2729,12 @@ 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
 };
 
-unsigned riscv_address_bits(struct target *target)
+static unsigned riscv_xlen_nonconst(struct target *target)
 {
        return riscv_xlen(target);
 }
@@ -1963,8 +2749,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,
@@ -1972,10 +2758,16 @@ 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_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,
@@ -1990,7 +2782,7 @@ struct target_type riscv_target = {
 
        .commands = riscv_command_handlers,
 
-       .address_bits = riscv_address_bits
+       .address_bits = riscv_xlen_nonconst,
 };
 
 /*** RISC-V Interface ***/
@@ -2004,72 +2796,52 @@ void riscv_info_init(struct target *target, riscv_info_t *r)
 
        memset(r->trigger_unique_id, 0xff, sizeof(r->trigger_unique_id));
 
-       for (size_t h = 0; h < RISCV_MAX_HARTS; ++h) {
+       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;
-       }
-}
-
-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;
-
-               riscv_halt_one_hart(target, i);
-       }
-
-       riscv_invalidate_register_cache(target);
-
-       return ERROR_OK;
 }
 
-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)
-               return ERROR_FAIL;
-       if (riscv_is_halted(target)) {
-               LOG_DEBUG("  hart %d requested halt, but was already halted", hartid);
-               return ERROR_OK;
-       }
 
-       int result = r->halt_current_hart(target);
-       register_cache_invalidate(target->reg_cache);
-       return result;
-}
+       /* Dummy variables to make mingw32-gcc happy. */
+       int first = 0;
+       int last = 1;
+       int step = 1;
+       switch (resume_order) {
+               case RO_NORMAL:
+                       first = 0;
+                       last = riscv_count_harts(target) - 1;
+                       step = 1;
+                       break;
+               case RO_REVERSED:
+                       first = riscv_count_harts(target) - 1;
+                       last = 0;
+                       step = -1;
+                       break;
+               default:
+                       assert(0);
+       }
 
-int riscv_resume_all_harts(struct target *target)
-{
-       for (int i = 0; i < riscv_count_harts(target); ++i) {
+       for (int i = first; i != last + step; i += step) {
                if (!riscv_hart_enabled(target, i))
                        continue;
 
-               riscv_resume_one_hart(target, i);
+               LOG_DEBUG("resuming hart %d", i);
+               if (riscv_set_current_hartid(target, i) != ERROR_OK)
+                       return ERROR_FAIL;
+               if (riscv_is_halted(target)) {
+                       if (r->resume_go(target) != ERROR_OK)
+                               return ERROR_FAIL;
+               } else {
+                       LOG_DEBUG("  hart %d requested resume, but was already resumed", i);
+               }
        }
 
        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);
@@ -2115,7 +2887,7 @@ bool riscv_supports_extension(struct target *target, int hartid, char letter)
        return r->misa[hartid] & (1 << num);
 }
 
-int riscv_xlen(const struct target *target)
+unsigned riscv_xlen(const struct target *target)
 {
        return riscv_xlen_of_hart(target, riscv_current_hartid(target));
 }
@@ -2127,7 +2899,6 @@ int riscv_xlen_of_hart(const struct target *target, int hartid)
        return r->xlen[hartid];
 }
 
-extern struct rtos_type riscv_rtos;
 bool riscv_rtos_enabled(const struct target *target)
 {
        return false;
@@ -2192,9 +2963,9 @@ int riscv_count_harts(struct target *target)
        if (target == NULL)
                return 1;
        RISCV_INFO(r);
-       if (r == NULL)
+       if (r == NULL || r->hart_count == NULL)
                return 1;
-       return r->hart_count;
+       return r->hart_count(target);
 }
 
 bool riscv_has_register(struct target *target, int hartid, int regid)
@@ -2202,6 +2973,55 @@ bool riscv_has_register(struct target *target, int hartid, int regid)
        return 1;
 }
 
+/**
+ * If write is true:
+ *   return true iff we are guaranteed that the register will contain exactly
+ *       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)
+{
+       /* 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 rbitrary
+        * 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
@@ -2217,7 +3037,23 @@ int riscv_set_register_on_hart(struct target *target, int hartid,
        RISCV_INFO(r);
        LOG_DEBUG("{%d} %s <- %" PRIx64, hartid, gdb_regno_name(regid), value);
        assert(r->set_register);
-       return r->set_register(target, hartid, regid, value);
+
+       /* TODO: Hack to deal with gdb that thinks these registers still exist. */
+       if (regid > GDB_REGNO_XPR15 && regid <= GDB_REGNO_XPR31 && value == 0 &&
+                       riscv_supports_extension(target, hartid, 'E'))
+               return ERROR_OK;
+
+       struct reg *reg = &target->reg_cache->reg_list[regid];
+       buf_set_u64(reg->value, 0, reg->size, value);
+
+       int result = r->set_register(target, hartid, regid, value);
+       if (result == ERROR_OK)
+               reg->valid = gdb_regno_cacheable(regid, true);
+       else
+               reg->valid = false;
+       LOG_DEBUG("[%s]{%d} wrote 0x%" PRIx64 " to %s valid=%d",
+                         target_name(target), hartid, value, reg->name, reg->valid);
+       return result;
 }
 
 int riscv_get_register(struct target *target, riscv_reg_t *value,
@@ -2233,14 +3069,31 @@ int riscv_get_register_on_hart(struct target *target, riscv_reg_t *value,
        RISCV_INFO(r);
 
        struct reg *reg = &target->reg_cache->reg_list[regid];
+       if (!reg->exist) {
+               LOG_DEBUG("[%s]{%d} %s does not exist.",
+                                 target_name(target), hartid, gdb_regno_name(regid));
+               return ERROR_FAIL;
+       }
 
        if (reg && reg->valid && hartid == riscv_current_hartid(target)) {
                *value = buf_get_u64(reg->value, 0, reg->size);
+               LOG_DEBUG("{%d} %s: %" PRIx64 " (cached)", hartid,
+                                 gdb_regno_name(regid), *value);
+               return ERROR_OK;
+       }
+
+       /* TODO: Hack to deal with gdb that thinks these registers still exist. */
+       if (regid > GDB_REGNO_XPR15 && regid <= GDB_REGNO_XPR31 &&
+                       riscv_supports_extension(target, hartid, 'E')) {
+               *value = 0;
                return ERROR_OK;
        }
 
        int result = r->get_register(target, value, hartid, regid);
 
+       if (result == ERROR_OK)
+               reg->valid = gdb_regno_cacheable(regid, false);
+
        LOG_DEBUG("{%d} %s: %" PRIx64, hartid, gdb_regno_name(regid), *value);
        return result;
 }
@@ -2351,7 +3204,9 @@ int riscv_enumerate_triggers(struct target *target)
                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);
+                       /* If we can't write tselect, then this hart does not support triggers. */
+                       if (riscv_set_register_on_hart(target, hartid, GDB_REGNO_TSELECT, t) != ERROR_OK)
+                               break;
                        uint64_t tselect_rb;
                        result = riscv_get_register_on_hart(target, &tselect_rb, hartid,
                                        GDB_REGNO_TSELECT);
@@ -2369,6 +3224,8 @@ int riscv_enumerate_triggers(struct target *target)
                                return result;
 
                        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
@@ -2397,10 +3254,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:
@@ -2421,12 +3336,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);
@@ -2444,20 +3433,29 @@ static int register_get(struct reg *reg)
 {
        riscv_reg_info_t *reg_info = reg->arch_info;
        struct target *target = reg_info->target;
-       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);
-       /* CSRs (and possibly other extension) registers may change value at any
-        * time. */
-       if (reg->number <= GDB_REGNO_XPR31 ||
-                       (reg->number >= GDB_REGNO_FPR0 && reg->number <= GDB_REGNO_FPR31) ||
-                       reg->number == GDB_REGNO_PC)
-               reg->valid = true;
-       LOG_DEBUG("[%d]{%d} read 0x%" PRIx64 " from %s (valid=%d)",
-                       target->coreid, riscv_current_hartid(target), value, reg->name,
-                       reg->valid);
+       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("[%d]{%d} read 0x%s from %s (valid=%d)", target->coreid,
+                       riscv_current_hartid(target), str, reg->name, reg->valid);
+       free(str);
        return ERROR_OK;
 }
 
@@ -2465,22 +3463,42 @@ static int register_set(struct reg *reg, uint8_t *buf)
 {
        riscv_reg_info_t *reg_info = reg->arch_info;
        struct target *target = reg_info->target;
+       RISCV_INFO(r);
+
+       char *str = buf_to_hex_str(buf, reg->size);
+       LOG_DEBUG("[%d]{%d} write 0x%s to %s (valid=%d)", target->coreid,
+                       riscv_current_hartid(target), str, reg->name, reg->valid);
+       free(str);
+
+       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;
+       }
 
-       uint64_t value = buf_get_u64(buf, 0, reg->size);
-
-       LOG_DEBUG("[%d]{%d} write 0x%" PRIx64 " to %s (valid=%d)",
-                       target->coreid, riscv_current_hartid(target), value, reg->name,
-                       reg->valid);
-       struct reg *r = &target->reg_cache->reg_list[reg->number];
-       /* CSRs (and possibly other extension) registers may change value at any
-        * time. */
-       if (reg->number <= GDB_REGNO_XPR31 ||
-                       (reg->number >= GDB_REGNO_FPR0 && reg->number <= GDB_REGNO_FPR31) ||
-                       reg->number == GDB_REGNO_PC)
-               r->valid = true;
-       memcpy(r->value, buf, (r->size + 7) / 8);
-
-       riscv_set_register(target, reg->number, value);
        return ERROR_OK;
 }
 
@@ -2503,13 +3521,11 @@ 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;
 
@@ -2527,14 +3543,19 @@ int riscv_init_registers(struct target *target)
 
        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);
+       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;
 
+       int hartid = riscv_current_hartid(target);
+
        static struct reg_feature feature_cpu = {
                .name = "org.gnu.gdb.riscv.cpu"
        };
@@ -2544,6 +3565,9 @@ 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"
        };
@@ -2551,14 +3575,117 @@ int riscv_init_registers(struct target *target)
                .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_union single_double_union = {
+               .fields = single_double_fields
        };
-       static struct reg_data_type type_ieee_double = {
-               .type = REG_TYPE_IEEE_DOUBLE,
-               .id = "ieee_double"
+       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[hartid];
+       info->type_uint8_vector.type = REG_TYPE_ARCH_DEFINED;
+       info->type_uint8_vector.id = "bytes";
+       info->type_uint8_vector.type_class = REG_TYPE_CLASS_VECTOR;
+       info->type_uint8_vector.reg_type_vector = &info->vector_uint8;
+
+       info->vector_uint16.type = &type_uint16;
+       info->vector_uint16.count = info->vlenb[hartid] / 2;
+       info->type_uint16_vector.type = REG_TYPE_ARCH_DEFINED;
+       info->type_uint16_vector.id = "shorts";
+       info->type_uint16_vector.type_class = REG_TYPE_CLASS_VECTOR;
+       info->type_uint16_vector.reg_type_vector = &info->vector_uint16;
+
+       info->vector_uint32.type = &type_uint32;
+       info->vector_uint32.count = info->vlenb[hartid] / 4;
+       info->type_uint32_vector.type = REG_TYPE_ARCH_DEFINED;
+       info->type_uint32_vector.id = "words";
+       info->type_uint32_vector.type_class = REG_TYPE_CLASS_VECTOR;
+       info->type_uint32_vector.reg_type_vector = &info->vector_uint32;
+
+       info->vector_uint64.type = &type_uint64;
+       info->vector_uint64.count = info->vlenb[hartid] / 8;
+       info->type_uint64_vector.type = REG_TYPE_ARCH_DEFINED;
+       info->type_uint64_vector.id = "longs";
+       info->type_uint64_vector.type_class = REG_TYPE_CLASS_VECTOR;
+       info->type_uint64_vector.reg_type_vector = &info->vector_uint64;
+
+       info->vector_uint128.type = &type_uint128;
+       info->vector_uint128.count = info->vlenb[hartid] / 16;
+       info->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[hartid] >= 2) {
+               info->vector_fields[0].next = info->vector_fields + 1;
+               info->vector_fields[1].name = "s";
+               info->vector_fields[1].type = &info->type_uint16_vector;
+       } else {
+               info->vector_fields[0].next = NULL;
+       }
+       if (info->vlenb[hartid] >= 4) {
+               info->vector_fields[1].next = info->vector_fields + 2;
+               info->vector_fields[2].name = "w";
+               info->vector_fields[2].type = &info->type_uint32_vector;
+       } else {
+               info->vector_fields[1].next = NULL;
+       }
+       if (info->vlenb[hartid] >= 8) {
+               info->vector_fields[2].next = info->vector_fields + 3;
+               info->vector_fields[3].name = "l";
+               info->vector_fields[3].type = &info->type_uint64_vector;
+       } else {
+               info->vector_fields[2].next = NULL;
+       }
+       if (info->vlenb[hartid] >= 16) {
+               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"
@@ -2572,6 +3699,8 @@ int riscv_init_registers(struct target *target)
        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
@@ -2592,6 +3721,11 @@ int riscv_init_registers(struct target *target)
                 * 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, hartid, '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:
@@ -2700,12 +3834,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, hartid, 'D')) {
                                r->size = 64;
-                       } else if (riscv_supports_extension(target,
-                                               riscv_current_hartid(target), 'F')) {
+                               if (riscv_supports_extension(target, hartid, 'F'))
+                                       r->reg_data_type = &type_ieee_single_double;
+                               else
+                                       r->reg_data_type = &type_ieee_double;
+                       } else if (riscv_supports_extension(target, hartid, 'F')) {
                                r->reg_data_type = &type_ieee_single;
                                r->size = 32;
                        } else {
@@ -2836,8 +3971,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, hartid, 'F');
                                        r->group = "float";
                                        r->feature = &feature_fpu;
                                        break;
@@ -2851,18 +3985,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, hartid, '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, hartid, 'S') ||
+                                               riscv_supports_extension(target, hartid, 'N');
                                        break;
 
+                               case CSR_PMPCFG1:
+                               case CSR_PMPCFG3:
                                case CSR_CYCLEH:
                                case CSR_TIMEH:
                                case CSR_INSTRETH:
@@ -2928,6 +4063,15 @@ 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, hartid, 'V');
+                                       break;
                        }
 
                        if (!r->exist && expose_csr) {
@@ -2946,7 +4090,16 @@ int riscv_init_registers(struct target *target)
                        r->feature = &feature_virtual;
                        r->size = 8;
 
-               } else {
+               } else if (number >= GDB_REGNO_V0 && number <= GDB_REGNO_V31) {
+                       r->caller_save = false;
+                       r->exist = riscv_supports_extension(target, hartid, 'V') && info->vlenb[hartid];
+                       r->size = info->vlenb[hartid] * 8;
+                       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(expose_custom);
 
@@ -2957,7 +4110,8 @@ int riscv_init_registers(struct target *target)
                        r->group = "custom";
                        r->feature = &feature_custom;
                        r->arch_info = calloc(1, sizeof(riscv_reg_info_t));
-                       assert(r->arch_info);
+                       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);
@@ -2974,8 +4128,48 @@ int riscv_init_registers(struct target *target)
                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];
+               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)