+/* 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)))
#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,
.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;
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;
/* 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] = { 0 };
+ if (bscan_tunnel_ir_width != 0)
+ return dtmcontrol_scan_via_bscan(target, out);
+
+
buf_set_u32(out_value, 0, 32, out);
jtag_add_ir_scan(target->tap, &select_dtmcontrol, TAP_IDLE);
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;
/* 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);
target->arch_info = NULL;
}
-static int oldriscv_halt(struct target *target)
-{
- struct target_type *tt = get_target_type(target);
- return tt->halt(target);
-}
-
static void trigger_from_breakpoint(struct trigger *trigger,
const struct breakpoint *breakpoint)
{
{
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
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)
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)
-{
- return riscv_get_gdb_reg_list_internal(target, reg_list, reg_list_size,
- reg_class, true);
-}
-
-static int riscv_arch_state(struct target *target)
+static int enable_triggers(struct target *target, riscv_reg_t *state)
{
- struct target_type *tt = get_target_type(target);
- return tt->arch_state(target);
-}
+ RISCV_INFO(r);
-/* Algorithm must end with a software breakpoint instruction. */
-static int riscv_run_algorithm(struct target *target, int num_mem_params,
- struct mem_param *mem_params, int num_reg_params,
- struct reg_param *reg_params, target_addr_t entry_point,
- target_addr_t exit_point, int timeout_ms, void *arch_info)
-{
- riscv_info_t *info = (riscv_info_t *) target->arch_info;
+ int hartid = riscv_current_hartid(target);
- if (num_mem_params > 0) {
- LOG_ERROR("Memory parameters are not supported for RISC-V algorithms.");
- return ERROR_FAIL;
+ if (r->manual_hwbp_set) {
+ /* Look at every trigger that may have been set. */
+ riscv_reg_t tselect;
+ if (riscv_get_register(target, &tselect, GDB_REGNO_TSELECT) != ERROR_OK)
+ return ERROR_FAIL;
+ for (unsigned 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)
+{
+ struct target_type *tt = get_target_type(target);
+ return tt->arch_state(target);
+}
+
+/* Algorithm must end with a software breakpoint instruction. */
+static int riscv_run_algorithm(struct target *target, int num_mem_params,
+ struct mem_param *mem_params, int num_reg_params,
+ struct reg_param *reg_params, target_addr_t entry_point,
+ target_addr_t exit_point, int timeout_ms, void *arch_info)
+{
+ riscv_info_t *info = (riscv_info_t *) target->arch_info;
+ int hartid = riscv_current_hartid(target);
+
+ if (num_mem_params > 0) {
+ LOG_ERROR("Memory parameters are not supported for RISC-V algorithms.");
+ return ERROR_FAIL;
}
if (target->state != TARGET_HALTED) {
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) {
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;
+ }
}
/* 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();
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, ®_value, regno) != ERROR_OK)
+ break;
+ LOG_ERROR("%s = 0x%" PRIx64, gdb_regno_name(regno), reg_value);
+ }
return ERROR_TARGET_TIMEOUT;
}
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;
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;
+ struct working_area *crc_algorithm;
+ struct reg_param reg_params[2];
+ int retval;
+
+ LOG_DEBUG("address=0x%" TARGET_PRIxADDR "; count=0x%" PRIx32, address, count);
+
+ static const uint8_t riscv32_crc_code[] = {
+#include "../../contrib/loaders/checksum/riscv32_crc.inc"
+ };
+ static const uint8_t riscv64_crc_code[] = {
+#include "../../contrib/loaders/checksum/riscv64_crc.inc"
+ };
+
+ static const uint8_t *crc_code;
+
+ unsigned xlen = riscv_xlen(target);
+ unsigned crc_code_size;
+ if (xlen == 32) {
+ crc_code = riscv32_crc_code;
+ crc_code_size = sizeof(riscv32_crc_code);
+ } else {
+ crc_code = riscv64_crc_code;
+ crc_code_size = sizeof(riscv64_crc_code);
+ }
+
+ if (count < crc_code_size * 4) {
+ /* Don't use the algorithm for relatively small buffers. It's faster
+ * just to read the memory. target_checksum_memory() will take care of
+ * that if we fail. */
+ return ERROR_FAIL;
+ }
+
+ retval = target_alloc_working_area(target, crc_code_size, &crc_algorithm);
+ if (retval != ERROR_OK)
+ return retval;
+
+ if (crc_algorithm->address + crc_algorithm->size > address &&
+ crc_algorithm->address < address + count) {
+ /* Region to checksum overlaps with the work area we've been assigned.
+ * Bail. (Would be better to manually checksum what we read there, and
+ * use the algorithm for the rest.) */
+ target_free_working_area(target, crc_algorithm);
+ return ERROR_FAIL;
+ }
+
+ retval = target_write_buffer(target, crc_algorithm->address, crc_code_size,
+ crc_code);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("Failed to write code to " TARGET_ADDR_FMT ": %d",
+ crc_algorithm->address, retval);
+ target_free_working_area(target, crc_algorithm);
+ return retval;
+ }
+
+ init_reg_param(®_params[0], "a0", xlen, PARAM_IN_OUT);
+ init_reg_param(®_params[1], "a1", xlen, PARAM_OUT);
+ buf_set_u64(reg_params[0].value, 0, xlen, address);
+ buf_set_u64(reg_params[1].value, 0, xlen, count);
+
+ /* 20 second timeout/megabyte */
+ int timeout = 20000 * (1 + (count / (1024 * 1024)));
+
+ retval = target_run_algorithm(target, 0, NULL, 2, reg_params,
+ crc_algorithm->address,
+ 0, /* Leave exit point unspecified because we don't know. */
+ timeout, NULL);
+
+ if (retval == ERROR_OK)
+ *checksum = buf_get_u32(reg_params[0].value, 0, 32);
+ else
+ LOG_ERROR("error executing RISC-V CRC algorithm");
+
+ destroy_reg_param(®_params[0]);
+ destroy_reg_param(®_params[1]);
+
+ target_free_working_area(target, crc_algorithm);
+
+ LOG_DEBUG("checksum=0x%" PRIx32 ", result=%d", *checksum, retval);
+
+ return retval;
}
/*** OpenOCD Helper Functions ***/
} 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;
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:
case RISCV_HALT_ERROR:
return ERROR_FAIL;
}
+ LOG_DEBUG("[%s] debug_reason=%d", target_name(target), target->debug_reason);
return ERROR_OK;
}
}
LOG_DEBUG(" hart %d halted", halted_hart);
- /* If we're here then at least one hart triggered. That means
- * we want to go and halt _every_ hart in the system, as that's
- * the invariant we hold here. Some harts might have already
- * halted (as we're either in single-step mode or they also
- * triggered a breakpoint), so don't attempt to halt those
- * harts. */
- for (int i = 0; i < riscv_count_harts(target); ++i)
- riscv_halt_one_hart(target, i);
+ 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 {
enum riscv_poll_hart out = riscv_poll_hart(target,
riscv_current_hartid(target));
if (out == RPH_NO_CHANGE || out == RPH_DISCOVERED_RUNNING)
- return ERROR_OK;
- else if (out == RPH_ERROR)
- return ERROR_FAIL;
-
- 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);
- }
-
- 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);
+ return ERROR_OK;
+ else if (out == RPH_ERROR)
+ return ERROR_FAIL;
- 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++;
- }
+ halted_hart = riscv_current_hartid(target);
+ LOG_DEBUG(" hart %d halted", halted_hart);
- if (result != ERROR_OK)
- return result;
+ enum riscv_halt_reason halt_reason = riscv_halt_reason(target, halted_hart);
+ if (set_debug_reason(target, halt_reason) != ERROR_OK)
+ return ERROR_FAIL;
+ target->state = TARGET_HALTED;
}
- int out = riscv_resume_all_harts(target);
- if (out != ERROR_OK) {
- LOG_ERROR("unable to resume all harts");
- return out;
+ if (target->debug_reason == DBG_REASON_BREAKPOINT) {
+ int retval;
+ switch (riscv_semihosting(target, &retval)) {
+ case SEMI_NONE:
+ case SEMI_WAITING:
+ target_call_event_callbacks(target, TARGET_EVENT_HALTED);
+ break;
+ case SEMI_HANDLED:
+ if (riscv_resume(target, true, 0, 0, 0, false) != ERROR_OK)
+ return ERROR_FAIL;
+ break;
+ case SEMI_ERROR:
+ return retval;
+ }
+ } else {
+ target_call_event_callbacks(target, TARGET_EVENT_HALTED);
}
- register_cache_invalidate(target->reg_cache);
- target->state = TARGET_RUNNING;
- target_call_event_callbacks(target, TARGET_EVENT_RESUMED);
- return out;
+ return ERROR_OK;
}
-int riscv_openocd_step(
- struct target *target,
- int current,
- target_addr_t address,
- int handle_breakpoints
-) {
+int riscv_openocd_step(struct target *target, int current,
+ target_addr_t address, int handle_breakpoints)
+{
LOG_DEBUG("stepping rtos hart");
if (!current)
riscv_set_register(target, GDB_REGNO_PC, address);
+ riscv_reg_t trigger_state[RISCV_MAX_HWBPS] = {0};
+ if (disable_triggers(target, trigger_state) != ERROR_OK)
+ return ERROR_FAIL;
+
int out = riscv_step_rtos_hart(target);
if (out != ERROR_OK) {
LOG_ERROR("unable to step rtos hart");
}
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;
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];
}
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;
uint32_t value;
if (r->authdata_read(target, &value) != ERROR_OK)
return ERROR_FAIL;
- command_print(CMD, "0x%" PRIx32, value);
+ command_print_sameline(CMD, "0x%08" PRIx32, value);
return ERROR_OK;
} else {
LOG_ERROR("authdata_read is not implemented for this target.");
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`."
.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`."
{
.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
};
COMMAND_REGISTRATION_DONE
};
-unsigned riscv_address_bits(struct target *target)
+static unsigned riscv_xlen_nonconst(struct target *target)
{
return riscv_xlen(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,
.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,
.commands = riscv_command_handlers,
- .address_bits = riscv_address_bits
+ .address_bits = riscv_xlen_nonconst,
};
/*** RISC-V Interface ***/
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);
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));
}
return r->xlen[hartid];
}
-extern struct rtos_type riscv_rtos;
bool riscv_rtos_enabled(const struct target *target)
{
return false;
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)
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
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,
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;
}
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);
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
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:
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);
{
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;
}
{
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;
}
riscv_free_registers(target);
target->reg_cache = calloc(1, sizeof(*target->reg_cache));
+ if (!target->reg_cache)
+ return ERROR_FAIL;
target->reg_cache->name = "RISC-V Registers";
target->reg_cache->num_regs = GDB_REGNO_COUNT;
target->reg_cache->reg_list =
calloc(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"
};
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"
};
.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"
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
* 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:
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 {
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;
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:
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) {
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);
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);
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);
+}